0

Context

I have a super build CMake project which integrate a foo CMake project (think of FetchContent() or add_subdirectory()).

I want to be able to change the value of a cached variable inside foo (i.e. don't like the default value)

question: How I can do that ?

Here a simple test.
note: I also test for an "option()" to see how smooth it is in this case.

Protocol

layout CMakeLists.txt:

# This is a super build project
cmake_minimum_required(VERSION 3.0)

project(SuperBuild VERSION 1.0 LANGUAGES CXX)

# Trying to override the FOO option of "third party" foo
set(FOO OFF)
# Trying to override the BAR cache variable of "third party" foo
set(BAR "foo")

# Incorporate the Foo third party project.
# Should be done using FetchContent(GIT_REPOSITORY .../foo.git) but, here
# I used add_subdirectory(foo) to keep it simple...
add_subdirectory(foo)

foo/CMakeLists.txt

cmake_minimum_required(VERSION 3.0)

# option() honors normal variables.
# without this policy, cmake will "clear" the super build variable value
# and will use the option defualt value -_-
# see: https://cmake.org/cmake/help/git-stage/policy/CMP0077.html
if(POLICY CMP0077)
  cmake_policy(SET CMP0077 NEW)
endif()

# An option ON by default
option(FOO "My FOO option" ON)
message(WARNING "FOO: ${FOO}")

# A string variable cached with default "bar"
# we can override it on command line using for example -DBAR:STRING="foo"
# but here since we are in a super build we want to override it in the super
# build CMakeLists.txt directly
set(BAR "bar" CACHE STRING "My cache entry with default 'bar'")
message(WARNING "BAR: ${BAR}")

Test 1

echo "First run"
cmake -S. -Bbuild
echo "Second run"
cmake -S. -Bbuild

Observed

First run

-- The CXX compiler identification is GNU 10.1.0
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ - works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
CMake Warning at foo/CMakeLists.txt:13 (message):
  FOO: OFF
CMake Warning at foo/CMakeLists.txt:20 (message):
  BAR: bar
-- Configuring done
-- Generating done
-- Build files have been written to: build

Second run

CMake Warning at foo/CMakeLists.txt:13 (message):
  FOO: OFF
CMake Warning at foo/CMakeLists.txt:20 (message):
  BAR: foo
-- Configuring done
-- Generating done
-- Build files have been written to: build

So first run, option FOO is correctly override by the super build variable thanks to CMP0077, while BAR cache variable is set to its default value.

During the second run, strangely (any explanation welcome, IMHO is BAR is already in the cache so the super build set will update it) BAR take the value of the super build...

Side Test

From scratch, just test we can override value in the cmdline (not my case but still interesting)

rm -rf build
cmake -S. -Bbuild -DBAR:STRING=plop
CMake Warning at foo/CMakeLists.txt:13 (message):
  FOO: OFF
CMake Warning at foo/CMakeLists.txt:20 (message):
  BAR: plop
-- Configuring done
-- Generating done
-- Build files have been written to: build

cmake -S. -Bbuild -DBAR:STRING=plop
CMake Warning at foo/CMakeLists.txt:13 (message):
  FOO: OFF
CMake Warning at foo/CMakeLists.txt:20 (message):
  BAR: plop
-- Configuring done
-- Generating done
-- Build files have been written to: build

ps: Sorry for the non syntax highlighting, contrary to github, SO doesn't recognize cmake...

Mizux
  • 8,222
  • 7
  • 32
  • 48

1 Answers1

0

One possible workaround

In the super build I can use:

set(BAR "foo" CACHE STRING "Override foo default value")

cons:

  1. need a "docstring"
  2. need to know the underlying variable type
  3. expose it to super build user ?
  4. ugly IMHO, see option() and variable interaction:

If <variable> is already set as a normal or cache variable, then the command does nothing (see policy CMP0077).

ref: https://cmake.org/cmake/help/latest/command/option.html

EDIT: As pointed out by Tsyvarev we can use:

set(BAR "foo" CACHE INTERNAL "")

which should fix 1,2,3 and mostly 4.
note: to backup and reapply previous cached variable value please take a look at the duplicate answer...

Mizux
  • 8,222
  • 7
  • 32
  • 48
  • 'need a "docstring"' - You may use anything as a docstring, even an empty value (`""`). "need to know the underlying variable type" - For CACHE variables "type" only affects on their representation in GUI. So you could create STRING variable for override FILEPATH one. "expose it to super build user ?" - You may use INTERNAL type for do not expose the variable. "ugly IMHO" - Actually, you are lucky that `foo` project could be ever built as a *subproject* one. If you think that superbuild approach should be applicable for any subproject, then this is not true. – Tsyvarev Jul 02 '20 at 19:16
  • First, many thanks for your valuable answer, I didn't found your related question and it seems I miss the INTERNAL option. Concerning subproject, yes you practically need to fix all project to use ALIAS, appropriate target_*() cmd instead of directory one but on the other hand, I find it worth it to provide standalone build/package, be able to use static lib, cross compilation stuff etc... But cmake still need to be "fixed" to allow this, e.g. https://gitlab.kitware.com/cmake/cmake/-/issues/17735 – Mizux Jul 03 '20 at 07:26