Files

195 lines
16 KiB
Markdown
Raw Permalink Normal View History

2026-05-18 09:41:16 +08:00
# C++ Template Design Source of Truth
This document is the implementation-facing source-of-truth index for the C++ project template redesign. It does not replace the detailed drafts; it identifies which parts are normative and records the constraints implementation agents must preserve.
## Normative Inputs
- `.sisyphus/plans/cpp-template-redesign.md` is the read-only executable implementation plan owned by Atlas/Orchestrator.
- `.sisyphus/drafts/cpp-template-redesign.md` is normative for consolidated dependency, tooling, style, preset, hook, install/export, FuzzTest, and QA decisions.
- `.sisyphus/drafts/cmake-api-design.md` is normative for the CMake helper API, dependency loading policy, install/export behavior, and FuzzTest patch/dependency policy.
Older research notes, review findings, and open-question discussions in the drafts are historical unless their decisions appear in the latest consolidated or final-resolution sections.
## Fixed Project Constraints
- Primary standard: C++14.
- C++11: best-effort/smoke compatibility only, not the default blocking path.
- C++17: isolated opt-in path, primarily for the `fuzz` preset and FuzzTest.
- Toolchains: Clang/GCC only; no MSVC support.
- Dependency loading: local CPM archives under `3rd/archives/` with `URL_HASH SHA256=...`; no hidden remote fallback.
- Offline strategy: local archive/source paths and hash verification, not a global offline mode switch.
- FuzzTest: required by the redesign only in the isolated `fuzz` preset/tree, with pristine upstream archive, patch files, script-driven apply/verify workflow, and no protobuf-backed FuzzTest path.
- HTTP baseline: cpp-httplib. `libhv` is excluded from v1 unless explicitly re-approved.
- Removed/not allowed by default: `magic_enum`, protobuf-backed FuzzTest integration, global offline mode, CMake `format`/`check_format`/`run_clang_tidy` targets.
2026-05-18 09:41:16 +08:00
## Normative Template C++ Style
These template-specific style rules intentionally supersede generic agent C++ defaults wherever they conflict:
- clang-format 17+ is required.
- Indentation: 4 spaces, no tabs.
- Column target: 120.
- Functions, classes, structs, enums, and namespaces use Allman braces.
- Control statements and lambdas use attached braces.
- Include order: corresponding/main header first, then project/generated quoted headers, then C headers, C++ standard headers, known third-party headers, OS/system headers, and other angle headers.
- Private/protected members use `m_` + `snake_case`.
- Static non-const members and function-local statics use `s_` + `snake_case`.
- Constants, including local constants, use `kCamelCase`.
- Public struct fields and local variables use `snake_case`.
- Types use `PascalCase`; functions and methods use `camelCase`; namespaces use `lower_case`.
- clang-tidy is strict from the start with warnings-as-errors for enabled checks.
- `.clang-format` is the formatter source-of-truth for enforceable whitespace, brace, column, pointer/reference, return-type, and include-order rules. `scripts/format.py --check` verifies selected C/C++ files and `scripts/format.py --fix` rewrites them; naming rules such as `m_`, `s_`, and `kCamelCase` remain documented conventions because clang-format cannot enforce identifier names.
## Implementation Handoff Rule
Implementation agents must treat the latest consolidated/final-resolution draft sections as authoritative. If an older section conflicts with the constraints above, the older section is historical/superseded and must not drive implementation.
## v1 Dependency Inventory (Frozen)
Source of truth: `cmake/deps/versions.cmake`.
| Name | Version | C++ Gate | CMake Targets | Presets | Archive |
|------|---------|----------|---------------|---------|---------|
| spdlog | 1.15.3 | C++11 | `spdlog::spdlog` | debug,release,fuzz | spdlog-1.15.3.tar.gz |
| fmt | 11.0.2 | C++11 | `fmt::fmt` | debug,release,fuzz | fmt-11.0.2.tar.gz |
| nlohmann/json | 3.11.3 | C++11 | `nlohmann_json::nlohmann_json` | debug,release,fuzz | nlohmann-json-3.11.3.tar.gz |
| toml11 | 4.4.0 | C++11 | `toml11::toml11` | debug,release,fuzz | toml11-4.4.0.tar.gz |
| CLI11 | 2.4.2 | C++11 | `CLI11::CLI11` | debug,release,fuzz | CLI11-2.4.2.tar.gz |
| asio (standalone) | 1.30.2 | C++11 | `asio::asio` | debug,release,fuzz | asio-1.30.2.tar.gz |
| concurrentqueue | 1.0.4 | C++11 | `concurrentqueue::concurrentqueue` | debug,release,fuzz | concurrentqueue-1.0.4.tar.gz |
| ghc::filesystem | 1.5.14 | C++11 | `ghcFilesystem::ghc_filesystem` | debug,release,fuzz | ghc-filesystem-1.5.14.tar.gz |
| expected-lite | 0.8.0 | C++11 | `nonstd::expected-lite` | debug,release,fuzz | expected-lite-0.8.0.tar.gz |
| cpp-httplib | 0.18.3 | C++11 | `httplib::httplib` | debug,release,fuzz | cpp-httplib-0.18.3.tar.gz |
| GTest/GMock (normal) | 1.16.0 | C++14 | `GTest::gtest`, `GTest::gtest_main`, `GTest::gmock`, `GTest::gmock_main` | debug,release | googletest-1.16.0.tar.gz |
| Benchmark | 1.7.1 | C++11 | `benchmark::benchmark`, `benchmark::benchmark_main` | debug,release | benchmark-1.7.1.tar.gz |
| GTest/GMock (fuzz) | 1.17.0 | C++17 | `GTest::gtest`, `GTest::gmock` | fuzz | googletest-1.17.0.tar.gz |
| Abseil | 20260107.1 | C++17 | `absl::*` | fuzz | abseil-cpp-20260107.1.tar.gz |
| RE2 | 2025-11-05 | C++17 | `re2::re2` | fuzz | re2-2025-11-05.tar.gz |
| ANTLR4 runtime | 4.13.2 | C++17 | `antlr4_static` | fuzz | antlr4-4.13.2.tar.gz |
| FuzzTest | 2026-02-19 | C++17 | `fuzztest::fuzztest` | fuzz | fuzztest-2026-02-19.tar.gz |
2026-05-18 09:41:16 +08:00
### Excluded from v1
- `libhv` — excluded; cpp-httplib is the v1 HTTP baseline.
- `magic_enum` — removed per user request.
- `protobuf` — FuzzTest protobuf-backed paths forbidden.
- `oatpp`, `microprofile`, `breakpad` — removed from default template.
2026-05-18 09:41:16 +08:00
- `crow` — C++17-only; cpp-httplib covers C++14 HTTP.
- `BLAKE3` — CMake propagates `cxx_std_20` PUBLIC.
- `bitsery` — repo/path verification unresolved.
- `outcome` — replaced by expected-lite.
- `OpenSSL` — rejected as default dependency; too heavy.
### Core Runtime Defaults
The `cc_library()`, `cc_executable()`, `cc_test()`, `cc_benchmark()`, and `cc_fuzz()` helpers automatically link the Core runtime defaults listed above from `spdlog` through `cpp-httplib`. Static/shared libraries receive the aggregate as `PUBLIC`, interface libraries as `INTERFACE`, and executable-like targets as `PRIVATE`. This includes `nlohmann_json::nlohmann_json` in the fuzz lane while keeping protobuf-backed FuzzTest integration out of scope.
2026-05-18 09:41:16 +08:00
### GTest Version Isolation
Normal C++14 builds use GTest/GMock 1.16.0. Fuzz/C++17 builds use GTest/GMock 1.17.x.
These must never coexist in the same build tree. The dependency modules enforce this separation.
### Archive & Hash Notes
- All v1 SHA256 hashes are concrete in `3rd/README.md` and `cmake/deps/versions.cmake`. The table above lists names and versions; `3rd/README.md` is the user-facing hash source of truth.
- FuzzTest SHA256: `1c6e04065eb988e2c99613369db8294aa58429d392bf479740b237f1255204ef`.
- CPM `URL_HASH SHA256=...` enforcement in per-dependency modules consumes `versions.cmake` variables.
2026-05-18 09:41:16 +08:00
- Abseil/RE2/ANTLR4 versions must match the pinned FuzzTest release's `BuildDependencies.cmake`; re-verify when rebasing.
## CMake Preset Contract (T6)
This section defines the visible CMake preset contract that later implementation tasks must preserve. T6 is a contract-only step: the repository currently has no root `CMakePresets.json`, and T11 owns the full preset implementation and configure/build/test validation. If T11 adds hidden/internal presets, each hidden preset must be minimal, justified in this document, and must not expand the visible user-facing set.
Normative compatibility constraints:
- CMake preset file compatibility is separate from the project CMake code minimum. Project CMake code remains minimum 3.19, but a single root `CMakePresets.json` containing configure, build, and test presets should use CMakePresets schema version 2, which requires CMake 3.20+ for preset users.
- If strict CMake 3.19 preset-file compatibility is required, schema version 1 supports configure presets only; build/test behavior must then be invoked manually or documented outside presets. Do not add a `$schema` field while the project supports older preset file versions.
2026-05-18 21:08:58 +08:00
- Every visible configure preset must use the Unix Makefiles generator.
2026-05-18 09:41:16 +08:00
- Every visible configure preset must set `CMAKE_EXPORT_COMPILE_COMMANDS=ON`.
- Every visible configure preset must use `binaryDir` equivalent to `${sourceDir}/build/${presetName}`; the documented shorthand is `build/${presetName}`.
- The visible configure preset names are exactly `debug`, `release`, `asan`, and `fuzz`. No MSVC presets are part of v1.
2026-05-18 09:41:16 +08:00
- Build presets should mirror each visible configure preset name and build its matching configure preset.
- Test presets should exist only where tests are part of the lane contract: `debug`, `release`, `asan`, and `fuzz` for deterministic fuzz smoke.
2026-05-18 09:41:16 +08:00
| Preset | Purpose | C++ standard | Build type | Tests, benchmark, fuzz behavior | Sanitizer flags intent | Build directory | Compile commands | Dependency lane |
|--------|---------|--------------|------------|-------------------------------|------------------------|-----------------|------------------|-----------------|
| `debug` | Default developer lane and fast local checks. | C++14 | `Debug` | Tests and benchmarks enabled; fuzz is requested by default but auto-skips because the lane is not Clang + C++17 + isolated GTest. | None; sanitizer behavior belongs only to `asan`. | `build/debug` | `ON` | Core deps plus normal GTest/GMock 1.16.0 and Benchmark 1.7.1. |
| `release` | Optimized normal build lane. | C++14 | `Release` | Tests and benchmarks enabled; fuzz is requested by default but auto-skips unless the isolated fuzz lane requirements are met. | None. | `build/release` | `ON` | Core deps plus normal GTest/GMock 1.16.0 and Benchmark 1.7.1. |
| `asan` | AddressSanitizer validation lane. | C++14 | `Debug` | Tests enabled; benchmarks disabled to avoid noisy sanitizer runs; fuzz is requested but auto-skips. | Preset sets `CC_ENABLE_ASAN=ON`; `cc_project()` applies AddressSanitizer compile/link flags. | `build/asan` | `ON` | Same normal dependency lane as `debug`; no FuzzTest stack. |
2026-05-18 09:41:16 +08:00
| `fuzz` | Isolated deterministic fuzz smoke and FuzzTest development lane. | C++17 | `Debug` | Normal tests are not the focus; deterministic fuzz smoke is registered in CTest with fixed seed/run count; long fuzzing stays direct executable invocation outside default CTest. Benchmarks disabled. | No default sanitizer requirement in the contract; sanitizer fuzzing, if added later, must be explicit and not affect normal lanes. | `build/fuzz` | `ON` | Core deps plus isolated FuzzTest lane: GTest/GMock 1.17.0, Abseil 20260107.1, RE2 2025-11-05, ANTLR4 runtime 4.13.2, FuzzTest 2026-02-19. Requires Clang in implementation. |
Implementation notes for T11 and later tasks:
- Preset options make lane behavior explicit through cache variables while keeping user `CMakeLists.txt` declarative. Normal lanes request tests/benchmarks/fuzz by default; fuzz target creation is capability-gated and auto-skips unless the build is Clang + C++17 with the isolated fuzz GTest lane.
- `debug` remains the fast default path for developer checks; it must not silently enable sanitizer flags or materialize FuzzTest when fuzz is unavailable.
- `fuzz` is the only v1 lane that materializes FuzzTest. The `fuzz` lane may require Clang for its specialized runtime. The fuzz lane must not load normal GoogleTest/GMock 1.16.0 into the same build tree as FuzzTest's GoogleTest/GMock 1.17.0 lane.
2026-05-18 09:41:16 +08:00
- Hidden presets are not required by this contract. If T11 uses a hidden base to remove duplication, keep it internal, do not expose extra user presets, and document the reason here and in evidence.
- Lane option cache variables are set in all presets: `CC_ENABLE_BENCHMARKS` (ON in debug/release, OFF in asan/fuzz), and `CC_ENABLE_ASAN` (ON only in asan).
- `cc_project()` computes `CC_ENABLE_FUZZTEST_AVAILABLE` before including FuzzTest dependencies. Unsupported fuzz requests skip cleanly instead of failing default configure.
- Fuzz targets registered through `cc_fuzz()` have label `fuzz_smoke` and are created only when `CC_ENABLE_FUZZTEST_AVAILABLE` is true. Normal `cc_test()` targets are skipped in the fuzz lane.
2026-05-18 09:41:16 +08:00
### Fuzz Smoke Testing
The `fuzz` preset runs FuzzTest targets in unit-test mode (`FUZZTEST_FUZZING_MODE=OFF`), which executes fuzz tests as deterministic GTest-compatible tests rather than continuous fuzzing campaigns.
#### Deterministic CTest Smoke
All fuzz targets registered via `cc_fuzz()` appear in CTest with the `fuzz_smoke` label:
```bash
# Run all fuzz smoke tests
ctest --test-dir build/fuzz --output-on-failure -L fuzz_smoke
```
Determinism is ensured through two mechanisms:
1. **`.WithSeeds()`** in the fuzz test source: provides concrete fixed inputs that always run. In unit-test mode, FuzzTest runs each seed once as a GTest test case.
2. **`FUZZTEST_PRNG_SEED`** environment variable: set via `SMOKE_ENV` in `cc_fuzz()` registration. Makes any additional PRNG-driven input generation reproducible across runs.
The `cc_fuzz()` helper supports three smoke-test customization keywords:
| Keyword | Purpose | Example |
|---------|---------|----------|
| `SMOKE_ARGS` | Arguments for the CTest smoke command | (empty by default) |
| `SMOKE_ENV` | Environment variables for CTest smoke | `FUZZTEST_PRNG_SEED=42` |
| `SMOKE_TIMEOUT` | CTest timeout in seconds | 30 (default) |
#### Long-Running Fuzzing (Outside CTest)
Continuous fuzzing campaigns use the fuzz executable directly with `--fuzz` flags. These are NOT registered in CTest because they run indefinitely.
**Important:** `--fuzz`, `--fuzz_for`, and `--time_limit_per_input` require a fuzzing-mode build (`FUZZTEST_FUZZING_MODE=ON`). The default `fuzz` preset CTest smoke runs in unit-test mode (`FUZZTEST_FUZZING_MODE=OFF`) using `.WithSeeds()` and `FUZZTEST_PRNG_SEED`. To use continuous fuzzing flags, rebuild with `FUZZTEST_FUZZING_MODE=ON` or invoke a fuzzing-mode binary directly.
```bash
# List available fuzz tests
./build/fuzz/calc_fuzz --gtest_list_tests
# Run a specific fuzz test in fuzzing mode with safety limits
# (requires FUZZTEST_FUZZING_MODE=ON build)
./build/fuzz/calc_fuzz \
--fuzz=CalcFuzz.AddNeverCrashes \
--time_limit_per_input=30s \
--fuzz_for=60s
```
Safety flags for long fuzzing (fuzzing mode only):
- `--fuzz_for=<duration>`: total fuzzing time (e.g., `60s`, `5m`).
- `--time_limit_per_input=<duration>`: max time per input before timeout.
- These flags have no effect in unit-test mode (`FUZZTEST_FUZZING_MODE=OFF`).
## T9 clang-tidy Policy
- `scripts/clang_tidy.py` is the script-first strict clang-tidy entry point; CMake must not define `tidy`, `run_clang_tidy`, `clang-tidy`, or `check_tidy` targets.
- The runner requires clang-tidy 17+ and a `compile_commands.json` in `--build-dir` before real execution. The default build directory is `build/debug`.
- Debug fast checks configure with `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` so `scripts/dev_check.py --fast` can feed clang-tidy without relying on CMake-integrated tidy targets.
- The runner uses the shared project path filter and checks only project C/C++ files, skipping build outputs, vendored trees, generated/task-state directories, `.sisyphus`, `.opencode`, `.git`, and `3rd/`.
2026-05-18 09:41:16 +08:00
- Warnings are errors through both `.clang-tidy` (`WarningsAsErrors: '*'`) and the runner CLI (`--warnings-as-errors=*`). Each file is invoked separately so failures report exact `failed_file:` paths.
## T10 Local Git Hook Workflow
- Git hooks are local and opt-in. The template does not automatically install hooks during configure, build, test, formatting, clang-tidy, or other developer checks.
- `.githooks/pre-commit` is the repository hook entry point. It resolves the repository root with `git rev-parse --show-toplevel`, changes to that root, and delegates to `python3 scripts/pre_commit.py`.
- `scripts/pre_commit.py` prints and runs the exact fast-check delegation: `python3 scripts/dev_check.py --fast`. Its `--dry-run` mode prints the same command plan without executing checks.
- `scripts/setup_hooks.py` is the only setup entry point for hook installation. Non-dry-run setup verifies `.githooks/pre-commit`, ensures it is executable, and runs only `git config --local core.hooksPath .githooks`.
- `scripts/setup_hooks.py --dry-run` prints the intended local-only git config command without mutating git config. `scripts/setup_hooks.py --status` reports hook file existence, executable state, and the local `core.hooksPath` value when available.
- Global or user git configuration must never be modified by the template hook workflow.