Here is a link to a public repository to show the problem:
The instructions are in the readme.md but I’ll repeat them here:
cmake_default_option_issue
By default cmake options do not support incremental builds. If a component A imports component B and B defines an option (say FOO) that A does not explicitly set, any change (in B) of the default value of FOO will be silently ignored by cmake (when doing incremental builds). With the config helper the incremental build will fail, which is still not good but better than silently producing bad binaries.
How to reproduce:
Run cmake and build normally. Run the executable and see this message.
.\A.exe FOO = ON
In B/CMakeLists.txt change the default for FOO from ON to OFF (o vice versa)
Do an incremental build. The change from step 2. is ignored. Cmake re-runs but nothing is rebuilt
.\A.exe FOO = ON
I’m just going by memory but doesn’t the “option” command add the variable into the cache?
Then subsequent uses of “option” are ignored as it’s already present in the cache.
You would then have to explicitly set the option value you want as the new default is ignored.
But I was hoping to point out that this behavior might not be desirable for projects.
And or is non-obvious.
Because if I absorb a project that is for example defaulting an option to be off since it is experimental code. Then after being fully tested they change the default to be on. Then I don’t get the new default.
If you want your project to follow the default options, then you need to delete those options from the cache (and any other cache variables that might depend on them) and re-run CMake. This will result in the new defaults being picked up. Look into the -U option of the cmake command line tool, or you can select which variables to remove from the cache interactively if using ccmake or the CMake GUI application.
You might want to also take in another TYPE parameter. Having option end up with a STRING variable seems…odd as well. And mark_as_advanced should also be left to the caller to decide. I’m also a chronic quoter in CMake .
I personally think it has enough utility. Even though it is short, it’s another useful tool in the toolbox and a decent convention to have (though bikeshedding over the spelling of <DEFAULT> is possible).
Note that another approach (though it involves a lot more work), is to add a DEFAULT property for CACHE variables and have CMake do this internally. This approach would also assist with this issue.
Something like this may work (I expect there to be design discussions, so I recommend opening an issue and Cc’ing at least me, @craig.scott and @brad.king). This could work as-if:
I suppose that CMake will have to do the <DEFAULT> translation at its cmCacheManager level to make this work. I don’t know if FORCE DEFAULT makes sense either.
Eh, booleans are harder to do here, so any option() support should be considered after we have the groundwork laid out. This is mostly a GUI (and curses) thing though, so maybe not that bad.
A lot of projects I’ve worked on have a terrible understanding of cache variables and they think when they update their defaults it will propagate correctly. And of course it doesn’t (unless the user deletes their cache, which people who are new to cmake don’t know to do). This has literally been a roadblock to cmake adoption at my current company.
Resulting in annoyed developers complaining to the build person. Resulting in resistance to using cmake as a build system (from my companies experience).
While setting a property like was mentioned by @ben.boeckel works.
It’s way too advanced for new cmake writers. I think it should be a policy change.
That way we can keep the syntax for set/option. Without adding complexity for people new to cmake.
I don’t think anyone really wants the existing behavior anyway (at least the people I’ve worked with). Also granted everyone I work with is just getting used to the idea of cmake. (Which I think is a perfect argument in my favor, cmake should be intuitive to new users).
And if people really want to use the old behavior then they can set the policy to use the old behavior.
In summary the policy would effectively make option/set do exactly what @YMba9g8j9CJp0wLoQf5y’s set_with_default does and I think everyone would be happier. I don’t see how this would even hurt existing projects.
The problem with any other naive setting is that it becomes indeterminate whether a cache variable that happens to be set to the default has that value because it was left as the default or was explicitly chosen to be that value (in which case, it should not be updated). Whatever new behavior is available, this distinction must be possible. As such, the existing set() behavior cannot be sufficient.
That’s not how policies work. Policies are there for backwards compatibility only, not feature behavior selection. A policy says “this is how CMake prefers to work now, but we’re supporting the old behavior until you upgrade”. Setting policies to OLD should go away as projects bump minimum CMake versions.