Presets are host-gated (see the condition blocks in CMakePresets.json):
cl-* and clangcl-* configure only on Windows, clang-* and gcc-release
only elsewhere. Picking a preset for the wrong host fails at configure time.
On Windows:
cmake --preset cl-debug
cmake --build --preset cl-debug
ctest --preset cl-debug
(substitute clangcl-debug to build with clang-cl instead of cl)
On Linux or macOS:
cmake --preset clang-debug
cmake --build --preset clang-debug
ctest --preset clang-debug
cl, clang-cl and clang++ must all stay green. g++ (gcc-release) has
no debug preset and is exercised by CI, not by this local list.
These are not style preferences. Each one is load-bearing, and most exist because the alternative was measured and failed.
-
Every file starts with
SPDX-License-Identifier: Apache-2.0.ctest -R hygiene.spdxenforces it. -
No
NOLINTanywhere. Suppressions belong in.clang-tidy, where they are reviewable in one place.ctest -R hygiene.nolintenforces it. -
The exported target stays minimal. The installed package config must never name Catch2, CPM, a test target, or an absolute path. The
PackageCI workflow asserts this. -
Public headers pull in no
<string>,<vector>,<format>or<iostream>. A consumer that only evaluates numbers must not compile them in every translation unit. Those belong in opt-in headers.ctest -R hygiene.headersenforces it. -
Every public
static_assertmessage beginsformula:and is stable, unique and greppable. The must-not-compile tests match on that text, so these strings are tested API; renaming one is a deliberate test change. -
Never identify a type with
decltype([]{}). A lambda in a default template argument gives the closure internal linkage, so the type differs in every translation unit. Verified to fail at link time oncl,clang-clandclang++. Use a named tag type. -
The version is a committed literal. Never derive it from
git describe:vcpkg_from_githubextracts a tarball with no.git.
-
User documentation describes the library as it is now. That is
README.md, everything underdocs/exceptdocs/superpowers/, and the doc comments ininclude/that form the API reference. It never says what the library used to do, what changed, or in which release something appeared; that belongs inCHANGELOG.md.ctest -R hygiene.docs-current-stateenforces it. A line that matches a history phrase but describes data goes incmake/docs-current-state-allowlist.txt, one entry per line in the formpath|exact trimmed line|reason; an entry that allows no reported line fails the check, so remove it when its line is reworded. -
The tutorial has a page in
docs/tutorial/and a program inexamples/tutorial/for each chapter. The page includes code with--8<-- "examples/tutorial/<file>.cpp:<region>"and output with--8<-- "examples/tutorial/<file>.expected.txt"; regions are marked in the program with// --8<-- [start:<region>]and// --8<-- [end:<region>]. Each program is registered inexamples/CMakeLists.txtwithformula_add_pinned_example, whose testexample.<name>.outputcompares the program's output with its.expected.txt. When a program's output changes, update its.expected.txt(example.<name>.outputfails until you do) and re-read the page. A new program also needsdocs/numeric-headroom.mdregenerated: build the targetformula-cpp-census-pagewith cl (the page's examples table is cl's) and commit the result. The target exists whenFORMULA_BUILD_TESTS,FORMULA_BUILD_EXAMPLESandFORMULA_TOOLSare all on, as they are by default in a top-level build.mkdocs build --strictfails on a missing file or region; thePagesworkflow runs it on every pull request. -
The README shows
examples/readme.cppand its output verbatim;docs.readme-snippetsanddocs.readme-outputcheck them. The CPM tag in the README and in tutorial chapter 1 (docs/tutorial/01-first-formula.md) must equal the project version;hygiene.versionchecks both. -
The consumer-globals test.
test/consumer_globals_tests.cppdeclares 309 ordinary globals (308 under compilers other than cl and clang-cl, as glibc declaresindex) such asresult,value,xandindexbefore including every header, and builds under cl/W4 /WXand g++-Wshadow -Werror: no header's local or parameter hides one of them in anything that test instantiates -- evaluation of every node kind,render,documentand the trace in every dialect, constraints, methods and every overlay operation (the test lists them). cl reports a template's local only in a template that is instantiated, and never a function template's parameter, so a template the test does not reach is not covered by it.
.clang-tidy is present but not yet enforced: no CI job runs clang-tidy or
clang-format. This is a known, tracked gap, not an oversight -- it stays open
because .clang-tidy's naming section still needs reconciling with names this
library spells deliberately in standard-library style.
index_in_tuple_v (include/formula-cpp/detail/type_list.hpp) deliberately
mirrors std::is_same_v and so deliberately violates the configured
readability-identifier-naming.GlobalConstantCase: CamelCase. If you run
clang-tidy locally against include/ and it flags names like that, that is
expected, not a bug in your setup; do not rename them to satisfy the linter,
and do not add a clang-tidy CI job without first resolving this conflict
deliberately.
.clang-format has the same status, and one more hazard: the checked-in
tree does not match it, and you should not reformat existing files to make it
match. Running clang-format over the tree today rewrites 99 files, most of
the drift predating any single change, and it silently turns this library's
central idiom var<Q> * x into var<Q>* x -- clang-format parses var<Q>
as a type and the * as a pointer declarator, and there is no setting that
rescues the DSL without turning every genuine pointer into Trace<Rep> *p.
So if your editor formats on save, exclude this repository or expect to
discard the result; reformatting the tree is separate work nobody has asked
for yet.
Three kinds, and new behaviour usually needs more than one:
- Runtime -- ordinary Catch2 assertions.
- Compile-time --
STATIC_REQUIRE, for anything that is a compile-time property. A regression should be a build failure, not a runtime report. - Must-not-compile --
formula_add_negative_test(<name> <expected-text>)intest/CMakeLists.txt, with the case intest/negative/. The harness asserts both that the build failed and that it failed with the expected message, so add the case with a deliberately wrong expected string first and watch it fail before committing the right one.