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

188 lines
10 KiB
Markdown

# BreezeCLI
`breeze`: a global command line tool for projects built on the breeze SDK (the CMake template in `breeze/`).
It is written with [CLIForge](source/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](#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:
```json
{ "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.