Skip to content
This repository was archived by the owner on May 1, 2026. It is now read-only.

build: expose KSUID_VERSION_* macros via configure_file - #8

Merged
justinjoy merged 1 commit into
mainfrom
feature/issue-3-version-macros
Apr 30, 2026
Merged

build: expose KSUID_VERSION_* macros via configure_file#8
justinjoy merged 1 commit into
mainfrom
feature/issue-3-version-macros

Conversation

@justinjoy

Copy link
Copy Markdown
Contributor

Closes #3.

Summary

Exposes compile-time version macros in the public header so downstream consumers can guard new API surface with the conventional preprocessor arithmetic:

#if KSUID_VERSION >= ((1 << 16) | (2 << 8) | 0)
    /* call symbol that landed in 1.2.0 */
#endif

Five macros land in the new generated header libksuid/ksuid_version.h, driven by meson.project_version() via a configure_file substitution from libksuid/ksuid_version.h.in:

Macro Type Note
KSUID_VERSION_MAJOR int 0 for the current release
KSUID_VERSION_MINOR int 1
KSUID_VERSION_PATCH int 0
KSUID_VERSION int (M << 16) | (m << 8) | p for #if arithmetic
KSUID_VERSION_STRING string meson.project_version() verbatim, preserves any pre-release / +build suffix

Single source of truth is meson.build's project(version : '0.1.0') clause; bumping it propagates to the binary soversion, the .pc file, and the public header in lockstep.

Pipeline that ran

Per the global GitHub-issue resolution workflow rule:

  1. Architect study — produced an atomic-commit-sized plan: one self-contained ABI-additive commit, .h.in template tracked in git, subdir('libksuid') so <libksuid/ksuid_version.h> resolves through the existing include path.
  2. Critic study — eight-item risk register (R1 non-semver parse, R2 reproducibility, R3 gst-indent, R4 clang-tidy, R5 install_headers, R6 string source, R7 header hygiene, R8 CRLF).
  3. Synthesize — single atomic commit with explicit mitigations for every R1..R8 item.
  4. Implementer landed commit 8d24ebd.
  5. Reviewer round 1: PASS (10/10 contractual requirements, 5 CI gates locally green).
  6. Architect meta-review: SIGN-OFF.
  7. Critic meta-review: SEND BACK — test pinned >= 0 instead of exact values + missing meson dist round-trip CI gate.
  8. Implementer amended with: exact-value asserts in test_smoke.c (deliberate sync point with meson.build's version : field) + new dist job in ci-pr.yml that does the full meson dist → extract → setup → compile → test round-trip.
  9. Reviewer round 2: PASS — both fixes verified, 11/11 local + 11/11 tarball round-trip + gst-indent clean. Bonus observation: empty @VERSION_MAJOR@ substitution would now fail to compile (blank #define + == is a syntax error), strictly stronger than the runtime check the implementer's comment described.
  10. Architect meta-review round 2: SIGN-OFF.
  11. Critic meta-review round 2: SIGN-OFF — both blockers genuinely closed.

What gates this PR

The new dist job in ci-pr.yml is a hard gate alongside the existing build matrix and sanitizer phases:

  • gst-indent + clang-tidy 22 (lint)
  • Build/test on Ubuntu GCC + Clang, macOS Clang, Windows MSVC
  • DESTDIR install verification, now including staging/usr/local/include/libksuid/ksuid_version.h
  • ASan + UBSan on Linux + macOS
  • meson dist round-trip on Ubuntu — proves the generated header rebuilds deterministically from a clean tarball

Test plan

  • lint phase green
  • build matrix green (Linux GCC/Clang, macOS, Windows MSVC)
  • DESTDIR install lays down ${prefix}/include/libksuid/ksuid_version.h
  • sanitizers green
  • meson dist job green (tarball → fresh extract → setup → compile → test)
  • test_version_macros_are_consistent passes (exact value pin)

Closes #3.

Adds compile-time version macros to the public header so downstream
consumers can guard new API surface with the conventional preprocessor
arithmetic:

  #if KSUID_VERSION >= ((1 << 16) | (2 << 8) | 0)
      /* call symbol that landed in 1.2.0 */
  #endif

The five macros land in libksuid/ksuid_version.h, which is generated
via meson's configure_file from libksuid/ksuid_version.h.in. The
single source of truth is meson.build's project(version : '0.1.0')
clause -- bumping the version there propagates through the binary
soversion, the .pc file, and the public header in lockstep.

  KSUID_VERSION_MAJOR    integer
  KSUID_VERSION_MINOR    integer
  KSUID_VERSION_PATCH    integer
  KSUID_VERSION          (M << 16) | (m << 8) | p, single int
  KSUID_VERSION_STRING   meson.project_version() verbatim

Implementation notes addressing the architect + critic risk register:

  R1 (non-semver project_version): meson.build asserts the version
     string has at least three dot-separated parts and strips a
     trailing -prerelease / +build suffix from the patch component
     before .to_int() coerces it. The full string still rides
     through KSUID_VERSION_STRING so callers can inspect the
     suffix when present.

  R2 (reproducibility / meson dist): the .h.in template is the only
     tracked artifact and substitutes only @VERSION_*@ tokens that
     come from meson.project_version() -- no vcs_tag, no __DATE__,
     no build-time variables. The generated header is regenerated
     deterministically from any tarball.

  R3 (gst-indent over generated header): the .h.in suffix takes the
     template out of the *.h glob the lint hook sweeps, and
     .gitignore picks up libksuid/ksuid_version.h as belt-and-braces
     against an in-tree generation by mistake.

  R4 (clang-tidy 22 on generated header): verified locally to
     produce zero findings -- the body is just five #defines with
     no executable code.

  R5 (install_headers + .pc): configure_file's install_dir lays
     ksuid_version.h next to ksuid.h under ${prefix}/include/libksuid/,
     and ci-pr.yml's DESTDIR install verification gains a matching
     test -f assertion. .pc Cflags already cover ${includedir} so no
     pkg-config edit is needed.

  R6 (KSUID_VERSION_STRING source): emitted from meson.project_version()
     directly rather than synthesised via #x stringification, so a
     pre-release suffix survives in KSUID_VERSION_STRING even though
     the integer macros only encode MAJOR.MINOR.PATCH.

  R7 (header hygiene): #ifndef/#define/#endif guard matching the
     project's existing convention; no #pragma once, no static
     definitions, no transitive includes.

  R8 (Windows CRLF): .gitattributes pins *.h.in to LF so configure_file
     output is byte-identical regardless of which platform's
     checkout the runner used.

A test_version_macros_are_consistent test in test_smoke.c pins the
(MAJOR, MINOR, PATCH, KSUID_VERSION layout, KSUID_VERSION_STRING
non-empty) invariants; 11/11 tests still pass on Linux GCC and the
release-mode install verifies the new header lands at the documented
path.
@justinjoy
justinjoy merged commit aa10e21 into main Apr 30, 2026
10 checks passed
@justinjoy
justinjoy deleted the feature/issue-3-version-macros branch April 30, 2026 05:30
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose KSUID_VERSION_MAJOR / MINOR / PATCH macros in the public header

1 participant