authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-09 18:23:48-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-10 15:02:20-07:00
log7fb5a0b18b8620750f71d24152dc6651a0437ae1
tree855688e466a9abb007605cd8e516ac229f74dd3a
parentc4587dc9f46e15d4fb875a7675bc1aa22138c1ab

introduce std.Build.path; deprecate LazyPath.relative

This adds the *std.Build owner to LazyPath so that lazy paths returned from a dependency can be used in the application without friction or footguns. closes #19313

5 files changed, 68 insertions(+), 42 deletions(-)

lib/std/Build.zig+56-36
......@@ -607,17 +607,17 @@ pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list:
607607 var h_list = [_][]const u8{ self.install_path, "include" };
608608
609609 if (dir_list.lib_dir) |dir| {
610 if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
610 if (fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
611611 lib_list[1] = dir;
612612 }
613613
614614 if (dir_list.exe_dir) |dir| {
615 if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
615 if (fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
616616 exe_list[1] = dir;
617617 }
618618
619619 if (dir_list.include_dir) |dir| {
620 if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
620 if (fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
621621 h_list[1] = dir;
622622 }
623623
......@@ -858,7 +858,7 @@ pub const TestOptions = struct {
858858 /// deprecated: use `.filters = &.{filter}` instead of `.filter = filter`.
859859 filter: ?[]const u8 = null,
860860 filters: []const []const u8 = &.{},
861 test_runner: ?[]const u8 = null,
861 test_runner: ?LazyPath = null,
862862 link_libc: ?bool = null,
863863 single_threaded: ?bool = null,
864864 pic: ?bool = null,
......@@ -1635,6 +1635,18 @@ pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
16351635 src_file.close();
16361636}
16371637
1638/// References a file or directory relative to the source root.
1639pub fn path(b: *Build, sub_path: []const u8) LazyPath {
1640 assert(!fs.path.isAbsolute(sub_path));
1641 return .{ .src_path = .{
1642 .owner = b,
1643 .sub_path = sub_path,
1644 } };
1645}
1646
1647/// This is low-level implementation details of the build system, not meant to
1648/// be called by users' build scripts. Even in the build system itself it is a
1649/// code smell to call this function.
16381650pub fn pathFromRoot(b: *Build, p: []const u8) []u8 {
16391651 return fs.path.resolve(b.allocator, &.{ b.build_root.path orelse ".", p }) catch @panic("OOM");
16401652}
......@@ -1674,10 +1686,9 @@ pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []con
16741686 return name;
16751687 }
16761688 var it = mem.tokenizeScalar(u8, PATH, fs.path.delimiter);
1677 while (it.next()) |path| {
1689 while (it.next()) |p| {
16781690 const full_path = self.pathJoin(&.{
1679 path,
1680 self.fmt("{s}{s}", .{ name, exe_extension }),
1691 p, self.fmt("{s}{s}", .{ name, exe_extension }),
16811692 });
16821693 return fs.realpathAlloc(self.allocator, full_path) catch continue;
16831694 }
......@@ -1687,10 +1698,9 @@ pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []con
16871698 if (fs.path.isAbsolute(name)) {
16881699 return name;
16891700 }
1690 for (paths) |path| {
1701 for (paths) |p| {
16911702 const full_path = self.pathJoin(&.{
1692 path,
1693 self.fmt("{s}{s}", .{ name, exe_extension }),
1703 p, self.fmt("{s}{s}", .{ name, exe_extension }),
16941704 });
16951705 return fs.realpathAlloc(self.allocator, full_path) catch continue;
16961706 }
......@@ -1771,7 +1781,7 @@ pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8)
17711781 .bin => self.exe_dir,
17721782 .lib => self.lib_dir,
17731783 .header => self.h_dir,
1774 .custom => |path| self.pathJoin(&.{ self.install_path, path }),
1784 .custom => |p| self.pathJoin(&.{ self.install_path, p }),
17751785 };
17761786 return fs.path.resolve(
17771787 self.allocator,
......@@ -2032,7 +2042,7 @@ fn dependencyInner(
20322042
20332043 const build_root: std.Build.Cache.Directory = .{
20342044 .path = build_root_string,
2035 .handle = std.fs.cwd().openDir(build_root_string, .{}) catch |err| {
2045 .handle = fs.cwd().openDir(build_root_string, .{}) catch |err| {
20362046 std.debug.print("unable to open '{s}': {s}\n", .{
20372047 build_root_string, @errorName(err),
20382048 });
......@@ -2093,9 +2103,9 @@ pub const GeneratedFile = struct {
20932103// so that we can join it with another path (e.g. build root, cache root, etc.)
20942104//
20952105// dirname("") should still be null, because we can't go up any further.
2096fn dirnameAllowEmpty(path: []const u8) ?[]const u8 {
2097 return fs.path.dirname(path) orelse {
2098 if (fs.path.isAbsolute(path) or path.len == 0) return null;
2106fn dirnameAllowEmpty(full_path: []const u8) ?[]const u8 {
2107 return fs.path.dirname(full_path) orelse {
2108 if (fs.path.isAbsolute(full_path) or full_path.len == 0) return null;
20992109
21002110 return "";
21012111 };
......@@ -2117,11 +2127,15 @@ test dirnameAllowEmpty {
21172127
21182128/// A reference to an existing or future path.
21192129pub const LazyPath = union(enum) {
2120 /// A source file path relative to build root.
2121 /// This should not be an absolute path, but in an older iteration of the zig build
2122 /// system API, it was allowed to be absolute. Absolute paths should use `cwd_relative`.
2130 /// Deprecated; use the `path` function instead.
21232131 path: []const u8,
21242132
2133 /// A source file path relative to build root.
2134 src_path: struct {
2135 owner: *std.Build,
2136 sub_path: []const u8,
2137 },
2138
21252139 /// A file that is generated by an interface. Those files usually are
21262140 /// not available until built by a build step.
21272141 generated: *const GeneratedFile,
......@@ -2150,11 +2164,10 @@ pub const LazyPath = union(enum) {
21502164 sub_path: []const u8,
21512165 },
21522166
2153 /// Returns a new file source that will have a relative path to the build root guaranteed.
2154 /// Asserts the parameter is not an absolute path.
2155 pub fn relative(path: []const u8) LazyPath {
2156 std.debug.assert(!std.fs.path.isAbsolute(path));
2157 return LazyPath{ .path = path };
2167 /// Deprecated. Call `path` instead.
2168 pub fn relative(p: []const u8) LazyPath {
2169 std.log.warn("deprecated. call std.Build.path instead", .{});
2170 return .{ .path = p };
21582171 }
21592172
21602173 /// Returns a lazy path referring to the directory containing this path.
......@@ -2168,13 +2181,16 @@ pub const LazyPath = union(enum) {
21682181 return switch (self) {
21692182 .generated => |gen| .{ .generated_dirname = .{ .generated = gen, .up = 0 } },
21702183 .generated_dirname => |gen| .{ .generated_dirname = .{ .generated = gen.generated, .up = gen.up + 1 } },
2184 .src_path => |sp| .{ .src_path = .{
2185 .owner = sp.owner,
2186 .sub_path = dirnameAllowEmpty(sp.sub_path) orelse {
2187 dumpBadDirnameHelp(null, null, "dirname() attempted to traverse outside the build root\n", .{}) catch {};
2188 @panic("misconfigured build script");
2189 },
2190 } },
21712191 .path => |p| .{
21722192 .path = dirnameAllowEmpty(p) orelse {
2173 dumpBadDirnameHelp(null, null,
2174 \\dirname() attempted to traverse outside the build root.
2175 \\This is not allowed.
2176 \\
2177 , .{}) catch {};
2193 dumpBadDirnameHelp(null, null, "dirname() attempted to traverse outside the build root\n", .{}) catch {};
21782194 @panic("misconfigured build script");
21792195 },
21802196 },
......@@ -2195,7 +2211,6 @@ pub const LazyPath = union(enum) {
21952211 } else {
21962212 dumpBadDirnameHelp(null, null,
21972213 \\dirname() attempted to traverse outside the current working directory.
2198 \\This is not allowed.
21992214 \\
22002215 , .{}) catch {};
22012216 @panic("misconfigured build script");
......@@ -2207,7 +2222,6 @@ pub const LazyPath = union(enum) {
22072222 .sub_path = dirnameAllowEmpty(dep.sub_path) orelse {
22082223 dumpBadDirnameHelp(null, null,
22092224 \\dirname() attempted to traverse outside the dependency root.
2210 \\This is not allowed.
22112225 \\
22122226 , .{}) catch {};
22132227 @panic("misconfigured build script");
......@@ -2220,7 +2234,8 @@ pub const LazyPath = union(enum) {
22202234 /// Either returns the path or `"generated"`.
22212235 pub fn getDisplayName(self: LazyPath) []const u8 {
22222236 return switch (self) {
2223 .path, .cwd_relative => self.path,
2237 .src_path => |sp| sp.sub_path,
2238 .path, .cwd_relative => |p| p,
22242239 .generated => "generated",
22252240 .generated_dirname => "generated",
22262241 .dependency => "dependency",
......@@ -2230,7 +2245,7 @@ pub const LazyPath = union(enum) {
22302245 /// Adds dependencies this file source implies to the given step.
22312246 pub fn addStepDependencies(self: LazyPath, other_step: *Step) void {
22322247 switch (self) {
2233 .path, .cwd_relative, .dependency => {},
2248 .src_path, .path, .cwd_relative, .dependency => {},
22342249 .generated => |gen| other_step.dependOn(gen.step),
22352250 .generated_dirname => |gen| other_step.dependOn(gen.generated.step),
22362251 }
......@@ -2250,6 +2265,7 @@ pub const LazyPath = union(enum) {
22502265 pub fn getPath2(self: LazyPath, src_builder: *Build, asking_step: ?*Step) []const u8 {
22512266 switch (self) {
22522267 .path => |p| return src_builder.pathFromRoot(p),
2268 .src_path => |sp| return sp.owner.pathFromRoot(sp.sub_path),
22532269 .cwd_relative => |p| return src_builder.pathFromCwd(p),
22542270 .generated => |gen| return gen.path orelse {
22552271 std.debug.getStderrMutex().lock();
......@@ -2262,13 +2278,13 @@ pub const LazyPath = union(enum) {
22622278 (src_builder.cache_root.join(src_builder.allocator, &.{"."}) catch @panic("OOM"));
22632279
22642280 const gen_step = gen.generated.step;
2265 var path = getPath2(LazyPath{ .generated = gen.generated }, src_builder, asking_step);
2281 var p = getPath2(LazyPath{ .generated = gen.generated }, src_builder, asking_step);
22662282 var i: usize = 0;
22672283 while (i <= gen.up) : (i += 1) {
22682284 // path is absolute.
22692285 // dirname will return null only if we're at root.
22702286 // Typically, we'll stop well before that at the cache root.
2271 path = fs.path.dirname(path) orelse {
2287 p = fs.path.dirname(p) orelse {
22722288 dumpBadDirnameHelp(gen_step, asking_step,
22732289 \\dirname() reached root.
22742290 \\No more directories left to go up.
......@@ -2277,7 +2293,7 @@ pub const LazyPath = union(enum) {
22772293 @panic("misconfigured build script");
22782294 };
22792295
2280 if (mem.eql(u8, path, cache_root_path) and i < gen.up) {
2296 if (mem.eql(u8, p, cache_root_path) and i < gen.up) {
22812297 // If we hit the cache root and there's still more to go,
22822298 // the script attempted to go too far.
22832299 dumpBadDirnameHelp(gen_step, asking_step,
......@@ -2288,7 +2304,7 @@ pub const LazyPath = union(enum) {
22882304 @panic("misconfigured build script");
22892305 }
22902306 }
2291 return path;
2307 return p;
22922308 },
22932309 .dependency => |dep| {
22942310 return dep.dependency.builder.pathJoin(&[_][]const u8{
......@@ -2302,6 +2318,10 @@ pub const LazyPath = union(enum) {
23022318 /// Duplicates the file source for a given builder.
23032319 pub fn dupe(self: LazyPath, b: *Build) LazyPath {
23042320 return switch (self) {
2321 .src_path => |sp| .{ .src_path = .{
2322 .owner = sp.owner,
2323 .sub_path = b.dupePath(sp.sub_path),
2324 } },
23052325 .path => |p| .{ .path = b.dupePath(p) },
23062326 .cwd_relative => |p| .{ .cwd_relative = b.dupePath(p) },
23072327 .generated => |gen| .{ .generated = gen },
lib/std/Build/Step/Compile.zig+9-4
......@@ -55,7 +55,7 @@ global_base: ?u64 = null,
5555zig_lib_dir: ?LazyPath,
5656exec_cmd_args: ?[]const ?[]const u8,
5757filters: []const []const u8,
58test_runner: ?[]const u8,
58test_runner: ?LazyPath,
5959test_server_mode: bool,
6060wasi_exec_model: ?std.builtin.WasiExecModel = null,
6161
......@@ -236,7 +236,7 @@ pub const Options = struct {
236236 version: ?std.SemanticVersion = null,
237237 max_rss: usize = 0,
238238 filters: []const []const u8 = &.{},
239 test_runner: ?[]const u8 = null,
239 test_runner: ?LazyPath = null,
240240 use_llvm: ?bool = null,
241241 use_lld: ?bool = null,
242242 zig_lib_dir: ?LazyPath = null,
......@@ -402,7 +402,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
402402 .zig_lib_dir = null,
403403 .exec_cmd_args = null,
404404 .filters = options.filters,
405 .test_runner = options.test_runner,
405 .test_runner = null,
406406 .test_server_mode = options.test_runner == null,
407407 .rdynamic = false,
408408 .installed_path = null,
......@@ -429,6 +429,11 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
429429 lp.addStepDependencies(&self.step);
430430 }
431431
432 if (options.test_runner) |lp| {
433 self.test_runner = lp.dupe(self.step.owner);
434 lp.addStepDependencies(&self.step);
435 }
436
432437 // Only the PE/COFF format has a Resource Table which is where the manifest
433438 // gets embedded, so for any other target the manifest file is just ignored.
434439 if (target.ofmt == .coff) {
......@@ -1402,7 +1407,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
14021407
14031408 if (self.test_runner) |test_runner| {
14041409 try zig_args.append("--test-runner");
1405 try zig_args.append(b.pathFromRoot(test_runner));
1410 try zig_args.append(test_runner.getPath(b));
14061411 }
14071412
14081413 for (b.debug_log_scopes) |log_scope| {
lib/std/Build/Step/ConfigHeader.zig+1
......@@ -58,6 +58,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader {
5858
5959 if (options.style.getPath()) |s| default_include_path: {
6060 const sub_path = switch (s) {
61 .src_path => |sp| sp.sub_path,
6162 .path => |path| path,
6263 .generated, .generated_dirname => break :default_include_path,
6364 .cwd_relative => |sub_path| sub_path,
test/standalone/test_runner_module_imports/build.zig+1-1
......@@ -3,7 +3,7 @@ const std = @import("std");
33pub fn build(b: *std.Build) void {
44 const t = b.addTest(.{
55 .root_source_file = .{ .path = "src/main.zig" },
6 .test_runner = "test_runner/main.zig",
6 .test_runner = b.path("test_runner/main.zig"),
77 });
88
99 const module1 = b.createModule(.{ .root_source_file = .{ .path = "module1/main.zig" } });
test/standalone/test_runner_path/build.zig+1-1
......@@ -9,7 +9,7 @@ pub fn build(b: *std.Build) void {
99 const test_exe = b.addTest(.{
1010 .root_source_file = .{ .path = "test.zig" },
1111 });
12 test_exe.test_runner = "test_runner.zig";
12 test_exe.test_runner = b.path("test_runner.zig");
1313
1414 const test_run = b.addRunArtifact(test_exe);
1515 test_step.dependOn(&test_run.step);