How do I pass project specific variables down to subdirectory? I wonder if there is a "official" way of doing this:
# (CMAKE_BUILD_TYPE is one of None, Debug, Release, RelWithDebInfo)
# ...
# set specific build type for 'my_library'
set( CMAKE_BUILD_TYPE_COPY "${CMAKE_BUILD_TYPE}" )
set( CMAKE_BUILD_TYPE "Release" )
add_subdirectory( "my_library" )
set( CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE_COPY} )
# continue with original build type
# ...
The library/subdirectory my_library should always be buildt with type "Release", main project and other subdirectories should be buildt with type defined by configuration. I am not able to modify the CMakeLists.txtof my_library.
Answering the revised question in your comment (how can I pass different values), so values other then CMAKE_BUILD_TYPE`:
There's no extra machinery for this. If the variable is project specific, you just set the variable:
set(MYLIB_SOME_OPTION OFF)
add_subdirectory(mylib)
If it's more general, you need to revert it:
set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}")
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(mylib)
set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}")
Or you can put it into a function and you don't need to revert the variables you changed since functions has a scope.
Related
I'm striving to understand how to create / modify a global variable (whose prefix is not <PACKAGE>_) from within a Find<package>.cmake file, so that it can be shared among different modules and reused by the global CMakeLists.txt.
Let's say I have a FindMyModule.cmake file:
set (MyModule_include_dirs "...") # <-- This is visible from CMakeLists.txt caller
set (LIBS_INCLUDE_DIRS "${LIBS_INCLUDE_DIRS} ${MyModule_include_dirs}") <-- This is not
In CMakeLists.txt
find_package(MyModule)
message("My Module include dirs: "${MyModule_include_dirs}) # <-- Prints "My Module include dirs: ..."
message("Libs include dirs: " ${LIBS_INCLUDE_DIRS}) # <-- Prints "Libs include dirs: "
Googling and so-ing around, I've tried CACHE, INTERNAL and PARENT_SCOPE but no successful result so far.
I really don't understand why would you want to change a global variable from within a findXYZ.cmake script, as you are tightly coupling other scripts to this script or visa versa.
If I recall correctly, as long as you set a variable as CACHE INTERNAL before you start processing anything it should be available at all time to the following CMake instructions. We could've fixed that easily if you'd provide a minimal reproducible example.
The other options that you have for "GLOBAL variables" is to use set_property() i.e.:
set_property(GLOBAL PROPERTY GLOBAL_LIBS_INCLUDE_DIR "${LIBS_INCLUDE_DIR} ${MyModule_include_dirs}")
And then everywhere else:
get_property(LIBS_INCLUDE_DIR GLOBAL PROPERTY GLOBAL_LIBS_INCLUDE_DIR)
To demonstrate that BOTH WORK:
CMakeLists.txt
cmake_minimum_required (VERSION 3.20)
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
project ("GlobalVariables")
set(CACHE_INTERNAL_VARIABLE "HELLO" CACHE INTERNAL "")
set_property(GLOBAL PROPERTY GLOBAL_PROPERTY_VARIABLE "HELLO ")
find_package(MODULE_VARIABLE MODULE)
get_property(GLOBAL_PROPERTY_VARIABLE GLOBAL PROPERTY GLOBAL_PROPERTY_VARIABLE)
message(STATUS ${GLOBAL_PROPERTY_VARIABLE})
message(STATUS ${CACHE_INTERNAL_VARIABLE})
findMODULE_VARIABLE.cmake
set(CACHE_INTERNAL_VARIABLE "${CACHE_INTERNAL_VARIABLE} WORLD" CACHE INTERNAL "")
get_property(TEMP GLOBAL PROPERTY GLOBAL_PROPERTY_VARIABLE)
set_property(GLOBAL PROPERTY GLOBAL_PROPERTY_VARIABLE "${TEMP} WORLD")
Both variables output the same:
-- HELLO WORLD
-- HELLO WORLD
I'm not familar with cmake but in CMakeLists.txt we set the target shared library name like this.
add_library( mylib SHARED ${source_list} )
This generates libmylib.so and other settings in CMakeLists.txt are defined for mylib like
about the mylib
and also we can use shell environment variable to do some selective settings like
target_compile_definitions( mylib PRIVATE -DQQQ -D... )
Also it is possible to use shell environment variable to do some selective things.
if(defined env{MYVAR})
set(CMAKE_C_FLAGS "-g -DXYZ")
else()
set(CMAKE_C_FLAGS "-DXYZ")
endif()
I would be happy if I could set the target shared library name as a variable according to the environment variable and use that selected name variable as the shared library name in all other settings. In other words, is it possible to do things like below?
if (defined ENV{FOR_QEMU})
set_name(target_name "simlib_qemu")
else ()
set_name(target_name "simlib")
endif ()
add_library(target_name SHARED ${source_list} )
target_compile_definitions( target_name PRIVATE -DQQQ -D... )
...
You can set the output name of a target to anything you like via:
set_target_properties(target_name PROPERTIES OUTPUT_NAME "whatever")
Then instead of libtarget_name.so, you'll get libwhatever.so. You would continue to refer to the target as target_name in your CMakeLists.txt.
However, since this will only work during configure time anyway, I strongly urge you to use a normal CMake variable instead. You may initialize it from the environment if it is not set, like so:
option(FOR_QEMU "Enable if building with Qemu support" "$ENV{FOR_QEMU}")
add_library(simlib SHARED ${source_list})
target_compile_definitions(simlib PRIVATE -DQQQ -D...)
if (FOR_QEMU)
set_target_properties(target_name PROPERTIES OUTPUT_NAME "simlib_qemu")
endif ()
This way, the CMake variable FOR_QEMU is the de-facto control and it is initialized on the first execution if the matching env-var is set. It will also appear with documentation in the cache, so other developers may query the build system directly for all its configuration points. Bear in mind: CMake is not Make and reading from the environment on every configure is a surprising behavior and generally bad practice.
I am writing function in my cmake project that needs to make two targets from one, and alter slightly one of them:
option(BORG_STRIP_TEST_BINARIES OFF "Strip symbols from test binaries to reduce disk space usage" )
function(add_borg_test target)
add_test(${target} ${target} --gtest_color=yes)
if(BORG_STRIP_TEST_BINARIES)
# copy target, but make it optional
duplicate_target(FROM ${target} TO ${target}_debug )
set_target_properties(${target}_debug PROPERTIES EXCLUDE_FROM_ALL TRUE)
# alter
target_link_options(${target} PRIVATE -s)
endif()
endfunction()
So this is supposed to work like this:
I create binary target that uses gtest. Set up all target_link_libraries and everything. Example name example-utests
instead add generic add_test, I use custom add_borg_test
now example-utests links with flag -s and is included in all target.
example-utests_debug is NOT included in all target but when requested explicitly, it links without -s.
How to implement duplicate_target from above code snippet?
I have a need to have some variable to be specified and exist in the environment.
In case it does not exist need to stop building.
example
if ( "${VARMUSTEXIST}" STREQUAL "ON" )
message(STATUS is ON)
elif ("${VARMUSTEXIST}" STREQUAL "OFF")
message(STATUS is OFF)
endif()
I don't want to put an if (defined VARMUSTEXIST) everywhere in the script.
In bash there is an option for that "set -u".
Some preliminary points:
if ( "${VARMUSTEXIST}" STREQUAL "ON" ) [...] elif(AGAIN LONG EXPRESSION) [...] endif()normally in cmake is simply: if (VARMUSTEXIST) [...] else() [...] endif()
The command if (DEFINED VARMUSTEXIST) requires DEFINED to be upper case.
You mention bash and environment variables:Environment variables are read using $ENV{VARIABLE_NAME}
For environment variables you will do:
if(NOT DEFINED ENV{VARMUSTEXIST})
message(FATAL_ERROR "You must set VARMUSTEXIST environment variable")
endif()
You say:
I don't want to put an if (defined VARMUSTEXIST) everywhere in the script
This is not clear to me: for each variable you need to check only once, possibly in the main CMakeLists.txt. Of course, you need to add NOT: if (NOT DEFINED VARMUSTEXIST) [stop]
If you can be more precise on your problem, we can design a macro that checks if one or a group of variables are defined or not.
If by environment you mean OS environment variables then the syntax is wrong anyway.
If those are options to be provided by the user, then the literal comparisons with ON and OFF are incorrect, since CMake has more ways to express booleans and they are all in widespread use and became idiomatic. Thus, by expecting either ON or OFF, you're making your build script weird and against everyone's expectations. And you're also making more work for yourself, as you've noticed.
First, you should document the options and give them safe default values using option(). Then it won't ever be that a variable could be undefined.
# early in the top-level CMakeLists.txt
option(VARMUSTEXIST "You can provide it. It exists anyway." NO)
And then you'll check its truth or falsehood rather simply:
# later in top-level file or in subdirectories
if (VARMUSTEXIST)
message("VARMUSTEXIST is true")
else()
message("VARMUSTEXIST is false")
endif()
I think this is the best approach...
CMake Variable:
if(NOT DEFINED VARIABLE)
message(FATAL_ERROR "VARIABLE is not set")
endif(NOT DEFINED VARIABLE)
Environment Variable:
if(NOT DEFINED ENV{VARIABLE})
message(FATAL_ERROR "VARIABLE is not set")
endif(NOT DEFINED ENV{VARIABLE})
I want to see what the current set of compiler definitions is in my CMake file. Ones automatically specified and the ones I'd added would be great. The COMPILE_DEFINITIONS macro doesn't appear to contain -- despite what the documentation says.
For example, in the below setup the message never includes GUI_BUILD
add_definitions( -DGUI_BUILD )
message( "COMPILE_DEFINITIONS = ${COMPILE_DEFINITIONS}" )
I don't need to see them in their final form, I just want a quick output to help verify that everything has been configured correctly.
You want to extract the COMPILE_DEFINITIONS property from the directory. E.g. use the following:
add_definitions( -DDebug )
get_directory_property( DirDefs DIRECTORY ${CMAKE_SOURCE_DIR} COMPILE_DEFINITIONS )
Then you can simply iterate over them, e.g.:
foreach( d ${DirDefs} )
message( STATUS "Found Define: " ${d} )
endforeach()
message( STATUS "DirDefs: " ${DirDefs} )
Note that definitions may also be associated with targets or source-files instead of directories. And note that they can differ between configurations. Depending on your requirements, you may need to check a large set of different properties.