CMake-gui toggle visibility of variable - cmake

I have a CMake setup where the accessibilty of one variable will depend whether another one is set or not. Small snippet:
option(build-compiler "Build the Nap Compiler" ON)
set(include_interrupt_dirs CACHE INTERNAL "interrupts/intr_4" FORCE)
if(build-compiler)
option(enable-runtime-compilation
"Build in the runtime code compilation link in intr_2 & intr_3)" ON)
if(enable-runtime-compilation)
list(APPEND include_interrupt_dirs "interrupts/intr_2" "interrupts/intr_3" )
endif()
endif()
I use cmake-gui for configuring the project, and what I would like to achieve is:
if the user selects the build-compiler the enable-runtime-compilation should also be presented. This part is done.
if the user deselects the build-compiler the enable-runtime-compilation should be hidden from the GUI. This is not working.
Do you have any idea how to make it work?

Using unset(var [CACHE]) is subtly tricky. If you just unset the variable, it will remain in the cache (although it will not be visible to the script, it will still be visible to the user). If you also delete it from the cache, then you lose the value that was there.
In my use case, I wanted to hide variables based on some condition. I found it that deleting the variables from the cache might get confusing as when reinstated, they would return to their default state rather than to what the user might have set before.
I prefer to hide the variables using mark_as_advanced(FORCE var) and unhide using mark_as_advanced(CLEAR var). It does exactly what you need-it hides the variable from the GUI but it still exists in the cache. You might use this together with the "soft" unset (the one without CACHE) to make sure that the hidden variable is not still being used in the configuration.
Additionally, there is CMakeDependentOption which is intended specifically for this use case (an option that is only available if some set of condition evaluates as true). This is apparently available since CMake 3.0.2.

You can use unset(var CACHE) to remove the variable from the cache:
if(build-compiler)
option(enable-runtime-compilation
"Build in the runtime code compilation link in intr_2 & intr_3)" ON)
if(enable-runtime-compilation)
list(APPEND include_interrupt_dirs "interrupts/intr_2" "interrupts/intr_3" )
endif()
else()
unset(enable-runtime-compilation CACHE)
endif()

Related

How to set value by code to a CACHE variable defined by 3d party CMake?

In my project, the CMakeLists includes other cmake files from a library and those dependencies need some cache variables to be configured by user values.
It is all working well if I define those values from the command line with the cmake command:
-DTHIRDPARTY_FRAMEWORK_ROOT="$thirdpartyFrameworkPath"
But can I define (= hardcode) such values in my own CMakeLists file?
To avoid my own users to do it when they configure my project (some values of the 3d party configuration are constant in my project), and make my own cmake interface simpler.
I tried to simply set the variable with a value, but it is both defined and used in the included cmake so it gets overwritten with their default value just before being used.
Using set(... FORCE) seems to work but it does not look clean to me, and might lead to confusing errors if they rename or change the type of the variables on their side. It also forces me to add a type and a doc string because of the set(... CACHE ...) syntax.
Is there a better way to do this?
Setting CACHE INTERNAL variable is a proper way for hardcode a parameter of the inner project in the outer one:
set(THIRDPARTY_FRAMEWORK_ROOT CACHE INTERNAL "Hardcoded root for 'thirdparty'" <value>)
INTERNAL type makes sure that this setting will overwrite the option (FORCE doesn't need) and makes sure that the option won't be shown for a "normal" user.
Since the parameter is not intended to be changed by a user, its real type is meaningless, so there is no needs for it to coincide with the one set in the inner project.
As for description, you could set it to be empty (the parameter is not shown to the normal user, remember?). Alternatively, in the description you could explain why do you set the variable in the outer project. So an "advanced" user will see your description.

How to use condition in cmake generator expression

I would like to compile a library as shared or static based on other variable eg:
add_library(MyLibrary $<$<IF:${BUILD_SHARED_LIBS},SHARED,STATIC> ${SOURCES})
For clarity I expect this to be equivalent with the following:
if(BUILD_SHARED_LIBS)
add_library(MyLibrary SHARED ${SOURCES})
elseif()
add_library(MyLibrary STATIC ${SOURCES})
endif()
AFAIK, you cannot do that with generator expressions (no way to query that aspect according to doc), since BUILD_SHARED_LIBS is there for exactly that reason; to allow you to select its value during configuration (using the -D command line option). This will work only if you do not set the library type explicitly as in your code, but like this
add_library(MyLibrary ${SOURCES})
Actually, that is the recommended practice. If you need to influence its value in association with some other condition, you can override it with the usual if()/else() logic, making sure that you print at least an informative message() for the user.
However, an even better approach would be to push those decisions to the user (via options) and check for the illegal combinations, issuing a message(FATAL_ERROR). Even if that condition is determined automatically, this is still a tactic that has merit.

Modifying CACHE variable in CMake is not working

I am using CMake 3.10.2 in Windows.
When I set the variable using CACHE like this
SET(ABAQUS_MAJORVERSION 2016)
SET(ABAQUS_MAJORVERSION ${ABAQUS_MAJORVERSION} CACHE STRING "" )
When I change the ABAQUS_MAJORVERSION variable to 2014 in GUI, this change is not updated in CMake. It keeps generating for 2016 version.
Please help in this regard.
Thanks in Advance
Edit1:
This is the project structure:
|CMakeLists.txt
|FindABAQUS.cmake
|-project1
|---source1.cpp
|---CMakeLists.txt which has SET(ABAQUS_MAJORVERSION 2016 CACHE STRING "")
|-project2
|---source2.cpp
|---CMakeLists.txt which has SET(ABAQUS_MAJORVERSION 2016 CACHE STRING "")
I changed the ABAQUS_MAJORVERSION to 2014 in GUI. The ABAQUS_MAJORVERSION became 2014 in CMakeCache.txt file.
But when printed with message(${ABAQUS_MAJORVERSION }) it shows 2016
Solution:
example: SET(MAJORVERSION 2016 CACHE STRING "")
One might need to unset all the Include paths and library paths, to take effect of the new version Include path and library paths.
example: UNSET(INCLUDE_PATH CACHE)
UNSET(LIBRARY_PATH CACHE)
It may depend on how you're using (or accidentally not using) the cache variable. You can have a normal variable and cache variable of the same name existing at the same time (which is exactly what you have going on) and still access them both (as per the docs on variable references) using ${var_name} for the regular variable, and $CACHE{var_name} for the cache variable.
This can trip people up because they aren't used to writing the explicit cache form, because usually the following behaviour takes effect:
When evaluating Variable References, CMake first searches the function call stack, if any, for a binding and then falls back to the binding in the current directory scope, if any. If a "set" binding is found, its value is used. If an "unset" binding is found, or no binding is found, CMake then searches for a cache entry. If a cache entry is found, its value is used. Otherwise, the variable reference evaluates to an empty string. The $CACHE{VAR} syntax can be used to do direct cache entry lookups.
I'm guessing this is what's tripping you up.
The following scenario can be another cause of confusion for anyone not aware of its behaviour, but I don't think it's what's tripping you up here.
In the CMake docs for setting cache variables:
Since cache entries are meant to provide user-settable values this does not overwrite existing cache entries by default. Use the FORCE option to overwrite existing entries.
For example, cache variables can be set on the command line with -D var_name:TYPE=VALUE.

Why do I need FORCE to override a CMake option?

I want to override an option, which formely was set to OFF. I am using
SET(USE_OPTION ON CACHE BOOL "Override option" FORCE)
Just for my curiosity: Why do I need 'FORCE' (without it, the set() does not work)
Options are variables meant to be changeable by the user after the configuration step and before the actual build enviroment generation through tools like the cmake-gui, ccmake or through the -D command line switch.
That's why they are cached in the first place. The choice the user has done has to be persisted. So
option(USE_OPTION "My option" ON)
is an equivalent to
set(USE_OPTION ON CACHE BOOL "My option")
So to change or force an "user definable value" / cached variable you have to FORCE it.
Or hide/overwrite it for the current variable scope with a non-cached variable:
set(USE_OPTION ON)
Reference
What's the CMake syntax to set and use variables?
From the docs:
If CACHE is present, then the is put in the cache instead,
unless it is already in the cache
I assume the previous option was also CACHE.
If FORCE is specified, the value of the cache variable is set, even
if the variable is already in the cache.
So, if you don't specify FORCE it doesn't get added to the cache as per above.

How to tell whether CMake used initial value for an option?

Say I have an option "ENABLE_Foo" in CMake:
option(ENABLE_Foo "Tell cmake to enable the package Foo" ON)
I want to detect whether the user specified -DENABLE_Foo=ON or -DENABLE_Foo=OFF, or whether the specified initial value ON is being used.
The reason I want to tell whether the user turned the option on or whether it is turned on by default is because Foo requires an additional package and I want to cover these cases:
1) User did not specify a value for the option ENABLE_Foo:
a) Package Foo is found -> use Foo
b) Package Foo is not found -> silently turn off Foo
2) User specified a value for the option ENABLE_Foo:
a) User said -DENABLE_Foo=ON:
i) Package Foo is found -> use Foo
ii) Package Foo is not found -> fatal error message
b) User said -DENABLE_Foo=OFF -> don't use Foo and don't try to find it
If there is no way to tell whether the option value came from user input or from the initial value, are there other ways to achieve what I have outlined above?
If the user gives -DENABLE_Foo=ON on the command line, an entry for the respective option will be added to the CMake cache. While it is possible to inspect this value before invoking the option command, you cannot distinguish whether the value was originally set by the user on the command line, or by the option command itself on a previous CMake run.
Still, achieving the behavior you described is possible.
The main issue is that you cannot model the configuration options you want with just one flag. You are actually dealing with two different options.
The first is whether support for Foo is desired at all, the second is whether Foo is to be considered an optional or a mandatory dependency. Note that the value of the latter is irrelevant in case support for Foo is disabled. One way to handle this would be to remove the option completely in this case. This allows for the following approach:
if(REQUIRE_Foo)
# REQUIRE_Foo implies ENABLE_Foo
unset(ENABLE_Foo CACHE)
endif()
option(ENABLE_Foo "Tell cmake to enable support for package Foo" ON)
if(ENABLE_Foo)
option(REQUIRE_Foo "Abort the build if Foo is not found." OFF)
find_package(Foo) # assuming that FindFoo is a well-behaving find script,
# which will set Foo_FOUND appropriately
else()
unset(REQUIRE_Foo CACHE)
set(Foo_FOUND FALSE)
endif()
if(REQUIRE_Foo AND NOT Foo_FOUND)
message(FATAL_ERROR "Required library Foo could not be found")
endif()
In case the user wants to require Foo support (your case 2a) they would invoke CMake with -DREQUIRE_Foo=TRUE. In case they want to disable Foo completely (your case 2b) they would use -DENABLE_Foo=FALSE. If the user specifies nothing, you get the behavior from your case 1.
Assuming that the rest of the build system is already prepared to handle the case where Foo was not found (case 1b from your question), this should just work without further changes.
Note that even if you could detect whether the flag was set by the user, it would still be undesirable to implement the original behavior from your question.
Assuming we could detect it, the initial value of REQUIRE_Foo in the above code would be set to true if and only if ENABLE_Foo=ON was set on the command line. Doing this implicitly without also adding the REQUIRE_Foo to the set of configuration options is problematic. A user would experience different behaviors on CMake runs even though the build options are the same. You should avoid magical behavior like this. It will only confuse users and give you a hard time debugging failing CMake runs.
Always make build options that depend on configuration given by the user visible to the user.