is it possible to make target shared library file name variable according to environment variable in cmake? - cmake

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.

Related

How to link same set of libraries to multiple targets/executables in cmake?

I have multiple executable/target files in my project structure, and they all use the same libraries. Is there a way to make this more compact? Perhaps something like a for-loop?
set(ALL_LIBS lib1 lib2 lib3) # etc.
add_executable(program1 program1.cpp)
target_link_libraries(program1 PRIVATE ${ALL_LIBS})
add_executable(program2 program2.cpp)
target_link_libraries(program2 PRIVATE ${ALL_LIBS})
add_executable(program3 program3.cpp)
target_link_libraries(program3 PRIVATE ${ALL_LIBS})
add_executable(program4 program4.cpp)
target_link_libraries(program4 PRIVATE ${ALL_LIBS})
I'm looking for a solution that achieves something similar or cleaner than this:
add_executable(program1 program1.cpp)
add_executable(program2 program2.cpp)
add_executable(program3 program3.cpp)
add_executable(program4 program4.cpp)
# somehow get list of target names
foreach(${TARGETS})
# link libraries to each target
target_link_libraries(${TARGET_NAME} PRIVATE ${ALL_LIBS})
endforeach()
You may define your own macro or function which defines an executable like normal add_executable but also links the executable with the common libraries.
set(ALL_LIBS lib1 lib2 lib3) # etc.
# A wrapper around add_executable which links all created executables with libraries
function(add_executable_common name)
# Forward all parameters to add_executable
add_executable(${ARGV})
# Perform additional actions
target_link_libraries(${name} PRIVATE ${ALL_LIBS})
endfunction()
# Created function can be used in the very same manner as add_executable.
add_executable_common(program1 program1.cpp)
add_executable_common(program2 program2.cpp)
add_executable_common(program3 program3.cpp)
add_executable_common(program4 program4.cpp)
CMake variables ARGV and ARGN provide a perfect way to forward parameters of one function/macro to another.
This allows to easily create wrappers to existed functions without needs to parse all parameters needed for the wrapped function.
E.g. while the function add_executable_common, defined above, doesn't parse parameters except the first one, it still can be used for define a STATIC or SHARED library and can be used for define a library with several sources:
add_executable_common(program5 SHARED program5.cpp additional_algos.cpp)
I found an okay solution to reduce line count. I just created a function to accept target name and file paths.
function(add_program_target TARGET_NAME)
add_executable(${TARGET_NAME} ${ARGN})
target_link_libraries(${TARGET_NAME} PRIVATE ${ALL_LIBS})
endfunction()
Then I just add targets like this:
add_program_target(program1 program1.cpp helper.cpp)
add_program_target(program2 program2.cpp)
add_program_target(program2 program3.cpp)
add_program_target(program2 program4.cpp)
# etc.

How to duplicate cmake target

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?

target_compile_definitions for multiple CMake targets?

I've been told it's bad practice to do things like seting CFLAGS directly in CMake, and that, instead, I should use the target_compile_definitions() command.
Ok, but - what if I want to use similar/identical definitions for multiple (independent) targets? I don't want to repeat myself over and over again.
I see three possible ways:
The preferred one using target_compile_definitions(... INTERFACE/PUBLIC ...) which would self-propagate the compiler definitions to targets depending on it via target_link_libraries() command.
Using the set_property(TARGET target1 target2 ... APPEND PROPERTY COMPILE_DEFINITIONS ...) to set the same definitions to multiple targets.
You may still use the "old commands" of add_definitions() and remove_definitions() to modify COMPILE_DEFINITIONS directory property (which would pre-set all COMPILE_DEFINITIONS target properties in this directories scope).
References
Is Cmake set variable recursive?
CMake: Is there a difference between set_property(TARGET ...) and set_target_properties?
tl;dr: You can iterate the targets in a loop.
If you have a bunch of targets with some common/similar features, you may want to simply manipulate them all in a loop! Remember - CMake is not like GNU Make, it's a full-fledged scripting language (well, sort of). So you could write:
set(my_targets
foo
bar
baz)
foreach(TARGET ${my_targets})
add_executable(${TARGET} "${TARGET}.cu")
target_compile_options(${TARGET} PRIVATE "--some_option=some_value")
target_link_libraries(${TARGET} PRIVATE some_lib)
# and so on
set_target_properties(
${TARGET}
PROPERTIES
C_STANDARD 99
C_STANDARD_REQUIRED YES
C_EXTENSIONS NO )
endforeach(TARGET)
And you could also initialize an empty list of targets, then add to it here-and-there, and only finally apply your common options and settings to all of them, centrally.
Note: In this example I added PRIVATE compile options, but if you need some of them to propagate to targets using your targets, you can make them PUBLIC).
another neat solution is to define an interface library target (a fake target that does not produce any binaries) with all required properties and compiler definitions, then link the other existing targets against it
example:
add_library(myfakelib INTERFACE)
target_compile_definitions(myfakelib INTERFACE MY_NEEDED_DEFINITION)
add_executable(actualtarget1 main1.cpp)
add_executable(actualtarget2 main2.cpp)
set_property(
TARGET actualtarget1 actualtarget2
APPEND PROPERTY LINK_LIBRARIES myfakelib
)
refs:
https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries

CMake: show all modified variables

I would like to have a command or option to list all the modified cache variables of the current build configuration. While cmake -L[AH] is nice, it is also quite overwhelming and doesn't show which are non-default values.
There seems to be a variable property MODIFIED that sounds exactly like what I'm looking for - but the documentation is not very reassuring:
Internal management property. Do not set or get.
This is an internal cache entry property managed by CMake to track interactive user modification of entries. Ignore it.
This question also didn't help: CMAKE: Print out all accessible variables in a script
There are so many ways you could change or initialize variables in CMake (command line, environment variables, script files, etc.) that you won't be able to cover them all.
I just came up with the following script that covers the command line switches. Put the following file in your CMake project's root folder and you get the modified variables printed:
PreLoad.cmake
set(_file "${CMAKE_BINARY_DIR}/UserModifiedVars.txt")
get_directory_property(_vars CACHE_VARIABLES)
list(FIND _vars "CMAKE_BACKWARDS_COMPATIBILITY" _idx)
if (_idx EQUAL -1)
list(REMOVE_ITEM _vars "CMAKE_COMMAND" "CMAKE_CPACK_COMMAND" "CMAKE_CTEST_COMMAND" "CMAKE_ROOT")
file(WRITE "${_file}" "${_vars}")
else()
file(READ "${_file}" _vars)
endif()
foreach(_var IN LISTS _vars)
message(STATUS "User modified ${_var} = ${${_var}}")
endforeach()
This will load before anything else and therefore can relatively easily identify the user modified variables and store them into a file for later reference.
The CMAKE_BACKWARDS_COMPATIBILITY is a cached variable set by CMake at the end of a configuration run and therefor is used here to identify an already configured CMake project.
Reference
CMake: In which Order are Files parsed (Cache, Toolchain, …)?

External library specific COMPILE_DEFINITIONS in cmake

I've written a cmake module for finding QCustomPlot. However, to use the shared library, one needs to #define QCUSTOMPLOT_USE_LIBRARY. I'd like to provide this define through cmake, automatically adding the definition to any project that uses QCustomPlot.
Here is a snippet of my cmake module and my current attempted solution:
SET(QCP_FOUND "NO")
IF(QCP_LIBRARY AND QCP_INCLUDE_DIR)
SET(QCP_FOUND "YES")
SET_PROPERTY(
GLOBAL
APPEND
PROPERTY COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
ENDIF(QCP_LIBRARY AND QCP_INCLUDE_DIR)
However, no linkers append the -DQCUSTOMPLOT_USE_LIBRARY flag in my compilations. What's the right way to approach this problem?
There is no global property COMPILE_DEFINITIONS. But there are such properties for directory, target and source file (see documentation). So probably the closest commands for you are:
set(QCP_FOUND "NO")
if(QCP_LIBRARY AND QCP_INCLUDE_DIR)
set(QCP_FOUND "YES")
set_directory_properties(
PROPERTIES COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
endif()
But I think that this kind of job is for imported targets:
add_library(QCP::qcp UNKNOWN IMPORTED)
set_target_properties(
QCP::qcp
PROPERTIES
IMPORTED_LOCATION "${QCP_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${QCP_INCLUDE_DIR}"
INTERFACE_COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
and usage:
add_executable(foo ...)
target_link_libraries(foo PUBLIC QCP::qcp)