Project variants?

Hi, I’ve just created my first full-scale CMake project, but I have a lot to learn. My project builds an executable and a set of shared libraries. The build of each shared library is enabled by a CMake option. I’m now considering how to present these options to fellow developers who aren’t familiar with CMake.

The developer will want to build a subset of libraries that depends on the project he/she is working on. How would I implement ‘super-options’ that would switch a set of options ON?

i.e. Proj1 requires -D Option1=ON Option3=ON

I guess I could do this with a shell script.

Or I could do it in CMakeLists.txt:

option(Proj1 "Build libraries for Proj1" OFF)
if(Proj1)
    set(Option1 "ON")
    set(Option3 "ON")
endif()

or is there a better way?

I noticed that VS Code extension ‘CMake-Tools’ has a concept of ‘CMake Variants’. Does CMake have anything similar?

Any recommendations appreciated.

BR
David

The documentation of the option command mentions the CMakeDependentOption module for this use case.

I don’t think that meets my usecase. I want to implement a ‘preset’ where a ‘super option’ turns on a subset of options. This isn’t a dependency.

@ben.boeckel This sounds exactly like what VTK does with groups. Did you have any tips?

Not with groups exactly. I’d structure the code this way:

set(PROJECT_VARIANT "<DEFAULT>"
  CACHE STRING "Project variant")
set_property(CACHE PROJECT_VARIANT
  PROPERTY STRINGS "<DEFAULT>;VARIANT1;VARIANT2;NONE")

# Defaults for each option.
set(option1_default ON)
set(option2_default OFF)

# Select a default variant. Do this in case the default changes
# in the future, old build trees automatically get it too.
if (PROJECT_VARIANT STREQUAL "<DEFAULT>")
  set(PROJECT_VARIANT "VARIANT1")
endif ()

if (PROJECT_VARIANT STREQUAL "VARIANT1")
  set(option1_default OFF) # VARIANT1 doesn't want this option
endif ()

option(OPTION1 "docstring" "${option1_default}")

@ben.boeckel Thanks, your suggestion is really helpful. I will try that out.

Just as a sanity check on my understanding: Am I correct in thinking that if several variants are to be built, they should be built in separate build directories - not in the same one? (Where a build directory is from where I call cmake).

This depends on whether they should conflict or not. Instead of a single string, you can use a set of booleans for whether that variant should be supported. Basically, if variants are mutually exclusive, use the above code structure. If they’re additive, use options.

@ben.boeckel Thanks again.

@ben.boeckel I have a few questions about your suggested solution:

Please will you explain the line:

set_property(CACHE PROJECT_VARIANT PROPERTY STRINGS "<DEFAULT>;VARIANT1;VARIANT2;NONE")

I understand that a target can have a property value but what does a property mean for the CACHE?

Why do we need that line in addition to:

set(PROJECT_VARIANT "<DEFAULT>" CACHE STRING "Project variant")

Is this:

"<DEFAULT>;VARIANT1;VARIANT2;NONE"

an enumeration of possible values?

Thanks in advance,
David

That’s exactly what it is. https://cmake.org/cmake/help/latest/prop_cache/STRINGS.html

@ben.boeckel Thanks again.