cmake_parse_arguments() and CMake cache slip through variables
Using cmake_parse_arguments()
allows cache variables to slip through when a specified argument to the function is omitted by the caller.
Knowing the nature / internal as to how local scope and cache variables work this is in many way not surprising, but as a developer of re-usable CMake modules, then users of such modules may not be aware that using a given module suddenly add restrictions on which names are allowed to use for cache variables.
For example, creating a CMake module foo.cmake
containing the following function:
function(foo)
cmake_parse_arguments(FOO "" "BAR" "BAZ" ${ARGN})
message("BAR: ${FOO_BAR}; BAZ: ${FOO_BAZ}")
endfunction()
and then having a CMakeList.txt file with:
cmake_minimum_required(VERSION 3.20)
project(test_foo)
include(foo)
foo() # Call foo without arguments.
Now running CMake without setting FOO_BAR
or FOO_BAZ
works as expected:
$ cmake -Bbuild .
...
BAR: ; BAZ:
...
but setting those on command line as cache variable, one will get:
$ cmake -Bbuild . -DFOO_BAR=bar -DFOO_BAZ=baz
...
BAR: bar; BAZ: baz
...
This is problematic, because a given project may not know all prohibited variables, as there exists several reusable CMake modules.
For example, using CMakeCompilerIdDetection.cmake
and GoogleTest.cmake
uses some generic prefixes which can easily collide with a project cache variable name.
cmake_parse_arguments(CID "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
CID
and ARGS
are not so unique prefixes, meaning that using those CMake modules restricts the use of certain cache variables.
Namely that variable with those prefixes should be avoided, hoever this is not highlighted by CMake or in its documentation.
Proposed solution:
- Highlight in
cmake_parse_arguments()
documentation that it is important to select unique prefixes, as to minimize the risk of colliding with cache variable names chosen by a given project. Also highlight that cache variables will slip through. - Introduce a warning in
cmake_parse_arguments()
so that a warning can be printed if a cache variable of identical name as the<PREFIX>_<ARGUMENT>
name exists, especially if<ARGUMENT>
is not provided when the function is called. For the example given above, such warning should be printed whenfoo()
is called without arguments butFOO_BAR
andFOO_BAZ
exists as cached variables.
The warning will help to ensure functions using cmake_parse_arguments()
uses non-colliding prefixes, as well as warn a project whenever it uses a CMake variable that has a risk of colliding with a CMake module, such as CID_PREFIX
(CMakeCompilerIdDetection) or ARGS_TARGET
(GoogleTest).