This question already has an answer here:
CMake generator expression is not evaluated
(1 answer)
Closed 3 years ago.
Suppose I have an option:
option(TARGET_IS_ADDIN "Shared library target is an Addin" ON)
and a variable:
set(MyVar $<IF:$<BOOL:${TARGET_IS_ADDIN}>,addin,script>)
why is this getting stored as:
"$<IF:$<BOOL:ON>,addin,script>"
and not "addin"?
While the value for your option TARGET_IS_ADDIN is expanded at CMake's configuration stage, generator expressions are evaluated at CMake's buildsystem generation stage. Thus, when you attempt to print a generator expression using message(), you will see its un-expanded form, as the generation stage has not yet taken place.
Furthermore, not all of CMake's commands even accept generator expressions. The set() command is not one that can process generator expressions because it is processed at the configuration stage. If a command can process generator expressions, it will be explicitly mentioned in that command's documentation (e.g. target_link_libraries() or target_compile_options()).
For examples, see Tsyvarev's answer here.
Related
This question already has answers here:
How do I add a linker or compile flag in a CMake file?
(7 answers)
Closed last year.
I used Set_directory_properties(PROPERTIES COMPILE_DEFINITIONS "--saferc=none") to mask misra check in directory, but I got Warning:
Ccv850:Warning:option "-D--saferc=none" ignored due to invalid argument. Expected name or name=string.
You should basically never use directory properties, ever.
For whichever targets cannot compile without the flag, you can add it like so:
target_compile_options(
my_target
PRIVATE
"$<$<CXX_COMPILER_ID:GHS>:--saferc=none>"
"$<$<C_COMPILER_ID:GHS>:--saferc=none>"
)
My understanding is that --saferc=none is a Green Hills flag, hence the check for $<CXX_COMPILER_ID:GHS>. If you are only using one of C or C++, you can delete the flag for the other language.
I know the meaning of below CMake statement:
cmake_minimum_required(VERSION 3.10)
I am just wondering what the VERSION part is syntactically?
Is it a unquoted argument? If it is an argument, there must be some other argument choices.
But according to here, it seems VERSION is the only choice for the cmake_minimum_required() command.
If so, why do we even need to specify this argument explicitly??
And according to here, this command sets the variable CMAKE_MINIMUM_REQUIRED_VERSION. Is there some kind of string concatenation here? So I can use set_minimum_required(XXX <some_value>) to sent an arbitrary variable with the name CMAKE_MINIMUM_REQUIRED_XXX to <some_value>?
ADD 1
I just tried with below statement in the CMakeLists.txt:
cmake_minimum_required(XXX 123)
And cmake complains that:
CMake Error at CMakeLists.txt:2 (cmake_minimum_required):
cmake_minimum_required called with unknown argument "XXX".
So it seems to be an argument.
But according to here for the project() command, a similar VERSION string is designated as an option. Seems a bit inconsistent.
ADD 2
I just tried with below statement in the CMakeLists.txt:
cmake_minimum_required(3.10)
And cmake complains that:
CMake Error at CMakeLists.txt:1 (cmake_minimum_required):
cmake_minimum_required called with unknown argument "3.10".
So it seems CMake relies on the VERSION part to properly interpret the "3.10" argument. So I guess the VERSION is also an option here.
And since there's another possible option FATAL_ERROR, it is necessary to have a VERSION option.
So to summarize my current understanding:
The essential paradigm of CMake language is:
CMake commands just manipulate variables based on arguments and options.
Some variables are required to be manipulated and some are optional.
For required ones, their values are specified through arguments. (kind of like positional argument)
For optional ones, their values are specified through argument following corresponding options. (kind of like named argument)
ADD 3
From here, for the VERSION in cmake_minimum_required():
The VERSION is a special keyword for this function. And the value of
the version follows the keyword.
So here it is called keyword instead of option ...
Add 4
Some feelings about CMake...
What is the "VERSION" in "cmake_minimum_required(VERSION 3.10)”
I am just wondering what the VERSION part is syntactically?
A string. VERSION. Nothing special.
Is it a unquoted argument?
Yes.
why do we even need to specify this argument explicitly??
To preserve forward compatibility. The idea is that maybe in the feature there will be cmake_minimum_required(DATE 2021-11-03) or similar, for example.
Is there some kind of string concatenation here?
No. It is set explicitly.
So I can use set_minimum_required(XXX <some_value>) to sent an arbitrary variable with the name CMAKE_MINIMUM_REQUIRED_XXX to <some_value>?
No.
Some variables are required to be manipulated and some are optional.
CMake argument parsing is really crude and simple. It's basically in shell pseudocode if $1 == "VERSION" then check_version($2) else error. How you call those strings is really up to your interpretation that depends on the context, in the context of cmake_minimum_required, sure VERSION is a "keyword". Or a "special required argument". Or similar.
All function arguments are strings, and functions compare them to other strings and execute logic upon that. There are no other variable types. Lists are strings, list elements are separated by ; character in a string.
And the implementation of cmake_minimum_required is here: https://gitlab.kitware.com/cmake/cmake/-/blob/master/Source/cmCMakeMinimumRequired.cxx#L29 . Funny, it looks like the parsing is in a loop, so doing cmake_minimum_required(VERSION VERSION VERSION VERSION 3.11) works as fine.
I think the input to a CMake command can be classified as 3 types:
keyword argument (also called option)
positional argument
variable arguments (like ... in C)
For 1, Like the VERSION in cmake_minimum_required, whose value follows it immediately. It is called keyword or option.
For 2, Such argument has a formal name and is referenced through that name within the command body.
For 3, the argument can be referenced with ARGV, ARGN, ARGVC.
I also see marker argument somewhere. But I forget where. Horrible syntax...
ADD 1
OK I found the marker argument. It's in the book Mastering CMake by the CMake team:
...while the CMake official document says it's an option:
Search the paths specified by the PATHS option or in the short-hand
version of the command. These are typically hard-coded guesses.
What a chaotic wording...
I've got the following generator expression working, which sets the /GS flag if the compiler is MSVC and it sets it for the build configurations RelWithDebInfo and Release:
target_compile_options(mytarget PRIVATE
"$<$<CONFIG:Release>: $<$<CXX_COMPILER_ID:MSVC>:/GS>>
$<$<CONFIG:RelWithDebInfo>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>")
Now I also want to let the user configure this, and I've added an option:
option(MYTARGET_ENABLE_GS "Enable /GS" OFF)
So now, I (obviously) want to enable the /GS flag if the user enabled this option, and if they did, I want to add it if the compiler is MSVC, and it should be added to the Release and RelWithDebInfo configurations.
This is pretty nested, and I can't seem to get it right. This is as far as I got:
target_compile_options(mytarget PRIVATE
"$<$<BOOL:MYTARGET_ENABLE_GS>:
$<$<CONFIG:Release>: $<$<CXX_COMPILER_ID:MSVC>:/GS>>
$<$<CONFIG:RelWithDebInfo>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>>")
Edit: Fixed, see below.
I've had to use the $<$<BOOL:...>> because that "translates" the option (which can be on/off or true/false, to 0 or 1, which the generator expression needs.
However above line doesn't work: It doesn't add (or not add) /GS.
I'd like to know:
1) Where is my mistake? How to do this? And
Stuff like this results in pretty convoluted nested expressions, that are really hard to read - imagine 6 months down the line reading that line of code again, even if it is documented. And it's so easy to misplace a > or something like that.
I could probably use "manual" ifs to make this more readable, but imagine having 5-10 of such options - writing an if/end with a target_compile_options inside results in like 15-30 lines of if/end code, which is also not very pretty to look at. What's the best way to do this?
Edit: I was nearly there. Variables have to be enclosed with ${...} in generator expressions. So it's for example:
target_compile_options(mytarget PRIVATE
"$<$<BOOL:${MYTARGET_ENABLE_GS}>:
$<$<CONFIG:Release>: $<$<CXX_COMPILER_ID:MSVC>:/GS>>
$<$<CONFIG:RelWithDebInfo>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>>")
and that works great.
Which still leaves point "2)", which I would be keen to getting insights on.
When you generate a MSVC solution, the build type (Release, Debug, etc.) is unknown until you actually run the build. Thus, using generator expression for that is correct.
But for the 2 other variables (does user set MYTARGET_ENABLE_GS to OFF and is generation performed for MSVC), they are resolved at configuration time. So, you don't have to check them in a generator expression. You could simply write:
if(MSVC AND MYTARGET_ENABLE_GS)
target_compile_options(mytarget PRIVATE "$<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:/GS>")
endif()
This solution also uses $<OR:?[,?]...> generator expression to regroup under the same expression both cases you build in Release OR RelWithDebInfo
Whenever you may use if command, use it. Generator expressions are not replacement for if command.
Generator expressions allow to use conditions dependent on build type. Because on multi-configuration build systems, like Visual Studio, build type isn't known at configuration stage, you cannot use such condition in if command.
But generator expressions are ugly, so do not use them when it is not needed.
There is nothing bad in
if(MYTARGET_ENABLE_GS)
target_compile_options(mytarget PRIVATE "$<$<CONFIG:Release>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>$<$<CONFIG:RelWithDebInfo>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>")
endif()
If you want to check an option at the beginning, but create a target later, you may store generator expression in the variable, and use this variable later:
set(additional_options)
# Depending on parameters, add options to 'additional_options' list.
if(MYTARGET_ENABLE_GS)
list(APPEND additional_options "$<$<CONFIG:Release>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>$<$<CONFIG:RelWithDebInfo>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>")
endif()
if(<other option>)
list(APPEND additional_options <...>)
endif()
# ...
target_compile_options(mytarget PRIVATE ${additional_options})
CMake's documentation a generator expressions works if your config matches any configuration listed after CONFIG:, but I couldn't find the syntax documented anywhere. Trial and error show the following works correctly:
if(MSVC AND MYTARGET_ENABLE_GS)
target_compile_options(mytarget PRIVATE "$<$<CONFIG:Release,RelWithDebInfo>:/GS>")
endif()
This question already has answers here:
cmake generate error on windows as it uses \ as escape seq
(3 answers)
Closed 5 years ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
Is there a robust way to normalize paths in CMake?
Example:
# Let's assume that an environment variable MY_ROOT_DIR is set
# that points to some directory.
set(MYFILE "$ENV{MY_ROOT_DIR}/somefile.txt")
message(${MYFILE})
# This will result for example in
# Win: C:\path\to\my\root\dir/somefile.txt
# Unix based: /path/to/my/root/dir/somefile.txt
In this example, it would be required to normalize MY_ROOT_DIR (that is to replace backslashes with slashes) prior to using it as path component. How would you do this in CMake?
CMake (or the tools further down the toolchain) may handle paths with mixed separators (/ or \), or may not. CMake uses / as the standard separator. A typical warning generated by CMake for paths with the wrong path separator \ may look similar to this:
CMake Warning (dev) at cmake_install.cmake:5 (set):
Syntax error in cmake code at
C:/path/to/my/root/build/cmake_install.cmake:5
when parsing string
C:\path\to\my\root/somefile.txt
Invalid escape sequence \p
Policy CMP0010 is not set: Bad variable reference syntax is an error. Run
"cmake --help-policy CMP0010" for policy details. Use the cmake_policy
command to set the policy and suppress this warning.
Thanks for any hints on this!
You can use the file(TO_CMAKE_PATH) command for this.
The TO_CMAKE_PATH mode converts a native <path> into a cmake-style path with forward-slashes (/). The input can be a single path or a system search path like $ENV{PATH}. A search path will be converted to a cmake-style list separated by ; characters.
Here is an example:
file(TO_CMAKE_PATH "$ENV{MY_DIR_VAR}" ENV_MY_DIR_VAR)
Due to the following warning:
CMake Error at test/CMakeLists.txt:29 (get_target_property):
The LOCATION property may not be read from target "my_exe". Use the
target name directly with add_custom_command, or use the generator
expression $<TARGET_FILE>, as appropriate.
which is the result from lines like this:
get_target_property(my_exe_path my_exe LOCATION)
Like recommended in the docs, I tried to use a generator expression like this:
add_executable(my_exe_path main.cpp)
message("path to executable: $<TARGET_FILE:my_exe_path>")
But TARGET_FILE is not being evaluated
path to executable: $<TARGET_FILE:my_exe>
I'm using CMake 3.4 and added cmake_minimum_required(VERSION 3.4) to my CMakeLists.txt so what am I doing wrong?
Here is a quick and easy way to print the value of a generator expression:
add_custom_target(print
${CMAKE_COMMAND} -E echo $<1:hello> $<0:world>
)
In this example, if you run cmake . and then make print, you will see "hello" (without the quotation marks) in the output.
However, if you just use message($<1:hello> $<0:world>), you will see "$<1:hello> $<0:world>" as output (again, without the quotation marks).
While generator expression is stored at configuration stage (when corresponded CMake command is executed),
evaluation of generator expressions is performed at build stage.
This is why message() command prints generator expression in non-dereferenced form: value denoted by the generator expression is not known at this stage.
Moreover, CMake never dereferences generator expressions by itself. Instead, it generates appropriate string in the build file, which is then interpreted by build utility (make, Visual Studio, etc.).
Note, that not every CMake command accepts generator expressions. Each possible usage of generator expressions is explicitely described in documentation for specific command. Moreover, different CMake command flows or different options have different policy about using of generator expressions.
For example, command flow
add_test(NAME <name> COMMAND <executable>)
accepts generator expressions for COMMAND option,
but command flow
add_test(<name> <executable>)
doesn't!
Another example of policies difference:
install(DIRECTORY <dir> DESTINATION <dest>)
In this command flow generator expressions are allowed for DESTINATION, but not for DIRECTORY option.
Again, read documentation carefully.