# Build lib/sprinter.lib — the Sprinter target libc archive. # # libc/ — ядро библиотеки (БЕЗ графики; графика вынесена в libbgi/). # Каждый .c под libc// — отдельный .rel в архиве (1 публичная # функция = 1 модуль); линкер тянет только нужные .rel → DCE на уровне # файлов. Плюс runtime-хелперы, которые автотянутся кодогеном SDCC # (heap для malloc). crt0-семейство НЕ в архиве — оно всегда явно # линкуется, не DCE-eligible. См. docs/libc-split-asm-cases.md. # # make build lib/sprinter.lib # make clean remove build/ + lib/sprinter.lib # # Симметрично libbgi/Makefile: исходники и build/ рядом, результат # ложится в общий каталог lib/ (откуда его линкует sprinter-cc). PROJ_ROOT := $(abspath $(CURDIR)/..) SDCC_BIN := $(PROJ_ROOT)/third_party/sdcc/bin SDCC := $(SDCC_BIN)/sdcc SDAR := $(SDCC_BIN)/sdar SDASZ80 := $(SDCC_BIN)/sdasz80 INC := -I$(CURDIR)/include CC_FLAGS := -mz80 --no-std-crt0 --std-c99 --opt-code-size $(INC) BUILD := $(CURDIR)/build # All libc C modules — every .c under libc// becomes its own .rel. # `libc/*/*.c` — ровно ОДИН уровень вложенности; графика (gfx/bgi) сюда # не попадает (вынесена в libbgi/). Один PUBLIC-символ на файл. LIBC_C := $(wildcard $(CURDIR)/*/*.c) # Runtime modules to bundle (pulled by symbol references from libc-using # code). bank.s НЕ bundled — его трамплин зависит от banking-окна (W1 # для BIG, W3 для HUGE), что решается per-build в sprinter-cc; он # ассемблирует bank.s с нужным BANK_W1 флагом на каждый banked-build. RUNTIME_S := $(PROJ_ROOT)/runtime/heap.s LIBC_RELS := $(patsubst $(CURDIR)/%.c,$(BUILD)/%.rel,$(LIBC_C)) RUNTIME_RELS := $(patsubst $(PROJ_ROOT)/runtime/%.s,$(BUILD)/%.rel,$(RUNTIME_S)) ALL_RELS := $(LIBC_RELS) $(RUNTIME_RELS) KNOWN_RELS := $(ALL_RELS) # Штамп списка модулей: обновляется при любом изменении состава # (добавление/удаление/переименование исходника) — иначе удаление # исходника не триггерит перелинковку и stale .rel остаётся в архиве. # Сразу сносим и архив: на exFAT (SAM8) гранулярность mtime грубая, # и «список изменился» через сравнение времён можно проглядеть. MODLIST := $(BUILD)/.modules ifneq ($(shell cat $(MODLIST) 2>/dev/null),$(KNOWN_RELS)) _ := $(shell mkdir -p $(BUILD); echo "$(KNOWN_RELS)" > $(MODLIST); rm -f $(LIB)) endif LIB_DIR := $(PROJ_ROOT)/lib LIB := $(LIB_DIR)/sprinter.lib all: $(LIB) # Pattern rule для C-модулей libc//file.c → build//file.rel # (сохраняет путь io|mem|stdio внутри архива). $(BUILD)/%.rel: $(CURDIR)/%.c @mkdir -p $(dir $@) $(SDCC) $(CC_FLAGS) -c -o $@ $< # Runtime .s → .rel (runtime/ вне libc/, но heap.s бандлится в архив). $(BUILD)/%.rel: $(PROJ_ROOT)/runtime/%.s @mkdir -p $(dir $@) $(SDASZ80) -o $@ $< # Archive — sdar rcs = replace/create/symtab. Перед упаковкой удаляем # stale .rel: wildcard не знает об удалённых/переименованных исходниках, # и осиротевший .rel из build/ иначе жил бы вечно. $(LIB): $(ALL_RELS) $(MODLIST) @find $(BUILD) -name '*.rel' -print | sort > $(BUILD)/.have @printf '%s\n' $(KNOWN_RELS) | sort > $(BUILD)/.want @comm -23 $(BUILD)/.have $(BUILD)/.want | while read f; do \ echo " stale: $$f (исходник удалён) — rm"; \ rm -f "$$f" "$${f%.rel}.asm" "$${f%.rel}.lst" "$${f%.rel}.sym"; \ done rm -f $@ $(SDAR) -rcs $@ $(ALL_RELS) @echo @echo " Built $@ with $(words $(ALL_RELS)) modules:" @$(SDAR) -t $@ | sed 's/^/ /' clean: rm -rf $(BUILD) $(LIB) .PHONY: all clean