Found by building a consumer against a fresh install: an installed libakstdlib was not usable. akstdlibConfig.cmake calls find_dependency(akerror), and the top-level build pulls the submodule in EXCLUDE_FROM_ALL, so `cmake --install` on this project installs only this project and leaves that dependency unresolvable. CI had been hiding it by installing libakerror@main in a separate step -- a different libakerror from the one actually compiled, which is the inconsistency TODO.md 2.3 recorded and the previous commit removed. Removing it exposed the real gap. CI now installs deps/libakerror, the same commit the top-level build compiles, so there is one libakerror in play and the install is consumable. tests/consumer/ is the check that would have caught it: a standalone project, configured against CMAKE_PREFIX_PATH rather than as part of this build, because being part of this build is exactly what would let it pass without testing anything. It exercises what only an install has -- find_package with a version request against the generated version file, find_dependency(akerror) resolving, and the exported akstdlib::akstdlib target -- and touches one function from each of the four sources, so a library installed with a source file missing from its link line fails there rather than in the next consumer to find it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
140 lines
6.4 KiB
Markdown
140 lines
6.4 KiB
Markdown
# Repository Guidelines
|
|
|
|
## Project Structure & Module Organization
|
|
|
|
`libakstdlib` is a C shared library that wraps libc calls and data structures in
|
|
`libakerror` error contexts.
|
|
|
|
Public API declarations live in `include/akstdlib.h`, which is also where the
|
|
Doxygen documentation lives -- the header is the contract, the sources carry the
|
|
reasoning. The implementation is split by domain:
|
|
|
|
| File | Covers |
|
|
|---|---|
|
|
| `src/stdlib.c` | memory, formatted output, string-to-number, realpath, djb2, and the list/tree traversal entry points |
|
|
| `src/string.c` | the `string.h` surface |
|
|
| `src/stream.c` | `stdio.h` beyond open/read/write/close |
|
|
| `src/collections.c` | list and tree operations, hash map, string buffer, FNV-1a |
|
|
| `src/aksl_internal.h` | shared internals; not installed, not public |
|
|
|
|
Tests are one-file CTest executables under `tests/test_<name>.c`, with shared
|
|
helpers in `tests/aksl_capture.h`. `tests/negative/` holds sources that must
|
|
*fail* to compile, and `tests/consumer/` is a standalone project built against
|
|
the *installed* package rather than the build tree -- see the testing section
|
|
below. CMake package templates are
|
|
in `cmake/` and `akstdlib.pc.in`. The vendored dependency is `deps/libakerror`;
|
|
update it as a submodule rather than editing generated files under `build/`,
|
|
`build-asan/` or `build-coverage/`.
|
|
|
|
## Build, Test, and Development Commands
|
|
|
|
Initialize dependencies before a fresh build:
|
|
|
|
```sh
|
|
git submodule update --init --recursive
|
|
cmake -S . -B build
|
|
cmake --build build
|
|
```
|
|
|
|
Run the normal suite with `ctest --test-dir build --output-on-failure`. Use the
|
|
instrumented build for memory and undefined-behavior checks:
|
|
|
|
```sh
|
|
cmake -S . -B build-asan -DAKSL_SANITIZE=ON
|
|
cmake --build build-asan
|
|
ctest --test-dir build-asan --output-on-failure
|
|
```
|
|
|
|
For coverage, configure a third tree; the report is part of that suite (the
|
|
`coverage_reset` / `coverage_report` CTest entries) and also lands in
|
|
`build-coverage/coverage-summary.txt`:
|
|
|
|
```sh
|
|
cmake -S . -B build-coverage -DAKSL_COVERAGE=ON
|
|
cmake --build build-coverage --target coverage
|
|
```
|
|
|
|
Run `cmake --build build --target mutation` only when you need the slower
|
|
mutation harness, and `cmake --build build --target docs` to regenerate the API
|
|
documentation -- that one fails if any public function, parameter or return value
|
|
is undocumented, so it is a check as well as a generator.
|
|
|
|
`.githooks/pre-push` runs the default build, the sanitizer build and the
|
|
documentation check before a push; enable it with
|
|
`git config core.hooksPath .githooks`. `rebuild.sh` installs to
|
|
`/home/andrew/local` and removes the local build directory, so treat it as a
|
|
local convenience script.
|
|
|
|
## Coding Style & Naming Conventions
|
|
|
|
Use C with 4-space indentation; existing files sometimes use tabs for continued
|
|
statements, so match the surrounding block. Public symbols use the `aksl_`
|
|
prefix, structs use `aksl_<Name>`, and tests use `test_<feature>.c` plus static
|
|
`test_<case>` functions. Preserve the `akerr_ErrorContext AKERR_NOIGNORE *`
|
|
return convention and the `PREPARE_ERROR` / `FAIL_*` / `SUCCEED_RETURN` pattern.
|
|
|
|
Four conventions hold across the whole library, and a new wrapper that breaks one
|
|
of them is wrong even if it compiles and passes:
|
|
|
|
- **A NULL out-param is a caller error**, not "don't care".
|
|
- **Finding nothing is success** -- searching functions write NULL or zero and
|
|
return NULL.
|
|
- **Truncation is a failure.** Take the destination's size, raise
|
|
AKERR_OUTOFBOUNDS, and write nothing rather than a prefix.
|
|
- **Clear `errno` before the wrapped call** and read it back through
|
|
`AKSL_ERRNO_OR`, so no error can carry status 0 -- which every downstream
|
|
`DETECT` reads as success.
|
|
|
|
Every new public function needs a Doxygen block on its **declaration** with
|
|
`@brief`, a `@param` per parameter, a `@throws` per status it can raise, and
|
|
`@return`. The `docs` target fails otherwise.
|
|
|
|
The build is `-Wall -Wextra` and CI adds `-Werror`. `-Wpedantic` is deliberately
|
|
off: libakerror's `FAIL_*` macros trip "ISO C99 requires at least one argument
|
|
for the ..." on their own expansion, not on anything at the call site.
|
|
|
|
## Testing Guidelines
|
|
|
|
Add a new test by creating `tests/test_mything.c` and adding `mything` to the
|
|
right list in `CMakeLists.txt`. `AKSL_TESTS` must exit zero.
|
|
`AKSL_WILL_FAIL_TESTS` are deliberate abort/contract tests.
|
|
`AKSL_KNOWN_FAILING_TESTS` assert documented defects from `TODO.md`; when one
|
|
starts unexpectedly passing, move it into `AKSL_TESTS` with the fix. Both of the
|
|
latter are currently empty -- all six confirmed defects are fixed -- but the
|
|
mechanism stays for the next one.
|
|
|
|
`tests/negative/` holds sources that must **fail** to compile. Each is an
|
|
`EXCLUDE_FROM_ALL` target built with `-Werror` and registered as a `WILL_FAIL`
|
|
CTest entry, so the test passes only when the compile fails. They cover the two
|
|
guarantees the compiler enforces and nothing else does: `AKERR_NOIGNORE` and the
|
|
format attributes. Drop either in a refactor and every ordinary test still
|
|
passes.
|
|
|
|
`tests/consumer/` is configured standalone against `CMAKE_PREFIX_PATH`, not as
|
|
part of this build, because being part of this build is what would let it pass
|
|
without testing anything. It covers the paths only an install has: the version
|
|
file, `find_dependency(akerror)` resolving, and the exported
|
|
`akstdlib::akstdlib` target. CI runs it after `cmake --install`.
|
|
|
|
Coverage is 99.5% of lines and 100% of functions across all four sources; CI
|
|
gates at 90 (line) / 40 (branch), so new code needs tests in the same commit. Run
|
|
`cmake --build build-coverage --target coverage` and check the uncovered-line
|
|
listing before proposing a change. Tests for behaviour that `TODO.md` records as
|
|
defective belong in `AKSL_KNOWN_FAILING_TESTS` asserting the *correct* contract —
|
|
do not pin current-but-wrong behaviour in `AKSL_TESTS`, since that turns the
|
|
eventual fix into a test failure.
|
|
|
|
## Commit & Pull Request Guidelines
|
|
|
|
Recent commits use short imperative summaries, for example `Add memory wrapper
|
|
tests` and `Make error-status assertions authoritative`. Keep commits focused
|
|
and include tests with behavior changes. Pull requests should describe the
|
|
changed API or behavior, list the CTest/sanitizer/mutation commands run, and
|
|
link the relevant `TODO.md` item or issue when fixing a known defect.
|
|
|
|
## Agent-Specific Instructions
|
|
|
|
Do not modify generated build trees, profiling artifacts, or untracked scratch
|
|
files unless explicitly asked. Prefer small, test-backed changes and update
|
|
`README.md` or `TODO.md` when changing documented workflows or known failures.
|