diff --git a/src/Package.zig b/src/Package.zig index cb1c64ae26..5de914e114 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -83,6 +83,16 @@ pub const Path = struct { return p.root_dir.handle.atomicFile(joined_path, options); } + pub fn access(p: Path, sub_path: []const u8, flags: fs.File.OpenFlags) !void { + var buf: [fs.MAX_PATH_BYTES]u8 = undefined; + const joined_path = if (p.sub_path.len == 0) sub_path else p: { + break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{ + p.sub_path, sub_path, + }) catch return error.NameTooLong; + }; + return p.root_dir.handle.access(joined_path, flags); + } + pub fn format( self: Path, comptime fmt_string: []const u8, diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index b688b24fe6..13ee477d40 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -231,6 +231,7 @@ pub fn run(f: *Fetch) RunError!void { ); f.package_root = try f.parent_package_root.join(arena, sub_path); try loadManifest(f, f.package_root); + try checkBuildFileExistence(f); if (!f.job_queue.recursive) return; // Package hashes are used as unique identifiers for packages, so // we still need one for relative paths. @@ -279,17 +280,18 @@ pub fn run(f: *Fetch) RunError!void { if (cache_root.handle.access(pkg_sub_path, .{})) |_| { f.package_root = .{ .root_dir = cache_root, - .sub_path = pkg_sub_path, + .sub_path = try arena.dupe(u8, pkg_sub_path), }; try loadManifest(f, f.package_root); + try checkBuildFileExistence(f); if (!f.job_queue.recursive) return; return queueJobsForDeps(f, expected_hash); } else |err| switch (err) { error.FileNotFound => {}, else => |e| { try eb.addRootErrorMessage(.{ - .msg = try eb.printString("unable to open global package cache directory '{s}': {s}", .{ - try cache_root.join(arena, &.{pkg_sub_path}), @errorName(e), + .msg = try eb.printString("unable to open global package cache directory '{}{s}': {s}", .{ + cache_root, pkg_sub_path, @errorName(e), }), }); return error.FetchFailed; @@ -381,10 +383,13 @@ fn runResource( // by the system. This is done even if the hash is invalid, in case the // package with the different hash is used in the future. - const dest_pkg_sub_path = "p" ++ s ++ Manifest.hexDigest(f.actual_hash); - renameTmpIntoCache(cache_root.handle, tmp_dir_sub_path, dest_pkg_sub_path) catch |err| { + f.package_root = .{ + .root_dir = cache_root, + .sub_path = try arena.dupe(u8, "p" ++ s ++ Manifest.hexDigest(f.actual_hash)), + }; + renameTmpIntoCache(cache_root.handle, tmp_dir_sub_path, f.package_root.sub_path) catch |err| { const src = try cache_root.join(arena, &.{tmp_dir_sub_path}); - const dest = try cache_root.join(arena, &.{dest_pkg_sub_path}); + const dest = try cache_root.join(arena, &.{f.package_root.sub_path}); try eb.addRootErrorMessage(.{ .msg = try eb.printString( "unable to rename temporary directory '{s}' into package cache directory '{s}': {s}", .{ src, dest, @errorName(err) }, @@ -423,6 +428,25 @@ fn runResource( return queueJobsForDeps(f, actual_hex); } +/// `computeHash` gets a free check for the existence of `build.zig`, but when +/// not computing a hash, we need to do a syscall to check for it. +fn checkBuildFileExistence(f: *Fetch) RunError!void { + const eb = &f.error_bundle; + if (f.package_root.access(Package.build_zig_basename, .{})) |_| { + f.has_build_zig = true; + } else |err| switch (err) { + error.FileNotFound => {}, + else => |e| { + try eb.addRootErrorMessage(.{ + .msg = try eb.printString("unable to access '{}{s}': {s}", .{ + f.package_root, Package.build_zig_basename, @errorName(e), + }), + }); + return error.FetchFailed; + }, + } +} + /// This function populates `f.manifest` or leaves it `null`. fn loadManifest(f: *Fetch, pkg_root: Package.Path) RunError!void { const eb = &f.error_bundle;