-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathMakefile
More file actions
265 lines (242 loc) · 13.6 KB
/
Copy pathMakefile
File metadata and controls
265 lines (242 loc) · 13.6 KB
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# gecko-wasm build orchestration.
#
# make firefox shallow-clone (depth 1) the Gecko engine fork at the pinned commit
# make vendor vendor the Rust std deps for -Z build-std (vendor-std-deps.py)
# make build build the engine -> obj-full-emscripten/dist/bin/libxul.so (+ -r relink)
# make configure force a reconfigure (needed after changing configure inputs outside CONFIGURE_INPUTS, e.g. re-checked-out firefox/)
# make libxul build the gecko.js package: engine artifacts + the rspack ESM bundle (default)
# make embed-demo / make chrome-demo build the library, then run its Vite demo
# make chrome-assets stage demo/chrome's GRE asset archive from the objdir
# make run alias for embed-demo (build + serve the basic embed demo)
# make web alias for libxul (back-compat)
# make clean remove the gecko.js build outputs
# make distclean also remove the objdir and the firefox/ checkout
#
# The runnable build is the gecko.js package + its Vite demos (embed-demo /
# chrome-demo); the old embed-xul/ + embed-chrome/ stub dirs have been removed.
# Prereqs are not installed here (see README.md): emsdk (emscripten 6.0.1; bundles
# binaryen v130), rust 1.95 + rust-src + the wasm32-unknown-emscripten target,
# python3, and node + pnpm (for the gecko.js bundle). The build env vars below
# default sensibly but honor the environment (?=), so CI can override e.g.
# MOZBUILD_STATE_PATH / EM_BINARYEN_ROOT / EMSDK.
ROOT := $(CURDIR)
FIREFOX_URL := https://github.com/HeyPuter/firefox.git
FIREFOX_REF := 2e1e835a5da5907e5ceef19aade58a7493f1e34d
# Pinned, repo-local emscripten. `make emsdk` clones the emsdk meta-repo here,
# installs + activates this version, and applies the WasmFS WISP-socket patches
# so WASMFS=1 builds keep working TCP sockets (patch-emsdk-wasmfs.mjs +
# emsdk-patches/wisp_socket.h). The whole build (engine + gecko.js relink) runs
# against $(EMSDK), exported below. Override EMSDK to reuse an existing install
# (then run `make emsdk` once to patch it -- the patch is idempotent).
EMSDK ?= $(ROOT)/emsdk
EMSDK_VERSION ?= 6.0.1
EMSDK_STAMP := $(EMSDK)/.wisp-patched
WISP_PATCH_SRC := gecko.js/patch-emsdk-wasmfs.mjs gecko.js/emsdk-patches/wisp_socket.h
EM_CONFIG ?= $(ROOT)/em_config
MOZCONFIG ?= $(ROOT)/mozconfig.full.emscripten
MOZBUILD_STATE_PATH ?= $(HOME)/.mozbuild
# RELEASE=1 turns on optimizations: -O3 at the emcc relink so wasm-opt's passes run
# over the final module (and -O3 engine codegen). NOTE: RELEASE does NOT enable engine
# LTO -- that's the separate LTO knob below.
RELEASE ?=
# LTO=1 enables cross-module ThinLTO for the engine (mozconfig reads GECKO_LTO). It is
# independent of RELEASE because the libxul LTO link needs a big-RAM host (>54 GiB) --
# the standard CI runner OOM-kills it. Only set LTO=1 on a ~64 GiB+ machine. Off by
# default so RELEASE builds are reliable (optimized + wasm-opt, just no engine LTO).
LTO ?=
export EM_CONFIG MOZCONFIG MOZBUILD_STATE_PATH EMSDK
export GECKO_RELEASE := $(RELEASE)
export GECKO_LTO := $(LTO)
# Pin the build timestamp for the whole build. The `build` target runs `mach build` TWICE
# with the libxul `-r` relink in between (see that recipe). mach regenerates buildid.cpp
# from the current time on each pass; a changed build id recompiles buildid.o (linked into
# libxul) and makes the relinked libxul.so look stale -- so the second pass re-links libxul
# as `-shared` and hits the wasm-ld SIGSEGV again, failing the build. A fixed MOZ_BUILD_DATE
# (14 digits, YYYYMMDDHHMMSS -- mach ignores any other length) keeps the build id identical
# across both passes, so pass 2 sees libxul.so up to date and skips the link. Evaluated
# once at make startup, so both passes inherit the same value.
export MOZ_BUILD_DATE := $(shell date +%Y%m%d%H%M%S)
# Engine build output (RELEASE uses its own objdir, matching the mozconfig + the
# gecko.js build script). `libxul` keys off this existing to decide whether a first
# engine build is needed.
OBJDIR := $(ROOT)/obj-full-emscripten$(if $(RELEASE),-release)
LIBXUL := $(OBJDIR)/dist/bin/libxul.so
# The files we treat as "configuration": editing one of these should force a
# reconfigure, anything else should not. `mach build` decides on its own whether
# to re-run configure by comparing config.status against EVERY entry in
# $(OBJDIR)/config_status_deps.in -- 100+ files, almost all of them Gecko's
# mach/mozbuild infra + version stamps, NOT our config. Re-checking out firefox/
# (or any op that bumps those mtimes) then forces a spurious reconfigure. The
# `build` recipe below bumps config.status past that broader list unless one of
# these real inputs actually changed, so only OUR config changes reconfigure.
CONFIGURE_INPUTS := $(MOZCONFIG) $(EM_CONFIG)
.PHONY: all release firefox vendor configure build web run clean distclean \
libxul embed-demo chrome-demo chrome-assets emsdk
all: libxul
# Pull + install + activate the pinned emsdk locally, then apply the WasmFS
# socket patches. The stamp depends on our patch sources, so editing them
# re-patches (and invalidates the cached libwasmfs so it rebuilds). Installing
# the toolchain downloads ~2GB the first time.
emsdk: $(EMSDK_STAMP)
$(EMSDK_STAMP): $(WISP_PATCH_SRC)
@if [ ! -x "$(EMSDK)/emsdk" ]; then \
echo ">> cloning emsdk -> $(EMSDK)"; \
git clone https://github.com/emscripten-core/emsdk.git "$(EMSDK)"; \
fi
cd "$(EMSDK)" && ./emsdk install $(EMSDK_VERSION) && ./emsdk activate $(EMSDK_VERSION)
node gecko.js/patch-emsdk-wasmfs.mjs
@touch "$@"
# Optimized build (engine LTO + wasm-opt). NOTE: toggling RELEASE changes the
# mozconfig (--enable-lto), which forces a full reconfigure + rebuild of libxul.
release:
$(MAKE) all RELEASE=1
# Pinned shallow clone. GitHub serves arbitrary reachable SHAs, so we fetch the
# exact commit at depth 1 (no submodule, no full history). The firefox/.git guard
# makes this a no-op once the checkout exists; to move the pin, bump FIREFOX_REF
# and `rm -rf firefox` (or `make distclean`).
firefox: firefox/.git
firefox/.git:
git init -q firefox
git -C firefox remote add origin $(FIREFOX_URL) 2>/dev/null || true
git -C firefox fetch --depth 1 origin $(FIREFOX_REF)
git -C firefox checkout -q --detach FETCH_HEAD
@echo ">> firefox at $$(git -C firefox rev-parse --short HEAD)"
vendor: firefox
python3 vendor-std-deps.py
build: firefox vendor $(EMSDK_STAMP)
@# Keep mach from reconfiguring on unrelated mtime changes: if none of our
@# CONFIGURE_INPUTS are newer than config.status, touch it so it stays newer
@# than everything in config_status_deps.in (mach then skips configure). If a
@# real config input IS newer, leave config.status alone so mach reconfigures.
@if [ -f "$(OBJDIR)/config.status" ]; then \
changed=$$(find $(CONFIGURE_INPUTS) -newer "$(OBJDIR)/config.status" 2>/dev/null); \
if [ -n "$$changed" ]; then \
echo ">> config input changed, mach will reconfigure:"; echo "$$changed"; \
else \
touch "$(OBJDIR)/config.status"; \
fi; \
fi
@# emscripten 6.0.x: the first `mach build` compiles everything but FAILS at the
@# libxul.so `-shared` link (wasm-ld SIGSEGVs in the ElemSection writer on the huge
@# module). relink-engine-r.sh relinks libxul/libnss3/libgkcodecs as `-r` relocatable
@# objects (also required so the embedder static-links them instead of treating them
@# as dynamic side modules); a second `mach build` then skips the up-to-date link and
@# finishes the resource/chrome tiers. (|| true masks only the expected link failure;
@# any real error resurfaces in the second build.)
cd firefox && ./mach build || true
bash gecko.js/relink-engine-r.sh
cd firefox && ./mach build
# Force a reconfigure. Needed when you change something configure inspects that
# isn't in CONFIGURE_INPUTS (e.g. after re-checking out firefox/): `build` above
# only reconfigures when $(CONFIGURE_INPUTS) change, so use this otherwise.
configure: firefox vendor $(EMSDK_STAMP)
cd firefox && ./mach configure
# Back-compat alias: the old embed-xul web build was removed; the web build IS the
# gecko.js package now.
web: libxul
# Build + serve the basic embed demo (Vite dev server with COOP/COEP + a WISP proxy).
run: embed-demo
# --- gecko.js library + demos (pnpm monorepo) -----------------------------
# Build the gecko.js package (the default `all` target): the engine artifacts
# (build/build-lib.sh stages a MINIMAL gre-stage -> gecko.{js,wasm,data,worker.js})
# + the rspack ESM bundle. Builds the engine (libxul.so) first if there isn't one.
libxul: $(EMSDK_STAMP)
@test -e "$(LIBXUL)" || $(MAKE) build
pnpm install
pnpm --filter gecko.js run build
# --- chrome-demo asset archive ---------------------------------------------
# gecko.data is intentionally stripped to stay minimal, so chrome-demo ships the
# non-binary GRE resource set from the engine objdir plus the Firefox browser/
# app dir as demo/chrome/public/chrome-assets.tar.zst (+ chrome-assets.json
# carrying the uncompressed size, which the in-browser zstd decode needs up
# front). The runtime downloads it and serves it to the engine from memory
# (demo/chrome/src/chrome-fs.ts). Skips when nothing under the sources is newer
# than the archive. Honors GECKO_OBJDIR (default: $(OBJDIR)) and CHROME_FULL=1.
CHROME_OBJDIR := $(or $(GECKO_OBJDIR),$(OBJDIR))
CHROME_GRE_SRC := $(CHROME_OBJDIR)/dist/bin
CHROME_FONT_SRC := $(ROOT)/firefox/toolkit/components/pdfjs/content/web/standard_fonts
CHROME_PUBLIC := demo/chrome/public
CHROME_ARCHIVE := $(CHROME_PUBLIC)/chrome-assets.tar.zst
CHROME_MANIFEST := $(CHROME_PUBLIC)/chrome-assets.json
CHROME_EXCLUDES := \
'*.so' \
'*.wasm' \
'*.a' \
'*.data' \
'*.dbg' \
'*.symbols' \
'/firefox' \
'/firefox-bin' \
'/pingsender' \
'/nsinstall' \
'/nsinstall_real'
# The executable names are anchored to the rsync transfer ROOT (leading slash) so
# they drop only dist/bin/<exe>, NOT nested directories that share the name. A bare
# `firefox` matched any path component and wrongly excluded the devtools debugger's
# client/firefox/ (commands.js, create.js) and netmonitor's src/utils/firefox/ ->
# the debugger devtools panel was blank with "Missing chrome or resource URL:
# resource://devtools/client/debugger/src/client/firefox/commands.js".
# Trim heavy, optional feature trees from the chrome bundle unless CHROME_FULL=1
# (which ships the complete Firefox asset set). None are needed to boot or render
# pages: hyphenation dicts (cosmetic line breaks) and spellcheck dicts.
# chrome/remote (Remote Agent / CDP+WebDriver, ~720K) must ship: this build has
# ENABLE_WEBDRIVER, so browser.js's gRemoteControl dereferences the Marionette
# service on EVERY chrome-window load (gBrowserInit.onLoad -> updateVisualCue).
# Excluding it left remote.manifest pointing at missing files -> the lazy service
# getter failed -> `Marionette` undefined -> onLoad threw before registering
# _delayedStartup, so PanelUI/BookmarkingUI/FullZoom etc. never initialized
# (first symptom: hamburger menu "PanelUI.panel is undefined").
# For pdf.js, keep the small integration modules in chrome/pdfjs/content/*.sys.mjs
# (PdfjsContextMenu/PdfStreamConverter/PdfJs are eagerly imported -- e.g. by the
# context-menu actor; dropping all of pdfjs broke right-click) and only drop the
# heavy viewer UI + engine (content/web ~4.6 MB + content/build ~3 MB). PDF *viewing*
# is disabled; PDFs download instead. (Excluded chrome.manifest entries remain, so a
# feature only errors if actually invoked.) Together ~15 MB uncompressed off the tar.
ifeq ($(CHROME_FULL),)
CHROME_EXCLUDES += \
'/chrome/pdfjs/content/web' \
'/chrome/pdfjs/content/build' \
'/hyphenation' \
'/dictionaries'
endif
chrome-assets:
@if [ ! -d "$(CHROME_GRE_SRC)" ]; then \
echo "chrome-assets: missing $(CHROME_GRE_SRC); build the engine first (make build)" >&2; exit 1; fi
@if [ ! -d "$(CHROME_FONT_SRC)" ]; then \
echo "chrome-assets: missing $(CHROME_FONT_SRC); the firefox/ source checkout is required for bundled fonts" >&2; exit 1; fi
@if [ -f "$(CHROME_ARCHIVE)" ] && [ -f "$(CHROME_MANIFEST)" ] && \
[ -z "$$(find "$(CHROME_GRE_SRC)" "$(CHROME_FONT_SRC)" -newer "$(CHROME_ARCHIVE)" -print -quit)" ] && \
[ -z "$$(find "$(CHROME_GRE_SRC)" "$(CHROME_FONT_SRC)" -newer "$(CHROME_MANIFEST)" -print -quit)" ]; then \
echo ">> $(CHROME_ARCHIVE) up to date"; exit 0; \
fi; \
set -e; \
stage=$$(mktemp -d); tarfile=$$(mktemp); \
trap 'rm -rf "$$stage" "$$tarfile" "$(CHROME_ARCHIVE).tmp"' EXIT; \
rsync -aL $(patsubst %,--exclude=%,$(CHROME_EXCLUDES)) "$(CHROME_GRE_SRC)/" "$$stage/"; \
for dest in fonts browser/fonts; do \
mkdir -p "$$stage/$$dest"; \
cp "$(CHROME_FONT_SRC)"/*.ttf "$$stage/$$dest/"; \
done; \
tar -cf "$$tarfile" -C "$$stage" .; \
size=$$(stat -c%s "$$tarfile"); \
mkdir -p "$(CHROME_PUBLIC)"; \
zstd -q -f -19 "$$tarfile" -o "$(CHROME_ARCHIVE).tmp"; \
chmod 644 "$(CHROME_ARCHIVE).tmp"; \
printf '{"uncompressedSize":%s}\n' "$$size" > "$(CHROME_MANIFEST)"; \
mv "$(CHROME_ARCHIVE).tmp" "$(CHROME_ARCHIVE)"; \
echo ">> wrote $(CHROME_ARCHIVE) ($$size bytes uncompressed)"
# Run the Vite demos (build the library first). `embed-demo` is the basic
# embed-a-web-page demo; `chrome-demo` supplies the Firefox front-end files.
embed-demo: libxul
pnpm --filter embed-demo dev
chrome-demo: libxul chrome-assets
pnpm --filter chrome-demo dev
clean:
rm -f gecko.js/wasm/gecko.js gecko.js/wasm/gecko.wasm gecko.js/wasm/gecko.data \
gecko.js/wasm/gecko.worker.js gecko.js/wasm/gecko.debug.wasm \
gecko.js/wasm/gecko.wasm.zst gecko.js/wasm/gecko.data.zst \
gecko.js/wasm/gecko-assets.json \
gecko.js/build/*.stripped.so gecko.js/build/*.o gecko.js/build/link.err
rm -rf gecko.js/build/gre-stage gecko.js/dist
distclean: clean
rm -rf obj-full-emscripten obj-full-emscripten-release firefox