kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 273bce54805eb3c219b67f86b86d704deb574330
parent fdf54b7c184c74cc10d483882d6110dd809dee69
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 12:46:59 -0700

selfdist: version backbone (VERSION + driver/version.c + --version everywhere)

Diffstat:
MMakefile | 8++++++++
AVERSION | 1+
Mdriver/driver.h | 25+++++++++++++++++++++++++
Mdriver/main.c | 32++++++++++++++++++++++++++++++++
Adriver/version.c | 27+++++++++++++++++++++++++++
Mmk/driver_srcs.mk | 3++-
Mmk/flags.mk | 46++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 141 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile @@ -159,6 +159,14 @@ $(BUILD_DIR)/driver/%.o: driver/%.c Makefile $(BUILD_CONFIG) @mkdir -p $(dir $@) $(CC) $(DRIVER_CFLAGS) $(DEPFLAGS) -c $< -o $@ +# version.o carries the injected VERSION / build-id / host-triple. It depends on +# the dedicated version sentinel (not BUILD_CONFIG) so only this one TU rebuilds +# when those change. The defines append to DRIVER_CFLAGS for this target only +# (the pattern rule above uses $(DRIVER_CFLAGS) directly). See mk/flags.mk. +$(BUILD_DIR)/driver/version.o $(BUILD_DIR)/custom-driver/version.o: $(VERSION_CONFIG) +$(BUILD_DIR)/driver/version.o $(BUILD_DIR)/custom-driver/version.o: \ + DRIVER_CFLAGS += $(KIT_VERSION_DEFS) + $(BUILD_DIR)/driver/env/%.o: driver/env/%.c Makefile $(BUILD_CONFIG) @mkdir -p $(dir $@) $(CC) $(DRIVER_ENV_CFLAGS) $(DRIVER_ENV_OS_CFLAGS) $(DEPFLAGS) -c $< -o $@ diff --git a/VERSION b/VERSION @@ -0,0 +1 @@ +2026.6.0 diff --git a/driver/driver.h b/driver/driver.h @@ -109,6 +109,31 @@ void driver_help_gram(void); * the multi-call dispatch. Writes to stdout. */ void driver_help_top(void); +/* ---------------------------------------------------------------------- + * Version / build provenance (driver/version.c) + * + * The single source of truth for the version string is the committed top-level + * VERSION file (CalVer YYYY.MINOR.PATCH); the build id (short git hash, with a + * "-dirty" suffix or "unknown" when unavailable) and host triple are injected + * at build time. These are driver-internal accessors, not libkit public API. + * See doc/plan/SELFDIST.md. + * ---------------------------------------------------------------------- */ +const char* kit_version_string(void); /* e.g. "2026.6.0" */ +const char* kit_build_id(void); /* e.g. "a3f91c2" / "unknown" */ +const char* kit_host_triple(void); /* e.g. "aarch64-macos" */ + +/* Print the shared version banner to stdout. A NULL tool prints + * "kit <ver> (<build>, <triple>)" + * and a non-NULL tool prints + * "kit <tool> <ver> (<build>, <triple>)" + * so `kit --version` and `cc --version` / `ld --version` answer uniformly. */ +void driver_print_version(const char* tool); + +/* Scan argv[1..argc-1] for a "--version" request, returning 1 on a hit. Stops + * at "--" (like driver_argv_wants_help) so a flag forwarded to a JITed program + * or emulated guest is never hijacked. */ +int driver_argv_wants_version(int argc, char** argv); + /* Tool grouping, used by `install` to pick a default set without * duplicating the tool list. The centralized table in main.c tags every * row; the groups with a non-zero bit make up the default install set diff --git a/driver/main.c b/driver/main.c @@ -234,6 +234,15 @@ static int dispatch(const char* name, int argc, char** argv, const KitDriverExtension* ext) { const DriverToolDesc* tool = find_tool(name); if (tool) { + /* `--version` is answered uniformly for every tool here, before the tool's + * own argument parser runs, so it works for both the symlink form + * (argv[0] == "cc ... --version") and the subcommand form + * ("kit cc ... --version", which also routes through dispatch). The scan + * stops at "--", so a flag forwarded to a JITed program / guest is safe. */ + if (driver_argv_wants_version(argc, argv)) { + driver_print_version(tool->name); + return 0; + } if (tool->main_ex) return tool->main_ex(argc, argv, ext); return tool->main(argc, argv); } @@ -269,6 +278,24 @@ int driver_argv_wants_help(int argc, char** argv, int accept_short_h) { return 0; } +int driver_argv_wants_version(int argc, char** argv) { + int i; + for (i = 1; i < argc; ++i) { + if (driver_streq(argv[i], "--")) break; /* see driver_argv_wants_help */ + if (driver_streq(argv[i], "--version")) return 1; + } + return 0; +} + +void driver_print_version(const char* tool) { + if (tool) + driver_printf("kit %s %s (%s, %s)\n", tool, kit_version_string(), + kit_build_id(), kit_host_triple()); + else + driver_printf("kit %s (%s, %s)\n", kit_version_string(), kit_build_id(), + kit_host_triple()); +} + void driver_help_top(void) { unsigned i; driver_printf( @@ -323,6 +350,11 @@ int kit_driver_main_ex(int argc, char** argv, const KitDriverExtension* ext) { return 0; } + if (driver_streq(argv[1], "--version") || driver_streq(argv[1], "version")) { + driver_print_version(NULL); + return 0; + } + if (driver_streq(argv[1], "help")) { if (argc < 3) { driver_help_top(); diff --git a/driver/version.c b/driver/version.c @@ -0,0 +1,27 @@ +/* kit version / build provenance. + * + * The three strings below are injected once per `make` invocation into this + * single translation unit (see mk/flags.mk + the version.o rule in the + * Makefile), so a version bump or a new commit recompiles only version.o and + * not the world. The compiled-in defaults keep a standalone compile working. + * + * These are driver-internal provenance accessors, not libkit public API; they + * live in driver.h, not include/kit/. The single source of truth for the + * version string is the committed top-level VERSION file (CalVer + * YYYY.MINOR.PATCH). See doc/plan/SELFDIST.md. */ + +#include "driver.h" + +#ifndef KIT_VERSION_STR +#define KIT_VERSION_STR "0.0.0" +#endif +#ifndef KIT_BUILD_ID +#define KIT_BUILD_ID "unknown" +#endif +#ifndef KIT_HOST_TRIPLE +#define KIT_HOST_TRIPLE "unknown" +#endif + +const char* kit_version_string(void) { return KIT_VERSION_STR; } +const char* kit_build_id(void) { return KIT_BUILD_ID; } +const char* kit_host_triple(void) { return KIT_HOST_TRIPLE; } diff --git a/mk/driver_srcs.mk b/mk/driver_srcs.mk @@ -20,7 +20,8 @@ DRIVER_ENV_SRCS_EFFECTIVE := $(filter-out driver/env/posix_dbg.c $(DRIVER_ENV_UC endif endif -DRIVER_SRCS = driver/main.c driver/lib/target.c $(DRIVER_ENV_SRCS_EFFECTIVE) +DRIVER_SRCS = driver/main.c driver/version.c driver/lib/target.c \ + $(DRIVER_ENV_SRCS_EFFECTIVE) # One entry per subcommand. cc.c serves both `cc` and `check`; the final # $(sort) below dedupes it. The cas/pkg tools additionally pull in diff --git a/mk/flags.mk b/mk/flags.mk @@ -78,6 +78,52 @@ $(BUILD_CONFIG): FORCE } > $@.tmp @if ! cmp -s $@.tmp $@; then mv $@.tmp $@; else rm -f $@.tmp; fi +# --- kit version / build-id (single source of truth: top-level VERSION) ----- +# Captured once per `make` invocation and injected only into driver/version.o +# (via KIT_VERSION_DEFS + the version.o rule in the Makefile), so a version bump +# or new commit recompiles exactly one TU rather than the world. The build id is +# the short git hash with a "-dirty" suffix when the tree has uncommitted +# tracked changes, or "unknown" without git. KIT_HOST_TRIPLE defaults to a +# canonical name for the build host and is overridden by the cross/release +# pipeline. No __DATE__/__TIME__ anywhere, and the values are stable for a given +# tree, so the self-host byte-identity gate is preserved. See doc/plan/SELFDIST.md. +KIT_VERSION := $(strip $(shell cat VERSION 2>/dev/null)) +ifeq ($(KIT_VERSION),) +KIT_VERSION := 0.0.0 +endif +KIT_GIT_HASH := $(shell git rev-parse --short=7 HEAD 2>/dev/null) +KIT_GIT_DIRTY := $(shell git diff --quiet HEAD 2>/dev/null || printf -- '-dirty') +KIT_BUILD_ID := $(if $(KIT_GIT_HASH),$(KIT_GIT_HASH)$(KIT_GIT_DIRTY),unknown) +# Canonical host triple (matches the scripts/hosted.sh selector naming used by +# the release index + artifact names), derived in pure make from HOST_ARCH / +# HOST_OS (mk/env.mk). Overridden outright by the cross/release pipeline. +KIT_TRIPLE_ARCH := $(HOST_ARCH) +ifeq ($(HOST_ARCH),rv64) +KIT_TRIPLE_ARCH := riscv64 +endif +KIT_TRIPLE_OS := $(HOST_OS) +ifeq ($(HOST_OS),darwin) +KIT_TRIPLE_OS := macos +else ifeq ($(HOST_OS),linux) +KIT_TRIPLE_OS := linux-gnu +endif +KIT_HOST_TRIPLE ?= $(KIT_TRIPLE_ARCH)-$(KIT_TRIPLE_OS) +KIT_VERSION_DEFS = -DKIT_VERSION_STR='"$(KIT_VERSION)"' \ + -DKIT_BUILD_ID='"$(KIT_BUILD_ID)"' \ + -DKIT_HOST_TRIPLE='"$(KIT_HOST_TRIPLE)"' + +# A dedicated sentinel so only version.o (which lists it as a prerequisite) +# rebuilds when the version, commit, or host triple changes. +VERSION_CONFIG = $(BUILD_DIR)/.version-config +$(VERSION_CONFIG): FORCE + @mkdir -p $(dir $@) + @{ \ + printf '%s\n' 'KIT_VERSION=$(KIT_VERSION)'; \ + printf '%s\n' 'KIT_BUILD_ID=$(KIT_BUILD_ID)'; \ + printf '%s\n' 'KIT_HOST_TRIPLE=$(KIT_HOST_TRIPLE)'; \ + } > $@.tmp + @if ! cmp -s $@.tmp $@; then mv $@.tmp $@; else rm -f $@.tmp; fi + # Freestanding objects must not see host SDK/libc headers. Homebrew clang can # also inject a configured sysroot before command-line flags; use # --no-default-config when the selected compiler supports it, but omit it for