assuming I currently use
target_link_libraries(...)
in my CMakeLists.txt, which requires the libraries to be installed on the computer I am compiling on, is there a way to move these external libraries inside my project (e.g. into a libs/ folder)?
Yes, but there is multiple ways.
My most preferred is to actually install those libraries in the libs/ directory so the structure look like this:
- libs/
-lib1/
- include
- lib
-lib2/
- include
- lib
It's done by simply installing the library inside the directory of your project:
# inside lib1/build
cmake .. -DCMAKE_INSTALL_PREFIX=~/my/project/libs/lib1
make install
Then, in your project you can set the prefix path to the libs/ directory:
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_PATH}/libs/")
# ...
find_package(lib1) # find the library in libs/lib1
The other ways would be to use add_subdirectory and have the source of the project inside libs. You'll be able to use the target they define so you can link to them.
Related
What is the file structure of CMake library?
For example, here is my library located in library folder:
library
\mylib1.so
\mylib2.so
The headers are in other dir out of library.
Can I use find_library(mylib1 PATHS library) to find my library and use target_include_directories() or include_directories() to include my headers? For my testing, it's failed. So what's the right structure?
Do I need put the header files (.h or .hpp) into library/include folder and put .so in lib folder or put them all into the library folder?
I didn't find any explain in CMake documents. I also find other commands in CMake documents but they are lack of something in details that I don't understand. So I'd like to known how do I find the documents like this.
find_library is used to find a file, but it doesn't create identify provide any information about include directories or other information required by the linking target.
To be able to use your lib after installing it's best to make use of find_package.
You could place files like into a install directory MyLibrary like this:
MyLibrary/lib/MyLib-0.1.1/MyLibraryConfig.cmake
MyLibrary/lib/MyLib-0.1.1/MyLibraryConfigVersion.cmake
MyLibrary/lib/MyLib-0.1.1/libmylibrary.so
MyLibrary/include/MyLib-0.1.1/include/...
MyLibraryConfigVersion.cmake
This file is used to check, if the version of the library is compatible with the build configuration of the project using find_package. Additionaly it provides cmake with information about the version of the library:
set(PACKAGE_VERSION 0.1.1)
if(NOT UNIX # must be the same target platform
OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8 # assuming the installed lib is 64 bit
OR PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE) # inform find_package this is not a suitable version of the lib
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
MyLibraryConfig.cmake
This file is used to actually create the imported lib to use by the project calling find_package; It's only included, if the version file considers the configuration suitable.
if (NOT TARGET MyLibrary) # avoid issues with using find_package(MyLibrary) multiple times
add_library(MyLibrary IMPORTED)
# provide the necessary info about the target
set_target_properties(MyLibrary PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/libmylibrary.so" # specify absolute library file location using the directory containing this cmake file
)
target_include_directories(MyLibrary INTERFACE "${CMAKE_CURRENT_LIST_DIR}/../../include/MyLib-0.1.1/include")
endif()
This allows you to "tell" cmake about the location of the install dir and use find_package like this:
list(APPEND CMAKE_PREFIX_PATH /path/to/MyLibrary) # this could be passed via -D option during configuration instead
find_package(MyLibrary REQUIRED)
Note that there are alternative paths where to put the cmake configuration files, see the documentation of find_package. Furthermore there is cmake functionality that can take over generating some, if not all of the cmake configuration files for you automatically on installation, see
install()
The CMakePackageConfigHelpers cmake module
I want to build two projects using cmake (A library and sandbox application using that library).
I'm currently having the following folder structure:
-- yanthra_engine
|
-- CMakeLists.txt
-- lib
-- ...
-- sandbox
|
-- CMakeLists.txt
-- out
-- ...
The yantra_engine builds a library where as sandbox builds an executable(using the above mentioned library).
Should I keep full fledged CMakeLists files for both the projects? Is there any efficient folder structure to follow?
I would like the library to build automatically when building my sandbox application, but not vice-versa.
You should keep seperate CMakeLists.txt files. It's good practice to use one CMakeLists.txt file per unrelated target. (Unrelated in the sense of not being different builds of the same library, e.g. one shared one static.)
Whether to add both targets to the same project is basically up to you. If the library is properly set up, you could create a seperate project for the library and use it's install target to install the library including cmake configuration files on your machine making it easy to import the installed library as a target to the sandbox project. This requires you to add the appropriate install commands to the library.
If you want to be able to build both the library and the sandbox via the same build files, basically all you need to do is to make sure both CMakeLists.txt files are reachable from a CMakeLists.txt file.
Without location of any of the files you could e.g.
Create a CMakeLists.txt file in the parent folder adding both subdirectories
cmake_minimum_required(VERSION 3.0)
project(CommonProject)
add_subdirectory(yanthra_engine)
add_subdirectory(sandbox EXCLUDE_FROM_ALL) # targets in sandbox not built by default.
You could also include the yanthra_engine directory from sandbox/CMakeLists.txt
...
add_subdirectory(../yanthra_engine "${CMAKE_BINARY_DIR}/yanthra_engine_build")
...
Both approaches allow you to either set up a build project for the lib on its own or set up a build project for both. In approach 1 the source directory for building both would be the parent directory of yanthra_engine and sandbox and in approach 2 it would be sandbox.
For both approaches you don't need to wory about unnecessarily building the sandbox project though as long as you specify the target you want to build. Since sandbox links the lib but there is no dependnecy established the other way round building sandbox makes sure the lib is up to date, but building the lib only builds the lib and its dependencies which excludes the sandbox.
One thing you can try is to let yanthra be a subdirectory of sandbox. Then you can do this in sandbox:
add_subdirectory(yanthra_engine)
I'm trying to include external libraries to a cmake project through ExternalProject_Add. To try out this feature I've created a minimal working example that involves adding pugixml to the project with ExternalProject_Add. However, I'm having problems finding a way to add the library's header files from the external project's local installation (i.e., pugixml's headers) in the project's include path.
The project tree of the minimal working example is organized as follows:
.
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
In this project tree, build refers to the build directory and the path where cmake is called to generate the build.
The contents of ./CMakeLists.txt are as follows:
cmake_minimum_required(VERSION 3.0)
include(ExternalProject)
ExternalProject_Add(pugixml
GIT_REPOSITORY https://github.com/zeux/pugixml.git
INSTALL_DIR ${PROJECT_BINARY_DIR}/extern_install/pugixml
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
)
add_subdirectory(src)
In the above example I've added pugixml as an external project that is to be installed within the project's binary dir, whose file will be used by an executable stored in ./src. Thus, the contents of ./src/CMakeLists.txt are:
project(foo)
add_executable(foo main.cpp)
target_link_libraries(foo ${pugixml_LIBRARIES})
include_directories(${pugixml_INCLUDE_DIR}) # this part doesn't work
This is precisely the part I'm having problems. I've assumed that once the external project was added and installed that ExternalProject_Add would have defined some convenience libraries to help refer to library files and include directories. However, that doesn't work. Does anyone know what's the right way of using ExternalProject_Add to include external libraries?
Unfortunately this will not work at all. The build of the external project is done at build time not during CMake configure/generate. The hint is The ExternalProject_Add function creates a custom target to drive download, update/patch, configure, build, install and test steps of an external project: (from CMake)
Hence You will have to define all the variables yourself. See also: CMake - linking to library downloaded from ExternalProject_add() (I'd actually mark this as a duplicate of the linked question as the problem is the same)
BTW: I do also dislike the way this was done. This way one cannot simply use find_package etc.
The intended way to use this is a "super-build project": Define 1 CMakeLists where you only have ExternalProject_Adds for all dependencies AND your project. Then it will work with find_package.
TL;DR I basically want a modular solution of this question. Not only one solution with everything in it, but also a solution for each executable alone.
In my question here I wanted to know how I should split my CMakeLists.txt among different folders. Some folders contain code that will be a static library and some code will be executables or dynamic libraries that build on those static libraries.
Folder structure looks like this:
/path/to/base/
CMakeLists.txt
app1/
<src files for app1>
CMakeLists.txt
app2/
<src files for app2>
CMakeLists.txt
lib/
<src files for lib>
CMakeLists.txt
If I take the approach of having a CMakeLists.txt in the parent folder to all my libraries and executables and from the include all the directories I have the problem that when I generate VS projects/solutions that my projects for each target contains all the projects - not only the ones necessary for my intended target.
Contents of CMakeLists.txt in the base folder:
cmake_minimum_required(VERSION 3.1)
add_subdirectory(lib)
add_subdirectory(app1)
add_subdirectory(app2)
and the CMakeLists.txt in the app1 folder (app2 is equivalent)
cmake_minimum_required(VERSION 3.1)
project(app1)
add_executable(app1 <src of app1>)
target_link_libraries(app1 lib)
and CMakeLists.txt of lib:
add_library(lib <src of lib>)
If I run cmake on the CMakeLists.txt from base the solution contains all the projects. And even if I open just one of the projects; it also contains everything. VS project of app1 also builds app2 - which I don't want.
If I only run cmake on the CMakeLists.txt of app1 I get a solution which doesn't contain app2 but it also doesn't contain lib, because that's only mentioned for linking inside the cmake file and not as a target (it's in the base file)
CMake will create a solution for each call to project, which will then include all the targets of the current directory and its subdirectories (regardless whether they were defined before or after the actual call to project).
Here's what a well-behaved CMake script should do: Each CMakeLists that you want to be able to act as a root of its own sub-tree within the larger project should start with a cmake_minimum_required call, followed by the project call. Writing project in a CMakeLists basically means: This is a fully self-contained component that can be built independently, even if you delete all the files from its parent directories. As such, it makes sense to have a separate solution for each such project.
With this in mind we can see why this doesn't work in your case: Your apps are not fully self-contained, they depend on lib. The solution to this is to write the build scripts for the apps as if lib was provided as a third-party component, with find_package calls and everything.
In the compound build, you already have a target for lib, so the script invoked by the find_package call should be made to short-circuit to using that target and only actually go scavenging the system if it cannot find that existing target.
Also read up on how CMake's packaging system works, which provides some automation to handle such cases pretty elegantly.
I'm pretty new to cmake, but I am stuck with an odd behaviour that I'd like to understand and overcome.
I'm creating an executable that uses the Paho MQTT C library. The content of this executable is irrelevant, let's say it just connects to a broker for now, nothing fancy.
To compile my program, I use cmake with a CMakeLists.txt file, which does a few things:
use an ExternalProject block to pull the MQTT C lib from Github
declare the executables and the linked libs
Here it is :
project(myproject)
include(ExternalProject)
########## CMAKE VERSION AND FLAGS ##########
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
########## MQTT EXTERNAL LIB ##########
set(MQTT_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/mqtt)
ExternalProject_Add(
project_mqtt
GIT_REPOSITORY https://github.com/eclipse/paho.mqtt.c
GIT_TAG v1.2.0
PREFIX ${MQTT_BUILD_DIR}
CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
)
add_library(mqtt STATIC IMPORTED)
set(MQTT_LIB ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib/libpaho-mqtt3a.1.2.0.dylib)
set_property(TARGET mqtt PROPERTY IMPORTED_LOCATION ${MQTT_LIB})
add_dependencies(mqtt project_mqtt)
include_directories(${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/include)
link_directories(${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib)
########## EXECUTABLE ##########
add_executable(myproject ../src/main.cpp)
target_link_libraries(myproject mqtt)
Note that in the file I especially compile for Mac and I hardcoded the .dylib suffix but this is not a problem here
My folder structure is as follows :
myproject/
-- CMakeLists.txt
-- bin/
-- build/
-- src/
- main.cpp
My compilation workflow is very standard : cd build && cmake ../ && make.
My sources are in /src, I build everything in /build and my executable is written in /bin. The *.dylib are explicitely built locally in the project folder (in bin/lib) since I don't want to install them in the system (I want my project to be self-contained in a way).
It works well (I mean, it compiles).
But when I run the program, I get a dyld: Library not loaded: libpaho-mqtt3a.1.dylib error.
.. which is quite "normal", because my lib has been installed in bin/lib and my executable searches for them in its own folder (which is bin/). So for instance if I run : cd bin/lib && ../myproject it works perfectly well.
So, now here are my questions :
[Philosophically,] Is it a good way of doing this ? I want to use this external lib but I don't want to include the Paho lib sources in my code obviously, so I though ExternalProject was a good way to go. It compiles the lib at compile time before compiling my project and I find the process quite handy. I looked for a "package manager" for C++ but there does not seem to be a strong consensus (I'm looking at something like *composer* or *npm* for web apps), so I did not go this route.
Why, oh why, does my executable look for the library file in its own directory ./ ? Can I change that ? Is it some cmake configuration that I missed ? I guess that I could just use a cmake custom command to copy the library file into the /bin folder, but this looks like a hack to me. Isn't it ?
To be honest, I'd like to have no "path dependencies" in my executable, it's to say that the executable, when compiled on a specific system, could be put anywhere on this system and work flawlessly afterwards. I understand that means that the compiled Paho MQTT lib should be statically in my executable, but I wonder how to do that (or even what that really means, in fact). Is it possible ?
I may miss a whole part of how the C/C++ compile and external lib workflow works but I think I'm close to something quite robust, it's just this lib path thing at runtime that I would like to properly understand and fix.
I'm really open to suggestions on this
Thanks a lot !