Should I be using types with -D options?

Just a general query for the CMake gurus on here. I’ve always done things like:

cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF ...

but I do see others using the more explicit typing:

cmake .. -DCMAKE_BUILD_TYPE:STRING=Release -DBUILD_SHARED_LIBS:BOOL=OFF ...

I guess my question is: Should I re-train my brain to do this? Am I possibly missing out on some type-checking CMake can do or the like by not doing so?

Thanks,
Matt

It doesn’t make a great deal of difference either way. The biggest difference the type makes is how ccmake and cmake-gui display the cache entry - giving it a type causes it to present as a more appropriate control, like a checkbox or a file input, instead of a simple text input.

1 Like

FWIW, I usually enter it without the type on the command line.

1 Like

Note that specifying a PATH type may convert the value to an absolute path on you. FILEPATH may have similar behaviors.

Edit: This might be more related to not having a type on the command line and then finding a set(var value CACHE PATH doc) command later.

1 Like

I did not know this! I’ll have to experiment!

Oh. That is interesting. I can see how that would be a Good Thing™. Thanks

Except when it does… If you don’t provide the type on the command line, it can lead to different behavior between the first and subsequent CMake runs. The situation arises because of a quirk where if the project sets a cache variable that was already set but didn’t yet have a type associated with it, this will discard any non-cache variable of the same name. Consider the following test project:

cmake_minimum_required(VERSION 3.4)
project(var_test)

set(SOMEVAR local_value)
set(SOMEVAR cache_value CACHE STRING "")
message(STATUS "SOMEVAR = ${SOMEVAR}")

Consider firstly if you don’t try to set SOMEVAR on the command line at all. On the first CMake run, this prints SOMEVAR = cache_value and on subsequent runs, it prints SOMEVAR = local_value. On the first run, the cache variable isn’t set, so setting the variable results in clearing any non-variable of the same name. On subsequent runs, the cache variable exists and has a type, so it doesn’t touch the non-cache variable, which then still takes precedence over the cache variable when we print ${SOMEVAR} at the end.

Now consider if you set the cache variable on the command line. If you set it without any type, like so:

cmake -D SOMEVAR=cmdline ...

On the first run, the cache variable doesn’t initially have a type, even though we’ve given it a value. When the project sets the cache variable, it sees the cache variable has no type, so it clears the non-cache variable (yes, this is quite surprising and probably not widely understood). On subsequent runs, the cache variable does have a type and the local variable is not cleared.

But if on the first run we do provide a type like so:

cmake -D SOMEVAR:STRING=cmdline ...

When we reach the line that sets the cache variable, it already has a type and so the non-cache variable’s value is used even on the first run.

Now, the above all likely sounds horribly unintuitive. And you’re right. This was fixed in CMake 3.21, subject to a new policy CMP0126. If we changed the first line of our example such that the new policy is always used:

cmake_minimum_required(VERSION 3.21)

Now the local variable’s value is left alone and will always be used whether it is the first run or not, whether we set the cache variable on the command line or not, and whether we include the type with the variable on the command line or not. In other words, the new behavior is what people expect.

For completeness, there is a similar situation for the option() command, but that was fixed back in CMake 3.13 with policy CMP0077.

4 Likes