| ... | ... | @@ -439,7 +439,8 @@ fn runResource( |
| 439 | 439 | const s = fs.path.sep_str; |
| 440 | 440 | const cache_root = f.job_queue.global_cache; |
| 441 | 441 | const rand_int = std.crypto.random.int(u64); |
| 442 | | const tmp_dir_sub_path = "tmp" ++ s ++ Manifest.hex64(rand_int); |
| 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 |
| 443 | 444 | |
| 444 | 445 | { |
| 445 | 446 | const tmp_directory_path = try cache_root.join(arena, &.{tmp_dir_sub_path}); |
| ... | ... | @@ -463,6 +464,34 @@ fn runResource( |
| 463 | 464 | |
| 464 | 465 | try unpackResource(f, resource, uri_path, tmp_directory); |
| 465 | 466 | |
| 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| { |
| 471 | 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), |
| 474 | }), |
| 475 | }); |
| 476 | return error.FetchFailed; |
| 477 | }; |
| 478 | tmp_package_root_sub_path = try fs.path.join(arena, &[_][]const u8{ tmp_dir_sub_path, sub_path }); |
| 479 | tmp_directory.handle.close(); |
| 480 | tmp_directory = .{ |
| 481 | .path = try cache_root.join(arena, &.{tmp_package_root_sub_path.?}), |
| 482 | .handle = handle, |
| 483 | }; |
| 484 | } else { |
| 485 | // btrfs workaround; reopen tmp_directory |
| 486 | if (native_os == .linux and f.job_queue.work_around_btrfs_bug) { |
| 487 | // https://github.com/ziglang/zig/issues/17095 |
| 488 | tmp_directory.handle.close(); |
| 489 | tmp_directory.handle = cache_root.handle.makeOpenPath(tmp_dir_sub_path, .{ |
| 490 | .iterate = true, |
| 491 | }) catch @panic("btrfs workaround failed"); |
| 492 | } |
| 493 | } |
| 494 | |
| 466 | 495 | // Load, parse, and validate the unpacked build.zig.zon file. It is allowed |
| 467 | 496 | // for the file to be missing, in which case this fetched package is |
| 468 | 497 | // considered to be a "naked" package. |
| ... | ... | @@ -481,15 +510,6 @@ fn runResource( |
| 481 | 510 | |
| 482 | 511 | // Compute the package hash based on the remaining files in the temporary |
| 483 | 512 | // directory. |
| 484 | | |
| 485 | | if (native_os == .linux and f.job_queue.work_around_btrfs_bug) { |
| 486 | | // https://github.com/ziglang/zig/issues/17095 |
| 487 | | tmp_directory.handle.close(); |
| 488 | | tmp_directory.handle = cache_root.handle.makeOpenPath(tmp_dir_sub_path, .{ |
| 489 | | .iterate = true, |
| 490 | | }) catch @panic("btrfs workaround failed"); |
| 491 | | } |
| 492 | | |
| 493 | 513 | f.actual_hash = try computeHash(f, tmp_directory, filter); |
| 494 | 514 | } |
| 495 | 515 | |
| ... | ... | @@ -503,7 +523,11 @@ fn runResource( |
| 503 | 523 | .root_dir = cache_root, |
| 504 | 524 | .sub_path = try arena.dupe(u8, "p" ++ s ++ Manifest.hexDigest(f.actual_hash)), |
| 505 | 525 | }; |
| 506 | | renameTmpIntoCache(cache_root.handle, tmp_dir_sub_path, f.package_root.sub_path) catch |err| { |
| 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| { |
| 507 | 531 | const src = try cache_root.join(arena, &.{tmp_dir_sub_path}); |
| 508 | 532 | const dest = try cache_root.join(arena, &.{f.package_root.sub_path}); |
| 509 | 533 | try eb.addRootErrorMessage(.{ .msg = try eb.printString( |
| ... | ... | @@ -512,6 +536,10 @@ fn runResource( |
| 512 | 536 | ) }); |
| 513 | 537 | return error.FetchFailed; |
| 514 | 538 | }; |
| 539 | // Remove temporary directory root if that not already done in rename. |
| 540 | if (tmp_package_root_sub_path) |_| { |
| 541 | cache_root.handle.deleteTree(tmp_dir_sub_path) catch {}; |
| 542 | } |
| 515 | 543 | |
| 516 | 544 | // Validate the computed hash against the expected hash. If invalid, this |
| 517 | 545 | // job is done. |
| ... | ... | @@ -608,6 +636,19 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void { |
| 608 | 636 | } |
| 609 | 637 | } |
| 610 | 638 | |
| 639 | // Finds package root subpath. |
| 640 | // Skips single root directory, returns null in all other cases. |
| 641 | fn findPackageRootSubPath(allocator: Allocator, parent: Cache.Directory) !?[]const u8 { |
| 642 | var iter = parent.handle.iterate(); |
| 643 | 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); |
| 647 | } |
| 648 | } |
| 649 | return null; |
| 650 | } |
| 651 | |
| 611 | 652 | fn queueJobsForDeps(f: *Fetch) RunError!void { |
| 612 | 653 | assert(f.job_queue.recursive); |
| 613 | 654 | |
| ... | ... | @@ -1700,3 +1741,37 @@ test { |
| 1700 | 1741 | _ = Filter; |
| 1701 | 1742 | _ = FileType; |
| 1702 | 1743 | } |
| 1744 | |
| 1745 | test "findPackageRootSubPath" { |
| 1746 | const testing = std.testing; |
| 1747 | |
| 1748 | var root = std.testing.tmpDir(.{ .iterate = true }); |
| 1749 | defer root.cleanup(); |
| 1750 | |
| 1751 | // folder1 |
| 1752 | // ├── folder2 |
| 1753 | // ├── file1 |
| 1754 | // |
| 1755 | try root.dir.makePath("folder1/folder2"); |
| 1756 | (try root.dir.createFile("folder1/file1", .{})).close(); |
| 1757 | |
| 1758 | // 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 | )).?; |
| 1763 | try testing.expectEqualStrings("folder1", sub_path); |
| 1764 | testing.allocator.free(sub_path); |
| 1765 | |
| 1766 | // start at folder1 returns null |
| 1767 | try testing.expect(null == (try findPackageRootSubPath( |
| 1768 | testing.allocator, |
| 1769 | Cache.Directory{ .path = null, .handle = try root.dir.openDir("folder1", .{ .iterate = true }) }, |
| 1770 | ))); |
| 1771 | |
| 1772 | // start at folder1/folder2 returns null |
| 1773 | try testing.expect(null == (try findPackageRootSubPath( |
| 1774 | testing.allocator, |
| 1775 | Cache.Directory{ .path = null, .handle = try root.dir.openDir("folder1/folder2", .{ .iterate = true }) }, |
| 1776 | ))); |
| 1777 | } |