authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2024-03-26 22:27:06+01:00
committergravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2024-04-03 17:06:20+02:00
logc4261bf5624d80f1e613bed475ebe03543ec83b8
tree1ff1079993da653c42908e932fea160b7ecb6029
parent15ca0c947176779cb57e2c0946b6a11ba0367257

fetch: find package root only for archives


1 files changed, 61 insertions(+), 64 deletions(-)

src/Package/Fetch.zig+61-64
......@@ -439,10 +439,9 @@ fn runResource(
439439 const s = fs.path.sep_str;
440440 const cache_root = f.job_queue.global_cache;
441441 const rand_int = std.crypto.random.int(u64);
442 const tmp_dir_sub_path = "tmp" ++ s ++ Manifest.hex64(rand_int); // root of the temporary directory
443 var tmp_package_root_sub_path: ?[]const u8 = null; // package root inside temporary directory
442 const tmp_dir_sub_path = "tmp" ++ s ++ Manifest.hex64(rand_int);
444443
445 {
444 const package_sub_path = blk: {
446445 const tmp_directory_path = try cache_root.join(arena, &.{tmp_dir_sub_path});
447446 var tmp_directory: Cache.Directory = .{
448447 .path = tmp_directory_path,
......@@ -462,25 +461,21 @@ fn runResource(
462461 };
463462 defer tmp_directory.handle.close();
464463
465 try unpackResource(f, resource, uri_path, tmp_directory);
464 const package_dir = try unpackResource(f, resource, uri_path, tmp_directory);
466465
467 // Strip leading root directory if needed.
468 if (findPackageRootSubPath(arena, tmp_directory) catch null) |sub_path| {
469 // Position tmp_directory to sub_path.
470 const handle = tmp_directory.handle.openDir(sub_path, .{ .iterate = true }) catch |err| {
466 if (package_dir) |dir_name| {
467 // Position tmp_directory to dir_name inside tmp_dir_sub_path.
468 const path = try cache_root.join(arena, &.{ tmp_dir_sub_path, dir_name });
469 const handle = tmp_directory.handle.openDir(dir_name, .{ .iterate = true }) catch |err| {
471470 try eb.addRootErrorMessage(.{
472 .msg = try eb.printString("fail to open temporary directory '{s}' sub path '{s}': {s}", .{
473 tmp_directory_path, sub_path, @errorName(err),
471 .msg = try eb.printString("unable to open temporary directory '{s}': {s}", .{
472 path, @errorName(err),
474473 }),
475474 });
476475 return error.FetchFailed;
477476 };
478 tmp_package_root_sub_path = try fs.path.join(arena, &[_][]const u8{ tmp_dir_sub_path, sub_path });
479477 tmp_directory.handle.close();
480 tmp_directory = .{
481 .path = try cache_root.join(arena, &.{tmp_package_root_sub_path.?}),
482 .handle = handle,
483 };
478 tmp_directory = .{ .path = path, .handle = handle };
484479 } else {
485480 // btrfs workaround; reopen tmp_directory
486481 if (native_os == .linux and f.job_queue.work_around_btrfs_bug) {
......@@ -511,7 +506,12 @@ fn runResource(
511506 // Compute the package hash based on the remaining files in the temporary
512507 // directory.
513508 f.actual_hash = try computeHash(f, tmp_directory, filter);
514 }
509
510 break :blk if (package_dir) |dir_name|
511 try fs.path.join(arena, &.{ tmp_dir_sub_path, dir_name })
512 else
513 tmp_dir_sub_path;
514 };
515515
516516 // Rename the temporary directory into the global zig package cache
517517 // directory. If the hash already exists, delete the temporary directory
......@@ -523,11 +523,7 @@ fn runResource(
523523 .root_dir = cache_root,
524524 .sub_path = try arena.dupe(u8, "p" ++ s ++ Manifest.hexDigest(f.actual_hash)),
525525 };
526 renameTmpIntoCache(
527 cache_root.handle,
528 if (tmp_package_root_sub_path) |p| p else tmp_dir_sub_path,
529 f.package_root.sub_path,
530 ) catch |err| {
526 renameTmpIntoCache(cache_root.handle, package_sub_path, f.package_root.sub_path) catch |err| {
531527 const src = try cache_root.join(arena, &.{tmp_dir_sub_path});
532528 const dest = try cache_root.join(arena, &.{f.package_root.sub_path});
533529 try eb.addRootErrorMessage(.{ .msg = try eb.printString(
......@@ -536,10 +532,8 @@ fn runResource(
536532 ) });
537533 return error.FetchFailed;
538534 };
539 // Remove temporary directory root if that's not already done in rename.
540 if (tmp_package_root_sub_path) |_| {
541 cache_root.handle.deleteTree(tmp_dir_sub_path) catch {};
542 }
535 // Remove temporary directory root.
536 cache_root.handle.deleteTree(tmp_dir_sub_path) catch {};
543537
544538 // Validate the computed hash against the expected hash. If invalid, this
545539 // job is done.
......@@ -636,14 +630,11 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void {
636630 }
637631}
638632
639// Finds package root subpath.
640// Skips single root directory, returns null in all other cases.
641fn findPackageRootSubPath(allocator: Allocator, parent: Cache.Directory) !?[]const u8 {
642 var iter = parent.handle.iterate();
633fn archivePackageDir(arena: Allocator, out_dir: fs.Dir) !?[]const u8 {
634 var iter = out_dir.iterate();
643635 if (try iter.next()) |entry| {
644 if (try iter.next() != null) return null;
645 if (entry.kind == .directory) {
646 return try allocator.dupe(u8, entry.name);
636 if (try iter.next() == null and entry.kind == .directory) {
637 return try arena.dupe(u8, entry.name);
647638 }
648639 }
649640 return null;
......@@ -1073,7 +1064,7 @@ fn unpackResource(
10731064 resource: *Resource,
10741065 uri_path: []const u8,
10751066 tmp_directory: Cache.Directory,
1076) RunError!void {
1067) RunError!?[]const u8 {
10771068 const eb = &f.error_bundle;
10781069 const file_type = switch (resource.*) {
10791070 .file => FileType.fromPath(uri_path) orelse
......@@ -1134,21 +1125,24 @@ fn unpackResource(
11341125
11351126 .git => .git_pack,
11361127
1137 .dir => |dir| return f.recursiveDirectoryCopy(dir, tmp_directory.handle) catch |err| {
1138 return f.fail(f.location_tok, try eb.printString(
1139 "unable to copy directory '{s}': {s}",
1140 .{ uri_path, @errorName(err) },
1141 ));
1128 .dir => |dir| {
1129 f.recursiveDirectoryCopy(dir, tmp_directory.handle) catch |err| {
1130 return f.fail(f.location_tok, try eb.printString(
1131 "unable to copy directory '{s}': {s}",
1132 .{ uri_path, @errorName(err) },
1133 ));
1134 };
1135 return null;
11421136 },
11431137 };
11441138
11451139 switch (file_type) {
1146 .tar => try unpackTarball(f, tmp_directory.handle, resource.reader()),
1140 .tar => return try unpackTarball(f, tmp_directory.handle, resource.reader()),
11471141 .@"tar.gz" => {
11481142 const reader = resource.reader();
11491143 var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader);
11501144 var dcp = std.compress.gzip.decompressor(br.reader());
1151 try unpackTarball(f, tmp_directory.handle, dcp.reader());
1145 return try unpackTarball(f, tmp_directory.handle, dcp.reader());
11521146 },
11531147 .@"tar.xz" => {
11541148 const gpa = f.arena.child_allocator;
......@@ -1161,7 +1155,7 @@ fn unpackResource(
11611155 ));
11621156 };
11631157 defer dcp.deinit();
1164 try unpackTarball(f, tmp_directory.handle, dcp.reader());
1158 return try unpackTarball(f, tmp_directory.handle, dcp.reader());
11651159 },
11661160 .@"tar.zst" => {
11671161 const window_size = std.compress.zstd.DecompressorOptions.default_window_buffer_len;
......@@ -1171,21 +1165,25 @@ fn unpackResource(
11711165 var dcp = std.compress.zstd.decompressor(br.reader(), .{
11721166 .window_buffer = window_buffer,
11731167 });
1174 return unpackTarball(f, tmp_directory.handle, dcp.reader());
1168 return try unpackTarball(f, tmp_directory.handle, dcp.reader());
11751169 },
1176 .git_pack => unpackGitPack(f, tmp_directory.handle, resource) catch |err| switch (err) {
1177 error.FetchFailed => return error.FetchFailed,
1178 error.OutOfMemory => return error.OutOfMemory,
1179 else => |e| return f.fail(f.location_tok, try eb.printString(
1180 "unable to unpack git files: {s}",
1181 .{@errorName(e)},
1182 )),
1170 .git_pack => {
1171 unpackGitPack(f, tmp_directory.handle, resource) catch |err| switch (err) {
1172 error.FetchFailed => return error.FetchFailed,
1173 error.OutOfMemory => return error.OutOfMemory,
1174 else => |e| return f.fail(f.location_tok, try eb.printString(
1175 "unable to unpack git files: {s}",
1176 .{@errorName(e)},
1177 )),
1178 };
1179 return null;
11831180 },
11841181 }
11851182}
11861183
1187fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {
1184fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!?[]const u8 {
11881185 const eb = &f.error_bundle;
1186 const arena = f.arena.allocator();
11891187 const gpa = f.arena.child_allocator;
11901188
11911189 var diagnostics: std.tar.Diagnostics = .{ .allocator = gpa };
......@@ -1193,7 +1191,7 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {
11931191
11941192 std.tar.pipeToFileSystem(out_dir, reader, .{
11951193 .diagnostics = &diagnostics,
1196 .strip_components = 1,
1194 .strip_components = 0,
11971195 // https://github.com/ziglang/zig/issues/17463
11981196 .mode_mode = .ignore,
11991197 .exclude_empty_directories = true,
......@@ -1237,6 +1235,8 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {
12371235 }
12381236 return error.FetchFailed;
12391237 }
1238
1239 return archivePackageDir(arena, out_dir) catch null;
12401240}
12411241
12421242fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!void {
......@@ -1742,36 +1742,33 @@ test {
17421742 _ = FileType;
17431743}
17441744
1745test "findPackageRootSubPath" {
1745test archivePackageDir {
17461746 const testing = std.testing;
17471747
1748 var root = std.testing.tmpDir(.{ .iterate = true });
1749 defer root.cleanup();
1748 var tmp = std.testing.tmpDir(.{ .iterate = true });
1749 defer tmp.cleanup();
17501750
17511751 // folder1
17521752 // ├── folder2
17531753 // ├── file1
17541754 //
1755 try root.dir.makePath("folder1/folder2");
1756 (try root.dir.createFile("folder1/file1", .{})).close();
1755 try tmp.dir.makePath("folder1/folder2");
1756 (try tmp.dir.createFile("folder1/file1", .{})).close();
17571757
17581758 // start at root returns folder1 as package root
1759 const sub_path = (try findPackageRootSubPath(
1760 testing.allocator,
1761 Cache.Directory{ .path = null, .handle = root.dir },
1762 )).?;
1759 const sub_path = (try archivePackageDir(testing.allocator, tmp.dir)).?;
17631760 try testing.expectEqualStrings("folder1", sub_path);
17641761 testing.allocator.free(sub_path);
17651762
17661763 // start at folder1 returns null
1767 try testing.expect(null == (try findPackageRootSubPath(
1764 try testing.expect(null == (try archivePackageDir(
17681765 testing.allocator,
1769 Cache.Directory{ .path = null, .handle = try root.dir.openDir("folder1", .{ .iterate = true }) },
1766 try tmp.dir.openDir("folder1", .{ .iterate = true }),
17701767 )));
17711768
17721769 // start at folder1/folder2 returns null
1773 try testing.expect(null == (try findPackageRootSubPath(
1770 try testing.expect(null == (try archivePackageDir(
17741771 testing.allocator,
1775 Cache.Directory{ .path = null, .handle = try root.dir.openDir("folder1/folder2", .{ .iterate = true }) },
1772 try tmp.dir.openDir("folder1/folder2", .{ .iterate = true }),
17761773 )));
17771774}