Files
RedBear-OS/Makefile
T
vasilito c2f5bb09fc feat: build-system hardening (validate gate, cache safeguards, status sync)
Phase 1 remediation of the build-system assessment:

- Makefile: wire 'validate' target into build/live/reimage flows; surface
  lint-config and init-service validators as a first-class gate.
- mk/disk.mk: add 'validate' target running lint-config, init-service
  validator, and file-ownership validator; suppress noisy unmount warnings
  that masked real failures.
- mk/redbear.mk: add source-fingerprint tracking so integrate-redbear.sh
  re-runs when local/recipes, local/Assets, or local/firmware change.
- src/cook/cook_build.rs: atomic dep_hashes.toml write (tmp + rename) to
  prevent torn-write cache corruption; binary-store restore now checks
  dep_hashes before silent restore; fix production bug in
  collect_files_recursive that silently dropped subdirectories whose
  name matched an exclude pattern.
- src/cook/fetch.rs, src/cook/fetch_repo.rs: harden atomic patch
  application and protected-recipe gating.
- AGENTS.md, local/AGENTS.md, local/docs/COLLISION-DETECTION-STATUS.md:
  sync collision-detection status to 'implemented (Phase 15.0)' now that
  CollisionTracker is wired across all four installer layers
  (installer submodule pointer tracked separately in 13cc6fb0c3).

Verified: cargo check (0 errors), cargo test --lib (35/35 passed),
cargo clippy (0 new warnings vs baseline), make -n validate, make -n live.
2026-07-18 16:30:23 +09:00

292 lines
10 KiB
Makefile

# This file contains the build system commands configuration
# and environment variables
include mk/config.mk
# Build system dependencies
include mk/depends.mk
# Delete a target's output file if its recipe exits with a non-zero status.
# Without this, a partially-written artifact (e.g. a build that died mid-link,
# a cookbook crash leaving a corrupt pkgar) survives as "up to date" and Make
# skips rebuilding it on the next run — producing silently wrong output.
# This is a global setting; it applies to every file target in this Makefile
# and every target defined by the mk/*.mk fragments included below.
.DELETE_ON_ERROR:
# Make's default shell is /bin/sh without -e. Recipes rely on `&&` chaining
# for sequence safety, which is correct but fragile when an author forgets a
# chain. Forcing -e would be the safer default but would change semantics for
# existing recipes that depend on the `|| true` idiom for optional cleanup.
# We instead rely on .DELETE_ON_ERROR above + per-recipe `&&` chains + the
# cookbook Rust binary's own Result-based error propagation.
#
# If a future audit confirms no recipe depends on shell-level error tolerance,
# adding `.SHELLFLAGS := -euo pipefail -c` here would be the next hardening.
all: lint-config $(BUILD)/harddrive.img
# Post-build validation gate. Runs init-service checks, file-ownership checks,
# and config-vs-package collision detection against the freshly built image.
# This is a MANDATORY part of `make all` — a build that produces a broken image
# (init services pointing at missing binaries, config overrides that will be
# silently overwritten by package staging, file ownership conflicts) is not a
# successful build even if the image file exists.
#
# Escape hatch: REDBEAR_NO_VALIDATE=1 skips the entire validation chain.
# Use only when you understand why validation is failing and have a separate
# plan to fix it. The collision-detection component has its own finer-grained
# escape hatch (REDBEAR_COLLISIONS_WARN=1) that downgrades strict failures to
# warnings while still running the rest of the gate.
ifneq ($(REDBEAR_NO_VALIDATE),1)
all: validate
endif
# ── Red Bear OS Build Cache (OBLIGATORY) ─────────────────────────────────
# Cache sync is a mandatory part of every successful build.
# The git-tracked cache survives make clean, make distclean, and git clone.
#
# Flow:
# make all → cache-restore (if needed) → build → cache-sync → cache-commit
#
# Commands:
# make cache-sync Sync built → git cache (manual)
# make cache-commit Sync + git commit (manual)
# make cache-restore Restore from git cache
# make cache-status Compare cache vs build state
CACHE_SYNC = local/scripts/cache-sync.sh
CACHE_SAVE = local/scripts/snapshot-cache.sh
CACHE_RESTORE = local/scripts/restore-cache.sh
cache-sync:
@bash $(CACHE_SYNC)
cache-commit:
@bash $(CACHE_SYNC) --commit
cache-restore:
@echo "Red Bear: restoring from git-tracked cache..."
@bash $(CACHE_SYNC) --restore
@if ls local/cache/rbos-cache-* >/dev/null 2>&1; then \
echo "Red Bear: restoring recipe stage artifacts from snapshot..."; \
bash $(CACHE_RESTORE); \
echo "Red Bear: verifying cache integrity..."; \
bash $(CACHE_RESTORE) --verify || { \
echo "Red Bear: \033[1;31;49mCACHE INTEGRITY CHECK FAILED\033[0m — restored artifacts may be incomplete."; \
echo "The build may produce incorrect results. Inspect local/cache/ and re-run snapshot-cache.sh if needed."; \
exit 1; \
}; \
else \
echo "Red Bear: no cache snapshot found (first build or cache purged) — full build required"; \
fi
cache-save:
@bash $(CACHE_SAVE)
cache-save-essential:
@bash $(CACHE_SAVE) --essential
cache-verify:
@bash $(CACHE_RESTORE) --verify
# Phase J / Improvement C: verify that all Cargo [patch]
# sections in the local sources' Cargo.toml files resolve
# to the expected local fork paths. Run after `make all`
# to confirm Phase J end-to-end. Returns non-zero exit on
# any unresolved patch.
verify-patches:
@bash local/scripts/check-cargo-patches.sh
# Phase I: also run the file-level patch check
verify-file-patches:
@bash local/scripts/check-unwired-patches.sh --strict
# Verify everything: file-level patches + Cargo [patch] sections
verify-all: verify-file-patches verify-patches
cache-list:
@bash $(CACHE_SAVE) --list
cache-status:
@bash $(CACHE_SYNC) --status
# Obligatory cache pipeline — runs before AND after every build
cache-auto:
@# ── BEFORE BUILD: restore cache if target is empty ──
@if [ ! -f $(BUILD)/repo.tag ]; then \
if ls local/cache/pkgar/*/stage.pkgar >/dev/null 2>&1; then \
echo "Red Bear: restoring build cache..."; \
bash $(CACHE_SYNC) --restore; \
fi; \
fi
@# ── AFTER BUILD: sync cache back to git-tracked storage ──
@if [ -f $(BUILD)/harddrive.img ]; then \
echo "Red Bear: syncing build cache..."; \
bash $(CACHE_SYNC); \
echo "Red Bear: committing cache to git..."; \
bash $(CACHE_SYNC) --commit 2>/dev/null || echo "Red Bear: cache commit skipped (no changes or not in git repo)"; \
fi
$(BUILD)/harddrive.img: cache-auto
live:
-$(FUMOUNT) $(BUILD)/filesystem/ || true
-$(FUMOUNT) /tmp/redbear_installer/ || true
rm -f $(LIVE_ISO) $(LIVE_IMG) $(LIVE_BOOTLOADER) $(LIVE_IPXE)
$(MAKE) $(LIVE_ISO)
popsicle: $(LIVE_ISO)
popsicle-gtk $(LIVE_ISO)
image:
-$(FUMOUNT) $(BUILD)/filesystem/ || true
-$(FUMOUNT) /tmp/redbear_installer/ || true
rm -f $(BUILD)/harddrive.img $(LIVE_ISO) $(LIVE_IMG) $(LIVE_BOOTLOADER) $(LIVE_IPXE)
$(MAKE) all
rebuild:
-$(FUMOUNT) $(BUILD)/filesystem/ || true
-$(FUMOUNT) /tmp/redbear_installer/ || true
rm -rf $(BUILD)/repo.tag $(BUILD)/harddrive.img $(LIVE_ISO) $(LIVE_IMG) $(LIVE_BOOTLOADER) $(LIVE_IPXE)
$(MAKE) all
# To tell that it's not safe
# to execute the cookbook binary
NOT_ON_PODMAN?=0
clean:
ifeq ($(PODMAN_BUILD),1)
ifneq ("$(wildcard $(CONTAINER_TAG))","")
$(PODMAN_RUN) make $@
else
$(info will not run cookbook clean as container is not built)
$(MAKE) clean PODMAN_BUILD=0 NOT_ON_PODMAN=1 SKIP_CHECK_TOOLS=1
endif # CONTAINER_TAG
else
ifneq ($(NOT_ON_PODMAN),1)
$(MAKE) repo_clean
-$(FUMOUNT) $(BUILD)/filesystem/ || true
-$(FUMOUNT) /tmp/redbear_installer/ || true
endif # NOT_ON_PODMAN
rm -rf repo
rm -rf $(BUILD) $(PREFIX)
$(MAKE) fstools_clean
endif # PODMAN_BUILD
# distclean: removes build artifacts, toolchain, and upstream source trees.
# local/ overlay source trees are PROTECTED — the repo binary ALWAYS refuses
# to unfetch local overlay recipes (they are internal Red Bear subprojects
# with no upstream). This is unconditional: no env var or flag can override it.
# This is the safe default for Red Bear OS. local/ is NEVER deleted.
distclean:
ifneq ($(REDBEAR_RELEASE),)
$(error distclean is disabled in release mode (REDBEAR_RELEASE=$(REDBEAR_RELEASE)). Sources are immutable. Use: make clean (build artifacts only, safe))
endif
ifeq ($(PODMAN_BUILD),1)
ifneq ("$(wildcard $(CONTAINER_TAG))","")
$(PODMAN_RUN) make $@
else
$(info will not run cookbook unfetch as container is not built)
$(MAKE) distclean PODMAN_BUILD=0 NOT_ON_PODMAN=1 SKIP_CHECK_TOOLS=1
endif # CONTAINER_TAG
else
ifneq ($(NOT_ON_PODMAN),1)
$(info ==> distclean: cleaning build artifacts and upstream source trees)
$(info ==> local/ overlay sources are PROTECTED and will NOT be deleted)
$(MAKE) fetch_clean
endif # NOT_ON_PODMAN
$(MAKE) clean NOT_ON_PODMAN=1
endif # PODMAN_BUILD
# distclean-nuclear: now a no-op for local/ recipes — local/ sources are
# unconditionally immutable. This target remains for compatibility but
# behaves identically to distclean. Use rm -rf directly if you really
# want to delete local/ sources (NOT recommended — local/ is internal).
distclean-nuclear:
ifeq ($(PODMAN_BUILD),1)
$(info distclean-nuclear is not supported in Podman mode; use native build)
else
$(warning !! distclean-nuclear is a no-op for local/ recipes; local/ is immutable)
$(MAKE) fetch_clean
$(MAKE) clean NOT_ON_PODMAN=1
endif # PODMAN_BUILD
pull:
git pull
rm -f $(FSTOOLS_TAG)
repo: $(BUILD)/repo.tag
repo_clean: c.--all
fetch_clean: u.--all
# Podman build recipes and vars
include mk/podman.mk
# Disk Imaging and Cookbook tools
include mk/fstools.mk
# Cross compiler recipes
include mk/prefix.mk
# Repository maintenance
include mk/repo.mk
# Disk images
include mk/disk.mk
# Emulation recipes
include mk/qemu.mk
include mk/virtualbox.mk
# CI
include mk/ci.mk
include mk/redbear.mk
# Ensure Red Bear OS integration runs before repo cook and disk image creation
$(BUILD)/harddrive.img: $(REDBEAR_TAG)
$(LIVE_ISO): $(REDBEAR_TAG)
$(REPO_TAG): $(REDBEAR_TAG)
env: prefix FORCE $(CONTAINER_TAG)
ifeq ($(PODMAN_BUILD),1)
$(PODMAN_RUN) make $@
else
export PATH="$(PREFIX_PATH):$$PATH" && \
bash
endif
setenv: FORCE
@echo export ARCH='$(ARCH)'
@echo export BOARD='$(BOARD)'
@echo export CONFIG_NAME='$(CONFIG_NAME)'
@echo BUILD='$(BUILD)'
export RUST_GDB=gdb-multiarch # Necessary when debugging for another architecture than the host
GDB_KERNEL_FILE=recipes/core/kernel/target/$(TARGET)/build/kernel.sym
gdb: FORCE
rust-gdb $(GDB_KERNEL_FILE) --eval-command="target remote :1234"
# This target allows debugging a userspace application without requiring gdbserver running inside
# the VM. Because gdb doesn't know when the userspace application is scheduled by the kernel and as
# it stops the entire VM rather than just the userspace application that the user wants to debug,
# connecting to a gdbserver running inside the VM is highly encouraged when possible. This target
# should only be used when the application to debug runs early during boot before the network stack
# has started or you need to debug the interaction between the application and the kernel.
# tl;dr: DO NOT USE THIS TARGET UNLESS YOU HAVE TO
gdb-userspace: FORCE
rust-gdb $(GDB_APP_FILE) --eval-command="add-symbol-file $(GDB_KERNEL_FILE)" --eval-command="target remote :1234"
# An empty target
FORCE:
# Wireshark
wireshark: FORCE
wireshark $(BUILD)/network.pcap
packages-sync: ; @bash local/scripts/sync-packages.sh
packages-list: ; @ls -la Packages/*.pkgar 2>/dev/null | wc -l && echo "pkgar files in Packages/"
validate-patches:
@bash local/scripts/validate-patches.sh