Repository navigation
Expand file tree
/
Copy pathbuild.zig
More file actions
214 lines (192 loc) · 9.62 KB
/
Copy pathbuild.zig
File metadata and controls
214 lines (192 loc) · 9.62 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
const std = @import("std");
const Translator = @import("translate_c").Translator;
fn runCommand(b: *std.Build, argv: []const []const u8) []const u8 {
return std.mem.trimEnd(u8, b.run(argv), "\n\r ");
}
pub fn build(b: *std.Build) void {
// Use native target by default. The VM architecture must match the boot image.
// Override with -Dtarget= if cross-compiling.
const target = b.standardTargetOptions(.{});
const native_target = target.result.cpu.arch == b.graph.host.result.cpu.arch and
target.result.os.tag == b.graph.host.result.os.tag and
target.result.abi == b.graph.host.result.abi;
const optimize = b.standardOptimizeOption(.{});
const keep_symbols = b.option(bool, "keep_symbols", "Keep symbol names in release builds for debugging") orelse false;
// Get git and date info at build time
const git_label = runCommand(b, &.{ "git", "log", "-1", "--format=heads/master-%h" });
const compile_time = runCommand(b, &.{ "date", "+%b %e %Y %H:%M:%S" });
const options = b.addOptions();
options.addOption([]const u8, "git_label", if (git_label.len > 0) git_label else "zig-vm");
options.addOption([]const u8, "compile_time", if (compile_time.len > 0) compile_time else "");
const translate_c = b.dependency("translate_c", .{});
const locale_c = Translator.init(translate_c, .{
.c_source_file = b.path("src/locale.h"),
.target = target,
.optimize = optimize,
}).mod;
const windows_c = if (target.result.os.tag == .windows) Translator.init(translate_c, .{
.c_source_file = b.path("src/windows.h"),
.target = target,
.optimize = optimize,
}).mod else null;
// Main Factor VM executable
const exe = b.addExecutable(.{
.name = "factor",
.use_llvm = true,
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
// Strip debug symbols in release builds to reduce binary size (~6x smaller)
.strip = if (optimize != .debug and !keep_symbols) true else null,
}),
});
exe.root_module.addOptions("build_options", options);
exe.root_module.addImport("locale_c", locale_c);
if (windows_c) |module| exe.root_module.addImport("windows_c", module);
// Link libc for system calls
exe.root_module.link_libc = true;
if (target.result.os.tag == .windows) {
exe.root_module.addCSourceFile(.{ .file = b.path("src/windows_format.c") });
}
// Export dynamic symbols so Factor runtime can find primitives via dlsym
// This is equivalent to -rdynamic in GCC
exe.rdynamic = true;
if (target.result.abi.isMusl()) {
exe.linkage = .dynamic;
}
// Platform-specific library linking
if (target.result.os.tag == .macos) {
// Link macOS frameworks (like the C++ Factor VM does)
// These are needed so dlopen(NULL) can find system symbols
// Use SDK paths for framework resolution
const sdk_path = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk";
// Always add framework path for both native and cross-compilation
exe.root_module.addSystemFrameworkPath(.{ .cwd_relative = sdk_path ++ "/System/Library/Frameworks" });
exe.root_module.addLibraryPath(.{ .cwd_relative = sdk_path ++ "/usr/lib" });
// Link frameworks - Factor needs CoreFoundation for IO, Foundation for
// ObjC runtime, and AppKit/Cocoa for GUI classes (NSStatusBar, etc.)
// These must be linked so dlopen(NULL) can find ObjC class symbols.
exe.root_module.linkFramework("CoreFoundation", .{});
exe.root_module.linkFramework("Foundation", .{});
exe.root_module.linkFramework("Cocoa", .{});
exe.root_module.linkFramework("AppKit", .{});
exe.root_module.linkFramework("OpenGL", .{});
exe.root_module.linkFramework("CoreServices", .{});
exe.root_module.linkFramework("CoreGraphics", .{});
exe.root_module.linkFramework("CoreText", .{});
exe.root_module.linkFramework("ApplicationServices", .{});
exe.root_module.linkFramework("Carbon", .{});
} else if (target.result.os.tag == .linux) {
// Link math library for floor/ceil/etc
exe.root_module.linkSystemLibrary("m", .{});
}
b.installArtifact(exe);
// Compile cross-target VMs without installing over the host executable.
const check_step = b.step("check", "Compile the VM and tests without installing or running them");
check_step.dependOn(&exe.step);
// On macOS, also drop the binary into Factor.app/Contents/MacOS/factor and
// refresh the ./factor symlink, mirroring `make`'s macos.app target. The
// bundle (Info.plist, Resources, Frameworks) is already in the repo. Running
// ./factor — which resolves into the bundle — gives the VM a proper bundle
// context (GUI session, NSBundle mainBundle, resources) that a bare
// zig-out/bin/factor lacks; without it Cocoa/UI code hangs headless.
// We copy from the freshly-built artifact (not ./factor) so we never mv the
// symlink over the real binary.
if (native_target and target.result.os.tag == .macos) {
// Run with cwd = build root, so the bundle paths stay relative.
const mkdir_bundle = b.addSystemCommand(&.{ "mkdir", "-p", "Factor.app/Contents/MacOS", "Factor.app/Contents/Frameworks" });
mkdir_bundle.setCwd(b.path("."));
const copy_to_bundle = b.addSystemCommand(&.{"cp"});
copy_to_bundle.setCwd(b.path("."));
copy_to_bundle.addArtifactArg(exe);
copy_to_bundle.addArg("Factor.app/Contents/MacOS/factor");
copy_to_bundle.step.dependOn(&mkdir_bundle.step);
// Re-sign after copy into the .app. A plain `cp` of a linker-signed
// Mach-O into Factor.app is rejected by taskgated on modern macOS
// (EXC_CRASH SIGKILL / CODESIGNING "Taskgated Invalid Signature").
// Ad-hoc sign with factor.entitlements so MAP_JIT / W^X stays allowed.
const codesign = b.addSystemCommand(&.{
"codesign",
"-s",
"-",
"-f",
"--timestamp=none",
"--entitlements",
"factor.entitlements",
"Factor.app/Contents/MacOS/factor",
});
codesign.setCwd(b.path("."));
codesign.step.dependOn(©_to_bundle.step);
const link_factor = b.addSystemCommand(&.{ "ln", "-sf", "Factor.app/Contents/MacOS/factor", "factor" });
link_factor.setCwd(b.path("."));
link_factor.step.dependOn(&codesign.step);
b.getInstallStep().dependOn(&link_factor.step);
} else if (native_target) {
// On non-macOS platforms there's no app bundle, so just drop the
// freshly-built binary at the build root as ./factor (mirroring `make`'s
// default target). Use the build system's own file-copy step rather than
// shelling out to `cp`, which doesn't exist on a stock Windows shell.
const copy_factor = b.addUpdateSourceFiles();
const factor_name = if (target.result.os.tag == .windows) "factor.exe" else "factor";
copy_factor.addCopyFileToSource(exe.getEmittedBin(), factor_name);
b.getInstallStep().dependOn(©_factor.step);
}
// Run command
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
run_cmd.addPassthruArgs();
const run_step = b.step("run", "Run the Factor VM");
run_step.dependOn(&run_cmd.step);
// Unit tests
const tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
tests.root_module.link_libc = true;
if (target.result.os.tag == .windows) {
tests.root_module.addCSourceFile(.{ .file = b.path("src/windows_format.c") });
}
tests.root_module.addOptions("build_options", options);
tests.root_module.addImport("locale_c", locale_c);
if (windows_c) |module| tests.root_module.addImport("windows_c", module);
const run_tests = b.addRunArtifact(tests);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_tests.step);
// Keep the native notification tests independently runnable on Windows,
// without requiring the rest of the VM to bootstrap first.
const process_wait_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("src/windows_process_wait.zig"),
.target = target,
.optimize = optimize,
}),
});
const run_process_wait_tests = b.addRunArtifact(process_wait_tests);
const process_wait_test_step = b.step("test-process-wait", "Test Windows process notifications");
process_wait_test_step.dependOn(&run_process_wait_tests.step);
if (target.result.os.tag == .windows) test_step.dependOn(&run_process_wait_tests.step);
check_step.dependOn(&tests.step);
check_step.dependOn(&process_wait_tests.step);
const docs_step = b.step("docs", "Build docs");
const docs = b.addObject(.{
.name = "factor",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
docs.root_module.addOptions("build_options", options);
docs.root_module.addImport("locale_c", locale_c);
if (windows_c) |module| docs.root_module.addImport("windows_c", module);
const install_docs = b.addInstallDirectory(.{
.source_dir = docs.getEmittedDocs(),
.install_dir = .prefix,
.install_subdir = "docs",
});
docs_step.dependOn(&install_docs.step);
}