Add canon: P1204R0 project scaffolding in C++23
canon new lib|exe creates a project following P1204R0 (Canonical Project Structure) with a .canon.toml manifest and a CMakeLists.txt whose managed block is rendered from that manifest. Generators return a plan; the executor checks every operation before writing, so conflicts write nothing and --dry-run writes nothing. Tests: unit tests next to each source file, golden copies of generated projects, and an end-to-end build of both project kinds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# canon
|
||||
|
||||
Scaffolds C++ projects that follow [P1204R0](https://wg21.link/p1204r0)
|
||||
(Canonical Project Structure). Written in C++23, no third-party dependencies.
|
||||
|
||||
```bash
|
||||
canon new lib libhello # library: libhello/hello/hello.{hpp,cpp,test.cpp} + tests/basics
|
||||
canon new exe hello # executable: hello/hello/hello.cpp
|
||||
canon new lib libhello --dry-run # show the plan, write nothing
|
||||
canon new exe hello --dir ~/src # create the project somewhere else
|
||||
```
|
||||
|
||||
Every generated project builds and passes its tests out of the box:
|
||||
|
||||
```bash
|
||||
cd libhello
|
||||
cmake -S . -B build
|
||||
cmake --build build
|
||||
ctest --test-dir build --output-on-failure
|
||||
```
|
||||
|
||||
## Build canon
|
||||
|
||||
```bash
|
||||
cmake -S . -B build
|
||||
cmake --build build
|
||||
ctest --test-dir build --output-on-failure
|
||||
cmake --install build --prefix ~/.local # puts canon in ~/.local/bin
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
Generators never touch the filesystem. They return a **plan** (a list of
|
||||
`make_directory` / `create_file` operations) and the **executor** applies it.
|
||||
The executor checks every operation first, so a conflict writes nothing, and
|
||||
`--dry-run` is just "check, don't write". canon never overwrites files.
|
||||
|
||||
Each project gets a **`.canon.toml` manifest** recording what canon declared.
|
||||
The block between `# >>> canon:managed >>>` and `# <<< canon:managed <<<` in
|
||||
`CMakeLists.txt` is rendered entirely from that manifest. Upcoming `canon add`
|
||||
commands will update the manifest and re-render the block, so running a command
|
||||
twice gives the same result. Anything outside the block belongs to you.
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `canon/canon.cpp` | `main`: wires the command line to the generators and executor |
|
||||
| `canon/cli.*` | argument parsing |
|
||||
| `canon/name.*` | project name rules (`lib` prefix, stem, namespace) |
|
||||
| `canon/generate.*` | builds the manifest and plan for `canon new`; file templates |
|
||||
| `canon/manifest.*` | the manifest model and its TOML output |
|
||||
| `canon/cmake.*` | renders `CMakeLists.txt` and the managed block from a manifest |
|
||||
| `canon/plan.*` | operations and plans (pure data) |
|
||||
| `canon/executor.*` | checks and applies a plan |
|
||||
| `canon/template.*` | `{{key}}` substitution |
|
||||
|
||||
## Tests
|
||||
|
||||
- **Unit tests**: `canon/<unit>.test.cpp`, next to the code they cover.
|
||||
- **Golden tests**: `tests/golden/` holds the exact expected output for
|
||||
`libhello` and `hello`. After an intentional template change, regenerate the
|
||||
copies and review the diff:
|
||||
`cmake -DCANON=build/canon -DWORK_DIR=build/tests/golden -DUPDATE=ON -P tests/golden/check.cmake`
|
||||
- **End to end**: `tests/e2e/` scaffolds both kinds, then configures, builds and
|
||||
runs their tests. Skip it with `ctest -LE e2e`.
|
||||
|
||||
## Roadmap
|
||||
|
||||
1. ✅ `new lib`, `new exe`, plan/apply, `--dry-run`, manifest
|
||||
2. `add module`, `add unit-test`: read `.canon.toml` and re-render the managed block
|
||||
3. `add target`, `add test`, `add dep`, `doctor` (check a project against P1204R0)
|
||||
4. User template overrides
|
||||
Reference in New Issue
Block a user