Skip to content

std.Build: fix relative path bugs #24168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 46 additions & 22 deletions lib/std/Build/Step/Run.zig
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,18 @@ fn checksContainStderr(checks: []const StdIo.Check) bool {
return false;
}

/// If `path` is cwd-relative, make it relative to the cwd of the child instead.
///
/// Whenever a path is included in the argv of a child, it should be put through this function first
/// to make sure the child doesn't see paths relative to a cwd other than its own.
fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 {
const b = run.step.owner;
const path_str = path.toString(b.graph.arena) catch @panic("OOM");
const child_lazy_cwd = run.cwd orelse return path_str;
const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(b.graph.arena) catch @panic("OOM");
return std.fs.path.relative(b.graph.arena, child_cwd, path_str) catch @panic("OOM");
}

const IndexedOutput = struct {
index: usize,
tag: @typeInfo(Arg).@"union".tag_type.?,
Expand Down Expand Up @@ -676,14 +688,14 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
man.hash.addBytes(bytes);
},
.lazy_path => |file| {
const file_path = file.lazy_path.getPath2(b, step);
try argv_list.append(b.fmt("{s}{s}", .{ file.prefix, file_path }));
const file_path = file.lazy_path.getPath3(b, step);
try argv_list.append(b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(file_path) }));
man.hash.addBytes(file.prefix);
_ = try man.addFile(file_path, null);
_ = try man.addFilePath(file_path, null);
},
.decorated_directory => |dd| {
const file_path = dd.lazy_path.getPath3(b, step);
const resolved_arg = b.fmt("{s}{}{s}", .{ dd.prefix, file_path, dd.suffix });
const resolved_arg = b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(file_path), dd.suffix });
try argv_list.append(resolved_arg);
man.hash.addBytes(resolved_arg);
},
Expand All @@ -696,7 +708,10 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
}
const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?;

try argv_list.append(b.fmt("{s}{s}", .{ pa.prefix, file_path }));
try argv_list.append(b.fmt("{s}{s}", .{
pa.prefix,
run.convertPathArg(.{ .root_dir = .cwd(), .sub_path = file_path }),
}));

_ = try man.addFile(file_path, null);
},
Expand Down Expand Up @@ -787,11 +802,14 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
b.cache_root, output_sub_dir_path, @errorName(err),
});
};
const output_path = placeholder.output.generated_file.path.?;
const arg_output_path = run.convertPathArg(.{
.root_dir = .cwd(),
.sub_path = placeholder.output.generated_file.getPath(),
});
argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)
output_path
arg_output_path
else
b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path });
b.fmt("{s}{s}", .{ placeholder.output.prefix, arg_output_path });
}

try runCommand(run, argv_list.items, has_side_effects, output_dir_path, prog_node, null);
Expand All @@ -816,12 +834,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
b.cache_root, output_sub_dir_path, @errorName(err),
});
};
const output_path = try b.cache_root.join(arena, &output_components);
placeholder.output.generated_file.path = output_path;
argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)
output_path
else
b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path });
const raw_output_path: Build.Cache.Path = .{
.root_dir = b.cache_root,
.sub_path = b.pathJoin(&output_components),
};
placeholder.output.generated_file.path = raw_output_path.toString(b.graph.arena) catch @panic("OOM");
argv_list.items[placeholder.index] = b.fmt("{s}{s}", .{
placeholder.output.prefix,
run.convertPathArg(raw_output_path),
});
}

try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, null);
Expand Down Expand Up @@ -899,20 +920,23 @@ pub fn rerunInFuzzMode(
try argv_list.append(arena, bytes);
},
.lazy_path => |file| {
const file_path = file.lazy_path.getPath2(b, step);
try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, file_path }));
const file_path = file.lazy_path.getPath3(b, step);
try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(file_path) }));
},
.decorated_directory => |dd| {
const file_path = dd.lazy_path.getPath3(b, step);
try argv_list.append(arena, b.fmt("{s}{}{s}", .{ dd.prefix, file_path, dd.suffix }));
try argv_list.append(arena, b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(file_path), dd.suffix }));
},
.artifact => |pa| {
const artifact = pa.artifact;
const file_path = if (artifact == run.producer.?)
b.fmt("{}", .{run.rebuilt_executable.?})
else
(artifact.installed_path orelse artifact.generated_bin.?.path.?);
try argv_list.append(arena, b.fmt("{s}{s}", .{ pa.prefix, file_path }));
const file_path: []const u8 = p: {
if (artifact == run.producer.?) break :p b.fmt("{}", .{run.rebuilt_executable.?});
break :p artifact.installed_path orelse artifact.generated_bin.?.path.?;
};
try argv_list.append(arena, b.fmt("{s}{s}", .{
pa.prefix,
run.convertPathArg(.{ .root_dir = .cwd(), .sub_path = file_path }),
}));
},
.output_file, .output_directory => unreachable,
}
Expand Down
7 changes: 1 addition & 6 deletions test/standalone/dirname/exists_in.zig
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,12 @@ fn run(allocator: std.mem.Allocator) !void {
return error.BadUsage;
};

if (!std.fs.path.isAbsolute(dir_path)) {
std.log.err("expected <dir> to be an absolute path", .{});
return error.BadUsage;
}

const relpath = args.next() orelse {
std.log.err("missing <path> argument", .{});
return error.BadUsage;
};

var dir = try std.fs.openDirAbsolute(dir_path, .{});
var dir = try std.fs.cwd().openDir(dir_path, .{});
defer dir.close();

_ = try dir.statFile(relpath);
Expand Down
5 changes: 0 additions & 5 deletions test/standalone/dirname/has_basename.zig
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ fn run(allocator: std.mem.Allocator) !void {
return error.BadUsage;
};

if (!std.fs.path.isAbsolute(path)) {
std.log.err("path must be absolute", .{});
return error.BadUsage;
}

const basename = args.next() orelse {
std.log.err("missing <basename> argument", .{});
return error.BadUsage;
Expand Down
7 changes: 1 addition & 6 deletions test/standalone/dirname/touch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,10 @@ fn run(allocator: std.mem.Allocator) !void {
return error.BadUsage;
};

if (!std.fs.path.isAbsolute(path)) {
std.log.err("path must be absolute: {s}", .{path});
return error.BadUsage;
}

const dir_path = std.fs.path.dirname(path) orelse unreachable;
const basename = std.fs.path.basename(path);

var dir = try std.fs.openDirAbsolute(dir_path, .{});
var dir = try std.fs.cwd().openDir(dir_path, .{});
defer dir.close();

_ = dir.statFile(basename) catch {
Expand Down
2 changes: 1 addition & 1 deletion test/standalone/run_output_caching/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub fn main() !void {
var args = try std.process.argsWithAllocator(std.heap.page_allocator);
_ = args.skip();
const filename = args.next().?;
const file = try std.fs.createFileAbsolute(filename, .{});
const file = try std.fs.cwd().createFile(filename, .{});
defer file.close();
try file.writeAll(filename);
}
5 changes: 4 additions & 1 deletion test/standalone/self_exe_symlink/create-symlink.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@ pub fn main() anyerror!void {
const exe_path = it.next() orelse unreachable;
const symlink_path = it.next() orelse unreachable;

try std.fs.cwd().symLink(exe_path, symlink_path, .{});
// If `exe_path` is relative to our cwd, we need to convert it to be relative to the dirname of `symlink_path`.
const exe_rel_path = try std.fs.path.relative(allocator, std.fs.path.dirname(symlink_path) orelse ".", exe_path);
defer allocator.free(exe_rel_path);
try std.fs.cwd().symLink(exe_rel_path, symlink_path, .{});
}