Files
2026-10-24 09:53:48 -03:00

10 KiB

BreezeCLI

breeze: a global command line tool for projects built on the breeze SDK (the CMake template in breeze/). It is written with CLIForge and is itself a breeze project: build it with breeze/scripts/build.sh (or, once you have a breeze, breeze build).

Three things, kept apart on purpose:

What it is Where it lives Versioned by
CLI this program; one per machine, any number of versions installed ~/.breeze/cli/<version>/ its own version (breeze cli version)
SDK the template: breeze/, replaced as a whole on an update in each project breeze/VERSION (breeze sdk version)
Tools extensions a project installs from a repository <project>/.breeze/tools/<id>/ the tool's tags

The CLI is global and the SDK is per project, so one CLI works on projects that are on different SDK versions, and on the layouts before breeze/ existed (see Older SDKs).

Commands

Run breeze --help, or breeze <command> --help, for the exact options. The commands inside a project find it by walking up from the current directory.

Projects and modules

breeze project new <name> [--id] [--sdk]     a new project (no demo code, one executable module to start from)
breeze project version|name|id [--set]       the project's version, name (CMake project()) and id
breeze add exec|obj|test|example <name> [--id] [--deps ...]
breeze add lib <name> (--static|--shared) [--id] [--deps ...]
breeze delete <id> [--yes]                   refuses while another module depends on it
breeze rename <id> [--name] [--id]           renames the directory and/or id, fixing the DEPENDS that name it
breeze ls [--filter] [--exec] [--obj] [--lib] [--test] [--example] [--shared] [--static]
breeze deps of <module> [--recursive]        what it depends on
breeze deps on <module> [--recursive]        what depends on it

Modules are read from, and written as, the add_*_module(...) calls in each CMakeLists.txt; the CLI changes only what it must (a rename touches the id, the directories and the DEPENDS entries, nothing else in the files). Ids default to <name in snake case> plus _exe, _lib, _obj, _test or _example; --deps takes modules (by id or directory) or any other CMake target (fmt::fmt).

Building and running

breeze setup [--required|--all]
breeze build   [-P platform] [-A arch] [-C compiler] [-M mode] [-j jobs] [-p preset] [--cargs ...] [--bargs ...] [--no-configure]
breeze install [the same, and --no-build]
breeze run [id] [-p/-P/-A/-C/-M ...] [-x arguments ...]
breeze docs
breeze clean [--no-build] [--docs]
breeze defaults build platform|arch|compiler|preset|mode [--set <value>]
breeze defaults run [--set <id>]

These drive the SDK's own scripts (breeze/scripts/*.sh, .bat on Windows), so the environment they set up (the pinned Ninja, the MSVC environment) is the one used. A platform, architecture and compiler name a preset (-P linux -A x64 -C gcc is linux-x64-gcc, ignoring case and punctuation: ClangCl finds clang-cl); whatever is missing comes from the project's defaults, then this machine. --set none clears a default. Defaults are kept in .breeze/defaults.json, meant to be committed.

--cargs and --bargs take everything up to the next option, dashes included: breeze build --cargs -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -j 8. --bargs (and --no-configure) run cmake --build on the directory the scripts configured, which the SDK's scripts have no way to do; so there must be a configured build first, and on Windows these two are not supported yet (the MSVC environment comes from the scripts). -j reaches the build through CMAKE_BUILD_PARALLEL_LEVEL.

run with no id runs breeze defaults run, or the project's only executable. -M is accepted for run and ignored: the SDK keeps one build directory per preset.

The CLI itself

breeze cli version | ls
breeze cli install <version> | pick <version> | uninstall <version>

Versions live in ~/.breeze/cli/<version>/ ($BREEZE_HOME moves ~/.breeze); cli/current names the picked one. Every other command first hands itself over to the picked version, so whichever breeze your shell finds is the picked one, and cli ... commands always run in the version you started. Put ~/.breeze/bin on PATH: the first cli install puts a copy of breeze there. Installing this program's own version needs nothing else:

./breeze cli install $(./breeze cli version)

Other versions come from the CLI source: breeze config cli-source --set <URL or directory>, expected to hold <version>/breeze-<platform>-<arch>[.exe] (platform linux, macos, windows; arch x64, arm64, arm32, x86). A URL is downloaded with curl. What is installed must run and say it is that version, or nothing is kept.

SDK versions

breeze sdk version | update | upgrade | migrate <version>

An SDK version is a snapshot of the whole template: breeze/ (the SDK proper) plus the files a project starts from. Versions come from the SDK source: breeze config sdk-source --set <git repository (URL or path) | directory>. A repository's tags are the versions (v1.2.0 or 1.2.0); a directory has one subdirectory per version (1.0.0/, 1.1.0/). The version of an SDK is the one line in breeze/VERSION, which the repository's tag should match.

  • update looks at the source and says what is there, and whether this project is behind.
  • upgrade moves to the newest version with the same major version.
  • migrate <version> moves to exactly that version, any way, across majors; latest is the newest release.

Moving replaces breeze/ (the old one is kept in .breeze/backup/), adds to project/Configuration.cmake any setting the new SDK has and the project's file lacks, and says which dot files (.clang-format, ...) now differ from the SDK's. Nothing else of the project is touched.

Tools

breeze tool install <repo> | uninstall <id> | update <id> | upgrade <id> | migrate <id> <version> | ls
breeze tool <id> <arguments...>

A tool is a git repository (or, while it is being written, a directory) with a breeze-tool.json at its root:

{ "id": "resembed", "version": "1.0.0", "description": "Embeds resources", "entry": "bin/resembed.sh" }

entry may be { "posix": "...", "windows": "..." }. Installed, it is copied into .breeze/tools/<id>/ and recorded in .breeze/tools.json (commit both); breeze tool <id> ... then runs the entry with those arguments, in the project's directory, with BREEZE_PROJECT_ROOT, BREEZE_SDK_DIR, BREEZE_SDK_VERSION, BREEZE_TOOL_ID, BREEZE_TOOL_DIR, BREEZE_CLI, BREEZE_HOME and BREEZE_EDITOR set. Everything after the id belongs to the tool, --help included, and its exit code is the CLI's. So breeze tool san asan enable <module> is whatever the san tool does with asan enable <module>. install, uninstall, update, upgrade, migrate and ls can't be tool ids.

Configuration

breeze config editor [--set]       the editor tools open files with (BREEZE_EDITOR in their environment)
breeze config sdk-source [--set]   where SDK versions come from
breeze config cli-source [--set]   where CLI versions come from

Kept in ~/.breeze/config.json; BREEZE_EDITOR, BREEZE_SDK_SOURCE and BREEZE_CLI_SOURCE in the environment win. --set none clears one.

Older SDKs

The CLI asks each project's SDK where things are instead of assuming:

  • breeze/ layout (with or without breeze/VERSION): everything above.
  • The flat layout (scripts/, cmake/, Configuration.cmake at the root): ls, deps, add, rename, delete, project name|version|id, build, run, install, clean work through that layout's scripts and presets; what an old SDK lacks is said when asked for (add obj on an SDK without objects modules; a preset on one without presets). breeze sdk migrate <version> moves a flat project to the breeze/ layout: the old CMakeLists.txt, Configuration.cmake, cmake/, scripts/ and .githooks/ go to .breeze/backup/, and the project's name, version, description, languages, C++ standard and any setting it had changed go into project/Configuration.cmake. The project's own README.md, sources and dot files stay.

Building and testing

breeze/scripts/build.sh --preset linux-x64-gcc --cargs -DBUILD_TESTS=ON
out/build/linux-x64-gcc/bin/tests/core_tests     # unit tests
tests/e2e.sh                                     # drives the built program through every command group

tests/e2e.sh needs cmake, a compiler and git, and no network: it builds its own SDK source, tool and releases in a temporary directory.

Layout: source/CLIForge (the command line library), source/BreezeCore (everything the commands do; unit tested), source/Breeze (the breeze executable: command registration only), source/tests/CoreTests.

nlohmann/json is used for the files the CLI keeps: an installed 3.11 or newer is found, else the release is fetched (pinned by hash) when you configure.

Notes on CLIForge

CLIForge is used as it came, with these changes to source/CLIForge, to be taken back into the library:

  • CliError::kind() returned kind() (endless recursion); it now returns m_kind.
  • #pragma once removed from the four .cpp files (a compiler warning each).

Things to know when writing commands with it: an action's return value is ignored (so Commands.cpp leaves exit codes in breeze::cli::exitCode() for main()); an exception that is not a CliError leaves Engine::run; a token starting with - is always read as an option (which is why main() rewrites the values of --cargs, --bargs and -x to the --cargs=-DX=1 form, see breeze/Arguments.hpp); parameters are required, so run [id] is two commands.

Status

Built and tested on Linux with gcc and clang (x64) and gcc for arm64 (run under qemu): the unit tests and the 94 checks of tests/e2e.sh pass. Downloading a CLI release over HTTP was tried against a local web server with curl; git sources were tried with local repositories only. MinGW compiles the Windows code (process creation, cmd.exe scripts, paths) but nothing has been run on Windows, and nothing has been built on macOS.