Take libakerror 2.0.2, libakstdlib and libakgl 0.9.0
Bumps all three ak* submodules to their current main, applies what their
upgrade notes require, and retires the two workarounds they make obsolete.
libakerror 2.0.1 -> 2.0.2 (63 commits). Two of its fixes were this
repository's own filed issues, and both workarounds are gone:
- It namespaces its embedded `coverage` target now, the same
CMAKE_SOURCE_DIR test it already applied to `mutation` (its issue #15).
The add_custom_target() shadow that renamed it on the way past could
only ever fire on a name the dependency has stopped using, so it is
deleted rather than left as dead code.
- It installs akerrorConfigVersion.cmake at SameMajorVersion (its issue
#16). MAINTENANCE.md said to add a `1.0` floor to our find_dependency
calls when this landed; the floor is now `2.0`, and we have no
find_dependency calls to add it to, so the paragraph says that instead
of an instruction nobody can follow.
Its IGNORE context also changed shape: `__akerr_last_ignored` was an extern
pointer, and is now a per-translation-unit `static akerr_last_ignored` holding
a copy, so the pool slot can be released. Nothing here referenced the symbol,
but it costs us 1.35 MiB of thread-local storage -- 38 TUs x 37,296 bytes,
measured as the entire TLS segment of build/basic, where 2.0.1 produced no TLS
segment at all -- plus 84 -Wunused-variable warnings. Filed upstream as
libakerror issue #37 and recorded in MAINTENANCE.md rather than patched here,
because patching a submodule forks it.
libakstdlib gains directory and file-metadata wrappers with no version bump.
aksl_snprintf keeps its `int *count` -- an intermediate commit removed it and
the merge put it back -- but now reports the required length on truncation
rather than 0. Every call site here reads it only after a successful return,
so nothing moved.
The directory wrappers close the gap DIRECTORY was refused for (libakstdlib
issue #10). The verb is still unwritten, so it still refuses, but it no longer
blames a wrapper that exists: the message is "DIRECTORY is not implemented
yet" and tests/disk_verbs.c asserts both that it says so and that it does not
name libakstdlib. What writing it would need is akbasic issue #55.
libakgl moves to the current main at 0.9.0. It registers libccd and tg as
submodules, so a tree that only ran `git submodule update --init --recursive`
before the bump needs it again or the configure fails on a missing
libccd/src/ccd/config.h.cmake.in.
Verified: 114/114 default, 116/116 under -DAKBASIC_WITH_AKGL=ON, docs_examples
green in both. libakerror's UPGRADING.md documents a 2.0.3 that project()
never stamped, so the version tables read 2.0.2 -- libakerror issue #38.
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
Co-Authored-By: Claude Code (Claude Opus 5, claude-opus-5[1m]) <noreply@anthropic.com>
This commit is contained in:
117
MAINTENANCE.md
117
MAINTENANCE.md
@@ -68,12 +68,19 @@ the `akgl_*` or `aksl_*` entry point should look like, and what tests would cove
|
||||
decision, which is true of *changing* it and not of *reporting* it. Follow the prose-paragraph style of the entries already
|
||||
there. Growing the dependency to serve the interpreter is a wanted outcome, not a detour.
|
||||
|
||||
It works. Four gaps were filed this way — text measurement, immediate-mode drawing, audio,
|
||||
and a non-blocking keystroke read — and all four landed upstream as `akgl_text_measure`, the
|
||||
`akgl_draw_*` family, `akgl_audio_*` and `akgl_controller_poll_key`. `FILTER` is the one verb
|
||||
still blocked on a gap, and `DIRECTORY` is refused pending an `opendir`/`readdir` wrapper in
|
||||
`libakstdlib`. Both refuse at execution and say so, rather than being silently ignored: a
|
||||
program that asks for a low-pass filter and gets an unfiltered square wave has been lied to.
|
||||
It works. **Five** gaps were filed this way and all five landed upstream: text measurement,
|
||||
immediate-mode drawing, audio and a non-blocking keystroke read became `akgl_text_measure`,
|
||||
the `akgl_draw_*` family, `akgl_audio_*` and `akgl_controller_poll_key`; and the
|
||||
directory-reading wrapper `DIRECTORY` was waiting on became `aksl_opendir`, `aksl_readdir`,
|
||||
`aksl_closedir` and `aksl_rewinddir` — `libakstdlib` issue #10, in the revision this tree
|
||||
pins.
|
||||
|
||||
That leaves the two refusals in different positions, and the difference is worth keeping
|
||||
straight. **`FILTER` is the one verb still blocked on a gap** — there is no filter stage in
|
||||
`akgl_audio_*` to configure. **`DIRECTORY` is no longer blocked on anything**; it is simply
|
||||
unwritten, and its refusal says so rather than naming a wrapper that now exists. Both refuse
|
||||
at execution and say so, rather than being silently ignored: a program that asks for a
|
||||
low-pass filter and gets an unfiltered square wave has been lied to.
|
||||
|
||||
### The Go reference
|
||||
|
||||
@@ -156,10 +163,10 @@ guards every dependency with `if(NOT TARGET ...)`, so a top-level build must def
|
||||
`akerror::akerror` and `akstdlib::akstdlib` from `deps/libakerror` and `deps/libakstdlib`
|
||||
**before** `add_subdirectory(deps/libakgl)`, or the targets are declared twice.
|
||||
|
||||
That order is load-bearing for a second reason: `deps/libakerror` is at **2.0.1**, whose 2.0.0
|
||||
That order is load-bearing for a second reason: `deps/libakerror` is at **2.0.2**, whose 2.0.0
|
||||
was a source and ABI break carrying an soname (`libakerror.so.2`). `libakstdlib` and `libakgl`
|
||||
must be compiled against that header, not a 1.x one, and an installed `libakerror.so.1` must
|
||||
not be picked up. The break is quiet if you get it wrong: `__akerr_last_ignored` became
|
||||
not be picked up. The break is quiet if you get it wrong: the context behind `IGNORE` became
|
||||
thread-local and `akerr_next_error()` now returns a context that already holds a reference, so
|
||||
a mixed build leaks pool slots or frees one twice rather than failing to link.
|
||||
|
||||
@@ -167,10 +174,47 @@ a mixed build leaks pool slots or frees one twice rather than failing to link.
|
||||
|
||||
| Submodule | Version | soname | ABI rule | Version API |
|
||||
|---|---|---|---|---|
|
||||
| `deps/libakerror` | 2.0.1 | `libakerror.so.2` | major only | **none** — no version macro; `include/akbasic/error.h` feature-tests `AKERR_THREAD_SAFE` and `AKERR_EXIT_STATUS_UNREPRESENTABLE` instead |
|
||||
| `deps/libakerror` | 2.0.2 | `libakerror.so.2` | major only | **none** — no version macro; `include/akbasic/error.h` feature-tests `AKERR_THREAD_SAFE` and `AKERR_EXIT_STATUS_UNREPRESENTABLE` instead |
|
||||
| `deps/libakstdlib` | 0.2.0 | `libakstdlib.so.0.2` | **`MAJOR.MINOR` while major is 0** | `AKSL_VERSION_*`, `aksl_version()`, `AKSL_VERSION_CHECK()` |
|
||||
| `deps/libakgl` | 0.9.0 | `libakgl.so.0.9` | **`MAJOR.MINOR` while major is 0** | `AKGL_VERSION*`, `akgl_version()`, `AKGL_VERSION_AT_LEAST()` |
|
||||
|
||||
**The `libakerror` row is what `project(akerror VERSION ...)` declares, and it disagrees with
|
||||
that library's own release notes**, which carry a "Release 2.0.3" section describing CI-only
|
||||
changes. The version the build stamps into the soname and into
|
||||
`akerrorConfigVersion.cmake` is 2.0.2; nothing consumers can observe says 2.0.3. Read the
|
||||
table as the ABI and the notes as the changelog, and do not "correct" this row to 2.0.3
|
||||
without the upstream `project()` bump to go with it. Reported as `libakerror` issue #38.
|
||||
|
||||
#### What 2.0.2's `IGNORE` change costs, measured
|
||||
|
||||
`IGNORE` now takes a *copy* of the context so the pool slot can be released, which fixes a
|
||||
real leak. The copy lives in the public header as a file-scope `static`:
|
||||
|
||||
```c norun
|
||||
static AKERR_THREAD_LOCAL akerr_ErrorContext akerr_last_ignored;
|
||||
```
|
||||
|
||||
`static` means one per translation unit, and `AKERR_THREAD_LOCAL` means each one is
|
||||
thread-local. `sizeof(akerr_ErrorContext)` is **37,296 bytes**, so:
|
||||
|
||||
| Build | TLS segment of `build/basic` |
|
||||
|---|---|
|
||||
| libakerror 2.0.1 | none at all |
|
||||
| libakerror 2.0.2 | 1,417,248 bytes — **1.35 MiB**, per thread |
|
||||
|
||||
38 copies × 37,296 accounts for the segment exactly; `readelf -sW build/basic` shows all 38.
|
||||
It scales with translation-unit count, not with anything about the program, and every thread
|
||||
a host game spawns pays it again — which is worth knowing in a tree that spent a release
|
||||
taking `akbasic_Runtime` from 10.75 MiB to 2.40 MiB.
|
||||
|
||||
It also emits **84 `-Wunused-variable` warnings**, one per TU that includes `akerror.h`
|
||||
without using `IGNORE`. Nothing here builds with `-Werror`, so it is noise rather than a
|
||||
failure — but a consumer that does cannot include the header.
|
||||
|
||||
**Both are `libakerror` issue #37 and neither is worked around here**, per the rule above: a
|
||||
one-line `extern` in the header and a definition in `src/error.c` fixes it upstream, and
|
||||
patching our copy would fork the submodule. Delete this subsection when it lands.
|
||||
|
||||
For both 0.x libraries the soname carries `MAJOR.MINOR` deliberately: 0.1 and 0.2 are
|
||||
*different* ABIs, and both become major-only at 1.0. Do not read `0.1 → 0.2` as a compatible
|
||||
bump — both libraries have actually made that jump, so anything built against the 0.1 headers
|
||||
@@ -196,16 +240,32 @@ precedes the build tree on the include path and a stray copy there would shadow
|
||||
one and pin every consumer. It publishes `AKGL_VERSION_AT_LEAST(major, minor, patch)` — the
|
||||
compile-time test `libakstdlib` could not write against `libakerror` — and `akgl_version()`.
|
||||
|
||||
**Version-pinning in `find_package` is asymmetric, and that is deliberate.**
|
||||
**Version-pinning in `find_package` used to be asymmetric. It no longer is.**
|
||||
`find_package(akstdlib 0.1)` and `find_package(akgl 0.1)` both work; each ships a
|
||||
`ConfigVersion.cmake` at `SameMinorVersion`, mirroring its soname. `find_package(akerror 1.0)`
|
||||
**fails against a correct install**, because `libakerror` ships `akerrorConfig.cmake` and
|
||||
`akerrorTargets.cmake` but no `akerrorConfigVersion.cmake`. Ask for `akerror` unversioned. Its
|
||||
floor is enforced instead by an `#error` feature-testing `AKERR_FIRST_CONSUMER_STATUS`, which
|
||||
`akstdlib.h`, `akgl/error.h` and our own `include/akbasic/error.h` all carry — include any of
|
||||
them and you inherit the guard. The missing version file is filed in
|
||||
`libakstdlib` issue #5 and `libakerror` issue #16; when it lands, add the `1.0` floor to the `find_dependency`
|
||||
calls.
|
||||
`ConfigVersion.cmake` at `SameMinorVersion`, mirroring its soname. `libakerror` shipped
|
||||
`akerrorConfig.cmake` and `akerrorTargets.cmake` but no `akerrorConfigVersion.cmake`, so any
|
||||
versioned request failed against a correct install and the advice here was to ask for
|
||||
`akerror` unversioned. That was `libakerror` issue #16 — closed — and `libakstdlib` issue #5,
|
||||
which tracks the same fix from the other side and is still open only because nobody has shut
|
||||
it. It has landed: `libakerror` now writes `akerrorConfigVersion.cmake` at
|
||||
**`SameMajorVersion`**, matching the soname's major-only rule, rather than the
|
||||
`SameMinorVersion` the other two use to match theirs.
|
||||
|
||||
Two things follow, and the second is the one that bites:
|
||||
|
||||
- **A versioned request now works** — but the floor to ask for is **`2.0`**, not the `1.0`
|
||||
this file used to say. `find_package(akerror 1.0)` fails *harder* than before: it is a
|
||||
request for major 1 against a major-2 install, which `SameMajorVersion` correctly rejects.
|
||||
- **There is nothing in this repository to change.** `akbasic` reaches all three dependencies
|
||||
by `add_subdirectory`, not `find_package`, and ships no CMake package config of its own —
|
||||
so it has no `find_dependency` calls to add a floor to. The instruction that used to live
|
||||
here was written for a consumer this project never became. It matters to anyone *installing*
|
||||
these libraries and linking `akbasic` against the installed copies, which is why it is
|
||||
recorded rather than deleted.
|
||||
|
||||
The compile-time floor is unchanged and still the real guard: an `#error` feature-testing
|
||||
`AKERR_FIRST_CONSUMER_STATUS`, which `akstdlib.h`, `akgl/error.h` and our own
|
||||
`include/akbasic/error.h` all carry — include any of them and you inherit it.
|
||||
|
||||
### Embedding all three dependencies collides four ways
|
||||
|
||||
@@ -250,14 +310,19 @@ CTest names. `libakstdlib` still uses bare `test_<name>` targets. **Name every t
|
||||
this repo `akbasic_test_<name>`** — it costs nothing and it is the collision that actually
|
||||
stopped a build.
|
||||
|
||||
**3. Duplicate custom targets.** `libakerror` namespaces its `mutation` target when embedded
|
||||
but **not** its `coverage` target, so any coverage-enabled top-level build fails with *"another
|
||||
target with the same name already exists"*. We shadow `add_custom_target` and rename that one
|
||||
to `akerror_coverage` on the way past. `libakstdlib` (both targets) and `libakgl` (its
|
||||
`mutation` target) namespace themselves correctly. **The real fix is upstream in
|
||||
`libakerror`** — the same `CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR` test it already
|
||||
applies to `mutation` — and it is filed as `libakerror` issue #15. Delete the
|
||||
workaround when it lands.
|
||||
**3. Duplicate custom targets — fixed upstream, and the workaround is gone.** `libakerror`
|
||||
used to namespace its `mutation` target when embedded but **not** its `coverage` target, so
|
||||
any coverage-enabled top-level build failed with *"another target with the same name already
|
||||
exists"*. This project shadowed `add_custom_target` and renamed that one to
|
||||
`akerror_coverage` on the way past, and recorded the real fix as `libakerror` issue #15: the
|
||||
same `CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR` test it already applied to
|
||||
`mutation`.
|
||||
|
||||
That landed in 2.0.2. `libakerror` now picks `akerror_coverage` itself when embedded, so the
|
||||
shadow was dead code that could only ever fire on a name the dependency had stopped using —
|
||||
and it is deleted. All three dependencies namespace both targets correctly now, so **there is
|
||||
no custom-target collision left**; only collisions 1, 2 and 4 below are live. The heading says
|
||||
four because four is what a reader coming from the issue tracker will be looking for.
|
||||
|
||||
**4. Stale build trees poison the coverage report.** See below; it is the reason for
|
||||
`cmake -S . -B build`.
|
||||
|
||||
Reference in New Issue
Block a user