From 5ede6a167eef1835f6fb6c768a82d977269dafe5 Mon Sep 17 00:00:00 2001 From: Brandon Black Date: Fri, 29 May 2026 13:14:49 -0500 Subject: [PATCH] Build.Step.Options: addOptionPathUntracked This interface is like `addOptionPath`, but does not assume that the provided `LazyPath` represents a generated file, and does not set up any further dependencies on whatever the path represents. This supports use-cases like setting an `.install_prefix` -based default directory path option for the application to use as a fallback when the user does not provide an override. Example: options.addOptionPathUntracked( "DefaultConfigDir", b.graph.path( .install_prefix, b.pathJoin(&.{ "etc", "myapp" }), ), ); --- lib/compiler/Maker/Step/Options.zig | 16 ++++++++++++++-- lib/std/Build/Configuration.zig | 4 +++- lib/std/Build/Serialize.zig | 8 ++++++++ lib/std/Build/Step/Options.zig | 25 +++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/compiler/Maker/Step/Options.zig b/lib/compiler/Maker/Step/Options.zig index b9e676ef6542343afe8a018e3f3bdff240a18936..9639dffa5bc9b6976bd19bbafff09c803f2fadf0 100644 --- a/lib/compiler/Maker/Step/Options.zig +++ b/lib/compiler/Maker/Step/Options.zig @@ -27,8 +27,11 @@ pub fn make( const contents = conf_options.contents.slice(conf); // 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. + // generated zig file are observable by dependant steps. + // Pathnames added to Options.args are assumed to be generated files whose + // contents are also observable, while pathnames added to + // Options.args_untracked are treated as pure path string data with no + // tracking of the path's state or contents. step.clearWatchInputs(maker); @@ -48,6 +51,15 @@ pub fn make( }); } + for (conf_options.args_untracked.slice) |arg| { + const name = arg.name.slice(conf); + const lazy_path = arg.path.get(conf); + const arg_path = try maker.resolveLazyPath(arena, lazy_path, step_index); + try args_bytes.print(arena, "pub const {f}: []const u8 = \"{f}\";\n", .{ + std.zig.fmtId(name), arg_path.fmtEscapeString(), + }); + } + man.hash.addBytes(contents); man.hash.addBytes(args_bytes.items); diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index f32fb26ec5ba4a2b6f1cb4bf52638cbb55c034a7..9fb44c80d89336542fe91fdc7d58f9696a18b40f 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -1379,6 +1379,7 @@ pub const Step = extern struct { generated_file: GeneratedFileIndex, contents: Bytes, args: Storage.FlagLengthPrefixedList(.flags, .args, Arg), + args_untracked: Storage.FlagLengthPrefixedList(.flags, .args_untracked, Arg), pub const Arg = extern struct { name: String, @@ -1388,7 +1389,8 @@ pub const Step = extern struct { pub const Flags = packed struct(u32) { tag: Tag = .options, args: bool, - _: u26 = 0, + args_untracked: bool, + _: u25 = 0, }; }; diff --git a/lib/std/Build/Serialize.zig b/lib/std/Build/Serialize.zig index eafc8e0ab7063ac42f80183e125f1c1b6c11358a..b61cc49fd55885a4ca1d450eafcf0c3eae916be0 100644 --- a/lib/std/Build/Serialize.zig +++ b/lib/std/Build/Serialize.zig @@ -654,13 +654,21 @@ pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !voi .path = try s.addLazyPath(src.path), }; + const args_untracked = try arena.alloc(Configuration.Step.Options.Arg, so.args_untracked.items.len); + for (args_untracked, so.args_untracked.items) |*dest, src| dest.* = .{ + .name = src.name, + .path = try s.addLazyPath(src.path), + }; + break :e try wc.addExtraErased(Configuration.Step.Options, .{ .flags = .{ .args = so.args.items.len != 0, + .args_untracked = so.args_untracked.items.len != 0, }, .generated_file = so.generated_file, .contents = try wc.addBytes(so.contents.items), .args = .{ .slice = args }, + .args_untracked = .{ .slice = args_untracked }, }); }, })), diff --git a/lib/std/Build/Step/Options.zig b/lib/std/Build/Step/Options.zig index a377bcf70422f7ed9baaa0c39fb4b8f0057924f4..b616306dbe755cce5855295aead29935e4ff31cd 100644 --- a/lib/std/Build/Step/Options.zig +++ b/lib/std/Build/Step/Options.zig @@ -13,6 +13,7 @@ step: Step, generated_file: Configuration.GeneratedFileIndex, contents: std.ArrayList(u8) = .empty, args: std.ArrayList(Arg) = .empty, +args_untracked: std.ArrayList(Arg) = .empty, encountered_types: std.StringHashMapUnmanaged(void), pub const base_tag: Step.Tag = .options; @@ -421,6 +422,11 @@ fn printStructValue( } /// The added option has type `[]const u8` and value of the provided path. +/// This interface assumes the provided path is a generated file, and causes a +/// transitive dependency through Options on the file's contents. +/// +/// See also: +/// * `addOptionPathUntracked` pub fn addOptionPath(options: *Options, name: []const u8, path: LazyPath) void { const graph = options.step.owner.graph; const arena = graph.arena; @@ -433,6 +439,25 @@ pub fn addOptionPath(options: *Options, name: []const u8, path: LazyPath) void { path.addStepDependencies(&options.step); } +/// The added option has type `[]const u8` and value of the provided path. +/// This interface treats the resolved path string as pure data, meaning it +/// does not track anything about the meaning or contents of this path, and +/// therefore also does not create any related dependencies. +/// +/// See also: +/// * `addOptionPath` +pub fn addOptionPathUntracked(options: *Options, name: []const u8, path: LazyPath) void { + const graph = options.step.owner.graph; + const arena = graph.arena; + const wc = &graph.wip_configuration; + + options.args_untracked.append(arena, .{ + .name = wc.addString(name) catch @panic("OOM"), + .path = path.dupe(options.step.owner.graph), + }) catch @panic("OOM"); + path.addStepDependencies(&options.step); +} + pub fn createModule(options: *Options) *std.Build.Module { return options.step.owner.createModule(.{ .root_source_file = options.getOutput(), -- 2.54.0