diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2023-10-01 23:05:01 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2023-10-02 17:02:25 -0700 |
| commit | ef9966c9855dd855afda767f212abec6e5a36307 (patch) | |
| tree | 4f23d4468f3d9d44c16868becad6b0c6bb9083d8 /src/main.zig | |
| parent | 309c53295f26999065e4dc76cef4d90f8d85fb38 (diff) | |
| download | zig-ef9966c9855dd855afda767f212abec6e5a36307.tar.gz zig-ef9966c9855dd855afda767f212abec6e5a36307.zip | |
introduce the 'zig fetch' command + symlink support
zig fetch [options] <url>
zig fetch [options] <path>
Fetches a package which is found at <url> or <path> into the global
cache directory, printing the package hash to stdout.
Closes #16972
Related to #14280
Additionally, this commit:
* Adds uncompressed .tar support to package fetching
* Introduces symlink support to package fetching
Diffstat (limited to 'src/main.zig')
| -rw-r--r-- | src/main.zig | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/src/main.zig b/src/main.zig index 26b08a9d47..e26efc3251 100644 --- a/src/main.zig +++ b/src/main.zig @@ -84,6 +84,7 @@ const normal_usage = \\Commands: \\ \\ build Build project from build.zig + \\ fetch Copy a package into global cache and print its hash \\ init-exe Initialize a `zig build` application in the cwd \\ init-lib Initialize a `zig build` library in the cwd \\ @@ -303,6 +304,8 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi return cmdFmt(gpa, arena, cmd_args); } else if (mem.eql(u8, cmd, "objcopy")) { return @import("objcopy.zig").cmdObjCopy(gpa, arena, cmd_args); + } else if (mem.eql(u8, cmd, "fetch")) { + return cmdFetch(gpa, arena, cmd_args); } else if (mem.eql(u8, cmd, "libc")) { return cmdLibC(gpa, cmd_args); } else if (mem.eql(u8, cmd, "init-exe")) { @@ -6589,3 +6592,127 @@ fn parseRcIncludes(arg: []const u8) Compilation.RcIncludes { return std.meta.stringToEnum(Compilation.RcIncludes, arg) orelse fatal("unsupported rc includes type: '{s}'", .{arg}); } + +pub const usage_fetch = + \\Usage: zig fetch [options] <url> + \\Usage: zig fetch [options] <path> + \\ + \\ Copy a package into the global cache and print its hash. + \\ + \\Options: + \\ -h, --help Print this help and exit + \\ --global-cache-dir [path] Override path to global Zig cache directory + \\ +; + +fn cmdFetch( + gpa: Allocator, + arena: Allocator, + args: []const []const u8, +) !void { + var opt_url: ?[]const u8 = null; + var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR"); + + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { + const stdout = io.getStdOut().writer(); + try stdout.writeAll(usage_fetch); + return cleanExit(); + } else if (mem.eql(u8, arg, "--global-cache-dir")) { + if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); + i += 1; + override_global_cache_dir = args[i]; + continue; + } else { + fatal("unrecognized parameter: '{s}'", .{arg}); + } + } else if (opt_url != null) { + fatal("unexpected extra parameter: '{s}'", .{arg}); + } else { + opt_url = arg; + } + } + } + + const url = opt_url orelse fatal("missing url or path parameter", .{}); + + var thread_pool: ThreadPool = undefined; + try thread_pool.init(.{ .allocator = gpa }); + defer thread_pool.deinit(); + + var http_client: std.http.Client = .{ .allocator = gpa }; + defer http_client.deinit(); + + var progress: std.Progress = .{ .dont_print_on_dumb = true }; + const root_prog_node = progress.start("Fetch", 0); + defer root_prog_node.end(); + + var report: Package.Report = .{ + .ast = null, + .directory = undefined, + .error_bundle = undefined, + }; + + var global_cache_directory: Compilation.Directory = l: { + const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); + break :l .{ + .handle = try fs.cwd().makeOpenPath(p, .{}), + .path = p, + }; + }; + defer global_cache_directory.handle.close(); + + var readable_resource: Package.ReadableResource = rr: { + if (fs.cwd().openIterableDir(url, .{})) |dir| { + break :rr .{ + .path = try gpa.dupe(u8, url), + .resource = .{ .dir = dir }, + }; + } else |dir_err| { + const file_err = if (dir_err == error.NotDir) e: { + if (fs.cwd().openFile(url, .{})) |f| { + break :rr .{ + .path = try gpa.dupe(u8, url), + .resource = .{ .file = f }, + }; + } else |err| break :e err; + } else dir_err; + + const uri = std.Uri.parse(url) catch |uri_err| { + fatal("'{s}' could not be recognized as a file path ({s}) or an URL ({s})", .{ + url, @errorName(file_err), @errorName(uri_err), + }); + }; + const fetch_location = try Package.FetchLocation.initUri(uri, 0, report); + const cwd: Cache.Directory = .{ + .handle = fs.cwd(), + .path = null, + }; + break :rr try fetch_location.fetch(gpa, cwd, &http_client, 0, report); + } + }; + defer readable_resource.deinit(gpa); + + var package_location = try readable_resource.unpack( + gpa, + &thread_pool, + global_cache_directory, + 0, + report, + root_prog_node, + ); + defer package_location.deinit(gpa); + + const hex_digest = Package.Manifest.hexDigest(package_location.hash); + + progress.done = true; + progress.refresh(); + + try io.getStdOut().writeAll(hex_digest ++ "\n"); + + return cleanExit(); +} |
