feature. See also
. The project being documented here (as the example) is the Zig library itself.
Maker.cmdFetch
fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void
File
Code
fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {
const environ_map = &graph.environ_map;
const io = graph.io;
const arena = graph.arena;
const color: Color = Color.settingFromEnvironment(environ_map);
var opt_path_or_url: ?[]const u8 = null;
var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map);
var override_pkg_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_PKG_DIR.get(environ_map);
var debug_hash: bool = false;
var save: union(enum) {
no,
yes: ?[]const u8,
exact: ?[]const u8,
} = .no;
var arg_i: usize = 0;
while (nextArg(args, &arg_i)) |arg| {
if (mem.startsWith(u8, arg, "-")) {
if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
try Io.File.stdout().writeStreamingAll(io, usage_fetch);
return process.cleanExit(io);
} else if (mem.eql(u8, arg, "--global-cache-dir")) {
override_global_cache_dir = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--cache-dir")) {
override_local_cache_dir = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--pkg-dir")) {
override_pkg_dir = nextArgOrFatal(args, &arg_i);
} else if (mem.eql(u8, arg, "--debug-hash")) {
debug_hash = true;
} else if (mem.eql(u8, arg, "--debug-log")) {
try graph.debug_log_scopes.append(arena, nextArgOrFatal(args, &arg_i));
} else if (mem.eql(u8, arg, "--save")) {
save = .{ .yes = null };
} else if (mem.cutPrefix(u8, arg, "--save=")) |rest| {
save = .{ .yes = rest };
} else if (mem.eql(u8, arg, "--save-exact")) {
save = .{ .exact = null };
} else if (mem.cutPrefix(u8, arg, "--save-exact=")) |rest| {
save = .{ .exact = rest };
} else {
fatal("unrecognized parameter: {q}", .{arg});
}
} else if (opt_path_or_url != null) {
fatal("unexpected extra parameter: {q}", .{arg});
} else {
opt_path_or_url = arg;
}
}
const path_or_url = opt_path_or_url orelse fatal("missing url or path parameter", .{});
var http_client: std.http.Client = .{ .allocator = gpa, .io = io };
defer http_client.deinit();
try http_client.initDefaultProxies(arena, environ_map);
var root_prog_node = std.Progress.start(io, .{
.root_name = "Fetch",
});
defer root_prog_node.end();
var local_storage: Fetch.LocalStorage = undefined;
var build_root: BuildRoot = undefined;
var build_root_initialized = false;
defer if (build_root_initialized) build_root.deinit(io);
const cwd_path = try std.zig.getResolvedCwd(io, arena);
const local_storage_ptr = switch (save) {
.no => null,
.yes, .exact => ls: {
build_root = try findBuildRoot(arena, io, .{ .cwd_path = cwd_path });
build_root_initialized = true;
local_storage = .{
.cache_root = if (override_local_cache_dir) |p| .initCwd(p) else .{
.root_dir = build_root.directory,
.sub_path = ".zig-cache",
},
.pkg_root = if (override_pkg_dir) |p| .initCwd(p) else .{
.root_dir = build_root.directory,
.sub_path = "zig-pkg",
},
};
break :ls &local_storage;
},
};
var job_queue: Fetch.JobQueue = .{
.io = io,
.http_client = &http_client,
.global_cache = graph.global_cache_root,
.local_storage = local_storage_ptr,
.recursive = false,
.read_only = false,
.debug_hash = debug_hash,
.mode = .all,
.prog_node = root_prog_node,
};
defer job_queue.deinit();
var fetch: Fetch = .{
.arena = std.heap.ArenaAllocator.init(gpa),
.location = .{ .path_or_url = path_or_url },
.location_tok = 0,
.hash_tok = .none,
.name_tok = 0,
.lazy_status = .eager,
.remote_package_root = undefined,
.parent_package_root = undefined,
.parent_manifest_ast = null,
.prog_node = root_prog_node,
.job_queue = &job_queue,
.omit_missing_hash_error = true,
.allow_missing_paths_field = false,
.use_latest_commit = true,
.package_root = undefined,
.error_bundle = undefined,
.manifest = undefined,
.manifest_ast = undefined,
.have_manifest = false,
.computed_hash = undefined,
.has_build_zig = false,
.oom_flag = false,
.latest_commit = null,
.cli_module = null,
};
defer fetch.deinit();
fetch.run() catch |err| switch (err) {
error.OutOfMemory, error.Canceled => |e| return e,
error.FetchFailed => {},
};
try job_queue.group.await(io);
if (fetch.error_bundle.root_list.items.len > 0) {
var errors = try fetch.error_bundle.toOwnedBundle("");
errors.renderToStderr(io, .{}, color) catch {};
process.exit(1);
}
const package_hash = fetch.computedPackageHash();
const package_hash_slice = package_hash.toSlice();
root_prog_node.end();
root_prog_node = .{ .index = .none };
const name = switch (save) {
.no => {
var data: [2][]const u8 = .{ package_hash_slice, "\n" };
const w = initStdoutWriter(io);
w.writeVecAll(&data) catch return stdout_writer_allocation.err.?;
try stdout_writer_allocation.flush();
return process.cleanExit(io);
},
.yes, .exact => |name| name: {
if (name) |n| break :name n;
if (!fetch.have_manifest)
fatal("unable to determine name; fetched package has no build.zig.zon file", .{});
break :name fetch.manifest.name;
},
};
const init_root_name = Dir.path.basename(build_root.directory.path orelse cwd_path);
var manifest, var ast = try loadManifest(gpa, arena, io, .{
.root_name = try sanitizeExampleName(arena, init_root_name),
.dir = build_root.directory.handle,
.color = color,
});
defer {
manifest.deinit(gpa);
ast.deinit(gpa);
}
var fixups: std.zig.Ast.Render.Fixups = .{};
defer fixups.deinit(gpa);
var saved_path_or_url = path_or_url;
if (fetch.latest_commit) |latest_commit| resolved: {
const latest_commit_hex = try arena.print("{f}", .{latest_commit});
var uri = try std.Uri.parse(path_or_url);
if (uri.fragment) |fragment| {
const target_ref = try fragment.toRawMaybeAlloc(arena);
if (std.mem.eql(u8, target_ref, latest_commit_hex)) break :resolved;
log.info("resolved ref {q} to commit {s}", .{ target_ref, latest_commit_hex });
uri.query = .{ .percent_encoded = try arena.print("ref={f}", .{
std.fmt.alt(fragment, .formatEscaped),
}) };
} else {
log.info("resolved to commit {s}", .{latest_commit_hex});
}
uri.fragment = .{ .raw = latest_commit_hex };
switch (save) {
.yes => saved_path_or_url = try arena.print("{f}", .{uri}),
.no, .exact => {},
}
}
const new_node_init = try arena.print(
\\.{{
\\ .url = "{f}",
\\ .hash = "{f}",
\\ }}
, .{
std.zig.fmtString(saved_path_or_url),
std.zig.fmtString(package_hash_slice),
});
const new_node_text = try arena.print(".{f} = {s},\n", .{
std.zig.fmtIdPU(name), new_node_init,
});
const dependencies_init = try arena.print(".{{\n {s} }}", .{
new_node_text,
});
const dependencies_text = try arena.print(".dependencies = {s},\n", .{
dependencies_init,
});
if (manifest.dependencies.get(name)) |dep| {
if (dep.hash) |h| {
switch (dep.location) {
.url => |u| {
if (mem.eql(u8, h, package_hash_slice) and mem.eql(u8, u, saved_path_or_url)) {
log.info("existing dependency named {q} is up-to-date", .{name});
process.exit(0);
}
},
.path => {},
}
}
const location_replace = try arena.print("{q}", .{saved_path_or_url});
const hash_replace = try arena.print("{q}", .{package_hash_slice});
log.warn("overwriting existing dependency named {q}", .{name});
try fixups.replace_nodes_with_string.put(gpa, dep.location_node, location_replace);
if (dep.hash_node.unwrap()) |hash_node| {
try fixups.replace_nodes_with_string.put(gpa, hash_node, hash_replace);
} else {
}
} else if (manifest.dependencies.count() > 0) {
const deps = manifest.dependencies.values();
const last_dep_node = deps[deps.len - 1].node;
try fixups.append_string_after_node.put(gpa, last_dep_node, new_node_text);
} else if (manifest.dependencies_node.unwrap()) |dependencies_node| {
try fixups.replace_nodes_with_string.put(gpa, dependencies_node, dependencies_init);
} else {
try fixups.append_string_after_node.put(gpa, manifest.version_node, dependencies_text);
}
var aw: Io.Writer.Allocating = .init(gpa);
defer aw.deinit();
try ast.render(gpa, &aw.writer, fixups);
const rendered = aw.written();
build_root.directory.handle.writeFile(io, .{ .sub_path = Package.Manifest.basename, .data = rendered }) catch |err| {
fatal("unable to write {s} file: {t}", .{ Package.Manifest.basename, err });
};
return process.cleanExit(io);
}