cmake-presets-exploder 0.3.3
Automatically generate combinations of different CMake Presets options
Requires Python: >=3.9

# CMake Presets Exploder

[![PyPI version](https://img.shields.io/pypi/v/cmake-presets-exploder)](https://pypi.org/project/cmake-presets-exploder/)
[![Python versions](https://img.shields.io/pypi/pyversions/cmake-presets-exploder)](https://pypi.org/project/cmake-presets-exploder/)
[![Tests status](https://github.com/harrymander/cmake-presets-exploder/actions/workflows/test.yml/badge.svg?event=push)]()
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/harrymander/cmake-presets-exploder/blob/main/LICENSE)

A simple Python tool to automatically generate combinations of different CMake
Presets options.

## Table of contents

1. [Motivation and basic usage](#motivation-and-basic-usage)
2. [Other tools](#other-tools)
3. [Pre-commit hook](#pre-commit-hook)
4. [Installation](#installation)

## Motivation and basic usage

[CMake Presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html)
are a useful tool for specifying and sharing configuration, build, and test
settings for CMake Projects. Many editors and IDEs, such as Visual Studio Code,
can integrate with presets files.

However, some projects have a large number of different configuration and build
options, and CMake
[currently doesn't support](https://gitlab.kitware.com/cmake/cmake/-/issues/22538)
specifying multiple presets. For example, suppose you have a project with a
compile-time option called `ENABLE_FEATURE_X`, and you need to support
compilation with both GCC and Clang toolchains. You would need to create a
separate preset for each combination of feature and toolchain:

```json
{
  "version": 5,
  "configurePresets": [
    {
      "name": "base-config",
      "generator": "Ninja"
    },
    {
      "name": "configure-feature-x-on-gcc",
      "inherits": ["base-config"],
      "cacheVariables": {
        "ENABLE_FEATURE_X": "on"
      },
      "toolchainFile": "gcc"
    },
    {
      "name": "configure-feature-x-off-gcc",
      "inherits": ["base-config"],
      "cacheVariables": {
        "ENABLE_FEATURE_X": "off"
      },
      "toolchainFile": "gcc"
    },
    {
      "name": "configure-feature-x-on-clang",
      "inherits": ["base-config"],
      "cacheVariables": {
        "ENABLE_FEATURE_X": "on"
      },
      "toolchainFile": "clang"
    },
    {
      "name": "configure-feature-x-off-clang",
      "inherits": ["base-config"],
      "cacheVariables": {
        "ENABLE_FEATURE_X": "off"
      },
      "toolchainFile": "clang"
    }
  ]
}
```

Now, suppose you add a new feature `ENABLE_FEATURE_Y`, or want to configure
different build configurations such as `Debug`, `Release`, or support different
config generators (such as `Ninja` and `Visual Studio`). The number of presets
increases exponentially, which would be tedious and error-prone to maintain
manually.

This tool automates the 'combinatorial explosion' of presets given a template
file. For example, the above config file could be generated by creating a file
named `CMakePresetsMatrixTemplate.json`:

```json
{
  "version": 5,
  "configurePresets": [
    {
      "name": "base-config",
      "generator": "Ninja",
      "hidden": true
    }
  ],
  "vendor": {
    "exploder": {
      "version": 0,
      "presetGroups": {
        "configure": {
          "type": "configure",
          "inherits": ["base-config"],
          "parameters": {
            "feature-x": {
              "feature-x-on": "on",
              "feature-x-off": "off"
            },
            "toolchainFile": ["gcc", "clang"]
          },
          "templates": {
            "feature-x": {
              "cacheVariables": {
                "ENABLE_FEATURE_X": "$value"
              }
            }
          }
        }
      }
    }
  }
}
```

And then running

```bash
cmake-presets-exploder --output CMakePresets.json
```

Which will generate the following `CMakePresets.json`:

```json
{
  "version": 5,
  "configurePresets": [
    {
      "name": "base-config",
      "generator": "Ninja",
      "hidden": true
    },
    {
      "name": "configure-feature-x-feature-x-on",
      "hidden": true,
      "cacheVariables": {
        "ENABLE_FEATURE_X": "on"
      }
    },
    {
      "name": "configure-feature-x-feature-x-off",
      "hidden": true,
      "cacheVariables": {
        "ENABLE_FEATURE_X": "off"
      }
    },
    {
      "name": "configure-toolchainFile-gcc",
      "hidden": true,
      "toolchainFile": "gcc"
    },
    {
      "name": "configure-toolchainFile-clang",
      "hidden": true,
      "toolchainFile": "clang"
    },
    {
      "name": "configure-feature-x-on-gcc",
      "inherits": [
        "base-config",
        "configure-feature-x-feature-x-on",
        "configure-toolchainFile-gcc"
      ]
    },
    {
      "name": "configure-feature-x-on-clang",
      "inherits": [
        "base-config",
        "configure-feature-x-feature-x-on",
        "configure-toolchainFile-clang"
      ]
    },
    {
      "name": "configure-feature-x-off-gcc",
      "inherits": [
        "base-config",
        "configure-feature-x-feature-x-off",
        "configure-toolchainFile-gcc"
      ]
    },
    {
      "name": "configure-feature-x-off-clang",
      "inherits": [
        "base-config",
        "configure-feature-x-feature-x-off",
        "configure-toolchainFile-clang"
      ]
    }
  ]
}
```

The template file can also be written in YAML or TOML if YAML or TOML support is
installed and `yaml` or `toml` are passed to the `--loader` option on the CLI.

Jinja2 templating is also optionally supported. As a contrived example, the
`ENABLE_FEATURE_X` option above could be capitalised with the following:

```json
{
  "templates": {
    "feature-x": {
      "cacheVariables": {
        "ENABLE_FEATURE_X": "{jinja}{{ value | upper }}"
      }
    }
  }
}
```

(See [Installation](#installation) for how to install optional dependencies.)

Run `cmake-presets-exploder --help` for more information on the different CLI
options.

(More documentation to follow...)

## Other tools

This tool was directly inspired by Scott Dixon's
[TCPM (The CMake Preset Matrix)](https://github.com/thirtytwobits/the-cmake-preset-matrix),
which supports more complex configuration generation. The project is still under
active development, so if you find you have more complex needs, check out that
project!

## Pre-commit hook

A [pre-commit](https://pre-commit.com/) hook is provided for running the tool
when changes to the `CMakePresets.json` or `CMakePresetsMatrixTemplate.json`
files are committed, preventing the files from going out of sync from one
another. To use it, add the following to your `.pre-commit-config.yaml`:

```yaml
repos:
  - repo: https://github.com/harrymander/cmake-presets-exploder
    rev: v0.3.2  # replace with desired tag (must be at least v0.3.1)
    hooks:
      - id: cmake-presets-exploder
```

## Installation

Available from PyPI under `cmake-presets-exploder`, which provides a script with
the same name for running the tool. I recommend using a tool such as
[pipx](https://pipx.pypa.io/stable/installation/) or
[uv](https://docs.astral.sh/uv/getting-started/installation/) to install the
script into an isolated environment.

```bash
pipx install cmake-presets-exploder

# or
uv tool install cmake-presets-exploder
```

You can also just run the script directly without installing it to `PATH` with
`pipx run cmake-presets-exploder` or `uvx cmake-presets-exploder`.

To install optional YAML, TOML, and/or Jinja2 support, install with the `yaml`
and `jinja2` extras. E.g.

```bash
pipx install cmake-presets-exploder[yaml,toml,jinja2]

# or
uv tool install cmake-presets-exploder[yaml,toml,jinja2]
```

(Note that Python 3.11 and higher has built-in TOML support, so installing the
`toml` extra on those versions has no effect.)

To install the latest development version, pass the Git URL, e.g.

```bash
uv tool install git+https://github.com/harrymander/cmake-presets-exploder.git
```