Files
libakerror/docs/building.md
Tachikoma 983ecf31c9
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
Fix the AKERR_USE_STDLIB=OFF build (issue #12)
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>
2026-08-05 10:30:40 -04:00

5.2 KiB

Building libakerror

The ordinary build is an out-of-tree CMake build, described in the README. This file covers what the build generates for you, what it links against, and the configure options that change either of those.

Configure options

Option Default What it does
AKERR_THREADS auto Threading backend: auto, pthread, or none. auto takes POSIX threads and fails the configure if it cannot find them. See Building single threaded. Must be none when AKERR_USE_STDLIB is OFF — the pthread backend calls into libc, and the configure fails otherwise.
AKERR_USE_STDLIB ON Link against the C standard library. See Dependencies for what you must supply instead when this is OFF.
AKERR_RUNTIME_HEADER (empty) Mandatory when AKERR_USE_STDLIB is OFF. Header providing exit, memset, snprintf, strcmp, strlen and strncpy; the generated header #errors at compile time if it is unset. Defaults to a thin, libc-backed convenience header (cmake/akerr_default_runtime.h) so this repository's own OFF build and test suite work without a real freestanding runtime — a genuinely freestanding consumer should override it with their own header.
AKERR_MAX_ERROR_FNAME_LENGTH 4096 Bytes reserved for the fname/function fields of akerr_ErrorContext. Defaults to PATH_MAX on Linux/glibc, which keeps sizeof(akerr_ErrorContext) and the soname unchanged; changing it is an ABI break.
AKERR_LAST_ERRNO_VALUE_FALLBACK 133 AKERR_LAST_ERRNO_VALUE to stamp when AKERR_USE_STDLIB is OFF, since that configuration cannot shell out to errno --list. Defaults to 133 (Linux's EHWPOISON).
AKERR_STATUS_NAME_SLOTS 4096 Slots in the status-name table; 75% of it is usable.
AKERR_MAX_RESERVED_STATUS_RANGES 64 How many status ranges may be reserved in one process.
AKERR_SANITIZE (empty) Sanitizer list applied to the library and the tests, e.g. thread or address,undefined.
AKERR_COVERAGE OFF Instrument the library with gcov counters.

Behavior change (2.0.2): pool exhaustion inside ENSURE_ERROR_READY (every context-producing macro goes through it) used to call exit(1) directly. It now calls akerr_exit(AKERR_EXIT_STATUS_UNREPRESENTABLE), so a process that runs out of pool slots exits 125 instead of 1. See UPGRADING.md.

The two capacity options are applied PRIVATE: the tables live entirely in src/error.c, so raising them never changes anything a consumer can see. See UPGRADING.md for what happens when you exhaust them.

Templating and autogenerated code

The build process relies upon scripts/generrno.sh which performs the following:

  1. When AKERR_USE_STDLIB is ON: executes errno --list and gathers up the output. When it is OFF, this is skipped entirely (errno --list needs moreutils and <errno.h>, neither freestanding-safe) and AKERR_LAST_ERRNO_VALUE is taken from the AKERR_LAST_ERRNO_VALUE_FALLBACK cache variable instead.
  2. Templates include/akerror.tmpl.h into include/akerror.h to set AKERR_LAST_ERRNO_VALUE, AKERR_THREAD_SAFE, and AKERR_MAX_ERROR_FNAME_LENGTH.
  3. Generates src/errno.c, which contains a function called by akerr_init that initializes all of the status names for the previously defined values of errno. Under AKERR_USE_STDLIB=OFF this file is a stub, and CMake does not compile it into the library at all — akerr_init_errno() is never called in that configuration.

Neither generated output is meant to be edited. Change the template or the generator.

Dependencies

This library depends upon stdlib, and upon POSIX threads unless it is built with -DAKERR_THREADS=none (see Thread safety). If you don't want to link against stdlib, build with -DAKERR_USE_STDLIB=OFF (which requires -DAKERR_THREADS=none — see the options table above) and supply a header, via the AKERR_RUNTIME_HEADER cache variable, that provides:

  • memset function
  • strncpy function
  • strlen function
  • strcmp function
  • snprintf function
  • exit function
  • bool type
  • NULL type
  • size_t type
  • INT_MAX constant

<stdbool.h> and <stddef.h> — which give you bool/NULL/size_t — are included unconditionally by the public header regardless of AKERR_USE_STDLIB, since both are freestanding-safe. AKERR_RUNTIME_HEADER is mandatory in this configuration: the generated header #errors at compile time if it is unset.

... then you can compile it thusly:

cmake -S . -B build -DAKERR_USE_STDLIB=OFF -DAKERR_THREADS=none \
    -DAKERR_RUNTIME_HEADER=/path/to/your/runtime.h
cmake --build build
cmake --install build

If you omit -DAKERR_RUNTIME_HEADER, the build falls back to a convenience header backed by the host's own libc (see the options table above), so the OFF configuration still builds and its test suite still runs on an ordinary hosted machine — useful for exercising the freestanding code paths without a real freestanding runtime, but not what an actually freestanding consumer wants. Supply your own header to get the real thing.