CMake: how to install only shared libraries found via find_library? - cmake

I've got several third-party libs, some them shared, some static, I need to install the shared ones.
Currently I'm doing find_library's, have a list of all the needed libs and pass it to install(FILES ...).
But this way both .a and .so libs are installed.
With install(TARGETS ...) there is a separation on RUNTIME, ARCHIVE etc.
But I do not want to create a dummy target for each of the libs.
I also do not want to separate libs into shared and static (there is another separation already).
Is there a nicer way for me to filter for shared libs only than just to regex the filename? Maybe libraries are treated as something 'more' than just filepaths after find_library so I somehow can get library type from them?

Let's see what is going on.
You install some libraries via you package manager (yum/dnf/apt-get/sth else).
You build your app.
You distribute your app.
If so, you should not ask cmake to install those libs, because if someone else would like to install same thirdparty libs via another rpm package, it would create conflict (and one package would have to be removed) - it's mess.
The place which manage library dependecies is package manager - create rpm package which would have:
Requires: all_your_dynamic_libs
BuildRequires: all_your_static_libs
If point 1 is rather - You install some libraries by make && make install then you should firstly create rpm package for that.
It is kind of a pain to create all this additional work, but trust me, you DO NOT want to create all-in-one package.

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.

conan.io package management - source only package

this is not a request about header-only packages. Those are straightforward
I've got a cross-platform library which I'd like to not package with any .a (or similar) prebuilt binaries but rather indicate its .cpp must be built along with the consuming application (add_subdirectory style).
The only way I see to do this are:
conan install -build style
Set up conan build profiles
Both of those two are reasonable, yet seem to be more effort than needed for a consumer who "just wants to recompile the C++" with whatever toolchain their top level CMake is pointed towards.
So, in other words, can conan serve similarly as a delivery mechanism for a git retrieve/add_subdirectory and present it as a CONAN_PKG?

How to get package name for use in CMake?

I have a couple of packages that I require as dependencies for a CMake build of my code. I got them through apt-get and they work.
The specific packages are;
mingw-w64
mingw-w64-i686-dev
I do realize that some people may want to build these dependencies from source, and I'm afraid the name for the apt-get package might not be the same name for the installed source package. I was just wondering, what package name do I put in my CMakeLists.txt file in the find_package(XXX REQUIRED) directive for either or both of these scenarios?
find_package(<package>) has two modes, Module and Config.
In Config mode (which will be used first) looks for a file called <package>-config.cmake / <package>Config.cmake in CMAKE_PREFIX_PATH.
In Module mode, CMake looks for a file calle Find<package>.cmake.
So, if your package is called mingw-w64, than there has to be one of the files mentioned above, e.g. mingw-w64Config.cmake.
The special architecture package mingw-w64-i686-dev is provided for 32bit machines. You will install that package on a 32bit machine and the package would be still called mingw-w64.
PS: Keep in mind that not all packages provide cmake files. If that's the case, you have to write your own Findmingw-w64.cmake file. Have a look here for inspiration.

How can I add external libraries to a cmake install?

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.

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.