| ... | @@ -439,7 +439,8 @@ fn runResource( | ... | @@ -439,7 +439,8 @@ fn runResource( |
| 439 | const s = fs.path.sep_str; | 439 | const s = fs.path.sep_str; |
| 440 | const cache_root = f.job_queue.global_cache; | 440 | const cache_root = f.job_queue.global_cache; |
| 441 | const rand_int = std.crypto.random.int(u64); | 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 | const tmp_directory_path = try cache_root.join(arena, &.{tmp_dir_sub_path}); | 446 | const tmp_directory_path = try cache_root.join(arena, &.{tmp_dir_sub_path}); |
| ... | @@ -463,6 +464,34 @@ fn runResource( | ... | @@ -463,6 +464,34 @@ fn runResource( |
| 463 | | 464 | |
| 464 | try unpackResource(f, resource, uri_path, tmp_directory); | 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 | // Load, parse, and validate the unpacked build.zig.zon file. It is allowed | 495 | // Load, parse, and validate the unpacked build.zig.zon file. It is allowed |
| 467 | // for the file to be missing, in which case this fetched package is | 496 | // for the file to be missing, in which case this fetched package is |
| 468 | // considered to be a "naked" package. | 497 | // considered to be a "naked" package. |
| ... | @@ -481,15 +510,6 @@ fn runResource( | ... | @@ -481,15 +510,6 @@ fn runResource( |
| 481 | | 510 | |
| 482 | // Compute the package hash based on the remaining files in the temporary | 511 | // Compute the package hash based on the remaining files in the temporary |
| 483 | // directory. | 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 | f.actual_hash = try computeHash(f, tmp_directory, filter); | 513 | f.actual_hash = try computeHash(f, tmp_directory, filter); |
| 494 | } | 514 | } |
| 495 | | 515 | |
| ... | @@ -503,7 +523,11 @@ fn runResource( | ... | @@ -503,7 +523,11 @@ fn runResource( |
| 503 | .root_dir = cache_root, | 523 | .root_dir = cache_root, |
| 504 | .sub_path = try arena.dupe(u8, "p" ++ s ++ Manifest.hexDigest(f.actual_hash)), | 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 | const src = try cache_root.join(arena, &.{tmp_dir_sub_path}); | 531 | const src = try cache_root.join(arena, &.{tmp_dir_sub_path}); |
| 508 | const dest = try cache_root.join(arena, &.{f.package_root.sub_path}); | 532 | const dest = try cache_root.join(arena, &.{f.package_root.sub_path}); |
| 509 | try eb.addRootErrorMessage(.{ .msg = try eb.printString( | 533 | try eb.addRootErrorMessage(.{ .msg = try eb.printString( |
| ... | @@ -512,6 +536,10 @@ fn runResource( | ... | @@ -512,6 +536,10 @@ fn runResource( |
| 512 | ) }); | 536 | ) }); |
| 513 | return error.FetchFailed; | 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 | // Validate the computed hash against the expected hash. If invalid, this | 544 | // Validate the computed hash against the expected hash. If invalid, this |
| 517 | // job is done. | 545 | // job is done. |
| ... | @@ -608,6 +636,19 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void { | ... | @@ -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 | fn queueJobsForDeps(f: *Fetch) RunError!void { | 652 | fn queueJobsForDeps(f: *Fetch) RunError!void { |
| 612 | assert(f.job_queue.recursive); | 653 | assert(f.job_queue.recursive); |
| 613 | | 654 | |
| ... | @@ -1700,3 +1741,37 @@ test { | ... | @@ -1700,3 +1741,37 @@ test { |
| 1700 | _ = Filter; | 1741 | _ = Filter; |
| 1701 | _ = FileType; | 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 | } |