What's the CMake syntax to set and use variables?
When writing CMake scripts there is a lot you need to know about the syntax and how to use variables in CMake.
The Syntax
Strings using set()
:
set(MyString "Some Text")
set(MyStringWithVar "Some other Text: ${MyString}")
set(MyStringWithQuot "Some quote: \"${MyStringWithVar}\"")
Or with string()
:
string(APPEND MyStringWithContent " ${MyString}")
Lists using set()
:
set(MyList "a" "b" "c")
set(MyList ${MyList} "d")
Or better with list()
:
list(APPEND MyList "a" "b" "c")
list(APPEND MyList "d")
Lists of File Names:
set(MySourcesList "File.name" "File with Space.name")
list(APPEND MySourcesList "File.name" "File with Space.name")
add_excutable(MyExeTarget ${MySourcesList})
The Documentation
- CMake/Language Syntax
- CMake: Variables Lists Strings
- CMake: Useful Variables
- CMake
set()
Command - CMake
string()
Command - CMake
list()
Command - Cmake: Generator Expressions
The Scope or "What value does my variable have?"
First there are the "Normal Variables" and things you need to know about their scope:
- Normal variables are visible to the
CMakeLists.txt
they are set in and everything called from there (add_subdirectory()
,include()
,macro()
andfunction()
). - The
add_subdirectory()
andfunction()
commands are special, because they open-up their own scope.- Meaning variables
set(...)
there are only visible there and they make a copy of all normal variables of the scope level they are called from (called parent scope). - So if you are in a sub-directory or a function you can modify an already existing variable in the parent scope with
set(... PARENT_SCOPE)
- You can make use of this e.g. in functions by passing the variable name as a function parameter. An example would be
function(xyz _resultVar)
is settingset(${_resultVar} 1 PARENT_SCOPE)
- Meaning variables
- On the other hand everything you set in
include()
ormacro()
scripts will modify variables directly in the scope of where they are called from.
Second there is the "Global Variables Cache". Things you need to know about the Cache:
- If no normal variable with the given name is defined in the current scope, CMake will look for a matching Cache entry.
- Cache values are stored in the
CMakeCache.txt
file in your binary output directory. The values in the Cache can be modified in CMake's GUI application before they are generated. Therefore they - in comparison to normal variables - have a
type
and adocstring
. I normally don't use the GUI so I useset(... CACHE INTERNAL "")
to set my global and persistant values.Please note that the
INTERNAL
cache variable type does implyFORCE
In a CMake script you can only change existing Cache entries if you use the
set(... CACHE ... FORCE)
syntax. This behavior is made use of e.g. by CMake itself, because it normally does not force Cache entries itself and therefore you can pre-define it with another value.- You can use the command line to set entries in the Cache with the syntax
cmake -D var:type=value
, justcmake -D var=value
or withcmake -C CMakeInitialCache.cmake
. - You can unset entries in the Cache with
unset(... CACHE)
.
The Cache is global and you can set them virtually anywhere in your CMake scripts. But I would recommend you think twice about where to use Cache variables (they are global and they are persistant). I normally prefer the set_property(GLOBAL PROPERTY ...)
and set_property(GLOBAL APPEND PROPERTY ...)
syntax to define my own non-persistant global variables.
Variable Pitfalls and "How to debug variable changes?"
To avoid pitfalls you should know the following about variables:
- Local variables do hide cached variables if both have the same name
- The
find_...
commands - if successful - do write their results as cached variables "so that no call will search again" - Lists in CMake are just strings with semicolons delimiters and therefore the quotation-marks are important
set(MyVar a b c)
is"a;b;c"
andset(MyVar "a b c")
is"a b c"
- The recommendation is that you always use quotation marks with the one exception when you want to give a list as list
- Generally prefer the
list()
command for handling lists
- The whole scope issue described above. Especially it's recommended to use
functions()
instead ofmacros()
because you don't want your local variables to show up in the parent scope. - A lot of variables used by CMake are set with the
project()
andenable_language()
calls. So it could get important to set some variables before those commands are used. - Environment variables may differ from where CMake generated the make environment and when the the make files are put to use.
- A change in an environment variable does not re-trigger the generation process.
- Especially a generated IDE environment may differ from your command line, so it's recommended to transfer your environment variables into something that is cached.
Sometimes only debugging variables helps. The following may help you:
- Simply use old
printf
debugging style by using themessage()
command. There also some ready to use modules shipped with CMake itself: CMakePrintHelpers.cmake, CMakePrintSystemInformation.cmake - Look into
CMakeCache.txt
file in your binary output directory. This file is even generated if the actual generation of your make environment fails. - Use variable_watch() to see where your variables are read/written/removed.
- Look into the directory properties CACHE_VARIABLES and VARIABLES
- Call
cmake --trace ...
to see the CMake's complete parsing process. That's sort of the last reserve, because it generates a lot of output.
Special Syntax
- Environment Variables
- You can can read
$ENV{...}
and writeset(ENV{...} ...)
environment variables
- You can can read
- Generator Expressions
- Generator expressions
$<...>
are only evaluated when CMake's generator writes the make environment (it comparison to normal variables that are replaced "in-place" by the parser) - Very handy e.g. in compiler/linker command lines and in multi-configuration environments
- Generator expressions
- References
- With
${${...}}
you can give variable names in a variable and reference its content. - Often used when giving a variable name as function/macro parameter.
- With
- Constant Values (see
if()
command)- With
if(MyVariable)
you can directly check a variable for true/false (no need here for the enclosing${...}
) - True if the constant is
1
,ON
,YES
,TRUE
,Y
, or a non-zero number. - False if the constant is
0
,OFF
,NO
,FALSE
,N
,IGNORE
,NOTFOUND
, the empty string, or ends in the suffix-NOTFOUND
. - This syntax is often use for something like
if(MSVC)
, but it can be confusing for someone who does not know this syntax shortcut.
- With
- Recursive substitutions
- You can construct variable names using variables. After CMake has substituted the variables, it will check again if the result is a variable itself. This is very powerful feature used in CMake itself e.g. as sort of a template
set(CMAKE_${lang}_COMPILER ...)
- But be aware this can give you a headache in
if()
commands. Here is an example whereCMAKE_CXX_COMPILER_ID
is"MSVC"
andMSVC
is"1"
:if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
is true, because it evaluates toif("1" STREQUAL "1")
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
is false, because it evaluates toif("MSVC" STREQUAL "1")
- So the best solution here would be - see above - to directly check for
if(MSVC)
- The good news is that this was fixed in CMake 3.1 with the introduction of policy CMP0054. I would recommend to always set
cmake_policy(SET CMP0054 NEW)
to "only interpretif()
arguments as variables or keywords when unquoted."
- You can construct variable names using variables. After CMake has substituted the variables, it will check again if the result is a variable itself. This is very powerful feature used in CMake itself e.g. as sort of a template
- The
option()
command- Mainly just cached strings that only can be
ON
orOFF
and they allow some special handling like e.g. dependencies - But be aware, don't mistake the
option
with theset
command. The value given tooption
is really only the "initial value" (transferred once to the cache during the first configuration step) and is afterwards meant to be changed by the user through CMake's GUI.
- Mainly just cached strings that only can be
References
- How is CMake used?
- cmake, lost in the concept of global variables (and PARENT_SCOPE or add_subdirectory alternatives)
- Looping over a string list
- How to store CMake build settings
- CMake compare to empty string with STREQUAL failed
- When should I quote variables?
Here are a couple basic examples to get started quick and dirty.
One item variable
Set variable:
SET(INSTALL_ETC_DIR "etc")
Use variable:
SET(INSTALL_ETC_CROND_DIR "${INSTALL_ETC_DIR}/cron.d")
Multi-item variable (ie. list)
Set variable:
SET(PROGRAM_SRCS
program.c
program_utils.c
a_lib.c
b_lib.c
config.c
)
Use variable:
add_executable(program "${PROGRAM_SRCS}")
CMake docs on variables
$ENV{FOO}
for usage, where FOO
is being picked up from the environment variable. otherwise use as ${FOO}
, where FOO
is some other variable. For setting, SET(FOO "foo")
would be used in CMake.