authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-08 14:33:39-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-08 16:54:31-07:00
log47a413361dd6702dd0412ed58003e9ca9d8ba928
tree84a34f34c649e765c3bedc74f105b287ffe6cc45
parent1fd95fc00519ee44de94fa5b73ca7a3285b92149

Package.Fetch: fix handling of relative paths


3 files changed, 54 insertions(+), 37 deletions(-)

lib/std/Build/Cache.zig+1-1
...@@ -61,7 +61,7 @@ pub const Directory = struct {...@@ -61,7 +61,7 @@ pub const Directory = struct {
61 writer: anytype,61 writer: anytype,
62 ) !void {62 ) !void {
63 _ = options;63 _ = options;
64 if (fmt_string.len != 0) fmt.invalidFmtError(fmt, self);64 if (fmt_string.len != 0) fmt.invalidFmtError(fmt_string, self);
65 if (self.path) |p| {65 if (self.path) |p| {
66 try writer.writeAll(p);66 try writer.writeAll(p);
67 try writer.writeAll(fs.path.sep_str);67 try writer.writeAll(fs.path.sep_str);
src/Package/Fetch.zig+46-35
...@@ -200,7 +200,7 @@ pub const JobQueue = struct {...@@ -200,7 +200,7 @@ pub const JobQueue = struct {
200pub const Location = union(enum) {200pub const Location = union(enum) {
201 remote: Remote,201 remote: Remote,
202 /// A directory found inside the parent package.202 /// A directory found inside the parent package.
203 relative_path: []const u8,203 relative_path: Package.Path,
204 /// Recursive Fetch tasks will never use this Location, but it may be204 /// Recursive Fetch tasks will never use this Location, but it may be
205 /// passed in by the CLI. Indicates the file contents here should be copied205 /// passed in by the CLI. Indicates the file contents here should be copied
206 /// into the global package cache. It may be a file relative to the cwd or206 /// 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,8 +239,8 @@ pub fn run(f: *Fetch) RunError!void {
239 // relative path, treat this the same as a cache hit. Otherwise, proceed.239 // relative path, treat this the same as a cache hit. Otherwise, proceed.
240240
241 const remote = switch (f.location) {241 const remote = switch (f.location) {
242 .relative_path => |sub_path| {242 .relative_path => |pkg_root| {
243 if (fs.path.isAbsolute(sub_path)) return f.fail(243 if (fs.path.isAbsolute(pkg_root.sub_path)) return f.fail(
244 f.location_tok,244 f.location_tok,
245 try eb.addString("expected path relative to build root; found absolute path"),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,31 +248,19 @@ pub fn run(f: *Fetch) RunError!void {
248 f.hash_tok,248 f.hash_tok,
249 try eb.addString("path-based dependencies are not hashed"),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);251 if (std.mem.startsWith(u8, pkg_root.sub_path, "../")) {
252 if (std.mem.startsWith(u8, f.package_root.sub_path, "../")) {
253 return f.fail(252 return f.fail(
254 f.location_tok,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 try checkBuildFileExistence(f);261 try checkBuildFileExistence(f);
260 if (!f.job_queue.recursive) return;262 if (!f.job_queue.recursive) return;
261 // Package hashes are used as unique identifiers for packages, so263 return queueJobsForDeps(f);
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));
276 },264 },
277 .remote => |remote| remote,265 .remote => |remote| remote,
278 .path_or_url => |path_or_url| {266 .path_or_url => |path_or_url| {
...@@ -310,7 +298,7 @@ pub fn run(f: *Fetch) RunError!void {...@@ -310,7 +298,7 @@ pub fn run(f: *Fetch) RunError!void {
310 try loadManifest(f, f.package_root);298 try loadManifest(f, f.package_root);
311 try checkBuildFileExistence(f);299 try checkBuildFileExistence(f);
312 if (!f.job_queue.recursive) return;300 if (!f.job_queue.recursive) return;
313 return queueJobsForDeps(f, expected_hash);301 return queueJobsForDeps(f);
314 } else |err| switch (err) {302 } else |err| switch (err) {
315 error.FileNotFound => {},303 error.FileNotFound => {},
316 else => |e| {304 else => |e| {
...@@ -450,7 +438,7 @@ fn runResource(...@@ -450,7 +438,7 @@ fn runResource(
450 // Spawn a new fetch job for each dependency in the manifest file. Use438 // Spawn a new fetch job for each dependency in the manifest file. Use
451 // a mutex and a hash map so that redundant jobs do not get queued up.439 // a mutex and a hash map so that redundant jobs do not get queued up.
452 if (!f.job_queue.recursive) return;440 if (!f.job_queue.recursive) return;
453 return queueJobsForDeps(f, actual_hex);441 return queueJobsForDeps(f);
454}442}
455443
456/// `computeHash` gets a free check for the existence of `build.zig`, but when444/// `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,27 +522,29 @@ fn loadManifest(f: *Fetch, pkg_root: Package.Path) RunError!void {
534 }522 }
535}523}
536524
537fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void {525fn queueJobsForDeps(f: *Fetch) RunError!void {
538 assert(f.job_queue.recursive);526 assert(f.job_queue.recursive);
539527
540 // If the package does not have a build.zig.zon file then there are no dependencies.528 // If the package does not have a build.zig.zon file then there are no dependencies.
541 const manifest = f.manifest orelse return;529 const manifest = f.manifest orelse return;
542530
543 const new_fetches = nf: {531 const new_fetches = nf: {
544 const deps = manifest.dependencies.values();532 const parent_arena = f.arena.allocator();
545 const gpa = f.arena.child_allocator;533 const gpa = f.arena.child_allocator;
534 const cache_root = f.job_queue.global_cache;
535 const deps = manifest.dependencies.values();
546 // Grab the new tasks into a temporary buffer so we can unlock that mutex536 // Grab the new tasks into a temporary buffer so we can unlock that mutex
547 // as fast as possible.537 // as fast as possible.
548 // This overallocates any fetches that get skipped by the `continue` in the538 // This overallocates any fetches that get skipped by the `continue` in the
549 // loop below.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 var new_fetch_index: usize = 0;541 var new_fetch_index: usize = 0;
552542
553 f.job_queue.mutex.lock();543 f.job_queue.mutex.lock();
554 defer f.job_queue.mutex.unlock();544 defer f.job_queue.mutex.unlock();
555545
556 try f.job_queue.all_fetches.ensureUnusedCapacity(gpa, new_fetches.len);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));
558548
559 // There are four cases here:549 // There are four cases here:
560 // * Correct hash is provided by manifest.550 // * Correct hash is provided by manifest.
...@@ -564,12 +554,8 @@ fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void...@@ -564,12 +554,8 @@ fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void
564 // * Hash is not provided by manifest.554 // * Hash is not provided by manifest.
565 // - Hash missing error emitted; `queueJobsForDeps` is not called.555 // - Hash missing error emitted; `queueJobsForDeps` is not called.
566 // * path-based location is used without a hash.556 // * path-based location is used without a hash.
567 // - We need to add `hash` to the table now.557 // - Hash is added to the table based on the path alone before
568 switch (f.location) {558 // calling run(); no need to add it again.
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 }
573559
574 for (deps) |dep| {560 for (deps) |dep| {
575 const new_fetch = &new_fetches[new_fetch_index];561 const new_fetch = &new_fetches[new_fetch_index];
...@@ -586,7 +572,16 @@ fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void...@@ -586,7 +572,16 @@ fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void
586 break :h multihash_digest;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 new_fetch_index += 1;586 new_fetch_index += 1;
592 f.job_queue.all_fetches.appendAssumeCapacity(new_fetch);587 f.job_queue.all_fetches.appendAssumeCapacity(new_fetch);
...@@ -630,6 +625,22 @@ fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void...@@ -630,6 +625,22 @@ fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void
630 }625 }
631}626}
632627
628pub 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
633pub fn workerRun(f: *Fetch) void {644pub fn workerRun(f: *Fetch) void {
634 defer f.job_queue.wait_group.finish();645 defer f.job_queue.wait_group.finish();
635 run(f) catch |err| switch (err) {646 run(f) catch |err| switch (err) {
src/main.zig+7-1
...@@ -4851,10 +4851,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4851,10 +4851,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4851 defer job_queue.deinit();4851 defer job_queue.deinit();
48524852
4853 try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1);4853 try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1);
4854 try job_queue.table.ensureUnusedCapacity(gpa, 1);
48544855
4855 var fetch: Package.Fetch = .{4856 var fetch: Package.Fetch = .{
4856 .arena = std.heap.ArenaAllocator.init(gpa),4857 .arena = std.heap.ArenaAllocator.init(gpa),
4857 .location = .{ .relative_path = "" },4858 .location = .{ .relative_path = build_mod.root },
4858 .location_tok = 0,4859 .location_tok = 0,
4859 .hash_tok = 0,4860 .hash_tok = 0,
4860 .parent_package_root = build_mod.root,4861 .parent_package_root = build_mod.root,
...@@ -4874,6 +4875,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4874,6 +4875,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4874 };4875 };
4875 job_queue.all_fetches.appendAssumeCapacity(&fetch);4876 job_queue.all_fetches.appendAssumeCapacity(&fetch);
48764877
4878 job_queue.table.putAssumeCapacityNoClobber(
4879 Package.Fetch.relativePathDigest(build_mod.root, global_cache_directory),
4880 &fetch,
4881 );
4882
4877 job_queue.wait_group.start();4883 job_queue.wait_group.start();
4878 try job_queue.thread_pool.spawn(Package.Fetch.workerRun, .{&fetch});4884 try job_queue.thread_pool.spawn(Package.Fetch.workerRun, .{&fetch});
4879 job_queue.wait_group.wait();4885 job_queue.wait_group.wait();