Have cmake append a directory to the build RPATH - cmake

I'd like to add another directory to a target's BUILD_RPATH property, but I'd like it at the end of the list, so it's searched last, after the other directories that cmake automatically adds to target's BUILD_RPATH. But there doesn't seem to be way to add to the property after the automatic RPATH directories.
At build time, my system libraries are not in the normal locations, but in a staging area. In order to run uninstalled built binaries, I need to add this staging area to the binaries' RPATHs. And this part is straightforward to do and works fine, like this:
add_executable(mybinary ${BINARY_SOURCES})
set_property(TARGET mybinary APPEND PROPERTY BUILD_RPATH ${STAGING_LIB_DIR})
But mybinary also uses a library that it built as part of the same project:
add_library(mylib SHARED ${LIB_SOURCES})
target_link_libraries(mybinary PRIVATE mylib)
When mybinary is run, I'd like it to use the mylib that was just built and is in ${CMAKE_CURRENT_BINARY_DIR}, not another copy somewhere else, perhaps in the system library directory from the last time make install was run to install the project. Or, in my case, a copy of the library in ${STAGING_LIB_DIR}.
cmake will automatically add ${CMAKE_CURRENT_BINARY_DIR}, or whatever is appropriate, for any libraries not from the system to the build RPATH of produced binaries. So when one runs mybinary from the build directory it will search for the mylib in the build directory.
But the problem is it appends these automatic library directories to whatever I have set BUILD_RPATH to. So one gets a final RPATH of ${STAGING_LIB_DIR}:${CMAKE_CURRENT_BINARY_DIR} and the wrong copy of mylib is used.

You could set the SKIP_BUILD_RPATH target property:
SKIP_BUILD_RPATH is a boolean specifying whether to skip automatic generation of an rpath allowing the target to run from the build tree. This property is initialized by the value of the variable CMAKE_SKIP_BUILD_RPATH if it is set when a target is created.
And then manually set the RPATH in whatever way/order you would like without worrying about CMake doing additional things to it.

Related

How to correctly link static library build and installed previously

There is a static library called revolta which is being built and then installed into a sysroot:
set( CMAKE_INSTALL_PREFIX <path to sysroot> )
# ReVolta c++ library name
set( TARGET_LIBREVOLTA "revolta" )
add_library( ${TARGET_LIBREVOLTA} STATIC )
target_include_directories( ${TARGET_LIBREVOLTA}
PUBLIC
# Once the librevolta targets are being exported, this include directory in which the lib is installed is used
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
)
target_sources( ${TARGET_LIBREVOLTA}
PUBLIC
...
)
Later then once the librevolta is built, it is installed into the sys root using:
# Install all the revolta headers into include directory and copy the built library
install( TARGETS ${TARGET_LIBREVOLTA} EXPORT ${TARGET_LIBREVOLTA}
FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
and the connected custom command:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
So far so good. This works as intended, once CMake builds the "revolta" target, it is built and installed into the sysroot as installed using the ${CMAKE_INSTALL_PREFIX}.
My problem is once I try to add the target as the linked one in other lib/executable, it includes somehow automatically the librevolta source path into includes and links the library using the relative path in the build directory rather than the one installed into sysroot as performed in the step right after the librevolta build.
Some other lib/executable:
target_link_libraries( ${APP_EXECUTABLE}
PRIVATE
revolta
)
Once being built, the include path -I/home/martin/git/revolta/source/librevolta is added (the source location) even though it is stated as PRIVATE in the snipped above:
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
and only the ${CMAKE_INSTALL_PREFIX}/include is made public...
Additionally, the library is taken from the build tree rather than from the location where it is installed:
../../librevolta/librevolta.a
instead of
/home/martin/git/revolta/sysroot/lib/librevolta.a
Could you please advice me how to correctly set the revolta target the way it correctly uses its sources for building itself but once used elsewhere it provides the sysroot installed headers and built library from the same location (respecting the standard locations)?
HINT: I also tried to remove the revolta target from the app completely, specifying only to use the sys root (gcc option --sysroot=/home/martin/git/revolta/sysroot), it works fine correct headers and lib is used BUT once the librevolta is not built and installed, the target is not run prior to app build as the dependency is not defined then...
TL;DR: You need to do what's done here:
How to create a ProjectConfig.cmake file
I see a few issues with these CMakeLists.txt files but they aren't related to your problem, because if I understand correctly what you are trying to do here, then there is no problem and it is used as intended.
Let me clarify:
You have a library project that has it's own CMakeLists.txt, where you define the target revolta
You have an executable project that has it's own CMakeLists.txt, where you define your executable target and then you add the revolta target via add_subdirectory() and target_link_libraries(my_executable revolta)
If that's the case then this is just bad:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
Forcing your build to automatically install this library is not the way to go, ever (you for example, need elevated privileges to build it in the first place, because of this command and that poses a security risk).
That being said what is happening is perfectly fine, because from the perspective of the my_executable's CMakeLists.txt you are still building i.e. you use the BUILD_INTERFACE. It is however something you do not want to do.
What instead you want to do is:
Create generator files for a revoltaConfig.cmake file. For that I will refer you to this tutorial:
How to create a ProjectConfig.cmake file
After you create such file, i.e. after building and installing revolta. You will (in the process) also create a revoltaConfig.cmake file. Which helps you populate the my_executable project via find_package(revolta).
The above is probably what you are interested in.
The generator expressions that you use to distinguish BUILD_INTERFACE and INSTALL_INTERFACE are mainly for header file locations (or other linked libraries). Because when you build the library the header files can have a different structure then when you install it (as you already know). And as such work perfectly fine in your CMakeLists.txt, because when you think about it:
You don't want to copy changes to your library files (into the install directory) just to test ongoing development (features/bugfixes) in your executable.
And during the build of the executable if your building another target then IT IS NOT INSTALLED but rather BEING BUILT. And you are most likely adding it as a built target.
So to sum up what would most likely happen here (using your old CMakeLists.txt) is that
The moment you start building the executable which adds the target library as a dependency via add_subdirectory you are implicitly using BUILD_INTERFACE because you are building.
If you were to then install both the executable and the library it would again use the correct install paths, i.e. you would then implicitly start using INSTALL_INTERFACE.
You could hack it without the projectConfig file using the same generator expressions by mixing them up, but I don't recommend it, because then the CMakeLists.txt wouldn't work without doing some weird steps beforehand.

How to understand the differences between CMAKE_BUILD_RPATH and CMAKE_INSTALL_RPATH in the right way?

As per the document, which says:
CMAKE_BUILD_RPATH¶ New in version 3.8.
Semicolon-separated list specifying runtime path (RPATH) entries to
add to binaries linked in the build tree (for platforms that support
it). The entries will not be used for binaries in the install tree.
See also the CMAKE_INSTALL_RPATH variable.
This is used to initialize the BUILD_RPATH target property for all
targets.
As per the document, which says:
CMAKE_INSTALL_RPATH¶ The rpath to use for installed targets.
A semicolon-separated list specifying the rpath to use in installed
targets (for platforms that support it). This is used to initialize
the target property INSTALL_RPATH for all targets.
How to understand the differences between CMAKE_BUILD_RPATH and CMAKE_INSTALL_RPATH in the right way?
A small and clear example is welcome.
When building binaries one can set the RPATH to support library path resolution at runtime.
There are two scenarios for which to build binaries. The first an obvious scenario is to debug a program. This means the binary will be built and (normally) executed from the location it has been built. Details can vary but in general it is under the cmake build directory.
This means if you build for example two libraries in your project libA and libB. libA dynamically linking libB. This means both libraries are located somewhere in the binary path. To run a binary in the build path with runtime dependency resolution you CAN specify the CMAKE_BUILD_PATH or the recommended target property BUILD_RPATH with an absolute or relative value.
lib
location
rpath
libA
/home/user/my_tool/build/liba
./../libb/ or /home/user/my_tool/build/libb
libB
/home/user/my_tool/build/libb
Then you can smoothly run your binary from the build path and everything should work without modifying the LD_LIBRARY_PATH system environment variable for dependency lookup.
The same applies to the RPATH if the binary gets installed (cmake install). In this case the value of the RPATH could be different. To accommodate this there are these two CMake features to distinguish between the scenarios.
lib
location
rpath
libA
/usr/lib/my_tool
. or /usr/bin/my_tool
libB
/usr/lib/my_tool

Cmake library outdir

I cant output my lib to my lib folder , I tried this , and it outputs to lib/debug
but I need output mylib.lib exactly to ${CMAKE_CURRENT_SOURCE_DIR}/lib
add_library(mylib STATIC ${SOURCES} ${HEADERS})
set_target_properties(mylib PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib
)
In CMake, Visual Studio is a multiconfiguration generator. Such generators have a specific when interpret ARCHIVE_OUTPUT_DIRECTORY property for the target:
Multi-configuration generators (Visual Studio, Xcode, Ninja Multi-Config) append a per-configuration subdirectory to the specified directory unless a generator expression is used.
If you want to use ${CMAKE_CURRENT_BINARY_DIR}/lib as output directory for the library only for Debug build, and Release build is not interesting for you, then set ARCHIVE_OUTPUT_DIRECTORY_DEBUG property instead:
set_target_properties(mylib PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}/lib
)
If you want to specify different output directories for Debug and Release builds, then set both properties:
set_target_properties(mylib PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}/lib
ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}/lib-release
)
Setting the same directory for both Debug and Release builds is discouraged: That way Release build will overwrite library created in Debug and vise versa.
Placing build artifacts (e.g. libraries and executables created by the project) in the source directory, as implied by ${CMAKE_CURRENT_SOURCE_DIR}/lib output directory, is discouraged too.
Usually, build artifacts are placed in the build directory and its descendant directories. So, deleting build directory completely removes the build.
If you want to store some build artifacts for use even when build directory is remove, then install those artifacts. CMake provides the functionality for installing libraries, executables and other files.
The documentation of ARCHIVE_OUTPUT_DIRECTORY presents you with a possible solution (emphasis mine):
Multi-configuration generators (Visual Studio, Xcode, Ninja Multi-Config) append a per-configuration subdirectory to the specified directory unless a generator expression is used.
You can use a generator expression not changing anything about the value to prevent the config based suffix. You should modify the archive name accordingly to allow for output files of different configurations to coexist in the same directory.
set_target_properties(mylib PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib$<0:>"
OUTPUT_NAME "mylib-$<CONFIG>"
)
Try this code:
set_target_properties(mylib PROPERTIES CMAKE_ARCHIVE_OUTPUT_DIRECTORY " ${CMAKE__BINARY_DIR}/lib")

How can I change the library path of an executable after it has been built/installed?

Let's say I build myTest with cmake. myTest uses /opt/path1/lib/lib.so at compile and link time. After running it a few times I decide that I want myTest to now use /opt/path2/lib.so (same lib name, same interfaces, just different path).
This might be cause I want to temporarily test changes to lib.so without affecting others that might be using it. I also may not have the source to myTest but know that it uses lib.so.
If I used a Makefile and used regular gnu/g++ make I can make this happen by setting LD_LIBRARY_PATH in the local folder. CMake ignores LD_LIB_PATH - so how do I make this happen?
For find a library at runtime, ldd uses (among other things) RPATH directories, embedded into the executable.
By default, when build the executable/library, CMake adds to RPATH directories, where linked libraries are located.
E.g., when link with library /opt/path1/lib/lib.so, CMake adds directory /opt/path1/lib to RPATH. So ldd always finds lib.so library as /opt/path1/lib/lib.so.
For tell CMake to not set RPATH, set CMAKE_SKIP_RPATH variable:
set(CMAKE_SKIP_RPATH TRUE)
After that, ldd will search lib.so in directory, listed in LD_LIBRARY_PATH environment variable.

cmake: how to keep path to libraries during installation

I have some executable which depends on config files which relative path are setup in the source.
The executable links against a library, which is created in the same project.
What I am hoping to achieve, is having the executable working out of the box after installation, i.e. the installation would copy the executable, config files and library in a suitable location, and the executable would be linked to the library.
What I have for the moment:
install(TARGETS ${test_executables} ${PROJECT_NAME}
RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/plot"
DESTINATION .)
${PROJECT_NAME} is the library, plot is the folder in which the config files are.
What happens after install is that all files are in the right place in the install folder, but the executable does not find the library.
ps:
I tried to add this before :
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) # tried also with TRUE
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib)
but this did not work
It is INSTALL_RPATH target's property which affects on RPATH for installed executable. This property is set to value of variable CMAKE_INSTALL_RPATH at target creation time.
So, variable CMAKE_INSTALL_RPATH needs to be set before add_executable() call for make effect on the target.
Most of global variables and target-unaware commands affect on the target only at target creation time.
There are exceptions, like command include_directories(), which affects on all targets created in the current directory. But preparing everything before creation of the target could be good practice.
I met the same issue, and just added
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
before add_library and add_executable command, then it worked.
And you can find more details here.