authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-23 15:34:31-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:36-07:00
log642d017fea531d1347b8ade16c33065389a20192
tree0787d894331e74f60c0352c02ce7f629aa6a3dd4
parent9b6dd7ee5c4137942f7fbbdd27d8e06f6ce7c7b1

Maker: fix resolveLazyPath accidental mutation


4 files changed, 35 insertions(+), 32 deletions(-)

lib/compiler/Maker.zig+4-2
......@@ -1708,12 +1708,14 @@ pub fn resolveLazyPath(
17081708 .source_path => |sp| try packagePath(maker, arena, sp.owner, sp.sub_path.slice(c)),
17091709 .relative => |relative| relativePath(maker, relative),
17101710 .generated => |gen| {
1711 const base = generatedPath(maker, gen.index);
1711 const base = generatedPath(maker, gen.index).*;
17121712 var file_path = base;
17131713 for (0..gen.flags.up) |_| {
17141714 file_path.sub_path = Dir.path.dirname(file_path.sub_path) orelse {
17151715 const s = stepByIndex(maker, asking_step_index);
1716 return s.fail(maker, "invalid LazyPath traversal: up {d} times from {f}", .{ gen.flags.up, base });
1716 return s.fail(maker, "invalid LazyPath traversal: up {d} times from {f}", .{
1717 gen.flags.up, base,
1718 });
17171719 };
17181720 }
17191721 return file_path.join(arena, gen.sub_path.slice(c));
lib/std/Build/Step/Run.zig+16-10
......@@ -242,21 +242,23 @@ pub fn addPrefixedArtifactArg(run: *Run, prefix: []const u8, artifact: *Step.Com
242242/// Returns a `std.Build.LazyPath` which can be used as inputs to other APIs
243243/// throughout the build system.
244244///
245/// `sub_path` is the name of the generated output file which may have zero or
246/// more path components.
247///
245248/// Related:
246249/// * `addPrefixedOutputFileArg` - same thing but prepends a string to the argument
247250/// * `addFileArg` - for input files given to the child process
248pub fn addOutputFileArg(run: *Run, basename: []const u8) std.Build.LazyPath {
249 return run.addPrefixedOutputFileArg("", basename);
251pub fn addOutputFileArg(run: *Run, sub_path: []const u8) std.Build.LazyPath {
252 return run.addPrefixedOutputFileArg("", sub_path);
250253}
251254
252255/// Provides a file path as a command line argument to the command being run.
253/// Asserts `basename` is not empty.
254256///
255/// For example, a prefix of "-o" and basename of "output.txt" will result in
257/// For example, a prefix of "-o" and `sub_path` of "output.txt" will result in
256258/// the child process seeing something like this: "-ozig-cache/.../output.txt"
257259///
258260/// The child process will see a single argument, regardless of whether the
259/// prefix or basename have spaces.
261/// prefix or `sub_path` have spaces.
260262///
261263/// The returned `std.Build.LazyPath` can be used as inputs to other APIs
262264/// throughout the build system.
......@@ -267,23 +269,27 @@ pub fn addOutputFileArg(run: *Run, basename: []const u8) std.Build.LazyPath {
267269pub fn addPrefixedOutputFileArg(
268270 run: *Run,
269271 prefix: []const u8,
270 basename: []const u8,
272 /// The name of the generated output file which may have zero or more path
273 /// components.
274 ///
275 /// Asserted to be non-empty.
276 sub_path: []const u8,
271277) std.Build.LazyPath {
272278 const b = run.step.owner;
273279 const graph = b.graph;
274280 const arena = graph.arena;
275 if (basename.len == 0) @panic("basename must not be empty");
281 assert(sub_path.len != 0);
276282
277 const output = arena.create(Output) catch @panic("OOM");
283 const output = graph.create(Output);
278284 output.* = .{
279285 .prefix = graph.dupeString(prefix),
280 .basename = graph.dupeString(basename),
286 .basename = graph.dupeString(sub_path),
281287 .generated_file = graph.addGeneratedFile(&run.step),
282288 };
283289 run.argv.append(arena, .{ .output_file = output }) catch @panic("OOM");
284290
285291 if (run.rename_step_with_output_arg) {
286 run.setName(b.fmt("{s} ({s})", .{ run.step.name, basename }));
292 run.setName(b.fmt("{s} ({s})", .{ run.step.name, sub_path }));
287293 }
288294
289295 return .{ .generated = .{ .index = output.generated_file } };
test/standalone/dirname/build.zig+6-10
......@@ -27,22 +27,16 @@ pub fn build(b: *std.Build) void {
2727 }),
2828 });
2929
30 // Known path:
31 addTestRun(test_step, exists_in, touch_src.dirname(), &.{"touch.zig"});
32
33 // Generated file:
34 addTestRun(test_step, exists_in, generated.dirname(), &.{"generated.txt"});
35
36 // Generated file multiple levels:
37 addTestRun(test_step, exists_in, generated.dirname().dirname(), &.{
30 addTestRun(test_step, exists_in, "run exists_in (known path)", touch_src.dirname(), &.{"touch.zig"});
31 addTestRun(test_step, exists_in, "run exists_in (generated file)", generated.dirname(), &.{"generated.txt"});
32 addTestRun(test_step, exists_in, "run exists_in (generated file multi level)", generated.dirname().dirname(), &.{
3833 "subdir" ++ std.fs.path.sep_str ++ "generated.txt",
3934 });
4035
41 // Absolute path:
4236 const write_files = b.addWriteFiles();
4337 _ = write_files.add("foo.txt", "");
4438 const abs_path = write_files.getDirectory();
45 addTestRun(test_step, exists_in, abs_path, &.{"foo.txt"});
39 addTestRun(test_step, exists_in, "run exists_in (absolute path)", abs_path, &.{"foo.txt"});
4640}
4741
4842// Runs exe with the parameters [dirname, args...].
......@@ -50,10 +44,12 @@ pub fn build(b: *std.Build) void {
5044fn addTestRun(
5145 test_step: *std.Build.Step,
5246 exe: *std.Build.Step.Compile,
47 step_name: []const u8,
5348 dirname: std.Build.LazyPath,
5449 args: []const []const u8,
5550) void {
5651 const run = test_step.owner.addRunArtifact(exe);
52 run.setName(step_name);
5753 run.addDirectoryArg(dirname);
5854 run.addArgs(args);
5955 run.expectExitCode(0);
test/standalone/dirname/touch.zig+9-10
......@@ -7,27 +7,26 @@
77//! Path must be absolute.
88
99const std = @import("std");
10const Io = std.Io;
1011
1112pub fn main(init: std.process.Init) !void {
13 const io = init.io;
14
1215 var args = try init.minimal.args.iterateAllocator(init.gpa);
1316 defer args.deinit();
14 _ = args.next() orelse unreachable; // skip binary name
17 _ = args.next().?; // skip binary name
1518
1619 const path = args.next() orelse {
1720 std.log.err("missing <path> argument", .{});
1821 return error.BadUsage;
1922 };
2023
21 const dir_path = std.Io.Dir.path.dirname(path) orelse unreachable;
22 const basename = std.Io.Dir.path.basename(path);
23
24 const io = std.Io.Threaded.global_single_threaded.io();
24 const dir_path = Io.Dir.path.dirname(path).?;
25 const basename = Io.Dir.path.basename(path);
2526
26 var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{});
27 var dir = try Io.Dir.cwd().openDir(io, dir_path, .{});
2728 defer dir.close(io);
2829
29 _ = dir.statFile(io, basename, .{}) catch {
30 var file = try dir.createFile(io, basename, .{});
31 file.close(io);
32 };
30 var file = try dir.createFile(io, basename, .{ .truncate = false });
31 file.close(io);
3332}