Skip to content

Commit 1108493

Browse files
make the windows binary smaller (#12523)
Co-authored-by: Jarred Sumner <[email protected]>
1 parent 36fd311 commit 1108493

File tree

5 files changed

+148
-108
lines changed

5 files changed

+148
-108
lines changed

CMakeLists.txt

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ set(CMAKE_C_STANDARD 17)
1515
set(CMAKE_CXX_STANDARD_REQUIRED ON)
1616
set(CMAKE_C_STANDARD_REQUIRED ON)
1717

18-
# Should not start with v
1918
# Used in process.version, process.versions.node, napi, and elsewhere
2019
set(REPORTED_NODEJS_VERSION "22.3.0")
2120

@@ -58,11 +57,8 @@ elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
5857
set(DEFAULT_ZIG_OPTIMIZE "ReleaseFast")
5958

6059
if(WIN32)
61-
# lld-link will strip it for you, so we can build directly to bun.exe
60+
# Debug symbols are in a separate file: bun.pdb
6261
set(bun "bun")
63-
64-
# TODO(@paperdave): Remove this
65-
# it is enabled for the time being to make sure to catch more bugs in the experimental windows builds
6662
set(DEFAULT_ZIG_OPTIMIZE "ReleaseSafe")
6763
else()
6864
if(ZIG_OPTIMIZE STREQUAL "Debug")
@@ -874,13 +870,24 @@ file(GLOB ZIG_FILES
874870
"${BUN_SRC}/*/*/*/*/*.zig"
875871
)
876872

873+
if(NOT BUN_ZIG_OBJ_FORMAT)
874+
# To use LLVM bitcode from Zig, more work needs to be done. Currently, an install of
875+
# LLVM 18.1.7 does not compatible with what bitcode Zig 0.13 outputs (has LLVM 18.1.7)
876+
# Change to "bc" to experiment, "Invalid record" means it is not valid output.
877+
set(BUN_ZIG_OBJ_FORMAT "obj")
878+
endif()
879+
877880
if(NOT BUN_ZIG_OBJ_DIR)
878881
set(BUN_ZIG_OBJ_DIR "${BUN_WORKDIR}/CMakeFiles")
879882
endif()
880883

881884
get_filename_component(BUN_ZIG_OBJ_DIR "${BUN_ZIG_OBJ_DIR}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
882885

883-
set(BUN_ZIG_OBJ "${BUN_ZIG_OBJ_DIR}/bun-zig.o")
886+
if(WIN32)
887+
set(BUN_ZIG_OBJ "${BUN_ZIG_OBJ_DIR}/bun-zig.o")
888+
else()
889+
set(BUN_ZIG_OBJ "${BUN_ZIG_OBJ_DIR}/bun-zig.o")
890+
endif()
884891

885892
set(USES_TERMINAL_NOT_IN_CI "")
886893

@@ -904,6 +911,7 @@ if(NOT BUN_LINK_ONLY AND NOT BUN_CPP_ONLY)
904911
"-Dtarget=${ZIG_TARGET}"
905912
"-Denable_logs=${ENABLE_LOGS}"
906913
"-Dreported_nodejs_version=${REPORTED_NODEJS_VERSION}"
914+
"-Dobj_format=${BUN_ZIG_OBJ_FORMAT}"
907915
DEPENDS
908916
"${CMAKE_CURRENT_SOURCE_DIR}/build.zig"
909917
"${ZIG_FILES}"
@@ -1101,13 +1109,36 @@ elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
11011109
if(USE_LTO)
11021110
target_compile_options(${bun} PUBLIC -Xclang -emit-llvm-bc)
11031111

1104-
# -emit-llvm seems to not be supported or under a different name on Windows.
11051112
list(APPEND LTO_FLAG "-flto=full")
1113+
list(APPEND LTO_LINK_FLAG "-flto=full")
11061114
list(APPEND LTO_LINK_FLAG "/LTCG")
1115+
list(APPEND LTO_LINK_FLAG "/OPT:REF")
1116+
list(APPEND LTO_LINK_FLAG "/OPT:NOICF")
11071117
endif()
11081118

1109-
target_compile_options(${bun} PUBLIC /O2 ${LTO_FLAG})
1110-
target_link_options(${bun} PUBLIC ${LTO_LINK_FLAG} /DEBUG:FULL)
1119+
target_compile_options(${bun} PUBLIC
1120+
/O2
1121+
${LTO_FLAG}
1122+
/Gy
1123+
/Gw
1124+
/GF
1125+
/GA
1126+
)
1127+
target_link_options(${bun} PUBLIC
1128+
${LTO_LINK_FLAG}
1129+
/DEBUG:FULL
1130+
1131+
/delayload:ole32.dll
1132+
/delayload:WINMM.dll
1133+
/delayload:dbghelp.dll
1134+
/delayload:VCRUNTIME140_1.dll
1135+
1136+
# libuv loads these two immediately, but for some reason it seems to still be slightly faster to delayload them
1137+
/delayload:WS2_32.dll
1138+
/delayload:WSOCK32.dll
1139+
/delayload:ADVAPI32.dll
1140+
/delayload:IPHLPAPI.dll
1141+
)
11111142
endif()
11121143
endif()
11131144

@@ -1451,7 +1482,6 @@ if(NOT WIN32)
14511482
target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libJavaScriptCore.a")
14521483
target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libbmalloc.a")
14531484
else()
1454-
target_link_options(${bun} PRIVATE "-static")
14551485
target_link_libraries(${bun} PRIVATE
14561486
"${WEBKIT_LIB_DIR}/WTF.lib"
14571487
"${WEBKIT_LIB_DIR}/JavaScriptCore.lib"
@@ -1464,6 +1494,7 @@ else()
14641494
userenv
14651495
dbghelp
14661496
wsock32 # ws2_32 required by TransmitFile aka sendfile on windows
1497+
delayimp.lib
14671498
)
14681499
endif()
14691500

build.zig

Lines changed: 97 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ comptime {
3333
}
3434
}
3535

36-
const default_reported_nodejs_version = "22.3.0";
37-
3836
const zero_sha = "0000000000000000000000000000000000000000";
3937

4038
const BunBuildOptions = struct {
@@ -48,7 +46,7 @@ const BunBuildOptions = struct {
4846
sha: []const u8,
4947
enable_logs: bool = false,
5048
tracy_callstack_depth: u16,
51-
reported_nodejs_version: []const u8 = default_reported_nodejs_version,
49+
reported_nodejs_version: Version,
5250

5351
generated_code_dir: []const u8,
5452

@@ -73,14 +71,7 @@ const BunBuildOptions = struct {
7371
opts.addOption([:0]const u8, "sha", b.allocator.dupeZ(u8, this.sha) catch @panic("OOM"));
7472
opts.addOption(bool, "baseline", this.isBaseline());
7573
opts.addOption(bool, "enable_logs", this.enable_logs);
76-
opts.addOption([:0]const u8, "reported_nodejs_version", b.allocator.dupeZ(u8, this.reported_nodejs_version) catch @panic("OOM"));
77-
if (this.reported_nodejs_version.len > 0 and this.reported_nodejs_version[0] == 'v') {
78-
@panic("Node.js version should not start with 'v'");
79-
}
80-
81-
if (this.reported_nodejs_version.len == 0) {
82-
@panic("Node.js version should not be empty");
83-
}
74+
opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{}", .{this.reported_nodejs_version}));
8475

8576
const mod = opts.createModule();
8677
this.cached_options_module = mod;
@@ -122,6 +113,23 @@ pub fn getOSGlibCVersion(os: OperatingSystem) ?Version {
122113
};
123114
}
124115

116+
pub fn getCpuModel(os: OperatingSystem, arch: Arch) ?Target.Query.CpuModel {
117+
// https://github.com/oven-sh/bun/issues/12076
118+
if (os == .linux and arch == .aarch64) {
119+
return .{ .explicit = &Target.aarch64.cpu.cortex_a35 };
120+
}
121+
122+
// Be explicit and ensure we do not accidentally target a newer M-series chip
123+
if (os == .mac and arch == .aarch64) {
124+
return .{ .explicit = &Target.aarch64.cpu.apple_m1 };
125+
}
126+
127+
// note: x86_64 is dealt with in the CMake config and passed in.
128+
// the reason for the explicit handling on aarch64 is due to troubles
129+
// passing the exact target in via flags.
130+
return null;
131+
}
132+
125133
pub fn build(b: *Build) !void {
126134
std.log.info("zig compiler v{s}", .{builtin.zig_version_string});
127135

@@ -147,9 +155,12 @@ pub fn build(b: *Build) !void {
147155
break :brk .{ os, arch };
148156
};
149157

150-
if (os == .linux and arch == .aarch64) {
151-
// #12076
152-
target_query.cpu_model = .{ .explicit = &std.Target.aarch64.cpu.cortex_a35 };
158+
// target must be refined to support older but very popular devices on
159+
// aarch64, this means moving the minimum supported CPU to support certain
160+
// raspberry PIs. there are also a number of cloud hosts that use virtual
161+
// machines with surprisingly out of date versions of glibc.
162+
if (getCpuModel(os, arch)) |cpu_model| {
163+
target_query.cpu_model = cpu_model;
153164
}
154165

155166
target_query.os_version_min = getOSVersionMin(os);
@@ -168,6 +179,8 @@ pub fn build(b: *Build) !void {
168179
break :ref_trace if (trace == 0) null else trace;
169180
};
170181

182+
const obj_format = b.option(ObjectFormat, "obj_format", "Output file for object files") orelse .obj;
183+
171184
var build_options = BunBuildOptions{
172185
.target = target,
173186
.optimize = optimize,
@@ -183,7 +196,10 @@ pub fn build(b: *Build) !void {
183196
break :canary if (rev == 0) null else rev;
184197
},
185198

186-
.reported_nodejs_version = b.option([]const u8, "reported_nodejs_version", "Reported Node.js version") orelse default_reported_nodejs_version,
199+
.reported_nodejs_version = try Version.parse(
200+
b.option([]const u8, "reported_nodejs_version", "Reported Node.js version") orelse
201+
"0.0.0-unset",
202+
),
187203

188204
.sha = sha: {
189205
const sha = b.option([]const u8, "sha", "Force the git sha") orelse
@@ -229,7 +245,7 @@ pub fn build(b: *Build) !void {
229245
var step = b.step("obj", "Build Bun's Zig code as a .o file");
230246
var bun_obj = addBunObject(b, &build_options);
231247
step.dependOn(&bun_obj.step);
232-
step.dependOn(&b.addInstallFile(bun_obj.getEmittedBin(), "bun-zig.o").step);
248+
step.dependOn(addInstallObjectFile(b, bun_obj, "bun-zig", obj_format));
233249
}
234250

235251
// zig build windows-shim
@@ -257,95 +273,59 @@ pub fn build(b: *Build) !void {
257273

258274
// zig build check-all
259275
{
260-
var step = b.step("check-all", "Check for semantic analysis errors on all supported platforms");
261-
inline for (.{
276+
const step = b.step("check-all", "Check for semantic analysis errors on all supported platforms");
277+
addMultiCheck(b, step, build_options, &.{
262278
.{ .os = .windows, .arch = .x86_64 },
263279
.{ .os = .mac, .arch = .x86_64 },
264280
.{ .os = .mac, .arch = .aarch64 },
265281
.{ .os = .linux, .arch = .x86_64 },
266282
.{ .os = .linux, .arch = .aarch64 },
267-
}) |check| {
268-
inline for (.{ .Debug, .ReleaseFast }) |mode| {
269-
const check_target = b.resolveTargetQuery(.{
270-
.os_tag = OperatingSystem.stdOSTag(check.os),
271-
.cpu_arch = check.arch,
272-
.cpu_model = if (check.os == .linux and check.arch == .aarch64) .{ .explicit = &std.Target.aarch64.cpu.cortex_a35 } else .{ .determined_by_cpu_arch = {} },
273-
.os_version_min = getOSVersionMin(check.os),
274-
.glibc_version = getOSGlibCVersion(check.os),
275-
});
276-
277-
var options = BunBuildOptions{
278-
.target = check_target,
279-
.os = check.os,
280-
.arch = check_target.result.cpu.arch,
281-
.optimize = mode,
282-
283-
.canary_revision = build_options.canary_revision,
284-
.sha = build_options.sha,
285-
.tracy_callstack_depth = build_options.tracy_callstack_depth,
286-
.version = build_options.version,
287-
.reported_nodejs_version = build_options.reported_nodejs_version,
288-
.generated_code_dir = build_options.generated_code_dir,
289-
};
290-
var obj = addBunObject(b, &options);
291-
obj.generated_bin = null;
292-
step.dependOn(&obj.step);
293-
}
294-
}
283+
});
295284
}
296285

297286
// zig build check-windows
298287
{
299-
var step = b.step("check-windows", "Check for semantic analysis errors on Windows x64");
300-
inline for (.{
288+
const step = b.step("check-windows", "Check for semantic analysis errors on Windows");
289+
addMultiCheck(b, step, build_options, &.{
301290
.{ .os = .windows, .arch = .x86_64 },
302-
}) |check| {
303-
inline for (.{ .Debug, .ReleaseFast }) |mode| {
304-
const check_target = b.resolveTargetQuery(.{
305-
.os_tag = OperatingSystem.stdOSTag(check.os),
306-
.cpu_arch = check.arch,
307-
.os_version_min = getOSVersionMin(check.os),
308-
.glibc_version = getOSGlibCVersion(check.os),
309-
});
310-
311-
var options = BunBuildOptions{
312-
.target = check_target,
313-
.os = check.os,
314-
.arch = check_target.result.cpu.arch,
315-
.optimize = mode,
316-
.canary_revision = build_options.canary_revision,
317-
.sha = build_options.sha,
318-
.tracy_callstack_depth = build_options.tracy_callstack_depth,
319-
.version = build_options.version,
320-
.reported_nodejs_version = build_options.reported_nodejs_version,
321-
.generated_code_dir = build_options.generated_code_dir,
322-
};
323-
var obj = addBunObject(b, &options);
324-
obj.generated_bin = null;
325-
step.dependOn(&obj.step);
326-
}
327-
}
291+
});
328292
}
293+
}
329294

330-
// Running `zig build` with no arguments is almost always a mistake.
331-
// TODO: revive this error. cannot right now since ZLS runs zig build without arguments
332-
{
333-
// const mistake_message = b.addSystemCommand(&.{
334-
// "echo",
335-
// \\
336-
// \\To build Bun from source, please use `bun run setup` instead of `zig build`"
337-
// \\For more info, see https://bun.sh/docs/project/contributing
338-
// \\
339-
// \\If you want to build the zig code in isolation, run:
340-
// \\ 'zig build obj -Dgenerated-code=./build/codegen [...opts]'
341-
// \\
342-
// \\If you want to test a compile without emitting an object:
343-
// \\ 'zig build check'
344-
// \\ 'zig build check-all' (run linux+mac+windows)
345-
// \\
346-
// });
347-
348-
// b.default_step.dependOn(&mistake_message.step);
295+
pub inline fn addMultiCheck(
296+
b: *Build,
297+
parent_step: *Step,
298+
root_build_options: BunBuildOptions,
299+
to_check: []const struct { os: OperatingSystem, arch: Arch },
300+
) void {
301+
inline for (to_check) |check| {
302+
inline for (.{ .Debug, .ReleaseFast }) |mode| {
303+
const check_target = b.resolveTargetQuery(.{
304+
.os_tag = OperatingSystem.stdOSTag(check.os),
305+
.cpu_arch = check.arch,
306+
.cpu_model = getCpuModel(check.os, check.arch) orelse .determined_by_cpu_arch,
307+
.os_version_min = getOSVersionMin(check.os),
308+
.glibc_version = getOSGlibCVersion(check.os),
309+
});
310+
311+
var options: BunBuildOptions = .{
312+
.target = check_target,
313+
.os = check.os,
314+
.arch = check_target.result.cpu.arch,
315+
.optimize = mode,
316+
317+
.canary_revision = root_build_options.canary_revision,
318+
.sha = root_build_options.sha,
319+
.tracy_callstack_depth = root_build_options.tracy_callstack_depth,
320+
.version = root_build_options.version,
321+
.reported_nodejs_version = root_build_options.reported_nodejs_version,
322+
.generated_code_dir = root_build_options.generated_code_dir,
323+
};
324+
325+
var obj = addBunObject(b, &options);
326+
obj.generated_bin = null;
327+
parent_step.dependOn(&obj.step);
328+
}
349329
}
350330
}
351331

@@ -392,6 +372,25 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile {
392372
return obj;
393373
}
394374

375+
const ObjectFormat = enum {
376+
bc,
377+
obj,
378+
};
379+
380+
pub fn addInstallObjectFile(
381+
b: *Build,
382+
compile: *Compile,
383+
name: []const u8,
384+
out_mode: ObjectFormat,
385+
) *Step {
386+
// bin always needed to be computed or else the compilation will do nothing. zig build system bug?
387+
const bin = compile.getEmittedBin();
388+
return &b.addInstallFile(switch (out_mode) {
389+
.obj => bin,
390+
.bc => compile.getEmittedLlvmBc(),
391+
}, b.fmt("{s}.o", .{name})).step;
392+
}
393+
395394
fn exists(path: []const u8) bool {
396395
const file = std.fs.openFileAbsolute(path, .{ .mode = .read_only }) catch return false;
397396
file.close();
@@ -452,7 +451,11 @@ fn addInternalPackages(b: *Build, obj: *Compile, opts: *BunBuildOptions) void {
452451

453452
fn validateGeneratedPath(path: []const u8) void {
454453
if (!exists(path)) {
455-
std.debug.panic("{s} does not exist in generated code directory!", .{std.fs.path.basename(path)});
454+
std.debug.panic(
455+
\\Generated file '{s}' is missing!
456+
\\
457+
\\Make sure to use CMake and Ninja, or pass a manual codegen folder with '-Dgenerated-code=...'
458+
, .{path});
456459
}
457460
}
458461

packages/bun-usockets/src/bsd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,7 @@ int bsd_connect_udp_socket(LIBUS_SOCKET_DESCRIPTOR fd, const char *host, int por
882882
}
883883

884884
freeaddrinfo(result);
885-
return LIBUS_SOCKET_ERROR;
885+
return (int)LIBUS_SOCKET_ERROR;
886886
}
887887

888888
int bsd_disconnect_udp_socket(LIBUS_SOCKET_DESCRIPTOR fd) {

0 commit comments

Comments
 (0)