authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-22 21:42:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:34-07:00
loga399d37886bfb8358e2f93744f0dd3f59542dcee
tree073b4d050fd92cc2009d06a78cf3ec3d4d498d60
parent81ee4ab32c8617af3ce9690d562131a6a0924f1c

maker: upgrade some of the run step logic


6 files changed, 175 insertions(+), 118 deletions(-)

BRANCH_TODO+1
...@@ -18,5 +18,6 @@...@@ -18,5 +18,6 @@
18* link_eh_frame_hdr should be DefaultingBool18* link_eh_frame_hdr should be DefaultingBool
19* make --foo, --no-foo CLI args uniform (make them -f args instead)19* make --foo, --no-foo CLI args uniform (make them -f args instead)
20* install steps should provide generated files for installed things, then delete the run step hack20* install steps should provide generated files for installed things, then delete the run step hack
21 - but artifact install steps also add paths for dyn libs on windows
2122
2223
lib/compiler/Maker.zig+2
...@@ -35,6 +35,7 @@ install_paths: InstallPaths,...@@ -35,6 +35,7 @@ install_paths: InstallPaths,
35scanned_config: *const ScannedConfig,35scanned_config: *const ScannedConfig,
36steps: []Step,36steps: []Step,
37generated_files: []Path,37generated_files: []Path,
38run_args: ?[]const []const u8,
3839
39available_rss: usize,40available_rss: usize,
40max_rss_is_default: bool,41max_rss_is_default: bool,
...@@ -534,6 +535,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -534,6 +535,7 @@ pub fn main(init: process.Init.Minimal) !void {
534 },535 },
535 .steps = try arena.alloc(Step, scanned_config.configuration.steps.len),536 .steps = try arena.alloc(Step, scanned_config.configuration.steps.len),
536 .generated_files = try arena.alloc(Path, scanned_config.configuration.generated_files_len),537 .generated_files = try arena.alloc(Path, scanned_config.configuration.generated_files_len),
538 .run_args = run_args,
537539
538 .available_rss = max_rss,540 .available_rss = max_rss,
539 .max_rss_is_default = false,541 .max_rss_is_default = false,
lib/compiler/Maker/Fuzz.zig+1-1
...@@ -203,7 +203,7 @@ fn fuzzWorkerRun(fuzz: *Fuzz, run: Configuration.Step.Index) void {...@@ -203,7 +203,7 @@ fn fuzzWorkerRun(fuzz: *Fuzz, run: Configuration.Step.Index) void {
203 const graph = owner.graph;203 const graph = owner.graph;
204 const io = graph.io;204 const io = graph.io;
205205
206 run.rerunInFuzzMode(fuzz, fuzz.prog_node) catch |err| switch (err) {206 run.rerunInFuzzMode(run, fuzz, fuzz.prog_node) catch |err| switch (err) {
207 error.MakeFailed => {207 error.MakeFailed => {
208 var buf: [256]u8 = undefined;208 var buf: [256]u8 = undefined;
209 const stderr = io.lockStderr(&buf, graph.stderr_mode) catch |e| switch (e) {209 const stderr = io.lockStderr(&buf, graph.stderr_mode) catch |e| switch (e) {
lib/compiler/Maker/Step/Run.zig+165-116
...@@ -12,6 +12,7 @@ const Path = std.Build.Cache.Path;...@@ -12,6 +12,7 @@ const Path = std.Build.Cache.Path;
12const assert = std.debug.assert;12const assert = std.debug.assert;
13const mem = std.mem;13const mem = std.mem;
14const process = std.process;14const process = std.process;
15const allocPrint = std.fmt.allocPrint;
1516
16const Step = @import("../Step.zig");17const Step = @import("../Step.zig");
17const Maker = @import("../../Maker.zig");18const Maker = @import("../../Maker.zig");
...@@ -26,115 +27,158 @@ cached_test_metadata: ?CachedTestMetadata = null,...@@ -26,115 +27,158 @@ cached_test_metadata: ?CachedTestMetadata = null,
26/// executable that contains fuzz tests.27/// executable that contains fuzz tests.
27rebuilt_executable: ?Path = null,28rebuilt_executable: ?Path = null,
2829
30/// Persisted to reuse memory on subsequent calls to `make`.
31argv: std.ArrayList([]const u8) = .empty,
32/// Persisted to reuse memory on subsequent calls to `make`.
33output_placeholders: std.ArrayList(IndexedOutput) = .empty,
34
29pub fn make(35pub fn make(
30 run: *Run,36 run: *Run,
31 step_index: Configuration.Step.Index,37 run_index: Configuration.Step.Index,
32 maker: *Maker,38 maker: *Maker,
33 progress_node: std.Progress.Node,39 progress_node: std.Progress.Node,
34) Step.ExtendedMakeError!void {40) Step.ExtendedMakeError!void {
35 if (true) @panic("TODO implement run.make()");
36 const graph = maker.graph;41 const graph = maker.graph;
37 const step = maker.stepByIndex(step_index);42 const gpa = maker.gpa;
43 const step = maker.stepByIndex(run_index);
38 const io = graph.io;44 const io = graph.io;
39 const arena = graph.arena; // TODO don't leak into the process arena45 const arena = graph.arena; // TODO don't leak into the process arena
40 const has_side_effects = run.hasSideEffects();46 const conf = &maker.scanned_config.configuration;
47 const conf_step = run_index.ptr(conf);
48 const conf_run = conf_step.extended.get(conf.extra).run;
49 const argv_list = &run.argv;
50 const output_placeholders = &run.output_placeholders;
4151
42 var argv_list = std.array_list.Managed([]const u8).init(arena);52 argv_list.clearRetainingCapacity();
43 var output_placeholders = std.array_list.Managed(IndexedOutput).init(arena);53 output_placeholders.clearRetainingCapacity();
4454
45 var man = graph.cache.obtain();55 var man = graph.cache.obtain();
46 defer man.deinit();56 defer man.deinit();
4757
48 if (run.environ_map) |environ_map| {58 if (conf_run.environ_map.value) |environ_map_index| {
49 for (environ_map.keys(), environ_map.values()) |key, value| {59 const environ_map = environ_map_index.get(conf);
50 man.hash.addBytes(key);60 for (environ_map.keys.slice(conf), environ_map.values.slice(conf)) |key, value| {
51 man.hash.addBytes(value);61 man.hash.addBytesZ(key.slice(conf));
62 man.hash.addBytesZ(value.slice(conf));
52 }63 }
53 }64 }
5465
55 man.hash.add(run.color);66 man.hash.add(conf_run.flags.color);
56 man.hash.add(run.disable_zig_progress);67 man.hash.add(conf_run.flags.disable_zig_progress);
5768
58 for (run.argv.items) |arg| {69 for (conf_run.args.slice) |arg_index| {
59 switch (arg) {70 const arg = arg_index.get(conf);
60 .bytes => |bytes| {71 try argv_list.ensureUnusedCapacity(gpa, 1);
61 try argv_list.append(bytes);72 switch (arg.flags.tag) {
62 man.hash.addBytes(bytes);73 .string => {
74 const prefix = arg.prefix.value.?.slice(conf);
75 argv_list.appendAssumeCapacity(prefix);
76 man.hash.addBytesZ(prefix);
63 },77 },
64 .lazy_path => |file| {78 .path_file => {
65 const file_path = file.lazy_path.getPath3(graph, step);79 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
66 try argv_list.append(graph.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(maker, file_path) }));80 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
67 man.hash.addBytes(file.prefix);81 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
82 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
83 prefix, try convertPathArg(run_index, maker, file_path), suffix,
84 }));
85 man.hash.addBytesZ(prefix);
86 man.hash.addBytesZ(suffix);
68 _ = try man.addFilePath(file_path, null);87 _ = try man.addFilePath(file_path, null);
69 },88 },
70 .decorated_directory => |dd| {89 .path_directory => {
71 const file_path = dd.lazy_path.getPath3(graph, step);90 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
72 const resolved_arg = graph.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(maker, file_path), dd.suffix });91 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
73 try argv_list.append(resolved_arg);92 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
93 const resolved_arg = try mem.concat(arena, u8, &.{
94 prefix, try convertPathArg(run_index, maker, file_path), suffix,
95 });
96 argv_list.appendAssumeCapacity(resolved_arg);
74 man.hash.addBytes(resolved_arg);97 man.hash.addBytes(resolved_arg);
75 },98 },
76 .file_content => |file_plp| {99 .file_content => {
77 const file_path = file_plp.lazy_path.getPath3(graph, step);100 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
101 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
102 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
78103
79 var result: std.Io.Writer.Allocating = .init(arena);104 var result: std.Io.Writer.Allocating = .init(arena);
80 errdefer result.deinit();105 result.writer.writeAll(prefix) catch return error.OutOfMemory;
81 result.writer.writeAll(file_plp.prefix) catch return error.OutOfMemory;
82106
83 const file = file_path.root_dir.handle.openFile(io, file_path.subPathOrDot(), .{}) catch |err| {107 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
84 return step.fail(108 return step.fail(maker, "unable to open input file {f}: {t}", .{ file_path, err });
85 "unable to open input file '{f}': {t}",
86 .{ file_path, err },
87 );
88 };
89 defer file.close(io);109 defer file.close(io);
90110
91 var buf: [1024]u8 = undefined;111 var file_reader = file.reader(io, &.{});
92 var file_reader = file.reader(io, &buf);
93 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {112 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {
94 error.ReadFailed => return step.fail(113 error.ReadFailed => switch (file_reader.err.?) {
95 "failed to read from '{f}': {t}",114 error.Canceled => |e| return e,
96 .{ file_path, file_reader.err.? },115 else => |e| return step.fail(maker, "failed to read from {f}: {t}", .{ file_path, e }),
97 ),116 },
98 error.WriteFailed => return error.OutOfMemory,117 error.WriteFailed => return error.OutOfMemory,
99 };118 };
119 result.writer.writeAll(suffix) catch return error.OutOfMemory;
100120
101 try argv_list.append(result.written());121 argv_list.appendAssumeCapacity(result.written());
102 man.hash.addBytes(file_plp.prefix);122 man.hash.addBytesZ(prefix);
123 man.hash.addBytesZ(suffix);
103 _ = try man.addFilePath(file_path, null);124 _ = try man.addFilePath(file_path, null);
104 },125 },
105 .artifact => |pa| {126 .artifact => {
106 const artifact = pa.artifact;127 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
107128 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
108 if (artifact.rootModuleTarget().os.tag == .windows) {129 const producer_index = arg.producer.value.?;
130 const producer_step = producer_index.ptr(conf);
131 const producer = producer_step.extended.get(conf.extra).compile;
132 const root_module = producer.root_module.get(conf);
133 const root_module_target = root_module.resolved_target.get(conf).?.result.get(conf);
134 const os_tag = root_module_target.flags.os_tag.unwrap().?;
135
136 if (true) @panic("TODO");
137
138 if (os_tag == .windows) {
109 // On Windows we don't have rpaths so we have to add .dll search paths to PATH139 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
110 addPathForDynLibs(artifact);140 addPathForDynLibs(producer_index);
111 }141 }
112 const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?;142 const file_path = producer_index.installed_path orelse producer_index.generated_bin.?.path.?;
113143
114 try argv_list.append(graph.fmt("{s}{s}", .{144 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
115 pa.prefix,145 prefix,
116 run.convertPathArg(maker, .{ .root_dir = .cwd(), .sub_path = file_path }),146 try convertPathArg(run_index, maker, .{ .root_dir = .cwd(), .sub_path = file_path }),
147 suffix,
117 }));148 }));
118149
119 _ = try man.addFile(file_path, null);150 _ = try man.addFile(file_path, null);
120 },151 },
121 .output_file, .output_directory => |output| {152 .output_file, .output_directory => {
122 man.hash.addBytes(output.prefix);153 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
123 man.hash.addBytes(output.basename);154 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
155 const basename = arg.basename.value.?.slice(conf);
156
157 man.hash.addBytesZ(prefix);
158 man.hash.addBytesZ(basename);
159 man.hash.addBytesZ(suffix);
160
124 // Add a placeholder into the argument list because we need the161 // Add a placeholder into the argument list because we need the
125 // manifest hash to be updated with all arguments before the162 // manifest hash to be updated with all arguments before the
126 // object directory is computed.163 // object directory is computed.
127 try output_placeholders.append(.{164 try output_placeholders.append(gpa, .{
128 .index = argv_list.items.len,165 .index = @intCast(argv_list.items.len),
129 .tag = arg,166 .arg_index = arg_index,
130 .output = output,
131 });167 });
132 _ = try argv_list.addOne();168 argv_list.items.len += 1;
169 },
170 .cli_rest_positionals => {
171 if (maker.run_args) |run_args| {
172 try argv_list.appendSlice(gpa, run_args);
173 for (run_args) |s| man.hash.addBytes(s);
174 }
133 },175 },
134 }176 }
135 }177 }
136178
137 switch (run.stdin) {179 if (true) @panic("TODO");
180
181 switch (conf_run.stdin.u) {
138 .bytes => |bytes| {182 .bytes => |bytes| {
139 man.hash.addBytes(bytes);183 man.hash.addBytes(bytes);
140 },184 },
...@@ -145,28 +189,30 @@ pub fn make(...@@ -145,28 +189,30 @@ pub fn make(
145 .none => {},189 .none => {},
146 }190 }
147191
148 if (run.captured_stdout) |captured| {192 if (conf_run.captured_stdout) |captured| {
149 man.hash.addBytes(captured.output.basename);193 man.hash.addBytes(captured.output.basename);
150 man.hash.add(captured.trim_whitespace);194 man.hash.add(captured.trim_whitespace);
151 }195 }
152196
153 if (run.captured_stderr) |captured| {197 if (conf_run.captured_stderr) |captured| {
154 man.hash.addBytes(captured.output.basename);198 man.hash.addBytes(captured.output.basename);
155 man.hash.add(captured.trim_whitespace);199 man.hash.add(captured.trim_whitespace);
156 }200 }
157201
158 std.log.err("TODO hashStdIo", .{});202 std.log.err("TODO hashStdIo", .{});
159 //hashStdIo(&man.hash, run.stdio);203 //hashStdIo(&man.hash, conf_run.stdio);
160204
161 for (run.file_inputs.items) |lazy_path| {205 for (conf_run.file_inputs.items) |lazy_path| {
162 _ = try man.addFile(lazy_path.getPath2(graph, step), null);206 _ = try man.addFile(lazy_path.getPath2(graph, step), null);
163 }207 }
164208
165 if (run.cwd) |cwd| {209 if (conf_run.cwd) |cwd| {
166 const cwd_path = cwd.getPath3(graph, step);210 const cwd_path = cwd.getPath3(graph, step);
167 _ = man.hash.addBytes(try cwd_path.toString(arena));211 _ = man.hash.addBytes(try cwd_path.toString(arena));
168 }212 }
169213
214 const has_side_effects = conf_run.flags.has_side_effects;
215
170 if (!has_side_effects and try step.cacheHitAndWatch(&man)) {216 if (!has_side_effects and try step.cacheHitAndWatch(&man)) {
171 // cache hit, skip running command217 // cache hit, skip running command
172 const digest = man.final();218 const digest = man.final();
...@@ -182,7 +228,7 @@ pub fn make(...@@ -182,7 +228,7 @@ pub fn make(
182 return;228 return;
183 }229 }
184230
185 const dep_output_file = run.dep_output_file orelse {231 const dep_output_file = conf_run.dep_output_file orelse {
186 // We already know the final output paths, use them directly.232 // We already know the final output paths, use them directly.
187 const digest = if (has_side_effects)233 const digest = if (has_side_effects)
188 man.hash.final()234 man.hash.final()
...@@ -205,18 +251,18 @@ pub fn make(...@@ -205,18 +251,18 @@ pub fn make(
205 else => unreachable,251 else => unreachable,
206 };252 };
207 graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {253 graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
208 return step.fail("unable to make path '{f}{s}': {t}", .{254 return step.fail(maker, "unable to make path '{f}{s}': {t}", .{
209 graph.cache_root, output_sub_dir_path, err,255 graph.cache_root, output_sub_dir_path, err,
210 });256 });
211 };257 };
212 const arg_output_path = run.convertPathArg(maker, .{258 const arg_output_path = try convertPathArg(run_index, maker, .{
213 .root_dir = .cwd(),259 .root_dir = .cwd(),
214 .sub_path = placeholder.output.generated_file.getPath(),260 .sub_path = placeholder.output.generated_file.getPath(),
215 });261 });
216 argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)262 argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)
217 arg_output_path263 arg_output_path
218 else264 else
219 graph.fmt("{s}{s}", .{ placeholder.output.prefix, arg_output_path });265 try allocPrint(arena, "{s}{s}", .{ placeholder.output.prefix, arg_output_path });
220 }266 }
221267
222 try runCommand(run, maker, progress_node, argv_list.items, has_side_effects, output_dir_path, null);268 try runCommand(run, maker, progress_node, argv_list.items, has_side_effects, output_dir_path, null);
...@@ -238,7 +284,7 @@ pub fn make(...@@ -238,7 +284,7 @@ pub fn make(
238 else => unreachable,284 else => unreachable,
239 };285 };
240 graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {286 graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
241 return step.fail("unable to make path '{f}{s}': {t}", .{287 return step.fail(maker, "unable to make path '{f}{s}': {t}", .{
242 graph.cache_root, output_sub_dir_path, err,288 graph.cache_root, output_sub_dir_path, err,
243 });289 });
244 };290 };
...@@ -247,9 +293,9 @@ pub fn make(...@@ -247,9 +293,9 @@ pub fn make(
247 .sub_path = graph.pathJoin(&output_components),293 .sub_path = graph.pathJoin(&output_components),
248 };294 };
249 placeholder.output.generated_file.path = raw_output_path.toString(arena) catch @panic("OOM");295 placeholder.output.generated_file.path = raw_output_path.toString(arena) catch @panic("OOM");
250 argv_list.items[placeholder.index] = graph.fmt("{s}{s}", .{296 argv_list.items[placeholder.index] = try mem.concat(arena, u8, .{
251 placeholder.output.prefix,297 placeholder.output.prefix,
252 run.convertPathArg(maker, raw_output_path),298 try convertPathArg(run_index, maker, raw_output_path),
253 });299 });
254 }300 }
255301
...@@ -268,7 +314,7 @@ pub fn make(...@@ -268,7 +314,7 @@ pub fn make(
268 man.final();314 man.final();
269315
270 const any_output = output_placeholders.items.len > 0 or316 const any_output = output_placeholders.items.len > 0 or
271 run.captured_stdout != null or run.captured_stderr != null;317 conf_run.captured_stdout != null or conf_run.captured_stderr != null;
272318
273 // Rename into place319 // Rename into place
274 if (any_output) {320 if (any_output) {
...@@ -277,17 +323,17 @@ pub fn make(...@@ -277,17 +323,17 @@ pub fn make(
277 graph.cache_root.handle.rename(tmp_dir_path, graph.cache_root.handle, o_sub_path, io) catch |err| switch (err) {323 graph.cache_root.handle.rename(tmp_dir_path, graph.cache_root.handle, o_sub_path, io) catch |err| switch (err) {
278 Dir.RenameError.DirNotEmpty => {324 Dir.RenameError.DirNotEmpty => {
279 graph.cache_root.handle.deleteTree(io, o_sub_path) catch |del_err| {325 graph.cache_root.handle.deleteTree(io, o_sub_path) catch |del_err| {
280 return step.fail("unable to remove dir '{f}'{s}: {t}", .{326 return step.fail(maker, "unable to remove dir '{f}'{s}: {t}", .{
281 graph.cache_root, tmp_dir_path, del_err,327 graph.cache_root, tmp_dir_path, del_err,
282 });328 });
283 };329 };
284 graph.cache_root.handle.rename(tmp_dir_path, graph.cache_root.handle, o_sub_path, io) catch |retry_err| {330 graph.cache_root.handle.rename(tmp_dir_path, graph.cache_root.handle, o_sub_path, io) catch |retry_err| {
285 return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{331 return step.fail(maker, "unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{
286 graph.cache_root, tmp_dir_path, graph.cache_root, o_sub_path, retry_err,332 graph.cache_root, tmp_dir_path, graph.cache_root, o_sub_path, retry_err,
287 });333 });
288 };334 };
289 },335 },
290 else => return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{336 else => return step.fail(maker, "unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{
291 graph.cache_root, tmp_dir_path, graph.cache_root, o_sub_path, err,337 graph.cache_root, tmp_dir_path, graph.cache_root, o_sub_path, err,
292 }),338 }),
293 };339 };
...@@ -309,6 +355,7 @@ pub fn make(...@@ -309,6 +355,7 @@ pub fn make(
309/// * The wait fails, indicating the child closed stdout and stderr355/// * The wait fails, indicating the child closed stdout and stderr
310fn waitZigTest(356fn waitZigTest(
311 run: *Run,357 run: *Run,
358 maker: *Maker,
312 child: *process.Child,359 child: *process.Child,
313 options: Step.MakeOptions,360 options: Step.MakeOptions,
314 multi_reader: *Io.File.MultiReader,361 multi_reader: *Io.File.MultiReader,
...@@ -412,6 +459,7 @@ fn waitZigTest(...@@ -412,6 +459,7 @@ fn waitZigTest(
412 switch (header.tag) {459 switch (header.tag) {
413 .zig_version => {460 .zig_version => {
414 if (!std.mem.eql(u8, builtin.zig_version_string, body)) return run.step.fail(461 if (!std.mem.eql(u8, builtin.zig_version_string, body)) return run.step.fail(
462 maker,
415 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",463 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
416 .{ builtin.zig_version_string, body },464 .{ builtin.zig_version_string, body },
417 );465 );
...@@ -1028,6 +1076,7 @@ const StdioPollEnum = enum { stdout, stderr };...@@ -1028,6 +1076,7 @@ const StdioPollEnum = enum { stdout, stderr };
10281076
1029fn evalZigTest(1077fn evalZigTest(
1030 run: *Run,1078 run: *Run,
1079 maker: *Maker,
1031 spawn_options: process.SpawnOptions,1080 spawn_options: process.SpawnOptions,
1032 options: Step.MakeOptions,1081 options: Step.MakeOptions,
1033 fuzz_context: ?FuzzContext,1082 fuzz_context: ?FuzzContext,
...@@ -1102,7 +1151,7 @@ fn evalZigTest(...@@ -1102,7 +1151,7 @@ fn evalZigTest(
11021151
1103 // The individual unit test results are irrelevant: the test runner itself broke!1152 // The individual unit test results are irrelevant: the test runner itself broke!
1104 // Fail immediately without populating `s.test_results`.1153 // Fail immediately without populating `s.test_results`.
1105 return run.step.fail("unable to write stdin ({t}); test process unexpectedly {f}", .{ err, fmtTerm(term) });1154 return run.step.fail(maker, "unable to write stdin ({t}); test process unexpectedly {f}", .{ err, fmtTerm(term) });
1106 },1155 },
1107 .no_poll => |no_poll| {1156 .no_poll => |no_poll| {
1108 // This might be a success (we requested exit and the child dutifully closed stdout) or1157 // This might be a success (we requested exit and the child dutifully closed stdout) or
...@@ -1141,7 +1190,7 @@ fn evalZigTest(...@@ -1141,7 +1190,7 @@ fn evalZigTest(
1141 if (!tests_done or !termMatches(.{ .exited = 0 }, term)) {1190 if (!tests_done or !termMatches(.{ .exited = 0 }, term)) {
1142 // The individual unit test results are irrelevant: the test runner itself broke!1191 // The individual unit test results are irrelevant: the test runner itself broke!
1143 // Fail immediately without populating `s.test_results`.1192 // Fail immediately without populating `s.test_results`.
1144 return run.step.fail("test process unexpectedly {f}", .{fmtTerm(term)});1193 return run.step.fail(maker, "test process unexpectedly {f}", .{fmtTerm(term)});
1145 }1194 }
11461195
1147 // We're done with all of the tests! Commit the test results and return.1196 // We're done with all of the tests! Commit the test results and return.
...@@ -1181,7 +1230,7 @@ fn evalZigTest(...@@ -1181,7 +1230,7 @@ fn evalZigTest(
1181 run.step.result_stderr = try arena.dupe(u8, stderr);1230 run.step.result_stderr = try arena.dupe(u8, stderr);
1182 // The individual unit test results in `results` are irrelevant: the test runner1231 // The individual unit test results in `results` are irrelevant: the test runner
1183 // is broken! Fail immediately without populating `s.test_results`.1232 // is broken! Fail immediately without populating `s.test_results`.
1184 return run.step.fail("test runner failed to respond for {f}", .{Io.Duration{ .nanoseconds = timeout.ns_elapsed }});1233 return run.step.fail(maker, "test runner failed to respond for {f}", .{Io.Duration{ .nanoseconds = timeout.ns_elapsed }});
1185 },1234 },
1186 }1235 }
1187 comptime unreachable;1236 comptime unreachable;
...@@ -1313,7 +1362,7 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E...@@ -1313,7 +1362,7 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
1313 switch (run.stdin) {1362 switch (run.stdin) {
1314 .bytes => |bytes| {1363 .bytes => |bytes| {
1315 child.stdin.?.writeStreamingAll(io, bytes) catch |err| {1364 child.stdin.?.writeStreamingAll(io, bytes) catch |err| {
1316 return run.step.fail("unable to write stdin: {t}", .{err});1365 return run.step.fail(maker, "unable to write stdin: {t}", .{err});
1317 };1366 };
1318 child.stdin.?.close(io);1367 child.stdin.?.close(io);
1319 child.stdin = null;1368 child.stdin = null;
...@@ -1321,7 +1370,7 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E...@@ -1321,7 +1370,7 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
1321 .lazy_path => |lazy_path| {1370 .lazy_path => |lazy_path| {
1322 const path = lazy_path.getPath3(graph, &run.step);1371 const path = lazy_path.getPath3(graph, &run.step);
1323 const file = path.root_dir.handle.openFile(io, path.subPathOrDot(), .{}) catch |err| {1372 const file = path.root_dir.handle.openFile(io, path.subPathOrDot(), .{}) catch |err| {
1324 return run.step.fail("unable to open stdin file: {t}", .{err});1373 return run.step.fail(maker, "unable to open stdin file: {t}", .{err});
1325 };1374 };
1326 defer file.close(io);1375 defer file.close(io);
1327 // TODO https://github.com/ziglang/zig/issues/239551376 // TODO https://github.com/ziglang/zig/issues/23955
...@@ -1330,15 +1379,15 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E...@@ -1330,15 +1379,15 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
1330 var write_buffer: [1024]u8 = undefined;1379 var write_buffer: [1024]u8 = undefined;
1331 var stdin_writer = child.stdin.?.writerStreaming(io, &write_buffer);1380 var stdin_writer = child.stdin.?.writerStreaming(io, &write_buffer);
1332 _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {1381 _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {
1333 error.ReadFailed => return run.step.fail("failed to read from {f}: {t}", .{1382 error.ReadFailed => return run.step.fail(maker, "failed to read from {f}: {t}", .{
1334 path, file_reader.err.?,1383 path, file_reader.err.?,
1335 }),1384 }),
1336 error.WriteFailed => return run.step.fail("failed to write to stdin: {t}", .{1385 error.WriteFailed => return run.step.fail(maker, "failed to write to stdin: {t}", .{
1337 stdin_writer.err.?,1386 stdin_writer.err.?,
1338 }),1387 }),
1339 };1388 };
1340 stdin_writer.interface.flush() catch |err| switch (err) {1389 stdin_writer.interface.flush() catch |err| switch (err) {
1341 error.WriteFailed => return run.step.fail("failed to write to stdin: {t}", .{1390 error.WriteFailed => return run.step.fail(maker, "failed to write to stdin: {t}", .{
1342 stdin_writer.err.?,1391 stdin_writer.err.?,
1343 }),1392 }),
1344 };1393 };
...@@ -1418,15 +1467,13 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E...@@ -1418,15 +1467,13 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
1418}1467}
14191468
1420const IndexedOutput = struct {1469const IndexedOutput = struct {
1421 index: usize,1470 index: u32,
1422 tag: Configuration.Step.Run.Arg.Tag,1471 arg_index: Configuration.Step.Run.Arg.Index,
1423 output: *Output,
1424};1472};
14251473
1426const Output = void; // TODO
1427
1428pub fn rerunInFuzzMode(1474pub fn rerunInFuzzMode(
1429 run: *Run,1475 run: *Run,
1476 run_index: Configuration.Step.Index,
1430 fuzz: *std.Build.Fuzz,1477 fuzz: *std.Build.Fuzz,
1431 prog_node: std.Progress.Node,1478 prog_node: std.Progress.Node,
1432) !void {1479) !void {
...@@ -1444,11 +1491,11 @@ pub fn rerunInFuzzMode(...@@ -1444,11 +1491,11 @@ pub fn rerunInFuzzMode(
1444 },1491 },
1445 .lazy_path => |file| {1492 .lazy_path => |file| {
1446 const file_path = file.lazy_path.getPath3(b, step);1493 const file_path = file.lazy_path.getPath3(b, step);
1447 try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(maker, file_path) }));1494 try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, convertPathArg(run_index, maker, file_path) }));
1448 },1495 },
1449 .decorated_directory => |dd| {1496 .decorated_directory => |dd| {
1450 const file_path = dd.lazy_path.getPath3(b, step);1497 const file_path = dd.lazy_path.getPath3(b, step);
1451 try argv_list.append(arena, b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(maker, file_path), dd.suffix }));1498 try argv_list.append(arena, b.fmt("{s}{s}{s}", .{ dd.prefix, convertPathArg(run_index, maker, file_path), dd.suffix }));
1452 },1499 },
1453 .file_content => |file_plp| {1500 .file_content => |file_plp| {
1454 const file_path = file_plp.lazy_path.getPath3(b, step);1501 const file_path = file_plp.lazy_path.getPath3(b, step);
...@@ -1477,7 +1524,7 @@ pub fn rerunInFuzzMode(...@@ -1477,7 +1524,7 @@ pub fn rerunInFuzzMode(
1477 };1524 };
1478 try argv_list.append(arena, b.fmt("{s}{s}", .{1525 try argv_list.append(arena, b.fmt("{s}{s}", .{
1479 pa.prefix,1526 pa.prefix,
1480 run.convertPathArg(maker, .{ .root_dir = .cwd(), .sub_path = file_path }),1527 convertPathArg(run_index, maker, .{ .root_dir = .cwd(), .sub_path = file_path }),
1481 }));1528 }));
1482 },1529 },
1483 .output_file, .output_directory => unreachable,1530 .output_file, .output_directory => unreachable,
...@@ -1675,7 +1722,7 @@ fn runCommand(...@@ -1675,7 +1722,7 @@ fn runCommand(
16751722
1676 const host_dl = graph.host.result.dynamic_linker.get() orelse "(none)";1723 const host_dl = graph.host.result.dynamic_linker.get() orelse "(none)";
16771724
1678 return step.fail(1725 return step.fail(maker,
1679 \\the host system is unable to execute binaries from the target1726 \\the host system is unable to execute binaries from the target
1680 \\ because the host dynamic linker is '{s}',1727 \\ because the host dynamic linker is '{s}',
1681 \\ while the target dynamic linker is '{s}'.1728 \\ while the target dynamic linker is '{s}'.
...@@ -1688,7 +1735,7 @@ fn runCommand(...@@ -1688,7 +1735,7 @@ fn runCommand(
1688 const host_name = try graph.host.result.zigTriple(b.allocator);1735 const host_name = try graph.host.result.zigTriple(b.allocator);
1689 const foreign_name = try root_target.zigTriple(b.allocator);1736 const foreign_name = try root_target.zigTriple(b.allocator);
16901737
1691 return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{1738 return step.fail(maker, "the host system ({s}) is unable to execute binaries from the target ({s})", .{
1692 host_name, foreign_name,1739 host_name, foreign_name,
1693 });1740 });
1694 },1741 },
...@@ -1706,12 +1753,12 @@ fn runCommand(...@@ -1706,12 +1753,12 @@ fn runCommand(
1706 break :term spawnChildAndCollect(run, maker, progress_node, interp_argv.items, &environ_map, has_side_effects, fuzz_context) catch |e| {1753 break :term spawnChildAndCollect(run, maker, progress_node, interp_argv.items, &environ_map, has_side_effects, fuzz_context) catch |e| {
1707 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;1754 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;
1708 if (e == error.MakeFailed) return error.MakeFailed; // error already reported1755 if (e == error.MakeFailed) return error.MakeFailed; // error already reported
1709 return step.fail("unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e });1756 return step.fail(maker, "unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e });
1710 };1757 };
1711 }1758 }
1712 if (err == error.MakeFailed) return error.MakeFailed; // error already reported1759 if (err == error.MakeFailed) return error.MakeFailed; // error already reported
17131760
1714 return step.fail("failed to spawn and capture stdio from {s}: {t}", .{ argv[0], err });1761 return step.fail(maker, "failed to spawn and capture stdio from {s}: {t}", .{ argv[0], err });
1715 };1762 };
17161763
1717 const generic_result = opt_generic_result orelse {1764 const generic_result = opt_generic_result orelse {
...@@ -1748,7 +1795,7 @@ fn runCommand(...@@ -1748,7 +1795,7 @@ fn runCommand(
1748 const sub_path = b.pathJoin(&output_components);1795 const sub_path = b.pathJoin(&output_components);
1749 const sub_path_dirname = Dir.path.dirname(sub_path).?;1796 const sub_path_dirname = Dir.path.dirname(sub_path).?;
1750 b.cache_root.handle.createDirPath(io, sub_path_dirname) catch |err| {1797 b.cache_root.handle.createDirPath(io, sub_path_dirname) catch |err| {
1751 return step.fail("unable to make path '{f}{s}': {s}", .{1798 return step.fail(maker, "unable to make path '{f}{s}': {s}", .{
1752 b.cache_root, sub_path_dirname, @errorName(err),1799 b.cache_root, sub_path_dirname, @errorName(err),
1753 });1800 });
1754 };1801 };
...@@ -1759,7 +1806,7 @@ fn runCommand(...@@ -1759,7 +1806,7 @@ fn runCommand(
1759 .trailing => mem.trimEnd(u8, stream.bytes.?, &std.ascii.whitespace),1806 .trailing => mem.trimEnd(u8, stream.bytes.?, &std.ascii.whitespace),
1760 };1807 };
1761 b.cache_root.handle.writeFile(io, .{ .sub_path = sub_path, .data = data }) catch |err| {1808 b.cache_root.handle.writeFile(io, .{ .sub_path = sub_path, .data = data }) catch |err| {
1762 return step.fail("unable to write file '{f}{s}': {s}", .{1809 return step.fail(maker, "unable to write file '{f}{s}': {s}", .{
1763 b.cache_root, sub_path, @errorName(err),1810 b.cache_root, sub_path, @errorName(err),
1764 });1811 });
1765 };1812 };
...@@ -1771,7 +1818,7 @@ fn runCommand(...@@ -1771,7 +1818,7 @@ fn runCommand(
1771 .check => |checks| for (checks.items) |check| switch (check) {1818 .check => |checks| for (checks.items) |check| switch (check) {
1772 .expect_stderr_exact => |expected_bytes| {1819 .expect_stderr_exact => |expected_bytes| {
1773 if (!mem.eql(u8, expected_bytes, generic_result.stderr.?)) {1820 if (!mem.eql(u8, expected_bytes, generic_result.stderr.?)) {
1774 return step.fail(1821 return step.fail(maker,
1775 \\========= expected this stderr: =========1822 \\========= expected this stderr: =========
1776 \\{s}1823 \\{s}
1777 \\========= but found: ====================1824 \\========= but found: ====================
...@@ -1784,7 +1831,7 @@ fn runCommand(...@@ -1784,7 +1831,7 @@ fn runCommand(
1784 },1831 },
1785 .expect_stderr_match => |match| {1832 .expect_stderr_match => |match| {
1786 if (mem.find(u8, generic_result.stderr.?, match) == null) {1833 if (mem.find(u8, generic_result.stderr.?, match) == null) {
1787 return step.fail(1834 return step.fail(maker,
1788 \\========= expected to find in stderr: =========1835 \\========= expected to find in stderr: =========
1789 \\{s}1836 \\{s}
1790 \\========= but stderr does not contain it: =====1837 \\========= but stderr does not contain it: =====
...@@ -1797,7 +1844,7 @@ fn runCommand(...@@ -1797,7 +1844,7 @@ fn runCommand(
1797 },1844 },
1798 .expect_stdout_exact => |expected_bytes| {1845 .expect_stdout_exact => |expected_bytes| {
1799 if (!mem.eql(u8, expected_bytes, generic_result.stdout.?)) {1846 if (!mem.eql(u8, expected_bytes, generic_result.stdout.?)) {
1800 return step.fail(1847 return step.fail(maker,
1801 \\========= expected this stdout: =========1848 \\========= expected this stdout: =========
1802 \\{s}1849 \\{s}
1803 \\========= but found: ====================1850 \\========= but found: ====================
...@@ -1810,7 +1857,7 @@ fn runCommand(...@@ -1810,7 +1857,7 @@ fn runCommand(
1810 },1857 },
1811 .expect_stdout_match => |match| {1858 .expect_stdout_match => |match| {
1812 if (mem.find(u8, generic_result.stdout.?, match) == null) {1859 if (mem.find(u8, generic_result.stdout.?, match) == null) {
1813 return step.fail(1860 return step.fail(maker,
1814 \\========= expected to find in stdout: =========1861 \\========= expected to find in stdout: =========
1815 \\{s}1862 \\{s}
1816 \\========= but stdout does not contain it: =====1863 \\========= but stdout does not contain it: =====
...@@ -1823,7 +1870,7 @@ fn runCommand(...@@ -1823,7 +1870,7 @@ fn runCommand(
1823 },1870 },
1824 .expect_term => |expected_term| {1871 .expect_term => |expected_term| {
1825 if (!termMatches(expected_term, generic_result.term)) {1872 if (!termMatches(expected_term, generic_result.term)) {
1826 return step.fail("process {f} (expected {f})", .{1873 return step.fail(maker, "process {f} (expected {f})", .{
1827 fmtTerm(generic_result.term),1874 fmtTerm(generic_result.term),
1828 fmtTerm(expected_term),1875 fmtTerm(expected_term),
1829 });1876 });
...@@ -2069,21 +2116,23 @@ fn hasAnyOutputArgs(run: Run) bool {...@@ -2069,21 +2116,23 @@ fn hasAnyOutputArgs(run: Run) bool {
2069///2116///
2070/// Whenever a path is included in the argv of a child, it should be put through this function first2117/// Whenever a path is included in the argv of a child, it should be put through this function first
2071/// to make sure the child doesn't see paths relative to a cwd other than its own.2118/// to make sure the child doesn't see paths relative to a cwd other than its own.
2072fn convertPathArg(run: *Run, maker: *Maker, path: Path) []const u8 {2119fn convertPathArg(run_index: Configuration.Step.Index, maker: *Maker, path: Path) ![]const u8 {
2073 const b = run.step.owner;2120 const conf = &maker.scanned_config.configuration;
2121 const conf_step = run_index.ptr(conf);
2122 const conf_run = conf_step.extended.get(conf.extra).run;
2074 const graph = maker.graph;2123 const graph = maker.graph;
2075 const arena = graph.arena;2124 const arena = graph.arena; // TODO don't leak into process arena
20762125
2077 const path_str = path.toString(arena) catch @panic("OOM");2126 const path_str = try path.toString(arena);
2078 if (Dir.path.isAbsolute(path_str)) {2127 if (Dir.path.isAbsolute(path_str)) {
2079 // Absolute paths don't need changing.2128 // Absolute paths don't need changing.
2080 return path_str;2129 return path_str;
2081 }2130 }
2082 const child_cwd_rel: []const u8 = rel: {2131 const child_cwd_rel: []const u8 = rel: {
2083 const child_lazy_cwd = run.cwd orelse break :rel path_str;2132 const child_lazy_cwd = conf_run.cwd.value orelse break :rel path_str;
2084 const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(arena) catch @panic("OOM");2133 const child_cwd = try maker.resolveLazyPathIndexAbs(arena, child_lazy_cwd, run_index);
2085 // Convert it from relative to *our* cwd, to relative to the *child's* cwd.2134 // Convert it from relative to *our* cwd, to relative to the *child's* cwd.
2086 break :rel Dir.path.relative(arena, graph.cache.cwd, &graph.environ_map, child_cwd, path_str) catch @panic("OOM");2135 break :rel try Dir.path.relative(arena, graph.cache.cwd, &graph.environ_map, child_cwd, path_str);
2087 };2136 };
2088 // Not every path can be made relative, e.g. if the path and the child cwd are on different2137 // Not every path can be made relative, e.g. if the path and the child cwd are on different
2089 // disk designators on Windows. In that case, `relative` will return an absolute path which we can2138 // disk designators on Windows. In that case, `relative` will return an absolute path which we can
...@@ -2094,10 +2143,10 @@ fn convertPathArg(run: *Run, maker: *Maker, path: Path) []const u8 {...@@ -2094,10 +2143,10 @@ fn convertPathArg(run: *Run, maker: *Maker, path: Path) []const u8 {
2094 // * On POSIX, the executable name cannot be a single component like 'foo'2143 // * On POSIX, the executable name cannot be a single component like 'foo'
2095 // * Some executables might treat a leading '-' like a flag, which we must avoid2144 // * Some executables might treat a leading '-' like a flag, which we must avoid
2096 // There's no harm in it, so just *always* apply this prefix.2145 // There's no harm in it, so just *always* apply this prefix.
2097 return Dir.path.join(arena, &.{ ".", child_cwd_rel }) catch @panic("OOM");2146 return Dir.path.join(arena, &.{ ".", child_cwd_rel });
2098}2147}
20992148
2100fn addPathForDynLibs(artifact: *Step.Compile) void {2149fn addPathForDynLibs(artifact: Configuration.Step.Index) void {
2101 if (true) @panic("TODO");2150 if (true) @panic("TODO");
2102 for (artifact.getCompileDependencies(true)) |compile| {2151 for (artifact.getCompileDependencies(true)) |compile| {
2103 if (compile.root_module.resolved_target.?.result.os.tag == .windows and2152 if (compile.root_module.resolved_target.?.result.os.tag == .windows and
...@@ -2127,13 +2176,13 @@ fn failForeign(...@@ -2127,13 +2176,13 @@ fn failForeign(
2127 const host_name = try graph.host.result.zigTriple(process_arena);2176 const host_name = try graph.host.result.zigTriple(process_arena);
2128 const foreign_name = try exe.rootModuleTarget().zigTriple(process_arena);2177 const foreign_name = try exe.rootModuleTarget().zigTriple(process_arena);
21292178
2130 return step.fail(2179 return step.fail(maker,
2131 \\unable to spawn foreign binary '{s}' ({s}) on host system ({s})2180 \\unable to spawn foreign binary '{s}' ({s}) on host system ({s})
2132 \\ consider using {s} or enabling skip_foreign_checks in the Run step2181 \\ consider using {s} or enabling skip_foreign_checks in the Run step
2133 , .{ argv0, foreign_name, host_name, suggested_flag });2182 , .{ argv0, foreign_name, host_name, suggested_flag });
2134 },2183 },
2135 else => {2184 else => {
2136 return step.fail("unable to spawn foreign binary '{s}'", .{argv0});2185 return step.fail(maker, "unable to spawn foreign binary '{s}'", .{argv0});
2137 },2186 },
2138 }2187 }
2139}2188}
lib/std/Build/Cache.zig+4-1
...@@ -189,12 +189,15 @@ pub const File = struct {...@@ -189,12 +189,15 @@ pub const File = struct {
189pub const HashHelper = struct {189pub const HashHelper = struct {
190 hasher: Hasher = hasher_init,190 hasher: Hasher = hasher_init,
191191
192 /// Record a slice of bytes as a dependency of the process being cached.
193 pub fn addBytes(hh: *HashHelper, bytes: []const u8) void {192 pub fn addBytes(hh: *HashHelper, bytes: []const u8) void {
194 hh.hasher.update(mem.asBytes(&bytes.len));193 hh.hasher.update(mem.asBytes(&bytes.len));
195 hh.hasher.update(bytes);194 hh.hasher.update(bytes);
196 }195 }
197196
197 pub fn addBytesZ(hh: *HashHelper, bytes: [:0]const u8) void {
198 hh.hasher.update(mem.absorbSentinel(u8, 0, bytes));
199 }
200
198 pub fn addOptionalBytes(hh: *HashHelper, optional_bytes: ?[]const u8) void {201 pub fn addOptionalBytes(hh: *HashHelper, optional_bytes: ?[]const u8) void {
199 hh.add(optional_bytes != null);202 hh.add(optional_bytes != null);
200 hh.addBytes(optional_bytes orelse return);203 hh.addBytes(optional_bytes orelse return);
lib/std/Build/Configuration.zig+2
...@@ -561,8 +561,10 @@ pub const Step = extern struct {...@@ -561,8 +561,10 @@ pub const Step = extern struct {
561561
562 pub const Tag = enum(u3) {562 pub const Tag = enum(u3) {
563 artifact,563 artifact,
564 /// `path` contains the file.
564 path_file,565 path_file,
565 path_directory,566 path_directory,
567 /// `prefix` contains the string.
566 string,568 string,
567 file_content,569 file_content,
568 output_file,570 output_file,