| ... | ... | @@ -200,7 +200,7 @@ pub const JobQueue = struct { |
| 200 | 200 | pub const Location = union(enum) { |
| 201 | 201 | remote: Remote, |
| 202 | 202 | /// A directory found inside the parent package. |
| 203 | | relative_path: []const u8, |
| 203 | relative_path: Package.Path, |
| 204 | 204 | /// Recursive Fetch tasks will never use this Location, but it may be |
| 205 | 205 | /// passed in by the CLI. Indicates the file contents here should be copied |
| 206 | 206 | /// into the global package cache. It may be a file relative to the cwd or |
| ... | ... | @@ -239,8 +239,8 @@ pub fn run(f: *Fetch) RunError!void { |
| 239 | 239 | // relative path, treat this the same as a cache hit. Otherwise, proceed. |
| 240 | 240 | |
| 241 | 241 | const remote = switch (f.location) { |
| 242 | | .relative_path => |sub_path| { |
| 243 | | if (fs.path.isAbsolute(sub_path)) return f.fail( |
| 242 | .relative_path => |pkg_root| { |
| 243 | if (fs.path.isAbsolute(pkg_root.sub_path)) return f.fail( |
| 244 | 244 | f.location_tok, |
| 245 | 245 | try eb.addString("expected path relative to build root; found absolute path"), |
| 246 | 246 | ); |
| ... | ... | @@ -248,31 +248,19 @@ pub fn run(f: *Fetch) RunError!void { |
| 248 | 248 | f.hash_tok, |
| 249 | 249 | try eb.addString("path-based dependencies are not hashed"), |
| 250 | 250 | ); |
| 251 | | f.package_root = try f.parent_package_root.resolvePosix(arena, sub_path); |
| 252 | | if (std.mem.startsWith(u8, f.package_root.sub_path, "../")) { |
| 251 | if (std.mem.startsWith(u8, pkg_root.sub_path, "../")) { |
| 253 | 252 | return f.fail( |
| 254 | 253 | f.location_tok, |
| 255 | | try eb.addString("dependency path outside package"), |
| 254 | try eb.printString("dependency path outside project: '{}{s}'", .{ |
| 255 | pkg_root.root_dir, pkg_root.sub_path, |
| 256 | }), |
| 256 | 257 | ); |
| 257 | 258 | } |
| 258 | | try loadManifest(f, f.package_root); |
| 259 | f.package_root = pkg_root; |
| 260 | try loadManifest(f, pkg_root); |
| 259 | 261 | try checkBuildFileExistence(f); |
| 260 | 262 | if (!f.job_queue.recursive) return; |
| 261 | | // Package hashes are used as unique identifiers for packages, so |
| 262 | | // we still need one for relative paths. |
| 263 | | const digest = h: { |
| 264 | | var hasher = Manifest.Hash.init(.{}); |
| 265 | | // This hash is a tuple of: |
| 266 | | // * whether it relative to the global cache directory or to the root package |
| 267 | | // * the relative file path from there to the build root of the package |
| 268 | | hasher.update(if (f.package_root.root_dir.eql(cache_root)) |
| 269 | | &package_hash_prefix_cached |
| 270 | | else |
| 271 | | &package_hash_prefix_project); |
| 272 | | hasher.update(f.package_root.sub_path); |
| 273 | | break :h hasher.finalResult(); |
| 274 | | }; |
| 275 | | return queueJobsForDeps(f, Manifest.hexDigest(digest)); |
| 263 | return queueJobsForDeps(f); |
| 276 | 264 | }, |
| 277 | 265 | .remote => |remote| remote, |
| 278 | 266 | .path_or_url => |path_or_url| { |
| ... | ... | @@ -310,7 +298,7 @@ pub fn run(f: *Fetch) RunError!void { |
| 310 | 298 | try loadManifest(f, f.package_root); |
| 311 | 299 | try checkBuildFileExistence(f); |
| 312 | 300 | if (!f.job_queue.recursive) return; |
| 313 | | return queueJobsForDeps(f, expected_hash); |
| 301 | return queueJobsForDeps(f); |
| 314 | 302 | } else |err| switch (err) { |
| 315 | 303 | error.FileNotFound => {}, |
| 316 | 304 | else => |e| { |
| ... | ... | @@ -450,7 +438,7 @@ fn runResource( |
| 450 | 438 | // Spawn a new fetch job for each dependency in the manifest file. Use |
| 451 | 439 | // a mutex and a hash map so that redundant jobs do not get queued up. |
| 452 | 440 | if (!f.job_queue.recursive) return; |
| 453 | | return queueJobsForDeps(f, actual_hex); |
| 441 | return queueJobsForDeps(f); |
| 454 | 442 | } |
| 455 | 443 | |
| 456 | 444 | /// `computeHash` gets a free check for the existence of `build.zig`, but when |
| ... | ... | @@ -534,27 +522,29 @@ fn loadManifest(f: *Fetch, pkg_root: Package.Path) RunError!void { |
| 534 | 522 | } |
| 535 | 523 | } |
| 536 | 524 | |
| 537 | | fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void { |
| 525 | fn queueJobsForDeps(f: *Fetch) RunError!void { |
| 538 | 526 | assert(f.job_queue.recursive); |
| 539 | 527 | |
| 540 | 528 | // If the package does not have a build.zig.zon file then there are no dependencies. |
| 541 | 529 | const manifest = f.manifest orelse return; |
| 542 | 530 | |
| 543 | 531 | const new_fetches = nf: { |
| 544 | | const deps = manifest.dependencies.values(); |
| 532 | const parent_arena = f.arena.allocator(); |
| 545 | 533 | const gpa = f.arena.child_allocator; |
| 534 | const cache_root = f.job_queue.global_cache; |
| 535 | const deps = manifest.dependencies.values(); |
| 546 | 536 | // Grab the new tasks into a temporary buffer so we can unlock that mutex |
| 547 | 537 | // as fast as possible. |
| 548 | 538 | // This overallocates any fetches that get skipped by the `continue` in the |
| 549 | 539 | // loop below. |
| 550 | | const new_fetches = try f.arena.allocator().alloc(Fetch, deps.len); |
| 540 | const new_fetches = try parent_arena.alloc(Fetch, deps.len); |
| 551 | 541 | var new_fetch_index: usize = 0; |
| 552 | 542 | |
| 553 | 543 | f.job_queue.mutex.lock(); |
| 554 | 544 | defer f.job_queue.mutex.unlock(); |
| 555 | 545 | |
| 556 | 546 | try f.job_queue.all_fetches.ensureUnusedCapacity(gpa, new_fetches.len); |
| 557 | | try f.job_queue.table.ensureUnusedCapacity(gpa, @intCast(new_fetches.len + 1)); |
| 547 | try f.job_queue.table.ensureUnusedCapacity(gpa, @intCast(new_fetches.len)); |
| 558 | 548 | |
| 559 | 549 | // There are four cases here: |
| 560 | 550 | // * Correct hash is provided by manifest. |
| ... | ... | @@ -564,12 +554,8 @@ fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void |
| 564 | 554 | // * Hash is not provided by manifest. |
| 565 | 555 | // - Hash missing error emitted; `queueJobsForDeps` is not called. |
| 566 | 556 | // * path-based location is used without a hash. |
| 567 | | // - We need to add `hash` to the table now. |
| 568 | | switch (f.location) { |
| 569 | | .remote => assert(f.job_queue.table.get(hash) == f), |
| 570 | | .relative_path => f.job_queue.table.putAssumeCapacityNoClobber(hash, f), |
| 571 | | .path_or_url => unreachable, |
| 572 | | } |
| 557 | // - Hash is added to the table based on the path alone before |
| 558 | // calling run(); no need to add it again. |
| 573 | 559 | |
| 574 | 560 | for (deps) |dep| { |
| 575 | 561 | const new_fetch = &new_fetches[new_fetch_index]; |
| ... | ... | @@ -586,7 +572,16 @@ fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void |
| 586 | 572 | break :h multihash_digest; |
| 587 | 573 | }, |
| 588 | 574 | } }, |
| 589 | | .path => |path| .{ .relative_path = path }, |
| 575 | .path => |rel_path| l: { |
| 576 | // This might produce an invalid path, which is checked for |
| 577 | // at the beginning of run(). |
| 578 | const new_root = try f.package_root.resolvePosix(parent_arena, rel_path); |
| 579 | const multihash_digest = relativePathDigest(new_root, cache_root); |
| 580 | const gop = f.job_queue.table.getOrPutAssumeCapacity(multihash_digest); |
| 581 | if (gop.found_existing) continue; |
| 582 | gop.value_ptr.* = new_fetch; |
| 583 | break :l .{ .relative_path = new_root }; |
| 584 | }, |
| 590 | 585 | }; |
| 591 | 586 | new_fetch_index += 1; |
| 592 | 587 | f.job_queue.all_fetches.appendAssumeCapacity(new_fetch); |
| ... | ... | @@ -630,6 +625,22 @@ fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void |
| 630 | 625 | } |
| 631 | 626 | } |
| 632 | 627 | |
| 628 | pub fn relativePathDigest( |
| 629 | pkg_root: Package.Path, |
| 630 | cache_root: Cache.Directory, |
| 631 | ) Manifest.MultiHashHexDigest { |
| 632 | var hasher = Manifest.Hash.init(.{}); |
| 633 | // This hash is a tuple of: |
| 634 | // * whether it relative to the global cache directory or to the root package |
| 635 | // * the relative file path from there to the build root of the package |
| 636 | hasher.update(if (pkg_root.root_dir.eql(cache_root)) |
| 637 | &package_hash_prefix_cached |
| 638 | else |
| 639 | &package_hash_prefix_project); |
| 640 | hasher.update(pkg_root.sub_path); |
| 641 | return Manifest.hexDigest(hasher.finalResult()); |
| 642 | } |
| 643 | |
| 633 | 644 | pub fn workerRun(f: *Fetch) void { |
| 634 | 645 | defer f.job_queue.wait_group.finish(); |
| 635 | 646 | run(f) catch |err| switch (err) { |