authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-18 22:02:45+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-18 22:02:45+02:00
log3f9fe9e28f83119f8ecf2fdafcd12967417634e9
tree0027863d7bf1aa63d959eb39082e74a1adaf68b4
parent80fe9b2b7fc981166cbfe98165e17dad6107cb34
parentc106e2759578e787e5be235c8f6b227623ff2c3b

Merge pull request 'std.Build.Step.Options: add addOptionPathDirectory' (#36876) from std.Build.Step.Options into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36876

5 files changed, 78 insertions(+), 32 deletions(-)

lib/compiler/Maker/Step/Options.zig+16-3
...@@ -30,7 +30,7 @@ pub fn make(...@@ -30,7 +30,7 @@ pub fn make(
30 // generated zig file are observable by dependant steps.30 // generated zig file are observable by dependant steps.
31 // Pathnames added to Options.args are assumed to be generated files whose31 // Pathnames added to Options.args are assumed to be generated files whose
32 // contents are also observable, while pathnames added to32 // contents are also observable, while pathnames added to
33 // Options.args_untracked are treated as pure path string data with no33 // Options.untracked_paths are treated as pure path string data with no
34 // tracking of the path's state or contents.34 // tracking of the path's state or contents.
3535
36 step.clearWatchInputs(maker);36 step.clearWatchInputs(maker);
...@@ -40,7 +40,7 @@ pub fn make(...@@ -40,7 +40,7 @@ pub fn make(
4040
41 var args_bytes: std.ArrayList(u8) = .empty;41 var args_bytes: std.ArrayList(u8) = .empty;
4242
43 for (conf_options.args.slice) |arg| {43 for (conf_options.files.slice) |arg| {
44 const name = arg.name.slice(conf);44 const name = arg.name.slice(conf);
45 const lazy_path = arg.path.get(conf);45 const lazy_path = arg.path.get(conf);
46 try step.addWatchInput(maker, arena, lazy_path);46 try step.addWatchInput(maker, arena, lazy_path);
...@@ -51,7 +51,20 @@ pub fn make(...@@ -51,7 +51,20 @@ pub fn make(
51 });51 });
52 }52 }
5353
54 for (conf_options.args_untracked.slice) |arg| {54 for (conf_options.directories.slice) |arg| {
55 const name = arg.name.slice(conf);
56 const lazy_path = arg.path.get(conf);
57 _ = try step.addDirectoryWatchInput(maker, lazy_path);
58 const arg_path = try maker.resolveLazyPath(arena, lazy_path, step_index);
59 _ = try man.addInputPath(arg_path, .{
60 .handle = .{ .dir = null },
61 });
62 try args_bytes.print(arena, "pub const {f}: []const u8 = \"{f}\";\n", .{
63 std.zig.fmtId(name), arg_path.fmtEscapeString(),
64 });
65 }
66
67 for (conf_options.untracked_paths.slice) |arg| {
55 const name = arg.name.slice(conf);68 const name = arg.name.slice(conf);
56 const lazy_path = arg.path.get(conf);69 const lazy_path = arg.path.get(conf);
57 const arg_path = try maker.resolveLazyPath(arena, lazy_path, step_index);70 const arg_path = try maker.resolveLazyPath(arena, lazy_path, step_index);
lib/std/Build/Cache.zig+8-3
...@@ -514,6 +514,11 @@ pub const Manifest = struct {...@@ -514,6 +514,11 @@ pub const Manifest = struct {
514 pub fn path(off: File.Offset, contents: []const u8) [:0]const u8 {514 pub fn path(off: File.Offset, contents: []const u8) [:0]const u8 {
515 return pathFallible(off, contents) catch unreachable;515 return pathFallible(off, contents) catch unreachable;
516 }516 }
517
518 pub fn pathOrDot(off: File.Offset, contents: []const u8) [:0]const u8 {
519 const p = pathFallible(off, contents) catch unreachable;
520 return if (p.len == 0) "." else p;
521 }
517 };522 };
518523
519 /// Intentionally matches if the files are different only by flags other than prefix.524 /// Intentionally matches if the files are different only by flags other than prefix.
...@@ -1176,7 +1181,7 @@ pub const Manifest = struct {...@@ -1176,7 +1181,7 @@ pub const Manifest = struct {
1176 const io = cache.io;1181 const io = cache.io;
1177 const input_file = file_off.get(contents);1182 const input_file = file_off.get(contents);
1178 const parent_dir = cache.prefixes()[input_file.flags.prefix].handle;1183 const parent_dir = cache.prefixes()[input_file.flags.prefix].handle;
1179 const file_path = file_off.path(contents);1184 const file_path = file_off.pathOrDot(contents);
1180 const gpa = cache.gpa;1185 const gpa = cache.gpa;
11811186
1182 if (input_path.have_digest) return;1187 if (input_path.have_digest) return;
...@@ -1352,7 +1357,7 @@ pub const Manifest = struct {...@@ -1352,7 +1357,7 @@ pub const Manifest = struct {
1352 const disk_file = file_off.get(disk_contents);1357 const disk_file = file_off.get(disk_contents);
1353 const input_file = file_off.get(input_contents);1358 const input_file = file_off.get(input_contents);
1354 const parent_dir = cache.prefixes()[disk_file.flags.prefix].handle;1359 const parent_dir = cache.prefixes()[disk_file.flags.prefix].handle;
1355 const file_path = file_off.path(disk_contents);1360 const file_path = file_off.pathOrDot(disk_contents);
13561361
1357 assert(disk_file.flags == input_file.flags);1362 assert(disk_file.flags == input_file.flags);
13581363
...@@ -1536,7 +1541,7 @@ pub const Manifest = struct {...@@ -1536,7 +1541,7 @@ pub const Manifest = struct {
1536 const gpa = cache.gpa;1541 const gpa = cache.gpa;
1537 const io = cache.io;1542 const io = cache.io;
1538 const parent_dir = cache.prefixes()[file.flags.prefix].handle;1543 const parent_dir = cache.prefixes()[file.flags.prefix].handle;
1539 const file_path = file_off.path(contents);1544 const file_path = file_off.pathOrDot(contents);
15401545
1541 if (file.flags.metadata_only) {1546 if (file.flags.metadata_only) {
1542 const actual_stat = parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) {1547 const actual_stat = parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) {
lib/std/Build/Configuration.zig+8-6
...@@ -1378,19 +1378,21 @@ pub const Step = extern struct {...@@ -1378,19 +1378,21 @@ pub const Step = extern struct {
1378 flags: @This().Flags,1378 flags: @This().Flags,
1379 generated_file: GeneratedFileIndex,1379 generated_file: GeneratedFileIndex,
1380 contents: Bytes,1380 contents: Bytes,
1381 args: Storage.FlagLengthPrefixedList(.flags, .args, Arg),1381 files: Storage.FlagLengthPrefixedList(.flags, .files, NamedPath),
1382 args_untracked: Storage.FlagLengthPrefixedList(.flags, .args_untracked, Arg),1382 directories: Storage.FlagLengthPrefixedList(.flags, .directories, NamedPath),
1383 untracked_paths: Storage.FlagLengthPrefixedList(.flags, .untracked_paths, NamedPath),
13831384
1384 pub const Arg = extern struct {1385 pub const NamedPath = extern struct {
1385 name: String,1386 name: String,
1386 path: LazyPath.Index,1387 path: LazyPath.Index,
1387 };1388 };
13881389
1389 pub const Flags = packed struct(u32) {1390 pub const Flags = packed struct(u32) {
1390 tag: Tag = .options,1391 tag: Tag = .options,
1391 args: bool,1392 files: bool,
1392 args_untracked: bool,1393 directories: bool,
1393 _: u25 = 0,1394 untracked_paths: bool,
1395 _: u24 = 0,
1394 };1396 };
1395 };1397 };
13961398
lib/std/Build/Serialize.zig+16-8
...@@ -648,27 +648,35 @@ pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !voi...@@ -648,27 +648,35 @@ pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !voi
648 .options => e: {648 .options => e: {
649 const so: *Step.Options = @fieldParentPtr("step", step);649 const so: *Step.Options = @fieldParentPtr("step", step);
650650
651 const args = try arena.alloc(Configuration.Step.Options.Arg, so.args.items.len);651 const files = try arena.alloc(Configuration.Step.Options.NamedPath, so.files.items.len);
652 for (args, so.args.items) |*dest, src| dest.* = .{652 for (files, so.files.items) |*dest, src| dest.* = .{
653 .name = src.name,653 .name = src.name,
654 .path = try s.addLazyPath(src.path),654 .path = try s.addLazyPath(src.path),
655 };655 };
656656
657 const args_untracked = try arena.alloc(Configuration.Step.Options.Arg, so.args_untracked.items.len);657 const directories = try arena.alloc(Configuration.Step.Options.NamedPath, so.directories.items.len);
658 for (args_untracked, so.args_untracked.items) |*dest, src| dest.* = .{658 for (directories, so.directories.items) |*dest, src| dest.* = .{
659 .name = src.name,
660 .path = try s.addLazyPath(src.path),
661 };
662
663 const untracked_paths = try arena.alloc(Configuration.Step.Options.NamedPath, so.untracked_paths.items.len);
664 for (untracked_paths, so.untracked_paths.items) |*dest, src| dest.* = .{
659 .name = src.name,665 .name = src.name,
660 .path = try s.addLazyPath(src.path),666 .path = try s.addLazyPath(src.path),
661 };667 };
662668
663 break :e try wc.addExtraErased(Configuration.Step.Options, .{669 break :e try wc.addExtraErased(Configuration.Step.Options, .{
664 .flags = .{670 .flags = .{
665 .args = so.args.items.len != 0,671 .files = so.files.items.len != 0,
666 .args_untracked = so.args_untracked.items.len != 0,672 .directories = so.directories.items.len != 0,
673 .untracked_paths = so.untracked_paths.items.len != 0,
667 },674 },
668 .generated_file = so.generated_file,675 .generated_file = so.generated_file,
669 .contents = try wc.addBytes(so.contents.items),676 .contents = try wc.addBytes(so.contents.items),
670 .args = .{ .slice = args },677 .files = .{ .slice = files },
671 .args_untracked = .{ .slice = args_untracked },678 .directories = .{ .slice = directories },
679 .untracked_paths = .{ .slice = untracked_paths },
672 });680 });
673 },681 },
674 })),682 })),
lib/std/Build/Step/Options.zig+30-12
...@@ -12,13 +12,14 @@ const Configuration = std.Build.Configuration;...@@ -12,13 +12,14 @@ const Configuration = std.Build.Configuration;
12step: Step,12step: Step,
13generated_file: Configuration.GeneratedFileIndex,13generated_file: Configuration.GeneratedFileIndex,
14contents: std.ArrayList(u8) = .empty,14contents: std.ArrayList(u8) = .empty,
15args: std.ArrayList(Arg) = .empty,15files: std.ArrayList(NamedPath) = .empty,
16args_untracked: std.ArrayList(Arg) = .empty,16directories: std.ArrayList(NamedPath) = .empty,
17untracked_paths: std.ArrayList(NamedPath) = .empty,
17encountered_types: std.StringHashMapUnmanaged(void),18encountered_types: std.StringHashMapUnmanaged(void),
1819
19pub const base_tag: Step.Tag = .options;20pub const base_tag: Step.Tag = .options;
2021
21pub const Arg = struct {22pub const NamedPath = struct {
22 name: Configuration.String,23 name: Configuration.String,
23 path: LazyPath,24 path: LazyPath,
24};25};
...@@ -421,28 +422,45 @@ fn printStructValue(...@@ -421,28 +422,45 @@ fn printStructValue(
421 }422 }
422}423}
423424
424/// The added option has type `[]const u8` and value of the provided path.425/// The added option has type `[]const u8` and value of the provided file path. If the provided path is a
425/// This interface assumes the provided path is a generated file, and causes a426/// generated file, causes a transitive dependency on the file's contents.
426/// transitive dependency through Options on the file's contents.
427///427///
428/// See also:428/// See also:
429/// * `addOptionPathDirectory`
429/// * `addOptionPathUntracked`430/// * `addOptionPathUntracked`
430pub fn addOptionPath(options: *Options, name: []const u8, path: LazyPath) void {431pub fn addOptionPath(options: *Options, name: []const u8, path: LazyPath) void {
431 const graph = options.step.owner.graph;432 const graph = options.step.owner.graph;
432 const arena = graph.arena;433 const arena = graph.arena;
433 const wc = &graph.wip_configuration;434 const wc = &graph.wip_configuration;
434435
435 options.args.append(arena, .{436 options.files.append(arena, .{
436 .name = wc.addString(name) catch @panic("OOM"),437 .name = wc.addString(name) catch @panic("OOM"),
437 .path = path.dupe(options.step.owner.graph),438 .path = path.dupe(options.step.owner.graph),
438 }) catch @panic("OOM");439 }) catch @panic("OOM");
439 path.addStepDependencies(&options.step);440 path.addStepDependencies(&options.step);
440}441}
441442
442/// The added option has type `[]const u8` and value of the provided path.443/// The added option has type `[]const u8` and value of the provided directory path. Causes a transitive
443/// This interface treats the resolved path string as pure data, meaning it444/// dependency through Options on the directory's contents.
444/// does not track anything about the meaning or contents of this path, and445///
445/// therefore also does not create any related dependencies.446/// See also:
447/// * `addOptionPath`
448/// * `addOptionPathUntracked`
449pub fn addOptionPathDirectory(options: *Options, name: []const u8, path: LazyPath) void {
450 const graph = options.step.owner.graph;
451 const arena = graph.arena;
452 const wc = &graph.wip_configuration;
453
454 options.directories.append(arena, .{
455 .name = wc.addString(name) catch @panic("OOM"),
456 .path = path.dupe(options.step.owner.graph),
457 }) catch @panic("OOM");
458 path.addStepDependencies(&options.step);
459}
460
461/// The added option has type `[]const u8` and value of the provided path. The resolved path is interpreted as
462/// a string without tracking anything about the meaning or contents, and therefore may be a file, directory,
463/// not exist, or even be an invalid path, and likewise does not create any file system or `Step` dependencies.
446///464///
447/// See also:465/// See also:
448/// * `addOptionPath`466/// * `addOptionPath`
...@@ -451,7 +469,7 @@ pub fn addOptionPathUntracked(options: *Options, name: []const u8, path: LazyPat...@@ -451,7 +469,7 @@ pub fn addOptionPathUntracked(options: *Options, name: []const u8, path: LazyPat
451 const arena = graph.arena;469 const arena = graph.arena;
452 const wc = &graph.wip_configuration;470 const wc = &graph.wip_configuration;
453471
454 options.args_untracked.append(arena, .{472 options.untracked_paths.append(arena, .{
455 .name = wc.addString(name) catch @panic("OOM"),473 .name = wc.addString(name) catch @panic("OOM"),
456 .path = path.dupe(options.step.owner.graph),474 .path = path.dupe(options.step.owner.graph),
457 }) catch @panic("OOM");475 }) catch @panic("OOM");