Files

273 lines
8.2 KiB
Markdown
Raw Permalink Normal View History

2026-05-18 09:41:16 +08:00
# C++ Project Template
A CMake-based C++ project template with vendored dependencies, offline builds, and developer checks.
## Prerequisites
| Tool | Minimum Version | Notes |
| ------------ | --------------- | ------------------------------- |
| CMake | 3.20 | Build system |
2026-05-18 21:08:58 +08:00
| make | system | Generator used by presets |
2026-05-18 09:41:16 +08:00
| Python | 3.8+ | Helper scripts |
| C++ compiler | C++14 | GCC or Clang for default lanes |
| clang-format | 17+ | Format checking |
| clang-tidy | 14+ | Static analysis |
| Clang | C++17 capable | Fuzz lane only |
The fuzz preset requires Clang and C++17. All other presets work with any
C++14 compiler.
## Quick Start
After a fresh clone, all dependencies are already vendored in `3rd/archives/`.
No network access is needed for the default build.
```bash
# Configure and build
cmake --preset debug
cmake --build --preset debug
# Run tests
ctest --preset debug
# Run the full fast check (format, build, test, clang-tidy)
python3 scripts/dev_check.py --fast
```
## Daily Workflow
### First-time setup
```bash
python3 scripts/setup_hooks.py
cmake --preset debug
cmake --build --preset debug
```
The hook runs `python3 scripts/dev_check.py --fast` on every commit. Build at
least once so `compile_commands.json` exists for clang-tidy.
### Edit and test loop
```bash
# Build and run tests (pick one)
cmake --build --preset debug && ctest --preset debug --output-on-failure
cmake --build --preset debug --target run_tests
# Run benchmarks
cmake --build --preset debug --target run_benchmarks
```
`run_tests` and `run_benchmarks` are aggregate targets that drive all registered
test and benchmark executables. See `docs/cmake-api.md` for helper API details.
### Fast and full checks
```bash
# Format + build + test + clang-tidy
python3 scripts/dev_check.py --fast
# Individual verification subcommands
python3 scripts/dev_check.py cmake-helper-fixtures
python3 scripts/dev_check.py no-network-default
```
Run `python3 scripts/dev_check.py --help` for the full list. CI runs all
subcommands independently; see the Developer Checks table below for details.
### Sanitizer builds
```bash
cmake --preset asan && cmake --build --preset asan
ctest --preset asan --output-on-failure
```
Sanitizer presets disable benchmarks to avoid false positives from
instrumented benchmark code.
### Fuzz smoke and long fuzzing
The fuzz preset requires Clang. Smoke tests run in deterministic unit-test mode:
```bash
cmake --preset fuzz && cmake --build --preset fuzz
ctest --preset fuzz --output-on-failure -L fuzz_smoke
```
Continuous fuzzing requires a separate build with `FUZZTEST_FUZZING_MODE=ON`
and runs the executable directly:
```bash
./build/fuzz/calc_fuzz --fuzz=CalcFuzz.AddNeverCrashes --fuzz_for=60s
```
## Build Presets
| Preset | Standard | Type | Tests | Notes |
| ------------- | -------- | ------- | ----- | ------------------------ |
| `debug` | C++14 | Debug | yes | Default developer lane |
| `release` | C++14 | Release | yes | Optimized build |
| `asan` | C++14 | Debug | yes | AddressSanitizer |
| `fuzz` | C++17 | Debug | fuzz smoke | FuzzTest, requires Clang |
2026-05-18 09:41:16 +08:00
Example:
```bash
cmake --preset release
cmake --build --preset release
ctest --preset release
```
## Offline Build Guarantee
Default build lanes (`debug`, `release`, `asan`) and the
2026-05-18 09:41:16 +08:00
`fuzz` lane resolve all dependencies from committed archives in `3rd/archives/`.
CPM loads each dependency via `URL` pointing at the local tarball with
`URL_HASH SHA256=...` integrity verification. No remote fetch occurs during the
build.
This is enforced by the `no-network-default` and `no-network-fuzztest-lane`
checks, which run CMake under invalid HTTP/HTTPS proxies with an isolated CPM
source cache:
```bash
python3 scripts/dev_check.py no-network-default
python3 scripts/dev_check.py no-network-fuzztest-lane
```
There is no global offline mode or `CMAKE_DISABLE_FIND_PACKAGE` flag. The
offline guarantee comes from CPM archive mode, not from suppressing network
access at the CMake level.
See `3rd/README.md` for archive policy, the full inventory table, and update
instructions.
## Developer Checks
`scripts/dev_check.py` runs structured verification subcommands:
| Command | What it checks |
| ---------------------------- | -------------------------------------------------- |
| `--fast` | Format, debug configure/build/test, clang-tidy |
| `no-network-default` | Configure (optionally build) under invalid proxies |
| `no-network-missing-archive` | Negative check for missing archive detection |
| `no-network-fuzztest-lane` | Fuzz lane configure+build under invalid proxies |
| `gtest-isolation` | GTest version isolation (normal=1.16, fuzz=1.17) |
| `fuzztest-optional-features` | FuzzTest optional deps stay disabled |
| `install-consumer` | Install and consume as a subproject |
| `cmake-helper-fixtures` | CMake helper API fixture tests |
Dry-run any command to see the plan without executing:
```bash
python3 scripts/dev_check.py --fast --dry-run
```
## Dependency Management
`scripts/fetch_deps.py` manages the vendored archive inventory:
```bash
# List dependency status
python3 scripts/fetch_deps.py --list
# Verify all archives match recorded hashes (offline)
python3 scripts/fetch_deps.py --check
# Download a specific dependency
python3 scripts/fetch_deps.py --fetch spdlog
# Update pending hashes in versions.cmake
python3 scripts/fetch_deps.py --update-hashes
```
Hashes and URLs are defined in `cmake/deps/versions.cmake`. The full inventory
of 26 dependencies with SHA256 values is in `3rd/README.md`.
2026-05-18 09:41:16 +08:00
2026-05-18 21:08:58 +08:00
## Project-local CMake
2026-05-18 20:43:30 +08:00
2026-05-18 21:08:58 +08:00
Project-local CMake binaries are committed under architecture-specific
2026-05-18 20:43:30 +08:00
directories:
2026-05-18 21:08:58 +08:00
- `scripts/bin/linux-x86_64/cmake/` for Linux amd64/x86_64
- `scripts/bin/linux-aarch64/cmake/` for Linux arm64/aarch64
2026-05-18 20:43:30 +08:00
2026-05-18 21:08:58 +08:00
The `scripts/bin/cmake` wrapper selects the current Linux architecture and runs
the matching bundled CMake. Put `scripts/bin` at the front of `PATH` when you
want the project-local CMake to take precedence over system installations:
2026-05-18 20:43:30 +08:00
```bash
export PATH="$PWD/scripts/bin:$PATH"
cmake --version
```
2026-05-18 21:08:58 +08:00
Presets use the `Unix Makefiles` generator and therefore use the system `make`
selected by CMake. Only Linux amd64 and arm64 are supported by the bundled CMake
layout. Other platforms should use system-installed CMake.
2026-05-18 20:43:30 +08:00
2026-05-18 09:41:16 +08:00
## Git Hooks
Opt-in pre-commit hooks run format checks and fast developer validation:
```bash
# Install hooks
python3 scripts/setup_hooks.py
# Check status
python3 scripts/setup_hooks.py --status
# Preview without installing
python3 scripts/setup_hooks.py --dry-run
```
## Troubleshooting
### Hash mismatch on configure
```
CMake Error: hash mismatch for ...
```
The archive doesn't match the SHA256 in `cmake/deps/versions.cmake`. Re-download:
```bash
python3 scripts/fetch_deps.py --fetch --force <name>
```
### Missing archive
```
Could not find archive: 3rd/archives/<name>.tar.gz
```
The tarball wasn't committed. Download it:
```bash
python3 scripts/fetch_deps.py --fetch <name>
```
### Unsupported compiler for fuzz lane
The fuzz preset requires C++17 and Clang. If you don't have Clang, skip the fuzz
preset. All other presets work with any C++14 compiler.
### clang-format not found
Format checks require clang-format 17 or newer. Install it and ensure it's on
your `PATH` as `clang-format`.
## Project Layout
```
├── cmake/ # CMake modules, dependency versions, helper API
├── scripts/ # Developer tooling (format, tidy, checks, hooks)
├── src/ # Application and library source
├── tests/ # Test sources
├── 3rd/
│ ├── archives/ # Vendored dependency tarballs (26 total)
2026-05-18 09:41:16 +08:00
│ └── patches/ # Per-dependency patches (FuzzTest)
├── CMakePresets.json # Build preset definitions
└── .githooks/ # Opt-in pre-commit hook
```