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;...@@ -5,7 +5,6 @@ const Step = Build.Step;
5const fs = std.fs;5const fs = std.fs;
6const mem = std.mem;6const mem = std.mem;
7const process = std.process;7const process = std.process;
8const ArrayList = std.ArrayList;
9const EnvMap = process.EnvMap;8const EnvMap = process.EnvMap;
10const assert = std.debug.assert;9const assert = std.debug.assert;
1110
...@@ -16,7 +15,7 @@ pub const base_id: Step.Id = .run;...@@ -16,7 +15,7 @@ pub const base_id: Step.Id = .run;
16step: Step,15step: Step,
1716
18/// See also addArg and addArgs to modifying this directly17/// See also addArg and addArgs to modifying this directly
19argv: ArrayList(Arg),18argv: std.ArrayListUnmanaged(Arg),
2019
21/// Use `setCwd` to set the initial current working directory20/// Use `setCwd` to set the initial current working directory
22cwd: ?Build.LazyPath,21cwd: ?Build.LazyPath,
...@@ -32,22 +31,26 @@ env_map: ?*EnvMap,...@@ -32,22 +31,26 @@ env_map: ?*EnvMap,
32/// If the Run step is determined to not have side-effects, then execution will31/// If the Run step is determined to not have side-effects, then execution will
33/// be skipped if all output files are up-to-date and input files are32/// be skipped if all output files are up-to-date and input files are
34/// unchanged.33/// unchanged.
35stdio: StdIo = .infer_from_args,34stdio: StdIo,
3635
37/// This field must be `.none` if stdio is `inherit`.36/// This field must be `.none` if stdio is `inherit`.
38/// It should be only set using `setStdIn`.37/// It should be only set using `setStdIn`.
39stdin: StdIn = .none,38stdin: StdIn,
4039
41/// Additional file paths relative to build.zig that, when modified, indicate40/// Deprecated: use `addFileInput`
42/// that the Run step should be re-executed.41extra_file_dependencies: []const []const u8,
43/// If the Run step is determined to have side-effects, this field is ignored42
44/// and the Run step is always executed when it appears in the build graph.43/// Additional input files that, when modified, indicate that the Run step
45extra_file_dependencies: []const []const u8 = &.{},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
47/// After adding an output argument, this step will by default rename itself50/// After adding an output argument, this step will by default rename itself
48/// for a better display name in the build summary.51/// for a better display name in the build summary.
49/// This can be disabled by setting this to false.52/// This can be disabled by setting this to false.
50rename_step_with_output_arg: bool = true,53rename_step_with_output_arg: bool,
5154
52/// If this is true, a Run step which is configured to check the output of the55/// If this is true, a Run step which is configured to check the output of the
53/// executed binary will not fail the build if the binary cannot be executed56/// executed binary will not fail the build if the binary cannot be executed
...@@ -58,25 +61,25 @@ rename_step_with_output_arg: bool = true,...@@ -58,25 +61,25 @@ rename_step_with_output_arg: bool = true,
58/// Rosetta (macOS) and binfmt_misc (Linux).61/// Rosetta (macOS) and binfmt_misc (Linux).
59/// If this Run step is considered to have side-effects, then this flag does62/// If this Run step is considered to have side-effects, then this flag does
60/// nothing.63/// nothing.
61skip_foreign_checks: bool = false,64skip_foreign_checks: bool,
6265
63/// If this is true, failing to execute a foreign binary will be considered an66/// If this is true, failing to execute a foreign binary will be considered an
64/// error. However if this is false, the step will be skipped on failure instead.67/// error. However if this is false, the step will be skipped on failure instead.
65///68///
66/// This allows for a Run step to attempt to execute a foreign binary using an69/// This allows for a Run step to attempt to execute a foreign binary using an
67/// external executor (such as qemu) but not fail if the executor is unavailable.70/// 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
70/// If stderr or stdout exceeds this amount, the child process is killed and73/// If stderr or stdout exceeds this amount, the child process is killed and
71/// the step fails.74/// the step fails.
72max_stdio_size: usize = 10 * 1024 * 1024,75max_stdio_size: usize,
7376
74captured_stdout: ?*Output = null,77captured_stdout: ?*Output,
75captured_stderr: ?*Output = null,78captured_stderr: ?*Output,
7679
77dep_output_file: ?*Output = null,80dep_output_file: ?*Output,
7881
79has_side_effects: bool = false,82has_side_effects: bool,
8083
81pub const StdIn = union(enum) {84pub const StdIn = union(enum) {
82 none,85 none,
...@@ -103,7 +106,7 @@ pub const StdIo = union(enum) {...@@ -103,7 +106,7 @@ pub const StdIo = union(enum) {
103 /// conditions.106 /// conditions.
104 /// Note that an explicit check for exit code 0 needs to be added to this107 /// Note that an explicit check for exit code 0 needs to be added to this
105 /// list if such a check is desirable.108 /// list if such a check is desirable.
106 check: std.ArrayList(Check),109 check: std.ArrayListUnmanaged(Check),
107 /// This Run step is running a zig unit test binary and will communicate110 /// This Run step is running a zig unit test binary and will communicate
108 /// extra metadata over the IPC protocol.111 /// extra metadata over the IPC protocol.
109 zig_test,112 zig_test,
...@@ -145,9 +148,21 @@ pub fn create(owner: *std.Build, name: []const u8) *Run {...@@ -145,9 +148,21 @@ pub fn create(owner: *std.Build, name: []const u8) *Run {
145 .owner = owner,148 .owner = owner,
146 .makeFn = make,149 .makeFn = make,
147 }),150 }),
148 .argv = ArrayList(Arg).init(owner.allocator),151 .argv = .{},
149 .cwd = null,152 .cwd = null,
150 .env_map = null,153 .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,
151 };166 };
152 return self;167 return self;
153}168}
...@@ -163,9 +178,10 @@ pub fn enableTestRunnerMode(self: *Run) void {...@@ -163,9 +178,10 @@ pub fn enableTestRunnerMode(self: *Run) void {
163}178}
164179
165pub fn addArtifactArg(self: *Run, artifact: *Step.Compile) void {180pub fn addArtifactArg(self: *Run, artifact: *Step.Compile) void {
181 const b = self.step.owner;
166 const bin_file = artifact.getEmittedBin();182 const bin_file = artifact.getEmittedBin();
167 bin_file.addStepDependencies(&self.step);183 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");
169}185}
170186
171/// Provides a file path as a command line argument to the command being run.187/// 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 {...@@ -181,6 +197,7 @@ pub fn addOutputFileArg(self: *Run, basename: []const u8) std.Build.LazyPath {
181}197}
182198
183/// Provides a file path as a command line argument to the command being run.199/// Provides a file path as a command line argument to the command being run.
200/// Asserts `basename` is not empty.
184///201///
185/// For example, a prefix of "-o" and basename of "output.txt" will result in202/// For example, a prefix of "-o" and basename of "output.txt" will result in
186/// the child process seeing something like this: "-ozig-cache/.../output.txt"203/// the child process seeing something like this: "-ozig-cache/.../output.txt"
...@@ -200,14 +217,15 @@ pub fn addPrefixedOutputFileArg(...@@ -200,14 +217,15 @@ pub fn addPrefixedOutputFileArg(
200 basename: []const u8,217 basename: []const u8,
201) std.Build.LazyPath {218) std.Build.LazyPath {
202 const b = self.step.owner;219 const b = self.step.owner;
220 if (basename.len == 0) @panic("basename must not be empty");
203221
204 const output = b.allocator.create(Output) catch @panic("OOM");222 const output = b.allocator.create(Output) catch @panic("OOM");
205 output.* = .{223 output.* = .{
206 .prefix = prefix,224 .prefix = b.dupe(prefix),
207 .basename = basename,225 .basename = b.dupe(basename),
208 .generated_file = .{ .step = &self.step },226 .generated_file = .{ .step = &self.step },
209 };227 };
210 self.argv.append(.{ .output = output }) catch @panic("OOM");228 self.argv.append(b.allocator, .{ .output = output }) catch @panic("OOM");
211229
212 if (self.rename_step_with_output_arg) {230 if (self.rename_step_with_output_arg) {
213 self.setName(b.fmt("{s} ({s})", .{ self.step.name, basename }));231 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...@@ -248,7 +266,7 @@ pub fn addPrefixedFileArg(self: *Run, prefix: []const u8, lp: std.Build.LazyPath
248 .prefix = b.dupe(prefix),266 .prefix = b.dupe(prefix),
249 .lazy_path = lp.dupe(b),267 .lazy_path = lp.dupe(b),
250 };268 };
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");
252 lp.addStepDependencies(&self.step);270 lp.addStepDependencies(&self.step);
253}271}
254272
...@@ -269,7 +287,7 @@ pub fn addPrefixedDirectoryArg(self: *Run, prefix: []const u8, directory_source:...@@ -269,7 +287,7 @@ pub fn addPrefixedDirectoryArg(self: *Run, prefix: []const u8, directory_source:
269 .prefix = b.dupe(prefix),287 .prefix = b.dupe(prefix),
270 .lazy_path = directory_source.dupe(b),288 .lazy_path = directory_source.dupe(b),
271 };289 };
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");
273 directory_source.addStepDependencies(&self.step);291 directory_source.addStepDependencies(&self.step);
274}292}
275293
...@@ -284,9 +302,8 @@ pub fn addDepFileOutputArg(self: *Run, basename: []const u8) std.Build.LazyPath...@@ -284,9 +302,8 @@ pub fn addDepFileOutputArg(self: *Run, basename: []const u8) std.Build.LazyPath
284/// write its discovered additional dependencies.302/// write its discovered additional dependencies.
285/// Only one dep file argument is allowed by instance.303/// Only one dep file argument is allowed by instance.
286pub fn addPrefixedDepFileOutputArg(self: *Run, prefix: []const u8, basename: []const u8) std.Build.LazyPath {304pub fn addPrefixedDepFileOutputArg(self: *Run, prefix: []const u8, basename: []const u8) std.Build.LazyPath {
287 assert(self.dep_output_file == null);
288
289 const b = self.step.owner;305 const b = self.step.owner;
306 assert(self.dep_output_file == null);
290307
291 const dep_file = b.allocator.create(Output) catch @panic("OOM");308 const dep_file = b.allocator.create(Output) catch @panic("OOM");
292 dep_file.* = .{309 dep_file.* = .{
...@@ -297,13 +314,14 @@ pub fn addPrefixedDepFileOutputArg(self: *Run, prefix: []const u8, basename: []c...@@ -297,13 +314,14 @@ pub fn addPrefixedDepFileOutputArg(self: *Run, prefix: []const u8, basename: []c
297314
298 self.dep_output_file = dep_file;315 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
302 return .{ .generated = &dep_file.generated_file };319 return .{ .generated = &dep_file.generated_file };
303}320}
304321
305pub fn addArg(self: *Run, arg: []const u8) void {322pub 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");
307}325}
308326
309pub fn addArgs(self: *Run, args: []const []const u8) void {327pub fn addArgs(self: *Run, args: []const []const u8) void {
...@@ -401,12 +419,14 @@ pub fn hasTermCheck(self: Run) bool {...@@ -401,12 +419,14 @@ pub fn hasTermCheck(self: Run) bool {
401}419}
402420
403pub fn addCheck(self: *Run, new_check: StdIo.Check) void {421pub fn addCheck(self: *Run, new_check: StdIo.Check) void {
422 const b = self.step.owner;
423
404 switch (self.stdio) {424 switch (self.stdio) {
405 .infer_from_args => {425 .infer_from_args => {
406 self.stdio = .{ .check = std.ArrayList(StdIo.Check).init(self.step.owner.allocator) };426 self.stdio = .{ .check = .{} };
407 self.stdio.check.append(new_check) catch @panic("OOM");427 self.stdio.check.append(b.allocator, new_check) catch @panic("OOM");
408 },428 },
409 .check => |*checks| checks.append(new_check) catch @panic("OOM"),429 .check => |*checks| checks.append(b.allocator, new_check) catch @panic("OOM"),
410 else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of Run instead"),430 else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of Run instead"),
411 }431 }
412}432}
...@@ -441,6 +461,16 @@ pub fn captureStdOut(self: *Run) std.Build.LazyPath {...@@ -441,6 +461,16 @@ pub fn captureStdOut(self: *Run) std.Build.LazyPath {
441 return .{ .generated = &output.generated_file };461 return .{ .generated = &output.generated_file };
442}462}
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
444/// Returns whether the Run step has side effects *other than* updating the output arguments.474/// Returns whether the Run step has side effects *other than* updating the output arguments.
445fn hasSideEffects(self: Run) bool {475fn hasSideEffects(self: Run) bool {
446 if (self.has_side_effects) return true;476 if (self.has_side_effects) return true;
...@@ -500,8 +530,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -500,8 +530,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
500 const self: *Run = @fieldParentPtr("step", step);530 const self: *Run = @fieldParentPtr("step", step);
501 const has_side_effects = self.hasSideEffects();531 const has_side_effects = self.hasSideEffects();
502532
503 var argv_list = ArrayList([]const u8).init(arena);533 var argv_list = std.ArrayList([]const u8).init(arena);
504 var output_placeholders = ArrayList(IndexedOutput).init(arena);534 var output_placeholders = std.ArrayList(IndexedOutput).init(arena);
505535
506 var man = b.graph.cache.obtain();536 var man = b.graph.cache.obtain();
507 defer man.deinit();537 defer man.deinit();
...@@ -579,6 +609,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -579,6 +609,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
579 for (self.extra_file_dependencies) |file_path| {609 for (self.extra_file_dependencies) |file_path| {
580 _ = try man.addFile(b.pathFromRoot(file_path), null);610 _ = try man.addFile(b.pathFromRoot(file_path), null);
581 }611 }
612 for (self.file_inputs.items) |lazy_path| {
613 _ = try man.addFile(lazy_path.getPath2(b, step), null);
614 }
582615
583 if (try step.cacheHit(&man)) {616 if (try step.cacheHit(&man)) {
584 // cache hit, skip running command617 // cache hit, skip running command