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.

buildExeSubprocess

Assumes argv has --listen=- in it and the child process is zig build-exe.

Result path is allocated via gpa.

zig.buildExeSubprocess
pub fn buildExeSubprocess(
    gpa: Allocator,
    io: Io,
    options: BuildExeSubprocessOptions,
) BuildExeSubprocessError!BuildExeSubprocessResult

File

lib/std/zig.zig:1641

Code

pub fn buildExeSubprocess(
    gpa: Allocator,
    io: Io,
    options: BuildExeSubprocessOptions,
) BuildExeSubprocessError!BuildExeSubprocessResult {
    const cmd: SubprocessCommand = .{ .argv = options.argv };

    var child = std.process.spawn(io, .{
        .argv = options.argv,
        .environ_map = options.environ_map,
        .stdin = .pipe,
        .stdout = .pipe,
        .stderr = .pipe,
        .progress_node = options.progress_node,
    }) catch |err| {
        log.err("spawning command {t}: {f}", .{ err, cmd });
        return error.AlreadyReported;
    };
    defer child.kill(io);

    var stderr_task = io.concurrent(readStreamAlloc, .{ gpa, io, child.stderr.?, .unlimited }) catch
        @panic("TODO use multireader instead");
    defer if (stderr_task.cancel(io)) |slice| gpa.free(slice) else |_| {};

    var stdout_buffer: [512]u8 = undefined;
    var stdout_reader: Io.File.Reader = .initStreaming(child.stdout.?, io, &stdout_buffer);
    const stdout = &stdout_reader.interface;

    {
        var w = child.stdin.?.writer(io, &.{});
        w.interface.writeStruct(Client.Message.Header{ .tag = .update, .bytes_len = 0 }, .little) catch |err| switch (err) {
            error.WriteFailed => {
                log.err("{t} writing to command: {f}", .{ w.err.?, cmd });
                return error.AlreadyReported;
            },
        };
        w.interface.writeStruct(Client.Message.Header{ .tag = .exit, .bytes_len = 0 }, .little) catch |err| switch (err) {
            error.WriteFailed => {
                log.err("{t} writing to command: {f}", .{ w.err.?, cmd });
                return error.AlreadyReported;
            },
        };
    }

    const Header = Server.Message.Header;

    var result: ?Cache.Path = null;
    defer if (result) |r| gpa.free(r.sub_path);

    var result_error_bundle: ErrorBundle = .empty;
    defer result_error_bundle.deinit(gpa);

    var body_buffer: std.ArrayList(u8) = .empty;
    defer body_buffer.deinit(gpa);

    var received_fs_inputs = false;
    var cache_hit = false;

    while (true) {
        const header = stdout.takeStruct(Header, .little) catch |err| switch (err) {
            error.ReadFailed => {
                log.err("{t} reading from command: {f}", .{ stdout_reader.err.?, cmd });
                return error.AlreadyReported;
            },
            error.EndOfStream => break,
        };
        body_buffer.clearRetainingCapacity();
        stdout.appendExact(gpa, &body_buffer, header.bytes_len) catch |err| switch (err) {
            error.ReadFailed => {
                log.err("{t} reading from command: {f}", .{ stdout_reader.err.?, cmd });
                return error.AlreadyReported;
            },
            error.OutOfMemory => |e| return e,
            error.EndOfStream => {
                log.err("unexpected end of stream from command: {f}", .{cmd});
                return error.AlreadyReported;
            },
        };
        const body = body_buffer.items;

        switch (header.tag) {
            .zig_version => {
                if (!mem.eql(u8, builtin.zig_version_string, body)) {
                    log.err("zig protocol version mismatch from command: {f}", .{cmd});
                    return error.AlreadyReported;
                }
            },
            .error_bundle => {
                result_error_bundle.deinit(gpa);
                result_error_bundle = Server.allocErrorBundle(gpa, body) catch |err| switch (err) {
                    error.EndOfStream => break,
                    else => |e| return e,
                };
            },
            .emit_digest => {
                const EmitDigest = Server.Message.EmitDigest;
                const ebp_hdr: *align(1) const EmitDigest = @ptrCast(body);
                cache_hit = ebp_hdr.flags.cache_hit;
                const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len];
                if (result) |r| gpa.free(r.sub_path);
                result = .{
                    .root_dir = options.cache_root,
                    .sub_path = try Dir.path.join(gpa, &.{ "o", &Cache.binToHex(digest.*) }),
                };
            },
            .file_system_inputs => if (options.cache_manifest) |man| {
                received_fs_inputs = true;
                var it = mem.splitScalar(u8, body, 0);
                while (it.next()) |prefixed_path| {
                    const prefix: Server.Message.PathPrefix = @fromBackingInt(@intCast(prefixed_path[0] - 1));
                    const sub_path = try gpa.dupe(u8, prefixed_path[1..]);
                    var keep = false;
                    defer if (!keep) gpa.free(sub_path);
                    keep = man.addPrefixedPathPost(.{
                        .prefix = @backingInt(prefix),
                        .sub_path = sub_path,
                    }) catch |err| switch (err) {
                        error.Canceled, error.OutOfMemory => |e| return e,
                        else => |e| {
                            log.err("adding {t} {s} to cache failed: {t}", .{ prefix, sub_path, e });
                            return error.AlreadyReported;
                        },
                    };
                }
            },
            else => {}, // ignore other messages
        }
    }

    const stderr_contents = stderr_task.await(io) catch |err| switch (err) {
        error.Canceled, error.OutOfMemory => |e| return e,
        else => |e| c: {
            log.warn("{t} reading stderr from command: {f}", .{ e, cmd });
            break :c "";
        },
    };
    if (stderr_contents.len > 0)
        log.warn("unexpected stderr from {s} command:\n{s}", .{ options.argv[0], stderr_contents });

    // Send EOF to stdin.
    child.stdin.?.close(io);
    child.stdin = null;

    const term = child.wait(io) catch |err| switch (err) {
        error.Canceled => |e| return e,
        else => |e| {
            log.err("{t} waiting for command: {f}", .{ e, cmd });
            return error.AlreadyReported;
        },
    };

    if (!term.success()) {
        log.err("command {f}: {f}", .{ term, cmd });
        if (received_fs_inputs) return error.FailedButCacheIntact;
        return error.AlreadyReported;
    }

    if (result_error_bundle.errorMessageCount() > 0) {
        result_error_bundle.renderToStderr(io, .{}, .auto) catch |err| switch (err) {
            error.Canceled => |e| return e,
            else => |e| {
                log.err("failed rendering error bundle: {t}", .{e});
                return error.AlreadyReported;
            },
        };
        if (!options.skip_log_cmdline_on_compile_errors) log.err("command reported {d} compilation errors: {f}", .{
            result_error_bundle.errorMessageCount(), cmd,
        });
        if (received_fs_inputs) return error.FailedButCacheIntact;
        return error.AlreadyReported;
    }

    const base_path = result orelse {
        log.err("command failed to report result: {f}", .{cmd});
        return error.AlreadyReported;
    };
    const parsed_target = system.resolveTargetQuery(io, std.Build.parseTargetQuery(.{
        .arch_os_abi = options.arch_os_abi orelse "native",
        .cpu_features = options.cpu_features,
    }) catch unreachable) catch unreachable;
    const bin_name = try binNameAlloc(gpa, .{
        .root_name = options.root_name,
        .cpu_arch = parsed_target.cpu.arch,
        .os_tag = parsed_target.os.tag,
        .ofmt = parsed_target.ofmt,
        .abi = parsed_target.abi,
        .output_mode = .Exe,
    });
    defer gpa.free(bin_name);
    return .{
        .received_fs_inputs = received_fs_inputs,
        .cache_hit = cache_hit,
        .path = try base_path.join(gpa, bin_name),
    };
}