Movimiento de "Cuadrado" veloz en mapa blanco
This commit is contained in:
commit
b695b60153
55 files changed
+9391
No files matched your search
@@ -0,0 +1,825 @@
|
||||
# CMakeTemplate — a generator-friendly CMake module system
|
||||
|
||||
A small template project (one shared library, two executables, one
|
||||
vendored dependency, one fetched dependency) that demonstrates a CMake
|
||||
setup where **adding, removing, or renaming a module never requires
|
||||
editing the root `CMakeLists.txt`**, output paths and project identity
|
||||
come from exactly one file, and Linux, macOS, Windows/MSVC, clang-cl,
|
||||
and MinGW are all first-class targets -- each with its own toolchain
|
||||
file and preset, built in its own directory -- across x64, ARM64, and
|
||||
(on Linux) 32-bit ARM.
|
||||
|
||||
Copy the structure, delete the demo code, keep the machinery.
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
scripts/setup.sh # once: offer to install missing tools, fetch pinned Ninja + Doxygen, install the git hook
|
||||
scripts/build.sh # configure (if needed) + build, Release by default
|
||||
scripts/run.sh hello_exe World
|
||||
```
|
||||
|
||||
```bat
|
||||
scripts\setup.bat
|
||||
scripts\build.bat
|
||||
scripts\run.bat hello_exe World
|
||||
```
|
||||
|
||||
`setup` starts by checking for the tools a build needs -- Git, CMake 3.25+
|
||||
and a C++ compiler, plus optional extras like Clang and clang-format --
|
||||
and asks, **one tool at a time**, whether to install each missing one,
|
||||
showing the exact command first. Nothing is installed without your yes;
|
||||
see [scripts/setup.sh / setup.bat](#scriptssetupsh--setupbat).
|
||||
|
||||
```
|
||||
Hello, World! (from demo_core)
|
||||
```
|
||||
|
||||
Pick a target explicitly with a preset, and switch freely -- every
|
||||
preset builds in its own directory, so none of this ever invalidates
|
||||
another build:
|
||||
|
||||
```bash
|
||||
scripts/build.sh --list-presets # what this OS offers
|
||||
scripts/build.sh --preset linux-x64-gcc
|
||||
scripts/build.sh --preset linux-x64-clang # leaves the GCC build alone
|
||||
scripts/run.sh hello_exe World # runs whichever you built last
|
||||
scripts/run.sh --preset linux-x64-gcc hello_exe World
|
||||
```
|
||||
|
||||
## What you get
|
||||
|
||||
- **One source of truth.** `Configuration.cmake` owns this project's
|
||||
identity, target selection, and every output path. Nothing else —
|
||||
not a `.cmake` module, not a shell script, not `.gitignore` —
|
||||
hardcodes a second copy of a value it defines. See
|
||||
[Configuration.cmake: the source of truth](#configurationcmake-the-source-of-truth).
|
||||
- **Sixteen targets, one toolchain file each.** GCC and Clang on Linux,
|
||||
each on x64, ARM64, and 32-bit ARM; Clang on macOS, on Apple Silicon
|
||||
and Intel; MSVC, clang-cl, and GNU-style Clang on Windows, each on x64
|
||||
and ARM64, plus both MinGW flavors (x86/x64) — see
|
||||
[Supported targets](#supported-targets-dist_target).
|
||||
Each has a toolchain file in `cmake/toolchains/` that picks the
|
||||
compiler and declares the target, and where the target isn't the
|
||||
machine you're on it sets up a real cross build: the ARM Linux targets
|
||||
and both MinGW ones cross-compile from a Linux machine — see
|
||||
[Toolchain files and cross-compiling](#toolchain-files-and-cross-compiling).
|
||||
- **Presets that never step on each other.** Every preset builds in its
|
||||
own `out/build/<preset>/`, so switching between GCC and Clang, or
|
||||
between two architectures, never invalidates — or deletes — the
|
||||
other's build. `scripts/run`, `install` and `docs` follow whichever
|
||||
you built last, or take `--preset`.
|
||||
- **Zero-edit module discovery.** Drop a directory with a one-line
|
||||
`CMakeLists.txt` under `source/`, `source/tests/`, or
|
||||
`source/examples/`; it's built, linked, installed, tested, and
|
||||
documented without touching anything else. See
|
||||
[Adding a module](#adding-a-module).
|
||||
- **Self-managing tooling.** `scripts/setup.sh`/`setup.bat` fetch a
|
||||
pinned Ninja and Doxygen (so every machine builds and documents
|
||||
identically), wire up a pre-commit hook, and offer — one tool at a
|
||||
time, with your say-so for each — to install whatever else is missing
|
||||
(CMake, Git, a compiler, Clang, MinGW, ...) through your package
|
||||
manager: apt, dnf, pacman, Homebrew or winget.
|
||||
- **A build script that takes real arguments.** Pick a preset (which
|
||||
selects the right toolchain), a configuration, and forward
|
||||
arbitrary `-D...` flags to CMake — safely, on both bash and
|
||||
`cmd.exe` — see [scripts/build.sh / build.bat](#scriptsbuildsh--buildbat).
|
||||
|
||||
## Project layout
|
||||
|
||||
```
|
||||
Configuration.cmake Source of truth: identity, DIST_TARGET, output paths
|
||||
CMakeLists.txt Root build: options, compiler settings, module
|
||||
discovery, install rules -- rarely needs editing
|
||||
CMakePresets.json One preset per supported DIST_TARGET, each
|
||||
pointing at its toolchain file and its own
|
||||
build directory -- for IDEs and
|
||||
scripts/build.sh|.bat --preset
|
||||
StyleGuide.md The C++ style guide .clang-format/.clang-tidy enforce
|
||||
|
||||
cmake/
|
||||
BuildHelpers.cmake add_lib_module / add_exe_module / add_test_module /
|
||||
add_example_module, and module discovery
|
||||
Dependencies.cmake add_vendor_header_only / add_vendor_subdirectory /
|
||||
add_dependency
|
||||
Documentation.cmake Per-module Doxygen wiring
|
||||
PrintConfig.cmake Lets scripts/*.sh|*.bat read Configuration.cmake
|
||||
BuildDir.cmake Tells scripts/*.sh|*.bat which build directory
|
||||
(preset) to work on -- see "Scripts"
|
||||
HostArch.cmake x64/arm64/arm32 labels from a processor string
|
||||
toolchains/ One toolchain file per DIST_TARGET, plus
|
||||
_common.cmake with what they share
|
||||
FetchNinja.cmake, NinjaPin.cmake Pinned Ninja download
|
||||
FetchDoxygen.cmake, DoxygenPin.cmake Pinned Doxygen download
|
||||
|
||||
scripts/ setup, build, run, install, docs, refactor
|
||||
-- .sh and .bat, one-to-one. _common.sh holds
|
||||
what the .sh ones share; _tools.sh (Linux/macOS)
|
||||
and _tools.ps1 (Windows) are the interactive tool
|
||||
installers setup runs first
|
||||
|
||||
source/
|
||||
Core/ core_lib -- a SHARED library module
|
||||
HelloApp/, FarewellApp/ Executable modules depending on core_lib
|
||||
tests/, examples/ Empty by default -- see Adding a module
|
||||
|
||||
vendor/
|
||||
tiny_case/ A vendored dependency with its own CMakeLists.txt
|
||||
tiny_ansi/ A vendored header-only dependency
|
||||
|
||||
c-api/.clang-tidy Stricter naming rules for a C-API subtree, if you add one
|
||||
.githooks/pre-commit clang-format/clang-tidy check, installed by scripts/setup
|
||||
```
|
||||
|
||||
## Configuration.cmake: the source of truth
|
||||
|
||||
Every value that used to (or could) drift into a second, hand-kept-in-sync
|
||||
copy lives in `Configuration.cmake`:
|
||||
|
||||
- **Identity** — `PROJECT_CODE_NAME` (the `cmake --install --component`
|
||||
name) and `DEFAULT_BUILD_TYPE`.
|
||||
- **Target selection** — `DIST_TARGET`, and the `PLATFORM` /
|
||||
`ARCHITECTURE` / `COMPILER_ID` metadata derived from it.
|
||||
- **Output paths** — `OUT_DIR`, `BUILD_OUTPUT_DIR`, `INSTALL_OUTPUT_DIR`,
|
||||
`DOCS_OUTPUT_DIR`, `TOOLCACHE_DIR`, and the `bin`/`lib` subdirectory
|
||||
names.
|
||||
|
||||
Retargeting this template for your own project, or changing where
|
||||
things land, means editing this one file — or passing `-D<NAME>=...` at
|
||||
configure time — never hunting through `CMakeLists.txt`, a `cmake/*.cmake`
|
||||
module, or a script for a hardcoded duplicate.
|
||||
|
||||
That includes the shell scripts, which run *before* any CMake configure
|
||||
exists to ask a question of. `cmake/PrintConfig.cmake` is a tiny
|
||||
`cmake -P` script that loads `Configuration.cmake` and prints one value;
|
||||
every script that needs one calls it instead of hardcoding a default.
|
||||
(`cmake/BuildDir.cmake` is its sibling for the one question that isn't a
|
||||
single value: *which* build directory a script should work on.)
|
||||
This is exactly the bug it replaces: `install.sh`/`install.bat` used to
|
||||
each hardcode their own literal `"demo"`, with a comment warning it
|
||||
*"must match `PROJECT_CODE_NAME` in `CMakeLists.txt`"* — the kind of
|
||||
comment that's really begging for the two to eventually disagree. They
|
||||
don't hardcode it anymore; they ask `Configuration.cmake`.
|
||||
|
||||
The one thing this deliberately can't do: a bare `cmake -P` script never
|
||||
runs `project()`, so it never gets real compiler detection. Anything
|
||||
in `Configuration.cmake` that depends on the compiler — `DIST_TARGET`
|
||||
and everything derived from it — is skipped in this "query mode" and
|
||||
fails loudly if a script asks for it anyway, rather than guessing. No
|
||||
script currently needs to; `cmake --install` reads what a real configure
|
||||
already baked into the build directory, not these variables directly.
|
||||
|
||||
## Supported targets (`DIST_TARGET`)
|
||||
|
||||
`DIST_TARGET` is a single `<platform>-<arch>-<compiler>` string that
|
||||
picks the toolchain metadata (`PLATFORM`, `ARCHITECTURE`, `COMPILER_ID`)
|
||||
used throughout the build and as the `cmake --install` destination
|
||||
subdirectory (`out/install/<DIST_TARGET>/...`). Each value below has a
|
||||
[toolchain file](#toolchain-files-and-cross-compiling) of the same name,
|
||||
`cmake/toolchains/<DIST_TARGET>.cmake`, which declares it — and which
|
||||
every preset selects. Without one (a bare `cmake -S . -B build`) it's
|
||||
auto-detected from whatever compiler CMake finds:
|
||||
|
||||
| `DIST_TARGET` | Compiler |
|
||||
|--------------------------|---------------------------------------------------------|
|
||||
| `linux-x64-gcc` | GCC on x86-64 Linux |
|
||||
| `linux-x64-clang` | Clang on x86-64 Linux |
|
||||
| `linux-arm64-gcc` | GCC on 64-bit ARM Linux (aarch64) |
|
||||
| `linux-arm64-clang` | Clang on 64-bit ARM Linux (aarch64) |
|
||||
| `linux-arm32-gcc` | GCC on 32-bit ARM Linux (armhf/armv7l and similar) |
|
||||
| `linux-arm32-clang` | Clang on 32-bit ARM Linux |
|
||||
| `macos-arm64-clang` | Clang on Apple Silicon macOS (Apple Clang or LLVM's) |
|
||||
| `macos-x64-clang` | Clang on Intel macOS (or Apple Silicon, via Rosetta 2) |
|
||||
| `windows-x64-msvc` | cl.exe (MSVC) targeting x64 |
|
||||
| `windows-arm64-msvc` | cl.exe (MSVC) targeting ARM64 |
|
||||
| `windows-x64-clang-cl` | clang-cl targeting x64 (Clang targeting the MSVC ABI) |
|
||||
| `windows-arm64-clang-cl` | clang-cl targeting ARM64 |
|
||||
| `windows-x64-clang` | Clang, own GNU-style driver, x64 (e.g. MSYS2 CLANG64) |
|
||||
| `windows-arm64-clang` | Clang, own GNU-style driver, ARM64 (e.g. MSYS2 CLANGARM64) |
|
||||
| `windows-x86-mingw32` | 32-bit GCC via MinGW |
|
||||
| `windows-x64-mingw64` | 64-bit GCC via MinGW-w64 |
|
||||
|
||||
No macOS GCC target and no universal one: Apple's own toolchain *is*
|
||||
Clang, and a universal binary is two architectures at once, which a
|
||||
`DIST_TARGET` (one architecture per name) can't express — see
|
||||
[macOS](#macos) for how to get a universal file anyway.
|
||||
|
||||
No `windows-arm32-*` and no ARM64 MinGW target: 32-bit ARM Windows has
|
||||
no real toolchain support left in any of the compilers above, and
|
||||
mainline MinGW-w64's GCC has no ARM64 Windows (PE/COFF) backend — ARM64
|
||||
Windows toolchains in that ecosystem are LLVM-based (MSYS2's
|
||||
CLANGARM64, covered by `windows-arm64-clang` above), not GCC-based.
|
||||
|
||||
### Toolchain files and cross-compiling
|
||||
|
||||
`cmake/toolchains/` has one toolchain file per target above, named after
|
||||
it, and every preset in `CMakePresets.json` selects the one with its own
|
||||
name. CMake reads a toolchain file *before* it detects anything, so the
|
||||
compiler and target are settled first instead of being discovered and
|
||||
then cross-checked after the fact. Each file:
|
||||
|
||||
1. **declares its `DIST_TARGET`**, so the label and the compiler travel
|
||||
together and can't be mismatched;
|
||||
2. **picks the compiler** — and, for clang-cl, pins `--target=` (so
|
||||
`windows-x64-clang-cl` means x64 on an ARM64 machine too);
|
||||
3. **where the target isn't the machine running CMake, sets up a real
|
||||
cross build**: `CMAKE_SYSTEM_NAME`/`CMAKE_SYSTEM_PROCESSOR`, package
|
||||
and library lookup restricted to the target's sysroot (so a host
|
||||
library is never linked into a foreign-architecture binary — fmt is
|
||||
fetched and built *for the target* instead), and, if one is
|
||||
installed, an emulator (`qemu-user` or `wine`) registered as
|
||||
`CMAKE_CROSSCOMPILING_EMULATOR`, which is what lets `ctest` and
|
||||
`scripts/run.sh` run what was just built.
|
||||
|
||||
| Target family | Host | What the toolchain does |
|
||||
|---|---|---|
|
||||
| `linux-*` | matching hardware (ARM64 for `linux-arm64-*`, …) | native build with the ordinary `g++` / `clang++` |
|
||||
| `linux-*` | any other Linux machine | cross build: `<triplet>-g++` (`apt install g++-aarch64-linux-gnu`, `g++-arm-linux-gnueabihf`), or `clang++ --target=<triplet>` plus that same package for the target's libraries (or `-DCMAKE_SYSROOT=...`); `qemu-user` runs the result |
|
||||
| `macos-*-clang` | any Mac | not a cross build in the Linux sense: Apple's clang is one multi-architecture compiler, so the architecture is just an `-arch` flag — pinned with `CMAKE_OSX_ARCHITECTURES`, along with a minimum macOS of 11.0. See [macOS](#macos) |
|
||||
| `windows-*-mingw*` | Windows (MSYS2, MinGW-w64) | native build with `<triplet>-g++` |
|
||||
| `windows-*-mingw*` | Linux | cross build with mingw-w64 (`apt install g++-mingw-w64-x86-64`, `g++-mingw-w64-i686`); `wine` runs the result |
|
||||
| `windows-*-msvc` | Windows | picks `cl` and fails early, with the fix, if it isn't on `PATH`. **cl.exe's architecture comes from the Visual Studio developer prompt, not from CMake** — no toolchain file can switch it — so the architecture check below verifies you opened the right one |
|
||||
| `windows-*-clang-cl` | Windows | picks `clang-cl` and pins its `--target` |
|
||||
| `windows-*-clang` | Windows | picks `clang++`; the MSYS2 CLANG64 / CLANGARM64 shell is what sets its target |
|
||||
|
||||
Windows executables are only built for Windows hosts, except MinGW,
|
||||
which also cross-builds from Linux; there is no cross-compiling *to*
|
||||
MSVC or clang-cl from Linux, and no cross-compiling to or from macOS
|
||||
beyond the two architectures the Mac's own compiler can already emit.
|
||||
|
||||
Two rules about overriding a toolchain, both deliberate: an explicit
|
||||
`-DCMAKE_CXX_COMPILER=...` beats the toolchain's compiler (the
|
||||
architecture and label checks below then catch an override that no longer
|
||||
matches the target), while a stray `CXX` environment variable does *not*
|
||||
— a target called `linux-x64-gcc` shouldn't be built with whatever
|
||||
`CXX=clang++` happens to be exported. And a 32-bit ARM userland on 64-bit
|
||||
ARM hardware reports `aarch64` from `uname -m`, so `linux-arm32-*` would
|
||||
try to cross-compile there; pass `-DCMAKE_CXX_COMPILER=g++` in that one
|
||||
case (the build is labeled `arm32`, correctly).
|
||||
[`cmake/toolchains/_common.cmake`](cmake/toolchains/_common.cmake)
|
||||
documents everything each helper does.
|
||||
|
||||
### macOS
|
||||
|
||||
`macos-arm64-clang` and `macos-x64-clang` build with Clang: Apple Clang
|
||||
from the Xcode Command Line Tools (`xcode-select --install`, which
|
||||
`scripts/setup.sh` offers to run), or LLVM's own Clang if you point
|
||||
`-DCMAKE_CXX_COMPILER` at it (Homebrew's: `$(brew --prefix llvm)/bin/clang++`).
|
||||
Both count as "clang" here. What is specific to the Mac:
|
||||
|
||||
- **The architecture is pinned, not inherited.** Apple's clang is a single
|
||||
multi-architecture compiler, so building x86_64 on an Apple Silicon Mac
|
||||
(or the reverse) is an `-arch` flag, not a cross compiler — no sysroot,
|
||||
no emulator, `CMAKE_CROSSCOMPILING` stays off. The toolchain sets
|
||||
`CMAKE_OSX_ARCHITECTURES` explicitly even when it equals the machine's,
|
||||
because an x86_64 `cmake` running under Rosetta 2 on an Apple Silicon Mac
|
||||
would otherwise quietly build x86_64 for a target named `arm64`. The
|
||||
x64 build then runs on an Apple Silicon Mac through Rosetta 2, if
|
||||
installed; the reverse (arm64 on Intel) can't run.
|
||||
- **Minimum macOS is 11.0** (the first release for Apple Silicon), set with
|
||||
`CMAKE_OSX_DEPLOYMENT_TARGET`. Unset, it would default to the SDK's own
|
||||
version and everything you build would refuse to start on any older
|
||||
macOS than yours. Pass `-DCMAKE_OSX_DEPLOYMENT_TARGET=...` to change it.
|
||||
Bear in mind Apple's libc++ marks some newer C++ library features
|
||||
(`std::format`, floating-point `std::to_chars`, `std::pmr`, ...) as
|
||||
unavailable below the macOS that introduced them; this project's fmt
|
||||
dependency doesn't use any.
|
||||
- **Libraries load through `@rpath`.** Every RPATH in the build is written
|
||||
`$ORIGIN` (the Linux spelling) and translated to `@loader_path` for
|
||||
Mach-O in one place, `_apply_rpath` in `cmake/BuildHelpers.cmake`;
|
||||
shared libraries get `@rpath/...` install names (CMake's default here).
|
||||
The layout is the same as everywhere else: everything flat in `bin/`.
|
||||
- **Universal binaries** aren't a target. Build both presets and combine
|
||||
the results: `lipo -create out/install/macos-arm64-clang/bin/hello
|
||||
out/install/macos-x64-clang/bin/hello -output hello`.
|
||||
- **The shell is bash 3.2.** macOS still ships it as `/bin/bash`, so every
|
||||
`.sh` script here is written for it (no `mapfile`, no bash-4 expansions,
|
||||
empty arrays guarded for `set -u`) and BSD `sed`/`mktemp`.
|
||||
- **Pinned Ninja and Doxygen work on macOS**: Ninja's universal binary
|
||||
(one asset for both chips) and Doxygen's `mac-arm` / `mac-intel` zips,
|
||||
chosen by the Mac's real architecture, Rosetta included.
|
||||
- **Homebrew's llvm is keg-only** (installed but not on `PATH`), and it is
|
||||
where `clang-tidy` comes from; `scripts/refactor.sh` and the pre-commit
|
||||
hook look there after the normal `PATH`.
|
||||
|
||||
How this was verified, so you know what to trust: everything above was
|
||||
run for real except the Apple toolchain itself. The macOS toolchain,
|
||||
`Configuration.cmake`'s detection and the RPATH translation were exercised
|
||||
by cross-compiling to Mach-O from Linux (with zig's clang as the compiler,
|
||||
`CMAKE_SYSTEM_NAME=Darwin`) and reading the resulting load commands
|
||||
(`LC_RPATH @loader_path`, `@rpath/libdemo_core.dylib`, arm64 and x86_64); the
|
||||
scripts were run under a real bash 3.2 build; the pinned downloads were
|
||||
fetched and hash-verified; the installer flows ran against a fake Mac. What
|
||||
no Mac has run yet is Apple's own clang and SDK — if something is off
|
||||
there, that is the place to look first.
|
||||
|
||||
### Architecture detection
|
||||
|
||||
Architecture is detected differently depending on compiler family,
|
||||
because `CMAKE_SYSTEM_PROCESSOR` isn't trustworthy for all of them:
|
||||
Linux (GCC/Clang) reads it through `cmake/HostArch.cmake` — `uname -m`
|
||||
for a native build, and whatever the toolchain file set it to for a
|
||||
cross build (so an aarch64 cross build from an x64 machine is labeled
|
||||
`arm64`, not `x64`) — while macOS reads `CMAKE_OSX_ARCHITECTURES`, which
|
||||
its toolchain files always set (see [macOS](#macos)), and Clang's
|
||||
GNU-style driver on Windows uses a
|
||||
direct environment-variable check instead of `CMAKE_SYSTEM_PROCESSOR`/
|
||||
`CMAKE_HOST_SYSTEM_PROCESSOR`: those report the architecture of the
|
||||
`cmake.exe` *process*, so a 32-bit `cmake.exe` running under WoW64 on an
|
||||
ordinary 64-bit machine reports `x86` there, machine be damned. MSVC and
|
||||
clang-cl have the equivalent problem one level down (the compiler, not
|
||||
cmake.exe, is what can be cross-targeting) and use
|
||||
`CMAKE_CXX_COMPILER_ARCHITECTURE_ID` instead — those two routinely
|
||||
differ for a same-machine cross build (an x64 Visual Studio install's
|
||||
"HostX64/ARM64" tools are the normal way to target ARM64 from an x64 dev
|
||||
machine), and CMake tracks the resulting mismatch as a known issue
|
||||
([#25466](https://gitlab.kitware.com/cmake/cmake/-/issues/25466)).
|
||||
|
||||
Auto-detection tells clang-cl apart from GNU-style Clang by
|
||||
`CMAKE_CXX_COMPILER_FRONTEND_VARIANT` (`MSVC` vs. `GNU` — both report
|
||||
`Clang` as the compiler ID), and mingw32 apart from mingw64 by pointer
|
||||
size, not by parsing the compiler's own executable name. An
|
||||
unrecognized compiler *or* architecture is a configure-time
|
||||
`FATAL_ERROR` naming every valid value above, not a silent guess —
|
||||
auto-detection guessing "must be MSVC" for anything it doesn't
|
||||
recognize is exactly what used to misidentify a MinGW build as MSVC and
|
||||
install it to the wrong directory.
|
||||
|
||||
**Two Windows gotchas worth knowing before you hit them:**
|
||||
- *"targeting X86 (32-bit)" on a 64-bit machine.* `cl.exe`/`clang-cl`
|
||||
found via `PATH` is the 32-bit-hosted, 32-bit-targeting one — usually
|
||||
from opening a plain shell instead of an "x64 Native Tools Command
|
||||
Prompt for VS" (or a Developer PowerShell defaulted to x64). The error
|
||||
says this; open the right prompt, delete that preset's build directory
|
||||
(`out/build/<preset>`), and reconfigure.
|
||||
- *"No pinned ninja/Doxygen binary is configured for this host platform"
|
||||
on an ordinary 64-bit machine.* Almost always a 32-bit `cmake.exe`
|
||||
(an entirely normal, common download) running under WoW64 — see
|
||||
`cmake/HostArch.cmake`'s header for why that fools a naive host-
|
||||
architecture check. Either install a 64-bit CMake, or just install
|
||||
ninja yourself and put it on `PATH` — `scripts/build.sh`/`build.bat`
|
||||
fall back to a system ninja either way (see below).
|
||||
|
||||
**A bare `-DDIST_TARGET=<value>` labels the build; it doesn't select a
|
||||
compiler.** CMake still uses whatever `CMAKE_CXX_COMPILER` it would
|
||||
otherwise find — your `PATH`, your environment, or an explicit
|
||||
`-DCMAKE_CXX_COMPILER=...`. Passing
|
||||
`-DDIST_TARGET=windows-x64-mingw64` alone, with no MinGW compiler
|
||||
actually configured, does not make CMake go find one — it configures
|
||||
whatever compiler it would have anyway (often MSVC, since CMake looks
|
||||
for that first on Windows) and would silently mislabel the result as
|
||||
MinGW. It doesn't, though: an explicit `DIST_TARGET` that disagrees with
|
||||
what the real compiler auto-detects to is a configure-time
|
||||
`FATAL_ERROR`, naming both what you asked for and what CMake actually
|
||||
found, plus the toolchain file and preset that fix it. The same check
|
||||
also catches a toolchain whose compiler was overridden afterwards (an
|
||||
explicit `-DCMAKE_CXX_COMPILER=...`, or a stale cache), and a
|
||||
`-DDIST_TARGET` that contradicts the toolchain's own target.
|
||||
|
||||
The simplest way to avoid hitting that in the first place is a
|
||||
`CMakePresets.json` preset, which selects the target's toolchain file —
|
||||
compiler and `DIST_TARGET` together — for exactly this reason.
|
||||
[`scripts/build.sh`/`build.bat`](#scriptsbuildsh--buildbat) take a
|
||||
preset name directly:
|
||||
|
||||
```bash
|
||||
scripts/build.sh --list-presets # see every preset for your OS
|
||||
scripts/build.sh --preset windows-x64-mingw64
|
||||
```
|
||||
|
||||
or use `cmake` directly — open the project in an IDE with CMake preset
|
||||
support (CLion, VS Code, Visual Studio) and pick one from the target
|
||||
dropdown, or:
|
||||
|
||||
```bash
|
||||
cmake --list-presets
|
||||
cmake --preset windows-x64-mingw64 # configure
|
||||
cmake --build --preset windows-x64-mingw64 # build
|
||||
```
|
||||
|
||||
Selecting a target without a preset still works — hand CMake the same
|
||||
toolchain file a preset would, e.g.
|
||||
`-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/windows-x64-mingw64.cmake` — or
|
||||
run from an environment (an MSYS2 MINGW64 shell, a Visual Studio
|
||||
Developer Prompt) where the right compiler is already the default and
|
||||
auto-detection alone is enough. Either way, the compiler itself has to
|
||||
already be installed and on `PATH` — nothing here installs MinGW,
|
||||
clang-cl, or MSVC for you (a toolchain file that can't find its compiler
|
||||
says which package or prompt provides it). What doesn't work: giving
|
||||
*both* `--preset <name>` and an explicit `-DDIST_TARGET=...` to
|
||||
`scripts/build.sh`/`build.bat` at the same time — a preset's toolchain
|
||||
already declares `DIST_TARGET`, so a second value alongside it is always
|
||||
either redundant or contradictory, and both scripts reject the
|
||||
combination up front with a one-line fix, rather than leaving it to
|
||||
whichever of the two CMake happens to prefer.
|
||||
|
||||
### Used as a dependency of another project
|
||||
|
||||
Short answer: the root (outer) project always decides, automatically —
|
||||
nothing extra to configure. `CMakePresets.json` presets are a
|
||||
command-line concept tied to whichever directory you point `cmake -S`/
|
||||
`--preset` at; they're never implicitly consulted for a directory pulled
|
||||
in as a dependency (`add_subdirectory()`, `FetchContent`) unless the
|
||||
outer project's own `CMakePresets.json` explicitly `"include"`s this
|
||||
one, which is the outer project's choice to make, not this template's.
|
||||
So:
|
||||
|
||||
- If the root project sets `DIST_TARGET` itself (its own preset, a
|
||||
plain `-DDIST_TARGET=...`, or one of this template's toolchain files,
|
||||
which declare it), that is visible everywhere in the build — including inside this template
|
||||
once it's a subdirectory — so this file's `if(NOT DIST_TARGET)` check
|
||||
simply sees it already set and skips straight to cross-checking it
|
||||
against the real compiler, exactly like a top-level build.
|
||||
- If the root project does nothing, `DIST_TARGET` here still
|
||||
auto-detects normally, from whatever compiler the whole build is
|
||||
using (there's only one `CMAKE_CXX_COMPILER` per build, shared across
|
||||
every subdirectory) — it just does so as a plain, directory-scoped
|
||||
variable rather than a cache one, so it never leaks back up into the
|
||||
root project's own variables.
|
||||
|
||||
One related thing this template does *not* handle when nested, since
|
||||
it's out of scope for a project meant to be built standalone: `OUT_DIR`
|
||||
and everything under it are defined from `CMAKE_SOURCE_DIR`, which is
|
||||
always the *outermost* project's source directory, not this one's — so
|
||||
if you vendor this template into a larger project, its `out/`,
|
||||
`.gitignore` sync, etc. would target the parent's root, not its own
|
||||
subdirectory. Retarget `OUT_DIR` (and friends) explicitly if you need
|
||||
this template's output layout to stay self-contained in that scenario.
|
||||
|
||||
**MinGW runtime note:** executables built for `windows-x86-mingw32` /
|
||||
`windows-x64-mingw64` statically link `libgcc`/`libstdc++`/`libwinpthread`
|
||||
so they run standalone. `core_lib` (a `SHARED` target) can't do the
|
||||
same — it would collide with the executable's own statically-linked
|
||||
copy of the C++ unwinder (`multiple definition of _Unwind_Resume`) — so
|
||||
it keeps the default dynamic runtime instead, and the build copies the
|
||||
three runtime DLLs next to it automatically (both in the build tree and
|
||||
at install time). You won't notice this unless you go looking for those
|
||||
DLLs next to `libdemo_core.dll`; it's mentioned here in case you do.
|
||||
|
||||
## Scripts
|
||||
|
||||
Every script has a `.sh` and a `.bat` twin with identical behavior,
|
||||
prints its own usage in a header comment, and supports `-h`/`--help`
|
||||
for that same usage at the command line. All of them assume the
|
||||
project root as the working directory but `cd` there themselves, so run
|
||||
them from anywhere inside the repo.
|
||||
|
||||
### `scripts/setup.sh` / `setup.bat`
|
||||
|
||||
Run once per clone, and again whenever you like: it is safe to re-run. In
|
||||
order:
|
||||
|
||||
1. **Checks the tools a build needs and offers to install what's missing —
|
||||
one tool at a time.** For each missing tool it says what the tool is
|
||||
for, shows the exact command it would run, and waits for you:
|
||||
|
||||
```
|
||||
CMake 3.25 or newer -- configures and drives every build
|
||||
Install CMake 3.25 or newer? [Y/n/a/q] y
|
||||
$ sudo apt-get install -y cmake
|
||||
```
|
||||
|
||||
`y` installs it, `n` skips this one, `a` installs everything still to
|
||||
come, `s` skips the rest of the *optional* tools, `q` stops asking
|
||||
(Enter takes the default: yes for a required tool, no for an optional
|
||||
one). Nothing is installed without your yes. What it knows about:
|
||||
|
||||
| | Required | Optional |
|
||||
|---|---|---|
|
||||
| **Linux** | Git, CMake 3.25+, a C++ compiler (GCC) | Clang, clang-format, clang-tidy; on apt systems also the ARM cross compilers, MinGW-w64, `qemu-user` and `wine` for the cross builds |
|
||||
| **macOS** | Xcode Command Line Tools, CMake 3.25+ (Homebrew is installed first if it's needed and missing) | clang-format, clang-tidy |
|
||||
| **Windows** | Git, CMake 3.25+, and a compiler: Visual Studio Build Tools *or* MinGW-w64 via MSYS2 | LLVM (clang-cl, clang-format, clang-tidy), the other MSYS2 toolchains |
|
||||
|
||||
It uses your platform's package manager — `apt`, `dnf` or `pacman` on
|
||||
Linux, Homebrew on macOS, `winget` on Windows — and tells you what to
|
||||
install by hand if it doesn't recognize yours (cross-compilers and
|
||||
emulators are offered on apt systems only, the layout the toolchain
|
||||
files were written and tested against). A few specifics worth knowing:
|
||||
an old distro CMake (Ubuntu 22.04 ships 3.22) is detected and the
|
||||
alternatives — `pipx`, `snap`, the cmake.org tarball — are listed rather
|
||||
than silently accepted; on macOS a `xcode-select --install` dialog is
|
||||
waited for, and Homebrew is fetched with its own documented installer;
|
||||
on Windows, `winget` doesn't put CMake or LLVM on `PATH`, so setup adds
|
||||
their folders to this session (so the remaining steps work) and asks
|
||||
whether to add them to your user `PATH` for good, and a fresh MSYS2 is
|
||||
brought up to date — twice, as MSYS2 recommends — before MinGW-w64 goes
|
||||
into it.
|
||||
|
||||
Flags: `--yes` installs the *required* tools without asking (for
|
||||
scripts and CI), `--all` also the optional ones, `--check` only reports
|
||||
and exits 1 if something required is missing, `--skip-tools` skips this
|
||||
step. Without a terminal and without `--yes`/`--all` it asks nothing and
|
||||
installs nothing, so a CI job can never hang on a prompt.
|
||||
2. **Fetches a pinned Ninja and Doxygen** into `.cache/tools/` (via
|
||||
`cmake/FetchNinja.cmake` / `FetchDoxygen.cmake` — skips anything
|
||||
already fetched). Where upstream ships no binary for your machine
|
||||
(Doxygen on ARM Linux/Windows; Ninja on 32-bit ARM Linux) or the
|
||||
download fails, setup says so and carries on — it offers to install a
|
||||
Ninja through your package manager instead, and the project builds fine
|
||||
without Doxygen.
|
||||
3. **Activates the pre-commit hook**: points `git config core.hooksPath`
|
||||
at `.githooks/`, so the check below is active from your very first
|
||||
commit (optional — the hook warns, rather than blocks, if
|
||||
`clang-format`/`clang-tidy` aren't installed).
|
||||
|
||||
The Linux/macOS side lives in `scripts/_tools.sh` (a table of tools and
|
||||
their package names per package manager, plus the prompting); on Windows
|
||||
`setup.bat` hands the same job to `scripts/_tools.ps1`, because prompts,
|
||||
`PATH` handling and `winget` are far more robust in PowerShell than in
|
||||
batch. Both are internal helpers, not entry points.
|
||||
|
||||
### `scripts/build.sh` / `build.bat`
|
||||
|
||||
```
|
||||
scripts/build.sh [--preset <name>] [BUILD_TYPE] [CMAKE_ARGS...]
|
||||
scripts/build.sh --list-presets
|
||||
scripts/build.sh --help
|
||||
```
|
||||
|
||||
`--preset <name>` configures with a `CMakePresets.json` preset instead
|
||||
of auto-detection — the preset selects that target's toolchain file,
|
||||
which picks the compiler and declares `DIST_TARGET` together, so you
|
||||
don't have to pass both by hand (see
|
||||
[Supported targets](#supported-targets-dist_target) above for why that
|
||||
pairing matters). `--list-presets` prints every preset for your OS and
|
||||
exits. `--preset=<name>` also works; whichever form is used must come
|
||||
first if given at all.
|
||||
|
||||
**One build directory per preset.** `--preset <name>` configures into
|
||||
`out/build/<name>/`; without a preset it's `out/build/default/` (the
|
||||
compiler is auto-detected there, so keep to one compiler per directory —
|
||||
or use presets). CMake keeps a compiler in its cache, so a directory can
|
||||
only ever belong to one compiler; giving each preset its own is what lets
|
||||
you flip between GCC and Clang, or between x64 and a cross-built ARM64
|
||||
target, with each build staying up to date, and it's what plain
|
||||
`cmake --preset` and IDEs get too (each preset's `binaryDir` is
|
||||
`out/build/${presetName}`). Earlier versions of this script kept a single
|
||||
`out/build/` and deleted it whenever the preset changed — if you're
|
||||
upgrading, `out/build/` may still hold that old build's files; delete it
|
||||
once (`scripts/run` and friends say so when that's all they find). The
|
||||
install directory follows the same naming: `out/build/<preset>` builds
|
||||
into `out/install/<preset>`.
|
||||
|
||||
The build *type* is not part of the directory name, so flipping one
|
||||
preset between `Debug` and `Release` reconfigures that preset's directory
|
||||
(a full rebuild of it), as before. If you flip often, derive a preset —
|
||||
in `CMakeUserPresets.json`, `{"name": "gcc-debug", "inherits":
|
||||
"linux-x64-gcc", "cacheVariables": {"CMAKE_BUILD_TYPE": "Debug"}}` — which
|
||||
inherits the toolchain and gets its own `out/build/gcc-debug/`. Set
|
||||
`BUILD_DIR=<dir>` to use an exact directory of your own instead.
|
||||
|
||||
Three things are rejected up front, before `cmake` even runs, each with
|
||||
that same preset list shown right there instead of a bare error:
|
||||
`--preset` with no value after it; `--preset` followed by something
|
||||
that's clearly another flag rather than a name; and — the ambiguity
|
||||
case — `--preset <name>` combined with an explicit `-DDIST_TARGET=...`
|
||||
later in `CMAKE_ARGS`, since the preset's toolchain already declares
|
||||
`DIST_TARGET` and a second value alongside it is never useful.
|
||||
`build.sh` additionally checks the preset *name* itself against
|
||||
`--list-presets` before configuring; `build.bat` instead lets `cmake`
|
||||
itself catch an unrecognized name and shows the preset list as a
|
||||
follow-up to that failure — different mechanism, same result, a
|
||||
deliberate choice given how much less predictable pre-validating a
|
||||
quoted string is in `cmd.exe` (see the `=` trap below for a taste of
|
||||
that general problem).
|
||||
|
||||
`BUILD_TYPE` (`Debug`/`Release`/`RelWithDebInfo`/`MinSizeRel`) is
|
||||
optional and defaults to `Configuration.cmake`'s `DEFAULT_BUILD_TYPE`.
|
||||
Anything else is forwarded to `cmake` verbatim at configure time:
|
||||
|
||||
```bash
|
||||
scripts/build.sh --preset windows-x64-mingw64 Debug -DBUILD_TESTS=ON
|
||||
scripts/build.sh -DBUILD_TESTS=ON # BUILD_TYPE omitted
|
||||
```
|
||||
|
||||
An argument is treated as `BUILD_TYPE` if it doesn't start with `-`;
|
||||
otherwise there's no `BUILD_TYPE` override and every argument is a
|
||||
CMake argument.
|
||||
|
||||
Prefers a pinned Ninja (from `setup.sh`) over a system one, and falls
|
||||
back to CMake's platform default generator with a warning — never an
|
||||
error — if neither is available; run `setup.sh` for the faster,
|
||||
version-pinned option. (A preset already fixes the generator to Ninja,
|
||||
so with `--preset` this only controls *which* `ninja` gets used, pinned
|
||||
or system, not whether one is used at all — if neither exists, the
|
||||
warning says so and the preset's own configure step is left to fail on
|
||||
its own.)
|
||||
|
||||
**The `cmd.exe` `=` trap, and why `build.bat` avoids it:** `cmd.exe`
|
||||
splits a batch file's own `%1`/`%2`/... parameters (and `shift`) on
|
||||
space, comma, semicolon, *and* `=` — so a naively-written script reading
|
||||
`-DBUILD_TESTS=ON` through `%1` sees it as two parameters, `-DBUILD_TESTS`
|
||||
and `ON`, silently. `%*` (the raw, unsplit command line) doesn't have
|
||||
this problem, and neither does `FOR /F`, which only splits on space/tab
|
||||
by default — `build.bat` parses arguments (including `--preset`'s own
|
||||
value) that way specifically so a `-D...=...` argument survives intact.
|
||||
`build.sh`'s `"$@"`/`shift` never had this problem to begin with; this
|
||||
paragraph is Windows-only trivia.
|
||||
|
||||
### `scripts/run.sh` / `run.bat`
|
||||
|
||||
```
|
||||
scripts/run.sh [--preset <name>] <target-name> [-- program-args...]
|
||||
```
|
||||
|
||||
Resolves a CMake target name to its built executable via the manifest
|
||||
`_write_module_manifest()` generates at configure time
|
||||
(`out/build/<preset>/module_manifest-<CONFIG>.txt`) — so this script
|
||||
never needs updating when a module is added, removed, or renamed. Run
|
||||
with no arguments to list every known target.
|
||||
|
||||
Which build it runs: `--preset <name>` picks one; without it, the build
|
||||
`scripts/build` made last (or the only one that exists), and it says
|
||||
which on stderr (`==> Running hello_exe (out/build/linux-x64-gcc)`) so a
|
||||
program's own stdout stays clean to pipe. If several builds exist and none
|
||||
was made through the scripts, it asks for `--preset` instead of guessing.
|
||||
A cross build runs under the emulator its toolchain registered — `qemu-user`
|
||||
for ARM Linux, `wine` for MinGW-from-Linux — when one was installed at
|
||||
configure time; otherwise it can only run on the target itself, and the
|
||||
error says so.
|
||||
|
||||
### `scripts/install.sh` / `install.bat`
|
||||
|
||||
```
|
||||
scripts/install.sh [--preset <name>] [BUILD_TYPE]
|
||||
```
|
||||
|
||||
Installs the build made with `--preset <name>` — by default the one made
|
||||
last, as for `run` — to `out/install/<DIST_TARGET>/{bin,lib}` (the same
|
||||
name as the `out/build/<preset>` it came from), always passing
|
||||
`--component` (read from `Configuration.cmake`'s `PROJECT_CODE_NAME`)
|
||||
so a vendored or fetched dependency's own, untagged `install()` rules
|
||||
are never pulled in alongside this project's own artifacts.
|
||||
|
||||
### `scripts/docs.sh` / `docs.bat`
|
||||
|
||||
```
|
||||
scripts/docs.sh [--preset <name>] [module-name]
|
||||
```
|
||||
|
||||
Builds the `docs` target (every module) or `docs_<module-name>` (just
|
||||
one) and prints the path to the generated landing page,
|
||||
`out/docs/index.html`. The output is shared by every preset —
|
||||
documentation doesn't depend on which compiler built the code — so
|
||||
`--preset` (default: the build made last) only chooses which configured
|
||||
build directory the docs targets run from. A no-op with a clear message — not an error —
|
||||
if Doxygen hasn't been fetched yet or `ENABLE_DOXYGEN=OFF`.
|
||||
|
||||
### `scripts/refactor.sh` / `refactor.bat`
|
||||
|
||||
```
|
||||
scripts/refactor.sh [path]
|
||||
```
|
||||
|
||||
Runs `clang-format -i` and `clang-tidy --fix` across `source/` and
|
||||
`c-api/` (or just the given path) — the "fix it" counterpart to the
|
||||
pre-commit hook, which only checks. Review with `git diff` afterward,
|
||||
like any auto-formatter.
|
||||
|
||||
## Adding a module
|
||||
|
||||
Every module — library, executable, test, or example — lives in its
|
||||
own directory with a single-purpose `CMakeLists.txt`. The root
|
||||
`CMakeLists.txt` discovers `source/*`, `source/tests/*`, and
|
||||
`source/examples/*` automatically (`_discover_module_subdirectories()`
|
||||
in `cmake/BuildHelpers.cmake`); nothing there needs editing.
|
||||
|
||||
```cmake
|
||||
# source/MyLib/CMakeLists.txt
|
||||
add_lib_module(my_lib
|
||||
TYPE SHARED # or STATIC; defaults to BUILD_SHARED_LIBS
|
||||
DEPENDS fmt::fmt
|
||||
)
|
||||
```
|
||||
|
||||
```cmake
|
||||
# source/MyApp/CMakeLists.txt
|
||||
add_exe_module(my_app DEPENDS my_lib)
|
||||
```
|
||||
|
||||
```cmake
|
||||
# source/tests/my_lib_test/CMakeLists.txt
|
||||
add_test_module(my_lib_test DEPENDS my_lib) # ctest-registered, built with -DBUILD_TESTS=ON
|
||||
```
|
||||
|
||||
```cmake
|
||||
# source/examples/my_example/CMakeLists.txt
|
||||
add_example_module(my_example DEPENDS my_lib) # built with -DBUILD_EXAMPLES=ON
|
||||
```
|
||||
|
||||
All four glob their own sources (`src/*.cpp` + `include/*.h(pp)` for a
|
||||
library; `*.cpp` + `include/*.h(pp)` for an executable, recursively
|
||||
from the module's own directory), apply per-target warning flags
|
||||
(`/W4 /permissive-` on MSVC-family compilers, `-Wall -Wextra -Wpedantic`
|
||||
elsewhere), set up `$ORIGIN`-relative `RPATH` on Linux, register the
|
||||
target for install and for `scripts/run.sh`, and get their own Doxygen
|
||||
target for free. `OUTPUT_NAME`, `EXTRA_SOURCES`, and `NO_DOCS` are
|
||||
available on all four when the defaults don't fit — see
|
||||
`cmake/BuildHelpers.cmake` for the full option list.
|
||||
|
||||
## Adding a dependency
|
||||
|
||||
Three patterns, in `cmake/Dependencies.cmake`, covering vendored and
|
||||
fetched code:
|
||||
|
||||
```cmake
|
||||
# Header-only, vendored under vendor/<name>/include
|
||||
add_vendor_header_only(tiny_ansi)
|
||||
|
||||
# Vendored, with its own CMakeLists.txt, under vendor/<subdir>
|
||||
add_vendor_subdirectory(tiny_case)
|
||||
|
||||
# Fetched from a Git repo (or an already-installed copy that's new enough)
|
||||
add_dependency(fmt
|
||||
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
|
||||
GIT_TAG 12.1.0
|
||||
GIT_SHALLOW TRUE
|
||||
FIND_PACKAGE_ARGS 12 NAMES fmt
|
||||
)
|
||||
```
|
||||
|
||||
Always give `FIND_PACKAGE_ARGS` a minimum version (the `12`): without one,
|
||||
*any* installed copy beats your pinned `GIT_TAG` — including a distro
|
||||
package years older than the pin, which is how a "pinned" dependency
|
||||
quietly stops being pinned. A dependency's own CMake options are set with
|
||||
`set(<OPTION> <value> CACHE ...)` *before* the call;
|
||||
[`source/Core/CMakeLists.txt`](source/Core/CMakeLists.txt) is the real,
|
||||
commented instance of both, including two fmt-specific traps it
|
||||
documents: why the pin is 12.1.0 rather than older (Clang ≥ 21 rejects
|
||||
fmt < 11.1's own `FMT_STRING` macro) or newer (12.2.0 doesn't compile for
|
||||
32-bit x86), and why `FMT_MODULE` is switched off (fmt 12 otherwise
|
||||
builds a C++20 module library by default, which needs a module scanner
|
||||
that not every Clang install has).
|
||||
|
||||
Then depend on whichever target the dependency defines, exactly like
|
||||
depending on another module: `add_lib_module(my_lib DEPENDS fmt::fmt)`.
|
||||
All three keep the dependency's warnings out of your build and its
|
||||
sources out of `compile_commands.json`, and are safe to call more than
|
||||
once for the same name if two modules need the same dependency.
|
||||
|
||||
## Documentation generation
|
||||
|
||||
Every module gets a Doxygen target automatically — nothing to add to
|
||||
its own `CMakeLists.txt`. `scripts/docs.sh` / `docs.bat` build all of
|
||||
them and print the landing page path; see
|
||||
[scripts/docs.sh / docs.bat](#scriptsdocssh--docsbat) above. Doxygen
|
||||
itself is fetched by `scripts/setup.sh` (pinned, not your system's, so
|
||||
every machine's output matches) — documentation targets print a
|
||||
pointer to that script instead of building until it has been run once. A
|
||||
pinned Doxygen exists for x64 Linux, x64 Windows and macOS (Apple Silicon
|
||||
and Intel); upstream publishes none for ARM Linux or ARM Windows, so
|
||||
documentation isn't available on those machines (the project builds fine
|
||||
without it).
|
||||
|
||||
Configure it with `-DENABLE_DOXYGEN=OFF`, `-DDOXYGEN_<TAG>=...` for any
|
||||
[Doxyfile tag](https://www.doxygen.nl/manual/config.html), or opt a
|
||||
single module out with `NO_DOCS` on its `add_*_module(...)` call. A
|
||||
module's own `README.md`, if it has one, becomes that module's Doxygen
|
||||
mainpage automatically.
|
||||
|
||||
## Code style & tooling
|
||||
|
||||
This project follows [`StyleGuide.md`](StyleGuide.md),
|
||||
enforced by:
|
||||
|
||||
- **`.clang-format`** — formatting (indentation, brace style, line
|
||||
length). Comments throughout cross-reference the style guide section
|
||||
each rule comes from.
|
||||
- **`.clang-tidy`** (and the stricter `c-api/.clang-tidy`, for a C-API
|
||||
subtree if you add one) — naming conventions and a handful of
|
||||
bug-prone patterns. Comments note what each check *can't* catch, so
|
||||
those gaps stay visible instead of silently unenforced.
|
||||
- **`.githooks/pre-commit`** — runs both tools against staged files
|
||||
before every commit, installed automatically by `scripts/setup.sh` /
|
||||
`setup.bat`. Neither tool is required to have this hook active: if a
|
||||
binary isn't on `PATH`, the hook warns and skips that check instead
|
||||
of blocking the commit. Only an actual formatting or lint issue from
|
||||
a tool that *is* installed blocks anything.
|
||||
- **`scripts/refactor.sh` / `refactor.bat`** — the auto-fix counterpart
|
||||
to the hook's check-only behavior.
|
||||
- **`.editorconfig`** / **`.gitattributes`** — editor-level whitespace
|
||||
consistency and normalized line endings (`.bat` files are kept
|
||||
`CRLF` deliberately; see the file for the rest).
|
||||
|
||||
## Output layout
|
||||
|
||||
Everything generated lands under `out/`, kept out of git by an
|
||||
auto-synced block in `.gitignore` (regenerated from
|
||||
`Configuration.cmake`'s actual path variables on every configure, so it
|
||||
can't silently drift out of sync with what's really produced):
|
||||
|
||||
```
|
||||
out/
|
||||
build/<preset>/ One CMAKE_BINARY_DIR per preset (out/build/default/
|
||||
when scripts/build.sh is run without one)
|
||||
bin/ Executables, .dll/.so
|
||||
lib/ Static libs, import libs
|
||||
install/<DIST_TARGET>/{bin,lib} cmake --install destination -- named like the
|
||||
out/build/<preset> it came from
|
||||
docs/<module>/html/, index.html Generated Doxygen output
|
||||
dist/ Reserved for packaged output (e.g. a future CPack integration)
|
||||
|
||||
.cache/tools/ Pinned Ninja + Doxygen (outside out/ on purpose --
|
||||
`rm -rf out/` shouldn't force a re-download)
|
||||
compile_commands.json Copied to the project root every build, for clangd/clang-tidy
|
||||
```
|
||||
Reference in new issue
Block a user