CMakePresets: What is the overwrite order for multiple inheritance?

I have the CMakeUserPreset.json:

{
  "version": 6,
  "cmakeMinimumRequired": {
    "major": 3,
    "minor": 25,
    "patch": 0
  },
  "configurePresets": [
    {
      "name": "dev",
      "displayName": "developer Ninja build on macos with Coverage",
      "inherits": ["ci-darwin", "coverage-darwin"],
      "generator": "Ninja"
    }
  ],
  "buildPresets": [
    {
      "name": "dev",
      "configurePreset": "dev",
      "configuration": "Coverage"
    }
  ],
  "testPresets": [
    {
      "name": "dev",
      "configurePreset": "dev",
      "configuration": "Coverage",
      "output": {
        "outputOnFailure": true
      }
    }
  ] ,
  "workflowPresets": [
    {
      "name": "dev",
      "steps": [
        {
          "type": "configure",
          "name": "dev"
        },
        {
          "type": "build",
          "name": "dev"
        },
        {
          "type": "test",
          "name": "dev"
        }
      ]
    }
  ]
}

The ci-darwin (Release build) should be overwritten by coverage-darwin!

But the result is this:

bash-3.2$ cmake --workflow --preset dev --fresh
Executing workflow step 1 of 3: configure preset "dev"

Preset CMake variables:

  CMAKE_BUILD_TYPE="Release"
  CMAKE_CXX_EXTENSIONS="OFF"
  CMAKE_CXX_FLAGS="-fstack-protector-strong -fcf-protection=full -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast"
  CMAKE_CXX_FLAGS_COVERAGE="-Og -g --coverage"
  CMAKE_CXX_STANDARD="17"
  CMAKE_CXX_STANDARD_REQUIRED="ON"
  CMAKE_EXE_LINKER_FLAGS_COVERAGE="--coverage"
  CMAKE_SHARED_LINKER_FLAGS_COVERAGE="--coverage"
  ENABLE_COVERAGE="ON"

-- The CXX compiler identification is AppleClang 14.0.3.14030022
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Performing Test COMPILER_HAS_HIDDEN_VISIBILITY
-- Performing Test COMPILER_HAS_HIDDEN_VISIBILITY - Success
-- Performing Test COMPILER_HAS_HIDDEN_INLINE_VISIBILITY
-- Performing Test COMPILER_HAS_HIDDEN_INLINE_VISIBILITY - Success
-- Performing Test COMPILER_HAS_DEPRECATED_ATTR
-- Performing Test COMPILER_HAS_DEPRECATED_ATTR - Success
-- Configuring done (2.1s)
-- Generating done (0.0s)
CMake Warning:
  Manually-specified variables were not used by the project:

    CMAKE_CXX_FLAGS_COVERAGE
    CMAKE_EXE_LINKER_FLAGS_COVERAGE
    CMAKE_SHARED_LINKER_FLAGS_COVERAGE
    ENABLE_COVERAGE


-- Build files have been written to: /Users/clausklein/Workspace/cpp/cmake-init-shared-static/build/coverage

Executing workflow step 2 of 3: build preset "dev"

[2/2] Linking CXX static library libshared.a

Executing workflow step 3 of 3: test preset "dev"

Test project /Users/clausklein/Workspace/cpp/cmake-init-shared-static/build/coverage
No tests were found!!!
bash-3.2$ 

No, that’s not how inherits works for presets. The presets documentation makes clear the behavior with the following statement:

If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits array will be preferred.

That seems wrong to me!

For single inheritance, the last preset overwrites a value.
So I expected the same rules for multiple preset inheritance in a list.

What is the reason for this different rules?

Hi,

I believe they are considered two different concepts. The multiple inheritances is a “diamond shape”, but it seems to me you think about it as an inheritance chain.

Let’s say you have the preset “release”, “debug”, “env”. If you create “default” that inherits from “debug” which in turn inherits from “env” it is a chain of inheritance “default"→"debug"→"env”. However, if you put [“release”, “debug”] in the inheritance of “default”, “default” inherits from both at the same level. The rule of resolution has nothing to do with the inheritance overwrite and the order in which the inheritance priority should be decided is a new question entirely, so why not choose to put priority from left to right (from the start of the list to the end)?

I hope this answer helps you despite being a few months late!

I can’t follow your arguments?

For me, I would expect that [release, debug, coverage] will result in same cache values than

 release
    | 
  debug
    |
 coverage

The last set values overwrite the previous one.

If I understand correctly, you expect that [release, debug, coverage] would behave like if coverage was inheriting from debug, and debug from release. However, in a chain of inheritance, you do not have to choose between multiple values for a single variable. Let say coverage has CMAKE_BUILD_TYPE set to RELEASE, debug inherits from coverage and defines CMAKE_BUILD_TYPE to DEBUG. Then debug only contains one value for CMAKE_BUILD_TYPE, which is DEBUG. So if you would make “default” that inherits from debug, default would have DEBUG as value for CMAKE_BUILD_TYPE because there is no other choice.

If instead you define “default” as inheriting from [release, debug, coverage], then you are in a completely different situation, you might have 3 definitions of CMAKE_BUILD_TYPE and need to choose which one you are going to keep. Their choice was to put priority on the first one in the list, but they could have done the opposite.

I am not saying I agree or disagree with this choice, the argument is here only to try to explain that the rules do not differ between chained inheritance and multi-inheritance but rather that we have two different concepts that have their set of rules. I am trying to address your last question:

What is the reason for this different rules?

I hope this makes it clearer.

In the meantime, I am completely confused from cmake docu and discussion:

The preset will inherit all of the fields from the inherits presets by default
(except name, hidden, inherits, description, and displayName),
but can override them as desired.

Does this mean, there is no inheritance hierarchy possible with cmake presets?
If I want to merge values from different presets, I have to use an inheritance list?

I do not think so. Let’s say you have a preset “default” that inherits from “debug”. “debug” has CMAKE_BUILD_TYPE set to DEBUG, then “default” inherits the field CMAKE_BUILD_TYPE with value DEBUG. Therefore “debug” has the field CMAKE_BUILD_TYPE set to DEBUG. Thus, if you would now create a preset “custom” that inherits from “default”, “custom” would inherit CMAKE_BUILD_TYPE with value DEBUG from “default”. So, indirectly, “custom” inherits from “debug”, but “debug” is not in the inheritance field of “custom”. If it is not clear enough, consider this specification as a technicality and keep in mind that, “custom” inherits fields from “debug” unless they have been redefined in “default”, or in “custom”.

I hope it helps

IMHO, the CMake preset documentation is not easy to understand.

The handling of multiple inheritance is given, but without good reasons!

A good example and a clear specification can be found multiple inheritance and C3_linearization

and what is the exact order of constructors in a multiple and/or virtual inheritance situation?

Is it clearer for you now?

Yes, I work with multiple inheritance since years.

The problem is the missing consistent concept for cmake presets inheritance rules.

There must also respect the condition results!

see i.e.:

{
    "version": 8,
    "configurePresets": [
        {
            "name": "ninja-multi",
            "hidden": true,
            "generator": "Ninja Multi-Config",
            "binaryDir": "${sourceDir}/build"
        },
        {
            "name": "windows-only",
            "inherits": "ninja-multi",
            "hidden": true,
            "condition": {
                "type": "equals",
                "lhs": "${hostSystemName}",
                "rhs": "Windows"
            },
            "environment": {
                "PLATFORM": "WINDOWS64"
            }
        },
        {
            "name": "unix-only",
            "inherits": "ninja-multi",
            "hidden": true,
            "condition": {
                "type": "notEquals",
                "lhs": "${hostSystemName}",
                "rhs": "Windows"
            },
            "environment": {
                "PLATFORM": "POSIX64"
            }
        },
        {
            "name": "default",
            "displayName": "Ninja Multi-Config",
            "description": "Default build using Ninja Multi-Config generator",
            "condition": {
                "type": "const",
                "value": true
            },
            "inherits": [
                "windows-only",
                "unix-only"
            ]
        }
    ]
}

see too Condition field for workflow presets