Fix the AKERR_USE_STDLIB=OFF build (issue #12) #28

Merged
andrew merged 1 commits from 12 into main 2026-08-05 10:48:15 -04:00
Collaborator

Closes #12.

Implements the settled plan (decisions a-f) from the ticket:

  1. Normalize AKERR_USE_STDLIB to plain 1/0 in CMake (fixes the -DAKERR_USE_STDLIB=ON spelling bug as well, decision f).
  2. Untangle header includes: <stdbool.h>/<stddef.h> unconditional, <stdlib.h>/<string.h>/<stdio.h> under AKERR_USE_STDLIB, <limits.h> moved out of the public header into src/error.c.
  3. Define the freestanding runtime contract AKERR_RUNTIME_HEADER (decision d), mandatory under OFF with a #error naming the required symbols.
  4. Route ENSURE_ERROR_READY's pool-exhaustion exit through akerr_exit() (decision e) -- exit code moves from 1 to AKERR_EXIT_STATUS_UNREPRESENTABLE (125), documented in docs/building.md and UPGRADING.md.
  5. Retire PATH_MAX; AKERR_MAX_ERROR_FNAME_LENGTH is now a stamped constant defaulting to 4096 for ABI neutrality (decision b) -- sizeof(akerr_ErrorContext) and soname unchanged.
  6. AKERR_USE_STDLIB=OFF with AKERR_THREADS resolving to pthread is now a configure FATAL_ERROR (decision c).
  7. Generated errno table kept out of the OFF build; AKERR_LAST_ERRNO_VALUE falls back to a cache variable (default 133) when freestanding.
  8. docs/building.md corrected (sprintf -> snprintf, size_t added, PATH_MAX dropped, known-defect paragraph removed, new options documented).

Also: guarded tests/err_errno.c's registered-name assertion behind AKERR_USE_STDLIB, added a freestanding CI job (OFF+none build/test plus a -nostdinc -ffreestanding compile-only fixture), and added cmake/akerr_default_runtime.h as a libc-backed default AKERR_RUNTIME_HEADER so this repo's own OFF build/tests work out of the box.

Verified locally:

  • -DAKERR_USE_STDLIB=OFF -DAKERR_THREADS=none: configures and builds clean, 33/33 tests pass.
  • -DAKERR_USE_STDLIB=ON: builds, 37/37 tests pass.
  • -DAKERR_USE_STDLIB=OFF with default/auto threads: configure fails with FATAL_ERROR naming -DAKERR_THREADS=none.
  • Default build (no flags): builds, 37/37 tests pass.
  • sizeof(akerr_ErrorContext) unchanged at 37296 bytes (ABI neutral).
  • freestanding fixture compiles clean under -nostdinc -ffreestanding against the generated header.
Closes #12. Implements the settled plan (decisions a-f) from the ticket: 1. Normalize AKERR_USE_STDLIB to plain 1/0 in CMake (fixes the -DAKERR_USE_STDLIB=ON spelling bug as well, decision f). 2. Untangle header includes: <stdbool.h>/<stddef.h> unconditional, <stdlib.h>/<string.h>/<stdio.h> under AKERR_USE_STDLIB, <limits.h> moved out of the public header into src/error.c. 3. Define the freestanding runtime contract AKERR_RUNTIME_HEADER (decision d), mandatory under OFF with a #error naming the required symbols. 4. Route ENSURE_ERROR_READY's pool-exhaustion exit through akerr_exit() (decision e) -- exit code moves from 1 to AKERR_EXIT_STATUS_UNREPRESENTABLE (125), documented in docs/building.md and UPGRADING.md. 5. Retire PATH_MAX; AKERR_MAX_ERROR_FNAME_LENGTH is now a stamped constant defaulting to 4096 for ABI neutrality (decision b) -- sizeof(akerr_ErrorContext) and soname unchanged. 6. AKERR_USE_STDLIB=OFF with AKERR_THREADS resolving to pthread is now a configure FATAL_ERROR (decision c). 7. Generated errno table kept out of the OFF build; AKERR_LAST_ERRNO_VALUE falls back to a cache variable (default 133) when freestanding. 8. docs/building.md corrected (sprintf -> snprintf, size_t added, PATH_MAX dropped, known-defect paragraph removed, new options documented). Also: guarded tests/err_errno.c's registered-name assertion behind AKERR_USE_STDLIB, added a freestanding CI job (OFF+none build/test plus a -nostdinc -ffreestanding compile-only fixture), and added cmake/akerr_default_runtime.h as a libc-backed default AKERR_RUNTIME_HEADER so this repo's own OFF build/tests work out of the box. Verified locally: - -DAKERR_USE_STDLIB=OFF -DAKERR_THREADS=none: configures and builds clean, 33/33 tests pass. - -DAKERR_USE_STDLIB=ON: builds, 37/37 tests pass. - -DAKERR_USE_STDLIB=OFF with default/auto threads: configure fails with FATAL_ERROR naming -DAKERR_THREADS=none. - Default build (no flags): builds, 37/37 tests pass. - sizeof(akerr_ErrorContext) unchanged at 37296 bytes (ABI neutral). - freestanding fixture compiles clean under -nostdinc -ffreestanding against the generated header.
tachikoma added 1 commit 2026-08-05 10:31:11 -04:00
Fix the AKERR_USE_STDLIB=OFF build (issue #12)
All checks were successful
libakerror CI Build / cmake_build_freestanding (push) Successful in 2m51s
libakerror CI Build / coverage (push) Successful in 2m51s
libakerror CI Build / cmake_build (push) Successful in 2m55s
libakerror CI Build / mutation_test (push) Successful in 47m2s
983ecf31c9
The freestanding build (-DAKERR_USE_STDLIB=OFF) did not compile at all:
bool, PATH_MAX and NULL were used unconditionally in the public header
but only included under the stdlib branch, and the CMake option was
pasted straight into a preprocessor definition, so a non-numeric cache
spelling (-DAKERR_USE_STDLIB=ON) silently evaluated to 0.

- Normalize AKERR_USE_STDLIB to a plain 1/0 in CMake before stamping it,
  and use a consistent '#if AKERR_USE_STDLIB' everywhere it is tested.
- Include <stdbool.h>/<stddef.h> unconditionally (freestanding-safe);
  keep <stdlib.h>/<string.h>/<stdio.h> behind AKERR_USE_STDLIB; move
  <limits.h> out of the public header into src/error.c, its only user.
- Define the freestanding runtime contract: AKERR_RUNTIME_HEADER must
  name a header providing exit, memset, snprintf, strcmp, strlen and
  strncpy when AKERR_USE_STDLIB is OFF, or the header #errors naming
  them. Add cmake/akerr_default_runtime.h, a libc-backed convenience
  default so this repo's own OFF build and tests work out of the box.
- Route ENSURE_ERROR_READY's pool-exhaustion path through akerr_exit()
  instead of a direct exit(1). Deliberate behavior change: that exit
  code moves from 1 to AKERR_EXIT_STATUS_UNREPRESENTABLE (125), the same
  sentinel every other unrepresentable status already uses. Documented
  in docs/building.md and UPGRADING.md. Not an ABI break.
- Retire PATH_MAX: AKERR_MAX_ERROR_FNAME_LENGTH is now stamped by
  scripts/generrno.sh from a new AKERR_MAX_ERROR_FNAME_LENGTH cache
  variable, defaulting to 4096 (PATH_MAX on Linux/glibc) so
  sizeof(akerr_ErrorContext) and the soname are unchanged. Rewrite the
  now-stale PATH_MAX justification in src/lock.h's feature-test-macro
  comment.
- Fail the configure with a FATAL_ERROR, not a warning, when
  AKERR_USE_STDLIB=OFF and AKERR_THREADS would resolve to pthread,
  naming -DAKERR_THREADS=none as the fix.
- Keep the generated errno table (errno.c) out of the OFF build; skip
  the 'errno --list' shellout in scripts/generrno.sh under OFF and
  stamp AKERR_LAST_ERRNO_VALUE from a new fallback cache variable
  (default 133, Linux's EHWPOISON) instead.
- Update docs/building.md: drop the known-defect paragraph, fix
  sprintf -> snprintf, add size_t, drop PATH_MAX, and document the new
  options and the exit-code change.
- Guard tests/err_errno.c's registered-name assertion behind
  AKERR_USE_STDLIB: akerr_init_errno() is not called when it is OFF.
- Add a CI job that configures/builds/tests AKERR_USE_STDLIB=OFF with
  AKERR_THREADS=none, plus a compile-only -nostdinc -ffreestanding
  check of tests/freestanding_fixture.c against the generated header.

Bump the project version to 2.0.2 (no ABI break: soname and struct
layout are unchanged).

Verified locally: OFF+none configures and builds clean with all tests
passing; ON and the plain default build both build and pass their full
test suites (37/37); OFF with the default/auto thread backend fails
configure with a message naming -DAKERR_THREADS=none; sizeof(akerr_ErrorContext)
is unchanged (37296 bytes, fname/function still 4096 each) versus the
pre-change tree.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
tachikoma requested review from andrew 2026-08-05 10:31:18 -04:00
andrew approved these changes 2026-08-05 10:48:01 -04:00
andrew merged commit 3a88492a5a into main 2026-08-05 10:48:15 -04:00
Sign in to join this conversation.