# ============================================================================== # StarForth Build System - The Fastest Forth in the West! # ============================================================================== # # Quick Start: # make - Standard optimized build (auto ARCH, TARGET=standard) # make TARGET=fastest - Maximum performance build # make ARCH=arm64 TARGET=fastest - Build for arm64 (e.g., Raspberry Pi 4) # make help - Show all available targets # # ============================================================================== # ============================================================================== # CONFIGURATION # ============================================================================== VERSION ?= 3.1.0 CC = gcc # Isabelle configuration # NOTE: Isabelle must be installed separately and available on PATH # See docs/DEVELOPER.md for installation instructions ISABELLE ?= isabelle # Architecture / target selection ARCH ?= $(shell uname -m) ARCH := $(strip $(ARCH)) TARGET ?= standard TARGET := $(strip $(TARGET)) # Friendly guardrail: 32-bit builds are not supported unsupported_32bit_arches := i386 i486 i586 i686 x86 armv6 armv7 armv7l arm ifneq ($(filter $(ARCH),$(unsupported_32bit_arches)),) $(error StarForth targets 64-bit platforms only. ARCH='$(ARCH)' is unsupported. Please build on amd64/arm64/raspi/riscv64.) endif # Canonical architecture normalization (x86_64→amd64, aarch64→arm64, etc.) canon_arch = $(strip $(if $(filter x86_64,$1),amd64,$(if $(filter amd64,$1),amd64,$(if $(filter aarch64 arm64,$1),arm64,$(if $(filter riscv riscv64,$1),riscv64,$1))))) ARCH_DIR := $(call canon_arch,$(ARCH)) # Normalize ARCH for consistent ifeq matching ARCH := $(call canon_arch,$(ARCH)) # Kconfig bridge (menuconfig/xconfig/config/oldconfig/*_defconfig). See # mk/Kconfig.mk for why KCONFIG_ARCH_DIR must be set here, before include. # .DEFAULT_GOAL is pinned explicitly because mk/Kconfig.mk's first rule # would otherwise silently become Make's default goal, since it's included # before this Makefile's own `all:` target is defined below. .DEFAULT_GOAL := all KCONFIG_ARCH_DIR := $(ARCH_DIR) include mk/Kconfig.mk # Kconfig platform-mode choice (Phase 5): PLATFORM_DEFAULT/PLATFORM_MINIMAL # in Kconfig.variant (PLATFORM_L4RE removed -- L4Re support is historical, # see the "Platform support" section below). Must run this early -- before # anything below that branches on MINIMAL -- since Make evaluates a Makefile # top-to-bottom and a variable's value at the point an `ifdef` is reached is # whatever it resolved to *so far*, not its final value. Only applies when a # developer hasn't already set MINIMAL explicitly (command line or # environment) -- `ifdef` in the "Platform support" section below cares # about definedness, not value, so this can't reuse the kconfig_bool/ # kconfig_int pattern (those always define their target variable once # active). Checked with $(origin ...) so an explicit `make MINIMAL=1` (or # unset) always wins over whatever .config says. ifeq ($(KCONFIG_ACTIVE),1) ifeq ($(origin MINIMAL),undefined) ifeq ($(CONFIG_PLATFORM_MINIMAL),y) MINIMAL := 1 endif endif endif ifeq ($(ARCH),amd64) ARCH_NAME = x86_64 ARCH_FLAGS = -march=native ARCH_DEFINES = -DARCH_X86_64=1 ASM_SYNTAX = -masm=intel else ifeq ($(ARCH),arm64) ARCH_NAME = ARM64 ARCH_FLAGS = -march=armv8-a+crc+simd -mtune=cortex-a72 ARCH_DEFINES = -DARCH_ARM64=1 ASM_SYNTAX = else ifeq ($(ARCH),raspi) ARCH_NAME = Raspberry Pi 4 (ARM64) ARCH_FLAGS = -march=armv8-a+crc+simd -mtune=cortex-a72 ARCH_DEFINES = -DARCH_ARM64=1 -DRASPBERRY_PI_BUILD=1 ASM_SYNTAX = else ifeq ($(ARCH),riscv64) ARCH_NAME = RISC-V 64 (QEMU) ARCH_FLAGS = -march=rv64gc -mabi=lp64d -mcmodel=medany ARCH_DEFINES = -DARCH_RISCV64=1 ASM_SYNTAX = else ifeq ($(ARCH),riscv) ARCH_NAME = RISC-V 64 (QEMU) ARCH_FLAGS = -march=rv64gc -mabi=lp64d -mcmodel=medany ARCH_DEFINES = -DARCH_RISCV64=1 ASM_SYNTAX = else $(warning Unknown architecture: $(ARCH), using generic flags) ARCH_NAME = generic ARCH_FLAGS = ARCH_DEFINES = ASM_SYNTAX = endif # Compiler flags # Feature switches (default: enabled) # Physics/SSM knob family: repointed through the Kconfig bridge (see # mk/Kconfig.mk's kconfig_bool/kconfig_int) as of the Kconfig migration's # Phase 3. Each call below is a drop-in replacement for a bare `?=`: when # no build/$(ARCH)/.config exists (KCONFIG_ACTIVE unset), it's exactly # `VAR ?= default`, identical to before; once a .config exists, the # Kconfig-fed value takes over. `make VAR=x` on the command line always # wins regardless of which branch fires. # # STRICT_PTR: VM memory bounds checking on every pointer access # Default: 1 (enabled). Disable only for raw benchmarking comparisons. $(eval $(call kconfig_bool,STRICT_PTR,1)) # ENABLE_HOTWORDS_CACHE: Physics-driven hot-words cache (1.78× speedup on dictionary lookups) # Default: 1 (enabled - experiments show cache is optimal) # Set ENABLE_HOTWORDS_CACHE=0 to disable (for research comparison only) $(eval $(call kconfig_bool,ENABLE_HOTWORDS_CACHE,0)) # ENABLE_PIPELINING: Speculative execution via word transition prediction # Default: 1 (enabled - experiments show pipelining is optimal) # Set ENABLE_PIPELINING=0 to disable (for research comparison only) # NOTE: As of 2025-11-19, all future experiments use both cache and pipelining enabled $(eval $(call kconfig_bool,ENABLE_PIPELINING,0)) # EMERGENCY_CONSOLE_ENABLED: 1 = REPL recovers from errors and continues (interactive fallthrough) # 0 = errors are non-recoverable; REPL exits and vm_fault_handler() is called instead # Default: 1 (dev/recovery builds) # Set EMERGENCY_CONSOLE_ENABLED=0 for production, embedded, or high-security builds # where no interactive fallthrough surface should exist. # Heartbeat family: repointed through the Kconfig bridge (Phase 4). Same # drop-in-replacement-for-`?=` behavior as the physics/SSM family (Phase 3). $(eval $(call kconfig_bool,EMERGENCY_CONSOLE_ENABLED,1)) # Heartbeat configuration # HEARTBEAT_THREAD_ENABLED: 1 = run vm_tick() in background thread (OPTIMAL), 0 = inline (legacy) # Default: 1 (experiments show threaded heartbeat is optimal for adaptive tuning) # NOTE: As of 2025-11-19, all future experiments use heartbeat enabled $(eval $(call kconfig_bool,HEARTBEAT_THREAD_ENABLED,1)) # HEARTBEAT_TICK_NS: Wake frequency for heartbeat thread (default 1ms) $(eval $(call kconfig_int,HEARTBEAT_TICK_NS,10000)) # HEARTBEAT_CHECK_FREQUENCY / HEARTBEAT_WINDOW_TUNING_FREQUENCY / # HEARTBEAT_SLOPE_VALIDATION_FREQUENCY: previously unwired in the hosted # Makefile -- no `?=` and no -D forwarding existed for any of these three # before Phase 4, so hosted builds always got starforth_config.h's bare # defaults (256/1000/5000) with no override mechanism at all. The kernel # Makefile has always had opt-in forwarding for them (VM_FEATURE_FLAG_VARS). # Promoted here for parity, same treatment as Phase 3's SSM_* knobs. $(eval $(call kconfig_int,HEARTBEAT_CHECK_FREQUENCY,256)) $(eval $(call kconfig_int,HEARTBEAT_WINDOW_TUNING_FREQUENCY,1000)) $(eval $(call kconfig_int,HEARTBEAT_SLOPE_VALIDATION_FREQUENCY,5000)) # HISTORICAL: L4Re had no pthreads, so this forced HEARTBEAT_THREAD_ENABLED # off whenever L4RE=1 was set. L4Re support is no longer an active target; # see "Platform support" below. # ifeq ($(L4RE),1) # HEARTBEAT_THREAD_ENABLED := 0 # endif # Feedback Loop Toggle Switches (for systematic performance measurement) # ============================================================================ # These allow turning each feedback loop on/off to measure additive performance gains # EXP_00-BASELINE: all loops OFF (plain FORTH-79) # EXP_01: Loop #1 ON # EXP_02: Loops #1-2 ON # ... etc, building cumulatively to measure each loop's contribution # ============================================================================== # SSM (Steady State Machine) Configuration - Data-Driven Architecture # ============================================================================== # Based on 2^7 DoE with 300 reps (128 configs, 38,400 runs). # Top 5% analysis (speed + stability) reveals optimal loop combinations. # # Experimentally Validated Architecture: # L1 (heat_tracking): DISABLED (harmful in 86% of top configs) # L4 (pipelining_metrics): DISABLED (harmful in 100% of top configs) # L7 (adaptive_heartrate): ALWAYS ON (beneficial in 71% of top configs) # L2, L3, L5, L6: Runtime-controlled by L8 (workload-dependent) # L8 (Jacquard): 4-bit mode selector (16 modes: C0-C15) # # L8 dynamically controls L2/L3/L5/L6 based on entropy, CV, and temporal metrics. # Top 5% validated modes: C4, C7, C9, C11, C12 (see src/ssm_jacquard.c) # Tuning Knobs (Physics-driven Optimization System) # ============================================================================ # Knob #7: ROLLING_WINDOW_SIZE (Initial execution history capture) # - Default: 4096 (conservative, ensures statistical significance at cold start) # - Usage: make ROLLING_WINDOW_SIZE=8192 (larger for complex workloads) # or ROLLING_WINDOW_SIZE=2048 (smaller for memory constraints) # - System AUTOMATICALLY shrinks during execution if diminishing returns detected # - Window starts conservative, self-tunes down if pattern diversity plateaus # - Window becomes "warm" (representative) after N executions, then adapts $(eval $(call kconfig_int,ROLLING_WINDOW_SIZE,4096)) # Knob #6: TRANSITION_WINDOW_SIZE (Pipelining context depth) # - Default: 8 (see starforth_config.h for the exact shipped value) # - Usage: make TRANSITION_WINDOW_SIZE=1 (or 2, 4 for shallower context) # - Empirically tuned via binary chop: 1 vs 2 vs 4 vs 8 $(eval $(call kconfig_int,TRANSITION_WINDOW_SIZE,8)) # SPECULATION_THRESHOLD_Q48 / SPECULATION_DEPTH / MIN_SAMPLES_FOR_SPECULATION / # MISPREDICTION_COST_Q48 / MINIMUM_PREFETCH_ROI: previously hardcoded, # non-overridable #define in physics_pipelining_metrics.h -- no Makefile # knob or -D forwarding existed for any of these five before Phase 5. # Promoted here for parity, same treatment as Phase 3/4's other # previously-unwired constants. All five are Q48.16-fixed-point or plain # counts meaningful only when ENABLE_PIPELINING is on; see Kconfig.physics # for the exact hex/decimal defaults. MINIMUM_PREFETCH_ROI's default was # corrected from the long-shipped 0x11999A to 0x1199A (rev y) -- the # former was ~17.6 in Q48.16, not the documented 1.10. $(eval $(call kconfig_int,SPECULATION_THRESHOLD_Q48,0x8000)) $(eval $(call kconfig_int,SPECULATION_DEPTH,1)) $(eval $(call kconfig_int,MIN_SAMPLES_FOR_SPECULATION,10)) $(eval $(call kconfig_int,MISPREDICTION_COST_Q48,0x190000)) $(eval $(call kconfig_int,MINIMUM_PREFETCH_ROI,0x1199A)) # Knobs #8-11: Adaptive Window Shrinking Control # ============================================================================ # These control how aggressively the rolling window learns and adapts # (NOT the initial window size - that's ROLLING_WINDOW_SIZE above) # Knob #8: ADAPTIVE_SHRINK_RATE (percentage to retain when shrinking) # - Default: 50 (shrink to 50% = discard 50% each cycle) # - Range: 50-95 (lower = more aggressive, higher = more conservative) # - Usage: make ADAPTIVE_SHRINK_RATE=75 (slower learning) or 90 (slow learning) $(eval $(call kconfig_int,ADAPTIVE_SHRINK_RATE,50)) # Knob #9: ADAPTIVE_MIN_WINDOW_SIZE (floor to prevent over-shrinking) # - Default: 256 (never shrink below 256 word IDs) # - Range: 64-1024 (smaller = leaner, larger = safer) # - Usage: make ADAPTIVE_MIN_WINDOW_SIZE=512 (conservative) or 128 (lean) $(eval $(call kconfig_int,ADAPTIVE_MIN_WINDOW_SIZE,256)) # Knob #10: ADAPTIVE_CHECK_FREQUENCY (how often to measure diversity) # - Default: 512 (check after every 512 executions) # - Range: 32-1024 (more = faster response, less = less overhead) # - Usage: make ADAPTIVE_CHECK_FREQUENCY=128 (responsive) or 256 (default of comparable knobs) $(eval $(call kconfig_int,ADAPTIVE_CHECK_FREQUENCY,512)) # Knob #11: ADAPTIVE_GROWTH_THRESHOLD (growth rate that signals saturation) # - Default: 5 (shrink when growth < 5%) # - Range: 0-10 (lower = eager shrinking, higher = cautious) # - Usage: make ADAPTIVE_GROWTH_THRESHOLD=0 (aggressive) or 1 (less conservative) $(eval $(call kconfig_int,ADAPTIVE_GROWTH_THRESHOLD,5)) # Knob #11a: INITIAL_DECAY_SLOPE_Q48 (Starting decay slope for inference engine) # - Default: 21845 (1/3 in Q48.16 format, = 0.333... starting slope) # - Range: 13107-43691 (0.2 to 0.67 in Q48.16) # - Usage: make INITIAL_DECAY_SLOPE_Q48=13107 (0.2) or 32768 (0.5) or 43691 (0.67) # - DoE: OPP #3 tests 0.2, 0.33, 0.5, 0.67 to find optimal starting point for convergence # - Note: Inference engine adapts this value at runtime; this is just the cold-start value $(eval $(call kconfig_int,INITIAL_DECAY_SLOPE_Q48,21845)) # Knob #11b: DECAY_MIN_INTERVAL (Minimum time before decay applies) # - Default: 500 (nanoseconds) # - Range: 500-5000 (balance between decay sensitivity and overhead) # - Usage: make DECAY_MIN_INTERVAL=1000 (slower decay) or 2000 (slower still) # - NOTE: as of the tick-based Loop #3 decay conversion (rev t, see # docs/working/architecture/VM-FLEET-ATTRACTOR-DESIGN-20260705.md), # Loop #3 no longer consults this value at all -- decay is gated on # elapsed heartbeat ticks, not elapsed nanoseconds. Kept only because # doe_metrics.c still reports it as a metrics field; informational, # not load-bearing. $(eval $(call kconfig_int,DECAY_MIN_INTERVAL,500)) # Knob #11d: HEARTBEAT_INFERENCE_FREQUENCY (How often to run inference engine) # - Default: 1000 (ticks between inference runs) # - Set VERY HIGH (999999) to effectively disable inference (static mode) # - Set LOW for frequent inference (adaptive mode) # - DoE: OPP #3 tests 999999 (static, no Loop #6) vs 1000 (adaptive, with Loop #6) # - Measures: Inference engine cost vs benefit in optimized performance $(eval $(call kconfig_int,HEARTBEAT_INFERENCE_FREQUENCY,1000)) # Knob #12: DECAY_RATE_PER_US_Q16 (Heat decay rate in Q16 fixed-point) # - Default: 1 (1/65536 heat units decayed per microsecond) # - Range: 0-65536 (0 = no decay, 65536 = 1 heat/µs) # - Half-life: ~6.5 seconds at default (100-heat word → 50-heat in 6.5s) # - Usage: make DECAY_RATE_PER_US_Q16=2 (faster decay) or 0 (no decay for baseline) # - DoE: Vary this to measure impact of heat decay on performance $(eval $(call kconfig_int,DECAY_RATE_PER_US_Q16,1)) # L8 Jacquard mode selector thresholds (ssm_jacquard.h). Previously unwired -- # no Makefile knob or -D forwarding existed for these before the Kconfig # migration's Phase 3; ssm_jacquard.h's own #ifndef fallback was the only # place any of these five values lived. See Kconfig.physics for the # rationale behind modeling the four float thresholds as Kconfig `string` # symbols (Kconfig has no native float type). $(eval $(call kconfig_int,SSM_ENTROPY_HIGH_THRESHOLD,0.75)) $(eval $(call kconfig_int,SSM_CV_HIGH_THRESHOLD,0.15)) $(eval $(call kconfig_int,SSM_TEMPORAL_DECAY_THRESHOLD,0.5)) $(eval $(call kconfig_int,SSM_TEMPORAL_DECAY_LOW_THRESHOLD,0.3)) $(eval $(call kconfig_int,SSM_HYSTERESIS_TICKS,5)) # Build the CFLAGS with tuning knobs # L8 FINAL INTEGRATION: All experimental loop flags removed. # L1-L7 are now always-on internal physics layers. # L8 Jacquard mode selector is the sole policy engine. BASE_CFLAGS = -std=c99 -Wall -Werror -Iinclude -Isrc/word_source -Isrc/test_runner/include \ -DSTRICT_PTR=$(STRICT_PTR) \ -DENABLE_HOTWORDS_CACHE=$(ENABLE_HOTWORDS_CACHE) \ -DENABLE_PIPELINING=$(ENABLE_PIPELINING) \ -DROLLING_WINDOW_SIZE=$(ROLLING_WINDOW_SIZE) \ -DTRANSITION_WINDOW_SIZE=$(TRANSITION_WINDOW_SIZE) \ -DSPECULATION_THRESHOLD_Q48=$(SPECULATION_THRESHOLD_Q48) \ -DSPECULATION_DEPTH=$(SPECULATION_DEPTH) \ -DMIN_SAMPLES_FOR_SPECULATION=$(MIN_SAMPLES_FOR_SPECULATION) \ -DMISPREDICTION_COST_Q48=$(MISPREDICTION_COST_Q48) \ -DMINIMUM_PREFETCH_ROI=$(MINIMUM_PREFETCH_ROI) \ -DADAPTIVE_SHRINK_RATE=$(ADAPTIVE_SHRINK_RATE) \ -DADAPTIVE_MIN_WINDOW_SIZE=$(ADAPTIVE_MIN_WINDOW_SIZE) \ -DADAPTIVE_CHECK_FREQUENCY=$(ADAPTIVE_CHECK_FREQUENCY) \ -DADAPTIVE_GROWTH_THRESHOLD=$(ADAPTIVE_GROWTH_THRESHOLD) \ -DINITIAL_DECAY_SLOPE_Q48=$(INITIAL_DECAY_SLOPE_Q48) \ -DDECAY_RATE_PER_US_Q16=$(DECAY_RATE_PER_US_Q16) \ -DHEARTBEAT_THREAD_ENABLED=$(HEARTBEAT_THREAD_ENABLED) \ -DHEARTBEAT_TICK_NS=$(HEARTBEAT_TICK_NS) \ -DHEARTBEAT_INFERENCE_FREQUENCY=$(HEARTBEAT_INFERENCE_FREQUENCY) \ -DHEARTBEAT_CHECK_FREQUENCY=$(HEARTBEAT_CHECK_FREQUENCY) \ -DHEARTBEAT_WINDOW_TUNING_FREQUENCY=$(HEARTBEAT_WINDOW_TUNING_FREQUENCY) \ -DHEARTBEAT_SLOPE_VALIDATION_FREQUENCY=$(HEARTBEAT_SLOPE_VALIDATION_FREQUENCY) \ -DEMERGENCY_CONSOLE_ENABLED=$(EMERGENCY_CONSOLE_ENABLED) \ -DSSM_ENTROPY_HIGH_THRESHOLD=$(SSM_ENTROPY_HIGH_THRESHOLD) \ -DSSM_CV_HIGH_THRESHOLD=$(SSM_CV_HIGH_THRESHOLD) \ -DSSM_TEMPORAL_DECAY_THRESHOLD=$(SSM_TEMPORAL_DECAY_THRESHOLD) \ -DSSM_TEMPORAL_DECAY_LOW_THRESHOLD=$(SSM_TEMPORAL_DECAY_LOW_THRESHOLD) \ -DSSM_HYSTERESIS_TICKS=$(SSM_HYSTERESIS_TICKS) ifeq ($(HEARTBEAT_THREAD_ENABLED),1) # HISTORICAL: this used to branch on L4RE (no pthreads there) -- # THREAD_FLAGS := if L4RE else -pthread. L4Re support is no longer an # active target; see "Platform support" below. THREAD_FLAGS := -pthread else THREAD_FLAGS := endif BASE_CFLAGS += $(THREAD_FLAGS) # Build profiles (TARGET values) SUPPORTED_TARGETS := standard fast fastest turbo asan TARGET_DESCRIPTION_standard := Standard optimized build TARGET_CFLAGS_standard := $(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O2 -flto=auto -fuse-linker-plugin -DNDEBUG \ -DUSE_ASM_OPT=1 \ -ffunction-sections -fdata-sections -fomit-frame-pointer \ -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-strict-aliasing TARGET_LDFLAGS_standard := -Wl,--gc-sections -s -flto=auto -fuse-linker-plugin -static $(THREAD_FLAGS) TARGET_DESCRIPTION_fast := Fast optimized build (no LTO, easier debugging) TARGET_CFLAGS_fast := $(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG TARGET_LDFLAGS_fast := -s $(THREAD_FLAGS) TARGET_DESCRIPTION_fastest := Maximum performance build (ASM + LTO + direct threading) TARGET_CFLAGS_fastest := $(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG \ -flto=auto -fuse-linker-plugin -funroll-loops -finline-functions -fomit-frame-pointer -fno-plt -fno-semantic-interposition TARGET_LDFLAGS_fastest := -flto=auto -fuse-linker-plugin -s -Wl,--gc-sections $(THREAD_FLAGS) TARGET_DESCRIPTION_turbo := Assembly-optimized build (no direct threading) TARGET_CFLAGS_turbo := $(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O3 -DUSE_ASM_OPT=1 -DNDEBUG -flto TARGET_LDFLAGS_turbo := -flto -s $(THREAD_FLAGS) TARGET_DESCRIPTION_asan := ASan/UBSan instrumented build for memory error detection TARGET_CFLAGS_asan := $(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O1 -g \ -fsanitize=address,undefined -fno-omit-frame-pointer TARGET_LDFLAGS_asan := -fsanitize=address,undefined $(THREAD_FLAGS) define TARGET_POST_fastest @echo "" @echo "🎯 You've built the FASTEST FORTH IN THE WEST!" @echo "" @echo "Quick-draw benchmark:" @time -f " ⏱️ Time: %E seconds" ./$(BINARY) -c ": BENCH 1000000 0 DO 1 2 + DROP LOOP ; BENCH BYE" 2>/dev/null || true endef TARGET_DESCRIPTION := $(TARGET_DESCRIPTION_$(TARGET)) ifeq ($(strip $(TARGET_DESCRIPTION)),) $(error Unknown TARGET '$(TARGET)'. Supported targets: $(SUPPORTED_TARGETS)) endif TARGET_CFLAGS_SELECTED := $(TARGET_CFLAGS_$(TARGET)) TARGET_LDFLAGS_SELECTED := $(TARGET_LDFLAGS_$(TARGET)) TARGET_POST := $(or $(TARGET_POST_$(TARGET)),@:) CFLAGS ?= $(TARGET_CFLAGS_SELECTED) LDFLAGS ?= $(TARGET_LDFLAGS_SELECTED) CFLAGS += $(EXTRA_CFLAGS) LDFLAGS += $(EXTRA_LDFLAGS) USE_ASM_OPT := $(if $(findstring -DUSE_ASM_OPT=1,$(CFLAGS)),1,0) USE_LTO := $(if $(or $(findstring -flto,$(CFLAGS)),$(findstring -flto,$(LDFLAGS))),1,0) # Platform support # # HISTORICAL: L4Re/Fiasco.OC was a supported platform target (`make L4RE=1`) # through mid-2026. Removed as an active target; src/platform/l4re/time.c # is retained for reference but no longer wired into any build. To restore: # reinstate the `else ifdef L4RE` branch below and the L4RE-conditional # logic marked HISTORICAL elsewhere in this Makefile (search "L4RE"/"L4Re"). # # ifdef MINIMAL # ... # else ifdef L4RE # CFLAGS += -D__l4__=1 # PLATFORM_TIME_SRC = src/platform/l4re/time.c src/platform/platform_init.c # PLATFORM_ALLOC_SRC = src/platform/alloc_kernel.c # else # ... # endif ifdef MINIMAL # DISABLED 2026-07-08: this was never a finished feature, not a small bug. # src/platform/starforth_minimal.c (referenced below) does not exist # anywhere in the repo, and only 5 of ~90 source files under src/ have any # STARFORTH_MINIMAL awareness despite -nostdlib -ffreestanding applying to # the entire tree. Confirmed by trying it: even a throwaway empty stub for # the missing file lets the build report "success" while linking a binary # with no _start entry point (this build's specific -nostdlib/-static/ # -pthread/-flto flag combination doesn't actually exclude libc, so most # calls silently resolve anyway -- the illusion of success is the trap). # Reviving this needs an actual freestanding platform layer written from # scratch, not a quick fix -- failing loudly here instead of letting # anyone hit a confusing compile error or, worse, a binary that "builds" # but can't run. See .claude/CLAUDE.md's punch list for status. $(error MINIMAL=1 build is disabled: incomplete freestanding platform layer, not currently supported. See the comment immediately above this line for why) CFLAGS += -DSTARFORTH_MINIMAL=1 -nostdlib -ffreestanding LDFLAGS += -nostdlib PLATFORM_SRC = src/platform/starforth_minimal.c PLATFORM_ALLOC_SRC = src/platform/alloc_kernel.c else PLATFORM_TIME_SRC = src/platform/linux/time.c src/platform/platform_init.c PLATFORM_ALLOC_SRC = src/platform/alloc_host.c endif PLATFORM_COMMON_SRC = src/platform/threading.c $(PLATFORM_ALLOC_SRC) # Source and object files SRC = $(wildcard src/*.c src/word_source/*.c src/test_runner/*.c src/test_runner/modules/*.c) $(PLATFORM_SRC) $(PLATFORM_TIME_SRC) $(PLATFORM_COMMON_SRC) BUILD_ROOT ?= build build_dir = $(BUILD_ROOT)/$(call canon_arch,$(or $2,$(ARCH)))/$1 binary_path = $(call build_dir,$1,$2)/starforth BUILD_DIR = $(call build_dir,$(TARGET),$(ARCH)) BINARY = $(call binary_path,$(TARGET),$(ARCH)) OBJ = $(patsubst src/%.c,$(BUILD_DIR)/%.o,$(SRC)) ASM_FILES = $(patsubst src/%.c,$(BUILD_DIR)/%.s,$(SRC)) # Profiler support for test targets PROFILE_LEVEL := $(strip $(PROFILE)) ifeq ($(PROFILE_LEVEL),) PROFILE_ARGS := else PROFILE_ARGS := --profile $(PROFILE_LEVEL) PROFILE_REPORT ?= 1 ifneq ($(strip $(PROFILE_REPORT)),0) PROFILE_ARGS += --profile-report endif endif # Installation directories PREFIX ?= . BINDIR = $(PREFIX)/bin MANDIR = $(PREFIX)/share/man/man1 INFODIR = $(PREFIX)/share/info DOCDIR = $(PREFIX)/share/doc/starforth CONFDIR = $(PREFIX)/etc/starforth # ============================================================================== # PHONY TARGETS # ============================================================================== .PHONY: all banner help clean clean-obj clean-docs starkernel mkcapsule capsule-gen capsule-manifest .PHONY: fastest fast turbo .PHONY: rpi4 rpi4-cross rpi4-fastest riscv64-clang .PHONY: minimal debug profile performance .PHONY: test bench benchmark .PHONY: asm sbom .PHONY: docs api-docs docs-latex docs-isabelle isabelle-build isabelle-check info math-companion math-companion-clean .PHONY: refinement-status refinement-init refinement-phase1 verify-defect refinement-annotate-check refinement-report .PHONY: install uninstall package deb rpm .PHONY: quality compile_commands clang_tidy cppcheck gcc_analyzer .PHONY: FORCE version-info build-manifest # ============================================================================== # MAIN TARGETS # ============================================================================== all: banner $(BINARY) @echo "" @echo "✓ Build complete: $(BINARY)" @echo " Architecture: $(ARCH_NAME)" @echo " Profile: $(TARGET_DESCRIPTION) ($(TARGET))" @echo " Output directory: $(BUILD_DIR)" @echo " Optimizations: $(if $(findstring USE_ASM_OPT,$(CFLAGS)),Enabled,Disabled)" @echo " Direct Threading: $(if $(findstring USE_DIRECT_THREADING,$(CFLAGS)),Enabled,Disabled)" @mkdir -p lfs/$(ARCH_DIR) @cp $(BINARY) lfs/$(ARCH_DIR)/starforth @echo " LFS copy: lfs/$(ARCH_DIR)/starforth" $(TARGET_POST) banner: @echo "════════════════════════════════════════════════════════════" @echo " ⚡ Building the Fastest Forth in the West! ⚡" @echo "════════════════════════════════════════════════════════════" @echo " Target Architecture: $(ARCH_NAME) ($(ARCH_DIR))" @echo " Build Profile: $(TARGET_DESCRIPTION) ($(TARGET))" @echo "" # ============================================================================== # OPTIMIZATION BUILDS - The Fastest in the West # ============================================================================== # Maximum performance - no compromises fastest: @echo "🏆 Building FASTEST configuration..." @echo " - Architecture: $(ARCH_NAME)" @echo " - Assembly optimizations: ENABLED" @echo " - Direct threading: ENABLED" @echo "" @$(MAKE) TARGET=fastest all # Fast without LTO (easier debugging) fast: @echo "⚡ Building FAST configuration (no LTO for debugging)..." @$(MAKE) TARGET=fast all # Assembly optimizations only (no direct threading) turbo: @echo "🚀 Building TURBO configuration (ASM only)..." @$(MAKE) TARGET=turbo all # ============================================================================== # PLATFORM-SPECIFIC BUILDS # ============================================================================== # Raspberry Pi 4 - native build rpi4: @echo "🥧 Building for Raspberry Pi 4 (native)..." @$(MAKE) ARCH=raspi TARGET=fastest CFLAGS="$(BASE_CFLAGS) -march=armv8-a+crc+simd -mtune=cortex-a72 -DARCH_ARM64=1 -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG -flto" LDFLAGS="-flto -s" all @echo "✓ Raspberry Pi build ready: $(call binary_path,fastest,raspi)" # Raspberry Pi 4 - cross-compile from x86_64 rpi4-cross: @echo "🥧 Cross-compiling for Raspberry Pi 4..." @$(MAKE) ARCH=raspi TARGET=fastest CC=aarch64-linux-gnu-gcc \ CFLAGS="$(BASE_CFLAGS) -march=armv8-a+crc+simd -mtune=cortex-a72 -DARCH_ARM64=1 -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG -flto -static" \ LDFLAGS="-flto -s -static" \ all @echo "✓ Cross-compiled binary ready: $(call binary_path,fastest,raspi)" @echo " Copy to RPi4: scp $(call binary_path,fastest,raspi) pi@raspberrypi.local:~/" # Raspberry Pi 4 - maximum optimization rpi4-fastest: @echo "🥧⚡ Building FASTEST for Raspberry Pi 4..." @$(MAKE) ARCH=raspi TARGET=fastest CC=aarch64-linux-gnu-gcc \ CFLAGS="$(BASE_CFLAGS) -march=armv8-a+crc+simd+crypto -mtune=cortex-a72 -DARCH_ARM64=1 -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG -flto -funroll-loops -finline-functions" \ LDFLAGS="-flto -s -static" \ all @echo "✓ Raspberry Pi FASTEST build ready: $(call binary_path,fastest,raspi)" # RISC-V 64 - cross-compile via clang (GCC fails to build this tree under # -std=c99: a nanosleep visibility failure, not yet root-caused — see # docs/lithosananke/hosted-acceptance-test/README.md and # docs/working/archive/session-logs/2026-07-24-punch-list.md item #3). # CFLAGS deliberately does NOT reuse $(BASE_CFLAGS): BASE_CFLAGS hardcodes # -std=c99, and clang needs -std=c11 -pthread here. This mirrors the exact # recipe verified in the hosted-acceptance-test doc, kept in sync with it. riscv64-clang: @echo "🦀 Cross-compiling for RISC-V 64 (clang)..." @$(MAKE) ARCH=riscv64 TARGET=fastest \ CC="clang-18 --target=riscv64-linux-gnu --sysroot=/usr/riscv64-linux-gnu" \ CFLAGS="-std=c11 -pthread -Wall -Werror -Iinclude -Isrc/word_source -Isrc/test_runner/include -DSTRICT_PTR=1 -march=rv64gc -mabi=lp64d -mcmodel=medany -DARCH_RISCV64=1 -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG -flto -static" \ LDFLAGS="-flto -s -static -fuse-ld=lld" \ all @echo "✓ Cross-compiled binary ready: $(call binary_path,fastest,riscv64)" @echo " Run: qemu-riscv64 $(call binary_path,fastest,riscv64)" # Minimal/embedded build minimal: $(MAKE) MINIMAL=1 # HISTORICAL: L4Re support removed as an active target (see "Platform # support" above). This target cross-compiled a minimal build with the # L4Re toolchain (l4-gcc) for smoke-testing without real L4Re headers. # fake-l4re: # $(MAKE) MINIMAL=1 CC=l4-gcc CFLAGS="$(BASE_CFLAGS) -DL4RE_TARGET=1" # ============================================================================== # DEBUG & DEVELOPMENT BUILDS # ============================================================================== # Debug build with symbols debug: banner @echo "🐛 Building DEBUG configuration..." $(MAKE) CFLAGS="$(BASE_CFLAGS) -O0 -g -DDEBUG" LDFLAGS="" $(BINARY) # Build with profiler support profile: $(MAKE) CFLAGS="$(BASE_CFLAGS) -g -O1" $(BINARY) # Performance build (legacy alias - redirects to 'fastest') performance: fastest # ============================================================================== # BUILD RULES # ============================================================================== # Generate version header from Makefile VERSION, ARCH, TARGET, and timestamp include/version.h: FORCE @mkdir -p include @echo "Generating version header..." @BUILD_TS=$$(date -Iseconds 2>/dev/null || echo "unknown"); \ printf '#ifndef STARFORTH_VERSION_H\n#define STARFORTH_VERSION_H\n\n' > $@; \ printf '#define STARFORTH_VERSION "%s"\n' "$(VERSION)" >> $@; \ printf '#define STARFORTH_ARCH "%s"\n' "$(ARCH_NAME)" >> $@; \ printf '#define STARFORTH_TARGET "%s"\n' "$(TARGET)" >> $@; \ printf '#define STARFORTH_TIMESTAMP "%s"\n' "$$BUILD_TS" >> $@; \ printf '#define STARFORTH_VERSION_FULL "StarForth v%s %s %s %s"\n\n' "$(VERSION)" "$(ARCH_NAME)" "$(TARGET)" "$$BUILD_TS" >> $@; \ printf '#endif /* STARFORTH_VERSION_H */\n' >> $@ # Ensure version.h exists and is up-to-date before compilation FORCE: # ============================================================================== # CAPSULE SUBSYSTEM # ============================================================================== CAPSULES_DIR ?= capsules MKCAPSULE_SRC = tools/mkcapsule.c MKCAPSULE_BIN = $(BUILD_DIR)/tools/mkcapsule CAPSULE_GENERATED = $(BUILD_DIR)/capsule_generated.c CAPSULE_GENERATED_OBJ = $(BUILD_DIR)/capsule_generated.o CAPSULE_MANIFEST = $(CAPSULES_DIR)/MANIFEST_AUTO.md # Build the mkcapsule host tool $(MKCAPSULE_BIN): $(MKCAPSULE_SRC) @mkdir -p $(dir $@) @echo " HOSTCC $<" @$(CC) -O2 -Wall -o $@ $< # Run mkcapsule to generate the capsule directory C source $(CAPSULE_GENERATED): $(MKCAPSULE_BIN) $(CAPSULES_DIR) @mkdir -p $(dir $@) @echo " MKCAP $(CAPSULES_DIR) -> $@" @$(MKCAPSULE_BIN) $(CAPSULES_DIR) $@ # Compile the generated capsule directory $(CAPSULE_GENERATED_OBJ): $(CAPSULE_GENERATED) | include/version.h @mkdir -p $(dir $@) @echo " CC $<" @$(CC) $(CFLAGS) -c $< -o $@ .PHONY: mkcapsule capsule-gen capsule-manifest mkcapsule: $(MKCAPSULE_BIN) capsule-manifest: $(MKCAPSULE_BIN) @echo " MANIFEST $(CAPSULES_DIR) -> $(CAPSULE_MANIFEST)" @$(MKCAPSULE_BIN) --manifest $(CAPSULES_DIR) $(CAPSULE_MANIFEST) capsule-gen: $(CAPSULE_GENERATED) capsule-manifest # starkernel: full capsule-aware build # 1. Build mkcapsule tool # 2. Generate capsule_generated.c from capsules/ # 3. Compile StarForth with __STARKERNEL__ and capsule_generated.o linked in starkernel: $(MKCAPSULE_BIN) $(CAPSULE_GENERATED_OBJ) @$(MAKE) $(BINARY) \ EXTRA_CFLAGS="-D__STARKERNEL__=1 -Iinclude" \ EXTRA_OBJ="$(CAPSULE_GENERATED_OBJ)" # ============================================================================== # Main executable $(BINARY): $(OBJ) $(EXTRA_OBJ) @echo "🔗 Linking $(BINARY)..." @mkdir -p $(dir $@) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) # Pattern rule for all C files (consolidates 5 repetitive rules) # Order-only dependency: version.h must exist before compilation, but changes don't trigger rebuild $(BUILD_DIR)/%.o: src/%.c | include/version.h @mkdir -p $(dir $@) @echo " CC $<" @$(CC) $(CFLAGS) -c $< -o $@ ifdef ASM @$(CC) $(CFLAGS) -S $(ASM_SYNTAX) $< -o $(patsubst %.o,%.s,$@) endif # Files that include version.h - rebuild when timestamp changes $(BUILD_DIR)/main.o: include/version.h $(BUILD_DIR)/cli.o: include/version.h $(BUILD_DIR)/word_source/starforth_words.o: include/version.h # ============================================================================== # TESTING & BENCHMARKING # ============================================================================== # Quick benchmark bench: $(BINARY) @echo "" @echo "🏇 Running quick-draw benchmark..." @echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" @echo "Test: 1 million stack operations" @time -f "Time: %E seconds" ./$(BINARY) -c ": BENCH 1000000 0 DO 1 2 + DROP LOOP ; BENCH BYE" 2>/dev/null || echo "Benchmark complete" @echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Full benchmark suite benchmark: $(BINARY) @echo "" @echo "🎯 Full Benchmark Suite - Fastest Forth in the West" @echo "════════════════════════════════════════════════════════════" @echo "" @echo "📊 Stack Operations (1M iterations):" @time -f " ⏱️ %E seconds" ./$(BINARY) -c ": BENCH-STACK 1000000 0 DO 1 2 3 DROP DROP DROP LOOP ; BENCH-STACK BYE" 2>/dev/null || true @echo "" @echo "📊 Arithmetic Operations (1M iterations):" @time -f " ⏱️ %E seconds" ./$(BINARY) -c ": BENCH-MATH 1000000 0 DO 10 20 + 5 * 100 / DROP LOOP ; BENCH-MATH BYE" 2>/dev/null || true @echo "" @echo "📊 Logic Operations (1M iterations):" @time -f " ⏱️ %E seconds" ./$(BINARY) -c ": BENCH-LOGIC 1000000 0 DO 255 DUP AND DUP OR XOR DROP LOOP ; BENCH-LOGIC BYE" 2>/dev/null || true @echo "" @echo "════════════════════════════════════════════════════════════" # ============================================================================== # ASSEMBLY OUTPUT & ANALYSIS # ============================================================================== # Generate assembly files for inspection asm: banner @echo "📝 Generating assembly output..." @echo " Cleaning to force rebuild..." @$(MAKE) clean-obj > /dev/null 2>&1 $(MAKE) ASM=1 CFLAGS="$(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1" LDFLAGS="" $(BINARY) @echo "✓ Assembly files generated in $(BUILD_DIR)/" @echo " Files generated: $$(find $(BUILD_DIR) -name '*.s' | wc -l)" @echo " Example: less $(BUILD_DIR)/stack_management.s" # Generate Software Bill of Materials (SBOM) in SPDX format sbom: @echo "📋 Generating SBOM (Software Bill of Materials)..." @if ! which syft >/dev/null 2>&1; then \ echo "Error: syft not found. Install from https://github.com/anchore/syft"; \ exit 1; \ fi @syft dir:. -o spdx-json=sbom.spdx.json -o spdx=sbom.spdx \ --source-name StarForth \ --source-version $(VERSION) \ --exclude './build/**' --exclude './tools/**' --exclude './.git/**' @echo "✅ SBOM generated:" @echo " SPDX JSON: sbom.spdx.json" @echo " SPDX: sbom.spdx" @echo " Version: $(VERSION)" # ============================================================================== # DOCUMENTATION # ============================================================================== # Generate API documentation (Doxygen XML → AsciiDoc) api-docs: @echo "📚 Generating API documentation from Doxygen..." @if [ ! -f scripts/generate-doxygen-appendix.sh ]; then \ echo "Error: scripts/generate-doxygen-appendix.sh not found"; \ exit 1; \ fi @./scripts/generate-doxygen-appendix.sh @echo "✅ API documentation generated: docs/src/appendix/" # ---------------------------------------------------------------------------- # Math companion to the SSRN paper. # Builds docs/SSRN_companion/Math_Companion_SSRN.pdf from the LaTeX source. # Requires: pdflatex (texlive-latex-base, texlive-latex-extra, texlive-science). # ---------------------------------------------------------------------------- math-companion: @echo "Building SSRN math companion (docs/SSRN_companion/Math_Companion_SSRN.pdf)..." @if ! command -v pdflatex >/dev/null 2>&1; then \ echo "Error: pdflatex not found."; \ echo " Install with: apt-get install texlive-latex-base texlive-latex-extra texlive-science"; \ exit 1; \ fi @cd docs/SSRN_companion && \ pdflatex -interaction=nonstopmode -halt-on-error Math_Companion_SSRN.tex >/dev/null && \ pdflatex -interaction=nonstopmode -halt-on-error Math_Companion_SSRN.tex >/dev/null && \ pdflatex -interaction=nonstopmode -halt-on-error Math_Companion_SSRN.tex >/dev/null @echo "Built: docs/SSRN_companion/Math_Companion_SSRN.pdf" @cd docs/SSRN_companion && ls -lh Math_Companion_SSRN.pdf | awk '{print " Size:", $$5, " Pages: see pdfinfo"}' math-companion-clean: @rm -f docs/SSRN_companion/Math_Companion_SSRN.{aux,log,out,toc,lof,lot,nav,snm,vrb} @rm -f docs/SSRN_companion/Math_Companion_SSRN.pdf @echo "Cleaned math companion build artifacts." # Convert all AsciiDoc to LaTeX docs-latex: @echo "📄 Converting AsciiDoc to LaTeX..." @if [ ! -f scripts/asciidoc-to-latex.sh ]; then \ echo "Error: scripts/asciidoc-to-latex.sh not found"; \ exit 1; \ fi @./scripts/asciidoc-to-latex.sh @echo "✅ LaTeX files generated: docs/latex/" # Build and verify Isabelle theories isabelle-build: @echo "🔬 Building Isabelle formal verification theories..." @if ! command -v $(ISABELLE) >/dev/null 2>&1; then \ echo "Error: Isabelle not found: $(ISABELLE)"; \ echo " Install Isabelle or set ISABELLE variable"; \ exit 1; \ fi @echo " Using: $$(command -v $(ISABELLE))" @echo " Building StarForth_Formal session (VM + Physics)..." @cd docs/src/internal/formal && $(ISABELLE) build -v -d . StarForth_Formal @echo "✅ Isabelle theories verified successfully" # Quick check of Isabelle theories (faster than full build) isabelle-check: @echo "🔬 Quick-checking Isabelle theories..." @if ! command -v $(ISABELLE) >/dev/null 2>&1; then \ echo "Error: Isabelle not found: $(ISABELLE)"; \ exit 1; \ fi @cd docs/src/internal/formal && \ for thy in *.thy; do \ echo " Checking $$thy..."; \ $(ISABELLE) process -T "$$thy" 2>&1 | head -5; \ done @echo "✅ Quick check complete" # Generate comprehensive Isabelle documentation (for auditing & CI/CD) # This generates documentation and verification reports regardless of proof status # Suitable for Jenkinsfile pipelines - shows complete state including errors docs-isabelle: @echo "📚 Generating comprehensive Isabelle documentation (audit mode)..." @mkdir -p docs/src/isabelle @echo "" @echo "Step 1: Attempting to verify Isabelle theories..." @echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" @cd docs/src/internal/formal && \ if $(ISABELLE) build -v -d . StarForth_Formal > "$(CURDIR)/docs/src/isabelle/build.log" 2>&1; then \ echo "✅ All theories verified successfully"; \ else \ echo "⚠️ Some theories have incomplete proofs (see build.log for details)"; \ fi @echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" @echo "" @echo "Step 2: Generating documentation from theory source..." @if [ ! -f scripts/isabelle-to-adoc.sh ]; then \ echo "⚠️ Warning: scripts/isabelle-to-adoc.sh not found"; \ echo " Creating default script..."; \ mkdir -p scripts; \ echo '#!/bin/bash' > scripts/isabelle-to-adoc.sh; \ echo 'echo "Generating Isabelle theory reports to AsciiDoc..."' >> scripts/isabelle-to-adoc.sh; \ echo 'cd docs/src/internal/formal' >> scripts/isabelle-to-adoc.sh; \ echo 'for thy in *.thy; do' >> scripts/isabelle-to-adoc.sh; \ echo ' base=$$(basename "$$thy" .thy)' >> scripts/isabelle-to-adoc.sh; \ echo ' echo "= $$base Theory" > "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \ echo ' echo "" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \ echo ' echo "Formal verification theory for StarForth VM." >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \ echo ' echo "" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \ echo ' echo "== Theory Source" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \ echo ' echo "" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \ echo ' echo "[source,isabelle]" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \ echo ' echo "----" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \ echo ' cat "$$thy" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \ echo ' echo "----" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \ echo 'done' >> scripts/isabelle-to-adoc.sh; \ chmod +x scripts/isabelle-to-adoc.sh; \ fi @bash scripts/isabelle-to-adoc.sh @echo "" @echo "Step 3: Creating audit summary..." @echo "✅ Comprehensive Isabelle documentation generated: docs/src/isabelle/" @echo "" @echo "📂 Documentation includes:" @echo " • index.adoc - Master index of all theories" @echo " • VERIFICATION_REPORT.adoc - Audit report" @echo " • .adoc - Individual theory documentation with source" @echo " • build.log - Isabelle build output (verification status)" # ============================================================================== # REFINEMENT VERIFICATION (C ⊑ Isabelle) # ============================================================================== # Refinement status dashboard refinement-status: @echo "════════════════════════════════════════════════════════════" @echo " StarForth C ⊑ Isabelle Refinement Status" @echo "════════════════════════════════════════════════════════════" @echo "" @if [ ! -f docs/REFINEMENT_CAPA.adoc ]; then \ echo "⚠️ No REFINEMENT_CAPA.adoc found. Initialize with: make refinement-init"; \ echo ""; \ echo "Refinement infrastructure status: UNINITIALIZED"; \ else \ echo "📋 Defect Summary:"; \ echo ""; \ echo -n " Total Defects: "; grep -c "^== DEFECT-" docs/REFINEMENT_CAPA.adoc || echo "0"; \ echo -n " OPEN: "; grep "| OPEN" docs/REFINEMENT_CAPA.adoc | wc -l; \ echo -n " IN-PROGRESS: "; grep "| IN-PROGRESS" docs/REFINEMENT_CAPA.adoc | wc -l; \ echo -n " CLOSED: "; grep "| CLOSED" docs/REFINEMENT_CAPA.adoc | wc -l; \ fi @echo "" @echo "🏆 Phase Status:" @echo " Phase 1: vm.c core .............. NOT STARTED" @echo " Phase 2: Root utilities ......... NOT STARTED" @echo " Phase 3: Test harness .......... NOT STARTED" @echo " Phase 4: Primitive dictionary .. NOT STARTED" @echo " Phase 5: VM expansion .......... NOT STARTED" @echo "" @echo "📂 Documentation:" @echo " • docs/REFINEMENT_CAPA.adoc - Defect tracking" @echo " • docs/REFINEMENT_ANNOTATIONS.adoc - Code annotation guide" @echo "" @echo "Next steps: make refinement-phase1 (to start Phase 1 proofs)" @echo "════════════════════════════════════════════════════════════" # Initialize refinement tracking refinement-init: @echo "Initializing refinement tracking..." @if [ -f docs/REFINEMENT_CAPA.adoc ]; then \ echo "✅ docs/REFINEMENT_CAPA.adoc already exists"; \ else \ echo "📋 Creating REFINEMENT_CAPA.adoc"; \ fi @if [ -f docs/REFINEMENT_ANNOTATIONS.adoc ]; then \ echo "✅ docs/REFINEMENT_ANNOTATIONS.adoc already exists"; \ else \ echo "📋 Creating REFINEMENT_ANNOTATIONS.adoc"; \ fi @echo "✅ Refinement infrastructure ready" @echo "" @echo "Next: Review docs/REFINEMENT_ANNOTATIONS.adoc" @echo " Then: make refinement-phase1" # Phase 1: VM Core refinement refinement-phase1: @echo "════════════════════════════════════════════════════════════" @echo " ⚙️ PHASE 1: VM Core Refinement (C ⊑ Isabelle)" @echo "════════════════════════════════════════════════════════════" @echo "" @echo "Building refinement proofs for:" @echo " • stack_push / stack_pop (DEFECT-001, DEFECT-002)" @echo " • execute_call / execute_return (DEFECT-002)" @echo " • instruction_dispatch (DEFECT-003)" @echo "" @echo "Proof target: StarForth_Refinement (Isabelle theory)" @if ! command -v $(ISABELLE) >/dev/null 2>&1; then \ echo "Error: Isabelle not found. Install or set ISABELLE variable"; \ exit 1; \ fi @echo "" @echo "Step 1: Creating StarForth_Refinement.thy if needed..." @if [ ! -f docs/src/internal/formal/StarForth_Refinement.thy ]; then \ echo "Creating template..."; \ mkdir -p docs/src/internal/formal; \ touch docs/src/internal/formal/StarForth_Refinement.thy; \ echo "TODO: Add refinement proofs here" > docs/src/internal/formal/StarForth_Refinement.thy; \ fi @echo "" @echo "Step 2: Attempting to build refinement proofs..." @echo "(This will fail until proofs are written)" @cd docs/src/internal/formal && \ $(ISABELLE) build -v -d . StarForth_Formal 2>&1 | tail -20 @echo "" @echo "📋 Next steps:" @echo " 1. Review open defects: grep 'OPEN' docs/REFINEMENT_CAPA.adoc" @echo " 2. Start working on DEFECT-001 (stack_push)" @echo " 3. Add refinement proofs to StarForth_Refinement.thy" @echo " 4. Run: make verify-defect DEFECT=001" # Verify specific defect resolution verify-defect: @if [ -z "$(DEFECT)" ]; then \ echo "Usage: make verify-defect DEFECT=001"; \ echo ""; \ echo "Available defects:"; \ grep "== DEFECT-" docs/REFINEMENT_CAPA.adoc | sed 's/== / /'; \ exit 1; \ fi @echo "════════════════════════════════════════════════════════════" @echo " 🔍 Verifying Resolution: $(DEFECT)" @echo "════════════════════════════════════════════════════════════" @echo "" @if ! grep -q "== DEFECT-$(DEFECT):" docs/REFINEMENT_CAPA.adoc; then \ echo "❌ DEFECT-$(DEFECT) not found in REFINEMENT_CAPA.adoc"; \ exit 1; \ fi @echo "Defect Details:" @grep -A 30 "== DEFECT-$(DEFECT):" docs/REFINEMENT_CAPA.adoc | head -35 @echo "" @echo "Next: Implement corrective action, then run this again" # Check code annotation coverage refinement-annotate-check: @echo "════════════════════════════════════════════════════════════" @echo " 📝 Checking Code Annotation Coverage" @echo "════════════════════════════════════════════════════════════" @echo "" @echo "Phase 1 target functions:" @echo "" @for func in stack_push stack_pop execute_instruction execute_call execute_return; do \ if grep -q "REFINEMENT.*$$func\|void $$func" src/vm.c; then \ if grep -q "REFINEMENT:" src/vm.c | grep -q "$$func"; then \ echo " ✅ $$func - ANNOTATED"; \ else \ echo " ❌ $$func - MISSING ANNOTATION"; \ fi; \ else \ echo " ⚠️ $$func - NOT FOUND"; \ fi; \ done @echo "" @echo "Guide: docs/REFINEMENT_ANNOTATIONS.adoc" # Generate refinement report refinement-report: @echo "🔄 Generating refinement status report..." @mkdir -p docs/reports @echo "# StarForth Refinement Status" > docs/reports/refinement-status.md @echo "" >> docs/reports/refinement-status.md @echo "Generated: $$(date)" >> docs/reports/refinement-status.md @echo "" >> docs/reports/refinement-status.md @echo "## Summary" >> docs/reports/refinement-status.md @echo "" >> docs/reports/refinement-status.md @grep -c "== DEFECT-" docs/REFINEMENT_CAPA.adoc | xargs -I {} echo "Total Defects: {}" >> docs/reports/refinement-status.md @echo "" >> docs/reports/refinement-status.md @echo "See docs/REFINEMENT_CAPA.adoc for details" >> docs/reports/refinement-status.md @echo "✅ Report: docs/reports/refinement-status.md" # Generate GNU info documentation docs/starforth.info: @if [ ! -f docs/starforth.texi ]; then \ echo "⚠️ Skipping info docs: docs/starforth.texi not present"; \ touch docs/starforth.info; \ else \ echo "📖 Building GNU info documentation..."; \ if ! command -v makeinfo >/dev/null 2>&1; then \ echo "Warning: makeinfo not found. Install with: sudo apt-get install texinfo"; \ touch docs/starforth.info; \ else \ makeinfo docs/starforth.texi -o docs/starforth.info && \ echo "✅ Info documentation: docs/starforth.info"; \ fi; \ fi # Generate all documentation docs: api-docs docs-isabelle @echo "✅ All documentation generated!" @echo "" @echo "📂 Documentation locations:" @echo " • API docs: docs/src/appendix/" @echo " • Isabelle docs: docs/src/isabelle/" @echo " • LaTeX: docs/latex/" # Clean generated documentation clean-docs: @echo "🗑️ Cleaning generated documentation..." @rm -rf docs/src/appendix/ docs/latex/ docs/src/isabelle/ @echo "✅ Documentation cleaned" # ============================================================================== # INSTALLATION # ============================================================================== install: $(BINARY) @echo "📦 Installing StarForth to $(PREFIX)..." @install -d $(BINDIR) @install -m 755 $(BINARY) $(BINDIR)/starforth @echo " ✓ Binary installed: $(BINDIR)/starforth" @if [ -f capsules/init.4th ]; then \ install -d $(CONFDIR); \ install -m 644 capsules/init.4th $(CONFDIR)/init.4th; \ echo " ✓ Config installed: $(CONFDIR)/init.4th"; \ fi @if [ -f man/starforth.1 ]; then \ install -d $(MANDIR); \ install -m 644 man/starforth.1 $(MANDIR)/starforth.1; \ echo " ✓ Man page installed: $(MANDIR)/starforth.1"; \ fi @if [ -f docs/starforth.info ]; then \ install -d $(INFODIR); \ install -m 644 docs/starforth.info $(INFODIR)/starforth.info; \ install-info --info-dir=$(INFODIR) $(INFODIR)/starforth.info 2>/dev/null || true; \ echo " ✓ Info docs installed: $(INFODIR)/starforth.info"; \ fi @install -d $(DOCDIR) @if [ -f README.md ]; then install -m 644 README.md $(DOCDIR)/; fi @if [ -f QUICKSTART.md ]; then install -m 644 QUICKSTART.md $(DOCDIR)/; fi @if ls docs/*.md >/dev/null 2>&1; then install -m 644 docs/*.md $(DOCDIR)/ 2>/dev/null || true; fi @echo " ✓ Documentation installed: $(DOCDIR)/" @echo "✅ Installation complete!" uninstall: @echo "🗑️ Uninstalling StarForth from $(PREFIX)..." @rm -f $(BINDIR)/starforth @rm -f $(MANDIR)/starforth.1 @install-info --delete --info-dir=$(INFODIR) $(INFODIR)/starforth.info 2>/dev/null || true @rm -f $(INFODIR)/starforth.info @rm -rf $(DOCDIR) @rm -rf $(CONFDIR) @echo "✅ Uninstall complete!" # ============================================================================== # PACKAGING # ============================================================================== # Build Debian package deb: $(BINARY) @echo "📦 Building Debian package..." @if ! command -v fpm >/dev/null 2>&1; then \ echo "Error: fpm not found. Install with: gem install fpm"; \ echo " or: sudo apt-get install ruby-dev && gem install fpm"; \ exit 1; \ fi @mkdir -p package/deb @fpm -s dir -t deb \ -n starforth \ -v $(VERSION) \ -a native \ --description "StarForth - The Fastest Forth in the West" \ --url "https://github.com/yourusername/starforth" \ --maintainer "StarForth Team" \ --license "MIT" \ --category "devel" \ --package package/deb/ \ --deb-compression xz \ $(BINARY)=/usr/bin/starforth \ capsules/init.4th=/etc/starforth/init.4th \ README.md=/usr/share/doc/starforth/README.md @echo "✅ Debian package created: package/deb/starforth_$(VERSION)_*.deb" # Build RPM package rpm: $(BINARY) @echo "📦 Building RPM package..." @if ! command -v fpm >/dev/null 2>&1; then \ echo "Error: fpm not found. Install with: gem install fpm"; \ exit 1; \ fi @mkdir -p package/rpm @fpm -s dir -t rpm \ -n starforth \ -v $(VERSION) \ -a native \ --description "StarForth - The Fastest Forth in the West" \ --url "https://github.com/yourusername/starforth" \ --maintainer "StarForth Team" \ --license "MIT" \ --category "Development/Languages" \ --package package/rpm/ \ $(BINARY)=/usr/bin/starforth \ capsules/init.4th=/etc/starforth/init.4th \ README.md=/usr/share/doc/starforth/README.md @echo "✅ RPM package created: package/rpm/starforth-$(VERSION)-*.rpm" # Build all packages package: deb rpm @echo "" @echo "✅ All packages built successfully!" @echo " Debian: package/deb/" @echo " RPM: package/rpm/" # ============================================================================== # VERSIONING & BUILD MANIFEST (Workflow/Governance Integration) # ============================================================================== # Parse version from include/version.h GET_VERSION = $(shell grep "define STARFORTH_VERSION" include/version.h | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+' | head -1) .PHONY: version-info version-info: @echo "Current Version: $(GET_VERSION)" @grep "STARFORTH_VERSION" include/version.h | grep define .PHONY: build-manifest build-manifest: $(BINARY) @echo "📋 Generating BUILD_MANIFEST.json..." @mkdir -p builds/manifest @VERSION=$$(grep "STARFORTH_VERSION_STRING" include/version.h | grep -o '"[^"]*"' | tr -d '"'); \ GIT_HASH=$$(git rev-parse --short HEAD 2>/dev/null || echo "0000000"); \ GIT_BRANCH=$$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown"); \ BUILD_TIME=$$(date -Iseconds); \ BINARY_PATH="$(BINARY)"; \ if [ -f "$$BINARY_PATH" ]; then \ BINARY_SHA256=$$(sha256sum "$$BINARY_PATH" | awk '{print $$1}'); \ else \ BINARY_SHA256="not-built"; \ fi; \ cat > builds/manifest/BUILD_MANIFEST.json << EOF; \ { \ "version": "$$VERSION", \ "timestamp": "$$BUILD_TIME", \ "git": { \ "commit_hash": "$$GIT_HASH", \ "branch": "$$GIT_BRANCH", \ "tag": "v$$VERSION" \ }, \ "build": { \ "timestamp": "$$BUILD_TIME", \ "compiler": "$$(gcc --version | head -1)", \ "compiler_flags": "$(CFLAGS) $(ARCH_FLAGS)", \ "target_architecture": "$(ARCH_NAME)", \ "use_asm_opt": $(USE_ASM_OPT), \ "use_lto": $(USE_LTO) \ }, \ "testing": { \ "total_tests": 936, \ "passed_tests": 0, \ "failed_tests": 0, \ "code_coverage_percent": 0, \ "smoke_test_result": "N/A" \ }, \ "binary": { \ "path": "$$BINARY_PATH", \ "sha256": "$$BINARY_SHA256" \ }, \ "signatures": { \ "builder": "jenkins-agent", \ "approval_timestamp": "$(shell date -Iseconds)" \ } \ } \ EOF @echo "✓ BUILD_MANIFEST created: builds/manifest/BUILD_MANIFEST.json" @cat builds/manifest/BUILD_MANIFEST.json | head -20 # ============================================================================== # CLEANUP # ============================================================================== clean: @echo "🧹 Cleaning build artifacts..." @rm -rfv build/* @rm -f src/*.gcda src/word_source/*.gcda src/*.gcno src/word_source/*.gcno @echo "✓ Clean complete" clean-obj: @echo "🧹 Cleaning object files..." @find build -name "*.o" -type f -delete 2>/dev/null || true @echo "✓ Object files cleaned" # ============================================================================== # HELP # ============================================================================== help: @echo "════════════════════════════════════════════════════════════" @echo " ⚡ StarForth Build System - Fastest in the West! ⚡" @echo "════════════════════════════════════════════════════════════" @echo "" @echo "🛠️ Variables:" @echo " ARCH= (default: $(ARCH); canonical directory: $(ARCH_DIR))" @echo " TARGET= (standard | fast | fastest | turbo)" @echo "" @echo "🏆 OPTIMIZATION BUILDS:" @echo " fastest - Maximum performance (ASM + direct threading + LTO)" @echo " fast - Fast without LTO (easier debugging)" @echo " turbo - Assembly optimizations only" @echo "" @echo "🥧 PLATFORM-SPECIFIC BUILDS:" @echo " rpi4 - Native build on Raspberry Pi 4" @echo " rpi4-cross - Cross-compile from x86_64" @echo " rpi4-fastest - Maximum optimization for RPi4" @echo " riscv64-clang - Cross-compile for RISC-V 64 via clang (GCC fails here)" @echo " minimal - Minimal/embedded build" @echo "" @echo "🔧 DEBUG & DEVELOPMENT:" @echo " all - Standard optimized build (default)" @echo " debug - Debug build with symbols (-g -O0)" @echo " profile - Build with profiling support" @echo " performance - Legacy performance build (use 'fastest')" @echo "" @echo "🧪 TESTING & BENCHMARKING:" @echo " test - Run full test suite" @echo " bench - Quick benchmark" @echo " benchmark - Full benchmark suite" @echo "" @echo "📝 ANALYSIS & DOCUMENTATION:" @echo " asm - Generate assembly output" @echo " sbom - Generate SBOM (Software Bill of Materials, SPDX)" @echo " docs - Generate all documentation" @echo " api-docs - Generate API docs (Doxygen → AsciiDoc)" @echo " docs-latex - Convert AsciiDoc to LaTeX" @echo " docs-isabelle - Generate Isabelle documentation for auditing (includes build log)" @echo " isabelle-build - Build and verify Isabelle formal theories (strict verification)" @echo " isabelle-check - Quick-check Isabelle theories" @echo " info - Build GNU info documentation" @echo "" @echo "🔬 REFINEMENT VERIFICATION (C ⊑ Isabelle):" @echo " refinement-init - Initialize refinement tracking (CAPA + annotations)" @echo " refinement-status - Show refinement defect summary and phase status" @echo " refinement-phase1 - Build Phase 1 refinement proofs (vm.c core)" @echo " verify-defect - Verify specific defect (Usage: make verify-defect DEFECT=001)" @echo " refinement-annotate-check - Check code annotation coverage" @echo " refinement-report - Generate refinement status report" @echo "" @echo "📦 INSTALLATION & PACKAGING:" @echo " install - Install to PREFIX (default: .)" @echo " uninstall - Uninstall from PREFIX" @echo " deb - Build Debian package" @echo " rpm - Build RPM package" @echo " package - Build all packages" @echo "" @echo "🧹 CLEANUP:" @echo " clean - Remove all build artifacts" @echo " clean-obj - Remove object files only" @echo " clean-docs - Remove generated documentation" @echo "" @echo "⚙️ CONFIGURATION VARIABLES:" @echo " CC - Compiler (default: gcc)" @echo " CFLAGS - Compiler flags (override defaults)" @echo " LDFLAGS - Linker flags (override defaults)" @echo " PREFIX - Install prefix (default: .)" @echo " MINIMAL=1 - Minimal build mode (DISABLED -- incomplete, errors out)" @echo " ASM=1 - Generate assembly files during build" @echo "" @echo "📋 EXAMPLES:" @echo " make fastest # Fastest build for current platform" @echo " make rpi4-cross # Cross-compile for Raspberry Pi 4" @echo " make riscv64-clang # Cross-compile for RISC-V 64 (clang)" @echo " make asm # Generate assembly for inspection" @echo " make debug # Debug build" @echo " make PREFIX=/usr/local install # Install system-wide" @echo "" @echo "🌟 CURRENT PLATFORM: $(ARCH_NAME)" @echo "════════════════════════════════════════════════════════════" quality: compile_commands clang_tidy cppcheck gcc_analyzer compile_commands: bear --output build/compile_commands.json -- make -j$(nproc) clang_tidy: clang-tidy -p build $(shell find src -name '*.c' -o -name '*.h') | tee build/clang-tidy.txt cppcheck: cppcheck --enable=all --inconclusive --std=c99 src 2> build/cppcheck.txt || true gcc_analyzer: $(MAKE) clean CFLAGS="$(CFLAGS) -fanalyzer -Wall -Wextra" $(MAKE) all 2>&1 | tee build/gcc-analyzer.txt