Code review fixes, all compile clean (hosted gcc + aarch64/riscv64 kernel flags):
- repl.c (H1): reentrancy guards on the MSG-TICK idle pump. sk_repl_idle()
now defers when Hera is mid-interpret (g_mama_interpreting) or when its
own vm_interpret is on the stack (g_idle_pump_active), so a blocking
KEY/EXPECT/QUERY inside a dispatched line can no longer re-enter the
interpreter and clobber the in-flight input buffer.
- virtio_rng.c: clamp device-returned used_len to VRNG_BUF_SIZE before the
caller's data_buf copy, closing a device-controlled OOB read.
- block_subsystem.c: first-write path now keys off created_time==0 instead
of dead magic==0 so fresh blocks get a real created_time stamp; first_free/
last_allocated fixed to absolute Forth LBNs (set in blk_compute_fresh_geometry
from slot->start_lbn, no longer the wrong physical-BAM-index values from
compute_totals_from_B); physical-bounds guard on blk_meta_zone_read/write
prevents unsigned underflow on a corrupt fence >= device size.
- capsule_zuse_boot.c / capsule_wirebind.c: identity seed validated magic ->
version -> CRC-64 (compute_crc64 over offsetof(crc)) before trusting it,
so a corrupt/format-mismatched record is refused, never loaded.
- log.h / starkernel/log.h: unused LOG_LINE_MAX 256 renamed LOG_MSG_LINE_MAX
to lift the include-order collision with vm.h's LOG_LINE_MAX 64; stale
include-order comments dropped (kernel_main.c, shim.c, capsule_birth.c).
- FABRIC-3.md: three stale-doc carry-forward items closed [x] with cbe7b49
notes.
Real KEY/?TERMINAL/QUERY/EXPECT bodies (console WIP):
- repl.h/repl.c: sk_console_getkey()/sk_console_key_available()/
sk_console_readline() public bodies; non-destructive peek buffers the
found byte so a following KEY returns it.
- shim.c: getchar()/fgetc()/fgets()/sf_terminal_ready() routed through the
real console paths instead of stubs; sf_terminal_ready() in platform_io.h
with sf_terminal_ready() implemented for the hosted build (linux/io.c,
POSIX select on fd 0) wired into Makefile.
- io_words.c: ?TERMINAL now returns actual terminal-readiness, not constant 0.
Artifacts: minted disk/artemis.img + rebuilt lfs kernel; BLOCK_MAP.md,
doe csv + qemu log regenerated.
1341 lines
62 KiB
Makefile
1341 lines
62 KiB
Makefile
# ==============================================================================
|
||
# 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/linux/io.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 " • <Theory>.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=<arch> (default: $(ARCH); canonical directory: $(ARCH_DIR))"
|
||
@echo " TARGET=<profile> (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
|