authorgravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-06-03 02:24:08-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-06-24 02:41:05+02:00
logb40b1178ef29ea9b013f73cc3cd4f9b976d0e120
tree66b4f4416ff944f95e72dedae4c52f4ebb356612
parentc5d6277ace183cf4261c13cc9b44a53ec1622199

Adds support for making run step argument paths absolute

* Adds a new API that takes an options struct instead of having a function for each combination of options * Adds support for prefix and suffix to every argument type * Adds support for making paths absolute when passed as arguments * Deprecates the old API * Fixes mismatch between documentation and behavior of `process.currentPath` on WASI

7 files changed, 384 insertions(+), 132 deletions(-)

lib/compiler/Maker/Step/Run.zig+31-13
......@@ -87,8 +87,9 @@ pub fn make(
8787 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
8888 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
8989 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
90 prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
90 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
9191 }));
92 man.hash.add(arg.flags.make_absolute);
9293 man.hash.addBytesZ(prefix);
9394 man.hash.addBytesZ(suffix);
9495 _ = try man.addFilePath(file_path, null);
......@@ -98,9 +99,10 @@ pub fn make(
9899 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
99100 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
100101 const resolved_arg = try mem.concat(arena, u8, &.{
101 prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
102 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
102103 });
103104 argv_list.appendAssumeCapacity(resolved_arg);
105 man.hash.add(arg.flags.make_absolute);
104106 man.hash.addBytes(resolved_arg);
105107 },
106108 .file_content => {
......@@ -142,9 +144,12 @@ pub fn make(
142144 const file_path = producer_make_comp.installed_path orelse maker.generatedPath(producer.generated_bin.value.?).*;
143145
144146 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
145 prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
147 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
146148 }));
147149
150 man.hash.add(arg.flags.make_absolute);
151 man.hash.addBytesZ(prefix);
152 man.hash.addBytesZ(suffix);
148153 _ = try man.addFilePath(file_path, null);
149154 },
150155 .output_file, .output_directory => {
......@@ -152,6 +157,7 @@ pub fn make(
152157 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
153158 const basename = arg.basename.value.?.slice(conf);
154159
160 man.hash.add(arg.flags.make_absolute);
155161 man.hash.addBytesZ(prefix);
156162 man.hash.addBytesZ(basename);
157163 man.hash.addBytesZ(suffix);
......@@ -181,7 +187,7 @@ pub fn make(
181187
182188 man.hash.add(conf_run.flags.test_runner_mode);
183189 if (conf_run.flags.test_runner_mode) {
184 const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root });
190 const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false);
185191
186192 try argv_list.ensureUnusedCapacity(gpa, 3);
187193 argv_list.appendAssumeCapacity(try allocPrint(arena, "--cache-dir={s}", .{cache_dir_string}));
......@@ -1552,7 +1558,7 @@ pub fn rerunInFuzzMode(
15521558 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
15531559 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
15541560 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
1555 prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
1561 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
15561562 }));
15571563 },
15581564 .path_directory => {
......@@ -1560,7 +1566,7 @@ pub fn rerunInFuzzMode(
15601566 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
15611567 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
15621568 const resolved_arg = try mem.concat(arena, u8, &.{
1563 prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
1569 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
15641570 });
15651571 argv_list.appendAssumeCapacity(resolved_arg);
15661572 },
......@@ -1602,7 +1608,7 @@ pub fn rerunInFuzzMode(
16021608 producer_make_comp.installed_path orelse
16031609 maker.generatedPath(producer.generated_bin.value.?).*;
16041610 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
1605 prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
1611 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
16061612 }));
16071613 },
16081614 .output_file => unreachable,
......@@ -1612,7 +1618,7 @@ pub fn rerunInFuzzMode(
16121618 }
16131619
16141620 if (conf_run.flags.test_runner_mode) {
1615 const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root });
1621 const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false);
16161622
16171623 try argv_list.ensureUnusedCapacity(gpa, 3);
16181624 argv_list.appendAssumeCapacity(try allocPrint(arena, "--cache-dir={s}", .{cache_dir_string}));
......@@ -1688,7 +1694,7 @@ fn populateGeneratedPathsCreateDirs(
16881694
16891695 maker.generatedPath(arg.generated.value.?).* = generated_path;
16901696
1691 const arg_output_path = try convertPathArg(arena, run_index, maker, generated_path);
1697 const arg_output_path = try convertPathArg(arena, run_index, maker, generated_path, arg.flags.make_absolute);
16921698 argv[placeholder.index] = try mem.concat(arena, u8, &.{ prefix, arg_output_path, suffix });
16931699 }
16941700}
......@@ -2290,11 +2296,18 @@ fn checksContainStderr(conf_run: *const Configuration.Step.Run) bool {
22902296 return conf_run.expect_stderr_exact.value != null or conf_run.expect_stderr_match.slice.len != 0;
22912297}
22922298
2293/// If `path` is cwd-relative, make it relative to the cwd of the child instead.
2299/// If `path` is absolute, return it unchanged. If `make_absolute` is true, make it absolute.
2300/// Otherwise, make it relative to the cwd of the child.
22942301///
2295/// Whenever a path is included in the argv of a child, it should be put through this function first
2296/// to make sure the child doesn't see paths relative to a cwd other than its own.
2297fn convertPathArg(arena: Allocator, run_index: Configuration.Step.Index, maker: *Maker, path: Path) ![]const u8 {
2302/// Whenever a path is included in the argv of a child, it should be put through this function
2303/// first.
2304fn convertPathArg(
2305 arena: Allocator,
2306 run_index: Configuration.Step.Index,
2307 maker: *Maker,
2308 path: Path,
2309 make_absolute: bool,
2310) ![]const u8 {
22982311 const conf = &maker.scanned_config.configuration;
22992312 const conf_step = run_index.ptr(conf);
23002313 const conf_run = conf_step.extended.get(conf.extra).run;
......@@ -2305,6 +2318,11 @@ fn convertPathArg(arena: Allocator, run_index: Configuration.Step.Index, maker:
23052318 // Absolute paths don't need changing.
23062319 return path_str;
23072320 }
2321
2322 if (make_absolute) {
2323 return Dir.path.join(arena, &.{ graph.cache.cwd, path_str });
2324 }
2325
23082326 const child_cwd_rel: []const u8 = rel: {
23092327 const child_lazy_cwd = conf_run.cwd.value orelse break :rel path_str;
23102328 const child_cwd = try maker.resolveLazyPathIndexAbs(arena, child_lazy_cwd, run_index);
lib/compiler/configurer.zig+22-14
......@@ -312,15 +312,16 @@ const Serialize = struct {
312312 .flags = .{
313313 .tag = .artifact,
314314 .prefix = a.prefix.len != 0,
315 .suffix = false,
315 .suffix = a.suffix.len != 0,
316316 .basename = false,
317317 .path = false,
318318 .producer = true,
319319 .generated = false,
320320 .dep_file = false,
321 .make_absolute = a.make_absolute,
321322 },
322323 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
323 .suffix = .{ .value = null },
324 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
324325 .basename = .{ .value = null },
325326 .path = .{ .value = null },
326327 .producer = .{ .value = stepIndex(s, &a.artifact.step) },
......@@ -330,15 +331,16 @@ const Serialize = struct {
330331 .flags = .{
331332 .tag = .path_file,
332333 .prefix = a.prefix.len != 0,
333 .suffix = false,
334 .suffix = a.suffix.len != 0,
334335 .basename = false,
335336 .path = true,
336337 .producer = false,
337338 .generated = false,
338339 .dep_file = false,
340 .make_absolute = a.make_absolute,
339341 },
340342 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
341 .suffix = .{ .value = null },
343 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
342344 .basename = .{ .value = null },
343345 .path = .{ .value = try addLazyPath(s, a.lazy_path) },
344346 .producer = .{ .value = null },
......@@ -354,6 +356,7 @@ const Serialize = struct {
354356 .producer = false,
355357 .generated = false,
356358 .dep_file = false,
359 .make_absolute = a.make_absolute,
357360 },
358361 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
359362 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
......@@ -366,15 +369,16 @@ const Serialize = struct {
366369 .flags = .{
367370 .tag = .file_content,
368371 .prefix = a.prefix.len != 0,
369 .suffix = false,
372 .suffix = a.suffix.len != 0,
370373 .basename = false,
371374 .path = true,
372375 .producer = false,
373376 .generated = false,
374377 .dep_file = false,
378 .make_absolute = false,
375379 },
376380 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
377 .suffix = .{ .value = null },
381 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
378382 .basename = .{ .value = null },
379383 .path = .{ .value = try addLazyPath(s, a.lazy_path) },
380384 .producer = .{ .value = null },
......@@ -390,6 +394,7 @@ const Serialize = struct {
390394 .producer = false,
391395 .generated = false,
392396 .dep_file = false,
397 .make_absolute = false,
393398 },
394399 .prefix = .{ .value = try wc.addString(a) },
395400 .suffix = .{ .value = null },
......@@ -402,15 +407,16 @@ const Serialize = struct {
402407 .flags = .{
403408 .tag = .output_file,
404409 .prefix = a.prefix.len != 0,
405 .suffix = false,
410 .suffix = a.suffix.len != 0,
406411 .basename = a.basename.len != 0,
407412 .path = false,
408413 .producer = false,
409414 .generated = true,
410415 .dep_file = tag == .output_file_dep,
416 .make_absolute = a.make_absolute,
411417 },
412418 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
413 .suffix = .{ .value = null },
419 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
414420 .basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null },
415421 .path = .{ .value = null },
416422 .producer = .{ .value = null },
......@@ -420,15 +426,16 @@ const Serialize = struct {
420426 .flags = .{
421427 .tag = .output_directory,
422428 .prefix = a.prefix.len != 0,
423 .suffix = false,
429 .suffix = a.suffix.len != 0,
424430 .basename = a.basename.len != 0,
425431 .path = false,
426432 .producer = false,
427433 .generated = true,
428434 .dep_file = false,
435 .make_absolute = a.make_absolute,
429436 },
430437 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
431 .suffix = .{ .value = null },
438 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
432439 .basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null },
433440 .path = .{ .value = null },
434441 .producer = .{ .value = null },
......@@ -444,6 +451,7 @@ const Serialize = struct {
444451 .producer = false,
445452 .generated = false,
446453 .dep_file = false,
454 .make_absolute = false,
447455 },
448456 .prefix = .{ .value = null },
449457 .suffix = .{ .value = null },
......@@ -1066,12 +1074,12 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
10661074 .args = .{ .slice = try s.initArgsList(run.argv.items) },
10671075 .cwd = .{ .value = try s.addOptionalLazyPath(run.cwd) },
10681076 .captured_stdout = .{ .value = if (run.captured_stdout) |cs| .{
1069 .basename = try wc.addString(cs.output.basename),
1070 .generated_file = cs.output.generated_file,
1077 .basename = try wc.addString(cs.basename),
1078 .generated_file = cs.generated_file,
10711079 } else null },
10721080 .captured_stderr = .{ .value = if (run.captured_stderr) |cs| .{
1073 .basename = try wc.addString(cs.output.basename),
1074 .generated_file = cs.output.generated_file,
1081 .basename = try wc.addString(cs.basename),
1082 .generated_file = cs.generated_file,
10751083 } else null },
10761084 .environ_map = .{ .value = try s.addEnvironMap(run.environ_map) },
10771085 .expect_term_value = .{ .value = if (expect_term) |t| t.value else null },
lib/std/Build/Configuration.zig+2-1
......@@ -610,7 +610,8 @@ pub const Step = extern struct {
610610 producer: bool,
611611 generated: bool,
612612 dep_file: bool,
613 _: u21 = 0,
613 make_absolute: bool,
614 _: u20 = 0,
614615 };
615616
616617 pub const Tag = enum(u4) {
lib/std/Build/Step/Run.zig+134-104
......@@ -133,10 +133,10 @@ pub const StdIo = union(enum) {
133133};
134134
135135pub const Arg = union(enum) {
136 artifact: PrefixedArtifact,
137 lazy_path: PrefixedLazyPath,
136 artifact: DecoratedArtifact,
137 lazy_path: DecoratedLazyPath,
138138 decorated_directory: DecoratedLazyPath,
139 file_content: PrefixedLazyPath,
139 file_content: DecoratedFileContent,
140140 bytes: []const u8,
141141 output_file: *Output,
142142 output_file_dep: *Output,
......@@ -145,17 +145,21 @@ pub const Arg = union(enum) {
145145 passthru,
146146};
147147
148pub const PrefixedArtifact = struct {
148pub const DecoratedArtifact = struct {
149149 prefix: []const u8,
150 suffix: []const u8,
150151 artifact: *Step.Compile,
152 make_absolute: bool,
151153};
152154
153pub const PrefixedLazyPath = struct {
155pub const DecoratedLazyPath = struct {
154156 prefix: []const u8,
155157 lazy_path: std.Build.LazyPath,
158 suffix: []const u8,
159 make_absolute: bool,
156160};
157161
158pub const DecoratedLazyPath = struct {
162pub const DecoratedFileContent = struct {
159163 prefix: []const u8,
160164 lazy_path: std.Build.LazyPath,
161165 suffix: []const u8,
......@@ -165,10 +169,14 @@ pub const Output = struct {
165169 generated_file: Configuration.GeneratedFileIndex,
166170 prefix: []const u8,
167171 basename: []const u8,
172 suffix: []const u8,
173 make_absolute: bool,
168174};
169175
170176pub const CapturedStdIo = struct {
171 output: Output,
177 generated_file: Configuration.GeneratedFileIndex,
178 prefix: []const u8,
179 basename: []const u8,
172180 trim_whitespace: TrimWhitespace,
173181
174182 pub const Options = struct {
......@@ -219,17 +227,38 @@ pub fn enableTestRunnerMode(run: *Run) void {
219227 run.test_runner_mode = true;
220228}
221229
230pub const ArgOptions = struct {
231 prefix: []const u8 = "",
232 suffix: []const u8 = "",
233};
234
235pub const PathArgOptions = struct {
236 prefix: []const u8 = "",
237 suffix: []const u8 = "",
238 /// Makes the path absolute before passing it to the child process. Not supported by all hosts,
239 /// prefer accepting relative paths when possible.
240 make_absolute: bool = false,
241};
242
243/// Deprecated, use `addArtifactArg2`.
222244pub fn addArtifactArg(run: *Run, artifact: *Step.Compile) void {
223 run.addPrefixedArtifactArg("", artifact);
245 run.addArtifactArg2(artifact, .{});
224246}
225247
248/// Deprecated, use `addArtifactArg2`.
226249pub fn addPrefixedArtifactArg(run: *Run, prefix: []const u8, artifact: *Step.Compile) void {
250 run.addArtifactArg2(artifact, .{ .prefix = prefix });
251}
252
253pub fn addArtifactArg2(run: *Run, artifact: *Step.Compile, options: PathArgOptions) void {
227254 const graph = run.step.owner.graph;
228255 const arena = graph.arena;
229256
230 const prefixed_artifact: PrefixedArtifact = .{
231 .prefix = graph.dupeString(prefix),
257 const prefixed_artifact: DecoratedArtifact = .{
258 .prefix = graph.dupeString(options.prefix),
232259 .artifact = artifact,
260 .suffix = graph.dupeString(options.suffix),
261 .make_absolute = options.make_absolute,
233262 };
234263 run.argv.append(arena, .{ .artifact = prefixed_artifact }) catch @panic("OOM");
235264
......@@ -237,19 +266,18 @@ pub fn addPrefixedArtifactArg(run: *Run, prefix: []const u8, artifact: *Step.Com
237266 bin_file.addStepDependencies(&run.step);
238267}
239268
240/// Provides a file path as a command line argument to the command being run.
241///
242/// Returns a `std.Build.LazyPath` which can be used as inputs to other APIs
243/// throughout the build system.
244///
245/// `sub_path` is the name of the generated output file which may have zero or
246/// more path components.
247///
248/// Related:
249/// * `addPrefixedOutputFileArg` - same thing but prepends a string to the argument
250/// * `addFileArg` - for input files given to the child process
269/// Deprecated, use `addOutputFileArg2`.
251270pub fn addOutputFileArg(run: *Run, sub_path: []const u8) std.Build.LazyPath {
252 return run.addPrefixedOutputFileArg("", sub_path);
271 return run.addOutputFileArg2(sub_path, .{});
272}
273
274/// Deprecated, use `addOutputFileArg2`.
275pub fn addPrefixedOutputFileArg(
276 run: *Run,
277 prefix: []const u8,
278 sub_path: []const u8,
279) std.Build.LazyPath {
280 return run.addOutputFileArg2(sub_path, .{ .prefix = prefix });
253281}
254282
255283/// Provides a file path as a command line argument to the command being run.
......@@ -264,16 +292,15 @@ pub fn addOutputFileArg(run: *Run, sub_path: []const u8) std.Build.LazyPath {
264292/// throughout the build system.
265293///
266294/// Related:
267/// * `addOutputFileArg` - same thing but without the prefix
268295/// * `addFileArg` - for input files given to the child process
269pub fn addPrefixedOutputFileArg(
296pub fn addOutputFileArg2(
270297 run: *Run,
271 prefix: []const u8,
272298 /// The name of the generated output file which may have zero or more path
273299 /// components.
274300 ///
275301 /// Asserted to be non-empty.
276302 sub_path: []const u8,
303 options: PathArgOptions,
277304) std.Build.LazyPath {
278305 const b = run.step.owner;
279306 const graph = b.graph;
......@@ -282,9 +309,11 @@ pub fn addPrefixedOutputFileArg(
282309
283310 const output = graph.create(Output);
284311 output.* = .{
285 .prefix = graph.dupeString(prefix),
312 .prefix = graph.dupeString(options.prefix),
286313 .basename = graph.dupeString(sub_path),
314 .suffix = graph.dupeString(options.suffix),
287315 .generated_file = graph.addGeneratedFile(&run.step),
316 .make_absolute = options.make_absolute,
288317 };
289318 run.argv.append(arena, .{ .output_file = output }) catch @panic("OOM");
290319
......@@ -295,17 +324,14 @@ pub fn addPrefixedOutputFileArg(
295324 return .{ .generated = .{ .index = output.generated_file } };
296325}
297326
298/// Appends an input file to the command line arguments.
299///
300/// The child process will see a file path. Modifications to this file will be
301/// detected as a cache miss in subsequent builds, causing the child process to
302/// be re-executed.
303///
304/// Related:
305/// * `addPrefixedFileArg` - same thing but prepends a string to the argument
306/// * `addOutputFileArg` - for files generated by the child process
327/// See `addFileArg2`.
307328pub fn addFileArg(run: *Run, lp: std.Build.LazyPath) void {
308 run.addPrefixedFileArg("", lp);
329 run.addFileArg2(lp, .{});
330}
331
332/// See `addFileArg2`.
333pub fn addPrefixedFileArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
334 run.addFileArg2(lp, .{ .prefix = prefix });
309335}
310336
311337/// Appends an input file to the command line arguments prepended with a string.
......@@ -318,36 +344,29 @@ pub fn addFileArg(run: *Run, lp: std.Build.LazyPath) void {
318344/// subsequent builds, causing the child process to be re-executed.
319345///
320346/// Related:
321/// * `addFileArg` - same thing but without the prefix
322347/// * `addOutputFileArg` - for files generated by the child process
323pub fn addPrefixedFileArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
348pub fn addFileArg2(run: *Run, lp: std.Build.LazyPath, options: PathArgOptions) void {
324349 const graph = run.step.owner.graph;
325350 const arena = graph.arena;
326351
327 const prefixed_file_source: PrefixedLazyPath = .{
328 .prefix = graph.dupeString(prefix),
352 const prefixed_file_source: DecoratedLazyPath = .{
353 .prefix = graph.dupeString(options.prefix),
329354 .lazy_path = lp.dupe(graph),
355 .suffix = graph.dupeString(options.suffix),
356 .make_absolute = options.make_absolute,
330357 };
331358 run.argv.append(arena, .{ .lazy_path = prefixed_file_source }) catch @panic("OOM");
332359 lp.addStepDependencies(&run.step);
333360}
334361
335/// Appends the content of an input file to the command line arguments.
336///
337/// The child process will see a single argument, even if the file contains whitespace.
338/// This means that the entire file content up to EOF is rendered as one contiguous
339/// string, including escape sequences. Notably, any (trailing) newlines will show up
340/// like this: "hello,\nfile world!\n"
341///
342/// Modifications to the source file will be detected as a cache miss in subsequent
343/// builds, causing the child process to be re-executed.
344///
345/// This function may not be used to supply the first argument of a `Run` step.
346///
347/// Related:
348/// * `addPrefixedFileContentArg` - same thing but prepends a string to the argument
362/// Deprecated, use `addFileContentArg2`.
349363pub fn addFileContentArg(run: *Run, lp: std.Build.LazyPath) void {
350 run.addPrefixedFileContentArg("", lp);
364 return run.addFileContentArg2(lp, .{});
365}
366
367/// Deprecated, use `addFileContentArg2`.
368pub fn addPrefixedFileContentArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
369 return run.addFileContentArg2(lp, .{ .prefix = prefix });
351370}
352371
353372/// Appends the content of an input file to the command line arguments prepended with a string.
......@@ -368,7 +387,7 @@ pub fn addFileContentArg(run: *Run, lp: std.Build.LazyPath) void {
368387///
369388/// Related:
370389/// * `addFileContentArg` - same thing but without the prefix
371pub fn addPrefixedFileContentArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
390pub fn addFileContentArg2(run: *Run, lp: std.Build.LazyPath, options: ArgOptions) void {
372391 const graph = run.step.owner.graph;
373392 const arena = graph.arena;
374393
......@@ -379,24 +398,27 @@ pub fn addPrefixedFileContentArg(run: *Run, prefix: []const u8, lp: std.Build.La
379398 @panic("'addFileContentArg'/'addPrefixedFileContentArg' cannot be first argument");
380399 }
381400
382 const prefixed_file_source: PrefixedLazyPath = .{
383 .prefix = graph.dupeString(prefix),
401 const file_content: DecoratedFileContent = .{
402 .prefix = graph.dupeString(options.prefix),
384403 .lazy_path = lp.dupe(graph),
404 .suffix = graph.dupeString(options.suffix),
385405 };
386 run.argv.append(arena, .{ .file_content = prefixed_file_source }) catch @panic("OOM");
406 run.argv.append(arena, .{ .file_content = file_content }) catch @panic("OOM");
387407 lp.addStepDependencies(&run.step);
388408}
389409
390/// Provides a directory path as a command line argument to the command being run.
391///
392/// Returns a `std.Build.LazyPath` which can be used as inputs to other APIs
393/// throughout the build system.
394///
395/// Related:
396/// * `addPrefixedOutputDirectoryArg` - same thing but prepends a string to the argument
397/// * `addDirectoryArg` - for input directories given to the child process
410/// Deprecated, use `addOutputDirectoryArg2`.
398411pub fn addOutputDirectoryArg(run: *Run, basename: []const u8) std.Build.LazyPath {
399 return run.addPrefixedOutputDirectoryArg("", basename);
412 return run.addOutputDirectoryArg2(basename, .{});
413}
414
415/// Deprecated, use `addOutputDirectoryArg2`.
416pub fn addPrefixedOutputDirectoryArg(
417 run: *Run,
418 prefix: []const u8,
419 basename: []const u8,
420) std.Build.LazyPath {
421 return run.addOutputDirectoryArg2(basename, .{ .prefix = prefix });
400422}
401423
402424/// Provides a directory path as a command line argument to the command being run.
......@@ -412,12 +434,11 @@ pub fn addOutputDirectoryArg(run: *Run, basename: []const u8) std.Build.LazyPath
412434/// throughout the build system.
413435///
414436/// Related:
415/// * `addOutputDirectoryArg` - same thing but without the prefix
416437/// * `addDirectoryArg` - for input directories given to the child process
417pub fn addPrefixedOutputDirectoryArg(
438pub fn addOutputDirectoryArg2(
418439 run: *Run,
419 prefix: []const u8,
420440 basename: []const u8,
441 options: PathArgOptions,
421442) std.Build.LazyPath {
422443 if (basename.len == 0) @panic("basename must not be empty");
423444 const graph = run.step.owner.graph;
......@@ -425,9 +446,11 @@ pub fn addPrefixedOutputDirectoryArg(
425446
426447 const output = arena.create(Output) catch @panic("OOM");
427448 output.* = .{
428 .prefix = graph.dupeString(prefix),
449 .prefix = graph.dupeString(options.prefix),
429450 .basename = graph.dupeString(basename),
451 .suffix = graph.dupeString(options.suffix),
430452 .generated_file = graph.addGeneratedFile(&run.step),
453 .make_absolute = options.make_absolute,
431454 };
432455 run.argv.append(arena, .{ .output_directory = output }) catch @panic("OOM");
433456
......@@ -438,56 +461,67 @@ pub fn addPrefixedOutputDirectoryArg(
438461 return .{ .generated = .{ .index = output.generated_file } };
439462}
440463
464/// Deprecated, use `addDirectoryArg2`.
441465pub fn addDirectoryArg(run: *Run, lazy_directory: std.Build.LazyPath) void {
442 run.addDecoratedDirectoryArg("", lazy_directory, "");
466 run.addDirectoryArg2(lazy_directory, .{});
443467}
444468
469/// Deprecated, use `addDirectoryArg2`.
445470pub fn addPrefixedDirectoryArg(run: *Run, prefix: []const u8, lazy_directory: std.Build.LazyPath) void {
446 const graph = run.step.owner.graph;
447 const arena = graph.arena;
448 run.argv.append(arena, .{ .decorated_directory = .{
449 .prefix = graph.dupeString(prefix),
450 .lazy_path = lazy_directory.dupe(graph),
451 .suffix = "",
452 } }) catch @panic("OOM");
453 lazy_directory.addStepDependencies(&run.step);
471 run.addDirectoryArg2(lazy_directory, .{ .prefix = prefix });
454472}
455473
474/// Deprecated, use `addDirectoryArg2`.
456475pub fn addDecoratedDirectoryArg(
457476 run: *Run,
458477 prefix: []const u8,
459478 lazy_directory: std.Build.LazyPath,
460479 suffix: []const u8,
480) void {
481 run.addDirectoryArg2(lazy_directory, .{ .prefix = prefix, .suffix = suffix });
482}
483
484pub fn addDirectoryArg2(
485 run: *Run,
486 lazy_directory: std.Build.LazyPath,
487 options: PathArgOptions,
461488) void {
462489 const graph = run.step.owner.graph;
463490 const arena = graph.arena;
464491 run.argv.append(arena, .{ .decorated_directory = .{
465 .prefix = graph.dupeString(prefix),
492 .prefix = graph.dupeString(options.prefix),
466493 .lazy_path = lazy_directory.dupe(graph),
467 .suffix = graph.dupeString(suffix),
494 .suffix = graph.dupeString(options.suffix),
495 .make_absolute = options.make_absolute,
468496 } }) catch @panic("OOM");
469497 lazy_directory.addStepDependencies(&run.step);
470498}
471499
472/// Add a path argument to a dep file (.d) for the child process to write its
473/// discovered additional dependencies.
474/// Only one dep file argument is allowed by instance.
500/// Deprecated, use `addDepFileOutputArg2`.
475501pub fn addDepFileOutputArg(run: *Run, basename: []const u8) std.Build.LazyPath {
476 return run.addPrefixedDepFileOutputArg("", basename);
502 return run.addDepFileOutputArg2(basename, .{});
477503}
478504
479/// Add a prefixed path argument to a dep file (.d) for the child process to
480/// write its discovered additional dependencies.
505/// Deprecated, use `addDepFileOutputArg2`.
481506pub fn addPrefixedDepFileOutputArg(run: *Run, prefix: []const u8, basename: []const u8) std.Build.LazyPath {
507 return run.addDepFileOutputArg2(basename, .{ .prefix = prefix });
508}
509
510/// Add a path argument to a dep file (.d) for the child process to write its
511/// discovered additional dependencies.
512/// Only one dep file argument is allowed by instance.
513pub fn addDepFileOutputArg2(run: *Run, basename: []const u8, options: PathArgOptions) std.Build.LazyPath {
482514 const b = run.step.owner;
483515 const graph = b.graph;
484516 const arena = graph.arena;
485517
486518 const dep_file = arena.create(Output) catch @panic("OOM");
487519 dep_file.* = .{
488 .prefix = graph.dupeString(prefix),
520 .prefix = graph.dupeString(options.prefix),
489521 .basename = graph.dupeString(basename),
522 .suffix = graph.dupeString(options.suffix),
490523 .generated_file = graph.addGeneratedFile(&run.step),
524 .make_absolute = options.make_absolute,
491525 };
492526
493527 run.argv.append(arena, .{ .output_file_dep = dep_file }) catch @panic("OOM");
......@@ -642,19 +676,17 @@ pub fn captureStdErr(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPa
642676 const graph = b.graph;
643677 const arena = graph.arena;
644678
645 if (run.captured_stderr) |captured| return .{ .generated = .{ .index = captured.output.generated_file } };
679 if (run.captured_stderr) |captured| return .{ .generated = .{ .index = captured.generated_file } };
646680
647681 const captured = arena.create(CapturedStdIo) catch @panic("OOM");
648682 captured.* = .{
649 .output = .{
650 .prefix = "",
651 .basename = if (options.basename) |basename| graph.dupeString(basename) else "stderr",
652 .generated_file = graph.addGeneratedFile(&run.step),
653 },
683 .prefix = "",
684 .basename = if (options.basename) |basename| graph.dupeString(basename) else "stderr",
685 .generated_file = graph.addGeneratedFile(&run.step),
654686 .trim_whitespace = options.trim_whitespace,
655687 };
656688 run.captured_stderr = captured;
657 return .{ .generated = .{ .index = captured.output.generated_file } };
689 return .{ .generated = .{ .index = captured.generated_file } };
658690}
659691
660692pub fn captureStdOut(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPath {
......@@ -665,19 +697,17 @@ pub fn captureStdOut(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPa
665697 const graph = b.graph;
666698 const arena = graph.arena;
667699
668 if (run.captured_stdout) |captured| return .{ .generated = .{ .index = captured.output.generated_file } };
700 if (run.captured_stdout) |captured| return .{ .generated = .{ .index = captured.generated_file } };
669701
670702 const captured = arena.create(CapturedStdIo) catch @panic("OOM");
671703 captured.* = .{
672 .output = .{
673 .prefix = "",
674 .basename = if (options.basename) |basename| graph.dupeString(basename) else "stdout",
675 .generated_file = graph.addGeneratedFile(&run.step),
676 },
704 .prefix = "",
705 .basename = if (options.basename) |basename| graph.dupeString(basename) else "stdout",
706 .generated_file = graph.addGeneratedFile(&run.step),
677707 .trim_whitespace = options.trim_whitespace,
678708 };
679709 run.captured_stdout = captured;
680 return .{ .generated = .{ .index = captured.output.generated_file } };
710 return .{ .generated = .{ .index = captured.generated_file } };
681711}
682712
683713/// Adds an additional input files that, when modified, indicates that this Run
test/standalone/build.zig.zon+3
......@@ -196,6 +196,9 @@
196196 .elf2 = .{
197197 .path = "elf2",
198198 },
199 .run_args = .{
200 .path = "run_args",
201 },
199202 },
200203 .paths = .{
201204 "build.zig",
test/standalone/run_args/build.zig created+160
......@@ -0,0 +1,160 @@
1const std = @import("std");
2
3/// Tests that args are passed to run steps correctly.
4///
5/// Note that when `make_absolute` is true we make sure the resulting path argument is absolute, but
6/// when it is false we allow either absolute or relative paths. This is because the maker receives
7/// absolute paths when build is run from anywhere other than the build root.
8pub fn build(b: *std.Build) !void {
9 const step = b.step("test", "Run artifact args standalone test cases");
10 b.default_step = step;
11
12 const exe = b.addExecutable(.{
13 .name = "exe",
14 .root_module = b.createModule(.{
15 .root_source_file = b.path("main.zig"),
16 .target = b.graph.host,
17 }),
18 });
19
20 // Arg
21 {
22 const run = b.addRunArtifact(exe);
23 step.dependOn(&run.step);
24 run.addArg("arg1");
25 run.expectStdErrEqual("arg1\n");
26 }
27
28 // Args
29 {
30 const run = b.addRunArtifact(exe);
31 step.dependOn(&run.step);
32 run.addArgs(&.{ "arg1", "arg2" });
33 run.expectStdErrEqual("arg1\narg2\n");
34 }
35
36 // Artifact Args
37 {
38 // Absolute
39 {
40 const run = b.addRunArtifact(exe);
41 step.dependOn(&run.step);
42 _ = run.addArtifactArg2(exe, .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
43 run.expectStdErrMatch("abs exe");
44 }
45 // Relative
46 {
47 const run = b.addRunArtifact(exe);
48 step.dependOn(&run.step);
49 _ = run.addArtifactArg2(exe, .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
50 run.expectStdErrMatch("exe\n");
51 }
52 }
53
54 // File Args
55 {
56 const write_files = b.addWriteFiles();
57 const file = write_files.add("file", "");
58
59 // Absolute
60 {
61 const run = b.addRunArtifact(exe);
62 step.dependOn(&run.step);
63 _ = run.addFileArg2(file, .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
64 run.expectStdErrEqual("abs file\n");
65 }
66 // Relative
67 {
68 const run = b.addRunArtifact(exe);
69 step.dependOn(&run.step);
70 _ = run.addFileArg2(file, .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
71 run.expectStdErrMatch("file\n");
72 }
73 }
74
75 // File Content
76 {
77 const write_files = b.addWriteFiles();
78 const file = write_files.add("file", "foo bar baz");
79
80 const run = b.addRunArtifact(exe);
81 step.dependOn(&run.step);
82 _ = run.addFileContentArg2(file, .{ .prefix = "content-prefix ", .suffix = " content-suffix" });
83 run.expectStdErrEqual("content-prefix foo bar baz content-suffix\n");
84 }
85
86 // Output File Args
87 {
88 // Absolute
89 {
90 const run = b.addRunArtifact(exe);
91 step.dependOn(&run.step);
92 _ = run.addOutputFileArg2("output-file", .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
93 run.expectStdErrEqual("abs output-file\n");
94 }
95 // Relative
96 {
97 const run = b.addRunArtifact(exe);
98 step.dependOn(&run.step);
99 _ = run.addOutputFileArg2("output-file", .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
100 run.expectStdErrMatch("output-file\n");
101 }
102 }
103
104 // Output Directory Args
105 {
106 // Absolute
107 {
108 const run = b.addRunArtifact(exe);
109 step.dependOn(&run.step);
110 _ = run.addOutputDirectoryArg2("output-dir", .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
111 run.expectStdErrEqual("abs output-dir\n");
112 }
113 // Relative
114 {
115 const run = b.addRunArtifact(exe);
116 step.dependOn(&run.step);
117 _ = run.addOutputDirectoryArg2("output-dir", .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
118 run.expectStdErrMatch("output-dir\n");
119 }
120 }
121
122 // Directory Args
123 {
124 const write_files = b.addWriteFiles();
125 const directory = try write_files.getDirectory().join(b.graph.arena, "dir");
126
127 // Absolute
128 {
129 const run = b.addRunArtifact(exe);
130 step.dependOn(&run.step);
131 _ = run.addDirectoryArg2(directory, .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
132 run.expectStdErrEqual("abs dir\n");
133 }
134 // Relative
135 {
136 const run = b.addRunArtifact(exe);
137 step.dependOn(&run.step);
138 _ = run.addDirectoryArg2(directory, .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
139 run.expectStdErrMatch("dir\n");
140 }
141 }
142
143 // Dep File Args
144 {
145 // Absolute
146 {
147 const run = b.addRunArtifact(exe);
148 step.dependOn(&run.step);
149 _ = run.addDepFileOutputArg2("deps.d", .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
150 run.expectStdErrEqual("abs deps.d\n");
151 }
152 // Relative
153 {
154 const run = b.addRunArtifact(exe);
155 step.dependOn(&run.step);
156 _ = run.addDepFileOutputArg2("deps.d", .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
157 run.expectStdErrMatch("deps.d\n");
158 }
159 }
160}
test/standalone/run_args/main.zig created+32
......@@ -0,0 +1,32 @@
1const std = @import("std");
2
3pub fn main(init: std.process.Init) !void {
4 const io = init.io;
5 const arena = init.arena.allocator();
6
7 var iter = try init.minimal.args.iterateAllocator(arena);
8 std.debug.assert(iter.skip());
9 while (iter.next()) |arg| {
10 const path_prefix = "path^";
11 const path_suffix = "$";
12 if (std.mem.startsWith(u8, arg, path_prefix) and std.mem.endsWith(u8, arg, path_suffix)) {
13 // If we're a path, log whether we're absolute or relative, and log the basename
14 const path = arg[path_prefix.len..][0 .. arg.len - path_prefix.len - path_suffix.len];
15 if (std.fs.path.isAbsolute(path)) {
16 std.debug.print("abs ", .{});
17 } else {
18 std.debug.print("rel ", .{});
19 }
20 std.debug.print("{s}\n", .{std.fs.path.basename(path)});
21
22 // Create an empty dep file if necessary
23 if (std.mem.endsWith(u8, path, ".d")) {
24 const file = try std.Io.Dir.cwd().createFile(io, path, .{});
25 defer file.close(io);
26 }
27 } else {
28 // If it's not a path, log the arg as is
29 std.debug.print("{s}\n", .{arg});
30 }
31 }
32}