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.

makeStep

Runs the "make" function of the single step s, updates its state, and then spawns newly-ready dependant steps in group. If s makes an RSS claim (i.e. s.max_rss != 0), the caller must have already subtracted this value from maker.available_rss. This function will release the RSS claim (i.e. add s.max_rss back into maker.available_rss) and queue any viable memory-blocked steps after "make" completes for s.

Maker.makeStep
fn makeStep(
    maker: *Maker,
    group: *Io.Group,
    step_index: Configuration.Step.Index,
    root_prog_node: std.Progress.Node,
) Io.Cancelable!void

File

lib/compiler/Maker.zig:2375

Code

fn makeStep(
    maker: *Maker,
    group: *Io.Group,
    step_index: Configuration.Step.Index,
    root_prog_node: std.Progress.Node,
) Io.Cancelable!void {
    const graph = maker.graph;
    const io = graph.io;
    const gpa = maker.gpa;
    const c = &maker.scanned_config.configuration;
    const conf_step = step_index.ptr(c);
    const step_name = conf_step.name.slice(c);
    const deps = conf_step.deps.slice(c);
    const make_step = maker.stepByIndex(step_index);

    {
        const step_prog_node = root_prog_node.start(step_name, 0);
        defer step_prog_node.end();

        if (maker.web_server) |ws| ws.updateStepStatus(step_index, .wip);

        const new_state: Step.State = for (deps) |dep_index| {
            const dep_make_step = maker.stepByIndex(dep_index);
            switch (@atomicLoad(Step.State, &dep_make_step.state, .monotonic)) {
                .precheck_unstarted => unreachable,
                .precheck_started => unreachable,
                .precheck_done => unreachable,

                .failure,
                .dependency_failure,
                .skipped_oom,
                => break .dependency_failure,

                .success, .skipped => {},
            }
        } else if (Step.make(step_index, maker, step_prog_node)) state: {
            break :state .success;
        } else |err| switch (err) {
            error.MakeFailed => .failure,
            error.MakeSkipped => .skipped,
            error.Canceled => |e| return e,
        };

        @atomicStore(Step.State, &make_step.state, new_state, .monotonic);

        switch (new_state) {
            .precheck_unstarted => unreachable,
            .precheck_started => unreachable,
            .precheck_done => unreachable,

            .failure,
            .dependency_failure,
            .skipped_oom,
            => {
                if (maker.web_server) |ws| ws.updateStepStatus(step_index, .failure);
                std.Progress.setStatus(.failure_working);
            },

            .success,
            .skipped,
            => {
                if (maker.web_server) |ws| ws.updateStepStatus(step_index, .success);
            },
        }
    }

    // No matter the result, we want to display error/warning messages.
    if (make_step.result_error_bundle.errorMessageCount() > 0 or
        make_step.result_error_msgs.items.len > 0 or
        make_step.result_stderr.len > 0)
    {
        const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
        defer io.unlockStderr();
        printErrorMessages(maker, step_index, .{}, stderr.terminal(), maker.error_style, maker.multiline_errors) catch |err| switch (err) {
            error.Canceled => |e| return e,
            error.WriteFailed => switch (stderr.file_writer.err.?) {
                error.Canceled => |e| return e,
                else => {},
            },
            else => {},
        };
    }

    const max_rss = conf_step.max_rss.toBytes();
    if (max_rss != 0) {
        var dispatch_set: std.ArrayList(Configuration.Step.Index) = .empty;
        defer dispatch_set.deinit(gpa);

        // Release our RSS claim and kick off some blocked steps if possible. We use `dispatch_set`
        // as a staging buffer to avoid recursing into `makeStep` while `maker.max_rss_mutex` is held.
        {
            try maker.max_rss_mutex.lock(io);
            defer maker.max_rss_mutex.unlock(io);
            maker.available_rss += max_rss;
            dispatch_set.ensureUnusedCapacity(gpa, maker.memory_blocked_steps.items.len) catch
                @panic("TODO eliminate memory allocation here");
            while (maker.memory_blocked_steps.getLast()) |candidate_index| {
                const candidate_max_rss = candidate_index.ptr(c).max_rss.toBytes();
                if (maker.available_rss < candidate_max_rss) break;
                assert(maker.memory_blocked_steps.pop() == candidate_index);
                dispatch_set.appendAssumeCapacity(candidate_index);
            }
        }
        for (dispatch_set.items) |candidate| {
            group.async(io, makeStep, .{ maker, group, candidate, root_prog_node });
        }
    }

    for (make_step.dependants.items) |dependant_index| {
        const dependant = maker.stepByIndex(dependant_index);
        // `.acq_rel` synchronizes with itself to ensure all dependencies' final states are visible when this hits 0.
        if (@atomicRmw(u32, &dependant.pending_deps, .Sub, 1, .acq_rel) == 1) {
            try stepReady(maker, group, dependant_index, root_prog_node);
        }
    }
}