Options for user-defined CMake functions - cmake

function(print2Args arg1 arg2)
message(STATUS ${arg1} " " ${arg2})
endfunction(print2Args)
Is it possible to update user defined function print2Args s.t. it will accept options like the built-in CMake function execute_process?

CMake offers this via the CMakeParseArguments. You do not specify the arguments in the function signature as you did in your example.
CMake accepts more arguments then given in the function signature. You define options, one-valued arguments and pairs of arguments in variables and pass these to cmake_parse_arguments. This command sets several variables which you can use to check what arguments where set.
Documentation and an example:
https://cmake.org/cmake/help/latest/module/CMakeParseArguments.html

Related

What is the "VERSION" in "cmake_minimum_required(VERSION 3.10)”

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...

Calling a CMake function: number of arguments

There is a function named myfunc defined as
function (myfunc var1 var2 var3)
...
endfunction()
Then, I see a function call
myfunc(custom hahaha platform ALL
COMMAND echo "hello world"
SOURCES ${some_var} )
My question is
The function myfunc takes 3 variables. In the above function call, which are the 3 variables? Also, how can there be additional commands COMMAND and SOURCES within the function call?
3 variables will be the first 3 arguments.
If your function was defined as follows:
function (myfunc var1 var2 var3)
message ("var1: ${var1}")
message ("var2: ${var2}")
message ("var3: ${var3}")
message ("number of arguments sent to function: ${ARGC}")
message ("all function arguments: ${ARGV}")
message ("all arguments beyond defined: ${ARGN}")
endfunction()
after calling it as you stated:
set (some_var "some var")
myfunc(custom hahaha platform ALL
COMMAND echo "hello world"
SOURCES ${some_var} )
the outuput will be:
var1: custom
var2: hahaha
var3: platform
number of arguments sent to function: 9
all function arguments: custom;hahaha;platform;ALL;COMMAND;echo;hello world;SOURCES;some var
all arguments beyond defined: ALL;COMMAND;echo;hello world;SOURCES;some var
so you have called your function with 9 arguments, that are referenced with ${ARGV}, all arguments that are not defined can also be referenced using variable ${ARGN}.
Note that when calling function, ALL, COMMAND, and SOURCES are just arguments to the function, nothing else.
In the end, here is the full documentation about cmake functions and arguments
To complement #gordan.sikic answer, you also might be interested in cmake_parse_arguments command.
It allows you to define named parameters to your function, like COMMAND ... and WORKING_DIRECTORY ... in add_custom_command. See example in the documentation page.

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.

Function vs. Macro in CMake

The official document of CMake 2.8.12 says about macro
When it is invoked, the commands recorded in the macro are first
modified by replacing formal parameters (${arg1}) with the arguments
passed, and then invoked as normal commands.
and about function
When it is invoked, the commands recorded in the function are first
modified by replacing formal parameters (${arg1}) with the arguments
passed, and then invoked as normal commands.
Obviously, the two quotes are almost the same but it's confusing. Does parameter replacement behave the same in functions and macros?
I wrote a sample code below:
set(var "ABC")
macro(Moo arg)
message("arg = ${arg}")
set(arg "abc")
message("# After change the value of arg.")
message("arg = ${arg}")
endmacro()
message("=== Call macro ===")
Moo(${var})
function(Foo arg)
message("arg = ${arg}")
set(arg "abc")
message("# After change the value of arg.")
message("arg = ${arg}")
endfunction()
message("=== Call function ===")
Foo(${var})
and the output is:
=== Call macro ===
arg = ABC
# After change the value of arg.
arg = ABC
=== Call function ===
arg = ABC
# After change the value of arg.
arg = abc
So it seems arg is assigned the value of var when calling Foo and ${arg} is just string replaced with ${var} when calling Moo.
So I think the above two quotes are very easy to make one confused, although the official documents also said that:
Note that the parameters to a macro and values such as ARGN are not variables in the usual CMake sense. They are string replacements much like the C preprocessor would do
with a macro. If you want true CMake variables and/or better CMake
scope control you should look at the function command.
UPDATE (1/29/2021)
Add the following statement after the statement Moo(${var}) to make the difference between macro and function even more clear.
message(${arg})
This statement will print out abc.
In other words, function pushes and pops new variable scope (variables created and changed exist only in the function), macro does not. However, you can override the function default behaviour with the PARENT_SCOPE parameter of the set command.
The cmake documentation you quoted is so misleading that it's basically wrong. It should be clarified/fixed like this:
macro: when it is invoked, the commands recorded in the macro are first all modified before any is run by replacing formal parameters (${arg1}) with the arguments passed.
cmake --trace-expand shows exactly what happens.
The cmake 3.13.3 doc hasn't changed compared to 2.8.12 with respect to this.
The macro expansion, answered by Yantao Xie really opens my eyes!
I also found the tutorial below comes with some concrete examples, which is helpful to understand the variable scope concept.
Cited from Learn cmake in 15 mins:
In CMake, you can use a pair of function/endfunction commands to define a function. Here’s one that doubles the numeric value of its argument, then prints the result:
function(doubleIt VALUE)
math(EXPR RESULT "${VALUE} * 2")
message("${RESULT}")
endfunction()
doubleIt("4") # Prints: 8
Functions run in their own scope. None of the variables defined in a function pollute the caller’s scope. If you want to return a value, you can pass the name of a variable to your function, then call the set command with the special argument PARENT_SCOPE:
function(doubleIt VARNAME VALUE)
math(EXPR RESULT "${VALUE} * 2")
set(${VARNAME} "${RESULT}" PARENT_SCOPE) # Set the named variable in caller's scope
endfunction()
doubleIt(RESULT "4") # Tell the function to set the variable named RESULT
message("${RESULT}") # Prints: 8
Similarly, a pair of macro/endmacro commands defines a macro. Unlike functions, macros run in the same scope as their caller. Therefore, all variables defined inside a macro are set in the caller’s scope. We can replace the previous function with the following:
macro(doubleIt VARNAME VALUE)
math(EXPR ${VARNAME} "${VALUE} * 2") # Set the named variable in caller's scope
endmacro()
doubleIt(RESULT "4") # Tell the macro to set the variable named RESULT
message("${RESULT}") # Prints: 8
Both functions and macros accept an arbitrary number of arguments. Unnamed arguments are exposed to the function as a list, through a special variable named ARGN.
Here’s a function that doubles every argument it receives, printing each one on a separate line:
function(doubleEach)
foreach(ARG ${ARGN}) # Iterate over each argument
math(EXPR N "${ARG} * 2") # Double ARG's numeric value; store result in N
message("${N}") # Print N
endforeach()
endfunction()
doubleEach(5 6 7 8) # Prints 10, 12, 14, 16 on separate lines
Another notable difference between function() and macro() is the behavior of return().
From the cmake documentation of return():
Note that a macro, unlike a function, is expanded in place and therefore cannot handle return().
So because it is expanded in place, in a macro() it returns from the caller. While in a function it just exits the function()
Example:
macro(my_macro)
return()
endmacro()
function(my_function)
return()
endfunction()
my_function()
message(hello) # is printed
my_macro()
message(hi) # is not printed

Optional Argument in cmake macro

I want to create a macro whose argument is optional. If not specified, the argument's value should be an empty string. How can I do this?
Any arguments named in your macro definition are required for callers. However, your macro can still accept optional arguments as long as they aren't explicitly named in the macro definition.
That is, callers are permitted to pass in extra arguments (which were not named in the macro's argument list). Your macro can check for the existence of such extra arguments by checking the ${ARGN} variable.
Beware: ARGN is not a "normal" cmake variable. To use it with the list() command, you'll need to copy it into a normal variable first:
macro (mymacro required_arg1 required_arg2)
# Cannot use ARGN directly with list() command,
# so copy it to a variable first.
set (extra_args ${ARGN})
# Did we get any optional args?
list(LENGTH extra_args extra_count)
if (${extra_count} GREATER 0)
list(GET extra_args 0 optional_arg)
message ("Got an optional arg: ${optional_arg}")
endif ()
endmacro (mymacro)
CMake does not (AFAIK) check the number of variables passed to a macro, so you can just go ahead and declare it as any other macro.
There is also a variable ${ARGN} which expands to a list of all the "remaining" variables passed to a macro, which may be useful.
Update
As stated in Sam's comment, CMake now fails unless all expected (named) arguments are given when calling a macro or function.
This happens by default if the argument isn't specified as CMake doesn't check the number of parameters required for a macro.