aboutsummaryrefslogtreecommitdiff
path: root/Makefile
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 07:28:39 +0000
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 07:28:39 +0000
commit7451eb090d890fb5ea5e4d40627a1b4df15a49de (patch)
tree357bdf4b528e860bffa688865306f656b788d2c2 /Makefile
parentanalyzer: close the ownership gaps that stopped the compiler compiling itself (diff)
build: embed images with #embed, keep everything generated in build/
the cores take their genome in with c23 #embed through src/core/genome.h, so images are plain little-endian word files (genome.bin) instead of generated c headers. zc0 writes that format, mkgenome checks an image against its map and shows a compiler's diagnostics when it gets them, and the javascript vm and the editor's vm host read the same files. src/tools/findcc.sh picks a compiler with #embed (clang 19 or gcc 15). zc gets an embed declaration, embed "file" as [type] name, for globals and locals: a byte string by default, char*, numbers read little-endian, and arrays of numbers. the compiler and language server carry their library and source texts through it, so embed.sh and the text it spliced into compiler.zc are gone. make writes executables to build/bin, the default map to build/core/map, the web page to build/web and the extension staging to build/vsix, and the genome now rebuilds when its sources change. the test suite lays out its own map and bootstrap and reads nothing from build/. also: rt.put_s of a byte string in zc0 images, retained function sources no longer count as live objects, a missing include no longer cascades into undefined-name errors, consecutive comment lines join into one hover paragraph, and stale test fixtures follow the include format.
Diffstat (limited to 'Makefile')
-rw-r--r--Makefile141
1 files changed, 75 insertions, 66 deletions
diff --git a/Makefile b/Makefile
index 6a3898d..7f8f3f2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,27 +1,30 @@
# tinyvm and zc. `make` builds the tools, the interpreter, the microcore and the
-# language server into bin/. nothing built is kept in the tree.
+# language server into build/bin/. everything built or generated goes under
+# build/; the tree holds sources only, and `make clean` (rm -rf build) returns it
+# to a fresh checkout.
#
# make everything below that is a program
# make test the whole suite (tests/test.sh)
# make check the minimal suite, for iterating (tests/test.sh -q)
# make test JOBS=4 the same, four sections at a time
-# make genome rebuild build/core/genome.h from src/ and check the fixpoint
-# make lsp bin/zclsp, the language server
-# make web src/web/zc.wasm (needs clang with the wasm32 target)
-# make js build/js/genome.json and the vm, for the javascript vm
+# make genome build/core/genome.bin from src/, with the fixpoint check
+# make lsp build/bin/zclsp, the language server
+# make web build/web/: the page, its script and zc.wasm
+# make js build/js/: the javascript vm, its layout and genome
# make run PROG=examples/hello.zc
# compile one program into a run-only image on a memory
# map sized to it (src/tools/automap.sh) -> build/run/zymr
# make vsix the vscode extension, server and all
-# make clean remove bin/ and build/ and the web build
+# make clean remove build/
#
-# nothing built is kept in the tree: the genome, the javascript vm and its
-# layout, and the packed editor server all land in build/ (the web build keeps
-# its historical src/web/zc.wasm path because the page loads it by name).
-#
-# CC picks the compiler (cc by default; clang works too).
+# the cores take their genome in with c23 #embed (src/core/genome.h), so CC has
+# to be clang 19 or gcc 15 or later. src/tools/findcc.sh picks one when CC is
+# not given on the command line or in the environment.
-CC ?= cc
+ifeq ($(origin CC),default)
+ CC := $(shell sh src/tools/findcc.sh || echo cc)
+endif
+export CC
CFLAGS ?= -O2 -w -fwrapv
ifeq ($(OS),Windows_NT)
EXE := .exe
@@ -35,99 +38,105 @@ else
MICRO_THREADS := -DZY_THREADS
endif
-BIN := bin
B := build
+BIN := $(B)/bin
JS := $(B)/js
-# the hand-written core headers, then the generated genome beside them
-COREINC := -Isrc/core -I$(B)/core
-# the sources the compiler genome is built from
+WEB := $(B)/web
+# the default memory map, generated from the const blocks in the zc sources
+MAP := $(B)/core/map
+# hand-written core headers, the generated map, and where #embed finds the genome image
+COREINC := -Isrc/core -I$(MAP) --embed-dir=$(B)/core
+CORE_H := src/core/genome.h src/core/fops.h src/core/bops.h src/core/stack.h $(MAP)/memmap.h
+# the sources the compiler genome is built from, and the files it embeds
ZC_SRC := src/runtime/rt.zc src/core/core.zc src/compiler/compiler.zc
+ZC_DEPS := $(ZC_SRC) src/lib/std.zc src/lib/icon.zc src/tools/build.zc
TOOLS := $(BIN)/zc0$(EXE) $(BIN)/mkgenome$(EXE) $(BIN)/genmap$(EXE)
PROGS := $(BIN)/zy$(EXE) $(BIN)/zym$(EXE) $(BIN)/zclsp$(EXE)
all: $(TOOLS) $(PROGS)
-$(BIN) $(B):
+$(BIN):
mkdir -p $@
-$(BIN)/zc0$(EXE): src/tools/zc0.c src/core/memmap.h src/core/fops.h src/core/bops.h | $(BIN)
- $(CC) -O1 -w -fwrapv src/tools/zc0.c -Isrc/core -o $@
-$(BIN)/mkgenome$(EXE): src/tools/mkgenome.c src/core/memmap.h | $(BIN)
- $(CC) -O1 -w -fwrapv src/tools/mkgenome.c -Isrc/core -o $@
+# genmap needs no map of its own; everything else is built against the one it writes
$(BIN)/genmap$(EXE): src/tools/genmap.c | $(BIN)
- $(CC) $(CFLAGS) src/tools/genmap.c -Isrc/core -o $@
+ $(CC) $(CFLAGS) src/tools/genmap.c -o $@
+$(MAP)/memmap.h: $(BIN)/genmap$(EXE) src/runtime/rt.zc src/compiler/compiler.zc
+ mkdir -p $(B)/core
+ $(BIN)/genmap$(EXE) --preset default --check -o $(MAP) >$(B)/core/genmap.log 2>&1 || { cat $(B)/core/genmap.log; exit 1; }
+$(BIN)/zc0$(EXE): src/tools/zc0.c src/core/fops.h src/core/bops.h $(MAP)/memmap.h | $(BIN)
+ $(CC) -O1 -w -fwrapv src/tools/zc0.c -Isrc/core -I$(MAP) -o $@
+$(BIN)/mkgenome$(EXE): src/tools/mkgenome.c $(MAP)/memmap.h | $(BIN)
+ $(CC) -O1 -w -fwrapv src/tools/mkgenome.c -I$(MAP) -o $@
+
+# bootstrap: zc0 compiles the compiler to g0, g0 compiles it to g1, g1 to g2.
+# g1 and g2 must be identical (the fixpoint), and g1 becomes build/core/genome.bin.
+# each stage's core embeds the image beside it, so no stage reads another's.
+genome: $(B)/core/genome.bin
+$(B)/core/genome.bin: $(ZC_DEPS) $(BIN)/zc0$(EXE) $(BIN)/mkgenome$(EXE) src/core/core.c $(IO) $(CORE_H)
+ mkdir -p $(B)/s0 $(B)/s1 $(B)/core
+ cat $(ZC_SRC) | $(BIN)/zc0$(EXE) > $(B)/s0/g0.bin
+ $(BIN)/mkgenome$(EXE) < $(B)/s0/g0.bin > $(B)/s0/genome.bin
+ $(CC) $(CFLAGS) -DJTYPE=int src/core/core.c $(IO) -Isrc/core -I$(MAP) --embed-dir=$(B)/s0 -o $(B)/s0/zy0$(EXE)
+ (cat src/tools/build.zc; printf '`'; cat $(ZC_SRC)) | $(B)/s0/zy0$(EXE) > $(B)/s1/g1.bin
+ $(BIN)/mkgenome$(EXE) < $(B)/s1/g1.bin > $(B)/s1/genome.bin
+ $(CC) $(CFLAGS) -DJTYPE=int src/core/core.c $(IO) -Isrc/core -I$(MAP) --embed-dir=$(B)/s1 -o $(B)/s1/zy1$(EXE)
+ (cat src/tools/build.zc; printf '`'; cat $(ZC_SRC)) | $(B)/s1/zy1$(EXE) > $(B)/s1/g2.bin
+ cmp $(B)/s1/g1.bin $(B)/s1/g2.bin
+ cp $(B)/s1/genome.bin $@
+ @echo "fixpoint: $$(( $$(wc -c < $@) / 8 )) words, $@ updated"
# the interpreter: the vm core with the compiler genome inside
-$(BIN)/zy$(EXE): src/core/core.c $(IO) $(B)/core/genome.h src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h | $(BIN)
+$(BIN)/zy$(EXE): src/core/core.c $(IO) $(B)/core/genome.bin $(CORE_H) | $(BIN)
$(CC) $(CFLAGS) -DJTYPE=int src/core/core.c $(IO) $(COREINC) -o $@
# the portable microcore
-$(BIN)/zym$(EXE): src/micro/micro.c src/micro/micro_port.h $(PORT) $(B)/core/genome.h src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h | $(BIN)
+$(BIN)/zym$(EXE): src/micro/micro.c src/micro/micro_port.h $(PORT) $(B)/core/genome.bin $(CORE_H) | $(BIN)
$(CC) $(CFLAGS) $(MICRO_THREADS) src/micro/micro.c $(PORT) $(COREINC) -Isrc/micro -o $@
# the language server is the compiler in check mode plus src/lsp/server.zc, on a map
# sized to it
+LSP_DEPS := src/core/core.c src/core/io_linux.c $(filter-out $(MAP)/memmap.h,$(CORE_H)) $(ZC_DEPS) src/lsp/server.zc \
+ src/tools/zc0.c src/tools/genmap.c src/tools/mkgenome.c src/tools/automap.sh src/tools/mklsp.sh
lsp: $(BIN)/zclsp$(EXE)
-$(BIN)/zclsp$(EXE): src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/memmap.h src/runtime/rt.zc src/core/core.zc src/compiler/compiler.zc src/lsp/server.zc src/lib/std.zc src/lib/icon.zc src/tools/embed.sh src/tools/zc0.c src/tools/genmap.c src/tools/automap.sh src/tools/mklsp.sh | $(BIN)
- CC="$(CC)" bash src/tools/mklsp.sh $(B)/lsp
+$(BIN)/zclsp$(EXE): $(LSP_DEPS) | $(BIN)
+ bash src/tools/mklsp.sh $(B)/lsp
cp $(B)/lsp/zclsp$(EXE) $@
-src/compiler/compiler.zc: src/lib/std.zc src/lib/icon.zc src/tools/embed.sh
- bash src/tools/embed.sh all
-
-# bootstrap: zc0 compiles the compiler to g0, g0 compiles it to g1, g1 to g2.
-# g1 and g2 must be identical (the fixpoint), and g1 becomes build/core/genome.h.
-# run this by hand after editing a .zc source; `make check` reports a stale genome.
-genome: $(BIN)/zc0$(EXE) $(BIN)/mkgenome$(EXE) src/compiler/compiler.zc | $(B)
- mkdir -p $(B)/s0 $(B)/s1 $(B)/core
- cp src/core/core.c $(IO) src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h $(B)/s0/
- cp src/core/core.c $(IO) src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h $(B)/s1/
- cat $(ZC_SRC) | $(BIN)/zc0$(EXE) > $(B)/s0/genome.h
- $(CC) $(CFLAGS) -DJTYPE=int $(B)/s0/core.c $(B)/s0/$(notdir $(IO)) -o $(B)/zy0$(EXE)
- (cat src/tools/build.zc; printf '`'; cat $(ZC_SRC)) | $(B)/zy0$(EXE) > $(B)/g1.bin
- $(BIN)/mkgenome$(EXE) < $(B)/g1.bin > $(B)/s1/genome.h
- $(CC) $(CFLAGS) -DJTYPE=int $(B)/s1/core.c $(B)/s1/$(notdir $(IO)) -o $(B)/zy1$(EXE)
- (cat src/tools/build.zc; printf '`'; cat $(ZC_SRC)) | $(B)/zy1$(EXE) > $(B)/g2.bin
- cmp $(B)/g1.bin $(B)/g2.bin
- cp $(B)/s1/genome.h $(B)/core/genome.h
- @echo "fixpoint: $$(( $$(wc -c < $(B)/g1.bin) / 8 )) words, $(B)/core/genome.h updated"
-
-# a fresh clone has no genome, so bootstrap one on demand. it is deliberately
-# not rebuilt from source dependencies: the fixpoint run is expensive, and
-# `make genome` is the explicit way to refresh it after editing a .zc file.
-$(B)/core/genome.h:
- $(MAKE) genome
-
-js: $(JS)/genome.json $(JS)/layout.js $(JS)/tinyvm.js
+# the javascript vm: tinyvm.js loads genome.bin from beside itself
+js: $(JS)/tinyvm.js $(JS)/layout.js $(JS)/genome.bin
$(JS):
mkdir -p $@
-$(JS)/genome.json: $(B)/core/genome.h src/core/memmap.h src/js/extract_genome.js src/js/mapvalues.js | $(JS)
- node src/js/extract_genome.js
-$(JS)/layout.ts $(JS)/layout.js: $(JS)/genome.json
- @test -f $@ || node src/js/extract_genome.js
+$(JS)/genome.bin: $(B)/core/genome.bin | $(JS)
+ cp $< $@
+$(JS)/layout.js: $(MAP)/memmap.h src/js/mklayout.js src/js/mapvalues.js | $(JS)
+ node src/js/mklayout.js --map $(MAP)/memmap.h --out $(JS)
$(JS)/tinyvm.js: src/js/tinyvm.ts src/js/layout.d.ts | $(JS)
tsc src/js/tinyvm.ts --target es2020 --lib es2020,dom --module commonjs --outDir $(JS)
# the language server image for the javascript vm, and the wasm server
-lspimage: $(JS)/lsp.json
-$(JS)/lsp.json: $(BIN)/zclsp$(EXE) src/js/extract_lsp.js src/js/mapvalues.js | $(JS)
+lspimage: $(JS)/lsp.bin
+$(JS)/lsp.bin: $(BIN)/zclsp$(EXE) src/js/extract_lsp.js src/js/mapvalues.js | $(JS)
node src/js/extract_lsp.js
# the vscode extension, with the server and its image inside it
-vsix: | $(B)
+vsix:
bash src/tools/mkvsix.sh
-web: src/web/zc.wasm
-src/web/zc.wasm: src/micro/micro.c src/micro/micro_port.h src/web/port_wasm.c src/web/build.sh $(B)/core/genome.h src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h
+# the browser compiler: the page and its loader beside the wasm build
+web: $(WEB)/zc.wasm $(WEB)/index.html $(WEB)/zc.js
+$(WEB)/zc.wasm: src/micro/micro.c src/micro/micro_port.h src/web/port_wasm.c src/web/build.sh $(B)/core/genome.bin $(CORE_H)
bash src/web/build.sh $@
+$(WEB)/index.html $(WEB)/zc.js: $(WEB)/%: src/web/%
+ mkdir -p $(WEB)
+ cp $< $@
# one program as a run-only image: the runtime, the program, and a map exactly
# big enough for them
-run: | $(B)
+run:
@test -n "$(PROG)" || { echo "make run PROG=examples/hello.zc"; exit 2; }
bash src/tools/automap.sh run $(B)/run src/runtime/run.zc $(PROG)
- cp src/micro/micro.c src/micro/micro_port.h $(PORT) src/core/fops.h src/core/bops.h src/core/stack.h $(B)/run/
- $(CC) $(CFLAGS) $(MICRO_THREADS) $(B)/run/micro.c $(B)/run/$(notdir $(PORT)) -I$(B)/run -o $(B)/run/zymr$(EXE)
+ $(CC) $(CFLAGS) $(MICRO_THREADS) src/micro/micro.c $(PORT) -Isrc/core -Isrc/micro -I$(B)/run --embed-dir=$(B)/run -o $(B)/run/zymr$(EXE)
@echo "built $(B)/run/zymr$(EXE)"
# JOBS sets how many sections run at once: make test JOBS=4
@@ -139,6 +148,6 @@ check:
bash tests/test.sh -q -j $(or $(JOBS),2)
clean:
- rm -rf $(BIN) $(B) src/web/zc.wasm
+ rm -rf $(B)
.PHONY: all lsp genome js lspimage vsix web run test check clean