With 'find_package': how do I know which variables have to be used for linking? - cmake

I use find_package() to find some library. How do I know which variables it defines, so I could use them for linking with that library?
For example, the following CMakeLists.txt successfully is run by CMake, but then make fails at the linking stage, because ${DOCOPT_LIBRARIES} was empty.
cmake_minimum_required(VERSION 3.1)
project(test)
set(PROJECT_LIBS "")
find_package(docopt REQUIRED)
include_directories(${DOCOPT_INCLUDE_DIRS})
set(PROJECT_LIBS ${PROJECT_LIBS} ${DOCOPT_LIBRARIES})
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${PROJECT_LIBS})
But I have no idea what else to try?

The usage of results of find_package(XXX) depends on the script which is actually used by this command:
FindXXX.cmake.
This file is either shipped with the project which wants to use it, or with CMake. Usually, such "Find" scripts start with some description about their usage.
XXXConfig.cmake or xxx-config.cmake.
"Config" scripts are usually installed with the package. Normally, they are generated by CMake using specific CMake commands. You need to consult the package's documentation for use such scripts properly.
If the package doesn't document the usage of its "Config" script, then you may only guess about its usage.
Reading a "Config" script itself could be difficult: CMake includes into it a lot of "sugar", which helps portability and compatibility.
Normally, a "Config" script provides some IMPORTED library target(s), which can be used with target_link_libraries call.
One may scan the project's CMakeLists.txt for a line like
install(TARGETS <targets...> EXPORT ...)
Here <targets...> denotes targets, which can be accessed after find_package() call.
Also scan for a line like
install(EXPORT ...)
if it has NAMESPACE option, then its value prepends all exported targets.
Example:
For docopt package, its CMakeLists.txt contains a line
install(TARGETS docopt EXPORT ${export_name} DESTINATION ${CMAKE_INSTALL_LIBDIR})
thus its docopt target is exported.
As installation of the export file
install(EXPORT ${export_name} DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/docopt")
doesn't contain NAMESPACE option, the export target is accessible with its original name:
find_package(docopt REQUIRED)
...
add_executable(${PROJECT_NAME} main.cpp)
# Use *IMPORTED* 'docopt' target
target_link_libraries(${PROJECT_NAME} docopt)

Related

Python library and CMake target with the same name

I'm constructing a library "mylib" that is C++ header-only and has a Python API using pybind11.
I want to use "mylib" both as CMake target, containing compile instructions, and as name of the Python API. However, this leads to a name conflict.
Problem description
Consider the following file structure:
CMakeLists.txt
include/mylib.hpp
python_api.cpp
In reality there are also tests and examples, each with their own CMakeLists.txt, but for the purpose of this example the only thing that matters is:
In the (main) CMakeLists.txt I am defining a CMake target "mylib" that has the include path to the header(s), but also 'links' the targets of dependencies. So that the user (or tests, examples, or build of the Python API) only has to 'link' the target and be good to go. (Finally, I'm also installing the target in mylibTargets.cmake when I install the headers such that there is CMake support for the end user).
Now the problem: My Python package should have the same name, "mylib". However, if I call pybind11_add_module with "mylib", CMake complains that
CMake Error at .../share/cmake/pybind11/pybind11Tools.cmake:166 (add_library):
add_library cannot create target "mylib" because another target with the
same name already exists. The existing target is an interface library
created in source directory "..".
See documentation for policy CMP0002 for more details.
It has the right to complain. At the same time I cannot use a different name for either the CMake target (since I want to install and use it using the only logical name, "mylib") or the pybind11 target (since it has to encode "mylib").
So: how do I solve this?
(The only solution I found was to rename one of targets, but as described I don't want to do this)
Detailed example
Consider the simplified, single, CMakeLists.txt:
cmake_minimum_required(VERSION 3.1..3.19)
# configure target
project(mylib)
find_package(xtensor REQUIRED)
add_library(${PROJECT_NAME} INTERFACE)
target_include_directories(${PROJECT_NAME} INTERFACE
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries(${PROJECT_NAME} INTERFACE xtensor)
# installation of headers and of CMake target
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" DESTINATION include)
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}-targets)
install(
EXPORT ${PROJECT_NAME}-targets
FILE "${PROJECT_NAME}Targets.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
# Build Python module
find_package(pybind11 CONFIG REQUIRED)
pybind11_add_module(${PROJECT_NAME} python_api.cpp) # <- target name conflict
target_link_libraries(example PUBLIC pybind11::module)
Too limited work around
I could entirely split building (and later install) the Python API to an independent CMakeLists.txt. However, I want to use the target "mylib", that I already equipped with everything it needs, to build the Python API. Since I want to do this without being forced to install the library forced, I don't know how to do this in a 'single' CMakeLists.txt
pybind11_add_module is just a wrapper around add_library, this is explicitely written in the documentation for that function. So, most of the "tricks", which works for the common libraries, works for python modules too.
That is, if you want resulted file to be named as mylib.so but cannot afford you to use mylib as a target name, then you could use any other name for the target but adjust OUTPUT_NAME property for that target. For example:
# Python library target has suffix '_python'
pybind11_add_module(mylib_python ...)
# But name of the library file doesn't have this suffix
set_target_properties(mylib_python PROPERTIES OUTPUT_NAME mylib)

Cmake and vcpkg - find the correct library name

I have troubles finding out the right "library target name" to be used in a cmake file, for packages installed using vcpkg.
In example, I installed the gtest package using vcpkg install gtest. My sample cmake file looks like:
#CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(example)
add_executable(main main.cpp)
find_package(gtest REQUIRED)
target_link_libraries(main gtest) # here, "gtest" is not the right name!
Running cmake, a solution for Visual Studio is generated, but after running cmake --build ., I get the error:
../use-cmake-vcpkg\main.cpp(1): fatal error C1083: Cannot open include file: 'gtest/gtest.h': No such file or directory ..
Turns out the line: target_link_libraries(main gtest) isn't correct, and I need to use another "name" to include/link the gtest package.
Is there a way (using cmake or vcpkg) to find out what is the correct target name to be used? (for gtest in this case, but also for any other pacakage?)
When use find_package(XXX), it can work in two modes: MODULE and CONFIG. And resulted variables and targets of this call depend on the mode.
If FindXXX.cmake file exists (and can be found), the MODULE mode is used and given file is processed. Otherwise, if the package is shipped with XXXConfig.cmake file, CONFIG mode is used and given file is processed. If none of this file exists, CMake emits an error (if called with REQUIRED keyword) or a warning (without REQUIRED keyword).
In case of gtest package, CMake is shipped with FindXXX.cmake script, so this script is processed in MODULE mode. You may find description of this script in the documentation, which tells that you need to use GTest::GTest target for link with gtest:
target_link_libraries(main GTest::GTest)
Not all packages provide a CMake library definition. If you're lucky, then vcpkg install will show you the name:
$ ./vcpkg install openssl
The package openssl is compatible with built-in CMake targets:
find_package(OpenSSL REQUIRED)
target_link_libraries(main PRIVATE OpenSSL::SSL OpenSSL::Crypto)
This will work even if you've already installed the package, so you can use it anytime to query the package name.
On the other hand, if vcpkg install <pkg> doesn't say anything about CMake, then you need to include it manually in your CMake file, by finding the include path and the library files.
Here is an example of such a case, here for the live555 library:
# Use one of the headers to locate the include location
find_path(LIVE555_INCLUDE_DIR liveMedia.hh)
# Find the libraries
find_library(LIVE555_LIBRARY1 liveMedia)
find_library(LIVE555_LIBRARY2 groupsock)
find_library(LIVE555_LIBRARY3 BasicUsageEnvironment)
find_library(LIVE555_LIBRARY4 UsageEnvironment)
add_executable(rtsp testRTSPClient.cpp)
target_include_directories(rtsp PRIVATE ${LIVE555_INCLUDE_DIR})
target_link_libraries(rtsp PRIVATE ${LIVE555_LIBRARY1} ${LIVE555_LIBRARY2} ${LIVE555_LIBRARY3} ${LIVE555_LIBRARY4})

Using CMAKE_DEBUG_POSTFIX with exported targets

When I use set(CMAKE_DEBUG_POSTFIX "d"), the build and install targets work as expected. But in the libfooTargets-debug.cmake file with the exported targets, there is a path to libfoo and not libfood.
I exported the targets like this:
install(TARGETS libfoo EXPORT libfoo-targets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
install(EXPORT libfoo-targets FILE libfooTargets.cmake DESTINATION ${CMAKE_INSTALL_PREFIX})
which creates and installs libfooTargets.cmake and libfooTargets-debug.cmake when building in debug mode, and libfooTargets.cmake and libfooTargets-release.cmake when building in release mode.
Both libfooTargets-release.cmake and libfooTargets-debug.cmake reference the name without a postfix as:
list(APPEND _IMPORT_CHECK_FILES_FOR_libfoo "${_IMPORT_PREFIX}/lib/libfoo.lib" )
and thus a program linking against the debug target still uses the release-build library and I would need to install release and debug versions into different folders to be able to link against the debug target.
How can I get the exported targets to work with a debug postfix?
I could of course try to change the library name depending on CMAKE_RELEASE_TYPE or a CONFIGURATION generator expression, but this will probably break the multi-configuration features in MSVC and other IDEs supporting different targets and seems not to work in the sense of how the exported targets feature is meant to simplify and unify the build.
I suspect that the install(EXPORT ...) command somehow drops the CMAKE_DEBUG_POSTFIX or does not implement it for generating the libfooTargets-{release,debug}.cmake files, but possibly I overlooked how to make this variable visible to the generator of the exported targets or something like this.
All target code
cmake_minimum_required(VERSION 3.11.1)
project(foo)
include(CMakePackageConfigHelpers)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_DEBUG_POSTFIX "d")
# ...
add_library(libfoo STATIC somesource.cpp someheader.h)
target_include_directories(libfoo PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
target_link_libraries(libfoo
somelibrary
)
target_include_directories (libfoo PUBLIC
somelibrary_header_dirs
)
install(TARGETS libfoo EXPORT libfoo-targets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
install(EXPORT libfoo-targets FILE libfooTargets.cmake DESTINATION ${CMAKE_INSTALL_PREFIX})
configure_package_config_file(libfooConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libfooConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libfooConfig.cmake DESTINATION ${CMAKE_INSTALL_PREFIX})
install(DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*.h")
The platform is a Windows 10 with cmake 3.11.1 and MSVC 2015. Of course the most general solution is probably the best one.
According to the documentation of the install command, you need to reference the configuration that you are interested in:
[...] If a CONFIGURATIONS option is given then the file will only be installed when one of the named configurations is installed. Additionally, the generated import file will reference only the matching target configurations. [...]
So, you need to add the CONFIGURATIONS option in both install commands and duplicate the commands for each configuration you want to install and export.

Making a CMake library accessible by other CMake packages automatically

I have one project that produces a library:
project (myCoolLibrary)
ADD_LIBRARY(my_cool_library SHARED ${mysources_SRC})
And another project that should be using this library:
find_package (myCoolLibrary REQUIRED)
INCLUDE_DIRECTORIES("${myCoolLibrary_INCLUDE_DIRS}" )
add_executable(myCoolExe ${my_sources_SRC} )
TARGET_LINK_LIBRARIES(myCoolExe ${myCoolLibrary_LIBRARIES} )
Is there a way that I can change the first file so that the second file works automatically? That by running CMake on the first file and then running make on the output, then running CMake on the second file, CMake is able to find the package?
An answer where I just give the address of where the first project is built to the second package is also acceptable.
Taking the code found in a blog post by #daniperez - Use CMake-enabled libraries in your CMake project (III) - I've come up with the following minimal solution:
myCoolLibrary/CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(myCoolLibrary)
function(my_export_target _target _include_dir)
file(
WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Config.cmake"
"
include(\"\$\{CMAKE_CURRENT_LIST_DIR\}/${_target}Targets.cmake\")
set_property(
TARGET ${_target}
APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES \"${_include_dir}\"
)
"
)
export(TARGETS ${_target} FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Targets.cmake")
# NOTE: The following call can pollute your PC's CMake package registry
# See comments/alternatives below
export(PACKAGE ${_target})
endfunction(my_export_target)
...
add_library(${PROJECT_NAME} SHARED ${mysources_SRC})
my_export_target(${PROJECT_NAME} "${CMAKE_CURRENT_SOURCE_DIR}")
myCoolExe/CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(myCoolExe)
find_package(myCoolLibrary REQUIRED)
...
add_executable(${PROJECT_NAME} ${my_sources_SRC})
target_link_libraries(${PROJECT_NAME} myCoolLibrary)
To make it reusable I have packed everything into my_export_target(). And I'm friend of self-propagating properties like INTERFACE_INCLUDE_DIRECTORIES.
As commented by #ruslo, using export(PACKAGE ...) can pollute your package registry. So alternatively you can:
Write the target configuration files directly to some dedicated place specific for a certain toolchain
See e.g. How to install your custom CMake-Find module and 0003659: FIND_PACKAGE command improvements.
Set CMAKE_MODULE_PATH via the second project's CMake command line (injecting the search path(s) from the outside). If you are building the two projects anyway with a build script, then this is the most direct way to propagate the module search path(s).
Additional References
export()
CMake/Tutorials/Package Registry
Unable to find Eigen3 with CMake
How to instruct CMake to use the build architecture compiler

What use is find_package() when you need to specify CMAKE_MODULE_PATH?

I'm trying to get a cross-plattform build system working using CMake. Now the software has a few dependencies. I compiled them myself and installed them on my system.
Some example files which got installed:
-- Installing: /usr/local/share/SomeLib/SomeDir/somefile
-- Installing: /usr/local/share/SomeLib/SomeDir/someotherfile
-- Installing: /usr/local/lib/SomeLib/somesharedlibrary
-- Installing: /usr/local/lib/SomeLib/cmake/FindSomeLib.cmake
-- Installing: /usr/local/lib/SomeLib/cmake/HelperFile.cmake
Now CMake has a find_package() which opens a Find*.cmake file and searches after the library on the system and defines some variables like SomeLib_FOUND etc.
My CMakeLists.txt contains something like this:
set(CMAKE_MODULE_PATH "/usr/local/lib/SomeLib/cmake/;${CMAKE_MODULE_PATH}")
find_package(SomeLib REQUIRED)
The first command defines where CMake searches after the Find*.cmake and I added the directory of SomeLib where the FindSomeLib.cmake can be found, so find_package() works
as expected.
But this is kind of weird because one of the reasons why find_package() exists is to get away from non-cross-plattform hard coded paths.
How is this usually done? Should I copy the cmake/ directory of SomeLib into my project and set the CMAKE_MODULE_PATH relatively?
Command find_package has two modes: Module mode and Config mode. You are trying to
use Module mode when you actually need Config mode.
Module mode
Find<package>.cmake file located within your project. Something like this:
CMakeLists.txt
cmake/FindFoo.cmake
cmake/FindBoo.cmake
CMakeLists.txt content:
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(Foo REQUIRED) # FOO_INCLUDE_DIR, FOO_LIBRARIES
find_package(Boo REQUIRED) # BOO_INCLUDE_DIR, BOO_LIBRARIES
include_directories("${FOO_INCLUDE_DIR}")
include_directories("${BOO_INCLUDE_DIR}")
add_executable(Bar Bar.hpp Bar.cpp)
target_link_libraries(Bar ${FOO_LIBRARIES} ${BOO_LIBRARIES})
Note that CMAKE_MODULE_PATH has high priority and may be usefull when you need to rewrite standard Find<package>.cmake file.
Config mode (install)
<package>Config.cmake file located outside and produced by install
command of other project (Foo for example).
foo library:
> cat CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(Foo)
add_library(foo Foo.hpp Foo.cpp)
install(FILES Foo.hpp DESTINATION include)
install(TARGETS foo DESTINATION lib)
install(FILES FooConfig.cmake DESTINATION lib/cmake/Foo)
Simplified version of config file:
> cat FooConfig.cmake
add_library(foo STATIC IMPORTED)
find_library(FOO_LIBRARY_PATH foo HINTS "${CMAKE_CURRENT_LIST_DIR}/../../")
set_target_properties(foo PROPERTIES IMPORTED_LOCATION "${FOO_LIBRARY_PATH}")
By default project installed in CMAKE_INSTALL_PREFIX directory:
> cmake -H. -B_builds
> cmake --build _builds --target install
-- Install configuration: ""
-- Installing: /usr/local/include/Foo.hpp
-- Installing: /usr/local/lib/libfoo.a
-- Installing: /usr/local/lib/cmake/Foo/FooConfig.cmake
Config mode (use)
Use find_package(... CONFIG) to include FooConfig.cmake with imported target foo:
> cat CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(Boo)
# import library target `foo`
find_package(Foo CONFIG REQUIRED)
add_executable(boo Boo.cpp Boo.hpp)
target_link_libraries(boo foo)
> cmake -H. -B_builds -DCMAKE_VERBOSE_MAKEFILE=ON
> cmake --build _builds
Linking CXX executable Boo
/usr/bin/c++ ... -o Boo /usr/local/lib/libfoo.a
Note that imported target is highly configurable. See my answer.
Update
Example
If you are running cmake to generate SomeLib yourself (say as part of a superbuild), consider using the User Package Registry. This requires no hard-coded paths and is cross-platform. On Windows (including mingw64) it works via the registry. If you examine how the list of installation prefixes is constructed by the CONFIG mode of the find_packages() command, you'll see that the User Package Registry is one of elements.
Brief how-to
Associate the targets of SomeLib that you need outside of that external project by adding them to an export set in the CMakeLists.txt files where they are created:
add_library(thingInSomeLib ...)
install(TARGETS thingInSomeLib Export SomeLib-export DESTINATION lib)
Create a XXXConfig.cmake file for SomeLib in its ${CMAKE_CURRENT_BUILD_DIR} and store this location in the User Package Registry by adding two calls to export() to the CMakeLists.txt associated with SomeLib:
export(EXPORT SomeLib-export NAMESPACE SomeLib:: FILE SomeLibConfig.cmake) # Create SomeLibConfig.cmake
export(PACKAGE SomeLib) # Store location of SomeLibConfig.cmake
Issue your find_package(SomeLib REQUIRED) commmand in the CMakeLists.txt file of the project that depends on SomeLib without the "non-cross-platform hard coded paths" tinkering with the CMAKE_MODULE_PATH.
When it might be the right approach
This approach is probably best suited for situations where you'll never use your software downstream of the build directory (e.g., you're cross-compiling and never install anything on your machine, or you're building the software just to run tests in the build directory), since it creates a link to a .cmake file in your "build" output, which may be temporary.
But if you're never actually installing SomeLib in your workflow, calling EXPORT(PACKAGE <name>) allows you to avoid the hard-coded path. And, of course, if you are installing SomeLib, you probably know your platform, CMAKE_MODULE_PATH, etc, so #user2288008's excellent answer will have you covered.
How is this usually done? Should I copy the cmake/ directory of SomeLib into my project and set the CMAKE_MODULE_PATH relatively?
If you don't trust CMake to have that module, then - yes, do that - sort of: Copy the find_SomeLib.cmake and its dependencies into your cmake/ directory. That's what I do as a fallback. It's an ugly solution though.
Note that the FindFoo.cmake modules are each a sort of a bridge between platform-dependence and platform-independence - they look in various platform-specific places to obtain paths in variables whose names is platform-independent.
You don't need to specify the module path per se. CMake ships with its own set of built-in find_package scripts, and their location is in the default CMAKE_MODULE_PATH.
The more normal use case for dependent projects that have been CMakeified would be to use CMake's external_project command and then include the Use[Project].cmake file from the subproject. If you just need the Find[Project].cmake script, copy it out of the subproject and into your own project's source code, and then you won't need to augment the CMAKE_MODULE_PATH in order to find the subproject at the system level.