CMake supports both functions and macros to provide a named abstraction for some repetitive works. A function or macro always define a new command.
function(<someName> [<arg1> ...])
where name is the name of the function, with arguments arg1, arg2, etc.
I am foo!!
I am foo!!
I am foo!!
I am foo!
Here the function name is case-insensitive. You can call in any case, but it’s always recommended to use the same name declared in the function definition.
A cmake function can take two types of arguments.
- named or keyword arguments
- optional arguments
Named arguments are mandatory and will throw error if not provided. You don’t need a comma between argument names.
Address: germany munich
Optional arguments can be accessed using some predefined variables.
ARGC : Total number of arguments(named arguments + optional arguments)
ARGV : list of variables containing both named and optional arguments
ARGN : list of variables containing only optional arguments
Other than those three variables, CMake also provides ARGV0, ARGV1, ARGV2, … which will have the actual values of the arguments passed in. Referencing to ARGV# arguments beyond ARGC will have undefined behavior.
Usually, named arguments are accessed using the variable and optional arguments are accessed using ARGN.
CMake functions introduce new scopes, a variable modified inside the function won’t be accessible outside the function. Another problem in CMake, the functions don’t return any value. This will make the function difficult to use. So, CMake provides the keyword
PARENT_SCOPE to set a variable in the parent scope.
You can send variable name as a function parameter. The function will set the variable in the parent scope.
This way of returning value from function is self documenting. But, it becomes repetitive if the function has to return many values. So, many library functions explicitly document well known variables which are set by a function and you don’t need to send any variable name as argument.
In some projects, it’s to common to have functions which set project specific variables in the parent scope. The cmake libraries provide
FindPackageHandleStandardArgs function which sets a variable called Package_FOUND in the parent scope.
You can call
return() to exit a function early.
macro(<name> [<arg1> ...])
where name is the name of the macro, with arguments arg1, arg2, etc.
Like functions, macro names are also case insensitive. You can call in any case, but it’s always recommended to use the same name declared in the macro definition.
Like functions, macros also take both named and positional arguments.
Functions always introduce a new scope when called. In case of macro, the macro call is substituted with the macro body and arguments are replaced using string substitution. No new scope is created. So both functions and macros behave differently in some cases.
DEFINED keyword, you can check if a variable, cache variable or environment variable with given is defined. The value of the variable does not matter.
Macros show strange behavior while using the three special variables(in some cases).
Unlike functions, macro introduce no new scope. The variables declared inside the macro(other than the arguments) will be available after the call.
Since, micro doesn’t create any new scope, return() will exit the current scope.
Redefining Functions and Macros
When function() or macro() is defined and if a command already exists with that name, the previous command will be overridden. The previous command can be accessed using the name and an underscore prepended.
If the same function is redefined again, the underscore version will call previously defined function. The original function will never be available.
third definition of good_func
second definition of good_func
It has two problems.
- If a developer writes a function without knowing that the name is already used for some cmake library functions, then there are chances that the original function will be hidden forever.
- There are some developers who use this feature(or a bug) to add behavior to an existing function. If you do it more than twice, it can cause infinite recursion. It’s difficult to debug when the function definition spans across many modules.
The desired behavior is that 2nd redefinition will call the original function. But, in this scope, the 2nd redefinition is always the underscore version.
CMake has a predefined command to parse function and macro arguments. This command is for use in macros or functions. It processes the arguments given to that macro or function, and defines a set of variables which hold the values of the respective options.
cmake_parse_arguments(<prefix> <options> <one_value_keywords> <multi_value_keywords> <args>...)
cmake_parse_arguments(PARSE_ARGV <N> <prefix> <options> <one_value_keywords> <multi_value_keywords>)
The first version can be used both in functions and macros. The 2nd version(
PARSE_ARGV) can only be used in functions. In this case the arguments that are parsed come from the
ARGV# variables of the calling function. The parsing starts with the
<N>th argument, where
<N> is an unsigned integer. This allows for the values to have special characters like ; in them.
prefix: a prefix string which will preceed all variable names.
options: all options for the respective macro/function, i.e. keywords which can be used when calling the macro without any value following. These are the variables if passed will be set as TRUE, else FALSE.
one_value_keywords: all keywords for this macro or funtion which are followed by one value, e.g.
multi_value_keywords: all keywords for this macro/function which can be followed by more than one value, like e.g. FILES=test.cpp;main.cpp
All remaining arguments are collected in a variable
<prefix>_UNPARSED_ARGUMENTS that will be undefined if all arguments were recognized. This can be checked afterwards to see whether your macro was called with unrecognized parameters.
The above example with PARSE_ARGV version