Upgrade to libakerror 1.0.0

Bump deps/libakerror 22 commits to 5ff8790 (1.0.0), which makes the
status-name table private, moves consumer status codes to a band starting
at AKERR_FIRST_CONSUMER_STATUS, enforces range ownership rather than
treating it as advisory, and gives the library an soname. See
deps/libakerror/UPGRADING.md.

src/stdlib.c needed no changes. This library defines no status codes of its
own -- it raises libakerror's AKERR_* codes and propagates errno, both
inside libakerror's reserved 0-255 band -- and it never referenced
AKERR_MAX_ERR_VALUE, __AKERR_ERROR_NAMES, AKERR_STATUS_RANGE_OK or
AKERR_STATUS_NAME_OK. What moved was everything around the code:

A -DAKSL_COVERAGE=ON build stopped configuring at all. libakerror
namespaces its `mutation` target when embedded but not its `coverage`
target, so it collided with ours. Shadow add_custom_target for the duration
of the add_subdirectory() call and rename the dependency's to
akerror_coverage, alongside the existing add_test shadow. Fix upstream and
delete the workaround; recorded in TODO.md.

Pin the 1.0.0 floor three ways, since no single one covers every
consumption path: an #error in akstdlib.h feature-testing
AKERR_FIRST_CONSUMER_STATUS, because libakerror publishes no version macro;
Requires: akerror >= 1.0.0 in akstdlib.pc, which also gets consumers
-lakerror transitively; and find_dependency(akerror) in
akstdlibConfig.cmake. The last was already broken before this bump -- the
template still carried its MyLibraryConfig placeholder with the dependency
commented out, so any external find_package(akstdlib) failed with a bare
"akerror::akerror not found" out of the generated targets file.

Branch coverage of src/stdlib.c fell from 51.0% to 44.3% with no source or
test change: the 1.0.0 PREPARE_ERROR/FAIL_* macros expand to more branches
at every call site, so 337/661 became 481/1087 -- 144 more branches covered,
426 more counted. Line coverage held at 99.0% (200/202) and function
coverage at 100% (21/21). Re-ratchet the CI branch gate 45 -> 40 rather than
chase branches that belong to libakerror's own suite.

tests/test_status_registry.c pins the contract that made the status-code
migration a no-op: libakstdlib reserves no consumer range, so an application
may allocate from AKERR_FIRST_CONSUMER_STATUS without coordinating with it,
and every status this library raises is inside the reserved band with a name
actually registered -- an unnamed one degrades to "Unknown Error" in every
later stack trace, which nothing else would notice. It exercises the new
ownership enforcement too, so the "reserves nothing" assertion cannot pass
vacuously.

ctest 13/13, ASan+UBSan 13/13, coverage 15/15 at 90/40, mutation 89.6%
(155/173, unchanged). Also verified out of tree: the #error fires as the
first diagnostic against a stale akerror.h, pkg-config refuses akerror
0.9.0, and an external find_package(akstdlib) consumer builds and runs
against a temp-prefix install.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 22:20:54 -04:00
parent a37ba3fb89
commit 07c448508b
10 changed files with 393 additions and 18 deletions

43
TODO.md
View File

@@ -426,10 +426,45 @@ cue to move it into `AKSL_TESTS`.
- [ ] `src/stdlib.c` triggers **"ISO C99 requires at least one argument for the `...`"** on
roughly 20 lines under `-Wpedantic`, because `FAIL_*` is called with a bare message and
no varargs. Either always pass an argument, or add a zero-arg-safe form in libakerror.
- [ ] **The `deps/libakerror` submodule is pinned 11 commits behind `main`** (pinned at
`4fad0ce "Add gitea workflow"`; upstream is at `4212ff0`). The pin predates the
refcount-leak fix, the stack-trace buffer-overflow fix, the `AKERR_MAX_ERR_VALUE`
correction and the format-string fix. Bump it.
- [x] **The `deps/libakerror` submodule was pinned 22 commits behind `main`** (pinned at
`4fad0ce "Add gitea workflow"`). Bumped to `5ff8790`, which is libakerror **1.0.0**
the release that made the status-name table private, moved consumer status codes to a
band starting at `AKERR_FIRST_CONSUMER_STATUS` (256), made range ownership enforced,
and gave the library an soname. See `deps/libakerror/UPGRADING.md`. The bump also
brings the refcount-leak fix, the stack-trace buffer-overflow fix and the
format-string fix. Nothing in this library's sources used the removed
`AKERR_MAX_ERR_VALUE`, `__AKERR_ERROR_NAMES`, `AKERR_STATUS_RANGE_OK` or
`AKERR_STATUS_NAME_OK`, so the source change was confined to the build, the packaging
and one compile-time guard. What it did move is recorded in the three items below.
- [ ] **libakerror does not namespace its `coverage` target when embedded**, only its
`mutation` target (`deps/libakerror/CMakeLists.txt:172` vs `:189`). A
`-DAKSL_COVERAGE=ON` top-level build therefore failed to configure at all —
*"another target with the same name already exists"* — the moment the dependency
gained a coverage target. Worked around in `CMakeLists.txt` by shadowing
`add_custom_target` for the duration of the `add_subdirectory()` call and renaming the
dependency's to `akerror_coverage`, alongside the existing `add_test` shadow. **Fix
upstream and delete the workaround**: libakerror should apply the same
`CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR` test to `coverage` that it
already applies to `mutation`.
- [ ] **Branch coverage of `src/stdlib.c` fell from 51.0% to 44.3% on the 1.0.0 bump**, with
no change to this library's sources or tests. Line coverage held at 99.0% (200/202)
and function coverage at 100% (21/21); the branch *denominator* went from 661 to 1087
because the 1.0.0 `PREPARE_ERROR`/`FAIL_*` macros expand to more branches at every
call site. 337/661 became 481/1087 — 144 more branches covered, 426 more counted. The
CI branch gate was re-ratcheted 45 → 40 to match. Most of the added branches are
unreachable from the way this library calls the macros, so raising the number here
would mean testing libakerror's macros rather than this library; that belongs to
libakerror's mutation suite, since macros expand at the call site and coverage cannot
see them properly from either side. Revisit if the gap ever hides a real regression.
- [ ] **`project()` declares no `VERSION`**, so `@PROJECT_VERSION@` expands to nothing and
the installed `akstdlib.pc` ships an empty `Version:` field — a consumer cannot pin
this library the way `akstdlib.pc` now pins `akerror >= 1.0.0`. libakstdlib also has
no `SOVERSION`, so `libakstdlib.so` carries no soname even though its public header
re-exports libakerror's ABI and therefore breaks whenever libakerror's does. Give the
project a version and a `SOVERSION`, and install an `akstdlibConfigVersion.cmake` via
`write_basic_package_version_file()` so `find_package(akstdlib 1.2 REQUIRED)` can work.
(libakerror has the same `ConfigVersion` gap, which is why
`cmake/akstdlib.cmake.in` calls `find_dependency(akerror)` with no version.)
- [ ] **CI does not build against the submodule it pins.** `.gitea/workflows/ci.yaml` clones
`libakerror@main` and installs it, while the build it then runs is top-level and so
compiles `deps/libakerror` at the pinned commit — two different libakerror versions