Install shared libraries only if required with cmake - cmake

I've got a cmake project that builds multiple executables and shared
libraries. There are option() settings that determine which of these
executables are built. What I'd like to be able to do is prevent the
building and installation of shared libraries that are not required. So
what I did is as follows:
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY TRUE) on the top level
CMakeLists file
add_library(${SHARED_LIBRARY} SHARED EXCLUDE_FROM_ALL ...)
install(TARGETS ${SHARED_LIBRARY} DESTINATION ${DESTINATION} OPTIONAL)
These three settings result in the desired behaviour. However, cmake
generates the following warning:
WARNING: Target "xxxx" has EXCLUDE_FROM_ALL set and will not be built by
default but an install rule has been provided for it. CMake does not
define behavior for this case.
Is this warning still valid in this specific scenario?
If so is there another/more correct way to achieve what I want?
If what I'm doing is acceptable, is there a way to suppress this
warning?
Thank you

Related

Explicitly tell to CMake target_link_libraries() to read dependency as target and not as a file from system path?

I got multiple warnings:
CMake Warning at libsysev/src/CMakeLists.txt:17 (add_library):
Cannot generate a safe runtime search path for target sysev because files
in some directories may conflict with libraries in implicit directories:
runtime library [libzlog.so] in /usr/lib/arm-linux-gnueabihf may be hidden by files in:
/home/user/project/BUILD/libzlog/src
Some of these libraries may not be found correctly.
Target defined as following:
add_library(sysev SHARED main.cpp)
target_link_libraries(sysev PUBLIC zlog)
zlog provided in source code and also installed to the system.
Root CMakeLists.txt looks like:
project(xxx)
add_subdirectory(zlog)
add_subdirectory(sysev)
I want to disambiguate zlog. CMake must know this is a target and not a file from system path.

Mark CMake package as required by some targets, not others

I have a CMake file that contains a target that depend on a package, namely Java, as well a target that dose not require Java.
I would like to be able to build the not-requiring-java target w/o requiring java.
add_executable(nojava_targ "")
add_executable(java_targ "")
The trouble is once CMake sees the requiring-java-project on a machine w/o Java, CMake errors out, refusing to build the Makefile. This prevents the not-requiring-java target from building even though it doesn't need java.
find_package(JNI COMPONENTS Development)
target_include_directories( java_targ PRIVATE
${JTARG_INCLUDES}
${JNI_INCLUDE_DIRS})
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
/mnt/src/prjx/JAVA_INCLUDE_PATH
used as include directory in directory /mnt/src/prjx/
Is there some way to signal to CMake that this package is required, but only by some targets?
find_package will set JNI_FOUND to true if it finds the package, so add any Java-target
commands only if JNI_Found is true. Something like this:
find_package(JNI COMPONENTS Development)
if(JNI_FOUND)
add_executable(java_targ java_targ.cpp)
target_include_directories(java_targ PRIVATE
${JTARG_INCLUDES}
${JNI_INCLUDE_DIRS})
endif()

Why use add_library({tgt} IMPORTED) versus target_link_libraries( -l {.so | .a})?

What is the purpose of using the statement:
add_library(<tgt> [SHARED|STATIC] IMPORTED)
From what I have found even if you create an imported library target above you still would need to specify the specific location of the actual .so or .a. This would take at least 3 cmake commands to link to an executable and the compiler still would not automatically search through the common include directories on your OS.
Example:
cmake code to link IMPORTED lib
From the CMake documentation I understand there are really 3 ways to link a library that is not built as a target in a subproject of your overall application/library.
CMake target_link_libraries() documentation
Using a CMake package for one of the shipped package scripts.
Using a linker flag:
target_link_libraries(<tgt> [SHARED|STATIC|...] -lncursesw)
Or using the IMPORTED library method (showcased in code at top).
A major difference when using the second method is that it only takes a single line of code and will search through all of your compiler's predefined include directories on you OS. Could anyone help me understand why the add_library() method is used?
Additional Realated SO Posts:
Include directories for IMPORTED libs
CMake imported library behavior
You should use add_library(<tgt> [SHARED|STATIC] IMPORTED) whenever you need to set properties such as dependencies, compile definitions, compile flags etc for <tgt>, and/or by extension, any targets that are linking against <tgt>.
Let's say you have two static libraries; libfoobar.a and libraboof.a, where libfoobar.a requires libraboof.a. Let's also say that these libraries contain some features that are enabled by -DSOME_FEATURE.
add_library(raboof STATIC IMPORTED)
set_target_properties(raboof PROPERTIES
IMPORTED_LOCATION <path-to-libraboof.a>
INTERFACE_COMPILE_DEFINITIONS "SOME_FEATURE"
)
add_library(foobar STATIC IMPORTED)
set_target_properties(foobar PROPERTIES
IMPORTED_LOCATION <path-to-libfoobar.a>
INTERFACE_LINK_LIBRARIES raboof
)
So when you link against libfoobar.a:
add_executable(my_app main.cpp)
target_link_libraries(my_app foobar)
CMake will make sure to link all dependencies in the correct order and will in this case also append -DSOME_FEATURE to the compile flags when you build my_app. Note that since we added libraboof.a as a dependency to libfoobar.a, -DSOME_FEATURE is added to any target that link against libfoobar.a through the transitive property.
If you don't use add_library(<tgt> <SHARED|STATIC> IMPORTED) in a scenario like this, you would have to manage any dependencies and required build options yourself for each target, which is quite error-prone.
This method is also often used in Config-modules for multi-component libraries to manage dependencies between the components.

CMAKE RPATH not working - could not find shared object file

I am trying to get rid of setting LD_LIBRARY_PATH everytime time I run my program. After adding in the library and targeting my executable to the library, when I run it tells me it can not open shared object library, no such file or directory.
In my CMakeLists.txt I have:
add_library(heart SHARED ${HEART_FILES})
add_executable(run ${RUN_FILES})
target_link_libraries(run heart)
set(CMAKE_SKIP_BUILD_PATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "~/person/target/usr/local/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
I set an absolute link to my library folder to test out whether this would create an rpath to my library and it seems like there isn't. I have checked and made sure that the shared library is indeed in lib. libheart.so is the file that is being linked. What else am I missing?
It is because you build heart and run from the same cmake project:
CMAKE_INSTALL_RPATH_USE_LINK_PATH is an interesting and very useful option. When building a target with RPATH, CMake determines the RPATH by using the directories of all libraries to which this target links. Some of these libraries may be located in the same build tree, e.g. libbar.so, these directories are also added to the RPATH.
If this option is enabled, all these directories except those which are also in the build tree will be added to the install RPATH automatically. The only directories which may then still be missing from the RPATH are the directories where the libraries from the same project (i.e. libbar.so) are installed to. If the install directory for the libraries is not one of the systems default library directories, you have to add this directory yourself to the install RPATH by setting CMAKE_INSTALL_RPATH accordingly
You can try this:
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
More documentation here cmake rpath handling
EDIT:
Only this should work:
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
add_library(heart SHARED ${HEART_FILES})
add_executable(run ${RUN_FILES})
target_link_libraries(run heart)
install(
TARGETS heart run
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
)
Clean your build directory and then:
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/home/person/target/usr/local ..
make install
At the end of the g++ line Linking CXX executable run you should see like -Wl,-rpath,/home/person/target/usr/local/lib
If you want a fully relocatable package:
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
PS: are you sur that it is libheart.so that is not found ?
In your CMake file, set the RPATH before defining the targets. The CMAKE_INSTALL_RPATH must be defined before calling add_executable(), otherwise it has no effect.
I had a similar issue as the original post. I created executables which linked to external shared libraries. This approach compiled and executed fine from the build directory. However, the executable that was installed to a separate directory could not find a shared library at runtime:
error while loading shared libraries: libxxxx.so.1: cannot open shared object file: No such file or directory
To solve, I
1) upgraded to CMake 3.17
2) used Craig Scott's recommended:
set(CMAKE_INSTALL_RPATH $ORIGIN)
as explained in his talk
3) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) as directly mentioned to solve this error in the second common question in Kitware's documention
4) Put all this before adding the targets as mentioned in this post
5) Used the "$ORIGIN/../lib" syntax instead of Craig's Scott's mentioned $ORIGIN as mentioned by #explo91
In summary, and to my suprise, only the "$ORIGIN/../lib" before the target definition was necessary from above (I tested the other combinations which did not fix the cannot open shared object file runtime issue).
Anyway the solution I finally applied, which may be of better, fine-grained CMake style or at least may be helpful to others on their RPATH journey is:
set_target_properties(target_defined_above PROPERTIES INSTALL_RPATH "$ORIGIN/../lib")

How to build and install cmake targets only if the other targets depend on them?

My application consists of the core, many shared libraries and many plugins that use these shared libraries. I'm using cmake option() command to enable / disable each plugin.
What I'd like to do is to build and install the shared library only if it's required by one of the enabled plugins.
I tried using the following in the directories of the shared libraries:
set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL true)
However, the targets are still being built in Visual Studio. GNU make in Linux correctly avoids building them. However, the required libraries are no longer installed using install() in either
system.
I also tried adding EXCLUDE_FROM_DEFAULT_BUILD false to the library targets,
but cmake complained about the undefined behavior of install() with disabled targets.
It sounds like you have something like this:
OPTION( BUILD_OPTIONAL_PLUGINS "Build plugins that are not required for core install" FALSE)
IF(${BUILD_OPTIONAL_PLUGINS})
ADD_SUBDIRECTORY( Plugins/Optional) # Whatever it really is.
ENDIF()
So far so good. However, you now can have a library like "libCommon" that nobody needs if all those option flags are false.
So, your simplest solution is this: Treat the library as optional TOO!
After all, you don't need to even build it if none of the consuming projects themselves are going to be built. So have an ordinary 'SET' variable like 'BUILD_COMMON' in that same top-level CMakeLists.txt, defaulted to TRUE. Then just do this:
SET( BUILD_COMMON 0)
IF(${BUILD_OPTIONAL_1} OR ...) # Detect if you need to build the lib
SET( BUILD_COMMON 1)
ENDIF()
IF(${BUILD_COMMON})
ADD_SUBDIRECTORY( common)
ENDIF()
Then when you eventually do 'make install' on this build, everything works as expected. If turned off all the optional plugins, the library that was only being used by them never got built either. Since it was never in the build, you don't need to try and make its install logic aware of whether it is needed or not.
You should also add a line like this to the INSTALL() command for the library as well as every optional plugin:
INSTALL( TARGETS <plugin> .... OPTIONAL)
That tells 'make install' to not try and build or install the target if it doesn't see its binaries.