kit

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

commit 6ff61d3c14d3eacb93b08ccc06f0dc8261fd892f
parent 6b2e04f0f93f4b32a33f92ffb37e565f412154e6
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed,  3 Jun 2026 09:09:52 -0700

build: give libkit.a members unique, path-flattened names

ar stores each archive member under its basename only, so the ~33 sources
that share a basename (the eight link.c, src/{abi,arch,obj,os}/registry.c,
obj/wasm vs arch/wasm emit.c, ...) collided as members in libkit.a. Harmless
for symbol-resolved linking, but it broke ar t/x, name-based member ops, and
linker-map attribution.

Fold each libkit/lang/vendor object basename to its full sub-path
(src/arch/aa64/native.c -> build/lib/arch/aa64/arch_aa64_native.o), keeping
the mirrored build/ subdirs. Member names are now unique by construction.
Since the basename no longer matches the source, the src/lang/vendor compile
rules become generated explicit rules (flatobjs + a per-group target-specific
RULE_CFLAGS so comma-bearing flags like -fsanitize=a,b never pass through
$(call)). driver/ objects are not archived, so they keep simple patterns.

relmap_md.awk now strips the dist_ dir-prefix when sub-splitting dist, since
the dist objects are flattened too.

Diffstat:
MMakefile | 66+++++++++++++++++++++++++++++++++++++++++-------------------------
Mmk/lib_srcs.mk | 33++++++++++++++++++++++++++-------
Mscripts/relmap_md.awk | 4+++-
3 files changed, 70 insertions(+), 33 deletions(-)

diff --git a/Makefile b/Makefile @@ -57,53 +57,69 @@ $(BIN): $(DRIVER_OBJS) $(LIB_AR) $(BUILD_CONFIG) # Compile rules, grouped by source tree: src/ (libkit) -> lang/ (frontends) -> # vendor/ -> driver/. Each tree gets its own include scope; see mk/flags.mk for # the *_CFLAGS definitions and the rationale behind each tree's include set. +# +# The libkit (src/, vendor/, lang/) objects use path-flattened basenames so the +# `ar` member names in libkit.a are unique (see `flatobjs` in mk/lib_srcs.mk); +# the basename no longer matches the source, so these are generated explicit +# rules rather than `%`-pattern rules. The per-group compile flags are attached +# as a target-specific RULE_CFLAGS, not threaded through $(call) — the flag +# strings can contain commas (e.g. -fsanitize=address,undefined). The driver/ +# objects are NOT archived (they link directly), so they keep simple patterns. # --------------------------------------------------------------------------- -# --- src/: libkit core (C + asm), freestanding, hidden visibility ----------- -$(BUILD_DIR)/lib/%.o: src/%.c Makefile $(BUILD_CONFIG) - @mkdir -p $(dir $@) - $(CC) $(LIB_CFLAGS) $(DEPFLAGS) -c $< -o $@ +# $(1)=source $(2)=tree root to strip $(3)=dest subdir under BUILD_DIR +define libkit_obj_rule +$(call flatobjs,$(1),$(2),$(3)): $(1) Makefile $$(BUILD_CONFIG) + @mkdir -p $$(@D) + $$(CC) $$(RULE_CFLAGS) $$(DEPFLAGS) -c $$< -o $$@ +endef -$(BUILD_DIR)/lib/%.o: src/%.S Makefile $(BUILD_CONFIG) - @mkdir -p $(dir $@) - $(CC) $(LIB_CFLAGS) $(DEPFLAGS) -c $< -o $@ +# --- src/: libkit core (C + asm), freestanding, hidden visibility ----------- +$(foreach s,$(LIB_SRCS_C_GENERAL) $(LIB_ASMS),$(eval $(call libkit_obj_rule,$(s),src,lib))) +$(call flatobjs,$(LIB_SRCS_C_GENERAL) $(LIB_ASMS),src,lib): RULE_CFLAGS = $(LIB_CFLAGS) # lang_registry.c is the one libkit source that crosses into lang/*; it # uses -Ilang so the frontend headers can be reached as "c/c.h" etc. -$(BUILD_DIR)/lib/api/lang_registry.o: src/api/lang_registry.c Makefile $(BUILD_CONFIG) - @mkdir -p $(dir $@) - $(CC) $(FREESTANDING_CFLAGS) $(LIB_VISIBILITY_CFLAGS) -Iinclude -Ilang -Isrc $(DEPFLAGS) -c $< -o $@ +ifneq ($(LIB_SRCS_LANGREG),) +$(foreach s,$(LIB_SRCS_LANGREG),$(eval $(call libkit_obj_rule,$(s),src,lib))) +$(call flatobjs,$(LIB_SRCS_LANGREG),src,lib): RULE_CFLAGS = $(FREESTANDING_CFLAGS) $(LIB_VISIBILITY_CFLAGS) -Iinclude -Ilang -Isrc +endif # --- lang/: frontends, each with its own include set ------------------------ -$(BUILD_DIR)/lang/cpp/%.o: lang/cpp/%.c Makefile $(BUILD_CONFIG) - @mkdir -p $(dir $@) - $(CC) $(FREESTANDING_CFLAGS) $(LIB_VISIBILITY_CFLAGS) -Iinclude -Ilang/cpp $(DEPFLAGS) -c $< -o $@ +ifeq ($(KIT_LANG_CPP_ENABLED),1) +$(foreach s,$(LANG_CPP_SRCS),$(eval $(call libkit_obj_rule,$(s),lang,lang))) +$(call flatobjs,$(LANG_CPP_SRCS),lang,lang): RULE_CFLAGS = $(FREESTANDING_CFLAGS) $(LIB_VISIBILITY_CFLAGS) -Iinclude -Ilang/cpp +endif # The C frontend includes the lexer and preprocessor headers (pp/pp.h, # lex/lex.h) which now live under lang/cpp/, and cpp_support.h is the # shared substrate. So lang/c objects build with -Ilang/cpp -Ilang/c. -$(BUILD_DIR)/lang/c/%.o: lang/c/%.c Makefile $(BUILD_CONFIG) - @mkdir -p $(dir $@) - $(CC) $(FREESTANDING_CFLAGS) $(LIB_VISIBILITY_CFLAGS) -Iinclude -Ilang/cpp -Ilang/c $(DEPFLAGS) -c $< -o $@ +ifeq ($(KIT_LANG_C_ENABLED),1) +$(foreach s,$(LANG_C_SRCS),$(eval $(call libkit_obj_rule,$(s),lang,lang))) +$(call flatobjs,$(LANG_C_SRCS),lang,lang): RULE_CFLAGS = $(FREESTANDING_CFLAGS) $(LIB_VISIBILITY_CFLAGS) -Iinclude -Ilang/cpp -Ilang/c +endif # The Wasm frontend is the one lang/* tree that reaches internal headers: it # includes "wasm/wasm.h" to share src/wasm/ (the module/encode/decode library) # with the Wasm backend, so it gets -Isrc. lang/c and lang/cpp deliberately do # not — they see only the public include/ surface. -$(BUILD_DIR)/lang/wasm/%.o: lang/wasm/%.c Makefile $(BUILD_CONFIG) - @mkdir -p $(dir $@) - $(CC) $(FREESTANDING_CFLAGS) $(LIB_VISIBILITY_CFLAGS) -Iinclude -Isrc -Ilang/wasm $(DEPFLAGS) -c $< -o $@ +ifeq ($(KIT_LANG_WASM_ENABLED),1) +$(foreach s,$(LANG_WASM_SRCS),$(eval $(call libkit_obj_rule,$(s),lang,lang))) +$(call flatobjs,$(LANG_WASM_SRCS),lang,lang): RULE_CFLAGS = $(FREESTANDING_CFLAGS) $(LIB_VISIBILITY_CFLAGS) -Iinclude -Isrc -Ilang/wasm +endif -$(BUILD_DIR)/lang/toy/%.o: lang/toy/%.c Makefile $(BUILD_CONFIG) - @mkdir -p $(dir $@) - $(CC) $(FREESTANDING_CFLAGS) $(LIB_VISIBILITY_CFLAGS) -Iinclude -Ilang/toy $(DEPFLAGS) -c $< -o $@ +ifeq ($(KIT_LANG_TOY_ENABLED),1) +$(foreach s,$(LANG_TOY_SRCS),$(eval $(call libkit_obj_rule,$(s),lang,lang))) +$(call flatobjs,$(LANG_TOY_SRCS),lang,lang): RULE_CFLAGS = $(FREESTANDING_CFLAGS) $(LIB_VISIBILITY_CFLAGS) -Iinclude -Ilang/toy +endif # --- vendor/: third-party sources compiled into libkit ---------------------- # Vendored third-party sources (monocypher) compiled into libkit. Same # freestanding flags as the rest of the library; symbols stay hidden. -$(BUILD_DIR)/vendor/%.o: vendor/%.c Makefile $(BUILD_CONFIG) - @mkdir -p $(dir $@) - $(CC) $(LIB_CFLAGS) $(DEPFLAGS) -c $< -o $@ +ifneq ($(LIB_SRCS_VENDOR),) +$(foreach s,$(LIB_SRCS_VENDOR),$(eval $(call libkit_obj_rule,$(s),vendor,vendor))) +$(call flatobjs,$(LIB_SRCS_VENDOR),vendor,vendor): RULE_CFLAGS = $(LIB_CFLAGS) +endif # --- driver/: hosted CLI; env/ is the OS/libc adapter sub-tree -------------- $(BUILD_DIR)/driver/%.o: driver/%.c Makefile $(BUILD_CONFIG) diff --git a/mk/lib_srcs.mk b/mk/lib_srcs.mk @@ -6,6 +6,17 @@ # `#if` gates in the corresponding sources so the build drops exactly what the # compile drops. The matching frontend `#if`s live in src/api/lang_registry.c. +# Flattened, archive-unique object path. libkit.a is built with `ar`, which +# stores each member under its basename only — so two sources sharing a +# basename (the eight link.c, or src/{abi,arch,obj,os}/registry.c, …) would +# collide as members even though their build/ paths differ. We keep the +# mirrored build/ subdirs but fold the full sub-path into the basename, making +# every member name unique. An immediate-parent prefix is not enough on its +# own: src/obj/wasm/emit.c and src/arch/wasm/emit.c would both be wasm_emit.o. +# $(1)=sources $(2)=tree root to strip $(3)=dest subdir under BUILD_DIR +# e.g. src/arch/aa64/native.c -> $(BUILD_DIR)/lib/arch/aa64/arch_aa64_native.o +flatobjs = $(foreach s,$(1),$(BUILD_DIR)/$(3)/$(dir $(patsubst $(2)/%,%,$(s)))$(subst /,_,$(basename $(patsubst $(2)/%,%,$(s)))).o) + # When a native feature is disabled, drop its per-arch TU from all three # native backends and, where one exists, compile a stub in its place. # $(1) = TU basename (e.g. disasm.c) $(2) = stub source, or empty @@ -235,16 +246,16 @@ LANG_TOY_SRCS = $(wildcard lang/toy/*.c) LANG_OBJS = ifeq ($(KIT_LANG_CPP_ENABLED),1) -LANG_OBJS += $(patsubst lang/cpp/%.c,$(BUILD_DIR)/lang/cpp/%.o,$(LANG_CPP_SRCS)) +LANG_OBJS += $(call flatobjs,$(LANG_CPP_SRCS),lang,lang) endif ifeq ($(KIT_LANG_C_ENABLED),1) -LANG_OBJS += $(patsubst lang/c/%.c,$(BUILD_DIR)/lang/c/%.o,$(LANG_C_SRCS)) +LANG_OBJS += $(call flatobjs,$(LANG_C_SRCS),lang,lang) endif ifeq ($(KIT_LANG_WASM_ENABLED),1) -LANG_OBJS += $(patsubst lang/wasm/%.c,$(BUILD_DIR)/lang/wasm/%.o,$(LANG_WASM_SRCS)) +LANG_OBJS += $(call flatobjs,$(LANG_WASM_SRCS),lang,lang) endif ifeq ($(KIT_LANG_TOY_ENABLED),1) -LANG_OBJS += $(patsubst lang/toy/%.c,$(BUILD_DIR)/lang/toy/%.o,$(LANG_TOY_SRCS)) +LANG_OBJS += $(call flatobjs,$(LANG_TOY_SRCS),lang,lang) endif LIB_ASMS = @@ -252,8 +263,16 @@ ifeq ($(KIT_JIT_ENABLED),1) LIB_ASMS += $(LIB_SRCS_JIT_ASM) endif -LIB_OBJS = $(patsubst src/%.c,$(BUILD_DIR)/lib/%.o,$(filter src/%.c,$(LIB_SRCS))) \ - $(patsubst vendor/%.c,$(BUILD_DIR)/vendor/%.o,$(filter vendor/%.c,$(LIB_SRCS))) \ +# Compile-rule source groups (each shares one compile-flag profile; the rules +# that consume these live in the Makefile). lang_registry.c is split out: it is +# a libkit TU but compiles with frontend includes (-Ilang), not the library set. +LIB_SRCS_LANGREG := $(filter src/api/lang_registry.c,$(LIB_SRCS)) +LIB_SRCS_C_GENERAL := $(filter-out $(LIB_SRCS_LANGREG),$(filter src/%.c,$(LIB_SRCS))) +LIB_SRCS_VENDOR := $(filter vendor/%.c,$(LIB_SRCS)) + +LIB_OBJS = $(call flatobjs,$(LIB_SRCS_C_GENERAL),src,lib) \ + $(call flatobjs,$(LIB_SRCS_LANGREG),src,lib) \ + $(call flatobjs,$(LIB_SRCS_VENDOR),vendor,vendor) \ $(LANG_OBJS) \ - $(patsubst src/%.S,$(BUILD_DIR)/lib/%.o,$(LIB_ASMS)) + $(call flatobjs,$(LIB_ASMS),src,lib) LIB_DEPS = $(LIB_OBJS:.o=.d) diff --git a/scripts/relmap_md.awk b/scripts/relmap_md.awk @@ -38,7 +38,9 @@ function classify(p, b) { if (p ~ /\/lib\/link\//) { label["link"]="linker (`src/link`)"; return "link" } if (p ~ /\/lib\/dist\//) { - b = stem(p) + # object basenames are path-flattened (src/dist/manifest.c -> dist_manifest.o, + # see flatobjs in mk/lib_srcs.mk); strip the dir prefix to recover the file. + b = stem(p); sub(/^dist_/, "", b) if (b ~ /^(manifest|kpkg|tar|trust|dist)$/) { label["dist_pkg"]="packaging (manifest/kpkg/tar/trust)"; return "dist_pkg" } if (b ~ /^(deflate|lz4|lz4frame)$/) { label["dist_cmp"]="vendored compression (deflate/lz4)"; return "dist_cmp" } if (b ~ /^(blob|tree|cas)$/) { label["dist_cas"]="CAS store (blob/tree/cas)"; return "dist_cas" }