forked from oven-sh/WebKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile.windows
More file actions
456 lines (429 loc) · 26.6 KB
/
Copy pathDockerfile.windows
File metadata and controls
456 lines (429 loc) · 26.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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# Cross-compiles JavaScriptCore (PORT=JSCOnly) for Windows on a Linux host:
# clang-cl --target=<arch>-pc-windows-msvc against an xwin-downloaded MSVC
# CRT + Windows SDK, linked with lld-link. Produces the same bun-webkit
# tarball layout as windows-release.ps1 does on a real Windows runner.
#
# Builds on the upstream Linux->Windows cross support in
# Source/cmake/OptionsMSVC.cmake (webkit.org bugs 282276 and 309681), which
# handles the -imsvc include paths, -libpath: linker flags, and llvm-rc/
# llvm-lib/llvm-mt selection once CMAKE_SYSTEM_NAME=Windows is set on a
# Linux host.
#
# The MSVC CRT and Windows SDK are downloaded from Microsoft's CDN by xwin
# at image-build time (the same mechanism cargo-xwin and Chromium use).
# Microsoft's license does not permit redistributing them, so unlike the
# macOS SDK they cannot be mirrored to a GitHub release — every cold image
# build pulls ~300 MB from download.visualstudio.microsoft.com.
#
# Differences from the native Windows build:
# - ICU is cross-compiled from source with clang's GNU driver + llvm-ar
# instead of MSBuild + the ICU Visual Studio solution. Same version
# (78.3), same /MT static CRT, same U_STATIC_IMPLEMENTATION, same
# statically-embedded 32 MB data table.
# - The "patch the cmake .bat files" workaround is not needed: cmake on a
# Linux host generates POSIX shell commands for custom commands.
ARG WIN_ARCH="x64"
ARG WIN_TRIPLE_ARCH="x86_64"
ARG WEBKIT_RELEASE_TYPE="Release"
ARG LTO_FLAG=""
ARG MARCH_FLAG="/clang:-march=nehalem"
ARG ICU_MARCH_FLAG="-march=nehalem"
# "address,undefined" for the -asan variant. x64 only: LLVM does not ship a
# Windows ARM64 ASAN runtime.
ARG ENABLE_SANITIZERS=""
ARG USE_MIMALLOC="OFF"
ARG USE_EXTERNAL_MIMALLOC="OFF"
ARG LLVM_VERSION="21"
ARG LLVM_DEBS_SHA256="4a79b0eae89af72b082997d361198f16aaefce3fa8ad3c56c8e97311ff31dd5f"
ARG XWIN_VERSION="0.9.0"
ARG XWIN_SHA256="31e1033f30608ba6b821d17f1461042bd54c23424813c9b4e9ae15b6d32fa4cd"
# Pinned MSVC CRT + Windows SDK versions. Bump deliberately; the manifest on
# Microsoft's CDN gains new versions with every VS release and old ones are
# eventually retired.
ARG MSVC_CRT_VERSION="14.44.17.14"
ARG WIN_SDK_VERSION="10.0.26100"
ARG ICU_VERSION="78.3"
ARG ICU_MAJOR="78"
ARG ICU_SHA256="3a2e7a47604ba702f345878308e6fefeca612ee895cf4a5f222e7955fabfe0c0"
# ───────────────────────────────────────────────────────────────────────────
# base — LLVM + the xwin-splatted MSVC CRT and Windows SDK
# ───────────────────────────────────────────────────────────────────────────
FROM ubuntu:24.04 AS base
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
ARG LLVM_VERSION
ARG LLVM_DEBS_SHA256
ARG XWIN_VERSION
ARG XWIN_SHA256
ARG MSVC_CRT_VERSION
ARG WIN_SDK_VERSION
RUN apt-get update && apt-get install -y --no-install-recommends \
wget unzip xz-utils ca-certificates curl \
cmake ninja-build make git \
ruby ruby-getoptlong perl python3 rsync file \
autoconf automake \
lsb-release software-properties-common gnupg \
&& rm -rf /var/lib/apt/lists/*
# Host clang. The debs are mirrored from apt.llvm.org to a GitHub release
# (see scripts/mirror-llvm-debs.sh) so the build doesn't depend on it.
ADD --checksum=sha256:${LLVM_DEBS_SHA256} \
https://github.com/oven-sh/WebKit/releases/download/llvm-${LLVM_VERSION}-debs/llvm-${LLVM_VERSION}-noble-amd64.tar.gz /tmp/llvm.tar.gz
RUN mkdir -p /tmp/llvm && tar xzf /tmp/llvm.tar.gz -C /tmp/llvm && \
apt-get update && apt-get install -y /tmp/llvm/*.deb && \
rm -rf /tmp/llvm /tmp/llvm.tar.gz /var/lib/apt/lists/* && \
for t in clang clang++ clang-cl lld-link ld.lld llvm-ar llvm-lib llvm-mt llvm-rc llvm-ranlib llvm-objcopy; do \
ln -sf /usr/bin/${t}-${LLVM_VERSION} /usr/local/bin/${t}; \
done
ENV CC=clang-${LLVM_VERSION}
ENV CXX=clang++-${LLVM_VERSION}
# xwin: download the MSVC CRT + Windows SDK from Microsoft's CDN and lay
# them out case-correctly for a case-sensitive filesystem. Both target
# arches are splatted so one image serves x64 and arm64 builds.
# --preserve-ms-arch-notation keeps the lib dirs named x64/arm64, which is
# what OptionsMSVC.cmake's WIN_LIB_ARCH expects.
#
# Two-phase `unpack` then `splat`: xwin processes payloads (one SdkLibs MSI
# per arch) on a rayon pool and each SdkLibs completion creates the same
# sdk/lib/<version> -> . compatibility symlink behind an `exists()` check, so
# with --arch x86_64,aarch64 the two can race and one dies on EEXIST. The
# unpack pass does the downloading/extracting (network- and CPU-bound) on all
# cores; the splat pass then runs with RAYON_NUM_THREADS=1 so the symlinks are
# created serially, and is cheap because the cache is already warm.
# --http-retry (default 0) covers the other observed failure here, transient
# I/O errors reading a .cab from download.visualstudio.microsoft.com.
ADD --checksum=sha256:${XWIN_SHA256} \
https://github.com/Jake-Shadle/xwin/releases/download/${XWIN_VERSION}/xwin-${XWIN_VERSION}-x86_64-unknown-linux-musl.tar.gz /xwin.tar.gz
RUN tar -xzf /xwin.tar.gz -C /usr/local/bin --strip-components=1 xwin-${XWIN_VERSION}-x86_64-unknown-linux-musl/xwin && \
rm /xwin.tar.gz && xwin --version
RUN xwin --accept-license --arch x86_64,aarch64 --cache-dir /xwin-cache --http-retry 5 \
--crt-version ${MSVC_CRT_VERSION} --sdk-version ${WIN_SDK_VERSION} \
unpack && \
RAYON_NUM_THREADS=1 \
xwin --accept-license --arch x86_64,aarch64 --cache-dir /xwin-cache --http-retry 5 \
--crt-version ${MSVC_CRT_VERSION} --sdk-version ${WIN_SDK_VERSION} \
splat --preserve-ms-arch-notation --include-debug-libs --output /winsdk && \
rm -rf /xwin-cache && \
test -f /winsdk/sdk/include/um/windows.h && \
test -f /winsdk/crt/lib/x64/libcmt.lib && \
test -f /winsdk/crt/lib/arm64/libcmt.lib
# Library and header references are case-insensitive on Windows; the build
# references e.g. `Winmm.lib`, `DbgHelp.lib`, and `Winver.h` while xwin
# extracts everything lowercase. Mirror the case-variant symlinks that
# Tools/Scripts/check-win-cross-build-deps creates: lowercase, UPPERCASE,
# Titlecase, and the known CamelCase names, for every lib in every lib dir.
RUN set -e; \
for dir in /winsdk/sdk/lib/um/x64 /winsdk/sdk/lib/um/arm64 \
/winsdk/sdk/lib/ucrt/x64 /winsdk/sdk/lib/ucrt/arm64 \
/winsdk/crt/lib/x64 /winsdk/crt/lib/arm64; do \
cd "$dir"; \
for f in *.lib; do \
base="${f%.lib}"; \
lower="$(echo "$base" | tr '[:upper:]' '[:lower:]')"; \
upper="$(echo "$base" | tr '[:lower:]' '[:upper:]')"; \
title="$(echo "${base:0:1}" | tr '[:lower:]' '[:upper:]')$(echo "${base:1}" | tr '[:upper:]' '[:lower:]')"; \
for v in "$lower.lib" "$upper.lib" "$upper.LIB" "$title.lib"; do \
[ -e "$v" ] || ln -s "$f" "$v"; \
done; \
done; \
for pair in "windowscodecs WindowsCodecs" "d2d1 D2d1" "dwrite Dwrite" "dwrite DWrite" \
"iphlpapi Iphlpapi" "shlwapi Shlwapi" "dbghelp DbgHelp"; do \
set -- $pair; \
if [ -e "$1.lib" ] && [ ! -e "$2.lib" ]; then ln -s "$1.lib" "$2.lib"; fi; \
done; \
done; \
ln -sf winver.h /winsdk/sdk/include/um/Winver.h && \
test -e /winsdk/sdk/lib/um/x64/Winmm.lib && test -e /winsdk/sdk/lib/um/x64/DbgHelp.lib
# clang_rt builtins for both target arches, extracted from the official LLVM
# Windows releases and mirrored to a GitHub release tag (the full archives
# are ~700 MB each for a pair of ~300 KB .lib files). compiler-rt is Apache
# 2.0 with LLVM exceptions; redistribution is permitted.
# OptionsMSVC.cmake's find_library(CLANG_BUILTINS_LIBRARY) searches
# WebKitLibraries/windows, which is symlinked to /winsdk in the build stage.
ADD --checksum=sha256:9cff03d2c5218693b1f91efc0074957c710eeddd932d57a681c8f558b81fbe8e \
https://github.com/oven-sh/WebKit/releases/download/compiler-rt-windows-21.1.8/clang_rt.builtins-x86_64.lib /winsdk/clang_rt.builtins-x86_64.lib
ADD --checksum=sha256:b1bfec6276dde6166aa038de10c378ec17996779786fe1747c40b833cb826e16 \
https://github.com/oven-sh/WebKit/releases/download/compiler-rt-windows-21.1.8/clang_rt.builtins-aarch64.lib /winsdk/clang_rt.builtins-aarch64.lib
# Windows ASAN/UBSan runtime for the -asan variant, mirrored from the same
# LLVM release as the builtins above (x64 only: LLVM ships no Windows ARM64
# ASAN runtime). Since LLVM 17 the ASAN runtime is a DLL even for /MT builds:
# executables link the clang_rt.asan_dynamic import lib plus the runtime
# thunk matching the CRT flavor (clang_rt.asan_static_runtime_thunk for /MT),
# and need the DLL next to them at run time. The import lib also provides the
# UBSan handlers, so -fsanitize=address,undefined needs no separate
# ubsan_standalone libraries. A Linux LLVM install doesn't ship any of these;
# WebKitCompilerFlags.cmake locates them via CLANG_LIB_PATH (set below).
ADD --checksum=sha256:3dff47943143ab2c1a786a7c90c88f897bfa34e8b5a4c9382eadf8373fd19ec0 \
https://github.com/oven-sh/WebKit/releases/download/compiler-rt-windows-21.1.8/clang_rt.asan_dynamic-x86_64.lib /winsdk/clang_rt.asan_dynamic-x86_64.lib
ADD --checksum=sha256:d4a0398ed7d2e1361674fbb2cfceec4df502052cfe17b51718b061e9fe523719 \
https://github.com/oven-sh/WebKit/releases/download/compiler-rt-windows-21.1.8/clang_rt.asan_static_runtime_thunk-x86_64.lib /winsdk/clang_rt.asan_static_runtime_thunk-x86_64.lib
ADD --checksum=sha256:b3c1d6c988792651cc0b0b61b4114cbe513a5e2f6beb067fd54a96b98be9b328 \
https://github.com/oven-sh/WebKit/releases/download/compiler-rt-windows-21.1.8/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib /winsdk/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
ADD --checksum=sha256:14025e779d3c67c53027312d7542aea8f814e5237e59f41cfc6165666189f886 \
https://github.com/oven-sh/WebKit/releases/download/compiler-rt-windows-21.1.8/clang_rt.asan_dynamic-x86_64.dll /winsdk/clang_rt.asan_dynamic-x86_64.dll
# Sanity check: compile + link a PE executable for both arches.
RUN for pair in "x86_64 x64" "aarch64 arm64"; do \
set -- $pair; \
export LIB="/winsdk/crt/lib/$2;/winsdk/sdk/lib/um/$2;/winsdk/sdk/lib/ucrt/$2"; \
echo 'int main(void){return 0;}' > /tmp/t.c && \
clang-cl --target=$1-pc-windows-msvc /vctoolsdir /winsdk/crt /winsdkdir /winsdk/sdk \
-fuse-ld=lld /MT /tmp/t.c -o /tmp/t.exe && \
file /tmp/t.exe | grep -q "PE32+" && rm -f /tmp/t.c /tmp/t.exe; \
done
# ───────────────────────────────────────────────────────────────────────────
# ICU — two-stage cross build. Host tools first (icupkg/pkgdata/genccode run
# on the build machine), then the target build with clang's GNU driver
# producing COFF objects archived by llvm-ar. The data table follows the same
# two transformations the Linux artifacts get:
# 1. build-icu.ps1's STAGE 1b filter (drop converters/translit/stringprep/
# confusables/unames, which bun never uses): ~32 MB → ~25 MB, matching the
# native windows sicudt.lib.
# 2. icu/compress-data.ts per-item zstd repack (same script as Dockerfile /
# Dockerfile.musl, in COFF mode): ~24 MB → ~11 MB. udata.cpp is patched
# with the weak bun_icu_maybe_decompress hook so bun decompresses items
# lazily on first use; ICU's own tools and the standalone jsc.exe leave
# the hook null and only read the kept-raw items.
# ───────────────────────────────────────────────────────────────────────────
FROM base AS build_icu
ARG WIN_ARCH
ARG WIN_TRIPLE_ARCH
ARG WEBKIT_RELEASE_TYPE
ARG ICU_MARCH_FLAG
ARG ICU_VERSION
ARG ICU_MAJOR
ARG ICU_SHA256
ARG LLVM_VERSION
# Tools for icu/compress-data.ts: zstd CLI (built from source — noble's apt
# has 1.5.5; 1.5.7 matches Bun's vendored decoder), node (>= 23.6 for type
# stripping), and patch for the udata.cpp hook.
ARG ZSTD_VERSION=1.5.7
ARG NODE_VERSION=24.16.0
RUN apt-get update && apt-get install -y --no-install-recommends patch && rm -rf /var/lib/apt/lists/* && \
curl -fsSL "https://github.com/facebook/zstd/releases/download/v${ZSTD_VERSION}/zstd-${ZSTD_VERSION}.tar.gz" | tar xz -C /tmp && \
make -C /tmp/zstd-${ZSTD_VERSION}/programs zstd -j$(nproc) CC=clang-${LLVM_VERSION} && \
cp /tmp/zstd-${ZSTD_VERSION}/programs/zstd /usr/local/bin/ && \
rm -rf /tmp/zstd-${ZSTD_VERSION} && \
zstd --version && \
curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/').tar.xz" \
| tar -xJ -C /usr/local --strip-components=1 && \
node --version
COPY icu/ /icu-bun/
ADD --checksum=sha256:${ICU_SHA256} \
https://github.com/unicode-org/icu/releases/download/release-${ICU_VERSION}/icu4c-${ICU_VERSION}-sources.tgz /icu-src.tgz
# The udata.cpp patch adds the weak per-item decompression hook (see the
# stage comment above); it lands in the cross-built sicuuc.lib.
RUN mkdir -p /icu-src && tar -xzf /icu-src.tgz -C /icu-src --strip-components=1 && rm /icu-src.tgz && \
patch -p1 -d /icu-src < /icu-bun/udata-decompress-hook.patch
# Stage 1: host build — only the tools matter.
RUN mkdir -p /icu-host && cd /icu-host && \
CC=clang CXX=clang++ /icu-src/source/runConfigureICU Linux \
--enable-static --disable-shared --disable-samples --disable-tests --disable-extras && \
make -j$(nproc) && test -x bin/pkgdata && test -x bin/icupkg
# Filter the prebuilt data package the same way the native build does
# (build-icu.ps1 STAGE 1b): drop charset converters, transliteration rules,
# stringprep profiles, confusables, and character names. Bun has zero
# ucnv_/utrans_/usprep_/uspoof_ consumers (TextCodecICU is removed in bun's
# TextEncodingRegistry.cpp), and this is what keeps the native sicudt.lib at
# ~24 MB instead of ~32 MB. Most of rbnf/ goes too, but root/ja/zh/zh_Hant stay: ICU
# reaches those on its own through the algorithmic numbering systems in
# numberingSystems.res (see the note and the staleness guard in ./Dockerfile).
# pkgdata packages data/in/icudt*l.dat verbatim in a cross build, so filtering it
# here is equivalent to the native flow.
RUN set -eu && \
DAT=$(ls /icu-src/source/data/in/icudt*l.dat) && \
FILTERED="${DAT%.dat}-filtered.dat" && \
/icu-host/bin/icupkg -l "$DAT" | \
grep -E '\.(cnv|spp|cfu)$|^cnvalias\.icu$|^translit/|^rbnf/|^unames\.icu$' | \
grep -vE '^rbnf/(root|res_index|ja|zh|zh_Hant)\.res$' > /tmp/icu-rm.lst && \
test -s /tmp/icu-rm.lst && \
/icu-host/bin/icupkg --auto_toc_prefix -r /tmp/icu-rm.lst "$DAT" "$FILTERED" && \
mv "$FILTERED" "$DAT" && \
ls -la "$DAT" && \
test "$(stat -c%s "$DAT")" -lt 27000000
# ICU's configure for non-GNU compilers insists that `link` on PATH is MS
# link.exe; coreutils' link(1) trips the check. Shadow it with lld-link.
RUN printf '#!/bin/sh\nexec /usr/local/bin/lld-link "$@"\n' > /usr/local/bin/link && chmod +x /usr/local/bin/link
# Stage 2: cross build. clang's GNU driver targeting *-pc-windows-msvc gives
# autoconf a Unix-style command line while emitting MSVC-ABI COFF objects.
# - ac_cv_c_compiler_gnu=yes -> picks the mh-mingw makefile fragment
# (GNU-style flags) instead of mh-msys-msvc
# (cl.exe flags the GNU driver rejects)
# - THREADS*FLAGS= -> drops mh-mingw's -mthreads (mingw-only)
# - -std=c++17 -> the MSVC STL requires C++14+; ICU's
# configure pins C++11
# - _MT without _DLL -> the STL headers emit DEFAULTLIB:libcpmt
# (static C++ runtime), matching /MT
RUN mkdir -p /icu-build && cd /icu-build && \
export LIB="/winsdk/crt/lib/${WIN_ARCH};/winsdk/sdk/lib/um/${WIN_ARCH};/winsdk/sdk/lib/ucrt/${WIN_ARCH}" && \
IMSVC="-isystem /winsdk/crt/include -isystem /winsdk/sdk/include/ucrt -isystem /winsdk/sdk/include/um -isystem /winsdk/sdk/include/shared" && \
DBG=""; if [ "${WEBKIT_RELEASE_TYPE}" = "Debug" ]; then DBG="-D_DEBUG"; fi && \
export CC=clang CXX=clang++ AR=llvm-ar RANLIB=llvm-ranlib && \
export CFLAGS="--target=${WIN_TRIPLE_ARCH}-pc-windows-msvc ${IMSVC} -O2 -DU_STATIC_IMPLEMENTATION -D_MT ${DBG} ${ICU_MARCH_FLAG}" && \
export CXXFLAGS="$CFLAGS" && \
export LDFLAGS="--target=${WIN_TRIPLE_ARCH}-pc-windows-msvc -fuse-ld=lld" && \
/icu-src/source/configure --host=${WIN_TRIPLE_ARCH}-w64-mingw32 \
--with-cross-build=/icu-host \
--enable-static --disable-shared --disable-samples --disable-tests \
--disable-extras --disable-tools --with-data-packaging=static \
--prefix=/icu-out \
ac_cv_c_compiler_gnu=yes ac_cv_cxx_compiler_gnu=yes && \
sed -i 's/-std=c++11/-std=c++17/' icudefs.mk && \
make -j$(nproc) THREADSCPPFLAGS= THREADSCFLAGS= THREADSCXXFLAGS= && \
test -f lib/libsicuuc.a && test -f lib/libsicuin.a && test -f lib/libsicudt.a
# Stage the libs under the names CMake's FindICU and Bun's build expect.
# sicudt.lib is produced by icu/compress-data.ts in COFF mode from the
# filtered data/in package: per-item zstd frames + the trained dictionary
# (icudt<major>_dat, bun_icu_zstd_dict, bun_icu_zstd_dict_size — already
# unprefixed, so no llvm-objcopy rename is needed). The libsicudt.a pkgdata
# produced during the cross make is ignored. Sanity-check the result is in
# the expected ballpark (the filtered-but-uncompressed table would be ~24 MB,
# an empty/stub one ~12 KB; the repacked one lands around 11 MB).
RUN mkdir -p /icu-out/lib /icu-out/include && \
cp -r /icu-src/source/common/unicode /icu-out/include/ && \
cp /icu-src/source/i18n/unicode/*.h /icu-out/include/unicode/ && \
cp /icu-build/lib/libsicuuc.a /icu-out/lib/sicuuc.lib && \
cp /icu-build/lib/libsicuin.a /icu-out/lib/sicuin.lib && \
DAT=$(ls /icu-src/source/data/in/icudt*l.dat) && \
node --experimental-strip-types /icu-bun/compress-data.ts "$DAT" /icu-out/lib/sicudt.lib \
--skip /icu-bun/keep-raw.txt --icupkg /icu-host/bin/icupkg \
--cc "clang --target=${WIN_TRIPLE_ARCH}-pc-windows-msvc" --ar llvm-ar --obj-format coff && \
SIZE=$(stat -c%s /icu-out/lib/sicudt.lib) && echo "sicudt.lib: ${SIZE} bytes" && \
test "$SIZE" -gt 8000000 && test "$SIZE" -lt 16000000 && \
llvm-nm-${LLVM_VERSION} /icu-out/lib/sicudt.lib | grep -q " R icudt${ICU_MAJOR}_dat" && \
llvm-nm-${LLVM_VERSION} /icu-out/lib/sicudt.lib | grep -q " R bun_icu_zstd_dict"
# ───────────────────────────────────────────────────────────────────────────
# WebKit (JSCOnly)
# ───────────────────────────────────────────────────────────────────────────
FROM base AS build_webkit
ARG WIN_ARCH
ARG WIN_TRIPLE_ARCH
ARG WEBKIT_RELEASE_TYPE
ARG LTO_FLAG
ARG MARCH_FLAG
ARG ENABLE_SANITIZERS
ARG USE_MIMALLOC
ARG USE_EXTERNAL_MIMALLOC
COPY --from=build_icu /icu-out /icu-out
COPY . /webkit
WORKDIR /webkit
ENV WEBKIT_OUT_DIR=/webkitbuild
# OptionsMSVC.cmake hardcodes the cross sysroot to
# ${CMAKE_SOURCE_DIR}/WebKitLibraries/windows; point it at the cached splat.
RUN rm -rf /webkit/WebKitLibraries/windows && ln -s /winsdk /webkit/WebKitLibraries/windows
# The LIB environment variable is read by lld-link the same way link.exe
# reads it. It must be set for cmake's initial compiler detection — that
# try_compile links an executable before OptionsMSVC.cmake has a chance to
# add the -libpath: flags.
#
# ${LTO_FLAG} goes in the _RELEASE config flags, not CMAKE_C(XX)_FLAGS:
# cmake's compiler detection compiles a test object with CMAKE_C_FLAGS and
# sniffs the target architecture out of it, and an LLVM bitcode object (which
# is what -flto produces) defeats the sniffing — cmake falls back to x64 and
# passes /machine:x64 to an arm64 link.
#
# EXE_LD_FLAGS adds /force:multiple to the standalone jsc.exe canary link, but
# only for the LTO variant. WTF/JSC declare the Bun integration hooks as
# `extern "C" __attribute__((weak))` (WTFTimer__*, Bun__reportUnhandledError,
# ...). COFF has no real weak-undefined symbols: clang lowers each referencing
# TU's declaration to an IMAGE_SYM_CLASS_WEAK_EXTERNAL plus a per-TU
# absolute-0 default, and lld-link reports those defaults as "duplicate
# symbol" as soon as two objects in the link reference the same *unresolved*
# hook. Non-LTO builds never hit it (exactly one TU references each hook);
# ThinLTO importing copies the referencing functions into other modules and
# trips it. Bun's own bun.exe link is unaffected — it defines every one of
# these hooks, and a resolved weak external never produces the duplicate.
# /force:multiple keeps the canary link working by picking the first
# (absolute-0) default, which is exactly the "hook absent" behaviour the
# standalone shell wants. It only applies to jsc.exe — the packaged static
# libraries are unchanged.
RUN --mount=type=tmpfs,target=/webkitbuild \
export LIB="/winsdk/crt/lib/${WIN_ARCH};/winsdk/sdk/lib/um/${WIN_ARCH};/winsdk/sdk/lib/ucrt/${WIN_ARCH};/winsdk" && \
export CRT="MultiThreaded"; if [ "${WEBKIT_RELEASE_TYPE}" = "Debug" ]; then CRT="MultiThreadedDebug"; fi && \
SAN_FLAGS=""; ENABLE_ASSERTS="AUTO"; \
if [ -n "${ENABLE_SANITIZERS}" ]; then SAN_FLAGS="/GF- -D_DISABLE_STRING_ANNOTATION -D_DISABLE_VECTOR_ANNOTATION"; ENABLE_ASSERTS="ON"; fi && \
EXE_LD_FLAGS=""; if [ -n "${LTO_FLAG}" ]; then EXE_LD_FLAGS="/force:multiple"; fi && \
cd /webkitbuild && \
cmake \
-DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_SYSTEM_PROCESSOR=$([ "${WIN_ARCH}" = "arm64" ] && echo ARM64 || echo AMD64) \
-DCMAKE_C_COMPILER=$(which clang-cl) \
-DCMAKE_CXX_COMPILER=$(which clang-cl) \
-DCMAKE_LINKER=$(which lld-link) \
-DCMAKE_AR=$(which llvm-lib) \
-DCMAKE_MT=$(which llvm-mt) \
-DCMAKE_RC_COMPILER=$(which llvm-rc) \
-DCMAKE_RANLIB=$(which llvm-ranlib) \
-DPORT=JSCOnly \
-DENABLE_STATIC_JSC=ON \
-DALLOW_LINE_AND_COLUMN_NUMBER_IN_BUILTINS=ON \
-DCMAKE_BUILD_TYPE=${WEBKIT_RELEASE_TYPE} \
-DUSE_THIN_ARCHIVES=OFF \
-DENABLE_JIT=ON \
-DENABLE_DFG_JIT=ON \
-DENABLE_FTL_JIT=ON \
-DENABLE_WEBASSEMBLY_BBQJIT=ON \
-DENABLE_WEBASSEMBLY_OMGJIT=ON \
-DENABLE_SAMPLING_PROFILER=ON \
-DENABLE_WEBASSEMBLY=ON \
-DUSE_BUN_JSC_ADDITIONS=ON \
-DUSE_BUN_EVENT_LOOP=ON \
-DUSE_MIMALLOC="$USE_MIMALLOC" \
-DUSE_EXTERNAL_MIMALLOC="$USE_EXTERNAL_MIMALLOC" \
-DENABLE_BUN_SKIP_FAILING_ASSERTIONS=ON \
-DENABLE_REMOTE_INSPECTOR=ON \
-DENABLE_SANITIZERS="${ENABLE_SANITIZERS}" \
-DENABLE_ASSERTS="${ENABLE_ASSERTS}" \
-DCLANG_LIB_PATH=/winsdk \
-DCMAKE_MSVC_RUNTIME_LIBRARY=${CRT} \
-DICU_ROOT=/icu-out \
-DICU_INCLUDE_DIR=/icu-out/include \
-DICU_DATA_LIBRARY_RELEASE=/icu-out/lib/sicudt.lib \
-DICU_I18N_LIBRARY_RELEASE=/icu-out/lib/sicuin.lib \
-DICU_UC_LIBRARY_RELEASE=/icu-out/lib/sicuuc.lib \
"-DCMAKE_EXE_LINKER_FLAGS=${EXE_LD_FLAGS}" \
"-DCMAKE_C_FLAGS=--target=${WIN_TRIPLE_ARCH}-pc-windows-msvc /Zi /DU_STATIC_IMPLEMENTATION ${MARCH_FLAG} ${SAN_FLAGS}" \
"-DCMAKE_CXX_FLAGS=--target=${WIN_TRIPLE_ARCH}-pc-windows-msvc /Zi /DU_STATIC_IMPLEMENTATION ${MARCH_FLAG} ${SAN_FLAGS} /clang:-fno-c++-static-destructors" \
"-DCMAKE_C_FLAGS_RELEASE=/O2 /Ob2 /DNDEBUG ${LTO_FLAG}" \
"-DCMAKE_CXX_FLAGS_RELEASE=/O2 /Ob2 /DNDEBUG ${LTO_FLAG}" \
"-DCMAKE_C_FLAGS_DEBUG=/FS /O0 /Ob0" \
"-DCMAKE_CXX_FLAGS_DEBUG=/FS /O0 /Ob0" \
-G Ninja \
/webkit && \
cmake --build /webkitbuild --config ${WEBKIT_RELEASE_TYPE} --target jsc --target testFFI && \
\
mkdir -p /output/lib /output/include/JavaScriptCore /output/include/wtf /output/include/bmalloc && \
cp -rL $WEBKIT_OUT_DIR/lib/* /output/lib/ && \
cp -rL $WEBKIT_OUT_DIR/bin /output/bin && \
# Sanitizer builds ship the ASAN runtime with the artifact: anything that
# links these instrumented static libs needs the version-matched import
# lib and /MT runtime thunk, and anything that runs (bin/jsc.exe, a
# consumer's bun.exe) needs clang_rt.asan_dynamic-x86_64.dll next to it.
if [ -n "${ENABLE_SANITIZERS}" ]; then \
cp /winsdk/clang_rt.asan_dynamic-x86_64.lib /winsdk/clang_rt.asan_static_runtime_thunk-x86_64.lib /winsdk/clang_rt.asan_dynamic-x86_64.dll /output/lib/ && \
cp /winsdk/clang_rt.asan_dynamic-x86_64.dll /output/bin/; \
fi && \
cp -L $WEBKIT_OUT_DIR/cmakeconfig.h /output/include/cmakeconfig.h && \
if [ "${WEBKIT_RELEASE_TYPE}" = "Debug" ]; then \
cp /icu-out/lib/sicudt.lib /output/lib/sicudtd.lib && \
cp /icu-out/lib/sicuin.lib /output/lib/sicuind.lib && \
cp /icu-out/lib/sicuuc.lib /output/lib/sicuucd.lib; \
else \
cp /icu-out/lib/sicudt.lib /output/lib/sicudt.lib && \
cp /icu-out/lib/sicuin.lib /output/lib/sicuin.lib && \
cp /icu-out/lib/sicuuc.lib /output/lib/sicuuc.lib; \
fi && \
# -L everywhere: WebKit's cmake stages the framework headers as symlinks
# into the source tree. Copied verbatim they'd dangle outside this
# container, so dereference every copy into /output.
cp -rL $WEBKIT_OUT_DIR/JavaScriptCore/DerivedSources/* /output/include/JavaScriptCore/ && \
cp -rfL $WEBKIT_OUT_DIR/JavaScriptCore/Headers/JavaScriptCore/* /output/include/JavaScriptCore/ && \
cp -rfL $WEBKIT_OUT_DIR/JavaScriptCore/PrivateHeaders/JavaScriptCore/* /output/include/JavaScriptCore/ && \
cp -fL $WEBKIT_OUT_DIR/JavaScriptCore/DerivedSources/*.h /output/include/JavaScriptCore/ && \
cp -fL $WEBKIT_OUT_DIR/JavaScriptCore/DerivedSources/*/*.h /output/include/JavaScriptCore/ && \
cp -fL $WEBKIT_OUT_DIR/JavaScriptCore/DerivedSources/*.json /output/ && \
{ cp -rL $WEBKIT_OUT_DIR/WTF/DerivedSources/* /output/include/wtf/ 2>/dev/null || true; } && \
cp -rL $WEBKIT_OUT_DIR/WTF/Headers/wtf/* /output/include/wtf/ && \
cp -rL $WEBKIT_OUT_DIR/bmalloc/Headers/bmalloc/* /output/include/bmalloc/ && \
sed -i 's|#import <JavaScriptCore/JSValuePrivate.h>|#include <JavaScriptCore/JSValuePrivate.h>|' /output/include/JavaScriptCore/JSValueInternal.h && \
cp -rL /icu-out/include/* /output/include/ && \
if find /output/include -type l -print -quit | grep -q .; then echo "ERROR: dangling symlinks in /output/include"; find /output/include -type l | head; exit 1; fi
FROM scratch AS artifact
COPY --from=build_webkit /output /