authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-05-04 14:49:38-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-05-05 09:42:51-04:00
logd582575aba5264aaa02a8af0cdb7da7c4f4c6220
tree71318b4b2c9809a66cd16341c70be5f17909657b
parentdb890dbae72bc31e50d4ec641f2afce683df772d

Run: add lazy path file inputs

This replaces `extra_file_dependencies` with support for lazy paths. Also assert output file basenames are not empty, avoid improper use of field default values, ensure stored strings are duplicated, and prefer `ArrayListUnmanaged` to discourage misuse of direct field access which wouldn't add step dependencies.

1 files changed, 67 insertions(+), 34 deletions(-)

lib/std/Build/Step/Run.zig+67-34
......@@ -5,7 +5,6 @@ const Step = Build.Step;
55const fs = std.fs;
66const mem = std.mem;
77const process = std.process;
8const ArrayList = std.ArrayList;
98const EnvMap = process.EnvMap;
109const assert = std.debug.assert;
1110
......@@ -16,7 +15,7 @@ pub const base_id: Step.Id = .run;
1615step: Step,
1716
1817/// See also addArg and addArgs to modifying this directly
19argv: ArrayList(Arg),
18argv: std.ArrayListUnmanaged(Arg),
2019
2120/// Use `setCwd` to set the initial current working directory
2221cwd: ?Build.LazyPath,
......@@ -32,22 +31,26 @@ env_map: ?*EnvMap,
3231/// If the Run step is determined to not have side-effects, then execution will
3332/// be skipped if all output files are up-to-date and input files are
3433/// unchanged.
35stdio: StdIo = .infer_from_args,
34stdio: StdIo,
3635
3736/// This field must be `.none` if stdio is `inherit`.
3837/// It should be only set using `setStdIn`.
39stdin: StdIn = .none,
38stdin: StdIn,
4039
41/// Additional file paths relative to build.zig that, when modified, indicate
42/// that the Run step should be re-executed.
43/// If the Run step is determined to have side-effects, this field is ignored
44/// and the Run step is always executed when it appears in the build graph.
45extra_file_dependencies: []const []const u8 = &.{},
40/// Deprecated: use `addFileInput`
41extra_file_dependencies: []const []const u8,
42
43/// Additional input files that, when modified, indicate that the Run step
44/// should be re-executed.
45/// If the Run step is determined to have side-effects, the Run step is always
46/// executed when it appears in the build graph, regardless of whether these
47/// files have been modified.
48file_inputs: std.ArrayListUnmanaged(std.Build.LazyPath),
4649
4750/// After adding an output argument, this step will by default rename itself
4851/// for a better display name in the build summary.
4952/// This can be disabled by setting this to false.
50rename_step_with_output_arg: bool = true,
53rename_step_with_output_arg: bool,
5154
5255/// If this is true, a Run step which is configured to check the output of the
5356/// executed binary will not fail the build if the binary cannot be executed
......@@ -58,25 +61,25 @@ rename_step_with_output_arg: bool = true,
5861/// Rosetta (macOS) and binfmt_misc (Linux).
5962/// If this Run step is considered to have side-effects, then this flag does
6063/// nothing.
61skip_foreign_checks: bool = false,
64skip_foreign_checks: bool,
6265
6366/// If this is true, failing to execute a foreign binary will be considered an
6467/// error. However if this is false, the step will be skipped on failure instead.
6568///
6669/// This allows for a Run step to attempt to execute a foreign binary using an
6770/// external executor (such as qemu) but not fail if the executor is unavailable.
68failing_to_execute_foreign_is_an_error: bool = true,
71failing_to_execute_foreign_is_an_error: bool,
6972
7073/// If stderr or stdout exceeds this amount, the child process is killed and
7174/// the step fails.
72max_stdio_size: usize = 10 * 1024 * 1024,
75max_stdio_size: usize,
7376
74captured_stdout: ?*Output = null,
75captured_stderr: ?*Output = null,
77captured_stdout: ?*Output,
78captured_stderr: ?*Output,
7679
77dep_output_file: ?*Output = null,
80dep_output_file: ?*Output,
7881
79has_side_effects: bool = false,
82has_side_effects: bool,
8083
8184pub const StdIn = union(enum) {
8285 none,
......@@ -103,7 +106,7 @@ pub const StdIo = union(enum) {
103106 /// conditions.
104107 /// Note that an explicit check for exit code 0 needs to be added to this
105108 /// list if such a check is desirable.
106 check: std.ArrayList(Check),
109 check: std.ArrayListUnmanaged(Check),
107110 /// This Run step is running a zig unit test binary and will communicate
108111 /// extra metadata over the IPC protocol.
109112 zig_test,
......@@ -145,9 +148,21 @@ pub fn create(owner: *std.Build, name: []const u8) *Run {
145148 .owner = owner,
146149 .makeFn = make,
147150 }),
148 .argv = ArrayList(Arg).init(owner.allocator),
151 .argv = .{},
149152 .cwd = null,
150153 .env_map = null,
154 .stdio = .infer_from_args,
155 .stdin = .none,
156 .extra_file_dependencies = &.{},
157 .file_inputs = .{},
158 .rename_step_with_output_arg = true,
159 .skip_foreign_checks = false,
160 .failing_to_execute_foreign_is_an_error = true,
161 .max_stdio_size = 10 * 1024 * 1024,
162 .captured_stdout = null,
163 .captured_stderr = null,
164 .dep_output_file = null,
165 .has_side_effects = false,
151166 };
152167 return self;
153168}
......@@ -163,9 +178,10 @@ pub fn enableTestRunnerMode(self: *Run) void {
163178}
164179
165180pub fn addArtifactArg(self: *Run, artifact: *Step.Compile) void {
181 const b = self.step.owner;
166182 const bin_file = artifact.getEmittedBin();
167183 bin_file.addStepDependencies(&self.step);
168 self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM");
184 self.argv.append(b.allocator, Arg{ .artifact = artifact }) catch @panic("OOM");
169185}
170186
171187/// Provides a file path as a command line argument to the command being run.
......@@ -181,6 +197,7 @@ pub fn addOutputFileArg(self: *Run, basename: []const u8) std.Build.LazyPath {
181197}
182198
183199/// Provides a file path as a command line argument to the command being run.
200/// Asserts `basename` is not empty.
184201///
185202/// For example, a prefix of "-o" and basename of "output.txt" will result in
186203/// the child process seeing something like this: "-ozig-cache/.../output.txt"
......@@ -200,14 +217,15 @@ pub fn addPrefixedOutputFileArg(
200217 basename: []const u8,
201218) std.Build.LazyPath {
202219 const b = self.step.owner;
220 if (basename.len == 0) @panic("basename must not be empty");
203221
204222 const output = b.allocator.create(Output) catch @panic("OOM");
205223 output.* = .{
206 .prefix = prefix,
207 .basename = basename,
224 .prefix = b.dupe(prefix),
225 .basename = b.dupe(basename),
208226 .generated_file = .{ .step = &self.step },
209227 };
210 self.argv.append(.{ .output = output }) catch @panic("OOM");
228 self.argv.append(b.allocator, .{ .output = output }) catch @panic("OOM");
211229
212230 if (self.rename_step_with_output_arg) {
213231 self.setName(b.fmt("{s} ({s})", .{ self.step.name, basename }));
......@@ -248,7 +266,7 @@ pub fn addPrefixedFileArg(self: *Run, prefix: []const u8, lp: std.Build.LazyPath
248266 .prefix = b.dupe(prefix),
249267 .lazy_path = lp.dupe(b),
250268 };
251 self.argv.append(.{ .lazy_path = prefixed_file_source }) catch @panic("OOM");
269 self.argv.append(b.allocator, .{ .lazy_path = prefixed_file_source }) catch @panic("OOM");
252270 lp.addStepDependencies(&self.step);
253271}
254272
......@@ -269,7 +287,7 @@ pub fn addPrefixedDirectoryArg(self: *Run, prefix: []const u8, directory_source:
269287 .prefix = b.dupe(prefix),
270288 .lazy_path = directory_source.dupe(b),
271289 };
272 self.argv.append(.{ .directory_source = prefixed_directory_source }) catch @panic("OOM");
290 self.argv.append(b.allocator, .{ .directory_source = prefixed_directory_source }) catch @panic("OOM");
273291 directory_source.addStepDependencies(&self.step);
274292}
275293
......@@ -284,9 +302,8 @@ pub fn addDepFileOutputArg(self: *Run, basename: []const u8) std.Build.LazyPath
284302/// write its discovered additional dependencies.
285303/// Only one dep file argument is allowed by instance.
286304pub fn addPrefixedDepFileOutputArg(self: *Run, prefix: []const u8, basename: []const u8) std.Build.LazyPath {
287 assert(self.dep_output_file == null);
288
289305 const b = self.step.owner;
306 assert(self.dep_output_file == null);
290307
291308 const dep_file = b.allocator.create(Output) catch @panic("OOM");
292309 dep_file.* = .{
......@@ -297,13 +314,14 @@ pub fn addPrefixedDepFileOutputArg(self: *Run, prefix: []const u8, basename: []c
297314
298315 self.dep_output_file = dep_file;
299316
300 self.argv.append(.{ .output = dep_file }) catch @panic("OOM");
317 self.argv.append(b.allocator, .{ .output = dep_file }) catch @panic("OOM");
301318
302319 return .{ .generated = &dep_file.generated_file };
303320}
304321
305322pub fn addArg(self: *Run, arg: []const u8) void {
306 self.argv.append(.{ .bytes = self.step.owner.dupe(arg) }) catch @panic("OOM");
323 const b = self.step.owner;
324 self.argv.append(b.allocator, .{ .bytes = self.step.owner.dupe(arg) }) catch @panic("OOM");
307325}
308326
309327pub fn addArgs(self: *Run, args: []const []const u8) void {
......@@ -401,12 +419,14 @@ pub fn hasTermCheck(self: Run) bool {
401419}
402420
403421pub fn addCheck(self: *Run, new_check: StdIo.Check) void {
422 const b = self.step.owner;
423
404424 switch (self.stdio) {
405425 .infer_from_args => {
406 self.stdio = .{ .check = std.ArrayList(StdIo.Check).init(self.step.owner.allocator) };
407 self.stdio.check.append(new_check) catch @panic("OOM");
426 self.stdio = .{ .check = .{} };
427 self.stdio.check.append(b.allocator, new_check) catch @panic("OOM");
408428 },
409 .check => |*checks| checks.append(new_check) catch @panic("OOM"),
429 .check => |*checks| checks.append(b.allocator, new_check) catch @panic("OOM"),
410430 else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of Run instead"),
411431 }
412432}
......@@ -441,6 +461,16 @@ pub fn captureStdOut(self: *Run) std.Build.LazyPath {
441461 return .{ .generated = &output.generated_file };
442462}
443463
464/// Adds an additional input files that, when modified, indicates that this Run
465/// step should be re-executed.
466/// If the Run step is determined to have side-effects, the Run step is always
467/// executed when it appears in the build graph, regardless of whether this
468/// file has been modified.
469pub fn addFileInput(self: *Run, file_input: std.Build.LazyPath) void {
470 file_input.addStepDependencies(&self.step);
471 self.file_inputs.append(self.step.owner.allocator, file_input.dupe(self.step.owner)) catch @panic("OOM");
472}
473
444474/// Returns whether the Run step has side effects *other than* updating the output arguments.
445475fn hasSideEffects(self: Run) bool {
446476 if (self.has_side_effects) return true;
......@@ -500,8 +530,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
500530 const self: *Run = @fieldParentPtr("step", step);
501531 const has_side_effects = self.hasSideEffects();
502532
503 var argv_list = ArrayList([]const u8).init(arena);
504 var output_placeholders = ArrayList(IndexedOutput).init(arena);
533 var argv_list = std.ArrayList([]const u8).init(arena);
534 var output_placeholders = std.ArrayList(IndexedOutput).init(arena);
505535
506536 var man = b.graph.cache.obtain();
507537 defer man.deinit();
......@@ -579,6 +609,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
579609 for (self.extra_file_dependencies) |file_path| {
580610 _ = try man.addFile(b.pathFromRoot(file_path), null);
581611 }
612 for (self.file_inputs.items) |lazy_path| {
613 _ = try man.addFile(lazy_path.getPath2(b, step), null);
614 }
582615
583616 if (try step.cacheHit(&man)) {
584617 // cache hit, skip running command