authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-25 12:20:18-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-26 19:08:50-04:00
log4dd645fdddec2c56f8eaabce9f358f95b54f8eec
treed1ec373560e390e2f1d9a505478114713c857d79
parentee0bd6c69ee01caf076e5c9f7461fe09fa8cf9ba

Runner: add --libc-runtimes support


9 files changed, 285 insertions(+), 112 deletions(-)

lib/compiler/Maker.zig+25-9
......@@ -3631,7 +3631,7 @@ pub fn resolveLazyPath(
36313631 const c = &maker.scanned_config.configuration;
36323632 return switch (lazy_path) {
36333633 .source_path => |sp| try packagePath(maker, arena, sp.owner, sp.sub_path.slice(c)),
3634 .relative => |relative| relativePath(maker, arena, relative),
3634 .relative => |relative| try relativePath(maker, arena, relative, stepByIndex(maker, asking_step_index)),
36353635 .generated => |gen| {
36363636 const base = generatedPath(maker, gen.index);
36373637 var file_path = base;
......@@ -3768,7 +3768,12 @@ pub fn packagePath(
37683768 };
37693769}
37703770
3771pub fn relativePath(maker: *const Maker, arena: Allocator, relative: Configuration.LazyPath.Relative) Allocator.Error!Path {
3771pub fn relativePath(
3772 maker: *const Maker,
3773 arena: Allocator,
3774 relative: Configuration.LazyPath.Relative,
3775 asking_step: *Step,
3776) error{ OutOfMemory, MakeFailed }!Path {
37723777 const graph = maker.graph;
37733778 const c = &maker.scanned_config.configuration;
37743779 const sub_path = relative.sub_path.slice(c);
......@@ -3789,12 +3794,12 @@ pub fn relativePath(maker: *const Maker, arena: Allocator, relative: Configurati
37893794 .root_dir = graph.build_root_directory,
37903795 .sub_path = sub_path,
37913796 },
3792 .zig_exe => .{
3793 .root_dir = .cwd(),
3794 .sub_path = if (sub_path.len == 0)
3795 graph.zig_exe
3796 else
3797 try Io.Dir.path.join(arena, &.{ graph.zig_exe, sub_path }),
3797 .zig_exe => {
3798 assert(sub_path.len == 0);
3799 return .{
3800 .root_dir = .cwd(),
3801 .sub_path = graph.zig_exe,
3802 };
37983803 },
37993804 .zig_lib => .{
38003805 .root_dir = graph.zig_lib_directory,
......@@ -3804,6 +3809,10 @@ pub fn relativePath(maker: *const Maker, arena: Allocator, relative: Configurati
38043809 .install_lib => try maker.install_paths.lib.join(arena, sub_path),
38053810 .install_bin => try maker.install_paths.bin.join(arena, sub_path),
38063811 .install_include => try maker.install_paths.include.join(arena, sub_path),
3812 .libc_runtimes => if (maker.graph.libc_runtimes_dir) |libc_runtimes_dir| .{
3813 .root_dir = .cwd(),
3814 .sub_path = try Dir.path.join(arena, &.{ libc_runtimes_dir, sub_path }),
3815 } else asking_step.fail(maker, "unknown LazyPath: \"--libc-runtimes\" not specified", .{}),
38073816 };
38083817}
38093818
......@@ -4391,15 +4400,22 @@ fn confPathDepToCachePath(
43914400 else => |index| try Dir.path.join(arena, &.{ index.get(c).?.root_path.slice(c), sub_path }),
43924401 },
43934402 },
4403 .zig_exe => {
4404 assert(sub_path.len == 0);
4405 return .{
4406 .root_dir = .cwd(),
4407 .sub_path = graph.zig_exe,
4408 };
4409 },
43944410 .zig_lib => .{
43954411 .root_dir = graph.zig_lib_directory,
43964412 .sub_path = sub_path,
43974413 },
4398 .zig_exe => @panic("TODO"),
43994414 .install_prefix => @panic("TODO"),
44004415 .install_lib => @panic("TODO"),
44014416 .install_bin => @panic("TODO"),
44024417 .install_include => @panic("TODO"),
4418 .libc_runtimes => @panic("TODO"),
44034419 };
44044420}
44054421
lib/compiler/Maker/Step.zig+5-5
......@@ -857,7 +857,7 @@ pub fn setWatchInputsFromManifestFiles(
857857}
858858
859859/// For steps that have a single input that never changes when re-running `make`.
860pub fn singleUnchangingWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_path: LazyPath) Allocator.Error!void {
860pub fn singleUnchangingWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_path: LazyPath) FailError!void {
861861 if (!step.inputs.populated()) try step.addWatchInput(maker, arena, lazy_path);
862862}
863863
......@@ -866,7 +866,7 @@ pub fn clearWatchInputs(step: *Step, maker: *Maker) void {
866866}
867867
868868/// Places a *file* dependency on the path.
869pub fn addWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_file: LazyPath) Allocator.Error!void {
869pub fn addWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_file: LazyPath) FailError!void {
870870 const conf = &maker.scanned_config.configuration;
871871 switch (lazy_file) {
872872 .source_path => |source_path| {
......@@ -875,7 +875,7 @@ pub fn addWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_file: La
875875 try addWatchInputPath(step, maker, pkg_path);
876876 },
877877 .relative => |relative| {
878 const resolved_path = try maker.relativePath(arena, relative);
878 const resolved_path = try maker.relativePath(arena, relative, step);
879879 try addWatchInputPath(step, maker, resolved_path);
880880 },
881881 // Nothing to watch because this dependency edge is modeled instead via `dependants`.
......@@ -890,7 +890,7 @@ pub fn addWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_file: La
890890/// Paths derived from this directory should also be manually added via
891891/// `addDirectoryWatchInputFromPath` if and only if this function returns
892892/// `true`.
893pub fn addDirectoryWatchInput(step: *Step, maker: *Maker, lazy_directory: LazyPath) Allocator.Error!bool {
893pub fn addDirectoryWatchInput(step: *Step, maker: *Maker, lazy_directory: LazyPath) FailError!bool {
894894 const graph = maker.graph;
895895 const arena = graph.arena; // TODO don't leak into the process arena
896896 switch (lazy_directory) {
......@@ -901,7 +901,7 @@ pub fn addDirectoryWatchInput(step: *Step, maker: *Maker, lazy_directory: LazyPa
901901 try addDirectoryWatchInputFromPath(step, maker, pkg_path);
902902 },
903903 .relative => |relative| {
904 const resolved_path = try maker.relativePath(arena, relative);
904 const resolved_path = try maker.relativePath(arena, relative, step);
905905 try addDirectoryWatchInputFromPath(step, maker, resolved_path);
906906 },
907907 // Nothing to watch because this dependency edge is modeled instead via `dependants`.
lib/compiler/Maker/Step/Run.zig+23-9
......@@ -250,9 +250,18 @@ pub fn make(
250250 protocol_args.appendAssumeCapacity(0);
251251 },
252252 .path_file => {
253 const lazy_path = arg.path.value.?.get(conf);
254 switch (lazy_path) {
255 else => {},
256 .relative => |relative| switch (relative.flags.base) {
257 else => {},
258 .libc_runtimes => if (graph.libc_runtimes_dir == null) continue,
259 },
260 }
261
253262 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
254263 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
255 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
264 const file_path = try maker.resolveLazyPath(arena, lazy_path, run_index);
256265
257266 try owned_files.resize(gpa, owned_files.bit_length + 1, true);
258267 try inherit_files.ensureUnusedCapacity(gpa, 1);
......@@ -283,9 +292,18 @@ pub fn make(
283292 }
284293 },
285294 .path_directory => {
295 const lazy_path = arg.path.value.?.get(conf);
296 switch (lazy_path) {
297 else => {},
298 .relative => |relative| switch (relative.flags.base) {
299 else => {},
300 .libc_runtimes => if (graph.libc_runtimes_dir == null) continue,
301 },
302 }
303
286304 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
287305 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
288 const dir_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
306 const dir_path = try maker.resolveLazyPath(arena, lazy_path, run_index);
289307 try input_dirs.append(gpa, dir_path);
290308
291309 const has_sub_path = dir_path.sub_path.len > 0;
......@@ -2242,12 +2260,8 @@ fn runCommand(
22422260 else => builtin.target,
22432261 };
22442262
2245 const need_cross_libc = config.flags.link_libc and root_target.os.tag == .linux and
2246 switch (producer.flags2.linkage) {
2247 .static => false,
2248 .dynamic => true,
2249 .default => root_target.isGnuLibC(),
2250 };
2263 const need_cross_libc = root_target.os.tag == .linux and
2264 config.flags.link_libc and config.flags.link_mode == .dynamic;
22512265 switch (std.zig.system.getExternalExecutor(io, &root_target, .{
22522266 .host_cpu_arch = host.cpu.arch,
22532267 .host_os_tag = host.os.tag,
......@@ -2316,7 +2330,7 @@ fn runCommand(
23162330 interp_argv.appendAssumeCapacity(bin_name);
23172331 interp_argv.appendAssumeCapacity("--dir=.");
23182332 for (conf_run.preopens.slice) |preopen| {
2319 const path = try maker.resolveLazyPath(arena, preopen.path.get(conf), run_index);
2333 const path = try maker.resolveLazyPathIndex(arena, preopen.path, run_index);
23202334 path.root_dir.handle.createDirPath(io, path.subPathOrDot()) catch |e|
23212335 return step.fail(maker, "failed creating directory {f}: {t}", .{ path, e });
23222336 interp_argv.appendAssumeCapacity(try arena.print("--dir={f}::{s}", .{ path, preopen.name.slice(conf) }));
lib/std/Build.zig+4-1
......@@ -2693,12 +2693,15 @@ fn validateConfigureDependency(lazy_path: LazyPath) void {
26932693 .src_path, .cwd_relative, .dependency => {}, // OK
26942694 .generated => @panic("configure phase cannot depend on files generated during make phase"),
26952695 .relative => |relative| switch (relative.base) {
2696 .cwd, .build_root, .local_cache, .global_cache, .zig_exe, .zig_lib => {}, // OK
2696 .cwd, .build_root, .local_cache, .global_cache, .zig_lib => {}, // OK
2697 .zig_exe => if (relative.sub_path.len > 0) @panic("file base cannot have a sub path"),
26972698 .install_prefix,
26982699 .install_lib,
26992700 .install_bin,
27002701 .install_include,
27012702 => @panic("configure phase cannot depend on files installed during make phase"),
2703 .libc_runtimes,
2704 => @panic("configure phase cannot depend on directory known only during make phase"),
27022705 },
27032706 }
27042707}
lib/std/Build/Configuration.zig+1
......@@ -1575,6 +1575,7 @@ pub const LazyPath = union(@This().Tag) {
15751575 install_lib,
15761576 install_bin,
15771577 install_include,
1578 libc_runtimes,
15781579 };
15791580 };
15801581};
lib/std/Io.zig+12-8
......@@ -218,8 +218,8 @@ pub const VTable = struct {
218218 childKill: *const fn (?*anyopaque, *std.process.Child) void,
219219
220220 progressParentFile: *const fn (?*anyopaque) std.Progress.ParentFileError!File,
221 inheritParentDir: *const fn (?*anyopaque, handle: Dir.Handle) (Cancelable || UnexpectedError)!Dir,
222 inheritParentFile: *const fn (?*anyopaque, handle: File.Handle, flags: File.Flags) (Cancelable || UnexpectedError)!File,
221 inheritParentDir: *const fn (?*anyopaque, handle: Dir.Handle) InheritParentHandleError!Dir,
222 inheritParentFile: *const fn (?*anyopaque, handle: File.Handle, flags: File.Flags) InheritParentHandleError!File,
223223
224224 now: *const fn (?*anyopaque, Clock) Timestamp,
225225 clockResolution: *const fn (?*anyopaque, Clock) Clock.ResolutionError!Duration,
......@@ -827,6 +827,10 @@ pub const UnexpectedError = error{
827827 Unexpected,
828828};
829829
830pub const InheritParentHandleError = error{
831 UnsupportedOperation,
832} || Cancelable || UnexpectedError;
833
830834pub const Clock = enum {
831835 /// A settable system-wide clock that measures real (i.e. wall-clock)
832836 /// time. This clock is affected by discontinuous jumps in the system
......@@ -2802,8 +2806,8 @@ pub const failing: std.Io = .{
28022806 .childKill = unreachableChildKill,
28032807
28042808 .progressParentFile = failingProgressParentFile,
2805 .inheritParentDir = unreachableInheritParentDir,
2806 .inheritParentFile = unreachableInheritParentFile,
2809 .inheritParentDir = failingInheritParentDir,
2810 .inheritParentFile = failingInheritParentFile,
28072811
28082812 .random = noRandom,
28092813 .randomSecure = failingRandomSecure,
......@@ -3474,17 +3478,17 @@ pub fn failingProgressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileE
34743478 return error.UnsupportedOperation;
34753479}
34763480
3477pub fn unreachableInheritParentDir(userdata: ?*anyopaque, handle: Dir.Handle) (Cancelable || UnexpectedError)!Dir {
3481pub fn failingInheritParentDir(userdata: ?*anyopaque, handle: Dir.Handle) InheritParentHandleError!Dir {
34783482 _ = userdata;
34793483 _ = handle;
3480 unreachable;
3484 return error.UnsupportedOperation;
34813485}
34823486
3483pub fn unreachableInheritParentFile(userdata: ?*anyopaque, handle: File.Handle, flags: File.Flags) (Cancelable || UnexpectedError)!File {
3487pub fn failingInheritParentFile(userdata: ?*anyopaque, handle: File.Handle, flags: File.Flags) InheritParentHandleError!File {
34843488 _ = userdata;
34853489 _ = handle;
34863490 _ = flags;
3487 unreachable;
3491 return error.UnsupportedOperation;
34883492}
34893493
34903494pub fn noRandom(userdata: ?*anyopaque, buffer: []u8) void {
lib/std/Io/Threaded.zig+5-1
......@@ -1975,6 +1975,10 @@ const have_copy_file_range = switch (native_os) {
19751975 .linux, .freebsd => true,
19761976 else => false,
19771977};
1978const have_fcntl = switch (native_os) {
1979 .linux, .dragonfly, .freebsd, .netbsd, .openbsd, .illumos, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => true,
1980 else => false,
1981};
19781982const have_fcopyfile = is_darwin;
19791983const have_fchmodat2 = native_os == .linux and
19801984 (builtin.os.isAtLeast(.linux, .{ .major = 6, .minor = 6, .patch = 0 }) orelse true) and
......@@ -17195,7 +17199,7 @@ pub fn posixExecveat(
1719517199 break :err posix.errno(posix.system.execve(p, argv, env_block.slice.ptr));
1719617200 };
1719717201 if (comptime native_os == .linux and (!builtin.link_libc or
17198 (builtin.target.abi.isGnu() and builtin.target.os.version_range.linux.glibc.order(
17202 (builtin.target.isGnuLibC() and builtin.target.os.version_range.linux.glibc.order(
1719917203 .{ .major = 2, .minor = 34, .patch = 0 },
1720017204 ).compare(.gte))))
1720117205 {
test/src/Runner.zig+209-79
......@@ -22,10 +22,12 @@ const Args = struct {
2222 src_dir: ?std.Io.Dir,
2323 targets: std.ArrayList(u32),
2424 target_bytes: std.ArrayList(u8),
25 libc_runtimes_dir: ?std.Io.Dir,
26 enable_darling: bool,
2527 enable_qemu: bool,
26 enable_wine: bool,
28 enable_rosetta: bool,
2729 enable_wasmtime: bool,
28 enable_darling: bool,
30 enable_wine: bool,
2931 quiet: bool,
3032
3133 fn deinit(args: *Args, gpa: std.mem.Allocator) void {
......@@ -73,7 +75,28 @@ pub fn runServer(runner: *Runner) ProtocolError {
7375 .args => {
7476 const args_body = try arena.alloc(u8, hdr.bytes_len);
7577 runner.server.in.readSliceAll(args_body) catch unreachable;
76 var state: enum { positional, zig, lib, src, target } = .positional;
78 const State = enum {
79 positional,
80 @"--zig",
81 @"--lib",
82 @"--src",
83 @"--target",
84 @"--libc-runtimes",
85 };
86 var state: State = .positional;
87 const state_expected: std.enums.EnumArray(State, enum {
88 path,
89 dir,
90 file,
91 string,
92 }) = .init(.{
93 .positional = .path,
94 .@"--zig" = .file,
95 .@"--lib" = .dir,
96 .@"--src" = .dir,
97 .@"--target" = .string,
98 .@"--libc-runtimes" = .dir,
99 });
77100 var input_dir: std.zig.Server.Message.InputDir = .cwd;
78101 var args_body_offset: usize = 0;
79102 while (args_body.len - args_body_offset > 0) {
......@@ -87,42 +110,56 @@ pub fn runServer(runner: *Runner) ProtocolError {
87110 args_body_offset = end + 1;
88111 switch (state) {
89112 .positional => if (std.mem.eql(u8, string, "--target")) {
90 state = .target;
113 state = .@"--target";
91114 continue;
92115 } else if (std.mem.cutPrefix(u8, string, "--target=")) |target| {
93116 try runner.args.addTarget(runner.gpa, target);
117 } else if (std.mem.eql(u8, string, "--libc-runtimes")) {
118 state = .@"--libc-runtimes";
119 continue;
120 } else if (std.mem.eql(u8, string, "-fdarling")) {
121 runner.args.enable_darling = true;
94122 } else if (std.mem.eql(u8, string, "-fqemu")) {
95123 runner.args.enable_qemu = true;
96 } else if (std.mem.eql(u8, string, "-fwine")) {
97 runner.args.enable_wine = true;
124 } else if (std.mem.eql(u8, string, "-frosetta")) {
125 runner.args.enable_rosetta = true;
98126 } else if (std.mem.eql(u8, string, "-fwasmtime")) {
99127 runner.args.enable_wasmtime = true;
100 } else if (std.mem.eql(u8, string, "-fdarling")) {
101 runner.args.enable_darling = true;
128 } else if (std.mem.eql(u8, string, "-fwine")) {
129 runner.args.enable_wine = true;
102130 } else if (std.mem.eql(u8, string, "--quiet")) {
103131 runner.args.quiet = true;
104132 },
105 .zig => unreachable,
106 .lib => unreachable,
107 .src => unreachable,
108 .target => try runner.args.addTarget(runner.gpa, string),
133 .@"--target" => try runner.args.addTarget(runner.gpa, string),
134 else => return runner.fail(
135 "\"{t}\" expected {t}, got {t}",
136 .{ state, state_expected.get(state), arg },
137 ),
109138 }
110139 },
111140 .prefix => {
112141 const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?;
113142 const string = args_body[args_body_offset..end];
114143 args_body_offset = end + 1;
144 assert(state == .positional);
115145 state = if (std.mem.eql(u8, string, "--zig="))
116 .zig
146 .@"--zig"
117147 else if (std.mem.eql(u8, string, "--lib="))
118 .lib
148 .@"--lib"
119149 else if (std.mem.eql(u8, string, "--src="))
120 .src
150 .@"--src"
151 else if (std.mem.eql(u8, string, "--libc-runtimes="))
152 .@"--libc-runtimes"
121153 else
122 unreachable;
154 return runner.fail("unsupported arg prefix: {q}", .{string});
123155 continue;
124156 },
125 .suffix => unreachable,
157 .suffix => {
158 const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?;
159 const string = args_body[args_body_offset..end];
160 args_body_offset = end + 1;
161 return runner.fail("unsupported arg suffix: {q}", .{string});
162 },
126163 .input_dir => {
127164 input_dir = @fromBackingInt(@backingInt(input_dir) + 1);
128165 continue :arg .output_dir;
......@@ -137,16 +174,19 @@ pub fn runServer(runner: *Runner) ProtocolError {
137174 dir_handle.*,
138175 ) catch |err| switch (err) {
139176 error.Canceled => |e| return e,
140 error.Unexpected => |e| return runner.fail(
177 else => |e| return runner.fail(
141178 "unable to inherit parent dir: {t}",
142179 .{e},
143180 ),
144181 };
145182 switch (state) {
146183 .positional => {
147 assert(runner.args.@"test" == null);
184 if (runner.args.@"test" != null or
185 runner.args.manifest_file != null) return runner.fail(
186 "{t} specified multiple times",
187 .{state},
188 );
148189 runner.args.@"test" = .{ .input_dir = input_dir, .dir = dir };
149 assert(runner.args.manifest_file == null);
150190 const manifest_path = "manifest";
151191 runner.args.manifest_file = dir.openFile(
152192 runner.io,
......@@ -161,16 +201,31 @@ pub fn runServer(runner: *Runner) ProtocolError {
161201 };
162202 try runner.discoverInput(input_dir, manifest_path);
163203 },
164 .zig => unreachable,
165 .lib => {
166 assert(runner.args.lib_dir == null);
204 .@"--lib" => {
205 if (runner.args.lib_dir != null) return runner.fail(
206 "\"{t}\" specified multiple times",
207 .{state},
208 );
167209 runner.args.lib_dir = dir;
168210 },
169 .src => {
170 assert(runner.args.src_dir == null);
211 .@"--src" => {
212 if (runner.args.src_dir != null) return runner.fail(
213 "\"{t}\" specified multiple times",
214 .{state},
215 );
171216 runner.args.src_dir = dir;
172217 },
173 .target => unreachable,
218 .@"--libc-runtimes" => {
219 if (runner.args.libc_runtimes_dir != null) return runner.fail(
220 "\"{t}\" specified multiple times",
221 .{state},
222 );
223 runner.args.libc_runtimes_dir = dir;
224 },
225 else => return runner.fail(
226 "\"{t}\" expected {t}, got {t}",
227 .{ state, state_expected.get(state), arg },
228 ),
174229 }
175230 },
176231 .input_file, .input_file_content, .output_file => {
......@@ -184,23 +239,31 @@ pub fn runServer(runner: *Runner) ProtocolError {
184239 .{ .nonblocking = false },
185240 ) catch |err| switch (err) {
186241 error.Canceled => |e| return e,
187 error.Unexpected => |e| return runner.fail(
242 else => |e| return runner.fail(
188243 "unable to inherit parent file: {t}",
189244 .{e},
190245 ),
191246 };
192247 switch (state) {
193248 .positional => {
194 assert(runner.args.manifest_file == null);
249 if (runner.args.@"test" != null or
250 runner.args.manifest_file != null) return runner.fail(
251 "{t} specified multiple times",
252 .{state},
253 );
195254 runner.args.manifest_file = file;
196255 },
197 .zig => {
198 assert(runner.args.zig_exe == null);
256 .@"--zig" => {
257 if (runner.args.zig_exe != null) return runner.fail(
258 "\"{t}\" specified multiple times",
259 .{state},
260 );
199261 runner.args.zig_exe = file;
200262 },
201 .lib => unreachable,
202 .src => unreachable,
203 .target => unreachable,
263 else => return runner.fail(
264 "\"{t}\" expected {t}, got {t}",
265 .{ state, state_expected.get(state), arg },
266 ),
204267 }
205268 },
206269 }
......@@ -480,7 +543,6 @@ fn testOne(runner: *Runner, test_index: u32) TestError!void {
480543 .target_query = target_query,
481544 .manifest_sr = .init(&manifest_fr.interface, runner.gpa, &runner.eb_wip, src_path_string: {
482545 var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined;
483
484546 const src_path = src_path: {
485547 break :src_path path_buffer[0 .. manifest_file.realPath(
486548 runner.io,
......@@ -1022,6 +1084,7 @@ const Compiler = struct {
10221084 const gpa = runner.gpa;
10231085 const arena = runner.arena.allocator();
10241086 const io = runner.io;
1087 const target = &comp.target.resolved;
10251088 const config = &comp.config.?;
10261089 const out_dir = ".zig-cache" ++ std.Io.Dir.path.sep_str ++
10271090 "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*);
......@@ -1036,16 +1099,18 @@ const Compiler = struct {
10361099 .stdout, .exit, .lldb => {},
10371100 }
10381101
1039 const executor = executor: switch (std.zig.system.getExternalExecutor(
1040 io,
1041 &comp.target.resolved,
1042 .{
1043 .host_cpu_arch = runner.host.cpu.arch,
1044 .host_os_tag = runner.host.os.tag,
1045 .link_mode = config.flags.link_mode,
1046 .link_libc = config.flags.link_libc,
1047 },
1048 )) {
1102 var argv: std.ArrayList([]const u8) = .initBuffer(try arena.alloc([]const u8, 4));
1103 var environ_map: std.process.Environ.Map = .init(gpa);
1104 defer environ_map.deinit();
1105 const need_cross_libc =
1106 target.os.tag == .linux and config.flags.link_libc and config.flags.link_mode == .dynamic;
1107 const use_executor = use_executor: switch (std.zig.system.getExternalExecutor(io, target, .{
1108 .host_cpu_arch = runner.host.cpu.arch,
1109 .host_os_tag = runner.host.os.tag,
1110 .qemu_fixes_dl = need_cross_libc and runner.args.libc_runtimes_dir != null,
1111 .link_mode = config.flags.link_mode,
1112 .link_libc = config.flags.link_libc,
1113 })) {
10491114 .bad_dl, .bad_os_or_cpu => {
10501115 // This binary cannot be executed on this host.
10511116 if (!runner.args.quiet) std.log.warn("skipping execution because host {q} cannot " ++
......@@ -1054,24 +1119,57 @@ const Compiler = struct {
10541119 });
10551120 return;
10561121 },
1057 .native, .rosetta => null,
1058 .qemu => |executor| if (runner.args.enable_qemu)
1059 executor
1060 else
1061 continue :executor .bad_os_or_cpu,
1062 .wine => |executor| if (runner.args.enable_wine)
1063 executor
1064 else
1065 continue :executor .bad_os_or_cpu,
1066 .wasmtime => |executor| if (runner.args.enable_wasmtime)
1067 executor
1068 else
1069 continue :executor .bad_os_or_cpu,
1070 .darling => |executor| if (runner.args.enable_darling)
1071 executor
1122 .native => false,
1123 .darling => |executor| if (runner.args.enable_darling) {
1124 argv.appendAssumeCapacity(executor);
1125 break :use_executor true;
1126 } else continue :use_executor .bad_os_or_cpu,
1127 .qemu => |executor| if (runner.args.enable_qemu) {
1128 argv.appendAssumeCapacity(executor);
1129 if (need_cross_libc) {
1130 const libc_runtimes_dir = runner.args.libc_runtimes_dir orelse
1131 continue :use_executor .bad_os_or_cpu;
1132 var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined;
1133 const libc_runtimes_path = path_buffer[0 .. libc_runtimes_dir.realPath(
1134 runner.io,
1135 &path_buffer,
1136 ) catch |err| switch (err) {
1137 error.Canceled => |e| return e,
1138 else => |e| return runner.fail("unable to get libc runtimes path: {t}", .{e}),
1139 }];
1140 argv.appendSliceAssumeCapacity(&.{ "-L", try std.Io.Dir.path.join(arena, &.{
1141 libc_runtimes_path,
1142 try if (target.isGnuLibC()) std.zig.target.glibcRuntimeTriple(
1143 arena,
1144 target.cpu.arch,
1145 target.os.tag,
1146 target.abi,
1147 ) else if (target.isMuslLibC()) std.zig.target.muslRuntimeTriple(
1148 arena,
1149 target.cpu.arch,
1150 target.abi,
1151 ) else unreachable,
1152 }) });
1153 }
1154 break :use_executor true;
1155 } else continue :use_executor .bad_os_or_cpu,
1156 .rosetta => if (runner.args.enable_rosetta)
1157 true
10721158 else
1073 continue :executor .bad_os_or_cpu,
1159 continue :use_executor .bad_os_or_cpu,
1160 .wine => |executor| if (runner.args.enable_wine) {
1161 if (!environ_map.contains("WINEDEBUG")) try environ_map.put("WINEDEBUG", "-all");
1162 argv.appendAssumeCapacity(executor);
1163 break :use_executor true;
1164 } else continue :use_executor .bad_os_or_cpu,
1165 .wasmtime => |executor| if (runner.args.enable_wasmtime) {
1166 if (!environ_map.contains("WASMTIME_BACKTRACE_DETAILS"))
1167 try environ_map.put("WASMTIME_BACKTRACE_DETAILS", "1");
1168 argv.appendSliceAssumeCapacity(&.{ executor, "--dir=.", "-Sinherit-env" });
1169 break :use_executor true;
1170 } else continue :use_executor .bad_os_or_cpu,
10741171 };
1172 argv.appendAssumeCapacity(bin_path);
10751173
10761174 var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined;
10771175 const run_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "run {s}", .{
......@@ -1088,11 +1186,12 @@ const Compiler = struct {
10881186 .errors => unreachable,
10891187 .stdout, .exit => {
10901188 const result = std.process.run(gpa, io, .{
1091 .exe = if (executor) |_| .search else .{ .file = bin_file },
1092 .argv = if (executor) |e| &.{ e, bin_path } else &.{bin_path},
1189 .exe = if (use_executor) .search else .{ .file = bin_file },
1190 .argv = argv.items,
10931191 .cwd = .{ .dir = src_dir },
1192 .environ_map = &environ_map,
10941193 .progress_node = run_prog_node,
1095 }) catch |err| if (executor) |_| {
1194 }) catch |err| if (use_executor) {
10961195 // Chances are the foreign executor isn't available. Skip this evaluation.
10971196 if (!runner.args.quiet) std.log.warn(
10981197 "skipping execution of {q} via executor for foreign target {q}: {t}",
......@@ -1151,7 +1250,7 @@ const Compiler = struct {
11511250 .{bin_path},
11521251 ),
11531252 }
1154 if (executor == null and result.stderr.len > 0) {
1253 if (!use_executor and result.stderr.len > 0) {
11551254 std.log.err("generated executable {q} had unexpected stderr:\n{s}", .{
11561255 bin_path, result.stderr,
11571256 });
......@@ -1257,10 +1356,12 @@ const ClientArgs = struct {
12571356 src_dir: std.Io.Dir,
12581357 targets: std.ArrayList([]const u8),
12591358 keep_src: bool,
1359 libc_runtimes_dir: ?std.Io.Dir,
1360 enable_darling: bool,
12601361 enable_qemu: bool,
1362 enable_rosetta: bool,
12611363 enable_wine: bool,
12621364 enable_wasmtime: bool,
1263 enable_darling: bool,
12641365 quiet: bool,
12651366};
12661367pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelable)!u8 {
......@@ -1275,6 +1376,7 @@ pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelabl
12751376 var test_path_arg: ?[]const u8 = null;
12761377 var zig_path_arg: ?[]const u8 = null;
12771378 var lib_path_arg: ?[]const u8 = null;
1379 var libc_runtimes_path_arg: ?[]const u8 = null;
12781380 var args: ClientArgs = .{
12791381 .test_file = undefined,
12801382 .test_kind = undefined,
......@@ -1283,10 +1385,12 @@ pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelabl
12831385 .src_dir = undefined,
12841386 .targets = .empty,
12851387 .keep_src = false,
1388 .libc_runtimes_dir = null,
1389 .enable_darling = false,
12861390 .enable_qemu = false,
1391 .enable_rosetta = false,
12871392 .enable_wine = false,
12881393 .enable_wasmtime = false,
1289 .enable_darling = false,
12901394 .quiet = false,
12911395 };
12921396 defer args.targets.deinit(init.gpa);
......@@ -1312,10 +1416,12 @@ pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelabl
13121416 .src_dir = null,
13131417 .targets = .empty,
13141418 .target_bytes = .empty,
1419 .libc_runtimes_dir = null,
1420 .enable_darling = false,
13151421 .enable_qemu = false,
1316 .enable_wine = false,
1422 .enable_rosetta = false,
13171423 .enable_wasmtime = false,
1318 .enable_darling = false,
1424 .enable_wine = false,
13191425 .quiet = false,
13201426 },
13211427 .host = std.zig.system.resolveTargetQuery(init.io, .{}) catch |err| switch (err) {
......@@ -1361,18 +1467,24 @@ pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelabl
13611467 try args.targets.append(init.gpa, try arena.dupe(u8, target));
13621468 } else if (std.mem.eql(u8, arg, "--keep-src")) {
13631469 args.keep_src = true;
1470 } else if (std.mem.eql(u8, arg, "--libc-runtimes")) {
1471 libc_runtimes_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg});
1472 } else if (std.mem.cutPrefix(u8, arg, "--libc-runtimes=")) |libc_runtimes_path| {
1473 libc_runtimes_path_arg = libc_runtimes_path;
1474 } else if (std.mem.eql(u8, arg, "-fdarling")) {
1475 args.enable_darling = true;
13641476 } else if (std.mem.eql(u8, arg, "-fqemu")) {
13651477 args.enable_qemu = true;
1478 } else if (std.mem.eql(u8, arg, "-frosetta")) {
1479 args.enable_rosetta = true;
13661480 } else if (std.mem.eql(u8, arg, "-fwine")) {
13671481 args.enable_wine = true;
13681482 } else if (std.mem.eql(u8, arg, "-fwasmtime")) {
13691483 args.enable_wasmtime = true;
1370 } else if (std.mem.eql(u8, arg, "-fdarling")) {
1371 args.enable_darling = true;
13721484 } else if (std.mem.eql(u8, arg, "--quiet")) {
13731485 args.quiet = true;
13741486 } else {
1375 if (test_path_arg) |_| fatal("unknown arg {q}", .{arg});
1487 if (test_path_arg) |_| fatal("unexpected arg {q}", .{arg});
13761488 test_path_arg = arg;
13771489 }
13781490 }
......@@ -1403,6 +1515,15 @@ pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelabl
14031515 else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }),
14041516 };
14051517 defer args.lib_dir.close(init.io);
1518 if (libc_runtimes_path_arg) |libc_runtimes_path| args.libc_runtimes_dir = cwd.openDir(
1519 init.io,
1520 libc_runtimes_path,
1521 .{},
1522 ) catch |err| switch (err) {
1523 error.Canceled => |e| return e,
1524 else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }),
1525 };
1526 defer if (args.libc_runtimes_dir) |libc_runtimes_dir| libc_runtimes_dir.close(init.io);
14061527
14071528 const src_dir_path = "src_" ++ std.fmt.hex(rand_int: {
14081529 var rand_int: u64 = undefined;
......@@ -1479,21 +1600,18 @@ fn runClient(
14791600 try argv.append(gpa, @backingInt(Arg.prefix));
14801601 try argv.appendSlice(gpa, "--zig=");
14811602 try argv.append(gpa, 0);
1482
14831603 try argv.append(gpa, @backingInt(Arg.input_file));
14841604 try argv.appendSlice(gpa, @ptrCast(&args.zig_exe.handle));
14851605
14861606 try argv.append(gpa, @backingInt(Arg.prefix));
14871607 try argv.appendSlice(gpa, "--lib=");
14881608 try argv.append(gpa, 0);
1489
14901609 try argv.append(gpa, @backingInt(Arg.input_dir));
14911610 try argv.appendSlice(gpa, @ptrCast(&args.lib_dir.handle));
14921611
14931612 try argv.append(gpa, @backingInt(Arg.prefix));
14941613 try argv.appendSlice(gpa, "--src=");
14951614 try argv.append(gpa, 0);
1496
14971615 try argv.append(gpa, @backingInt(Arg.output_dir));
14981616 try argv.appendSlice(gpa, @ptrCast(&args.src_dir.handle));
14991617
......@@ -1507,11 +1625,28 @@ fn runClient(
15071625 try argv.append(gpa, 0);
15081626 }
15091627
1628 if (args.libc_runtimes_dir) |libc_runtimes_dir| {
1629 try argv.append(gpa, @backingInt(Arg.prefix));
1630 try argv.appendSlice(gpa, "--libc-runtimes=");
1631 try argv.append(gpa, 0);
1632 try argv.append(gpa, @backingInt(Arg.input_dir));
1633 try argv.appendSlice(gpa, @ptrCast(&libc_runtimes_dir.handle));
1634 }
1635 if (args.enable_darling) {
1636 try argv.append(gpa, @backingInt(Arg.string));
1637 try argv.appendSlice(gpa, "-fdarling");
1638 try argv.append(gpa, 0);
1639 }
15101640 if (args.enable_qemu) {
15111641 try argv.append(gpa, @backingInt(Arg.string));
15121642 try argv.appendSlice(gpa, "-fqemu");
15131643 try argv.append(gpa, 0);
15141644 }
1645 if (args.enable_rosetta) {
1646 try argv.append(gpa, @backingInt(Arg.string));
1647 try argv.appendSlice(gpa, "-frosetta");
1648 try argv.append(gpa, 0);
1649 }
15151650 if (args.enable_wine) {
15161651 try argv.append(gpa, @backingInt(Arg.string));
15171652 try argv.appendSlice(gpa, "-fwine");
......@@ -1522,11 +1657,6 @@ fn runClient(
15221657 try argv.appendSlice(gpa, "-fwasmtime");
15231658 try argv.append(gpa, 0);
15241659 }
1525 if (args.enable_darling) {
1526 try argv.append(gpa, @backingInt(Arg.string));
1527 try argv.appendSlice(gpa, "-fdarling");
1528 try argv.append(gpa, 0);
1529 }
15301660
15311661 if (args.quiet) {
15321662 try argv.append(gpa, @backingInt(Arg.string));
test/tests.zig+1
......@@ -3411,6 +3411,7 @@ pub fn addIncrementalTests(
34113411 run.addArgs(&.{ "--target", target_str });
34123412 }
34133413
3414 run.addPrefixedDirectoryArg("--libc-runtimes=", .{ .relative = .{ .base = .libc_runtimes } });
34143415 run.addThirdPartyEnabledArgDarling(.{ .enabled = "-fdarling" });
34153416 run.addThirdPartyEnabledArgQemu(.{ .enabled = "-fqemu" });
34163417 run.addThirdPartyEnabledArgRosetta(.{ .enabled = "-frosetta" });