How find_library does handle so version numbers? - cmake

How does CMake's find_library handle so version numbers like in "libFOO.so.3.2"? Some libraries have symbolic links from libFOO.so to the right version, some do not.
Does CMake find a library without the symbolic link when I just use find_library(NAMES FOO)?
What should I do to help CMake figuring out the right library?

Assuming a linux distribution, the system package manager will generally provide runtime packages and development packages. If you have installed the development package for a library (e.g. libFOO-dev), it will generally include the following three files in your /usr/lib or /usr/local/lib
libFOO.so.3.2.0 (the versioned binary)
libFOO.so.3 (soname = symlink to versioned binary)
libFOO.so (namelink = symlink to soname)
The namelink has no version details in the file name. This will be used to find the library by linker command line option, e.g. -lFOO
You should use the namelink (e.g. FOO) in the find_library cmake command.
The linker will embed the soname file into your binary.
If you have multiple versions of the library installed, be sure to symlink the namelink file to the soname file that you want to use. This is really only an issue if you have multiple major versions of the same library installed. Within a major version, APIs should be backwards compatible.

In addition to #marksisson's answer:
In case you download an SDK as a zip archive with such symlinked .so files it may happen that the symlinks are broken. Instead of real symlinks you have small text files containing the names of the linked files. As a consequence the linker complains about an unrecognizable file format.
To fix that, just recreate the symlinks after unpacking the zip file.

Related

How can I manage the installation directory with CMake when cross-compiling?

I am cross-compiling a C library using CMake and a toolchain file. My toolchain file sets CMAKE_SYSROOT to the appropriate value so compilation works with no issues. However, when installing, the library does not install to the directory pointed to by CMAKE_SYSROOT. I can achieve that effect by running make install DESTDIR=xxx though.
I understand that there are two separate concepts here:
The cross-compilation toolchain, which consists of binaries that can be run on my local architecture
The CMAKE_SYSROOT which is the root directory of a target-architecture filesystems, containing header files and libraries, passed to e.g. gcc through the --sysroot flag.
I have two questions:
Is it a good idea to conflate the sysroot where my cross-compilation toolchain lives, with the sysroot where all my cross-compiled libraries will be installed? It feels to me like it should be the same, but am not sure, and to CMake it appears they are distinct concepts. Update: answered in the comments below, these are indeed distinct concepts.
What is the modern CMake way to specify the installation directory when cross-compiling like described above? Update: I believe this should be the same as CMAKE_SYSROOT, and I feel CMake should offer a way to only define this once somewhere.
Thanks!
There is no interference between sysroot and install directory (prefix).
Sysroot is given by CMAKE_SYSROOT variable and denotes prefix for tools used during build process.
Install directory(prefix) is given by CMAKE_INSTALL_PREFIX variable and denotes the path, where the project will be used upon installation. E.g. with install prefix /usr/local the project's executable foo expects to be run as /usr/local/bin/foo.
Note, that with default installation procedure, CMake installs files to the host machine. For install files onto the target machine, this procedure is needed to be adjusted. Parameter DESTDIR=xxx for make install is a way for install files directly to the target machine. Another way is to create a package (e.g. with CPack) on host, and install that package on target machine.
Note, that in the above paragraph it is irrelevant, whether cross-compilation took a place or not: it is possible to build the project on one machine and install it to the other, but similar one, without any cross-compilation.

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.

CMake does not compĂ­le Qt5 generate from buildroot

I generate a tarball with Qt5 using Buildroot 2014.05.
When I uncompressed this files to compile my project using CMake I receive the following error message:
CMake Error at <project> /sysroot/usr/lib/cmake/Qt5Core/Qt5CoreConfig.cmake:27 (message):
The imported target "Qt5::Core" references the file
"<project>/host/usr/bin/qmake"
but this file does not exist. Possible reasons include:
* The file was deleted, renamed, or moved to another location.
* An install or uninstall procedure did not complete successfully.
* The installation package was faulty and contained
"/<project>/sysroot/usr/lib/cmake/Qt5Core/Qt5CoreConfigExtras.cmake"
but not all the files it references.
I'm using the cmake configs under
<project>/sysroot/usr/lib/cmake/
and my CMakeLists.txt
set(CMAKE_PREFIX_PATH <project>/sysroot/usr/lib/cmake/QtWidgets/ )
find_package(Qt5Widgets REQUIRED)
Any suggestion?
Buildroot 2014.05 does not have qmake to install
This part: I generate a tarball with Qt5 using Buildroot does make much sense. What do you mean exactly by this ?
If by this you mean that you tarball'ed the contents of output/host/ (which is the toolchain and all the libraries that you need to build applications for your target), then beware that it is not relocatable. So it has to be installed in the same location as the one it was generated in. So for now, if you want to do this, you should set the BR2_HOST_DIR configuration option to something like /opt/<something>/, and then tarball that up and uncompress it on the other machines where you want to use the toolchain.
It's in our TODO-list to make this relocatable.

cmake: install executables and create links to them

I'm using cmake and cpack to build my project and build packages. I'm creating a few executables in my project, let's call them EXE1 and EXE2.
When creating different versions of these executables, I want to name to reflect the version of the executable (let's say EXE1_1.0.0). I can change the name of the output for a target by doing set_target_properties.
However, now when doing an install, I want to do create a symlink to this versioned name of the executable, i.e. I want to have
the "versioned" executable installed in bin directory, i.e. EXE1_1.0.0
create a symlink to the "versioned" executable, i.e. create symlink EXE1, which points to EXE1_1.0.0
Can someone suggest me how to do this?
Second question is:
How to install configuration files /etc/MYPROJECT/ directory? What DESTINATION I need to use for configuration files, like I use bin for executables and lib for libraries? Is using an absolute path like /etc an acceptable practice with cmake?
I asked this question on cmake mailing list subsequently, and this is the response I received:
The validity of the answer will depend on which CMake version you use
and which set of platform you want to support.
Symlinks are not that portable
a) Creation may not be [currently] done portably but if you are
targeting Unix you can use cmake -E create_symlink to create one.
b) Depending on the CPack generator you use and CMake/CPack version
symlinks may be embedded in the package or not.
i.e. CPack pre 2.8.7 cannot create ZIP archive which contains
symlinks CPack 2.8.8 can do that now.
Then you can use an install(SCRIPT ... or install(CODE ...) to do that
at install time.
Another option if you are using RPM is to use package specific post
install script. cpack --help-variable
CPACK_RPM_POST_INSTALL_SCRIPT_FILE
this last solution will off course only work for CPack RPM.
For second question
You can use absolute destination path, they should be handled just
fine by CPack DEB and RPM, I don't know for other.
If your software should be installed on Windows this is won't work
with archive generator (ZIP, TGZ, etc...) and/or NSIS.
May be you can do something like:
if(UNIX AND NOT APPLE) set(CONFDEST "/etc/${CMAKE_PROJECT_NAME}")
else() set(CONFDEST "etc") endif()
install(FILES yourconffile DESTINATION ${CONFDEST})

Building .so module with autotools/libtool without .la and .a variants being installed

How to build and install a .so module with autotools/libtool without .la and .a libraries
being also installed into --prefix path?
Currently i am using following Makefile.am:
lib_LTLIBRARIES = libCurlDownloader.la
libCurlDownloader_la_SOURCES = Curl.cpp
libCurlDownloader_la_LDFLAGS = -module -avoid-version
It works, but in addition to libCurlDownloader.so it also installs libCurlDownloader.la and libCurlDownloader.a, what is undesirable.
Update #1
It is possible to make .a not be generated, by using either
./configure --disable-static
or
AC_ENABLE_SHARED(yes)
AC_ENABLE_STATIC(no)
in configure.ac
But it is still the question how to make .la not being installed into installation --prefix while having .so installed.
Update #2
It is possible to remove .la files from installation --prefix using
install-exec-hook: find $(DESTDIR)$(libdir) -type f -name \*.la -delete
You should not necessarily delete the .la files. The .la files contains information that is used in two situations:
Statically linking against the built library. When statically linking (i.e., .a with -static), there is no information about the dependencies of the library being linked, so libtool can use the information in the .la file to create an appropriate ld command referencing all the necessary dependencies. This is often more important on an environment like MinGW where the linker requires the same library specified multiples in a particular order to resolve recursive dependencies. This is only a concern if one intends to build a static binary.
Dynamically loading the build library on some platforms (i.e., with lt_dlopen if using libltdl). Similarly, on certain platforms, dependencies of the compile module are not encoded in the binary, so the .la file is needed so that lt_dlopen will find and load the correct dependencies. On ELF platforms (including Linux) and PE platforms (i.e., Windows), the dependencies are store in the library, so lt_dlopen will work without the .la files. The MachO format on MacOS can require .la files when building bundles.
The Debian/Ubuntu packagers have decided to exclude .la files from their packages because the second reason isn't appropriate on Linux and they would prefer you didn't build static binaries in the first place. On other platforms, which libtool is designed to support, the .la files may be needed for linking or running the program.
I stumbled upon this question because it used the term "module", which in automake/libtool speak is the term for a plugin. I have a plugin system in Finit, so I build my plugins with '-module' to avoid creating .a files. But I still get the .la files installed, which really is not even applicable in the '-module' case.
I've yet to find a documented way to skip .la files for plugins, but here is how I do it:
AM_LDFLAGS = -module -avoid-version -shared
pkglib_LTLIBRARIES = alsa-utils.la bootmisc.la
install-exec-hook:
#(cd $(DESTDIR)$(pkglibdir) && $(RM) $(pkglib_LTLIBRARIES))
To be clear, in my use-case there is nobody going to "link" against my plugin .so's, so the .la files really are of no use.