How to tell whether CMake used initial value for a

2019-07-21 18:44发布

问题:

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?

回答1:

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.



标签: cmake