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(...@@ -1708,12 +1708,14 @@ pub fn resolveLazyPath(
1708 .source_path => |sp| try packagePath(maker, arena, sp.owner, sp.sub_path.slice(c)),1708 .source_path => |sp| try packagePath(maker, arena, sp.owner, sp.sub_path.slice(c)),
1709 .relative => |relative| relativePath(maker, relative),1709 .relative => |relative| relativePath(maker, relative),
1710 .generated => |gen| {1710 .generated => |gen| {
1711 const base = generatedPath(maker, gen.index);1711 const base = generatedPath(maker, gen.index).*;
1712 var file_path = base;1712 var file_path = base;
1713 for (0..gen.flags.up) |_| {1713 for (0..gen.flags.up) |_| {
1714 file_path.sub_path = Dir.path.dirname(file_path.sub_path) orelse {1714 file_path.sub_path = Dir.path.dirname(file_path.sub_path) orelse {
1715 const s = stepByIndex(maker, asking_step_index);1715 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 });
1717 };1719 };
1718 }1720 }
1719 return file_path.join(arena, gen.sub_path.slice(c));1721 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...@@ -242,21 +242,23 @@ pub fn addPrefixedArtifactArg(run: *Run, prefix: []const u8, artifact: *Step.Com
242/// Returns a `std.Build.LazyPath` which can be used as inputs to other APIs242/// Returns a `std.Build.LazyPath` which can be used as inputs to other APIs
243/// throughout the build system.243/// throughout the build system.
244///244///
245/// `sub_path` is the name of the generated output file which may have zero or
246/// more path components.
247///
245/// Related:248/// Related:
246/// * `addPrefixedOutputFileArg` - same thing but prepends a string to the argument249/// * `addPrefixedOutputFileArg` - same thing but prepends a string to the argument
247/// * `addFileArg` - for input files given to the child process250/// * `addFileArg` - for input files given to the child process
248pub fn addOutputFileArg(run: *Run, basename: []const u8) std.Build.LazyPath {251pub fn addOutputFileArg(run: *Run, sub_path: []const u8) std.Build.LazyPath {
249 return run.addPrefixedOutputFileArg("", basename);252 return run.addPrefixedOutputFileArg("", sub_path);
250}253}
251254
252/// Provides a file path as a command line argument to the command being run.255/// Provides a file path as a command line argument to the command being run.
253/// Asserts `basename` is not empty.
254///256///
255/// For example, a prefix of "-o" and basename of "output.txt" will result in257/// For example, a prefix of "-o" and `sub_path` of "output.txt" will result in
256/// the child process seeing something like this: "-ozig-cache/.../output.txt"258/// the child process seeing something like this: "-ozig-cache/.../output.txt"
257///259///
258/// The child process will see a single argument, regardless of whether the260/// 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.
260///262///
261/// The returned `std.Build.LazyPath` can be used as inputs to other APIs263/// The returned `std.Build.LazyPath` can be used as inputs to other APIs
262/// throughout the build system.264/// throughout the build system.
...@@ -267,23 +269,27 @@ pub fn addOutputFileArg(run: *Run, basename: []const u8) std.Build.LazyPath {...@@ -267,23 +269,27 @@ pub fn addOutputFileArg(run: *Run, basename: []const u8) std.Build.LazyPath {
267pub fn addPrefixedOutputFileArg(269pub fn addPrefixedOutputFileArg(
268 run: *Run,270 run: *Run,
269 prefix: []const u8,271 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,
271) std.Build.LazyPath {277) std.Build.LazyPath {
272 const b = run.step.owner;278 const b = run.step.owner;
273 const graph = b.graph;279 const graph = b.graph;
274 const arena = graph.arena;280 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);
278 output.* = .{284 output.* = .{
279 .prefix = graph.dupeString(prefix),285 .prefix = graph.dupeString(prefix),
280 .basename = graph.dupeString(basename),286 .basename = graph.dupeString(sub_path),
281 .generated_file = graph.addGeneratedFile(&run.step),287 .generated_file = graph.addGeneratedFile(&run.step),
282 };288 };
283 run.argv.append(arena, .{ .output_file = output }) catch @panic("OOM");289 run.argv.append(arena, .{ .output_file = output }) catch @panic("OOM");
284290
285 if (run.rename_step_with_output_arg) {291 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 }));
287 }293 }
288294
289 return .{ .generated = .{ .index = output.generated_file } };295 return .{ .generated = .{ .index = output.generated_file } };
test/standalone/dirname/build.zig+6-10
...@@ -27,22 +27,16 @@ pub fn build(b: *std.Build) void {...@@ -27,22 +27,16 @@ pub fn build(b: *std.Build) void {
27 }),27 }),
28 });28 });
2929
30 // Known path:30 addTestRun(test_step, exists_in, "run exists_in (known path)", touch_src.dirname(), &.{"touch.zig"});
31 addTestRun(test_step, exists_in, touch_src.dirname(), &.{"touch.zig"});31 addTestRun(test_step, exists_in, "run exists_in (generated file)", generated.dirname(), &.{"generated.txt"});
3232 addTestRun(test_step, exists_in, "run exists_in (generated file multi level)", generated.dirname().dirname(), &.{
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(), &.{
38 "subdir" ++ std.fs.path.sep_str ++ "generated.txt",33 "subdir" ++ std.fs.path.sep_str ++ "generated.txt",
39 });34 });
4035
41 // Absolute path:
42 const write_files = b.addWriteFiles();36 const write_files = b.addWriteFiles();
43 _ = write_files.add("foo.txt", "");37 _ = write_files.add("foo.txt", "");
44 const abs_path = write_files.getDirectory();38 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"});
46}40}
4741
48// Runs exe with the parameters [dirname, args...].42// Runs exe with the parameters [dirname, args...].
...@@ -50,10 +44,12 @@ pub fn build(b: *std.Build) void {...@@ -50,10 +44,12 @@ pub fn build(b: *std.Build) void {
50fn addTestRun(44fn addTestRun(
51 test_step: *std.Build.Step,45 test_step: *std.Build.Step,
52 exe: *std.Build.Step.Compile,46 exe: *std.Build.Step.Compile,
47 step_name: []const u8,
53 dirname: std.Build.LazyPath,48 dirname: std.Build.LazyPath,
54 args: []const []const u8,49 args: []const []const u8,
55) void {50) void {
56 const run = test_step.owner.addRunArtifact(exe);51 const run = test_step.owner.addRunArtifact(exe);
52 run.setName(step_name);
57 run.addDirectoryArg(dirname);53 run.addDirectoryArg(dirname);
58 run.addArgs(args);54 run.addArgs(args);
59 run.expectExitCode(0);55 run.expectExitCode(0);
test/standalone/dirname/touch.zig+9-10
...@@ -7,27 +7,26 @@...@@ -7,27 +7,26 @@
7//! Path must be absolute.7//! Path must be absolute.
88
9const std = @import("std");9const std = @import("std");
10const Io = std.Io;
1011
11pub fn main(init: std.process.Init) !void {12pub fn main(init: std.process.Init) !void {
13 const io = init.io;
14
12 var args = try init.minimal.args.iterateAllocator(init.gpa);15 var args = try init.minimal.args.iterateAllocator(init.gpa);
13 defer args.deinit();16 defer args.deinit();
14 _ = args.next() orelse unreachable; // skip binary name17 _ = args.next().?; // skip binary name
1518
16 const path = args.next() orelse {19 const path = args.next() orelse {
17 std.log.err("missing <path> argument", .{});20 std.log.err("missing <path> argument", .{});
18 return error.BadUsage;21 return error.BadUsage;
19 };22 };
2023
21 const dir_path = std.Io.Dir.path.dirname(path) orelse unreachable;24 const dir_path = Io.Dir.path.dirname(path).?;
22 const basename = std.Io.Dir.path.basename(path);25 const basename = Io.Dir.path.basename(path);
23
24 const io = std.Io.Threaded.global_single_threaded.io();
2526
26 var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{});27 var dir = try Io.Dir.cwd().openDir(io, dir_path, .{});
27 defer dir.close(io);28 defer dir.close(io);
2829
29 _ = dir.statFile(io, basename, .{}) catch {30 var file = try dir.createFile(io, basename, .{ .truncate = false });
30 var file = try dir.createFile(io, basename, .{});31 file.close(io);
31 file.close(io);
32 };
33}32}