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
// Build script for bun_uws_sys: compiles the uSockets C library (libusockets)
// using the `cc` crate. This provides real us_socket_* / us_socket_group_*
// symbols, replacing the no-op stubs in bao_native_stubs.
//
// Two compilation modes:
// 1. Plain TCP (default): compiles C sources without BoringSSL, links
// crypto/ssl_stubs.c for us_internal_ssl_* no-ops.
// 2. With TLS (future, Wave 74-TLS): define BAO_UWS_WITH_TLS, compile
// crypto/openssl.c, link BoringSSL.
use std::env;
use std::path::PathBuf;
fn main() {
let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
// W0b publish incorporation: all C/C++ sources are vendored in-package
// under csrc/ (byte-identical copies of packages/bun-usockets/src,
// packages/bun-uws/src, vendor/lsquic public + internal headers,
// vendor/lsqpack lsxpack_header.h, vendor/lshpack headers, and the
// vendor/boringssl include tree). A crates.io package can only ship
// in-package files, so the published crate carries its own source set and
// local builds compile the exact same bytes from csrc/. Keep csrc/ in
// sync when absorbing upstream changes.
let csrc_dir = crate_dir.join("csrc");
let usockets_dir = csrc_dir.join("bun-usockets");
let usockets_src = usockets_dir.join("src");
let with_tls = env::var("BAO_UWS_WITH_TLS").as_deref() != Ok("0");
// ── C compilation: uSockets core ──────────────────────────────────────
let mut c_build = cc::Build::new();
// Use clang: the uSockets C sources use __attribute__((always_inline))
// on static functions, which is incompatible with GCC + -fPIC.
// Bun's upstream build uses clang exclusively.
c_build.compiler("clang");
// Compiler flags
c_build
.opt_level(1) // -O1: always_inline requires optimization
.flag("-DBUN_DEBUG=1") // makes nonnull_arg/nonnull_fn_decl expand to empty
.flag("-DLIBUS_USE_EPOLL=1")
.flag("-DLIBUS_MAX_READY_POLLS=1024")
.flag("-DLIBUS_SOCKET_DESCRIPTOR=int")
.flag("-DLIBUS_SOCKET_ERROR=-1")
.flag("-DLIBUS_EXT_ALIGNMENT=16");
// GCC compat: __has_feature is Clang-only. Define it as 0 via a wrapper
// flag. We use a separate .h file to define it as a function-like macro.
let wrapper_h = crate_dir.join("src").join("_gcc_compat.h");
if wrapper_h.exists() {
c_build.flag(format!("-include{}", wrapper_h.display()));
}
if with_tls {
c_build
.flag("-DLIBUS_USE_OPENSSL=1")
.flag("-DLIBUS_USE_BORINGSSL=1")
.flag("-DWITH_BORINGSSL=1");
}
// Include paths
let lsquic_dir = csrc_dir.join("deps").join("lsquic");
let lsqpack_dir = csrc_dir.join("deps").join("lsqpack");
let lshpack_dir = csrc_dir.join("deps").join("lshpack");
c_build
.include(&usockets_dir) // for #include "libusockets.h"
.include(&usockets_src) // for #include "internal/internal.h"
.include(usockets_src.join("internal")) // for internal/ sub-includes
.include(usockets_src.join("internal/networking")) // for bsd.h
.include(lsquic_dir.join("include")) // for #include "lsquic.h" (quic.c)
.include(lsquic_dir.join("src").join("liblsquic")) // for lsquic internal headers
.include(&lsqpack_dir) // for #include "lsxpack_header.h" (quic.c)
.include(&lshpack_dir); // for #include "lshpack.h" (quic.c → lsxpack)
// C source files (uSockets core — platform-independent)
let core_sources = [
"bsd.c",
"context.c",
"loop.c",
"socket.c",
"udp.c",
"quic.c",
];
for src in &core_sources {
let path = usockets_src.join(src);
if path.exists() {
c_build.file(&path);
} else {
panic!("uSockets source file not found: {:?}", path);
}
}
// Platform-specific eventing backend
#[cfg(target_os = "linux")]
{
c_build.file(usockets_src.join("eventing/epoll_kqueue.c"));
}
#[cfg(target_os = "macos")]
{
c_build.file(usockets_src.join("eventing/epoll_kqueue.c"));
}
// SSL: stubs or real OpenSSL
if with_tls {
c_build.file(usockets_src.join("crypto/openssl.c"));
let boringssl_dir = csrc_dir.join("boringssl");
c_build.include(boringssl_dir.join("include"));
} else {
c_build.file(usockets_src.join("crypto/ssl_stubs.c"));
}
// Skip QUIC and UDP for now (not needed for P1-B HTTP server)
// Skip libuv eventing backend (we use epoll/kqueue)
c_build.compile("usockets");
// ── C++ compilation: TLS crypto helpers (sni_tree + root_certs) ────────
// These are C++ files that openssl.c calls into; compiled separately
// because the main uSockets build uses the C compiler.
if with_tls {
let boringssl_dir = csrc_dir.join("boringssl");
let mut tls_cpp = cc::Build::new();
tls_cpp.compiler("clang++");
tls_cpp.cpp(true);
tls_cpp.opt_level(1);
tls_cpp
.flag("-std=c++17")
.flag("-fno-exceptions")
.flag("-fno-rtti")
.flag("-DBORINGSSL_IMPLEMENTATION=1")
.include(boringssl_dir.join("include"))
.include(&usockets_dir)
.include(&usockets_src)
.include(usockets_src.join("internal"));
tls_cpp.file(usockets_src.join("crypto/sni_tree.cpp"));
tls_cpp.file(usockets_src.join("crypto/root_certs.cpp"));
// Platform-specific system certificate loading
#[cfg(target_os = "linux")]
{
tls_cpp.file(usockets_src.join("crypto/root_certs_linux.cpp"));
}
tls_cpp.compile("usockets_tls");
}
// ── C++ compilation: uWS C-ABI wrapper (libuwsockets.cpp) ────────────
// Provides uws_app_*, uws_res_*, uws_req_* symbols that Rust FFI calls.
let uws_dir = csrc_dir.join("bun-uws");
let uws_src = uws_dir.join("src");
let mut cpp_build = cc::Build::new();
cpp_build.compiler("clang++");
cpp_build.cpp(true);
cpp_build.opt_level(1);
cpp_build
.flag("-std=c++20")
.flag("-DBUN_DEBUG=1")
.flag("-DLIBUS_USE_EPOLL=1")
.flag("-DLIBUS_MAX_READY_POLLS=1024")
.flag("-DLIBUS_SOCKET_DESCRIPTOR=int")
.flag("-DLIBUS_SOCKET_ERROR=-1")
.flag("-DLIBUS_EXT_ALIGNMENT=16")
.flag("-fno-exceptions") // uWS is compiled without exceptions
.flag("-Wno-deprecated-declarations");
// GCC compat wrapper
if wrapper_h.exists() {
cpp_build.flag(format!("-include{}", wrapper_h.display()));
}
if with_tls {
cpp_build
.flag("-DLIBUS_USE_OPENSSL=1")
.flag("-DLIBUS_USE_BORINGSSL=1")
.flag("-DWITH_BORINGSSL=1");
let boringssl_dir = csrc_dir.join("boringssl");
cpp_build.include(boringssl_dir.join("include"));
}
// Include paths for uWS C++ headers + uSockets internals
cpp_build
.include(&csrc_dir) // for #include <bun-uws/src/App.h>
.include(&uws_dir) // for #include "App.h" via bun-uws/src/
.include(&uws_src) // for #include "App.h" etc.
.include(&usockets_dir) // for #include "libusockets.h"
.include(&usockets_src) // for #include "internal/internal.h"
.include(usockets_src.join("internal"))
.include(usockets_src.join("internal/networking"))
.include(&crate_dir) // for #include "_libusockets.h"
.include(crate_dir.join("src")); // for #include <wtf/Assertions.h>
cpp_build.file(crate_dir.join("libuwsockets.cpp"));
cpp_build.compile("uwsockets");
// ── Link dependencies ─────────────────────────────────────────────────
// pthread is needed for bsd.c (pthread_atfork in some code paths)
println!("cargo:rustc-link-lib=pthread");
// zlib for HTTP content-encoding (gzip/deflate) in libuwsockets.cpp
println!("cargo:rustc-link-lib=z");
// libdeflate for fast compression/decompression in libuwsockets.cpp
println!("cargo:rustc-link-lib=deflate");
// SPEC (CLAUDE.md L13): libuwsockets.a (C++ wrapper) depends on libusockets.a
// (C core). For static archives, the linker resolves undefined symbols only
// from libraries listed AFTER the reference. cc::compile emits
// `cargo:rustc-link-lib=static=usockets` BEFORE
// `cargo:rustc-link-lib=static=uwsockets`, which puts usockets first in the
// link line — but uwsockets (compiled later) references symbols in usockets,
// so usockets must come AFTER uwsockets. Re-declare usockets LAST to fix the
// order (Cargo dedupes link libs in dep-graph order, so this becomes the
// effective position).
println!("cargo:rustc-link-lib=static=usockets");
// ── Rebuild hints ─────────────────────────────────────────────────────
// Rebuild if any C source changes
println!("cargo:rerun-if-changed={}", usockets_src.join("bsd.c").display());
println!("cargo:rerun-if-changed={}", usockets_src.join("context.c").display());
println!("cargo:rerun-if-changed={}", usockets_src.join("loop.c").display());
println!("cargo:rerun-if-changed={}", usockets_src.join("socket.c").display());
println!("cargo:rerun-if-changed={}", usockets_src.join("udp.c").display());
println!("cargo:rerun-if-changed={}", usockets_src.join("crypto/ssl_stubs.c").display());
println!("cargo:rerun-if-changed={}", usockets_src.join("eventing/epoll_kqueue.c").display());
println!("cargo:rerun-if-changed={}", usockets_src.join("internal/internal.h").display());
println!("cargo:rerun-if-changed={}", usockets_src.join("libusockets.h").display());
// ── Rebuild hints: C++ wrapper TU ─────────────────────────────────────
// libuwsockets.cpp is one translation unit that #includes the uWS headers
// (App.h → HttpContext.h → HttpParser.h → HttpContextData.h …), so an edit
// to any of them must rerun this script. `cc` only emits rerun-if-changed
// for the compiled .cpp file itself, never for headers — without these
// lines a header-only change (e.g. an absorbed upstream parser fix)
// silently links the stale archive.
println!("cargo:rerun-if-changed={}", crate_dir.join("libuwsockets.cpp").display());
println!("cargo:rerun-if-changed={}", crate_dir.join("libuwsockets_h3.cpp").display());
println!("cargo:rerun-if-changed={}", crate_dir.join("_libusockets.h").display());
if let Ok(entries) = std::fs::read_dir(&uws_src) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().map_or(false, |ext| ext == "h" || ext == "cpp") {
println!("cargo:rerun-if-changed={}", path.display());
}
}
}
// wtf/ helpers are included as <wtf/*.h> from the crate's src dir.
if let Ok(entries) = std::fs::read_dir(crate_dir.join("src").join("wtf")) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().map_or(false, |ext| ext == "h") {
println!("cargo:rerun-if-changed={}", path.display());
}
}
}
}