Referencing an undefined option() is different between first & subsequent CMake runs

Consider a CMake script something like:

cmake_dependent_option(
    MYPROJECT_ENABLE_COOL_FEATURE 
    "Enable cool feature (requires MYPROJECT_ENABLES_EXCEPTIONS=ON)" ON
    "MYPROJECT_ENABLE_EXCEPTIONS" OFF
)

...

option(MYPROJECT_ENABLE_EXCEPTIONS "Enable exceptions" ON)

The idea here is obvious: an option to enable exceptions, and a dependent option to enable another feature.

There’s a subtle bug here, though: CMake apparently treats not-yet-defined option() as false, so the first time you use this to configure your project, MYPROJECT_ENABLE_COOL_FEATURE will always be OFF, because it relies on the not-yet-defined MYPROJECT_ENABLE_EXCEPTIONS option.

Subsequent builds will be ‘correct’ since (apparently) the option() values are already cached. This makes for really subtle, hard-to-find build issues.

1 Like

I know this probably isn’t all that helpful, but I try very hard to never use cmake_dependent_option() in projects I work on. The idea of dependent options and the desire to use that sort of structure is understandable, but it leads to issues like this where it can take two CMake runs for options to be set the way you expect/want. Users rarely expect that, and they shouldn’t have to know that or be subjected to such requirements.

In many cases, you can get the configurability you’re after without having to use cmake_dependent_option(). Sure, you might see some things visible in the CMake cache that won’t be used anyway, but that’s a penalty I’ll pay every time if the benefit is a more robust build. Also, some things don’t need to be cache options. They can sometimes be ordinary variables that are either undefined or only defined as regular variables in the project. This would be appropriate for things that are fully dependent on other things rather than being something a user can modify directly.

1 Like

Heh, so it sounds like you’re saying it’s basically a footgun that should ~never be used… so I have to ask, why does it exist in the first place?

For another view…I use it all the time. I don’t mind having users have to configure multiple times to make things appear. The alternative is to have things like message(FATAL_ERROR) if an incompatible flag setup is encountered. I prefer for cache variables to have concrete effects rather than being silently ignored (see below) because I’ve gotten a lot of reports over time asking “why doesn’t this flag I found in the editor seem to do anything?” when the answer ends up being “it’s ignored because it makes no sense given that you also disabled feature X”.

Below: Of course, this doesn’t help -D on the command line from being ignored, but scripted cases should have more doc-based research behind their decisions…