How can I add external libraries to a cmake install? - cmake

My cmake project builds against external libraries, e.g. Boost. I would now like to advise cmake to generate "make install code" that causes all used external libraries to be added to the installation package.
My hope is that cmake can inspect the built shared objects and executables, e.g. using ldd, to find out which external libraries are required and add them without explicit naming of the individual libraries in the CMakeLists.txt.
Of course there is the other case in which the built code expicitly loads the external libaries (dlopen(), ...), e.g. as done by Intel IPP. In this case I would probably somehow need to explicitly name the libraries to install, e.g. using some variables set by the FindXXX cmake scripts.

Related

Should I supply external libraries with a CMakeLists.txt or supply find_packages instead?

I am working on a project that needs some external libraries. Since it is meant to be cross platform, I am using cmake.
What is the preferred way when distributing such projects? Should I supply the external libraries (such as zlib) with their own CMakeLists.txt or should I signal the dependency by simply supplying find_packages()?
the former provides all things needed. while the latter let's the developer decide how to supply the dependency (vcpkg for example)
Althoug there is no universally preferred approach, I absolutely believe you should stick to find_package. Declare your dependencies like this:
find_package(Pkg [version] REQUIRED [components])
Include [version] and [components] only if you know Pkg itself provides first-party CMake package configuration files. If you are writing and distributing a library, you will include equivalent find_dependency calls in your MyProjConfig.cmake file.
If some dependency does not have a standard CMake find module or provide its own CMake package configuration file, you should write your own in ./cmake and add list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") to the root CMakeLists.txt, before any find_package call. You will install your find modules, too, and include the same addition to the module path in your config files.
Inside the find module, you can use whatever approach you want to create some imported targets for your dependencies. Using PkgConfig is a good approach here.
Going through find_package instantly works with a number of dependency providers: vcpkg, the cmake_paths Conan generator, Linux distro system packages, and so on.
The primary alternative to doing this is to vendor the code, meaning including your dependencies in your build directly, whether through copy/paste into your source tree, a git submodule, or by build-time download from the internet (FetchContent).
The mechanism used to build these is nearly always add_subdirectory in the end, which pulls your dependencies' CMake builds into yours.
Perhaps the biggest issue with this is that most projects' CMake code is totally unprepared to be used in this way. It might trample your cache variables, inject invalid flags into your targets, overwrite your generated headers, and so on. Integration is a nightmare.
Also, from a software distribution standpoint, doing this ties your code to particular versions of your dependencies and takes control away from others who might want to package your code. For instance, Debian packages are not allowed to bundle their dependencies... if libA depends on libB, then each gets its own package. With find_package, it is trivial for a maintainer to inject the appropriate dependencies into your build. Without, it typically involves a difficult-to-maintain patch.

How to only build libraries that are actual dependencies in CMake

I have a library that is brought in as an external to several applications and included using add_subdirectory. It has four build flavours due to the flexibility of its use: GUI/CLI and shared/static. GUI apps tend to build the GUI flavour as then the user warnings it produces appear as pop-up dialogs. However it is also used by some command line apps which use the CLI version that produces user warnings on the terminal. Some applications use the shared version because they use other libraries that use this same library, and need shared resources such as output logfile handles. Some applications use the static version because they are small utilities that we want to distribute as standalone binaries without dependencies.
If I am building a CMake project that only uses one of these four flavours, I really only want to build that one single flavour of the library. Yet despite this CMake insists on building all four, which is a total waste of time.
How can I flag to CMake that the library should only be built if it is explicitly listed as a dependency of something else that is being built?
For each target, set the EXCLUDE_FROM_ALL property to TRUE in the library's CMakeLists.txt. It will then only be built if actually required.
set_target_properties(MyLib PROPERTIES
EXCLUDE_FROM_ALL TRUE
)

install shared imported library with necessary links

This question asks how to install a shared library with cmake which has been imported rather than being built by the current project:
Can I install shared imported library?
To repeat the issue:
add_library(libfoobar SHARED IMPORTED)
# this install command is illegal
install(TARGET libfoobar LIBRARY DESTINATION "${RPMBUILDROOT}${LIBDIR}")
This was raised as a [https://gitlab.kitware.com/cmake/cmake/issues/14311|issue] with cmake that has been closed, effectively with a resolution of will not fix. The grounds are, quite reasonably, that cmake does not know enough about an imported target to reliably install it.
One point the answer to that question misses is that install(TARGET) will automagically create links from libfoo.so to libfoo.so.major and libfoo.so.minor version on GNU/Linux and other unix-like platforms where this is required.
Is there a way to hijack cmake into treating a custom target as if it was built by the project or otherwise persuade it to create those links?
Something like:
add_library(libfoobar SHARED IMPORTED)
#? add_custom_target(X libfoobar)
install(TARGET X LIBRARY DESTINATION "${RPMBUILDROOT}${LIBDIR}")
What is a canonical way to do this?
When a library is built by CMake, it is CMake who assigns soversion numbers to it (according to project's settings).
When a library isn't built by CMake, CMake doesn't know soversion, so it cannot create symlinks for you.
If you bother about that CMake actually installs symlink instead of file, resolve symlinks before installing, like in that question.
Well, you may ask CMake to guess soversion of the library (e.g. by resolving symlinks and checking their names). But why you ever need symlinks?
The main purpose of soversion symlink is to resolve compatibility issues with the future library's updates. But updates are possible only when the library is installed by the project who creates it.
If your project installs library produced by other project, it is unlikely that you want to support updates for the local library's installation. So there is no needs for you to support soversions.

Using project() for dependent CMake subdirectories

I have several projects consisting of a few libraries, each living in its own subdirectory, knitted together by the topmost CMakeLists.txt file. I am in the habit of using project(<DIRNAME>) at the top of each CMakeLists.txt file and I try to structure the subprojects in such a way that they could be compiled separately from the top project. However, while this might make sense for standalone, core libraries, it cannot work for the libraries that depend on them because I need to do stuff like
target_link_libraries(gui core)
And core will nor be defined if I am trying to compile gui as a standalone project.
Is it wrong to use project() in this context, or am I missing something?
A Matter of Taste
This is in my opinion mainly a matter of taste. I don't think multiple project() commands itself are a problem, its more that projects I have seen using this approach tend to repeat itself in other parts and sometimes are running into problems with global cached variables.
Depending Libraries
The more relevant fact is, that the depending libraries will also add an include dependencies.
For standalone static library targets - not executable or shared library targets who really link the library - the simple target_link_libraries() command could be ignored with something like:
if (TARGET core)
target_link_libraries(gui core)
endif()
But the header files include dependency remains.
Standalone Projects in CMake
For me a (sub-)project to be really standalone needs not only the project() command, but it should also have a export(TARGETS ...) command. Then you could e.g. use find_package() commands to resolve any open dependencies with something like:
if (NOT TARGET core)
find_package(core REQUIRED)
endif()
target_link_libraries(gui core)
References
Making cmake library accessible by other cmake packages automatically
CMake share library with multiple executables

CMake deploying required libraries

Is it possible to get CMake to gather up all the package libraries used during compilation, recursively grab their required libraries, and then put them all in a single directory?
As an example if my application requires gtk it would grab glib and its required libraries libiconv, gettext, and libffi.
You can use the GetPrerequesites CMake module.
See here for more information