aboutsummaryrefslogtreecommitdiff
path: root/Makefile
blob: d5caa92894df791338dab71cf68603cb1db95e66 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# tinyvm and zc. `make` builds the tools, the interpreter, the microcore and the
# 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          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 build/
#
# 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.

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
  IO  := src/core/io_win.c
  PORT := src/micro/port_win.c
else
  EXE :=
  IO  := src/core/io_linux.c
  PORT := src/micro/port_linux.c
  CFLAGS += -pthread
  MICRO_THREADS := -DZY_THREADS
endif

B   := build
BIN := $(B)/bin
JS  := $(B)/js
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):
	mkdir -p $@

# 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 -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.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.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): $(LSP_DEPS) | $(BIN)
	bash src/tools/mklsp.sh $(B)/lsp
	cp $(B)/lsp/zclsp$(EXE) $@

# 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.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.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:
	bash src/tools/mkvsix.sh

# the browser compiler: the page, its loader and the examples it offers beside the wasm build
WEB_EXAMPLES := $(patsubst examples/%,$(WEB)/examples/%,$(wildcard examples/*.zc))
web: $(WEB)/zc.wasm $(WEB)/index.html $(WEB)/zc.js $(WEB_EXAMPLES)
$(WEB)/examples/%.zc: examples/%.zc
	mkdir -p $(WEB)/examples
	cp $< $@
$(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:
	@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)
	$(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
test:
	bash tests/test.sh -j $(or $(JOBS),1)

# the minimal suite: bootstrap, fixpoint, interpreter and lsp checks
check:
	bash tests/test.sh -q -j $(or $(JOBS),2)

clean:
	rm -rf $(B)

.PHONY: all lsp genome js lspimage vsix web run test check clean