authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-21 18:06:13+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-04-21 21:00:46+02:00
log92517c04c7ab0f87b60ed3b9a8c3e544674678fa
tree0bedfd1ce290c0be3c1d37671e428e9d7586d28b
parent58ea88f6cfbaa7a96236a53e26c86ead1ce7c9c2
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

Merge pull request 'package fetching fixes and enhancements' (#31992) from fetch-enhancements into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31992

3 files changed, 157 insertions(+), 114 deletions(-)

lib/std/zig.zig+1
...@@ -744,6 +744,7 @@ pub fn parseTargetQueryOrReportFatalError(...@@ -744,6 +744,7 @@ pub fn parseTargetQueryOrReportFatalError(
744pub const EnvVar = enum {744pub const EnvVar = enum {
745 ZIG_GLOBAL_CACHE_DIR,745 ZIG_GLOBAL_CACHE_DIR,
746 ZIG_LOCAL_CACHE_DIR,746 ZIG_LOCAL_CACHE_DIR,
747 ZIG_LOCAL_PKG_DIR,
747 ZIG_LIB_DIR,748 ZIG_LIB_DIR,
748 ZIG_LIBC,749 ZIG_LIBC,
749 ZIG_BUILD_RUNNER,750 ZIG_BUILD_RUNNER,
src/Package/Fetch.zig+81-69
...@@ -56,6 +56,9 @@ location_tok: std.zig.Ast.TokenIndex,...@@ -56,6 +56,9 @@ location_tok: std.zig.Ast.TokenIndex,
56hash_tok: std.zig.Ast.OptionalTokenIndex,56hash_tok: std.zig.Ast.OptionalTokenIndex,
57name_tok: std.zig.Ast.TokenIndex,57name_tok: std.zig.Ast.TokenIndex,
58lazy_status: LazyStatus,58lazy_status: LazyStatus,
59/// Same as `parent_packge_root` except it is unchanged when recursing into
60/// relative file paths (as opposed to URL).
61remote_package_root: Cache.Path,
59parent_package_root: Cache.Path,62parent_package_root: Cache.Path,
60parent_manifest_ast: ?*const std.zig.Ast,63parent_manifest_ast: ?*const std.zig.Ast,
61prog_node: std.Progress.Node,64prog_node: std.Progress.Node,
...@@ -104,6 +107,12 @@ pub const LazyStatus = enum {...@@ -104,6 +107,12 @@ pub const LazyStatus = enum {
104 unavailable,107 unavailable,
105};108};
106109
110pub const LocalStorage = struct {
111 cache_root: Cache.Path,
112 /// Path to "zig-pkg" inside the package in which the user ran `zig build`.
113 pkg_root: Cache.Path,
114};
115
107/// Contains shared state among all `Fetch` tasks.116/// Contains shared state among all `Fetch` tasks.
108pub const JobQueue = struct {117pub const JobQueue = struct {
109 io: Io,118 io: Io,
...@@ -122,9 +131,8 @@ pub const JobQueue = struct {...@@ -122,9 +131,8 @@ pub const JobQueue = struct {
122 /// This tracks `Fetch` tasks as well as recompression tasks.131 /// This tracks `Fetch` tasks as well as recompression tasks.
123 group: Io.Group = .init,132 group: Io.Group = .init,
124 global_cache: Cache.Directory,133 global_cache: Cache.Directory,
125 local_cache: Cache.Path,134 /// If `null`, indicates fetch globally only.
126 /// Path to "zig-pkg" inside the package in which the user ran `zig build`.135 local_storage: ?*const LocalStorage,
127 root_pkg_path: Cache.Path,
128 /// If true then, no fetching occurs, and:136 /// If true then, no fetching occurs, and:
129 /// * The `global_cache` directory is assumed to be the direct parent137 /// * The `global_cache` directory is assumed to be the direct parent
130 /// directory of on-disk packages rather than having the "p/" directory138 /// directory of on-disk packages rather than having the "p/" directory
...@@ -341,7 +349,7 @@ pub const JobQueue = struct {...@@ -341,7 +349,7 @@ pub const JobQueue = struct {
341 );349 );
342 }350 }
343351
344 fn recompress(jq: *JobQueue, package_hash: Package.Hash) Io.Cancelable!void {352 fn recompress(jq: *JobQueue, package_hash: Package.Hash, package_root: Cache.Path) Io.Cancelable!void {
345 const pkg_hash_slice = package_hash.toSlice();353 const pkg_hash_slice = package_hash.toSlice();
346354
347 const prog_node = jq.prog_node.startFmt(0, "recompress {s}", .{pkg_hash_slice});355 const prog_node = jq.prog_node.startFmt(0, "recompress {s}", .{pkg_hash_slice});
...@@ -359,7 +367,7 @@ pub const JobQueue = struct {...@@ -359,7 +367,7 @@ pub const JobQueue = struct {
359 defer arena_instance.deinit();367 defer arena_instance.deinit();
360 const arena = arena_instance.allocator();368 const arena = arena_instance.allocator();
361369
362 recompressFallible(jq, arena, dest_path, pkg_hash_slice, prog_node) catch |err| switch (err) {370 recompressFallible(jq, arena, dest_path, pkg_hash_slice, package_root, prog_node) catch |err| switch (err) {
363 error.Canceled => |e| return e,371 error.Canceled => |e| return e,
364 error.ReadFailed => comptime unreachable,372 error.ReadFailed => comptime unreachable,
365 error.WriteFailed => comptime unreachable,373 error.WriteFailed => comptime unreachable,
...@@ -372,6 +380,7 @@ pub const JobQueue = struct {...@@ -372,6 +380,7 @@ pub const JobQueue = struct {
372 arena: Allocator,380 arena: Allocator,
373 dest_path: Cache.Path,381 dest_path: Cache.Path,
374 pkg_hash_slice: []const u8,382 pkg_hash_slice: []const u8,
383 package_root: Cache.Path,
375 prog_node: std.Progress.Node,384 prog_node: std.Progress.Node,
376 ) !void {385 ) !void {
377 const gpa = jq.http_client.allocator;386 const gpa = jq.http_client.allocator;
...@@ -386,7 +395,7 @@ pub const JobQueue = struct {...@@ -386,7 +395,7 @@ pub const JobQueue = struct {
386 var scanned_files: std.ArrayList(ScannedFile) = .empty;395 var scanned_files: std.ArrayList(ScannedFile) = .empty;
387 defer scanned_files.deinit(gpa);396 defer scanned_files.deinit(gpa);
388397
389 var pkg_dir = try jq.root_pkg_path.openDir(io, pkg_hash_slice, .{ .iterate = true });398 var pkg_dir = try package_root.root_dir.handle.openDir(io, package_root.sub_path, .{ .iterate = true });
390 defer pkg_dir.close(io);399 defer pkg_dir.close(io);
391400
392 {401 {
...@@ -513,7 +522,6 @@ pub fn run(f: *Fetch) RunError!void {...@@ -513,7 +522,6 @@ pub fn run(f: *Fetch) RunError!void {
513 const eb = &f.error_bundle;522 const eb = &f.error_bundle;
514 const arena = f.arena.allocator();523 const arena = f.arena.allocator();
515 const gpa = f.arena.child_allocator;524 const gpa = f.arena.child_allocator;
516 const local_cache_root = job_queue.local_cache;
517525
518 try eb.init(gpa);526 try eb.init(gpa);
519527
...@@ -534,32 +542,19 @@ pub fn run(f: *Fetch) RunError!void {...@@ -534,32 +542,19 @@ pub fn run(f: *Fetch) RunError!void {
534 );542 );
535 // Packages fetched by URL may not use relative paths to escape outside the543 // Packages fetched by URL may not use relative paths to escape outside the
536 // fetched package directory from within the package cache.544 // fetched package directory from within the package cache.
537 if (pkg_root.root_dir.eql(local_cache_root.root_dir)) {545
538 // `parent_package_root.sub_path` contains a path like this:546 // This code path is only reachable recursively and the sub_path
539 // "p/$hash", or547 // will already have been resolved to no longer have extra ".." or
540 // "p/$hash/foo", with possibly more directories after "foo".548 // "." components.
541 // We want to fail unless the resolved relative path has a549 assert(job_queue.local_storage != null);
542 // prefix of "p/$hash/".550 log.debug("checking pkg root \"{s}\" against parent package root \"{s}\"", .{
543 const prefix_len: usize = if (job_queue.read_only) 0 else "p/".len;551 pkg_root.sub_path, f.remote_package_root.sub_path,
544 const parent_sub_path = f.parent_package_root.sub_path;552 });
545 const end = find_end: {553 assert(pkg_root.root_dir.eql(f.remote_package_root.root_dir));
546 if (parent_sub_path.len > prefix_len) {554 if (!std.mem.startsWith(u8, pkg_root.sub_path, f.remote_package_root.sub_path)) return f.fail(
547 // Use `isSep` instead of `indexOfScalarPos` to account for555 f.location_tok,
548 // Windows accepting both `\` and `/` as path separators.556 try eb.printString("dependency path outside project: '{f}'", .{pkg_root}),
549 for (parent_sub_path[prefix_len..], prefix_len..) |c, i| {557 );
550 if (std.fs.path.isSep(c)) break :find_end i;
551 }
552 }
553 break :find_end parent_sub_path.len;
554 };
555 const expected_prefix = parent_sub_path[0..end];
556 if (!std.mem.startsWith(u8, pkg_root.sub_path, expected_prefix)) {
557 return f.fail(
558 f.location_tok,
559 try eb.printString("dependency path outside project: '{f}'", .{pkg_root}),
560 );
561 }
562 }
563 f.package_root = pkg_root;558 f.package_root = pkg_root;
564 try loadManifest(f, pkg_root);559 try loadManifest(f, pkg_root);
565 if (!f.has_build_zig) try checkBuildFileExistence(f);560 if (!f.has_build_zig) try checkBuildFileExistence(f);
...@@ -602,6 +597,7 @@ pub fn run(f: *Fetch) RunError!void {...@@ -602,6 +597,7 @@ pub fn run(f: *Fetch) RunError!void {
602 log.debug("using fork {f} for {s}", .{ fork.path, fork.manifest.name });597 log.debug("using fork {f} for {s}", .{ fork.path, fork.manifest.name });
603 fork.uses += 1;598 fork.uses += 1;
604 f.package_root = fork.path;599 f.package_root = fork.path;
600 f.remote_package_root = f.package_root;
605 f.manifest_ast = fork.manifest_ast;601 f.manifest_ast = fork.manifest_ast;
606 f.manifest = fork.manifest;602 f.manifest = fork.manifest;
607 f.have_manifest = true;603 f.have_manifest = true;
...@@ -610,31 +606,34 @@ pub fn run(f: *Fetch) RunError!void {...@@ -610,31 +606,34 @@ pub fn run(f: *Fetch) RunError!void {
610 return queueJobsForDeps(f);606 return queueJobsForDeps(f);
611 }607 }
612608
613 const package_root = try job_queue.root_pkg_path.join(arena, expected_hash.toSlice());609 if (job_queue.local_storage) |ls| {
614 if (package_root.root_dir.handle.access(io, package_root.sub_path, .{})) |_| {610 const package_root = try ls.pkg_root.join(arena, expected_hash.toSlice());
615 assert(f.lazy_status != .unavailable);611 if (package_root.root_dir.handle.access(io, package_root.sub_path, .{})) |_| {
616 f.package_root = package_root;612 assert(f.lazy_status != .unavailable);
617 try loadManifest(f, f.package_root);613 f.package_root = package_root;
618 try checkBuildFileExistence(f);614 f.remote_package_root = f.package_root;
619 if (!job_queue.recursive) return;615 try loadManifest(f, f.package_root);
620 return queueJobsForDeps(f);616 try checkBuildFileExistence(f);
621 } else |err| switch (err) {617 if (!job_queue.recursive) return;
622 error.FileNotFound => {618 return queueJobsForDeps(f);
623 log.debug("FileNotFound: {f}", .{package_root});619 } else |err| switch (err) {
624 if (job_queue.read_only and f.lazy_status == .eager) return f.fail(620 error.FileNotFound => {
625 f.name_tok,621 log.debug("FileNotFound: {f}", .{package_root});
626 try eb.printString("package not found at '{f}'", .{package_root}),622 if (job_queue.read_only and f.lazy_status == .eager) return f.fail(
627 );623 f.name_tok,
628 },624 try eb.printString("package not found at '{f}'", .{package_root}),
629 error.Canceled => |e| return e,625 );
630 else => |e| {626 },
631 try eb.addRootErrorMessage(.{627 error.Canceled => |e| return e,
632 .msg = try eb.printString("unable to open package cache directory {f}: {t}", .{628 else => |e| {
633 package_root, e,629 try eb.addRootErrorMessage(.{
634 }),630 .msg = try eb.printString("unable to open package cache directory {f}: {t}", .{
635 });631 package_root, e,
636 return error.FetchFailed;632 }),
637 },633 });
634 return error.FetchFailed;
635 },
636 }
638 }637 }
639638
640 // Check global cache before remote fetch.639 // Check global cache before remote fetch.
...@@ -713,7 +712,14 @@ fn runResource(...@@ -713,7 +712,14 @@ fn runResource(
713 break :r x;712 break :r x;
714 };713 };
715 const tmp_dir_sub_path = ".tmp-" ++ std.fmt.hex(rand_int);714 const tmp_dir_sub_path = ".tmp-" ++ std.fmt.hex(rand_int);
716 const tmp_directory_path = try job_queue.root_pkg_path.join(arena, tmp_dir_sub_path);715 const tmp_tmp_dir_sub_path = "tmp/" ++ tmp_dir_sub_path;
716 const tmp_directory_path: Cache.Path = if (job_queue.local_storage) |ls|
717 try ls.pkg_root.join(arena, tmp_dir_sub_path)
718 else
719 .{
720 .root_dir = job_queue.global_cache,
721 .sub_path = tmp_tmp_dir_sub_path,
722 };
717723
718 const package_sub_path = blk: {724 const package_sub_path = blk: {
719 var tmp_directory: Cache.Directory = .{725 var tmp_directory: Cache.Directory = .{
...@@ -772,19 +778,24 @@ fn runResource(...@@ -772,19 +778,24 @@ fn runResource(
772 // zig package directory untouched as it may be in use. This is done even778 // zig package directory untouched as it may be in use. This is done even
773 // if the hash is invalid, in case the package with the different hash is779 // if the hash is invalid, in case the package with the different hash is
774 // used in the future.780 // used in the future.
775 f.package_root = try job_queue.root_pkg_path.join(arena, computed_package_hash.toSlice());781 if (job_queue.local_storage) |ls| {
776 renameTmpIntoCache(io, package_sub_path, f.package_root) catch |err| {782 f.package_root = try ls.pkg_root.join(arena, computed_package_hash.toSlice());
777 try eb.addRootErrorMessage(.{ .msg = try eb.printString(783 renameTmpIntoCache(io, package_sub_path, f.package_root) catch |err| {
778 "unable to rename temporary directory {f} into package cache directory {f}: {t}",784 try eb.addRootErrorMessage(.{ .msg = try eb.printString(
779 .{ package_sub_path, f.package_root, err },785 "unable to rename temporary directory {f} into package cache directory {f}: {t}",
780 ) });786 .{ package_sub_path, f.package_root, err },
781 return error.FetchFailed;787 ) });
782 };788 return error.FetchFailed;
789 };
790 } else {
791 f.package_root = tmp_directory_path;
792 }
793 f.remote_package_root = f.package_root;
783794
784 if (!disable_recompress) {795 if (!disable_recompress) {
785 // Spin off a task to recompress the tarball, with filtered files deleted, into796 // Spin off a task to recompress the tarball, with filtered files deleted, into
786 // the global cache.797 // the global cache.
787 job_queue.group.async(io, JobQueue.recompress, .{ job_queue, computed_package_hash });798 job_queue.group.async(io, JobQueue.recompress, .{ job_queue, computed_package_hash, f.package_root });
788 }799 }
789800
790 // Remove temporary directory root if not already renamed to global cache.801 // Remove temporary directory root if not already renamed to global cache.
...@@ -991,6 +1002,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {...@@ -991,6 +1002,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {
991 .all => .eager,1002 .all => .eager,
992 },1003 },
993 .parent_package_root = f.package_root,1004 .parent_package_root = f.package_root,
1005 .remote_package_root = f.remote_package_root,
994 .parent_manifest_ast = &f.manifest_ast,1006 .parent_manifest_ast = &f.manifest_ast,
995 .prog_node = f.prog_node,1007 .prog_node = f.prog_node,
996 .job_queue = f.job_queue,1008 .job_queue = f.job_queue,
...@@ -1185,7 +1197,7 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u...@@ -1185,7 +1197,7 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u
1185 if (ascii.eqlIgnoreCase(uri.scheme, "file")) {1197 if (ascii.eqlIgnoreCase(uri.scheme, "file")) {
1186 const path = try uri.path.toRawMaybeAlloc(arena);1198 const path = try uri.path.toRawMaybeAlloc(arena);
1187 const file = f.parent_package_root.openFile(io, path, .{}) catch |err| {1199 const file = f.parent_package_root.openFile(io, path, .{}) catch |err| {
1188 return f.fail(f.location_tok, try eb.printString("unable to open '{f}{s}': {t}", .{1200 return f.fail(f.location_tok, try eb.printString("unable to open {f}/{s}: {t}", .{
1189 f.parent_package_root, path, err,1201 f.parent_package_root, path, err,
1190 }));1202 }));
1191 };1203 };
src/main.zig+75-45
...@@ -1360,12 +1360,7 @@ fn buildOutputType(...@@ -1360,12 +1360,7 @@ fn buildOutputType(
1360 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {1360 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
1361 override_lib_dir = args_iter.nextOrFatal();1361 override_lib_dir = args_iter.nextOrFatal();
1362 } else if (mem.eql(u8, arg, "--debug-log")) {1362 } else if (mem.eql(u8, arg, "--debug-log")) {
1363 if (!build_options.enable_logging) {1363 try addDebugLog(arena, args_iter.nextOrFatal());
1364 warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
1365 _ = args_iter.nextOrFatal();
1366 } else {
1367 try log_scopes.append(arena, args_iter.nextOrFatal());
1368 }
1369 } else if (mem.eql(u8, arg, "--listen")) {1364 } else if (mem.eql(u8, arg, "--listen")) {
1370 const next_arg = args_iter.nextOrFatal();1365 const next_arg = args_iter.nextOrFatal();
1371 if (mem.eql(u8, next_arg, "-")) {1366 if (mem.eql(u8, next_arg, "-")) {
...@@ -4962,6 +4957,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -4962,6 +4957,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
4962 var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);4957 var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);
4963 var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);4958 var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
4964 var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map);4959 var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map);
4960 var override_pkg_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_PKG_DIR.get(environ_map);
4965 var override_build_runner: ?[]const u8 = EnvVar.ZIG_BUILD_RUNNER.get(environ_map);4961 var override_build_runner: ?[]const u8 = EnvVar.ZIG_BUILD_RUNNER.get(environ_map);
4966 var child_argv: std.ArrayList([]const u8) = .empty;4962 var child_argv: std.ArrayList([]const u8) = .empty;
4967 var forks: std.ArrayList(Fork) = .empty;4963 var forks: std.ArrayList(Fork) = .empty;
...@@ -5053,6 +5049,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5053,6 +5049,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5053 i += 1;5049 i += 1;
5054 override_local_cache_dir = args[i];5050 override_local_cache_dir = args[i];
5055 continue;5051 continue;
5052 } else if (mem.eql(u8, arg, "--pkg-dir")) {
5053 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
5054 i += 1;
5055 override_pkg_dir = args[i];
5056 continue;
5056 } else if (mem.eql(u8, arg, "--global-cache-dir")) {5057 } else if (mem.eql(u8, arg, "--global-cache-dir")) {
5057 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});5058 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
5058 i += 1;5059 i += 1;
...@@ -5097,11 +5098,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5097,11 +5098,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5097 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});5098 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
5098 try child_argv.appendSlice(arena, args[i .. i + 2]);5099 try child_argv.appendSlice(arena, args[i .. i + 2]);
5099 i += 1;5100 i += 1;
5100 if (!build_options.enable_logging) {5101 try addDebugLog(arena, args[i]);
5101 warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
5102 } else {
5103 try log_scopes.append(arena, args[i]);
5104 }
5105 continue;5102 continue;
5106 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {5103 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
5107 if (build_options.enable_debug_extensions) {5104 if (build_options.enable_debug_extensions) {
...@@ -5332,9 +5329,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5332,9 +5329,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5332 .parent = root_mod,5329 .parent = root_mod,
5333 });5330 });
53345331
5335 var cleanup_build_dir: ?Io.Dir = null;
5336 defer if (cleanup_build_dir) |*dir| dir.close(io);
5337
5338 if (dev.env.supports(.fetch_command)) {5332 if (dev.env.supports(.fetch_command)) {
5339 const fetch_prog_node = root_prog_node.start("Fetch Packages", 0);5333 const fetch_prog_node = root_prog_node.start("Fetch Packages", 0);
5340 defer fetch_prog_node.end();5334 defer fetch_prog_node.end();
...@@ -5346,33 +5340,29 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5346,33 +5340,29 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5346 .io = io,5340 .io = io,
5347 .http_client = &http_client,5341 .http_client = &http_client,
5348 .global_cache = dirs.global_cache,5342 .global_cache = dirs.global_cache,
5349 .local_cache = .{ .root_dir = dirs.local_cache, .sub_path = "" },5343 .local_storage = &.{
5350 .root_pkg_path = .{ .root_dir = build_root.directory, .sub_path = "zig-pkg" },5344 .cache_root = .{ .root_dir = dirs.local_cache, .sub_path = "" },
5351 .read_only = false,5345 .pkg_root = if (override_pkg_dir) |p|
5346 .initCwd(p)
5347 else if (system_pkg_dir_path) |p|
5348 .initCwd(p)
5349 else
5350 .{
5351 .root_dir = build_root.directory,
5352 .sub_path = "zig-pkg",
5353 },
5354 },
5352 .recursive = true,5355 .recursive = true,
5353 .debug_hash = false,5356 .debug_hash = false,
5354 .unlazy_set = unlazy_set,5357 .unlazy_set = unlazy_set,
5355 .fork_set = fork_set,5358 .fork_set = fork_set,
5356 .mode = fetch_mode,5359 .mode = fetch_mode,
5357 .prog_node = fetch_prog_node,5360 .prog_node = fetch_prog_node,
5361 .read_only = system_pkg_dir_path != null,
5358 };5362 };
5359 defer job_queue.deinit();5363 defer job_queue.deinit();
53605364
5361 if (system_pkg_dir_path) |p| {5365 if (system_pkg_dir_path == null) {
5362 const system_pkg_path: Path = .{
5363 .root_dir = .{
5364 .path = p,
5365 .handle = Io.Dir.cwd().openDir(io, p, .{}) catch |err| {
5366 fatal("unable to open system package directory '{s}': {t}", .{ p, err });
5367 },
5368 },
5369 .sub_path = "",
5370 };
5371 job_queue.global_cache = system_pkg_path.root_dir;
5372 job_queue.root_pkg_path = system_pkg_path;
5373 job_queue.read_only = true;
5374 cleanup_build_dir = job_queue.global_cache.handle;
5375 } else {
5376 try http_client.initDefaultProxies(arena, environ_map);5366 try http_client.initDefaultProxies(arena, environ_map);
5377 }5367 }
53785368
...@@ -5388,6 +5378,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5388,6 +5378,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5388 .hash_tok = .none,5378 .hash_tok = .none,
5389 .name_tok = 0,5379 .name_tok = 0,
5390 .lazy_status = .eager,5380 .lazy_status = .eager,
5381 .remote_package_root = phantom_package_root,
5391 .parent_package_root = phantom_package_root,5382 .parent_package_root = phantom_package_root,
5392 .parent_manifest_ast = null,5383 .parent_manifest_ast = null,
5393 .prog_node = fetch_prog_node,5384 .prog_node = fetch_prog_node,
...@@ -5408,6 +5399,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5408,6 +5399,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
54085399
5409 .module = build_mod,5400 .module = build_mod,
5410 };5401 };
5402
5411 job_queue.all_fetches.appendAssumeCapacity(&fetch);5403 job_queue.all_fetches.appendAssumeCapacity(&fetch);
54125404
5413 job_queue.table.putAssumeCapacityNoClobber(5405 job_queue.table.putAssumeCapacityNoClobber(
...@@ -7040,7 +7032,10 @@ const usage_fetch =...@@ -7040,7 +7032,10 @@ const usage_fetch =
7040 \\Options:7032 \\Options:
7041 \\ -h, --help Print this help and exit7033 \\ -h, --help Print this help and exit
7042 \\ --global-cache-dir [path] Override path to global Zig cache directory7034 \\ --global-cache-dir [path] Override path to global Zig cache directory
7035 \\ --cache-dir [path] Override path to local cache directory
7036 \\ --pkg-dir [path] Override path to local package directory
7043 \\ --debug-hash Print verbose hash information to stdout7037 \\ --debug-hash Print verbose hash information to stdout
7038 \\ --debug-log [scope] Enable printing debug/info log messages for scope
7044 \\ --save Add the fetched package to build.zig.zon7039 \\ --save Add the fetched package to build.zig.zon
7045 \\ --save=[name] Add the fetched package to build.zig.zon as name7040 \\ --save=[name] Add the fetched package to build.zig.zon as name
7046 \\ --save-exact Add the fetched package to build.zig.zon, storing the URL verbatim7041 \\ --save-exact Add the fetched package to build.zig.zon, storing the URL verbatim
...@@ -7060,6 +7055,8 @@ fn cmdFetch(...@@ -7060,6 +7055,8 @@ fn cmdFetch(
7060 const color: Color = .auto;7055 const color: Color = .auto;
7061 var opt_path_or_url: ?[]const u8 = null;7056 var opt_path_or_url: ?[]const u8 = null;
7062 var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);7057 var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
7058 var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map);
7059 var override_pkg_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_PKG_DIR.get(environ_map);
7063 var debug_hash: bool = false;7060 var debug_hash: bool = false;
7064 var save: union(enum) {7061 var save: union(enum) {
7065 no,7062 no,
...@@ -7076,11 +7073,23 @@ fn cmdFetch(...@@ -7076,11 +7073,23 @@ fn cmdFetch(
7076 try Io.File.stdout().writeStreamingAll(io, usage_fetch);7073 try Io.File.stdout().writeStreamingAll(io, usage_fetch);
7077 return cleanExit(io);7074 return cleanExit(io);
7078 } else if (mem.eql(u8, arg, "--global-cache-dir")) {7075 } else if (mem.eql(u8, arg, "--global-cache-dir")) {
7079 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});7076 if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg});
7080 i += 1;7077 i += 1;
7081 override_global_cache_dir = args[i];7078 override_global_cache_dir = args[i];
7079 } else if (mem.eql(u8, arg, "--cache-dir")) {
7080 if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg});
7081 i += 1;
7082 override_local_cache_dir = args[i];
7083 } else if (mem.eql(u8, arg, "--pkg-dir")) {
7084 if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg});
7085 i += 1;
7086 override_pkg_dir = args[i];
7082 } else if (mem.eql(u8, arg, "--debug-hash")) {7087 } else if (mem.eql(u8, arg, "--debug-hash")) {
7083 debug_hash = true;7088 debug_hash = true;
7089 } else if (mem.eql(u8, arg, "--debug-log")) {
7090 if (i + 1 >= args.len) fatal("expected argument after: {s}", .{arg});
7091 i += 1;
7092 try addDebugLog(arena, args[i]);
7084 } else if (mem.eql(u8, arg, "--save")) {7093 } else if (mem.eql(u8, arg, "--save")) {
7085 save = .{ .yes = null };7094 save = .{ .yes = null };
7086 } else if (mem.cutPrefix(u8, arg, "--save=")) |rest| {7095 } else if (mem.cutPrefix(u8, arg, "--save=")) |rest| {
...@@ -7121,27 +7130,39 @@ fn cmdFetch(...@@ -7121,27 +7130,39 @@ fn cmdFetch(
7121 };7130 };
7122 defer global_cache_directory.handle.close(io);7131 defer global_cache_directory.handle.close(io);
71237132
7133 var local_storage: Package.Fetch.LocalStorage = undefined;
7134 var build_root: BuildRoot = undefined;
7135 var build_root_initialized = false;
7136 defer if (build_root_initialized) build_root.deinit(io);
7137
7124 const cwd_path = try introspect.getResolvedCwd(io, arena);7138 const cwd_path = try introspect.getResolvedCwd(io, arena);
71257139
7126 var build_root = try findBuildRoot(arena, io, .{7140 const local_storage_ptr = switch (save) {
7127 .cwd_path = cwd_path,7141 .no => null,
7128 });7142 .yes, .exact => ls: {
7129 defer build_root.deinit(io);7143 build_root = try findBuildRoot(arena, io, .{ .cwd_path = cwd_path });
7144 build_root_initialized = true;
7145
7146 local_storage = .{
7147 .cache_root = if (override_local_cache_dir) |p| .initCwd(p) else .{
7148 .root_dir = build_root.directory,
7149 .sub_path = ".zig-cache",
7150 },
7151 .pkg_root = if (override_pkg_dir) |p| .initCwd(p) else .{
7152 .root_dir = build_root.directory,
7153 .sub_path = "zig-pkg",
7154 },
7155 };
71307156
7131 const local_cache_path: Path = .{7157 break :ls &local_storage;
7132 .root_dir = build_root.directory,7158 },
7133 .sub_path = ".zig-cache",
7134 };7159 };
71357160
7136 var job_queue: Package.Fetch.JobQueue = .{7161 var job_queue: Package.Fetch.JobQueue = .{
7137 .io = io,7162 .io = io,
7138 .http_client = &http_client,7163 .http_client = &http_client,
7139 .global_cache = global_cache_directory,7164 .global_cache = global_cache_directory,
7140 .local_cache = local_cache_path,7165 .local_storage = local_storage_ptr,
7141 .root_pkg_path = .{
7142 .root_dir = build_root.directory,
7143 .sub_path = "zig-pkg",
7144 },
7145 .recursive = false,7166 .recursive = false,
7146 .read_only = false,7167 .read_only = false,
7147 .debug_hash = debug_hash,7168 .debug_hash = debug_hash,
...@@ -7157,6 +7178,7 @@ fn cmdFetch(...@@ -7157,6 +7178,7 @@ fn cmdFetch(
7157 .hash_tok = .none,7178 .hash_tok = .none,
7158 .name_tok = 0,7179 .name_tok = 0,
7159 .lazy_status = .eager,7180 .lazy_status = .eager,
7181 .remote_package_root = undefined,
7160 .parent_package_root = undefined,7182 .parent_package_root = undefined,
7161 .parent_manifest_ast = null,7183 .parent_manifest_ast = null,
7162 .prog_node = root_prog_node,7184 .prog_node = root_prog_node,
...@@ -7801,3 +7823,11 @@ fn randInt(io: Io, comptime T: type) T {...@@ -7801,3 +7823,11 @@ fn randInt(io: Io, comptime T: type) T {
7801 io.random(@ptrCast(&x));7823 io.random(@ptrCast(&x));
7802 return x;7824 return x;
7803}7825}
7826
7827fn addDebugLog(arena: Allocator, scope_name: []const u8) error{OutOfMemory}!void {
7828 if (!build_options.enable_logging) {
7829 warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
7830 } else {
7831 try log_scopes.append(arena, scope_name);
7832 }
7833}