Map in Cmake script - cmake

I'd like to map a variable to a string, as in this example in pseudo-cmake code
map(mymap "val1" "key1" "val2" "key2") # <-- this seems not to exist
set(filename "prependfilename${mymap[${avar}]}otherpartoffilename")
basically in my case i have to concat the strings "a32" or "a64" on the filename based on the ${ANDROID_ABI} (which is a variable expressing the target architecture type) value.
How to achieve a simple map behaviour for variables in CMake?

CMake doesn't support [] or {} operators, but mapping could be achieved by naming scheme for a variable:
# The naming scheme: mymap_<val>
set(mymap_val1 key1) # Maps val1 => key1
set(mymap_val2 key2) # Maps val2 => key2
# ...
set(avar val1) # Some key
message("Key ${avar} is mapped into: ${mymap_${avar}}")
This way for "mapping" is actively used by CMake itself.
E.g. variable CMAKE_<LANG>_COMPILER maps a programming language to the compiler and variable CMAKE_<LANG>_FLAGS_<CONFIG> maps both language and configuration type into the compiler flags, specific for this configuration.

Related

Detect presence of generator expressions in CMake

The use case for the following problem is macro packages which are deployed, so the input is entirely user defined.
Consider the following minimal example:
function(foo)
cmake_parse_arguments(FOO "" "POSSIBLY_RELATIVE_POSSIBLY_GENEX_PATH" "" ${ARGN})
# Chosen as an example for a necessary step which doesn't have a generator expression equivalent
get_filename_component(
ABSOLUTE_PATH
"${FOO_POSSIBLY_RELATIVE_POSSIBLY_GENEX_PATH}"
REALPATH
BASE_DIR
"${CMAKE_CURRENT_SOURCE_DIR}"
)
# Compatible with generator expressions by itself
add_custom_command(
OUTPUT
${MY_OUTPUT}
COMMAND
${MY_TOOL}
${ABSOLUTE_PATH}
)
endfunction()
add_custom_command itself supports generator expressions, but the need to go via get_filename_component to handle one class of input (relative paths, for brevity in calling code), and the use of that intermediate step being impossible for the other class of input (build type dependent generator expressions, which get scrambled by get_filename_component) clashes.
get_filename_component effectively can't be used whenever POSSIBLY_RELATIVE_POSSIBLY_GENEX_PATH had contained any genex components. get_filename_component has no generator expression equivalent either, so substitution of that logic isn't possible.
Is there a robust way to detect the presence of valid generator expressions in a CMake variable, so it can be used as a signal not to attempt any non-genex transformations on that input?
You could use command string(GENEX_STRIP) which strips generator expressions from the string, and compare its result with original string:
string(GENEX_STRIP "${FOO_POSSIBLY_RELATIVE_POSSIBLY_GENEX_PATH}" no_genex)
if(FOO_POSSIBLY_RELATIVE_POSSIBLY_GENEX_PATH STREQUAL no_genex)
# The string doesn't contain generator expressions.
# It is safe to use e.g. 'get_filename_component'
else()
# The string contains generator expressions.
# This case requires special handling.
endif()

How do I derive an executable name from a source file in Meson build?

I'm trying to create a list of unit test targets in Meson, with each test case built from a single source file. The source files are defined with a files() command in a subdirectory:
my_test_files = files(['test_foo.c','test_bar.c','test_baz.c'])
What I'd like to do is something like this in the top-level build:
foreach t_file : my_test_files
t_name = t.split('.')[0]
test(t_name, executable(t_name, t_file, ...))
endforeach
I know it's possible to do this if the file names are plain strings, but the above approach fails with a 'File object is not callable' error.
Is there a more 'Mesonic' way to derive the executable / test name from the source file name?
It should work if you define your variable simply as array, e.g.:
my_test_files = ['test_foo.c','test_bar.c','test_baz.c']
the loop stays the same, except some typo fixed with:
foreach t_file : my_test_files
t_name = t_file.split('.')[0]
test(t_name, executable(t_name, t_file, ...))
endforeach
instead of building array of file objects. This is because executable() accepts input files in many forms: as file objects (which you tried to do) and as strings either source files (that should be compiled) or object files (to be linked) - detected by file name extension.
For more flexibility and better control, one can use array of arrays (which is, of course, extendable and may contain anything that is needed to generate tests):
foo_files = files('test_foo.c')
bar_files = files('test_bar.c')
baz_files = files('test_baz.c')
test_files = [
['foo', foo_files, foo_deps],
['bar', bar_files, []],
['baz', baz_files, baz_deps]]
foreach t : test_files
test(t[0], executable(t[0], t[1], dependencies=t[2], ...))
endforeach

How can I generate a GUID in CMake?

I need to generate GUIDS in CMake to define unique identifiers in some custom configuration files. How can I use CMake to generate GUIDs?
There are several ways to create unique identifiers in CMake (see string() Generation).
E.g. string(TIMESTAMP ...), string(RANDOM ...) or string(UUID ...):
string(UUID <output variable> NAMESPACE <namespace> NAME <name>
TYPE <MD5|SHA1> [UPPER])
Create a univerally unique identifier (aka GUID) as per RFC4122
Examples from CMake's string() Unit Test Uuid.cmake:
set(UUID_DNS_NAMESPACE 6ba7b810-9dad-11d1-80b4-00c04fd430c8)
string(
UUID WWW_EXAMPLE_COM_MD5_UUID
NAMESPACE ${UUID_DNS_NAMESPACE}
NAME "www.example.com"
TYPE MD5
)
string(
UUID WWW_EXAMPLE_COM_SHA1_UUID
NAMESPACE ${UUID_DNS_NAMESPACE}
NAME "www.example.com"
TYPE SHA1 UPPER
)

CMake interpret string as variable

I'm looking for a way to interpret a string as variable name in cmake.
Given:
set(MY_SECRET_VAR "foo")
# later only the name of the variable is known.
set(THE_NAME "MY_SECRET_VAR")
# Now i'm looking for a way to get the value "foo" from the name
# something like:
set(THE_VALUE "${THE_NAME}")
# THE_VALUE should be "foo"
A second level of unwrapping:
set(THE_VALUE "${${THE_NAME}}")

What's the CMake syntax to set and use variables?

I'm asking this as a reminder to myself the next time I use CMake. It never sticks, and Google results aren't great.
What's the syntax to set and use variables in CMake?
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() and function()).
The add_subdirectory() and function() 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 setting set(${_resultVar} 1 PARENT_SCOPE)
On the other hand everything you set in include() or macro() 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 a docstring. I normally don't use the GUI so I use set(... CACHE INTERNAL "") to set my global and persistant values.
Please note that the INTERNAL cache variable type does imply FORCE
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, just cmake -D var=value or with cmake -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" and set(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 of macros() 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() and enable_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 the message() 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 write set(ENV{...} ...) environment variables
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
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.
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.
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 where CMAKE_CXX_COMPILER_ID is "MSVC" and MSVC is "1":
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") is true, because it evaluates to if("1" STREQUAL "1")
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") is false, because it evaluates to if("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 interpret if() arguments as variables or keywords when unquoted."
The option() command
Mainly just cached strings that only can be ON or OFF and they allow some special handling like e.g. dependencies
But be aware, don't mistake the option with the set command. The value given to option 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.
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 CMake 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.