zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 43209551b73b670cbb1505fd5fc45980d763aa9c (tree)
parent bd4c1e34d28bb7ab88ada31bb0fa01fda6e4b201
Author: Andrew Kelley <andrew@ziglang.org>
Date:   Sun, 17 May 2026 14:49:43 -0700

maker: implement Step.Options

also revert #35224

Diffstat:
MBRANCH_TODO | 3+++
Mlib/compiler/Maker/Step.zig | 6++++--
Mlib/compiler/Maker/Step/Options.zig | 122+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
3 files changed, 79 insertions(+), 52 deletions(-)

diff --git a/BRANCH_TODO b/BRANCH_TODO @@ -20,6 +20,8 @@ * make the generated dependencies.zig be dependencies.zon and don't put absolute paths in there - and adjust dependencyInner to not openDir() +* re-evaluate https://codeberg.org/ziglang/zig/pulls/35224 + ## Followup Issues * stop leaking into global process arena * reduce the size of Maker.Step.Extended (make Run smaller) probably by using an arena per make @@ -32,6 +34,7 @@ * fmt step: import zig fmt code directly rather than child proc * UpdateSourceFiles: introduce Group * WriteFiles: introduce Group +* re-examine the use case of adding file paths to Options steps ## Already Filed Followup Issues * build system fmt step with check=false does not acquire a write lock on source files #35204 diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig @@ -23,6 +23,7 @@ pub const Fmt = @import("Step/Fmt.zig"); pub const InstallArtifact = @import("Step/InstallArtifact.zig"); pub const InstallFile = @import("Step/InstallFile.zig"); pub const ObjCopy = @import("Step/ObjCopy.zig"); +pub const Options = @import("Step/Options.zig"); pub const Run = @import("Step/Run.zig"); pub const UpdateSourceFiles = @import("Step/UpdateSourceFiles.zig"); @@ -79,7 +80,7 @@ pub const Extended = union(enum) { install_dir: Todo, install_file: InstallFile, obj_copy: ObjCopy, - options: Todo, + options: Options, remove_dir: Todo, run: Run, top_level: TopLevel, @@ -321,7 +322,8 @@ pub fn reset(step: *Step, maker: *Maker) void { step.result_peak_rss = 0; step.result_failed_command = null; step.test_results = .{}; - step.clearWatchInputs(maker); + // We do not clearWatchInputs here because each step manages that choice + // independently. step.result_error_bundle.deinit(gpa); step.result_error_bundle = std.zig.ErrorBundle.empty; diff --git a/lib/compiler/Maker/Step/Options.zig b/lib/compiler/Maker/Step/Options.zig @@ -1,7 +1,9 @@ const Options = @This(); const std = @import("std"); +const Io = std.Io; const Configuration = std.Build.Configuration; +const Cache = std.Build.Cache; const Step = @import("../Step.zig"); const Maker = @import("../../Maker.zig"); @@ -12,6 +14,8 @@ pub fn make( maker: *Maker, progress_node: std.Progress.Node, ) Step.ExtendedMakeError!void { + _ = options; + // This step completes so quickly that no progress reporting is necessary. _ = progress_node; @@ -19,64 +23,82 @@ pub fn make( const step = maker.stepByIndex(step_index); const io = graph.io; const cache_root = graph.local_cache_root; + const arena = graph.arena; // TODO don't leak into the process arena + const conf = &maker.scanned_config.configuration; + const conf_step = step_index.ptr(conf); + const conf_options = conf_step.extended.get(conf.extra).options; + const contents = conf_options.contents.slice(conf); - for (options.args.items) |arg| { - options.addOption( - []const u8, - arg.name, - arg.path.getPath2(b, step), - ); - } - if (!step.inputs.populated()) for (options.args.items) |arg| { - try step.addWatchInput(arg.path); - }; + // This step operates under the assumption that all contents of the + // generated zig file are observable by dependant steps, as well as the + // contents of files added via Options.Arg. - const basename = "options.zig"; + step.clearWatchInputs(maker); + + var man = graph.cache.obtain(); + defer man.deinit(); - // Hash contents to file name. - var hash = graph.cache.hash; - // Random bytes to make unique. Refresh this with new random bytes when - // implementation is modified in a non-backwards-compatible way. - hash.add(@as(u32, 0xad95e922)); - hash.addBytes(options.contents.items); - const sub_path = "c" ++ fs.path.sep_str ++ hash.final() ++ fs.path.sep_str ++ basename; + var args_bytes: std.ArrayList(u8) = .empty; - options.generated_file.path = try cache_root.join(arena, &.{sub_path}); + for (conf_options.args.slice) |arg| { + const name = arg.name.slice(conf); + const lazy_path = arg.path.get(conf); + try step.addWatchInput(maker, arena, lazy_path); + const arg_path = try maker.resolveLazyPath(arena, lazy_path, step_index); + _ = try man.addFilePath(arg_path, null); + try args_bytes.print(arena, "pub const {f}: []const u8 = \"{f}\";\n", .{ + std.zig.fmtId(name), arg_path.fmtEscapeString(), + }); + } - // Optimize for the hot path. Stat the file, and if it already exists, - // cache hit. - if (cache_root.handle.access(io, sub_path, .{})) |_| { - // This is the hot path, success. + man.hash.addBytes(contents); + man.hash.addBytes(args_bytes.items); + + const basename = "options.zig"; + + if (try step.cacheHitAndWatch(maker, &man)) { + const digest = man.final(); + maker.generatedPath(conf_options.generated_file).* = .{ + .root_dir = cache_root, + .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, basename }), + }; step.result_cached = true; return; - } else |outer_err| switch (outer_err) { - error.FileNotFound => { - var atomic_file = cache_root.handle.createFileAtomic(io, sub_path, .{ - .replace = false, - .make_path = true, - }) catch |err| return step.fail("failed to create temporary path for '{f}{s}': {t}", .{ - cache_root, sub_path, err, - }); - defer atomic_file.deinit(io); - - atomic_file.file.writeStreamingAll(io, options.contents.items) catch |err| { - return step.fail("failed to write options to temporary path for '{f}{s}': {t}", .{ - cache_root, sub_path, err, - }); - }; + } - atomic_file.link(io) catch |err| switch (err) { - error.PathAlreadyExists => { - step.result_cached = true; - return; - }, - else => return step.fail("failed to link temporary file into '{f}{s}': {t}", .{ - cache_root, sub_path, err, - }), + const digest = man.final(); + const out_path: Cache.Path = .{ + .root_dir = cache_root, + .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, basename }), + }; + + var file: Io.File = out_path.root_dir.handle.createFile(io, out_path.sub_path, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + error.FileNotFound => f: { + out_path.root_dir.handle.createDirPath(io, Io.Dir.path.dirname(out_path.sub_path).?) catch |inner| switch (inner) { + error.Canceled => |e| return e, + else => |e| return step.fail(maker, "failed to create {f}: {t}", .{ out_path, e }), + }; + break :f out_path.root_dir.handle.createFile(io, out_path.sub_path, .{}) catch |inner| switch (inner) { + error.Canceled => |e| return e, + else => |e| return step.fail(maker, "failed to create {f}: {t}", .{ out_path, e }), }; }, - else => |e| return step.fail("unable to access options file '{f}{s}': {t}", .{ - cache_root, sub_path, e, - }), - } + else => |e| return step.fail(maker, "failed to create {f}: {t}", .{ out_path, e }), + }; + defer file.close(io); + + // No buffer because we already have all contents buffered. + var file_writer = file.writer(io, &.{}); + var data: [2][]const u8 = .{ contents, args_bytes.items }; + file_writer.interface.writeVecAll(&data) catch |write_err| switch (write_err) { + error.WriteFailed => switch (file_writer.err.?) { + error.Canceled => |e| return e, + else => |e| return step.fail(maker, "failed to write to {f}: {t}", .{ out_path, e }), + }, + }; + + try step.writeManifestAndWatch(maker, &man); + + maker.generatedPath(conf_options.generated_file).* = out_path; }