feature. See also
. The project being documented here (as the example) is the Zig library itself.
Fetch.run
pub fn run(f: *Fetch) RunError!void
File
Code
pub fn run(f: *Fetch) RunError!void {
const job_queue = f.job_queue;
const io = job_queue.io;
const eb = &f.error_bundle;
const arena = f.arena.allocator();
const gpa = f.arena.child_allocator;
try eb.init(gpa);
// so, load, parse, and validate the build.zig.zon file therein, and skip
// ahead to queuing up jobs for dependencies. Likewise if the location is a
// relative path, treat this the same as a cache hit. Otherwise, proceed.
const remote = switch (f.location) {
.relative_path => |pkg_root| {
if (fs.path.isAbsolute(pkg_root.sub_path)) return f.fail(
f.location_tok,
try eb.addString("expected path relative to build root; found absolute path"),
);
if (f.hash_tok.unwrap()) |hash_tok| return f.fail(
hash_tok,
try eb.addString("path-based dependencies are not hashed"),
);
// fetched package directory from within the package cache.
// This code path is only reachable recursively and the sub_path
// will already have been resolved to no longer have extra ".." or
// "." components.
assert(job_queue.local_storage != null);
assert(pkg_root.root_dir.eql(f.remote_package_root.root_dir));
if (!std.mem.startsWith(u8, pkg_root.sub_path, f.remote_package_root.sub_path)) return f.fail(
f.location_tok,
try eb.printString("dependency path outside project: '{f}'", .{pkg_root}),
);
f.package_root = pkg_root;
try loadManifest(f, pkg_root);
if (!f.has_build_zig) try checkBuildFileExistence(f);
if (!job_queue.recursive) return;
return queueJobsForDeps(f);
},
.remote => |remote| remote,
.path_or_url => |path_or_url| {
if (Io.Dir.cwd().openDir(io, path_or_url, .{ .iterate = true })) |dir| {
var resource: Resource = .{ .dir = dir };
return f.runResource(path_or_url, &resource, null, false);
} else |dir_err| {
var server_header_buffer: [init_resource_buffer_size]u8 = undefined;
const file_err = if (dir_err == error.NotDir) e: {
if (Io.Dir.cwd().openFile(io, path_or_url, .{})) |file| {
var resource: Resource = .{ .file = file.reader(io, &server_header_buffer) };
return f.runResource(path_or_url, &resource, null, false);
} else |err| break :e err;
} else dir_err;
const uri = std.Uri.parse(path_or_url) catch |uri_err| {
return f.fail(0, try eb.printString(
"'{s}' could not be recognized as a file path ({t}) or an URL ({t})",
.{ path_or_url, file_err, uri_err },
));
};
var resource: Resource = undefined;
try f.initResource(uri, &resource, &server_header_buffer);
return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, null, false);
}
},
};
var resource_buffer: [init_resource_buffer_size]u8 = undefined;
if (remote.hash) |expected_hash| {
const expected_project_id: Package.ProjectId = expected_hash.projectId();
if (job_queue.fork_set.getKeyPtrAdapted(expected_project_id, @as(JobQueue.Fork.Adapter, .{}))) |fork| {
log.debug("using fork {f} for {s}", .{ fork.path, fork.manifest.name });
fork.uses += 1;
f.package_root = fork.path;
f.remote_package_root = f.package_root;
f.manifest_ast = fork.manifest_ast;
f.manifest = fork.manifest;
f.have_manifest = true;
try checkBuildFileExistence(f);
if (!job_queue.recursive) return;
return queueJobsForDeps(f);
}
if (job_queue.local_storage) |ls| {
const package_root = try ls.pkg_root.join(arena, expected_hash.toSlice());
if (package_root.root_dir.handle.access(io, package_root.sub_path, .{})) |_| {
assert(f.lazy_status != .unavailable);
f.package_root = package_root;
f.remote_package_root = f.package_root;
try loadManifest(f, f.package_root);
try checkBuildFileExistence(f);
if (!job_queue.recursive) return;
return queueJobsForDeps(f);
} else |err| switch (err) {
error.FileNotFound => {
log.debug("FileNotFound: {f}", .{package_root});
if (job_queue.read_only and f.lazy_status == .eager) return f.fail(
f.name_tok,
try eb.printString("package not found at '{f}'", .{package_root}),
);
},
error.Canceled => |e| return e,
else => |e| {
try eb.addRootErrorMessage(.{
.msg = try eb.printString("unable to open package cache directory {f}: {t}", .{
package_root, e,
}),
});
return error.FetchFailed;
},
}
}
const cached_tarball_sub_path = try std.fmt.allocPrint(arena, "p/{s}.tar.gz", .{expected_hash.toSlice()});
const cached_tarball_path: Path = .{
.root_dir = job_queue.global_cache,
.sub_path = cached_tarball_sub_path,
};
if (cached_tarball_path.root_dir.handle.openFile(io, cached_tarball_path.sub_path, .{})) |file| {
log.debug("found global cached tarball {f}", .{cached_tarball_path});
var resource: Resource = .{ .file = file.reader(io, &resource_buffer) };
return f.runResource(cached_tarball_sub_path, &resource, remote.hash, true);
} else |err| switch (err) {
error.FileNotFound => log.debug("FileNotFound: {f}", .{cached_tarball_path}),
error.Canceled => |e| return e,
else => |e| {
try eb.addRootErrorMessage(.{
.msg = try eb.printString("unable to open globally cached package {f}: {t}", .{
cached_tarball_path, e,
}),
});
return error.FetchFailed;
},
}
switch (f.lazy_status) {
.eager => {},
.available => if (!job_queue.unlazy_set.contains(expected_hash)) {
f.lazy_status = .unavailable;
return;
},
.unavailable => unreachable,
}
} else if (job_queue.read_only) {
try eb.addRootErrorMessage(.{
.msg = try eb.addString("dependency is missing hash field"),
.src_loc = try f.srcLoc(f.location_tok),
});
return error.FetchFailed;
}
const uri = std.Uri.parse(remote.url) catch |err| return f.fail(
f.location_tok,
try eb.printString("invalid URI: {t}", .{err}),
);
var resource: Resource = undefined;
try f.initResource(uri, &resource, &resource_buffer);
return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, remote.hash, false);
}