I'm tearing my hair out about how to include thrid party libraries in my cmake project. Currently I build Poco and a bunch of others that all generate their respective Config.cmake which I use with find_package. I have a wrapping build script that builds all of my dependencies and package them separately for debug and release (I don't want to tweak their cmake-scripts unless I really really really need to because maintanance).
I thought I could just do:
find_package(Foo
HINTS "${CMAKE_SOURCE_DIR}/some/path/debug/libFoo/lib/cmake"
REQUIRED
)
get_target_property(LIB_FOO_DEBUG lib_foo LOCATION)
find_package(Foo
HINTS "${CMAKE_SOURCE_DIR}/some/path/release/libFoo/lib/cmake"
REQUIRED
)
get_target_property(LIB_FOO_RELEASE lib_foo LOCATION)
set(LIB_FOO_LIBRARIES optimized "${LIB_FOO_RELEASE}" debug "${LIB_FOO_DEBUG}")
message("LIB_FOO_LIBRARIES: \"${LIB_FOO_LIBRARIES}\"")
This yeilds: LIB_FOO_LIBRARIES: "optimized;C:/path/to/proj/some/path/debug/libFoo/lib/foo.lib;debug;C:/path/to/proj/some/path/debug/libFoo/lib/foo.lib"
It seems like the first call to find_package for target Foo is cached, whis I don't really want.
Am I going about this the wrong way? How do I properly work with third party libraries with the Visual Studio generator?
Any pointers are greatly appreciated.
the first call to find_package for target Foo is cached
Yes. So you cannot issue find_package twice and get different results (unless the first call failed).
It is third-party package who is responsible for multiconfig-usage, that is it should have properly written *Config.cmake/Find*.cmake file. (E.g., FindBoost.cmake support multi-config usage).
Otherwise, you should do some tricks for use package in multiconfig manner.
E.g., if you guess that only difference between configurations is debug/release substrings in paths, you can call find_package() for debug installation, then use string(REPLACE) for get release-specific paths:
find_package(Foo
HINTS "${CMAKE_SOURCE_DIR}/some/path/debug/libFoo/lib/cmake"
REQUIRED
)
get_target_property(LIB_FOO_DEBUG lib_foo LOCATION)
string(REPLACE debug release LIB_FOO_RELEASE ${LIB_FOO_DEBUG})
# Use generator expressions, so variable can be used not only by target_link_libraries.
set(LIB_FOO_LIBRARIES
"$<$<NOT:$<CONFIG:DEBUG>>:${LIB_FOO_RELEASE}>"
"$<$<CONFIG:DEBUG>:${LIB_FOO_DEBUG}>"
)
Related
I'm trying to import libuv into my CMake project so I can link it. I have libuv 1.12.0 installed from here and I placed it in C:\Program Files\libuv\.
project(tls-server LANGUAGES C)
set(LIBUV_ROOT_DIR "C:\\Program Files\\libuv")
add_library(libuv SHARED IMPORTED)
set_property(TARGET libuv PROPERTY IMPORTED_LOCATION "${LIBUV_ROOT_DIR}\\libuv.dll")
set_property(TARGET libuv PROPERTY IMPORTED_IMPLIB "${LIBUV_ROOT_DIR}\\libuv.lib")
add_executable(tls-server "${CMAKE_SOURCE_DIR}/src/main.c")
target_link_libraries(tls-server libuv)
However, given the above code I am still getting undefined symbol errors in Visual Studio:
How can I fix this? I believe the paths are all correct. I'm also using Windows 10.
Never hardcode library paths or names like this in CMake.
Instead use the find_library command, which does a decent job of notifying you early if something is wrong with the provided library.
On Windows in particular, since there are no default locations where libraries are located on the system (something like the /usr/local/lib on *nix systems), you may want to provide an additional customization point for the library's location. I personally like to use environment variables for this, but a normal CMake option will also do:
project(tls-server LANGUAGES C)
find_library(LIBUV_LIBRARIES NAMES uv libuv
HINTS $ENV{LIBUV_ROOT})
add_executable(tls-server ${PROJECT_SOURCE_DIR}/src/main.c)
target_link_libraries(tls-server ${LIBUV_LIBRARIES})
Note that CMake in general never takes care of copying runtime dependencies to the correct place! That is, if libuv was built as a .dll, you must ensure that that .dll is in the correct path when running the program.
You can of course manually insert a copy command in CMake for getting all the dlls into place, but that can be quite cumbersome. Unfortunately there is no more comfortable solution for this problem right now.
Using imported targets here is possible, but really only pays off if you need to pass on more complex properties to the depending target. In my experience, imported targets work best if the dependency provides a fully-fledged package config file. Writing imported targets manually is often not worth the trouble in terms of additional complexity.
I finally managed to solve the issue.
Firstly I reinstalled the binaries from the target library (libuv). Then, I made sure that my cmake was generating x64 project files by using cmake -G "Visual Studio 14 2015 Win64". That was sufficient to get rid of the undefined symbols error. Then all I needed to do was copy the libuv.dll file to the same directory as the executable file, and everything ran fine.
If anyone knows why this error occured, please comment so you can help out other people in the future target the cause of the error better.
I don't understand, why do we need cmake to build libraries ? I am sorry if my question is stupid, but i need to use some libraries on Widnows, and what ever library i choose i need to build it and/or compile it with cmake.. What is it for ? Why cant i just #include "path" the things that i need into my project, and than it can be compiled/built at the same time as my project ?
And also, sometimes i needed to install Ruby, Perl, Python all of them some specific version so cmake can build libraries... Why do i need those programs, and will i need them only to build library or later in my project too ? (concrete can i uninstall those programs after building libraries ?)
Building things in c++ on different platforms is a mess currently.
There are several different build system out there and there is no standard way to do this. Just providing a visual studio solution wont help compilation on linux or mac.
If you add a makefile for linux or mac you need to repeat configurations between the solution and the makefiles. Which can result in a lot of maintenance overhead. Also makefiles are not really a good build tool compared to the new ones out there.
That you have only CMake libraries is mostly a coincidence. CMake is though a popular choice currently.
There are several solutions out there to unify builds. CMake is a build tool in a special way. It can create makefiles and build them but you can also tell cmake to create a visual studio solution if you like.
The same goes with external programs. They are the choice of the maintainer of the library you use and there are no standards for things like code generation.
While CMake may not be "the" solution (although the upcoming visual studio 2015 is integrating cmake support) but the trend for those build system which are cross-platform is going more and more in this direction.
To your question why you cannot only include the header:
Few libraries are header only and need to be compiled. Either you can get precompiled libs/dlls and just include the header + add the linker path. This is easier in linux because you can have -dev packages which just install a prebuild library and it's header via the package manager. Windows has no such thing natively.
Or you have to build it yourself with whatever buildtool the library uses.
The short answer is that you don't, but it would probably be difficult to build the project without it.
CMake does not build code, but is instead a build file generator. It was developed by KitWare (during the ITK project around 2000) to make building code across multiple platforms "simpler". It's not an easy language to use (which Kitware openly admits), but it unifies several things that Windows, Mac, and Linux do differently when building code.
On Linux, autoconf is typically used to make build files, which are then compiled by gcc/g++ (and/or clang)
On Windows, you would typically use the Visual Studio IDE and create what they call a "Solution" that is then compiled by msvc (the Microsoft Visual C++ compiler)
On Mac, I admit I am not familiar with the compiler used, but I believe it is something to do with XCode
CMake lets you write a single script you can use to build on multiple machines and specify different options for each.
Like C++, CMake has been divided between traditional/old-style CMake (version < 3.x) and modern CMake (version >= 3.0). Use modern CMake. The following are excellent tutorials:
Effective CMake, by Daniel Pfeifer, C++Now 2017*
Modern CMake Patterns, by Matheiu Ropert, CppCon 2017
Better CMake
CMake Tutorial
*Awarded the most useful talk at the C++Now 2017 Conference
Watch these in the order listed. You will learn what Modern CMake looks like (and old-style CMake) and gain understanding of how
CMake helps you specify build order and dependencies, and
Modern CMake helps prevent creating cyclic dependencies and common bugs while scaling to larger projects.
Additionally, the last video introduces package managers for C++ (useful when using external libraries, like Boost, where you would use the CMake find_package() command), of which the two most common are:
vcpkg, and
Conan
In general,
Think of targets as objects
a. There are two kinds, executables and libraries, which are "constructed" with
add_executable(myexe ...) # Creates an executable target "myexe"
add_library(mylib ...) # Creates a library target "mylib"
Each target has properties, which are variables for the target. However, they are specified with underscores, not dots, and (often) use capital letters
myexe_FOO_PROPERTY # Foo property for myexe target
Functions in CMake can also set some properties on target "objects" (under the hood) when run
target_compile_definitions()/features()/options()
target_sources()
target_include_directories()
target_link_libraries()
CMake is a command language, similar shell scripting, but there's no nesting or piping of commands. Instead
a. Each command (function) is on its own line and does one thing
b. The argument(s) to all commands (functions) are strings
c. Unless the name of a target is explicitly passed to the function, the command applies to the target that was last created
add_executable(myexe ...) # Create exe target
target_compile_definitions(...) # Applies to "myexe"
target_include_directories(...) # Applies to "myexe"
# ...etc.
add_library(mylib ...) # Create lib target
target_sources(...) # Applies to "mylib"
# ...etc.
d. Commands are executed in order, top-to-bottom, (NOTE: if a target needs another target, you must create the target first)
The scope of execution is the currently active CMakeLists.txt file. Additional files can be run (added to the scope) using the add_subdirectory() command
a. This operates much like the shell exec command; the current CMake environment (targets and properties, except PRIVATE properties) are "copied" over into a new scope ("shell"), where additional work is done.
b. However, the "environment" is not the shell environment (CMake target properties are not passed to the shell as environment variables like $PATH). Instead, the CMake language maintains all targets and properties in the top-level global scope CACHE
PRIVATE properties get used by the current module. INTERFACE properties get passed to subdirectory modules. PUBLIC is for the current module and submodules (the property is appropriate for the current module and applies to/should be used by modules that link against it).
target_link_libraries is for direct module dependencies, but it also resolves all transitive dependencies. This means when you link to a library, you gets all the PUBLIC properties of the parent modules as well.
a. If you want to link to a library that has a direct path, you can use target_link_libraries, and
b. if you want to link to a module with a project and take its interface, you also use target_link_libraries
You run CMake on CMakeLists.txt files to generate the build files you want for your system (ninja, Visual Studio solution, Linux make, etc.) and the run those to compile and link the code.
I'd like to use imported and internal libraries in my CMake projects so that the projects don't need to know the details of the library. (By "Internal library" I mean other CMake library targets, not sure about the correct term...)
The information should contain:
library locations for each configuration type (including dll's and .pdb files)
library include files folder
In some project I'd like to write something like:
SET(TARGET_DEPENDS ext_lib1 ext_lib2 internal_lib1)
and let the build system take care of all the include folder and configuration stuff.
What is the best way to achieve this?
AFAIK the add_library(... IMPORTED) supports pretty much everything else, but the include folder information.
When building your "internal" library, you could also prepare a YourInternalLibraryConfig.cmake file which prepares all those things a user would need to "import" the library.
This YourInternalLibraryConfig.cmake needs to be created when building the internal library or when installing it.
In this YourInternalLibraryConfig.cmake, it should call add_library( YourInternalLibrary IMPORTED ) and make sure all the properties have been correctly prepared. E.g. it can prepare
a YourInternalLibrary_INCLUDE_DIRS variable with the necessary include-dirs.
a YourInternalLibrary_DEFINES with the required compiler-defines
a YourInternalLibrary_LIBRARIES summarizing all the libraries provided
That way a user (e.g. the main app ) of your internal library can simply call:
find_package( YourInternalLibrary )
include_directories( ${YourInternalLibrary_INCLUDE_DIRS} )
add_definitions( ${YourInternalLibrary_DEFINES} )
target_link_library( main ${YourInternalLibrary_LIBRARIES} )
For more complex projects you could choose to generate a separate UseYourInternalLibrary.cmake which does these three steps for you, optionally with switches enabling/disabling certain parts of your internal.
For some good examples take a look at the sources for Vtk or OpenCV
I'm using CMake and CTest with Visual Studio 2010.
After ENABLE_TESTING() and several ADD_TEST()s my solution contains projects with names 'Continuous', 'Experimental' and others.
I don't need them all. Moreover, I'm going to pass all the code to a person, which is not very familiar with CMake and afraid that this abundance will confuse and mislead him.
So, I'd like to leave only RUN_TESTS project and get rid of all other testing projects.
How can I do this?
Double check that there is no include(CTest) in one of your CMake list files. The inclusion of this CMake module creates the targets Continuous, Experimental, ... as a side effect. Calling enable_testing is sufficient for getting a RUN_TESTS target.
This post and this post says that with Visual Studio, the run time library can be static/dynamic, and it shouldn't be mixed. Even one can have debugging version/release version for the library. And there are four possibilities (static/dynamic and debug/release).
So, with Visual Studio, the library provider has to provide four different versions of the same library?
ADDED
I tried to link CppUnit test (debug) with release build library, and I got an error. So, I wondered normally library provider might need to provide all the possible combination of libraries.
depends..
under normal cicrcumstances you only provide a realease version. Then you have the option for static/dynamic. In the case of static, you don't have to provide anything since it's static: your lib already contains all functions from the crt it needs. In case of dynamic, it also depends: if you expect your clients to build applications using your lib, they already should have the required lib on their build machine. Else, yes, you can provide them with a crt installer for the dynamic release version (or just ship the corresponding dlls but that's considered rather bad practice)
Also if I remember correctly, you cannot redistribute the debug versions of VS's debug libraries, so in the end this would mean the library provider should only provide one version.
This is really the case with ANY C++ library (we have the same 4 options in our Unix side builds).
Please note that you only have to provide the debug versions if you intend them to be used by other developers, who will need them to debug - otherwise, for end users, you can only provide optimized ones.