feat(build-time-versioning): version macro/helper functions with unknown fallback
Adds xce_version_field()/xce_compose_version() (utils.h/utils.c) to compose a version string carrying commit id + build time, rendering "unknown" for either field when its underlying meson-supplied define is empty -- ready for controller/scheduler/worker to consume. MK_VERSION/STR (the pre-existing, compile-time-only macro pair) can't express this empty-to-"unknown" substitution at the preprocessor level, so this is a runtime mechanism instead. Round 1 review REJECTED an earlier version of this task for silently orphaning MK_VERSION/STR (the task explicitly asked to "extend" them) and for leaving an undocumented architectural trap: argp_program_version is a file-scope const char* initialized at compile time, which cannot call a runtime function -- consuming code must instead compute into a static buffer inside main() and assign the pointer before argp_parse() runs. Round 2 adds an explicit SUPERSEDED doc comment on MK_VERSION/STR plus NOTE FOR CALLERS guidance covering both points, and was APPROVED. Also expands task 3.2's boundary to include controller/testsuite/test.c (a third MK_VERSION/STR call site discovered during review) so no caller of the superseded macro survives implementation. 5 new unit tests in test_utils.c (RED->GREEN, TDD). Task 3.1/13 of build-time-versioning. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
d08fda76a5
commit
48a86cdab6
4 changed files with 159 additions and 7 deletions
|
|
@ -61,6 +61,55 @@
|
|||
|
||||
#define MK_VERSION(ver, commit) ver commit
|
||||
|
||||
/* SUPERSEDED for version-string composition (build-time-versioning task
|
||||
* 3.1). MK_VERSION/STR (immediately above) cannot express an
|
||||
* empty-value -> "unknown" fallback: an unset commit_id/build_time meson
|
||||
* option makes STR(x) expand to an empty string literal ("", not a missing
|
||||
* token), and a preprocessor macro has no way to branch on "is this string
|
||||
* empty" at expansion time. xce_version_field()/xce_compose_version()
|
||||
* (extern decls below, implementation + doc comments in utils.c) are the
|
||||
* runtime replacement — they DO perform that substitution, and are what
|
||||
* compose the version string everywhere it now matters. Use those, not
|
||||
* MK_VERSION, for any new version-string composition.
|
||||
*
|
||||
* MK_VERSION/STR are kept in place here (not deleted) only because task 3.2
|
||||
* — which will migrate controller/main.c's and scheduler/src/main.c's
|
||||
* argp_program_version construction to xce_compose_version() — has not
|
||||
* landed yet. Grep finding (2026-08-20, `grep -rn 'STR(\|MK_VERSION(' src/`,
|
||||
* excluding NULLSTR/comment/definition matches): STR() has ZERO call sites
|
||||
* anywhere in this codebase outside this exact MK_VERSION-based
|
||||
* argp_program_version pattern — controller/main.c:23,
|
||||
* scheduler/src/main.c:26, AND controller/testsuite/test.c:662 (the
|
||||
* xce_test REPL tool). That third caller is NOT listed in task 3.2's
|
||||
* boundary (`controller/main.c, scheduler/main.c` only, per tasks.md) —
|
||||
* worth confirming whether it's in scope when 3.2 lands, or it will keep
|
||||
* printing a commit-id-only, build-time-less version string after the
|
||||
* other two are migrated. STR() itself is a generic single-argument
|
||||
* stringification macro (not inherently version-specific), but per this
|
||||
* grep it currently has no unrelated use, so it is not being kept around
|
||||
* for any other live caller — only in service of the not-yet-migrated
|
||||
* MK_VERSION call sites above. Once task 3.2 (and any update to
|
||||
* controller/testsuite/test.c) lands and no caller of MK_VERSION remains,
|
||||
* both MK_VERSION and this STR usage can be deleted outright — not done
|
||||
* here, out of this task's boundary.
|
||||
*
|
||||
* NOTE FOR TASK 3.2 (or whoever migrates argp_program_version):
|
||||
* xce_compose_version() is a REGULAR RUNTIME FUNCTION, not a constant
|
||||
* expression — unlike MK_VERSION's pure string-literal concatenation, it
|
||||
* CANNOT initialize a file-scope variable. `const char* argp_program_version
|
||||
* = xce_compose_version(...)` at file scope will not compile (C file-scope
|
||||
* initializers must be constant expressions). controller/main.c:22-23 and
|
||||
* scheduler/src/main.c:25-26 currently declare argp_program_version exactly
|
||||
* that way — a file-scope const initialized at compile time — so this is
|
||||
* not a one-line macro-call swap. Instead: compute the version string at
|
||||
* RUNTIME into a `static char buf[...]` (module scope or local to main()),
|
||||
* via xce_compose_version(), early in main() and BEFORE argp_parse() runs
|
||||
* (argp reads argp_program_version by pointer when --version/-V is
|
||||
* processed) — then assign argp_program_version (no longer const-qualified
|
||||
* at its declaration; assigned at runtime instead of initialized) to point
|
||||
* at that buffer. See xce_compose_version()'s doc comment in utils.c for
|
||||
* the buffer-sizing contract. */
|
||||
|
||||
#ifndef FREEUP
|
||||
#define FREEUP(p) \
|
||||
if ((p) != NULL) { \
|
||||
|
|
@ -114,6 +163,13 @@ extern int mylog_init(char*, char*, int, int);
|
|||
extern void mylog_fini(void);
|
||||
extern int mylog_setlevel(char*);
|
||||
extern char* verbose_to_loglevel(int);
|
||||
/* See the SUPERSEDED/NOTE comment above MK_VERSION for what these replace
|
||||
* and why, and the constraint they impose on any file-scope
|
||||
* argp_program_version-style global (xce_compose_version() is not a
|
||||
* constant expression). */
|
||||
extern const char* xce_version_field(const char*);
|
||||
extern int xce_compose_version(char*, size_t, const char*, const char*,
|
||||
const char*);
|
||||
extern void set_error(char*, char*, int, int);
|
||||
extern ectx_t* get_ectx(void);
|
||||
extern int xce_vprintf(const char*, va_list);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue