| 1 | # todo-spinel. See README.md for what each piece is. |
| 2 | # |
| 3 | # make build dist/app for this machine (x86-64, dynamic glibc) |
| 4 | # make build-arm64 dist/app-linux-arm64 (static, aarch64) |
| 5 | # make test test/run.sh against dist/app, plain and under GC stress |
| 6 | # make deploy ship dist/app-linux-arm64 to Carloku |
| 7 | |
| 8 | SHELL := bash |
| 9 | .SHELLFLAGS := -euo pipefail -c |
| 10 | .DELETE_ON_ERROR: |
| 11 | |
| 12 | TC ?= $(HOME)/.local/share/bakeoff-toolchains |
| 13 | SPINEL_HOST ?= $(TC)/spinel-src/spinel/bin/spinel |
| 14 | SPINEL_ARM64?= $(TC)/spinel-arm64/bin/spinel |
| 15 | TEP_SRC ?= $(TC)/spinel-src/tep |
| 16 | SPINELKIT ?= $(TC)/spinel-src/spinelkit |
| 17 | RUBY ?= $(shell PATH="$(HOME)/.pixi/bin:$$PATH" command -v ruby) |
| 18 | ARM64 := $(abspath ../../scripts/arm64.sh) |
| 19 | |
| 20 | SQLITE_VER := 3500400 |
| 21 | SQLITE_ZIP := .cache/src/sqlite-amalgamation-$(SQLITE_VER).zip |
| 22 | SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-$(SQLITE_VER).zip |
| 23 | SQLITE_SHA := 1d3049dd0f830a025a53105fc79fd2ab9431aea99e137809d064d8ee8356b032 |
| 24 | SQLITE_DIR := .cache/src/sqlite-amalgamation-$(SQLITE_VER) |
| 25 | # One connection, used from one thread; no extension loading (static glibc |
| 26 | # can't dlopen anyway). |
| 27 | SQLITE_DEFS := -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_DQS=0 \ |
| 28 | -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS |
| 29 | |
| 30 | PATCHES := $(sort $(wildcard patches/*.patch)) |
| 31 | SOURCES := app.rb $(wildcard assets/static/*) |
| 32 | VERSION := $(shell git rev-parse --short HEAD 2>/dev/null || echo dev) |
| 33 | |
| 34 | export LANG := C.UTF-8 |
| 35 | export LC_ALL := C.UTF-8 |
| 36 | export SPINELKIT_DIR := $(SPINELKIT) |
| 37 | export TEP_QUIET := 1 |
| 38 | |
| 39 | .PHONY: build build-arm64 test clean clean-deps bump unbump deploy deps FORCE |
| 40 | |
| 41 | build: dist/app |
| 42 | build-arm64: dist/app-linux-arm64 |
| 43 | |
| 44 | # ---- version stamp --------------------------------------------------------- |
| 45 | # Rewritten only when the commit changes, so it doesn't force rebuilds. |
| 46 | version.rb: FORCE |
| 47 | @printf '# Written by make; not checked in.\nAPP_VERSION = "%s"\n' '$(VERSION)' > $@.tmp |
| 48 | @if cmp -s $@.tmp $@; then rm $@.tmp; else mv $@.tmp $@; fi |
| 49 | |
| 50 | # ---- third-party sources (kept by clean-deps) ------------------------------- |
| 51 | $(SQLITE_ZIP): |
| 52 | mkdir -p $(@D) |
| 53 | curl -sSfL -o $@.part $(SQLITE_URL) |
| 54 | echo "$(SQLITE_SHA) $@.part" | sha256sum -c --quiet |
| 55 | mv $@.part $@ |
| 56 | |
| 57 | $(SQLITE_DIR)/sqlite3.c: $(SQLITE_ZIP) |
| 58 | python3 -I -m zipfile -e $< .cache/src |
| 59 | touch $@ |
| 60 | |
| 61 | # ---- per-target compiled deps (removed by clean-deps) ----------------------- |
| 62 | # A patched copy of tep per target: tep bakes the paths of its C shims |
| 63 | # (sphttp.o, tep_sqlite.o) into the translated source, so each target's |
| 64 | # copy holds that target's objects. |
| 65 | .cache/%/tep/.patched: $(PATCHES) |
| 66 | rm -rf .cache/$*/tep |
| 67 | mkdir -p .cache/$* |
| 68 | cp -a $(TEP_SRC) .cache/$*/tep |
| 69 | rm -rf .cache/$*/tep/.git |
| 70 | for p in $(PATCHES); do patch -s -p1 -d .cache/$*/tep < $$p; done |
| 71 | touch $@ |
| 72 | |
| 73 | # Host (x86-64): the system cc. |
| 74 | .cache/x86/libsqlite3.a: $(SQLITE_DIR)/sqlite3.c |
| 75 | mkdir -p $(@D) |
| 76 | cc -O2 $(SQLITE_DEFS) -c $< -o .cache/x86/sqlite3.o |
| 77 | ar rcs $@ .cache/x86/sqlite3.o |
| 78 | |
| 79 | .cache/x86/tep/tep/sphttp.o: .cache/x86/tep/.patched |
| 80 | cc -O2 -c .cache/x86/tep/tep/sphttp.c -o $@ |
| 81 | |
| 82 | .cache/x86/tep/native/sqlite/tep_sqlite.o: .cache/x86/tep/.patched $(SQLITE_DIR)/sqlite3.c |
| 83 | cc -O2 -I$(SQLITE_DIR) -c .cache/x86/tep/native/sqlite/tep_sqlite.c -o $@ |
| 84 | |
| 85 | # arm64: gcc inside the emulated arm64 root (slow: SQLite takes minutes). |
| 86 | .cache/arm64/libsqlite3.a: $(SQLITE_DIR)/sqlite3.c |
| 87 | mkdir -p $(@D) |
| 88 | $(ARM64) cc -O2 $(SQLITE_DEFS) -c $< -o .cache/arm64/sqlite3.o |
| 89 | $(ARM64) ar rcs $@ .cache/arm64/sqlite3.o |
| 90 | |
| 91 | .cache/arm64/tep/tep/sphttp.o: .cache/arm64/tep/.patched |
| 92 | $(ARM64) cc -O2 -c .cache/arm64/tep/tep/sphttp.c -o $@ |
| 93 | |
| 94 | .cache/arm64/tep/native/sqlite/tep_sqlite.o: .cache/arm64/tep/.patched $(SQLITE_DIR)/sqlite3.c |
| 95 | $(ARM64) cc -O2 -I$(SQLITE_DIR) -c .cache/arm64/tep/native/sqlite/tep_sqlite.c -o $@ |
| 96 | |
| 97 | X86_DEPS := .cache/x86/libsqlite3.a .cache/x86/tep/tep/sphttp.o .cache/x86/tep/native/sqlite/tep_sqlite.o |
| 98 | ARM64_DEPS := .cache/arm64/libsqlite3.a .cache/arm64/tep/tep/sphttp.o .cache/arm64/tep/native/sqlite/tep_sqlite.o |
| 99 | |
| 100 | deps: $(X86_DEPS) $(ARM64_DEPS) |
| 101 | |
| 102 | # ---- the app --------------------------------------------------------------- |
| 103 | # tep translates app.rb (writing .app.tep.rb beside it) and runs Spinel. |
| 104 | dist/app: $(SOURCES) version.rb $(X86_DEPS) |
| 105 | mkdir -p dist .cache/x86/tmp |
| 106 | TMPDIR=$(abspath .cache/x86/tmp) SPINEL=$(SPINEL_HOST) LIBRARY_PATH=$(abspath .cache/x86) \ |
| 107 | $(RUBY) .cache/x86/tep/bin/tep build app.rb -o $@ |
| 108 | |
| 109 | # The host Spinel turns Ruby into C (fast, native); scripts/arm64-cc then |
| 110 | # compiles and links that C statically inside the arm64 root. |
| 111 | dist/app-linux-arm64: $(SOURCES) version.rb $(ARM64_DEPS) |
| 112 | mkdir -p dist .cache/arm64/tmp |
| 113 | TMPDIR=$(abspath .cache/arm64/tmp) SPINEL=$(abspath scripts/spinel-cross) \ |
| 114 | SPINEL_HOST=$(SPINEL_HOST) SPINEL_ARM64=$(SPINEL_ARM64) \ |
| 115 | ARM64_WORK=$(abspath .cache/arm64/work) ARM64_LIB_DIR=$(abspath .cache/arm64) \ |
| 116 | $(RUBY) .cache/arm64/tep/bin/tep build app.rb -o $@ |
| 117 | file $@ | grep -q 'ARM aarch64.*statically linked' |
| 118 | |
| 119 | # ---- the rest ---------------------------------------------------------------- |
| 120 | # The second run collects garbage every few KB, which shakes out values |
| 121 | # the generated code forgot to root (one such bug is why tep is patched). |
| 122 | test: dist/app |
| 123 | test/run.sh dist/app |
| 124 | SPINEL_GC_STRESS=1 test/run.sh dist/app |
| 125 | |
| 126 | clean: |
| 127 | rm -rf dist .app.tep.rb version.rb .cache/x86/tmp .cache/arm64/tmp .cache/arm64/work |
| 128 | |
| 129 | # Every compiled third-party artifact goes; the downloaded SQLite source stays. |
| 130 | clean-deps: clean |
| 131 | rm -rf .cache/x86 .cache/arm64 |
| 132 | |
| 133 | BUMP_FROM := BUILD_NOTE = "todo-spinel" |
| 134 | BUMP_TO := BUILD_NOTE = "todo-spinel (bumped)" |
| 135 | |
| 136 | bump: |
| 137 | grep -qF '$(BUMP_FROM)' app.rb |
| 138 | sed -i 's/^$(BUMP_FROM)$$/$(BUMP_TO)/' app.rb |
| 139 | |
| 140 | unbump: |
| 141 | grep -qF '$(BUMP_TO)' app.rb |
| 142 | sed -i 's/^$(BUMP_TO)$$/$(BUMP_FROM)/' app.rb |
| 143 | |
| 144 | MESSAGE ?= Deploy todo-spinel $(VERSION) |
| 145 | |
| 146 | deploy: dist/app-linux-arm64 |
| 147 | carlos deploy --account bfj --app todo-spinel --target linux-arm64 --message "$(MESSAGE)" dist/app-linux-arm64 |
| 148 | |