CMake: Set compiler flags in way that can be changed by user. - cmake

I'm trying to set up default for compiler flags in the way that can be later changed by user using -DCMAKE_CXX_FLAGS_RELEASE="..." and similar on commandline.
If I use:
SET( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DNVALGRIND" )
than flags cannot be changed using commandline or ccmake.
If I use
SET( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DNVALGRIND" CACHE STRING "" )
than flags are not set at all.
Is there any right way to do this?

So, by experimenting I figured this out (sort of).
First I found out that the CACHE version does not work because there already is value in cache. That is if I apply FORCE at end of it it gets set:
SET( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DNVALGRIND" CACHE STRING "" FORCE )
obviously this would not allow users to specify flags by themselves, making it equivalent to first option.
The solution is:
Put cache setting command just at the beginning of cmake file (before project command), somehow this sets values before cmake sets them internally. So now it looks like this:
SET( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DNVALGRIND" CACHE STRING "" )
SET( CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG -DNVALGRIND" CACHE STRING "" )
...
project( whatever )
...
and it works. I guess that this will be bad if you use compilers which require some different default flags. But then you should not set default by yourself anyway.
I'm still wondering if there is cleaner way.

Related

How to pass combined compiler options with target_compile_options in CMake?

I'm using a customized clang/llvm to build my project. The customization is basically the addition of optimization passes. To pass options to my passes when compiling with clang I'm using:
clang [..] -mllvm -MyOption [..]
Now it happens that I need to pass multiple options this way:
clang [..] -mllvm -MyOption -mllvm -MyOption2=value [..]
This in combination with CMake's target_compile_options() stops working, CMake removes the second -mllvm because it seems to think it is duplicated.
target_compile_options(vslib INTERFACE -mllvm -MyOption)
target_compile_options(vslib INTERFACE -mllvm -MyOption2=val)
I tried putting " around both options, doesn't work.
Is there a way to achieve this with CMake?
https://cmake.org/cmake/help/v3.12/command/target_compile_options.html:
The set of options is de-duplicated to avoid repetition. While beneficial for individual options, the de-duplication step can break up option groups. For example, -D A -D B becomes -D A B. One may specify a group of options using shell-like quoting along with a SHELL: prefix. The SHELL: prefix is dropped and the rest of the option string is parsed using the separate_arguments() UNIX_COMMAND mode. For example, "SHELL:-D A" "SHELL:-D B" becomes -D A -D B.
So in your case that would be:
target_compile_options(vslib INTERFACE "SHELL:-mllvm -MyOption" "SHELL:-mllvm -MyOption2=val")
Try:
get_property(tmp TARGET vslib PROPERTY INTERFACE_COMPILE_OPTIONS)
list(APPEND tmp -mllvm)
list(APPEND tmp -MyOption)
list(APPEND tmp -mllvm)
list(APPEND tmp -MyOption2=value)
set_property(TARGET vslib PROPERTY INTERFACE_COMPILE_OPTIONS "${tmp}")
or maybe just:
set_property(TARGET vslib APPEND PROPERTY INTERFACE_COMPILE_OPTIONS -mllvm -MyOption)
set_property(TARGET vslib APPEND PROPERTY INTERFACE_COMPILE_OPTIONS -mllvm -MyOption2=value)

cmake 3.15 adding JOB_POOL to add_custom_command SOMETIMES

For users that are using cmake 3.15 or later and are also using Ninja as a generator, I want to set the new JOB_POOL argument to some large add_custom_command() blocks. For other users, I want to keep my add_custom_command() the same (no JOB_POOL).
In earlier steps, I check the version and the generator and set ${JOB_POOLS} and I also set a variable such that users who should use a pool will see (something like):
For historical reasons, I leave this here, although #Tsyvarev
points out that this is the source of my problem!
The double-quotes are NOT wanted here!
set(USE_POOL "JOB_POOL pool_A")
Users that are not using a pool will not have that variable set.
Now how to leverage that variable in my custom command...?
1.) Generator expressions don't work, just including the text with the previous line...
add_custom_command(
...
$<USE_POOL>
)
2.) I can't seem to simply place the variable in the command, again just including the variable contents on the previous line. For example, when ${JOB_POOL} is set to the string "JOB_POOL pool_A", this code...
For historical reasons, I leave this here, although #Tsyvarev
points out that this is the source of my problem!
Don't use a STRING! No double-quotes!
add_custom_command(
OUTPUT foo
DEPENDS bar
# Comment line here...
${USE_POOL}
COMMAND
${CMAKE_COMMAND} -E ...
)
gives this error...
ninja: error: '../path/to/src/dir/JOB_POOL pool_A', needed by 'path/to/src/dir/foo', missing and no known rule to make it
It simply considers the ${JOB_POOL} string to be another dependency!
3.) I can't use the "APPEND" feature of add_custom_command(). It's just ignored...
if (${USE_POOL})
add_custom_command(
...
APPEND
JOB_POOL pool_A
)
endif()
The only thing that seems to work is to put an "if" around my
entire command, which offends my sensibility since I don't like to duplicate so much code...
if(${USE_POOL})
add_custom_command(
...many lines...
JOB_POOL pool_A
)
else()
add_custom_command(
...many lines...
)
endif()
Do you have a better idea...?
Here's a standalone example for #tsyvarev:
cmake_minimum_required(VERSION 3.15)
project foo
set_property(GLOBAL PROPERTY JOB_POOLS pool_A=2)
# For historical reasons, I leave this here, although #Tsyvarev
# points out that this is the source of my problem!
# Don't use a STRING! No double-quotes!
set(USE_POOL "JOB_POOL pool_A")
add_custom_command(
OUTPUT foo.out
DEPENDS foo.in
${USE_POOL}
COMMAND
${CMAKE_COMMAND} -E copy foo.in foo.out
COMMENT "Converting foo.in -> foo.out"
VERBATIM
)
add_custom_target(foo-out
DEPENDS foo.out
)
% cmake -GNinja .
% ninja foo-out
ninja: error: 'JOB_POOL pool_A', needed by 'foo.out', missing and no known rule to make it
It considers the string to be a dependency... If I move the USE_POOL to after the comment, it considers it part of the comment... If I move it to after the command, it considers it part of the command...
Your JOB_POOL option serves for the user's choice. You may create another variable which contains sequence of related parameters for add_custom_command:
if(JOB_POOL)
set(JOB_POOL_PARAMS JOB_POOL pool_A) # Will add 'JOB_POOL pool_A' sequence of params
else()
set(JOB_POOL_PARAMS) # Will add nothing
endif()
Then use new variable directly in add_custom_command call:
add_custom_command(
...
${JOB_POOL_PARAMS} # Will expand to additional parameters when needed
)

Passing variables down to subdirectory only

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.

Using intltool with cmake

I'm writing a GNOME application and use CMake. Now I'm considering making the app translatable, for which GNU provides intltool, gettext, msgfmt, etc.. autotools supports these tools and the entire i18n process out of the box.
How do I get this to work with CMake? Are there any modules or snippets of code around?
Nowadays, the best way to use intltool and gettext together with CMake is to, first, detect if the modules are present in the system and set a few variables like this:
# Setting up Intl
find_package (Intl REQUIRED)
find_package(Gettext REQUIRED)
include_directories(${INTL_INCLUDE_DIRS})
link_directories(${INTL_LIBRARY_DIRS})
Then, you can build the po files like this::
FIND_PROGRAM(GETTEXT_MSGFMT_EXECUTABLE msgfmt)
IF(NOT GETTEXT_MSGFMT_EXECUTABLE)
MESSAGE("------
NOTE: msgfmt not found. Translations will *not* be installed
------")
ELSE(NOT GETTEXT_MSGFMT_EXECUTABLE)
SET(catalogname rkward)
FILE(GLOB PO_FILES *.po)
SET(GMO_FILES)
FOREACH(_poFile ${PO_FILES})
GET_FILENAME_COMPONENT(_poFileName ${_poFile} NAME)
STRING(REGEX REPLACE "^${catalogname}_?" "" _langCode ${_poFileName} )
STRING(REGEX REPLACE "\\.po$" "" _langCode ${_langCode} )
IF( _langCode )
GET_FILENAME_COMPONENT(_lang ${_poFile} NAME_WE)
SET(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/${_lang}.gmo)
ADD_CUSTOM_COMMAND(OUTPUT ${_gmoFile}
COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} --check -o ${_gmoFile} ${_poFile}
DEPENDS ${_poFile})
INSTALL(FILES ${_gmoFile} DESTINATION ${LOCALE_INSTALL_DIR}/${_langCode}/LC_MESSAGES/ RENAME ${catalogname}.mo)
LIST(APPEND GMO_FILES ${_gmoFile})
ENDIF( _langCode )
ENDFOREACH(_poFile ${PO_FILES})
ADD_CUSTOM_TARGET(translations ALL DEPENDS ${GMO_FILES})
ENDIF(NOT GETTEXT_MSGFMT_EXECUTABLE)

CMake: Print out all accessible variables in a script

I'm wondering if there is a way to print out all accessible variables in CMake. I'm not interested in the CMake variables - as in the --help-variables option. I'm talking about my variables that I defined, or the variables defined by included scripts.
I'm currently including:
INCLUDE (${CMAKE_ROOT}/Modules/CMakeBackwardCompatibilityCXX.cmake)
And I was hoping that I could just print out all the variables that are here, instead of having to go through all the files and read what was available - I may find some variables I didn't know about that may be useful. It would be good to aid learning & discovery. It is strictly for debugging/development.
This is similar to the question in Print all local variables accessible to the current scope in Lua, but for CMake!
Has anyone done this?
Using the get_cmake_property function, the following loop will print out all CMake variables defined and their values:
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
This can also be embedded in a convenience function which can optionally use a regular expression to print only a subset of variables with matching names
function(dump_cmake_variables)
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
if (ARGV0)
unset(MATCHED)
string(REGEX MATCH ${ARGV0} MATCHED ${_variableName})
if (NOT MATCHED)
continue()
endif()
endif()
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
endfunction()
To print environment variables, use CMake's command mode:
execute_process(COMMAND "${CMAKE_COMMAND}" "-E" "environment")
Another way is to simply use:
cmake -LAH
From the manpage:
-L[A][H]
List non-advanced cached variables.
List cache variables will run CMake and list all the variables from the CMake cache that are not marked as INTERNAL or ADVANCED. This will effectively display current CMake settings [...].
If A is specified, then it will display also advanced variables.
If H is specified, it will also display help for each variable.
ccmake is a good interactive option to interactively inspect cached variables (option( or set( CACHE:
sudo apt-get install cmake-curses-gui
mkdir build
cd build
cmake ..
ccmake ..
Another way to view all cmake's internal variables, is by executing cmake with the --trace-expand option.
This will give you a trace of all .cmake files executed and variables set on each line.
based on #sakra
function(dump_cmake_variables)
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
if (ARGV0)
unset(MATCHED)
#case sensitive match
# string(REGEX MATCH ${ARGV0} MATCHED ${_variableName})
#
#case insenstitive match
string( TOLOWER "${ARGV0}" ARGV0_lower )
string( TOLOWER "${_variableName}" _variableName_lower )
string(REGEX MATCH ${ARGV0_lower} MATCHED ${_variableName_lower})
if (NOT MATCHED)
continue()
endif()
endif()
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
endfunction()
dump_cmake_variables("^Boost")
variable names are case sensitive
btw if you are interested in boost, it is Boost_INCLUDE_DIRS not BOOST_INCLUDE_DIRS, and it is Boost_LIBRARIES not BOOST_LIBRARIES, and by mistake I had BOOST_LIBRARIES instead of Boost_LIBRARIES, https://cmake.org/cmake/help/v3.0/module/FindBoost.html , better example for boost:
set(Boost_USE_STATIC_LIBS ON)
find_package(Boost REQUIRED COMPONENTS RANDOM)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(myfile PRIVATE
${Boost_LIBRARIES}
)
You can use message :
message([STATUS] "SUB_SOURCES : ${SUB_SOURCES}")
None of the current answers allowed me to see the variables in my project subdirectory. Here's a solution:
function(print_directory_variables dir)
# Dump variables:
get_property(_variableNames DIRECTORY ${dir} PROPERTY VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
get_directory_property(_variableValue DIRECTORY ${dir} DEFINITION ${_variableName})
message(STATUS "DIR ${dir}: ${_variableName}=${_variableValue}")
endforeach()
endfunction(print_directory_variables)
# for example
print_directory_variables(.)
print_directory_variables(ui/qt)