aboutsummaryrefslogtreecommitdiff
path: root/src/main.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-10-01 23:05:01 -0700
committerAndrew Kelley <andrew@ziglang.org>2023-10-02 17:02:25 -0700
commitef9966c9855dd855afda767f212abec6e5a36307 (patch)
tree4f23d4468f3d9d44c16868becad6b0c6bb9083d8 /src/main.zig
parent309c53295f26999065e4dc76cef4d90f8d85fb38 (diff)
downloadzig-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.zig127
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();
+}