1const Run = @This();
2const builtin = @import("builtin");
3
4const std = @import("std");
5const Io = std.Io;
6const Build = std.Build;
7const Step = std.Build.Step;
8const Dir = std.Io.Dir;
9const mem = std.mem;
10const process = std.process;
11const EnvMap = std.process.Environ.Map;
12const assert = std.debug.assert;
13const Path = std.Build.Cache.Path;
14const Configuration = std.Build.Configuration;
15
16pub const base_tag: Step.Tag = .run;
17
18step: Step,
19
20/// See also addArg and addArgs to modifying this directly
21argv: std.ArrayList(Arg),
22
23/// Use `setCwd` to set the initial current working directory
24cwd: ?Build.LazyPath,
25
26/// Override this field to modify the environment, or use setEnvironmentVariable
27environ_map: ?*EnvMap,
28
29/// Named files that will be provided to the parent process.
30/// See `std.process.Preopens`.
31preopens: std.array_hash_map.Auto(Configuration.String, Build.LazyPath),
32
33/// Controls the `NO_COLOR` and `CLICOLOR_FORCE` environment variables.
34color: Color = .auto,
35
36/// When `true` prevents `ZIG_PROGRESS` environment variable from being passed
37/// to the child process, which otherwise would be used for the child to send
38/// progress updates to the parent.
39disable_zig_progress: bool,
40
41/// Configures whether the Run step is considered to have side-effects, and also
42/// whether the Run step will inherit stdio streams, forwarding them to the
43/// parent process, in which case will require a global lock to prevent other
44/// steps from interfering with stdio while the subprocess associated with this
45/// Run step is running.
46/// If the Run step is determined to not have side-effects, then execution will
47/// be skipped if all output files are up-to-date and input files are
48/// unchanged.
49stdio: StdIo,
50
51/// This field must be `.none` if stdio is `inherit`.
52/// It should be only set using `setStdIn`.
53stdin: StdIn,
54
55/// Additional input files that, when modified, indicate that the Run step
56/// should be re-executed.
57/// If the Run step is determined to have side-effects, the Run step is always
58/// executed when it appears in the build graph, regardless of whether these
59/// files have been modified.
60file_inputs: std.ArrayList(std.Build.LazyPath),
61
62/// After adding an output argument, this step will by default rename itself
63/// for a better display name in the build summary.
64/// This can be disabled by setting this to false.
65rename_step_with_output_arg: bool,
66
67/// If this is true, a Run step which is configured to check the output of the
68/// executed binary will not fail the build if the binary cannot be executed
69/// due to being for a foreign binary to the host system which is running the
70/// build graph.
71///
72/// Command-line arguments such as -fqemu and -fwasmtime may affect whether a
73/// binary is detected as foreign, as well as system configuration such as
74/// Rosetta (macOS) and binfmt_misc (Linux).
75///
76/// If this Run step is considered to have side-effects, then this flag does
77/// nothing.
78skip_foreign_checks: bool,
79
80/// If this is true, failing to execute a foreign binary will be considered an
81/// error. However if this is false, the step will be skipped on failure instead.
82///
83/// This allows for a Run step to attempt to execute a foreign binary using an
84/// external executor (such as qemu) but not fail if the executor is unavailable.
85failing_to_execute_foreign_is_an_error: bool,
86
87/// If stderr or stdout exceeds this amount, the child process is killed and
88/// the step fails.
89stdio_limit: std.Io.Limit,
90
91captured_stdout: ?*CapturedStdIo,
92captured_stderr: ?*CapturedStdIo,
93
94has_side_effects: bool,
95test_runner_mode: bool = false,
96
97/// If this Run step was produced by a Compile step, it is tracked here.
98producer: ?*Step.Compile,
99
100pub const Color = std.Build.Configuration.Step.Run.Color;
101
102pub const StdIn = union(enum) {
103 none,
104 bytes: []const u8,
105 lazy_path: std.Build.LazyPath,
106};
107
108pub const StdIo = union(enum) {
109 /// Whether the Run step has side-effects will be determined by whether or not one
110 /// of the args is an output file (added with `addOutputFileArg`).
111 /// If the Run step is determined to have side-effects, this is the same as `inherit`.
112 /// The step will fail if the subprocess crashes or returns a non-zero exit code.
113 infer_from_args,
114 /// Causes the Run step to be considered to have side-effects, and therefore
115 /// always execute when it appears in the build graph.
116 /// It also means that this step will obtain a global lock to prevent other
117 /// steps from running in the meantime.
118 /// The step will fail if the subprocess crashes or returns a non-zero exit code.
119 inherit,
120 /// Causes the Run step to be considered to *not* have side-effects. The
121 /// process will be re-executed if any of the input dependencies are
122 /// modified. The exit code and standard I/O streams will be checked for
123 /// certain conditions, and the step will succeed or fail based on these
124 /// conditions.
125 /// Note that an explicit check for exit code 0 needs to be added to this
126 /// list if such a check is desirable.
127 check: std.ArrayList(Check),
128 /// This Run step is running a zig unit test binary and will communicate
129 /// extra metadata over the IPC protocol.
130 zig_test,
131
132 pub const Check = union(enum) {
133 expect_stderr_exact: []const u8,
134 expect_stderr_match: []const u8,
135 expect_stdout_exact: []const u8,
136 expect_stdout_match: []const u8,
137 expect_term: process.Child.Term,
138 expect_stderr_snapshot: std.Build.LazyPath,
139 expect_stdout_snapshot: std.Build.LazyPath,
140 };
141};
142
143pub const Arg = union(enum) {
144 artifact: DecoratedArtifact,
145 lazy_path: DecoratedLazyPath,
146 decorated_directory: DecoratedLazyPath,
147 file_content: DecoratedFileContent,
148 bytes: []const u8,
149 output_file: *Output,
150 output_file_dep: *Output,
151 output_directory: *Output,
152 /// The arguments passed after "--" on the "zig build" CLI.
153 passthru,
154
155 enable_darling: ToggleFlags,
156 enable_qemu: ToggleFlags,
157 enable_rosetta: ToggleFlags,
158 enable_wasmtime: ToggleFlags,
159 enable_wine: ToggleFlags,
160};
161
162pub const ToggleFlags = struct {
163 /// The string to pass when enabled, or null to omit the arg.
164 enabled: ?[]const u8 = null,
165 /// The string to pass when disabled, or null to omit the arg.
166 disabled: ?[]const u8 = null,
167};
168
169pub const DecoratedArtifact = struct {
170 prefix: []const u8,
171 suffix: []const u8,
172 artifact: *Step.Compile,
173 make_absolute: bool,
174};
175
176pub const DecoratedLazyPath = struct {
177 prefix: []const u8,
178 lazy_path: std.Build.LazyPath,
179 suffix: []const u8,
180 make_absolute: bool,
181};
182
183pub const DecoratedFileContent = struct {
184 prefix: []const u8,
185 lazy_path: std.Build.LazyPath,
186 suffix: []const u8,
187};
188
189pub const Output = struct {
190 generated_file: Configuration.GeneratedFileIndex,
191 prefix: []const u8,
192 basename: []const u8,
193 suffix: []const u8,
194 make_absolute: bool,
195};
196
197pub const CapturedStdIo = struct {
198 generated_file: Configuration.GeneratedFileIndex,
199 prefix: []const u8,
200 basename: []const u8,
201 trim_whitespace: TrimWhitespace,
202
203 pub const Options = struct {
204 /// `null` means `stdout`/`stderr`.
205 basename: ?[]const u8 = null,
206 /// Does not affect `expectStdOutEqual`/`expectStdErrEqual`.
207 trim_whitespace: TrimWhitespace = .none,
208 };
209
210 pub const TrimWhitespace = std.Build.Configuration.Step.Run.TrimWhitespace;
211};
212
213pub fn create(owner: *std.Build, name: []const u8) *Run {
214 const run = owner.allocator.create(Run) catch @panic("OOM");
215 run.* = .{
216 .step = .init(.{
217 .tag = base_tag,
218 .name = name,
219 .owner = owner,
220 }),
221 .argv = .empty,
222 .cwd = null,
223 .environ_map = null,
224 .preopens = .empty,
225 .disable_zig_progress = false,
226 .stdio = .infer_from_args,
227 .stdin = .none,
228 .file_inputs = .empty,
229 .rename_step_with_output_arg = true,
230 .skip_foreign_checks = false,
231 .failing_to_execute_foreign_is_an_error = true,
232 .stdio_limit = .unlimited,
233 .captured_stdout = null,
234 .captured_stderr = null,
235 .has_side_effects = false,
236 .producer = null,
237 };
238 return run;
239}
240
241pub fn setName(run: *Run, name: []const u8) void {
242 run.step.name = name;
243 run.rename_step_with_output_arg = false;
244}
245
246pub fn enableTestRunnerMode(run: *Run) void {
247 if (run.test_runner_mode) return;
248 run.stdio = .zig_test;
249 run.test_runner_mode = true;
250}
251
252pub const ArgOptions = struct {
253 prefix: []const u8 = "",
254 suffix: []const u8 = "",
255};
256
257pub const PathArgOptions = struct {
258 prefix: []const u8 = "",
259 suffix: []const u8 = "",
260 /// Makes the path absolute before passing it to the child process. Not supported by all hosts,
261 /// prefer accepting relative paths when possible.
262 make_absolute: bool = false,
263};
264
265/// Deprecated, use `addArtifactArg2`.
266pub fn addArtifactArg(run: *Run, artifact: *Step.Compile) void {
267 run.addArtifactArg2(artifact, .{});
268}
269
270/// Deprecated, use `addArtifactArg2`.
271pub fn addPrefixedArtifactArg(run: *Run, prefix: []const u8, artifact: *Step.Compile) void {
272 run.addArtifactArg2(artifact, .{ .prefix = prefix });
273}
274
275pub fn addArtifactArg2(run: *Run, artifact: *Step.Compile, options: PathArgOptions) void {
276 const graph = run.step.owner.graph;
277 const arena = graph.arena;
278
279 const prefixed_artifact: DecoratedArtifact = .{
280 .prefix = graph.dupeString(options.prefix),
281 .artifact = artifact,
282 .suffix = graph.dupeString(options.suffix),
283 .make_absolute = options.make_absolute,
284 };
285 run.argv.append(arena, .{ .artifact = prefixed_artifact }) catch @panic("OOM");
286
287 const bin_file = artifact.getEmittedBin();
288 bin_file.addStepDependencies(&run.step);
289}
290
291/// Deprecated, use `addOutputFileArg2`.
292pub fn addOutputFileArg(run: *Run, sub_path: []const u8) std.Build.LazyPath {
293 return run.addOutputFileArg2(sub_path, .{});
294}
295
296/// Deprecated, use `addOutputFileArg2`.
297pub fn addPrefixedOutputFileArg(
298 run: *Run,
299 prefix: []const u8,
300 sub_path: []const u8,
301) std.Build.LazyPath {
302 return run.addOutputFileArg2(sub_path, .{ .prefix = prefix });
303}
304
305/// Provides a file path as a command line argument to the command being run.
306///
307/// For example, a prefix of "-o" and `sub_path` of "output.txt" will result in
308/// the child process seeing something like this: "-ozig-cache/.../output.txt"
309///
310/// The child process will see a single argument, regardless of whether the
311/// prefix or `sub_path` have spaces.
312///
313/// The returned `std.Build.LazyPath` can be used as inputs to other APIs
314/// throughout the build system.
315///
316/// Related:
317/// * `addFileArg` - for input files given to the child process
318pub fn addOutputFileArg2(
319 run: *Run,
320 /// The name of the generated output file which may have zero or more path
321 /// components.
322 ///
323 /// Asserted to be non-empty.
324 sub_path: []const u8,
325 options: PathArgOptions,
326) std.Build.LazyPath {
327 const b = run.step.owner;
328 const graph = b.graph;
329 const arena = graph.arena;
330 assert(sub_path.len != 0);
331
332 const output = graph.create(Output);
333 output.* = .{
334 .prefix = graph.dupeString(options.prefix),
335 .basename = graph.dupeString(sub_path),
336 .suffix = graph.dupeString(options.suffix),
337 .generated_file = graph.addGeneratedFile(&run.step),
338 .make_absolute = options.make_absolute,
339 };
340 run.argv.append(arena, .{ .output_file = output }) catch @panic("OOM");
341
342 if (run.rename_step_with_output_arg) {
343 run.setName(b.fmt("{s} ({s})", .{ run.step.name, sub_path }));
344 }
345
346 return .{ .generated = .{ .index = output.generated_file } };
347}
348
349/// See `addFileArg2`.
350pub fn addFileArg(run: *Run, lp: std.Build.LazyPath) void {
351 run.addFileArg2(lp, .{});
352}
353
354/// See `addFileArg2`.
355pub fn addPrefixedFileArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
356 run.addFileArg2(lp, .{ .prefix = prefix });
357}
358
359/// Appends an input file to the command line arguments prepended with a string.
360///
361/// For example, a prefix of "-F" will result in the child process seeing something
362/// like this: "-Fexample.txt"
363///
364/// The child process will see a single argument, even if the prefix has
365/// spaces. Modifications to this file will be detected as a cache miss in
366/// subsequent builds, causing the child process to be re-executed.
367///
368/// Related:
369/// * `addOutputFileArg` - for files generated by the child process
370pub fn addFileArg2(run: *Run, lp: std.Build.LazyPath, options: PathArgOptions) void {
371 const graph = run.step.owner.graph;
372 const arena = graph.arena;
373
374 const prefixed_file_source: DecoratedLazyPath = .{
375 .prefix = graph.dupeString(options.prefix),
376 .lazy_path = lp.dupe(graph),
377 .suffix = graph.dupeString(options.suffix),
378 .make_absolute = options.make_absolute,
379 };
380 run.argv.append(arena, .{ .lazy_path = prefixed_file_source }) catch @panic("OOM");
381 lp.addStepDependencies(&run.step);
382}
383
384/// Deprecated, use `addFileContentArg2`.
385pub fn addFileContentArg(run: *Run, lp: std.Build.LazyPath) void {
386 return run.addFileContentArg2(lp, .{});
387}
388
389/// Deprecated, use `addFileContentArg2`.
390pub fn addPrefixedFileContentArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
391 return run.addFileContentArg2(lp, .{ .prefix = prefix });
392}
393
394/// Appends the content of an input file to the command line arguments prepended with a string.
395///
396/// For example, a prefix of "-F" will result in the child process seeing something
397/// like this: "-Fmy file content"
398///
399/// The child process will see a single argument, even if the prefix and/or the file
400/// contain whitespace.
401/// This means that the entire file content up to EOF is rendered as one contiguous
402/// string, including escape sequences. Notably, any (trailing) newlines will show up
403/// like this: "hello,\nfile world!\n"
404///
405/// Modifications to the source file will be detected as a cache miss in subsequent
406/// builds, causing the child process to be re-executed.
407///
408/// This function may not be used to supply the first argument of a `Run` step.
409///
410/// Related:
411/// * `addFileContentArg` - same thing but without the prefix
412pub fn addFileContentArg2(run: *Run, lp: std.Build.LazyPath, options: ArgOptions) void {
413 const graph = run.step.owner.graph;
414 const arena = graph.arena;
415
416 // Some parts of this step's configure phase API rely on the first argument being somewhat
417 // transparent/readable, but the content of the file specified by `lp` remains completely
418 // opaque until its path can be resolved during the make phase.
419 if (run.argv.items.len == 0) {
420 @panic("'addFileContentArg'/'addPrefixedFileContentArg' cannot be first argument");
421 }
422
423 const file_content: DecoratedFileContent = .{
424 .prefix = graph.dupeString(options.prefix),
425 .lazy_path = lp.dupe(graph),
426 .suffix = graph.dupeString(options.suffix),
427 };
428 run.argv.append(arena, .{ .file_content = file_content }) catch @panic("OOM");
429 lp.addStepDependencies(&run.step);
430}
431
432/// Deprecated, use `addOutputDirectoryArg2`.
433pub fn addOutputDirectoryArg(run: *Run, basename: []const u8) std.Build.LazyPath {
434 return run.addOutputDirectoryArg2(basename, .{});
435}
436
437/// Deprecated, use `addOutputDirectoryArg2`.
438pub fn addPrefixedOutputDirectoryArg(
439 run: *Run,
440 prefix: []const u8,
441 basename: []const u8,
442) std.Build.LazyPath {
443 return run.addOutputDirectoryArg2(basename, .{ .prefix = prefix });
444}
445
446/// Provides a directory path as a command line argument to the command being run.
447/// Asserts `basename` is not empty.
448///
449/// For example, a prefix of "-o" and basename of "output_dir" will result in
450/// the child process seeing something like this: "-ozig-cache/.../output_dir"
451///
452/// The child process will see a single argument, regardless of whether the
453/// prefix or basename have spaces.
454///
455/// The returned `std.Build.LazyPath` can be used as inputs to other APIs
456/// throughout the build system.
457///
458/// Related:
459/// * `addDirectoryArg` - for input directories given to the child process
460pub fn addOutputDirectoryArg2(
461 run: *Run,
462 basename: []const u8,
463 options: PathArgOptions,
464) std.Build.LazyPath {
465 if (basename.len == 0) @panic("basename must not be empty");
466 const graph = run.step.owner.graph;
467 const arena = graph.arena;
468
469 const output = arena.create(Output) catch @panic("OOM");
470 output.* = .{
471 .prefix = graph.dupeString(options.prefix),
472 .basename = graph.dupeString(basename),
473 .suffix = graph.dupeString(options.suffix),
474 .generated_file = graph.addGeneratedFile(&run.step),
475 .make_absolute = options.make_absolute,
476 };
477 run.argv.append(arena, .{ .output_directory = output }) catch @panic("OOM");
478
479 if (run.rename_step_with_output_arg) {
480 run.setName(std.fmt.allocPrint(arena, "{s} ({s})", .{ run.step.name, basename }) catch @panic("OOM"));
481 }
482
483 return .{ .generated = .{ .index = output.generated_file } };
484}
485
486/// Deprecated, use `addDirectoryArg2`.
487pub fn addDirectoryArg(run: *Run, lazy_directory: std.Build.LazyPath) void {
488 run.addDirectoryArg2(lazy_directory, .{});
489}
490
491/// Deprecated, use `addDirectoryArg2`.
492pub fn addPrefixedDirectoryArg(run: *Run, prefix: []const u8, lazy_directory: std.Build.LazyPath) void {
493 run.addDirectoryArg2(lazy_directory, .{ .prefix = prefix });
494}
495
496/// Deprecated, use `addDirectoryArg2`.
497pub fn addDecoratedDirectoryArg(
498 run: *Run,
499 prefix: []const u8,
500 lazy_directory: std.Build.LazyPath,
501 suffix: []const u8,
502) void {
503 run.addDirectoryArg2(lazy_directory, .{ .prefix = prefix, .suffix = suffix });
504}
505
506pub fn addDirectoryArg2(
507 run: *Run,
508 lazy_directory: std.Build.LazyPath,
509 options: PathArgOptions,
510) void {
511 const graph = run.step.owner.graph;
512 const arena = graph.arena;
513 run.argv.append(arena, .{ .decorated_directory = .{
514 .prefix = graph.dupeString(options.prefix),
515 .lazy_path = lazy_directory.dupe(graph),
516 .suffix = graph.dupeString(options.suffix),
517 .make_absolute = options.make_absolute,
518 } }) catch @panic("OOM");
519 lazy_directory.addStepDependencies(&run.step);
520}
521
522/// Deprecated, use `addDepFileOutputArg2`.
523pub fn addDepFileOutputArg(run: *Run, basename: []const u8) std.Build.LazyPath {
524 return run.addDepFileOutputArg2(basename, .{});
525}
526
527/// Deprecated, use `addDepFileOutputArg2`.
528pub fn addPrefixedDepFileOutputArg(run: *Run, prefix: []const u8, basename: []const u8) std.Build.LazyPath {
529 return run.addDepFileOutputArg2(basename, .{ .prefix = prefix });
530}
531
532/// Add a path argument to a dep file (.d) for the child process to write its
533/// discovered additional dependencies.
534/// Only one dep file argument is allowed by instance.
535pub fn addDepFileOutputArg2(run: *Run, basename: []const u8, options: PathArgOptions) std.Build.LazyPath {
536 const b = run.step.owner;
537 const graph = b.graph;
538 const arena = graph.arena;
539
540 const dep_file = arena.create(Output) catch @panic("OOM");
541 dep_file.* = .{
542 .prefix = graph.dupeString(options.prefix),
543 .basename = graph.dupeString(basename),
544 .suffix = graph.dupeString(options.suffix),
545 .generated_file = graph.addGeneratedFile(&run.step),
546 .make_absolute = options.make_absolute,
547 };
548
549 run.argv.append(arena, .{ .output_file_dep = dep_file }) catch @panic("OOM");
550
551 return .{ .generated = .{ .index = dep_file.generated_file } };
552}
553
554/// Appends the contents of `arg`, verbatim, to the command line that will be
555/// passed to the process being run.
556///
557/// If `arg` is an input file, `addFileInput` (or related function) must be
558/// used instead to ensure correct cache behavior.
559///
560/// If `arg` is an output file, `addOutputFileArg` (or related function) must
561/// be used instead to ensure correct cache behavior.
562pub fn addArg(run: *Run, arg: []const u8) void {
563 const graph = run.step.owner.graph;
564 const arena = graph.arena;
565 run.argv.append(arena, .{ .bytes = graph.dupeString(arg) }) catch @panic("OOM");
566}
567
568/// Appends each of `args`, verbatim, to the command line that will be passed
569/// to the process being run.
570///
571/// If any element of `args` is an input file, `addFileInput` must be used
572/// instead to ensure correct cache behavior.
573///
574/// If any element of `args` is an output file, `addOutputFileArg` (or related
575/// function) must be used instead to ensure correct cache behavior.
576pub fn addArgs(run: *Run, args: []const []const u8) void {
577 for (args) |arg| run.addArg(arg);
578}
579
580/// Appends the extra arguments provided to `zig build` to the command line
581/// that will be passed to the process being run.
582///
583/// This causes the step to be considered to have side effects, disabling
584/// caching.
585///
586/// In the example command `zig build run -- arg1 arg2`, "arg1" and "arg2" will
587/// be passed to the process being run.
588pub fn addPassthruArgs(run: *Run) void {
589 const graph = run.step.owner.graph;
590 const arena = graph.arena;
591 run.argv.append(arena, .passthru) catch @panic("OOM");
592}
593
594/// Appends a custom string to the command line depending on the `-fdarling`
595/// value passed to `zig build`.
596pub fn addThirdPartyEnabledArgDarling(run: *Run, toggle_flags: ToggleFlags) void {
597 const graph = run.step.owner.graph;
598 const arena = graph.arena;
599 run.argv.append(arena, .{ .enable_darling = toggle_flags }) catch @panic("OOM");
600}
601
602/// Appends a custom string to the command line depending on the `-fqemu`
603/// value passed to `zig build`.
604pub fn addThirdPartyEnabledArgQemu(run: *Run, toggle_flags: ToggleFlags) void {
605 const graph = run.step.owner.graph;
606 const arena = graph.arena;
607 run.argv.append(arena, .{ .enable_qemu = toggle_flags }) catch @panic("OOM");
608}
609
610/// Appends a custom string to the command line depending on the `-frosetta`
611/// value passed to `zig build`.
612pub fn addThirdPartyEnabledArgRosetta(run: *Run, toggle_flags: ToggleFlags) void {
613 const graph = run.step.owner.graph;
614 const arena = graph.arena;
615 run.argv.append(arena, .{ .enable_rosetta = toggle_flags }) catch @panic("OOM");
616}
617
618/// Appends a custom string to the command line depending on the `-fwasmtime`
619/// value passed to `zig build`.
620pub fn addThirdPartyEnabledArgWasmtime(run: *Run, toggle_flags: ToggleFlags) void {
621 const graph = run.step.owner.graph;
622 const arena = graph.arena;
623 run.argv.append(arena, .{ .enable_wasmtime = toggle_flags }) catch @panic("OOM");
624}
625
626/// Appends a custom string to the command line depending on the `-fwine`
627/// value passed to `zig build`.
628pub fn addThirdPartyEnabledArgWine(run: *Run, toggle_flags: ToggleFlags) void {
629 const graph = run.step.owner.graph;
630 const arena = graph.arena;
631 run.argv.append(arena, .{ .enable_wine = toggle_flags }) catch @panic("OOM");
632}
633
634pub fn setStdIn(run: *Run, stdin: StdIn) void {
635 switch (stdin) {
636 .lazy_path => |lazy_path| lazy_path.addStepDependencies(&run.step),
637 .bytes, .none => {},
638 }
639 run.stdin = stdin;
640}
641
642pub fn setCwd(run: *Run, cwd: Build.LazyPath) void {
643 const graph = run.step.owner.graph;
644 cwd.addStepDependencies(&run.step);
645 run.cwd = cwd.dupe(graph);
646}
647
648pub fn clearEnvironment(run: *Run) void {
649 const b = run.step.owner;
650 const new_env_map = b.allocator.create(EnvMap) catch @panic("OOM");
651 new_env_map.* = .init(b.allocator);
652 run.environ_map = new_env_map;
653}
654
655pub fn getEnvMap(run: *Run) *EnvMap {
656 return getEnvMapInternal(run);
657}
658
659fn getEnvMapInternal(run: *Run) *EnvMap {
660 const graph = run.step.owner.graph;
661 const arena = graph.arena;
662 return run.environ_map orelse {
663 const cloned_map = arena.create(EnvMap) catch @panic("OOM");
664 cloned_map.* = graph.environ_map.clone(arena) catch @panic("OOM");
665 run.environ_map = cloned_map;
666 return cloned_map;
667 };
668}
669
670pub fn setEnvironmentVariable(run: *Run, key: []const u8, value: []const u8) void {
671 const environ_map = run.getEnvMap();
672 // This data structure already dupes keys and values.
673 environ_map.put(key, value) catch @panic("OOM");
674}
675
676pub fn removeEnvironmentVariable(run: *Run, key: []const u8) void {
677 _ = run.getEnvMap().swapRemove(key);
678}
679
680pub fn setPreopen(run: *Run, name: []const u8, resource: Build.LazyPath) void {
681 const graph = run.step.owner.graph;
682 const wc = &graph.wip_configuration;
683 const arena = graph.arena;
684 resource.addStepDependencies(&run.step);
685 run.preopens.put(
686 arena,
687 wc.addString(name) catch @panic("OOM"),
688 resource.dupe(graph),
689 ) catch @panic("OOM");
690}
691
692/// Adds a check for exact stderr match. Does not add any other checks.
693pub fn expectStdErrEqual(run: *Run, bytes: []const u8) void {
694 const graph = run.step.owner.graph;
695 run.addCheck(.{ .expect_stderr_exact = graph.dupeString(bytes) });
696}
697
698pub fn expectStdErrMatch(run: *Run, bytes: []const u8) void {
699 const graph = run.step.owner.graph;
700 run.addCheck(.{ .expect_stderr_match = graph.dupeString(bytes) });
701}
702
703/// Adds a check for exact stdout match as well as a check for exit code 0, if
704/// there is not already an expected termination check.
705pub fn expectStdOutEqual(run: *Run, bytes: []const u8) void {
706 const graph = run.step.owner.graph;
707 run.addCheck(.{ .expect_stdout_exact = graph.dupeString(bytes) });
708 if (!run.hasTermCheck()) run.expectExitCode(0);
709}
710
711/// Adds a check for stdout match as well as a check for exit code 0, if there
712/// is not already an expected termination check.
713pub fn expectStdOutMatch(run: *Run, bytes: []const u8) void {
714 const graph = run.step.owner.graph;
715 run.addCheck(.{ .expect_stdout_match = graph.dupeString(bytes) });
716 if (!run.hasTermCheck()) run.expectExitCode(0);
717}
718
719pub fn expectExitCode(run: *Run, code: u8) void {
720 const new_check: StdIo.Check = .{ .expect_term = .{ .exited = code } };
721 run.addCheck(new_check);
722}
723
724pub fn hasTermCheck(run: Run) bool {
725 for (run.stdio.check.items) |check| switch (check) {
726 .expect_term => return true,
727 else => continue,
728 };
729 return false;
730}
731
732pub fn addCheck(run: *Run, new_check: StdIo.Check) void {
733 const b = run.step.owner;
734
735 switch (run.stdio) {
736 .infer_from_args => {
737 run.stdio = .{ .check = .empty };
738 run.stdio.check.append(b.allocator, new_check) catch @panic("OOM");
739 },
740 .check => |*checks| checks.append(b.allocator, new_check) catch @panic("OOM"),
741 else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of Run instead"),
742 }
743
744 switch (new_check) {
745 .expect_stderr_snapshot,
746 .expect_stdout_snapshot,
747 => |file| run.addFileInput(file),
748 else => {},
749 }
750}
751
752pub fn captureStdErr(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPath {
753 assert(run.stdio != .inherit);
754 assert(run.stdio != .zig_test);
755
756 const b = run.step.owner;
757 const graph = b.graph;
758 const arena = graph.arena;
759
760 if (run.captured_stderr) |captured| return .{ .generated = .{ .index = captured.generated_file } };
761
762 const captured = arena.create(CapturedStdIo) catch @panic("OOM");
763 captured.* = .{
764 .prefix = "",
765 .basename = if (options.basename) |basename| graph.dupeString(basename) else "stderr",
766 .generated_file = graph.addGeneratedFile(&run.step),
767 .trim_whitespace = options.trim_whitespace,
768 };
769 run.captured_stderr = captured;
770 return .{ .generated = .{ .index = captured.generated_file } };
771}
772
773pub fn captureStdOut(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPath {
774 assert(run.stdio != .inherit);
775 assert(run.stdio != .zig_test);
776
777 const b = run.step.owner;
778 const graph = b.graph;
779 const arena = graph.arena;
780
781 if (run.captured_stdout) |captured| return .{ .generated = .{ .index = captured.generated_file } };
782
783 const captured = arena.create(CapturedStdIo) catch @panic("OOM");
784 captured.* = .{
785 .prefix = "",
786 .basename = if (options.basename) |basename| graph.dupeString(basename) else "stdout",
787 .generated_file = graph.addGeneratedFile(&run.step),
788 .trim_whitespace = options.trim_whitespace,
789 };
790 run.captured_stdout = captured;
791 return .{ .generated = .{ .index = captured.generated_file } };
792}
793
794/// Adds an additional input files that, when modified, indicates that this Run
795/// step should be re-executed.
796/// If the Run step is determined to have side-effects, the Run step is always
797/// executed when it appears in the build graph, regardless of whether this
798/// file has been modified.
799pub fn addFileInput(run: *Run, file_input: std.Build.LazyPath) void {
800 const graph = run.step.owner.graph;
801 const arena = graph.arena;
802 file_input.addStepDependencies(&run.step);
803 run.file_inputs.append(arena, file_input.dupe(graph)) catch @panic("OOM");
804}