Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

run

Fetch.run
pub fn run(f: *Fetch) RunError!void

File

lib/compiler/Maker/Fetch.zig:523

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);

    // Check the global zig package cache to see if the hash already exists. If
    // 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"),
            );
            // Packages fetched by URL may not use relative paths to escape outside the
            // 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;
                },
            }
        }

        // Check global cache before remote fetch.
        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;
    }

    // Fetch and unpack the remote into a temporary directory.
    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);
}