Mark CMake package as required by some targets, not others - cmake

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()

Related

CMake compile options for compile test only

I am using CMake to cross compile a C project for an embedded (heterogeneous) multi-core system. The compiler takes an mandatory argument (-t<type>, the target type). This flag has to be set to pass CMake's compiler test. I am adding this flag in a toolchain file as follows:
add_compile_options(-tMYPLATFORMTYPE)
The problem with this approach is, all project files will be compiled with this flag. Is there a way to configure compile flags for the test compilation only, without affecting the main project configuration? (Note: Within the project different files shall have different values for this flag.)
What I am looking for is something like:
set(CMAKE_TRY_COMPILE_COMPILE_OPTIONS "-tMYPLATFORMTYPE")
I could disabled the compile test, but I would prefer to keep it.
You can check the IN_TRY_COMPILE property and set the flag for try-compile configurations only:
get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
if(IS_IN_TRY_COMPILE)
add_compile_options(-tMYPLATFORMTYPE)
endif()

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.

Install shared libraries only if required with 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

Is it possible to alter CMAKE_MODULE_PATH from CMake commandline?

Edit: The accepted answer actually shows that it is pretty normally possible to set CMAKE_MODULE_PATH as any other CMake variable e.g. via the -DCMAKE_MODULE_PATH path CLI parameter. It seems that in my case there is some included CMake script that calls set(CMAKE_MODULE_PATH /library_path), which erases all previous paths set to the variable. That's why I couldn't get the variable to do what I wanted it to do. I'll leave the question here in case anybody else faces this kind of situation.
I'm building a (3rd party) project that uses the Protobuf library (but this question is general). My system has a system-wide install of a newer version of Protobuf than the project is compatible with. So I've downloaded and compiled from source an older version of Protobuf.
The project uses CMake, and in its CMakeLists.txt, there is:
find_package(Protobuf REQUIRED)
Which, however, finds the (incompatible) system install. Of course, CMake doesn't know about my custom build of Protobuf. But how do I tell it?
I've created a FindProtobuf.cmake file in, say, ~/usr/share/cmake-3.0/Modules/ and want the build process to use this one for finding Protobuf. But I haven't succeeded forcing CMake to pick up this one and not the system one. I think the reason is quite obvious from the CMake docs of find_package:
The command has two modes by which it searches for packages: “Module” mode and “Config” mode. Module mode is available when the command is invoked with the above reduced signature. CMake searches for a file called Find<package>.cmake in the CMAKE_MODULE_PATH followed by the CMake installation. If the file is found, it is read and processed by CMake. ... If no module is found and the MODULE option is not given the command proceeds to Config mode.
So until I succeed to change CMAKE_MODULE_PATH, CMake will just pick up the FindProtobuf.cmake installed to the default system path and won't ever proceed to the "Config" mode where I could probably make use of CMAKE_PREFIX_PATH.
It's important for me to not edit the CMakeLists.txt since it belongs to a 3rd party project I don't maintain.
What I've tried (all without success):
calling CMAKE_MODULE_PATH=~/usr/share/cmake-3.0/Modules cmake ... (the env. variable is not "transferred" to the CMake variable with the same name)
calling cmake -DCMAKE_MODULE_PATH=~/usr/share/cmake-3.0/Modules ... (doesn't work, probably by design?)
calling Protobuf_DIR=path/to/my/protobuf cmake ... (the project doesn't support this kind of override for Protobuf)
It seems to me that, unfortunately, the only way to alter the CMAKE_MODULE_PATH used by find_package is to alter it from within CMakeLists.txt, which is exactly what I want to avoid.
Do you have any ideas/workarounds on how not to touch the CMakeLists.txt and still convince find_package to find my custom Protobuf?
For reference, the CMake part of this project is on github .
As a direct answer to your question, yes, you can set CMAKE_MODULE_PATH at the command line by running cmake -DCMAKE_MODULE_PATH=/some/path -S /path/to/src -B /path/to/build.
But that probably doesn't do what you want it to do; see below.
The Bitbucket link you supplied is dead, but here are a few suggestions that might help.
Avoid writing your own find modules, especially when the upstream supplies CMake config modules.
You can direct CMake to your custom Protobuf installation by setting one of CMAKE_PREFIX_PATH or Protobuf_ROOT (v3.12+) to the Protobuf install root.
You can tell find_package to try CONFIG mode first by setting CMAKE_FIND_PACKAGE_PREFER_CONFIG to true (v3.15+). Then set Protobuf_DIR to the directory containing ProtobufConfig.cmake.
Failing all else, you can manually set the variables documented in CMake's own FindProtobuf module, here: https://cmake.org/cmake/help/latest/module/FindProtobuf.html
All these variables can be set at the configure command line with the -D flag.
There are very few environment variables that populate CMake variables to start and I would avoid relying on them. There is an exhaustive list here: https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html. CMAKE_MODULE_PATH is not among them.

CMake find_package dependency on subproject

I have the following directory layout:
main_folder
+ static_lib1
+ executable
Both 'static_lib1' and 'executable' have a full CMakeLists so that they can be
built independently.
The 'executable' depends on 'static_lib1'. It uses find_package() to locate 'static_lib1'.
The main_folder contains a CMakeLists that includes both 'static_lib1' and 'executable' via add_subdirectory for conveniently building the whole project in one go.
Everything works fine if I manually build 'static_lib1' and then 'executable'. But when running the CMakeLists from the main folder, I get an error because find_package is unable to find the library files from 'static_lib1' which have not yet been built.
How can I resolve this while keeping the CMakeLists files separate (i.e. without including the static_lib's CMakeLists from the executable's CMakeLists)?
In executable's CMakeLists.txt you can check if you are building stand-alone or as part of project:
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
# stand-alone build
find_package(static_lib1)
else()
include_directories(../static_lib1)
link_directories(../static_lib1)
...
target_link_libraries(executable static_lib1)
endif()
Switch from a file-based approach to a target-based approach for handling the dependency from executable to static_lib1.
The original problem occurred because executable called find_package for locating static_lib1, which then attempted to fill a variable like STATIC_LIB1_LIBRARY with the paths to the library files by calling find_library. executable then consumes the content of that variable in a target_link_libraries(executable ${STATIC_LIB1_LIBRARY}) call. The problem here is, since those library files only get generated as part of the build, that call to find_library will not be able to find anything.
Building executable needs to support two scenarios here:
Building standalone, where a pre-compiled version of static_lib1 is located somewhere on the disc.
Building from main_folder, where both executable and static_lib1 are part of the same build.
The approach from the question supports scenario 1, but not scenario 2.
Instead of using using a variable to communicate a dependency between the two builds, use a target. The CMakeLists.txt for static_lib1 likely creates a library target like add_library(static_lib1 [...]). In executable we now simply do target_link_libraries(executable PUBLIC static_lib1). This is sufficient to support scenario 2.
To also allow for scenario 1 at the same time, we look at the call to find_package(static_lib1) in the CMakeLists.txt for executable. Instead of providing a variable like before, this call now needs to provide a target static_lib1 for consumption.
So we adapt the find script for static_lib1 to the following behavior:
If a target static_lib1 already exists, there's nothing to be done and the find script can just return (this is scenario 2).
Otherwise, we call find_library to locate the library file on disc (as before in the original approach) and then create a new imported target: add_library(static_lib1 STATIC IMPORTED). We then configure all relevant properties of the static library to that target. For instance, to add the location of the library file, we could do
set_target_properties(static_lib1 PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION ${STATIC_LIB1_LIBRARY}
)
To support multi-config generators like MSVC, instead of setting IMPORTED_LOCATION and IMPORTED_LINK_INTERFACE_LANGUAGES, you will want to set the configuration specific properties like IMPORTED_LOCATION_DEBUG and IMPORTED_LOCATION_RELEASE instead. Since this can get quite tedious to do manually, you can have CMake generate this information (and a bunch of other convenient stuff) for you in a package script. The find mechanism for package scripts works slightly different under the hood, but the code in the CMakeLists.txt for executable will look just the same, a simple call to find_package(static_lib1). The main difference is that this call will then not dispatch to a hand-written find script, but to a package script that was automatically generated by CMake as part of the build process of static_lib1.
I guess I will leave this answer for posterity since only recently I have searched for a solution to this problem and found out that...
Since CMake 3.24 it is possible!
It is possible to override subsequent calls to find_package() with FetchContent_Declare() flag OVERRIDE_FIND_PACKAGE.
Your
add_subdirectory("path/to/static_lib1")
call has to be replaced in main_folder/CMakeLists.txt with:
include(FetchContent)
FetchContent_Declare(
static_lib1
SOURCE_DIR "path/to/static_lib1"
OVERRIDE_FIND_PACKAGE
)
Any calls to find_package(static_lib1) will call FetchContent_MakeAvailable() for you, virtually making it identical to add_subdirectory() call.
You can read more about OVERRIDE_FIND_PACKAGE in CMake documentation.