Stop a failed controller-DB fetch from destroying the tracked fallback
Closes Defects -> Known and still open item 12, both halves. include/akgl/SDL_GameControllerDB.h is tracked on purpose: it is the offline fallback that keeps the library buildable when upstream is unreachable. The script that writes it had no set -e and never checked curl, so a failed fetch wrote AKGL_SDL_GAMECONTROLLER_DB_LEN 0 and an empty array over the good copy and exited 0. The safety net destroyed itself, and because CMake re-ran the generator on every build, an offline build was enough to do it. The script now runs under set -euo pipefail, fetches into a temporary directory, and moves the result into place only after checking curl's exit status (with --fail, so an HTTP error is a status rather than an error page in the body), a plausible minimum mapping count, and that no mapping carries a quote or backslash that would break the C string literal it becomes. Any of those failing leaves the tracked header exactly as it was and exits non-zero. Verified against all five failure modes -- unresolvable host, 404, truncated response, empty-but-successful response (the original failure exactly), and a response containing a quote. Each refuses, and the header's checksum is unchanged after every one. AKGL_CONTROLLERDB_URL and AKGL_CONTROLLERDB_MIN_LINES are environment-overridable, which is how. The build no longer runs it at all. The add_custom_command declared a relative OUTPUT, which CMake resolves against the binary directory while the script writes to the source directory, so the declared output never appeared and every build re-ran the command -- needing network access and leaving the tree dirty. It is an explicit `controllerdb` target now, and the header is no longer listed as a library source, which is all it was there for. The empty-initializer problem goes with it: an empty array initializer is a constraint violation in ISO C that compiles only as a GCC extension, and the minimum-count check guarantees at least one entry. Also here, because it rested on a premise that turned out to be false: the character suite is no longer excluded from CI's coverage and memory-check jobs, nor from the mutation harness by default. It was excluded on the grounds that it failed deliberately. It did not -- it was reporting success while running one of its four tests. 25/25 pass, memcheck clean, shellcheck clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
57
TODO.md
57
TODO.md
@@ -740,32 +740,43 @@ Each was found by a test written to assert correct behavior.
|
||||
`tests/controller.c` passes -1 and -4096 to each.
|
||||
|
||||
12. **A failed controller-DB fetch silently destroys the tracked fallback.**
|
||||
`include/akgl/SDL_GameControllerDB.h` is committed deliberately so the
|
||||
library still builds if upstream disappears. But `mkcontrollermappings.sh`
|
||||
has no `set -e` and never checks `curl`'s exit status: on a failed fetch it
|
||||
writes `mappings.txt` empty, then overwrites the good tracked header with
|
||||
`AKGL_SDL_GAMECONTROLLER_DB_LEN 0` and an empty initializer — and exits 0.
|
||||
Verified by pointing the script at an unresolvable host.
|
||||
**Fixed in 0.5.0**, both halves.
|
||||
|
||||
Two compounding problems:
|
||||
`mkcontrollermappings.sh` now runs under `set -euo pipefail`, fetches into a
|
||||
temporary directory, and moves the result into place only after checking
|
||||
three things: curl's exit status (with `--fail`, so an HTTP error is a
|
||||
status rather than an error page in the body), a plausible minimum mapping
|
||||
count, and that no mapping carries a quote or backslash that would break the
|
||||
C string literal it becomes. Any of those failing leaves the tracked header
|
||||
exactly as it was and exits non-zero. `AKGL_CONTROLLERDB_URL` and
|
||||
`AKGL_CONTROLLERDB_MIN_LINES` are environment-overridable, which is how the
|
||||
failure paths were exercised.
|
||||
|
||||
- `add_custom_command` at `CMakeLists.txt:89-93` declares
|
||||
`OUTPUT include/akgl/SDL_GameControllerDB.h` as a *relative* path, which
|
||||
CMake resolves against the binary directory, while the script writes to
|
||||
the source directory. The declared output never appears, so the command is
|
||||
permanently out of date and re-runs on every build — making every build
|
||||
depend on network access and leaving the file dirty in the working tree
|
||||
each time.
|
||||
- `const char *SDL_GAMECONTROLLER_DB[] = {\n};` is an empty initializer,
|
||||
which is a constraint violation in ISO C and compiles only as a GCC
|
||||
extension.
|
||||
Verified against all five: an unresolvable host, a 404, a truncated
|
||||
response, an empty-but-successful response -- the original failure exactly
|
||||
-- and a response containing a quote. Each refuses, and the header's
|
||||
checksum is unchanged after every one.
|
||||
|
||||
The build no longer runs it. The `add_custom_command` declared
|
||||
`OUTPUT include/akgl/SDL_GameControllerDB.h` as a *relative* path, which
|
||||
CMake resolves against the binary directory while the script writes to the
|
||||
source directory, so the declared output never appeared, the command was
|
||||
permanently out of date, and every build re-ran it -- needing network access
|
||||
and leaving the tree dirty. It is an explicit `controllerdb` target now, and
|
||||
the header is no longer listed as a library source, which is all it was
|
||||
there for.
|
||||
|
||||
The empty-initializer problem goes with it: `const char *SDL_GAMECONTROLLER_DB[] = {};`
|
||||
is a constraint violation in ISO C that compiles only as a GCC extension,
|
||||
and the minimum-count check is what guarantees at least one entry.
|
||||
|
||||
Regenerating against the real upstream produced byte-identical mappings --
|
||||
2255 entries, no content change -- so the rewrite is faithful. The only diff
|
||||
is the `$(date)` stamp and the include guard, which is
|
||||
`_AKGL_SDL_GAMECONTROLLERDB_H_` now to match every other header. That change
|
||||
was made in the generator rather than by hand, and the tracked copy carries
|
||||
it so a future regeneration shows no spurious diff.
|
||||
|
||||
Fix: have the script fetch to a temporary file, check `curl`'s status and a
|
||||
plausible minimum line count, and leave the existing header untouched on
|
||||
failure (exiting non-zero). Separately, make regeneration explicit — a
|
||||
dedicated `controllerdb` target, or an `OUTPUT` that matches where the
|
||||
script actually writes — so an ordinary build neither needs the network nor
|
||||
dirties the tree.
|
||||
13. **A stale build tree in the source directory breaks the coverage run.**
|
||||
`CMakeLists.txt:208` and `CMakeLists.txt:223` pass
|
||||
`--root "${CMAKE_CURRENT_SOURCE_DIR}"` to gcovr, and gcovr searches for
|
||||
|
||||
Reference in New Issue
Block a user