authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-06-15 15:01:06+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-06-15 15:01:06+02:00
log1ca213dab02db42c63a6b7e191044c0124f9bfb2
tree0d7c42e0ab9f64927c62648a5162847762a201ea
parent878b7b80c15d4b9a894f6ab1d4e8753de77568ad
parent14e033ed95a43a5ff994df9dc524e5bc4f34452a
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #24168 from mlugg/relative-paths

std.Build: fix relative path bugs

6 files changed, 53 insertions(+), 41 deletions(-)

lib/std/Build/Step/Run.zig+46-22
...@@ -622,6 +622,18 @@ fn checksContainStderr(checks: []const StdIo.Check) bool {...@@ -622,6 +622,18 @@ fn checksContainStderr(checks: []const StdIo.Check) bool {
622 return false;622 return false;
623}623}
624624
625/// If `path` is cwd-relative, make it relative to the cwd of the child instead.
626///
627/// Whenever a path is included in the argv of a child, it should be put through this function first
628/// to make sure the child doesn't see paths relative to a cwd other than its own.
629fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 {
630 const b = run.step.owner;
631 const path_str = path.toString(b.graph.arena) catch @panic("OOM");
632 const child_lazy_cwd = run.cwd orelse return path_str;
633 const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(b.graph.arena) catch @panic("OOM");
634 return std.fs.path.relative(b.graph.arena, child_cwd, path_str) catch @panic("OOM");
635}
636
625const IndexedOutput = struct {637const IndexedOutput = struct {
626 index: usize,638 index: usize,
627 tag: @typeInfo(Arg).@"union".tag_type.?,639 tag: @typeInfo(Arg).@"union".tag_type.?,
...@@ -676,14 +688,14 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -676,14 +688,14 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
676 man.hash.addBytes(bytes);688 man.hash.addBytes(bytes);
677 },689 },
678 .lazy_path => |file| {690 .lazy_path => |file| {
679 const file_path = file.lazy_path.getPath2(b, step);691 const file_path = file.lazy_path.getPath3(b, step);
680 try argv_list.append(b.fmt("{s}{s}", .{ file.prefix, file_path }));692 try argv_list.append(b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(file_path) }));
681 man.hash.addBytes(file.prefix);693 man.hash.addBytes(file.prefix);
682 _ = try man.addFile(file_path, null);694 _ = try man.addFilePath(file_path, null);
683 },695 },
684 .decorated_directory => |dd| {696 .decorated_directory => |dd| {
685 const file_path = dd.lazy_path.getPath3(b, step);697 const file_path = dd.lazy_path.getPath3(b, step);
686 const resolved_arg = b.fmt("{s}{}{s}", .{ dd.prefix, file_path, dd.suffix });698 const resolved_arg = b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(file_path), dd.suffix });
687 try argv_list.append(resolved_arg);699 try argv_list.append(resolved_arg);
688 man.hash.addBytes(resolved_arg);700 man.hash.addBytes(resolved_arg);
689 },701 },
...@@ -696,7 +708,10 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -696,7 +708,10 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
696 }708 }
697 const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?;709 const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?;
698710
699 try argv_list.append(b.fmt("{s}{s}", .{ pa.prefix, file_path }));711 try argv_list.append(b.fmt("{s}{s}", .{
712 pa.prefix,
713 run.convertPathArg(.{ .root_dir = .cwd(), .sub_path = file_path }),
714 }));
700715
701 _ = try man.addFile(file_path, null);716 _ = try man.addFile(file_path, null);
702 },717 },
...@@ -787,11 +802,14 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -787,11 +802,14 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
787 b.cache_root, output_sub_dir_path, @errorName(err),802 b.cache_root, output_sub_dir_path, @errorName(err),
788 });803 });
789 };804 };
790 const output_path = placeholder.output.generated_file.path.?;805 const arg_output_path = run.convertPathArg(.{
806 .root_dir = .cwd(),
807 .sub_path = placeholder.output.generated_file.getPath(),
808 });
791 argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)809 argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)
792 output_path810 arg_output_path
793 else811 else
794 b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path });812 b.fmt("{s}{s}", .{ placeholder.output.prefix, arg_output_path });
795 }813 }
796814
797 try runCommand(run, argv_list.items, has_side_effects, output_dir_path, prog_node, null);815 try runCommand(run, argv_list.items, has_side_effects, output_dir_path, prog_node, null);
...@@ -816,12 +834,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -816,12 +834,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
816 b.cache_root, output_sub_dir_path, @errorName(err),834 b.cache_root, output_sub_dir_path, @errorName(err),
817 });835 });
818 };836 };
819 const output_path = try b.cache_root.join(arena, &output_components);837 const raw_output_path: Build.Cache.Path = .{
820 placeholder.output.generated_file.path = output_path;838 .root_dir = b.cache_root,
821 argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)839 .sub_path = b.pathJoin(&output_components),
822 output_path840 };
823 else841 placeholder.output.generated_file.path = raw_output_path.toString(b.graph.arena) catch @panic("OOM");
824 b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path });842 argv_list.items[placeholder.index] = b.fmt("{s}{s}", .{
843 placeholder.output.prefix,
844 run.convertPathArg(raw_output_path),
845 });
825 }846 }
826847
827 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, null);848 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, null);
...@@ -899,20 +920,23 @@ pub fn rerunInFuzzMode(...@@ -899,20 +920,23 @@ pub fn rerunInFuzzMode(
899 try argv_list.append(arena, bytes);920 try argv_list.append(arena, bytes);
900 },921 },
901 .lazy_path => |file| {922 .lazy_path => |file| {
902 const file_path = file.lazy_path.getPath2(b, step);923 const file_path = file.lazy_path.getPath3(b, step);
903 try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, file_path }));924 try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(file_path) }));
904 },925 },
905 .decorated_directory => |dd| {926 .decorated_directory => |dd| {
906 const file_path = dd.lazy_path.getPath3(b, step);927 const file_path = dd.lazy_path.getPath3(b, step);
907 try argv_list.append(arena, b.fmt("{s}{}{s}", .{ dd.prefix, file_path, dd.suffix }));928 try argv_list.append(arena, b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(file_path), dd.suffix }));
908 },929 },
909 .artifact => |pa| {930 .artifact => |pa| {
910 const artifact = pa.artifact;931 const artifact = pa.artifact;
911 const file_path = if (artifact == run.producer.?)932 const file_path: []const u8 = p: {
912 b.fmt("{}", .{run.rebuilt_executable.?})933 if (artifact == run.producer.?) break :p b.fmt("{}", .{run.rebuilt_executable.?});
913 else934 break :p artifact.installed_path orelse artifact.generated_bin.?.path.?;
914 (artifact.installed_path orelse artifact.generated_bin.?.path.?);935 };
915 try argv_list.append(arena, b.fmt("{s}{s}", .{ pa.prefix, file_path }));936 try argv_list.append(arena, b.fmt("{s}{s}", .{
937 pa.prefix,
938 run.convertPathArg(.{ .root_dir = .cwd(), .sub_path = file_path }),
939 }));
916 },940 },
917 .output_file, .output_directory => unreachable,941 .output_file, .output_directory => unreachable,
918 }942 }
test/standalone/dirname/exists_in.zig+1-6
...@@ -29,17 +29,12 @@ fn run(allocator: std.mem.Allocator) !void {...@@ -29,17 +29,12 @@ fn run(allocator: std.mem.Allocator) !void {
29 return error.BadUsage;29 return error.BadUsage;
30 };30 };
3131
32 if (!std.fs.path.isAbsolute(dir_path)) {
33 std.log.err("expected <dir> to be an absolute path", .{});
34 return error.BadUsage;
35 }
36
37 const relpath = args.next() orelse {32 const relpath = args.next() orelse {
38 std.log.err("missing <path> argument", .{});33 std.log.err("missing <path> argument", .{});
39 return error.BadUsage;34 return error.BadUsage;
40 };35 };
4136
42 var dir = try std.fs.openDirAbsolute(dir_path, .{});37 var dir = try std.fs.cwd().openDir(dir_path, .{});
43 defer dir.close();38 defer dir.close();
4439
45 _ = try dir.statFile(relpath);40 _ = try dir.statFile(relpath);
test/standalone/dirname/has_basename.zig-5
...@@ -31,11 +31,6 @@ fn run(allocator: std.mem.Allocator) !void {...@@ -31,11 +31,6 @@ fn run(allocator: std.mem.Allocator) !void {
31 return error.BadUsage;31 return error.BadUsage;
32 };32 };
3333
34 if (!std.fs.path.isAbsolute(path)) {
35 std.log.err("path must be absolute", .{});
36 return error.BadUsage;
37 }
38
39 const basename = args.next() orelse {34 const basename = args.next() orelse {
40 std.log.err("missing <basename> argument", .{});35 std.log.err("missing <basename> argument", .{});
41 return error.BadUsage;36 return error.BadUsage;
test/standalone/dirname/touch.zig+1-6
...@@ -26,15 +26,10 @@ fn run(allocator: std.mem.Allocator) !void {...@@ -26,15 +26,10 @@ fn run(allocator: std.mem.Allocator) !void {
26 return error.BadUsage;26 return error.BadUsage;
27 };27 };
2828
29 if (!std.fs.path.isAbsolute(path)) {
30 std.log.err("path must be absolute: {s}", .{path});
31 return error.BadUsage;
32 }
33
34 const dir_path = std.fs.path.dirname(path) orelse unreachable;29 const dir_path = std.fs.path.dirname(path) orelse unreachable;
35 const basename = std.fs.path.basename(path);30 const basename = std.fs.path.basename(path);
3631
37 var dir = try std.fs.openDirAbsolute(dir_path, .{});32 var dir = try std.fs.cwd().openDir(dir_path, .{});
38 defer dir.close();33 defer dir.close();
3934
40 _ = dir.statFile(basename) catch {35 _ = dir.statFile(basename) catch {
test/standalone/run_output_caching/main.zig+1-1
...@@ -4,7 +4,7 @@ pub fn main() !void {...@@ -4,7 +4,7 @@ pub fn main() !void {
4 var args = try std.process.argsWithAllocator(std.heap.page_allocator);4 var args = try std.process.argsWithAllocator(std.heap.page_allocator);
5 _ = args.skip();5 _ = args.skip();
6 const filename = args.next().?;6 const filename = args.next().?;
7 const file = try std.fs.createFileAbsolute(filename, .{});7 const file = try std.fs.cwd().createFile(filename, .{});
8 defer file.close();8 defer file.close();
9 try file.writeAll(filename);9 try file.writeAll(filename);
10}10}
test/standalone/self_exe_symlink/create-symlink.zig+4-1
...@@ -11,5 +11,8 @@ pub fn main() anyerror!void {...@@ -11,5 +11,8 @@ pub fn main() anyerror!void {
11 const exe_path = it.next() orelse unreachable;11 const exe_path = it.next() orelse unreachable;
12 const symlink_path = it.next() orelse unreachable;12 const symlink_path = it.next() orelse unreachable;
1313
14 try std.fs.cwd().symLink(exe_path, symlink_path, .{});14 // If `exe_path` is relative to our cwd, we need to convert it to be relative to the dirname of `symlink_path`.
15 const exe_rel_path = try std.fs.path.relative(allocator, std.fs.path.dirname(symlink_path) orelse ".", exe_path);
16 defer allocator.free(exe_rel_path);
17 try std.fs.cwd().symLink(exe_rel_path, symlink_path, .{});
15}18}