1 MOXIE ?= $(CURDIR)/../moxie/moxie
2 MOXIEJS ?= $(CURDIR)/../moxie/moxiejs
3 # An inherited MOXIEROOT is ignored on purpose: `?=` cannot tell an exported
4 # stale root from an intended one, and a stale root silently compiles a
5 # different tree's src/ and pkg/ (that is how `make build-app-wasm` failed
6 # with "'err' shadows declaration in enclosing scope" from an old checkout,
7 # while the same build with the repo root succeeds). Pass it on the command
8 # line to build against another tree: make MOXIEROOT=/path/to/moxie ...
9 ifeq ($(origin MOXIEROOT),environment)
10 MOXIEROOT := $(CURDIR)/../moxie
11 endif
12 MOXIEROOT ?= $(CURDIR)/../moxie
13 JSRUNTIME ?= $(CURDIR)/../moxie/jsruntime
14 ISKRA ?= $(HOME)/.local/bin/moxie-iskra
15 # relay-proxy builds from source with the tree compiler. The earlier workaround
16 # of using an installed moxie (its cached bitcode "avoided too much recursion
17 # during _start()") was a symptom of a compiler bug: taking the address of a
18 # frame local stored a stack pointer into the receiver's arena. Fixed in
19 # _mxc_stage4 (markAddrEscaping mirrors the legacy compiler's Alloc.Heap), and
20 # a fresh build now produces the same 430KB module the installed compiler did.
21 MOXIE_RELAY_PROXY ?= $(MOXIE)
22 23 # Moxie closures do not capture: a closure receives what it needs through its
24 # parameters, and the legacy compiler rejects a capturing one. stage4 enforces
25 # the same rule by default. This app predates that enforcement and registers
26 # hundreds of handlers that close over the enclosing frame (286 sites in
27 # web/wasm/app alone), so every build here opts out explicitly. Porting them to
28 # explicit parameters or to holder types with the callback as an interface is
29 # tracked with the app, not the compiler; until it lands, the debt is declared
30 # here rather than silently relying on stage4's old acceptance.
31 CAPTURES ?= -allow-closure-captures
32 33 # === Relay (native) ===
34 build-relay:
35 MOXIEROOT=$(MOXIEROOT) $(MOXIE) build $(CAPTURES) -o smesh .
36 37 # === Frontend (WASM) ===
38 build-app-wasm:
39 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE) build $(CAPTURES) -target wasm -o $(CURDIR)/web/static/app.wasm ./web/wasm/app/
40 41 build-relay-proxy-wasm:
42 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE_RELAY_PROXY) build $(CAPTURES) -target wasm -o $(CURDIR)/web/static/relay-proxy.wasm ./web/wasm/relay-proxy/
43 44 build-store-wasm:
45 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE) build $(CAPTURES) -target wasm -o $(CURDIR)/web/static/store.wasm ./web/wasm/store/
46 47 build-verify-wasm:
48 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE) build $(CAPTURES) -target wasm -o $(CURDIR)/web/static/verify.wasm ./web/wasm/verify/
49 50 build-profile-wasm:
51 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE) build $(CAPTURES) -target wasm -o $(CURDIR)/web/static/profile.wasm ./web/wasm/profile/
52 53 build-feed-wasm:
54 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE) build $(CAPTURES) -target wasm -o $(CURDIR)/web/static/feed.wasm ./web/wasm/feed/
55 56 build-mls-wasm:
57 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE) build $(CAPTURES) -target wasm -o $(CURDIR)/web/static/mls.wasm ./web/wasm/mls/
58 59 60 build-notif-wasm:
61 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE) build $(CAPTURES) -target wasm -o $(CURDIR)/web/static/notif.wasm ./web/wasm/notif/
62 63 build-sw:
64 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE) build $(CAPTURES) -target wasm -o $(CURDIR)/web/static/\$$sw/sw.wasm ./web/wasm/sw/
65 66 # === Signer WASM (page-embedded worker) ===
67 # The web app uses signer.wasm directly via signer-wasm-host.mjs.
68 # No browser extension is required for normal operation.
69 build-signer:
70 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE) build $(CAPTURES) -target wasm -o $(CURDIR)/web/static/signer.wasm ./web/wasm/signer/
71 72 # Extension build (optional, not part of default build).
73 build-ext-firefox: build-signer
74 cp $(CURDIR)/web/static/signer.wasm $(CURDIR)/web/ext/bg/signer.wasm
75 bash scripts/pack-ext.sh firefox
76 cp dist/smesh-signer-firefox.xpi web/static/smesh-signer.xpi
77 78 build-ext: build-ext-firefox
79 80 # === All ===
81 # Direct messaging (MLS/Marmot) is parked again: the wasm worker panics in
82 # Moxie's codec while generating its first KeyPackage (an invalid type
83 # descriptor, kind=0), so its worker is not built or served and the app has no
84 # /msg route. The mls code, host plumbing and worker all stay in the tree;
85 # re-enable by restoring the sidebar entry, the router cases, the mlsWorkerTypes
86 # branch and build-mls-wasm in `build`/`dev` (see docs/relay-test-coverage.md).
87 build: build-relay build-app-wasm build-relay-proxy-wasm build-store-wasm build-verify-wasm build-profile-wasm build-feed-wasm build-notif-wasm build-signer build-sw
88 89 # === Test ===
90 build-test-relay:
91 MOXIEROOT=$(MOXIEROOT) $(MOXIE) build $(CAPTURES) -o smesh-test .
92 93 # Unit tests: every package carrying a *_test.mx, run by the compiler's own test
94 # harness. Nothing ran these before - the files sat in the tree, and the ones
95 # that had stopped compiling or passing were invisible until they were run.
96 #
97 # UNIT_SKIP names the packages whose tests are known not to build or pass yet.
98 # Each one is recorded in docs/relay-test-coverage.md with its reason: declared
99 # debt, not silence.
100 #
101 # pkg/crawler came off the list once stage4 rejected `push` on a non-slice
102 # (the same error legacy gives) instead of emitting IR for it; its test helper
103 # pushed onto a *tag.S instead of onto its T slice, which is what the old
104 # "IR-emit bug" report was.
105 #
106 # web/common/marmot came off the list once its closure compiled: the mls
107 # test-support files shadowed the named `err` return from nested blocks, and
108 # the test's `wantN int` is a type this compiler no longer defines (stage4
109 # emits a ptr where the fmt interface slot needs {i64,ptr}, and clang refuses
110 # the module). Both are fixed in place; the test is int32 and uses explicit
111 # return after Fatal, and 6 tests pass.
112 #
113 # pkg/nostr/ws came off the list once stage4 boxed a concrete operand
114 # (syscall.Errno sentinels, errors.Const) into the interface representation
115 # before comparing it against an error value, the way legacy already did
116 # through go/ssa. The loopback WebSocket tests now connect and pass.
117 #
118 # pkg/lol came off the list once stage4 stopped printing a phantom
119 # `%!(NOVERB)` after every formatted value: its 9 tests pass.
120 UNIT_SKIP ?=
121 test-unit:
122 @set -e ; for f in $$( { [ -f main_test.mx ] && echo main_test.mx ; find pkg web -name '*_test.mx' ; } | sort ) ; do \
123 d=$$(dirname $$f) ; \
124 case " $(UNIT_SKIP) " in *" $$d "*) echo "=== unit: $$d (skipped: see docs/relay-test-coverage.md)" ; continue ;; esac ; \
125 echo "=== unit: $$d" ; \
126 MOXIEROOT=$(MOXIEROOT) $(MOXIE) test $(CAPTURES) ./$$d || exit 1 ; \
127 done
128 129 test-signer: build-signer
130 node web/_test/signertest/run.mjs web/ext/bg/signer.wasm
131 132 # Unit coverage: the same package loop with the harness's own report. `moxie
133 # test -cover` writes the site table, starts the instrumented test binary and
134 # prints the per-file table itself, so nothing external joins the halves.
135 unit-cover:
136 @set -e ; for f in $$( { [ -f main_test.mx ] && echo main_test.mx ; find pkg web -name '*_test.mx' ; } | sort ) ; do \
137 d=$$(dirname $$f) ; \
138 case " $(UNIT_SKIP) " in *" $$d "*) echo "=== unit-cover: $$d (skipped: see docs/relay-test-coverage.md)" ; continue ;; esac ; \
139 echo "=== unit-cover: $$d" ; \
140 MOXIEROOT=$(MOXIEROOT) $(MOXIE) test $(CAPTURES) -cover ./$$d || exit 1 ; \
141 done
142 143 test: test-unit build-test-relay test-signer
144 python3 -m pytest test/ -v --tb=short -x
145 146 # === Coverage ===
147 #
148 # `make cover` measures the integration suite's line coverage of this module:
149 # the relay is built with -cover, which makes the compiler emit a coverage hit
150 # per statement and write <output>.cover_sites.json (id -> pkg.Func@file:line);
151 # MOXIE_COVER_OUT makes the instrumented runtime append its counters to one file
152 # when a domain exits, which the test fixture's SIGTERM triggers; mxcover joins
153 # the table to the counters and prints the per-file table plus every statement
154 # that never ran.
155 #
156 # The file is appended to, not rewritten, because each test starts a fresh relay
157 # and the relay is a spawn tree - the store, the ingest workers, the proxy, the
158 # blossom and broadcast workers each run in a forked domain with a private copy
159 # of the counter table - so every process and domain contributes its own dump.
160 # The target deletes the file first, so one run's report is one run's coverage.
161 #
162 # The report is the work list. A coverage run rebuilds the whole closure (a
163 # cached package would have no hits to count) and the instrumented relay is
164 # slower, so this is not part of `make test`.
165 #
166 # test_relay_proxy_high_event_volume runs with a widened post-EOSE window. The
167 # old poll treated the gap after EOSE as "quiet" and finished 2s later, so a
168 # slow relay whose first event lands after EOSE read 0 events - which is what
169 # every `smesh-test` run did, and what the stock relay did in about 1 isolated
170 # run in 4. The test now waits for the first event up to PROXY_VOLUME_WINDOW_MS,
171 # so the coverage run widens it (the worker lingers 25s for those stragglers).
172 COVER_BIN := $(CURDIR)/smesh-test
173 COVER_SITES := $(COVER_BIN).cover_sites.json
174 COVER_DIR := $(CURDIR)/tmp
175 COVER_COUNTS := $(COVER_DIR)/cover.counts
176 COVER_TOOL := $(COVER_DIR)/mxcover
177 # Site names are "module/path/file.mx:line" for both compilers, so the report
178 # selects this module out of the link set by the module path from moxie.mod.
179 COVER_PREFIX := $(shell sed -n 's/^module[[:space:]]*//p' $(CURDIR)/moxie.mod)
180 cover:
181 @mkdir -p $(COVER_DIR)
182 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE) build -cover $(CAPTURES) -o $(COVER_BIN) .
183 rm -f $(COVER_COUNTS)
184 @rc=0 ; SMESH_RELAY_BIN=$(COVER_BIN) MOXIE_COVER_OUT=$(COVER_COUNTS) \
185 PROXY_VOLUME_WINDOW_MS=180000 \
186 python3 -m pytest test/ -q --tb=short || rc=$$? ; \
187 MOXIEROOT=$(MOXIEROOT) $(MOXIE) build -o $(COVER_TOOL) $(MOXIEROOT)/cmd/mxcover || rc=$$? ; \
188 $(COVER_TOOL) $(COVER_SITES) $(COVER_COUNTS) $(COVER_PREFIX) || rc=$$? ; \
189 exit $$rc
190 191 test-smoke: build-test-relay
192 python3 -m pytest test/test_smoke.py -v --tb=short
193 194 # Allocation instrumentation: the app wasm is rebuilt with -tags alloc_trace
195 # and -print-allocs, which makes src/runtime publish the __moxie_* counters the
196 # worker host reads in its __mem_query reply. The pattern is anchored to the
197 # entry package: the compiler names a function by its package path, and the app
198 # is the root package of its own module, so it is "main.<func>" - the same name
199 # the legacy compiler reports.
200 #
201 # The instrumented module is scratch: the stock app.wasm is put back whether
202 # the build or the tests fail, so this never leaves an instrumented artifact or
203 # a stray side file in the tree. An instrumented build recompiles everything
204 # serially (site ids are numbered in emit order), so it is slower than
205 # build-app-wasm.
206 APP_WASM := $(CURDIR)/web/static/app.wasm
207 test-memory: build-test-relay
208 @if [ -f "$(APP_WASM)" ]; then cp "$(APP_WASM)" "$(APP_WASM).stock"; fi ; \
209 MOXIEROOT=$(MOXIEROOT) GOWORK=off $(MOXIE) build $(CAPTURES) -target wasm -tags alloc_trace \
210 -print-allocs '^main\.' -o $(APP_WASM) ./web/wasm/app/ ; \
211 rc=$$? ; \
212 rm -f "$(APP_WASM).alloc_sites.json" ; \
213 if [ $$rc -eq 0 ]; then \
214 python3 -m pytest test/test_memory_profile.py -v --tb=short -m memory ; rc=$$? ; \
215 fi ; \
216 if [ -f "$(APP_WASM).stock" ]; then mv "$(APP_WASM).stock" "$(APP_WASM)"; fi ; \
217 exit $$rc
218 219 # === Dev (frontend only, relay already running) ===
220 dev: build-app-wasm build-relay-proxy-wasm build-store-wasm build-verify-wasm build-profile-wasm build-feed-wasm build-notif-wasm build-sw
221 222 .PHONY: build-relay build-app-wasm build-relay-proxy-wasm build-store-wasm build-verify-wasm build-profile-wasm build-feed-wasm build-mls-wasm build-notif-wasm build-sw build-signer build-ext-firefox build-ext-chrome build-ext build dev build-test-relay test-signer test-unit test cover test-smoke test-memory unit-cover
223