authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-01 18:26:19-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:35-07:00
logfa26566867cddbb0cd067cbc1d41bd41e68002a1
tree43bc4abdf7a328e36d54333df462108dc01c06f5
parentc57bf9904396c365191d25b9ce6410bb47daa96a

configurer: get InstallDir and Options steps compiling


10 files changed, 215 insertions(+), 327 deletions(-)

BRANCH_TODO-1
......@@ -26,7 +26,6 @@
2626 - but artifact install steps also add paths for dyn libs on windows
2727* no more "artifact arg" to run step. if you want to run the post-install binary, get the lazy path
2828 from the install step.
29* -D options which are files need to be accounted for in the configure cache
3029
3130
3231## Release Notes
build.zig+8-5
......@@ -208,7 +208,8 @@ pub fn build(b: *std.Build) !void {
208208 .single_threaded = single_threaded,
209209 });
210210 exe.pie = pie;
211 exe.entitlements = entitlements;
211 // https://codeberg.org/ziglang/zig/issues/32173
212 exe.entitlements = if (entitlements) |p| .{ .cwd_relative = p } else null;
212213 exe.use_new_linker = b.option(bool, "new-linker", "Use the new linker");
213214
214215 const use_llvm = b.option(bool, "use-llvm", "Use the llvm backend");
......@@ -1498,11 +1499,13 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath {
14981499 }),
14991500 });
15001501
1501 var dir = b.build_root.handle.openDir(io, "doc/langref", .{ .iterate = true }) catch |err| {
1502 std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{
1503 b.build_root, @errorName(err),
1504 });
1502 const langref_path: std.Build.Cache.Path = .{
1503 .root_dir = b.build_root,
1504 .sub_path = "doc/langref",
15051505 };
1506
1507 var dir = langref_path.root_dir.handle.openDir(io, langref_path.sub_path, .{ .iterate = true }) catch |err|
1508 std.debug.panic("unable to open directory {f}: {t}", .{ langref_path, err });
15061509 defer dir.close(io);
15071510
15081511 var wf = b.addWriteFiles();
lib/compiler/Maker/Step/InstallDir.zig created+66
......@@ -0,0 +1,66 @@
1const InstallDir = @This();
2
3const std = @import("std");
4const Configuration = std.Build.Configuration;
5
6const Step = @import("../Step.zig");
7const Maker = @import("../../Maker.zig");
8
9pub fn make(
10 install_dir: *InstallDir,
11 step_index: Configuration.Step.Index,
12 maker: *Maker,
13 progress_node: std.Progress.Node,
14) !void {
15 const graph = maker.graph;
16 const arena = maker.graph.arena; // TODO don't leak into process arena
17 const io = graph.io;
18 const step = maker.stepByIndex(step_index);
19
20 step.clearWatchInputs();
21 const dest_prefix = b.getInstallPath(install_dir.options.install_dir, install_dir.options.install_subdir);
22 const src_dir_path = install_dir.options.source_dir.getPath3(b, step);
23 const need_derived_inputs = try step.addDirectoryWatchInput(install_dir.options.source_dir);
24 var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
25 return step.fail("unable to open source directory '{f}': {t}", .{ src_dir_path, err });
26 };
27 defer src_dir.close(io);
28 var it = try src_dir.walk(arena);
29 var all_cached = true;
30 next_entry: while (try it.next(io)) |entry| {
31 for (install_dir.options.exclude_extensions) |ext| {
32 if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry;
33 }
34 if (install_dir.options.include_extensions) |incs| {
35 for (incs) |inc| {
36 if (std.mem.endsWith(u8, entry.path, inc)) break;
37 } else {
38 continue :next_entry;
39 }
40 }
41
42 const src_path = try install_dir.options.source_dir.join(arena, entry.path);
43 const dest_path = b.pathJoin(&.{ dest_prefix, entry.path });
44 switch (entry.kind) {
45 .directory => {
46 if (need_derived_inputs) _ = try step.addDirectoryWatchInput(src_path);
47 const p = try step.installDir(dest_path);
48 all_cached = all_cached and p == .existed;
49 },
50 .file => {
51 for (install_dir.options.blank_extensions) |ext| {
52 if (std.mem.endsWith(u8, entry.path, ext)) {
53 try b.truncateFile(dest_path);
54 continue :next_entry;
55 }
56 }
57
58 const p = try step.installFile(src_path, dest_path);
59 all_cached = all_cached and p == .fresh;
60 },
61 else => continue,
62 }
63 }
64
65 step.result_cached = all_cached;
66}
lib/compiler/Maker/Step/InstallFile.zig+1
......@@ -19,6 +19,7 @@ pub fn make(
1919 const conf = &maker.scanned_config.configuration;
2020 const conf_step = step_index.ptr(conf);
2121 const conf_if = conf_step.extended.get(conf.extra).install_file;
22
2223 try step.singleUnchangingWatchInput(maker, arena, conf_if.source.get(conf));
2324 const p = try maker.installLazyPathSub(arena, conf_if.source, conf_if.dest_dir, conf_if.dest_sub_path.slice(conf), step_index);
2425 step.result_cached = p == .fresh;
lib/compiler/Maker/Step/Options.zig created+84
......@@ -0,0 +1,84 @@
1const Options = @This();
2
3const std = @import("std");
4const Configuration = std.Build.Configuration;
5
6const Step = @import("../Step.zig");
7const Maker = @import("../../Maker.zig");
8
9
10fn make(
11 options: *Options,
12 step_index: Configuration.Step.Index,
13 maker: *Maker,
14 progress_node: std.Progress.Node,
15) !void {
16 // This step completes so quickly that no progress reporting is necessary.
17 _ = progress_node;
18
19 const graph = maker.graph;
20 const step = maker.stepByIndex(step_index);
21 const io = graph.io;
22 const cache_root = graph.local_cache_root;
23
24 for (options.args.items) |arg| {
25 options.addOption(
26 []const u8,
27 arg.name,
28 arg.path.getPath2(b, step),
29 );
30 }
31 if (!step.inputs.populated()) for (options.args.items) |arg| {
32 try step.addWatchInput(arg.path);
33 };
34
35 const basename = "options.zig";
36
37 // Hash contents to file name.
38 var hash = graph.cache.hash;
39 // Random bytes to make unique. Refresh this with new random bytes when
40 // implementation is modified in a non-backwards-compatible way.
41 hash.add(@as(u32, 0xad95e922));
42 hash.addBytes(options.contents.items);
43 const sub_path = "c" ++ fs.path.sep_str ++ hash.final() ++ fs.path.sep_str ++ basename;
44
45 options.generated_file.path = try cache_root.join(arena, &.{sub_path});
46
47 // Optimize for the hot path. Stat the file, and if it already exists,
48 // cache hit.
49 if (cache_root.handle.access(io, sub_path, .{})) |_| {
50 // This is the hot path, success.
51 step.result_cached = true;
52 return;
53 } else |outer_err| switch (outer_err) {
54 error.FileNotFound => {
55 var atomic_file = cache_root.handle.createFileAtomic(io, sub_path, .{
56 .replace = false,
57 .make_path = true,
58 }) catch |err| return step.fail("failed to create temporary path for '{f}{s}': {t}", .{
59 cache_root, sub_path, err,
60 });
61 defer atomic_file.deinit(io);
62
63 atomic_file.file.writeStreamingAll(io, options.contents.items) catch |err| {
64 return step.fail("failed to write options to temporary path for '{f}{s}': {t}", .{
65 cache_root, sub_path, err,
66 });
67 };
68
69 atomic_file.link(io) catch |err| switch (err) {
70 error.PathAlreadyExists => {
71 step.result_cached = true;
72 return;
73 },
74 else => return step.fail("failed to link temporary file into '{f}{s}': {t}", .{
75 cache_root, sub_path, err,
76 }),
77 };
78 },
79 else => |e| return step.fail("unable to access options file '{f}{s}': {t}", .{
80 cache_root, sub_path, e,
81 }),
82 }
83}
84
lib/std/Build.zig+7-7
......@@ -99,19 +99,19 @@ pub const Graph = struct {
9999 return @enumFromInt(graph.generated_files.items.len - 1);
100100 }
101101
102 pub fn dupeString(graph: *Graph, bytes: []const u8) []const u8 {
102 pub fn dupeString(graph: *const Graph, bytes: []const u8) []const u8 {
103103 return graph.arena.dupe(u8, bytes) catch @panic("OOM");
104104 }
105105
106 pub fn dupePath(graph: *Graph, bytes: []const u8) []const u8 {
106 pub fn dupePath(graph: *const Graph, bytes: []const u8) []const u8 {
107107 const arena = graph.arena;
108 if (builtin.os.tag != .windows) return graph.arena.dupe(u8, bytes) catch @panic("OOM");
108 if (builtin.os.tag != .windows) return arena.dupe(u8, bytes) catch @panic("OOM");
109109 const the_copy = arena.dupe(u8, bytes) catch @panic("OOM");
110110 mem.replaceScalar(u8, the_copy, '/', '\\');
111111 return the_copy;
112112 }
113113
114 pub fn dupeStrings(graph: *Graph, strings: []const []const u8) []const []const u8 {
114 pub fn dupeStrings(graph: *const Graph, strings: []const []const u8) []const []const u8 {
115115 const arena = graph.arena;
116116 const array = arena.alloc([]const u8, strings.len) catch @panic("OOM");
117117 for (array, strings) |*dest, source| dest.* = dupeString(graph, source);
......@@ -2186,7 +2186,7 @@ pub const LazyPath = union(enum) {
21862186 ///
21872187 /// The `b` parameter is only used for its allocator. All *Build instances
21882188 /// share the same allocator.
2189 pub fn dupe(lazy_path: LazyPath, graph: *Graph) LazyPath {
2189 pub fn dupe(lazy_path: LazyPath, graph: *const Graph) LazyPath {
21902190 return switch (lazy_path) {
21912191 .src_path => |sp| .{ .src_path = .{ .owner = sp.owner, .sub_path = sp.owner.dupePath(sp.sub_path) } },
21922192 .cwd_relative => |p| .{ .cwd_relative = graph.dupePath(p) },
......@@ -2245,9 +2245,9 @@ pub const InstallDir = union(enum) {
22452245 custom: []const u8,
22462246
22472247 /// Duplicates the install directory including the path if set to custom.
2248 pub fn dupe(dir: InstallDir, builder: *Build) InstallDir {
2248 pub fn dupe(dir: InstallDir, graph: *const Graph) InstallDir {
22492249 if (dir == .custom) {
2250 return .{ .custom = builder.dupe(dir.custom) };
2250 return .{ .custom = graph.dupeString(dir.custom) };
22512251 } else {
22522252 return dir;
22532253 }
lib/std/Build/Configuration.zig+21-2
......@@ -1039,10 +1039,20 @@ pub const Step = extern struct {
10391039
10401040 pub const InstallDir = struct {
10411041 flags: @This().Flags,
1042 source_dir: LazyPath.Index,
1043 dest_dir: InstallDestDir,
1044 dest_sub_path: Storage.FlagOptional(.flags, .dest_sub_path, String),
1045 exclude_extensions: Storage.FlagLengthPrefixedList(.flags, .exclude_extensions, String),
1046 include_extensions: Storage.FlagLengthPrefixedList(.flags, .include_extensions, String),
1047 blank_extensions: Storage.FlagLengthPrefixedList(.flags, .blank_extensions, String),
10421048
10431049 pub const Flags = packed struct(u32) {
10441050 tag: Tag = .install_dir,
1045 _: u27 = 0,
1051 dest_sub_path: bool,
1052 exclude_extensions: bool,
1053 include_extensions: bool,
1054 blank_extensions: bool,
1055 _: u23 = 0,
10461056 };
10471057 };
10481058
......@@ -1069,10 +1079,19 @@ pub const Step = extern struct {
10691079
10701080 pub const Options = struct {
10711081 flags: @This().Flags,
1082 generated_file: GeneratedFileIndex,
1083 contents: Bytes,
1084 args: Storage.FlagLengthPrefixedList(.flags, .args, Arg),
1085
1086 pub const Arg = extern struct {
1087 name: String,
1088 path: LazyPath.Index,
1089 };
10721090
10731091 pub const Flags = packed struct(u32) {
10741092 tag: Tag = .options,
1075 _: u27 = 0,
1093 args: bool,
1094 _: u26 = 0,
10761095 };
10771096 };
10781097
lib/std/Build/Step/InstallDir.zig+11-64
......@@ -1,9 +1,10 @@
1const InstallDir = @This();
2
13const std = @import("std");
24const mem = std.mem;
35const fs = std.fs;
46const Step = std.Build.Step;
57const LazyPath = std.Build.LazyPath;
6const InstallDir = @This();
78
89step: Step,
910options: Options,
......@@ -28,83 +29,29 @@ pub const Options = struct {
2829 /// `@import("test.zig")` would be a compile error.
2930 blank_extensions: []const []const u8 = &.{},
3031
31 fn dupe(opts: Options, b: *std.Build) Options {
32 fn dupe(opts: Options, graph: *const std.Build.Graph) Options {
3233 return .{
33 .source_dir = opts.source_dir.dupe(b),
34 .install_dir = opts.install_dir.dupe(b),
35 .install_subdir = b.dupe(opts.install_subdir),
36 .exclude_extensions = b.dupeStrings(opts.exclude_extensions),
37 .include_extensions = if (opts.include_extensions) |incs| b.dupeStrings(incs) else null,
38 .blank_extensions = b.dupeStrings(opts.blank_extensions),
34 .source_dir = opts.source_dir.dupe(graph),
35 .install_dir = opts.install_dir.dupe(graph),
36 .install_subdir = graph.dupeString(opts.install_subdir),
37 .exclude_extensions = graph.dupeStrings(opts.exclude_extensions),
38 .include_extensions = if (opts.include_extensions) |incs| graph.dupeStrings(incs) else null,
39 .blank_extensions = graph.dupeStrings(opts.blank_extensions),
3940 };
4041 }
4142};
4243
4344pub fn create(owner: *std.Build, options: Options) *InstallDir {
4445 const install_dir = owner.allocator.create(InstallDir) catch @panic("OOM");
46 const graph = owner.graph;
4547 install_dir.* = .{
4648 .step = Step.init(.{
4749 .tag = base_tag,
4850 .name = owner.fmt("install {s}/", .{options.source_dir.getDisplayName()}),
4951 .owner = owner,
50 .makeFn = make,
5152 }),
52 .options = options.dupe(owner),
53 .options = options.dupe(graph),
5354 };
5455 options.source_dir.addStepDependencies(&install_dir.step);
5556 return install_dir;
5657}
57
58fn make(step: *Step, options: Step.MakeOptions) !void {
59 _ = options;
60 const b = step.owner;
61 const io = b.graph.io;
62 const install_dir: *InstallDir = @fieldParentPtr("step", step);
63 step.clearWatchInputs();
64 const arena = b.allocator;
65 const dest_prefix = b.getInstallPath(install_dir.options.install_dir, install_dir.options.install_subdir);
66 const src_dir_path = install_dir.options.source_dir.getPath3(b, step);
67 const need_derived_inputs = try step.addDirectoryWatchInput(install_dir.options.source_dir);
68 var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
69 return step.fail("unable to open source directory '{f}': {t}", .{ src_dir_path, err });
70 };
71 defer src_dir.close(io);
72 var it = try src_dir.walk(arena);
73 var all_cached = true;
74 next_entry: while (try it.next(io)) |entry| {
75 for (install_dir.options.exclude_extensions) |ext| {
76 if (mem.endsWith(u8, entry.path, ext)) continue :next_entry;
77 }
78 if (install_dir.options.include_extensions) |incs| {
79 for (incs) |inc| {
80 if (mem.endsWith(u8, entry.path, inc)) break;
81 } else {
82 continue :next_entry;
83 }
84 }
85
86 const src_path = try install_dir.options.source_dir.join(b.allocator, entry.path);
87 const dest_path = b.pathJoin(&.{ dest_prefix, entry.path });
88 switch (entry.kind) {
89 .directory => {
90 if (need_derived_inputs) _ = try step.addDirectoryWatchInput(src_path);
91 const p = try step.installDir(dest_path);
92 all_cached = all_cached and p == .existed;
93 },
94 .file => {
95 for (install_dir.options.blank_extensions) |ext| {
96 if (mem.endsWith(u8, entry.path, ext)) {
97 try b.truncateFile(dest_path);
98 continue :next_entry;
99 }
100 }
101
102 const p = try step.installFile(src_path, dest_path);
103 all_cached = all_cached and p == .fresh;
104 },
105 else => continue,
106 }
107 }
108
109 step.result_cached = all_cached;
110}
lib/std/Build/Step/InstallFile.zig+10-7
......@@ -1,17 +1,18 @@
1const InstallFile = @This();
2
13const std = @import("std");
24const Step = std.Build.Step;
35const LazyPath = std.Build.LazyPath;
46const InstallDir = std.Build.InstallDir;
5const InstallFile = @This();
67const assert = std.debug.assert;
78
8pub const base_tag: Step.Tag = .install_file;
9
109step: Step,
1110source: LazyPath,
1211dir: InstallDir,
1312dest_rel_path: []const u8,
1413
14pub const base_tag: Step.Tag = .install_file;
15
1516pub fn create(
1617 owner: *std.Build,
1718 source: LazyPath,
......@@ -19,16 +20,18 @@ pub fn create(
1920 dest_rel_path: []const u8,
2021) *InstallFile {
2122 assert(dest_rel_path.len != 0);
22 const install_file = owner.allocator.create(InstallFile) catch @panic("OOM");
23 const graph = owner.graph;
24 const arena = graph.arena;
25 const install_file = arena.create(InstallFile) catch @panic("OOM");
2326 install_file.* = .{
2427 .step = Step.init(.{
2528 .tag = base_tag,
2629 .name = owner.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }),
2730 .owner = owner,
2831 }),
29 .source = source.dupe(owner),
30 .dir = dir.dupe(owner),
31 .dest_rel_path = owner.dupePath(dest_rel_path),
32 .source = source.dupe(graph),
33 .dir = dir.dupe(graph),
34 .dest_rel_path = graph.dupePath(dest_rel_path),
3235 };
3336 source.addStepDependencies(&install_file.step);
3437 return install_file;
lib/std/Build/Step/Options.zig+7-241
......@@ -9,15 +9,19 @@ const Step = std.Build.Step;
99const LazyPath = std.Build.LazyPath;
1010const Configuration = std.Build.Configuration;
1111
12pub const base_tag: Step.Tag = .options;
13
1412step: Step,
1513generated_file: Configuration.GeneratedFileIndex,
16
1714contents: std.ArrayList(u8),
1815args: std.ArrayList(Arg),
1916encountered_types: std.StringHashMapUnmanaged(void),
2017
18pub const base_tag: Step.Tag = .options;
19
20pub const Arg = struct {
21 name: []const u8,
22 path: LazyPath,
23};
24
2125pub fn create(owner: *std.Build) *Options {
2226 const graph = owner.graph;
2327 const arena = graph.arena;
......@@ -28,7 +32,6 @@ pub fn create(owner: *std.Build) *Options {
2832 .tag = base_tag,
2933 .name = "options",
3034 .owner = owner,
31 .makeFn = make,
3235 }),
3336 .generated_file = graph.addGeneratedFile(&options.step),
3437 .contents = .empty,
......@@ -439,240 +442,3 @@ pub fn createModule(options: *Options) *std.Build.Module {
439442pub fn getOutput(options: *Options) LazyPath {
440443 return .{ .generated = .{ .index = options.generated_file } };
441444}
442
443fn make(step: *Step, make_options: Step.MakeOptions) !void {
444 // This step completes so quickly that no progress reporting is necessary.
445 _ = make_options;
446
447 const b = step.owner;
448 const io = b.graph.io;
449 const options: *Options = @fieldParentPtr("step", step);
450
451 for (options.args.items) |item| {
452 options.addOption(
453 []const u8,
454 item.name,
455 item.path.getPath2(b, step),
456 );
457 }
458 if (!step.inputs.populated()) for (options.args.items) |item| {
459 try step.addWatchInput(item.path);
460 };
461
462 const basename = "options.zig";
463
464 // Hash contents to file name.
465 var hash = b.graph.cache.hash;
466 // Random bytes to make unique. Refresh this with new random bytes when
467 // implementation is modified in a non-backwards-compatible way.
468 hash.add(@as(u32, 0xad95e922));
469 hash.addBytes(options.contents.items);
470 const sub_path = "c" ++ fs.path.sep_str ++ hash.final() ++ fs.path.sep_str ++ basename;
471
472 options.generated_file.path = try b.cache_root.join(b.allocator, &.{sub_path});
473
474 // Optimize for the hot path. Stat the file, and if it already exists,
475 // cache hit.
476 if (b.cache_root.handle.access(io, sub_path, .{})) |_| {
477 // This is the hot path, success.
478 step.result_cached = true;
479 return;
480 } else |outer_err| switch (outer_err) {
481 error.FileNotFound => {
482 var atomic_file = b.cache_root.handle.createFileAtomic(io, sub_path, .{
483 .replace = false,
484 .make_path = true,
485 }) catch |err| return step.fail("failed to create temporary path for '{f}{s}': {t}", .{
486 b.cache_root, sub_path, err,
487 });
488 defer atomic_file.deinit(io);
489
490 atomic_file.file.writeStreamingAll(io, options.contents.items) catch |err| {
491 return step.fail("failed to write options to temporary path for '{f}{s}': {t}", .{
492 b.cache_root, sub_path, err,
493 });
494 };
495
496 atomic_file.link(io) catch |err| switch (err) {
497 error.PathAlreadyExists => {
498 step.result_cached = true;
499 return;
500 },
501 else => return step.fail("failed to link temporary file into '{f}{s}': {t}", .{
502 b.cache_root, sub_path, err,
503 }),
504 };
505 },
506 else => |e| return step.fail("unable to access options file '{f}{s}': {t}", .{
507 b.cache_root, sub_path, e,
508 }),
509 }
510}
511
512const Arg = struct {
513 name: []const u8,
514 path: LazyPath,
515};
516
517test Options {
518 if (builtin.os.tag == .wasi) return error.SkipZigTest;
519
520 const io = std.testing.io;
521
522 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
523 defer arena.deinit();
524
525 const cwd = try std.process.currentPathAlloc(io, std.testing.allocator);
526 defer std.testing.allocator.free(cwd);
527
528 var graph: std.Build.Graph = .{
529 .io = io,
530 .arena = arena.allocator(),
531 .cache = .{
532 .io = io,
533 .gpa = arena.allocator(),
534 .manifest_dir = Io.Dir.cwd(),
535 .cwd = cwd,
536 },
537 .zig_exe = "test",
538 .environ_map = std.process.Environ.Map.init(arena.allocator()),
539 .global_cache_root = .{ .path = "test", .handle = Io.Dir.cwd() },
540 .host = .{
541 .query = .{},
542 .result = try std.zig.system.resolveTargetQuery(io, .{}),
543 },
544 .zig_lib_directory = std.Build.Cache.Directory.cwd(),
545 .time_report = false,
546 };
547
548 var builder = try std.Build.create(
549 &graph,
550 .{ .path = "test", .handle = Io.Dir.cwd() },
551 .{ .path = "test", .handle = Io.Dir.cwd() },
552 &.{},
553 );
554
555 const options = builder.addOptions();
556
557 const KeywordEnum = enum {
558 @"0.8.1",
559 };
560
561 const NormalEnum = enum {
562 foo,
563 bar,
564 };
565
566 const nested_array = [2][2]u16{
567 [2]u16{ 300, 200 },
568 [2]u16{ 300, 200 },
569 };
570 const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
571
572 const NormalStruct = struct {
573 hello: ?[]const u8,
574 world: bool = true,
575 };
576
577 const NestedStruct = struct {
578 normal_struct: NormalStruct,
579 normal_enum: NormalEnum = .foo,
580 };
581
582 options.addOption(usize, "option1", 1);
583 options.addOption(?usize, "option2", null);
584 options.addOption(?usize, "option3", 3);
585 options.addOption(comptime_int, "option4", 4);
586 options.addOption(comptime_float, "option5", 5.01);
587 options.addOption([]const u8, "string", "zigisthebest");
588 options.addOption(?[]const u8, "optional_string", null);
589 options.addOption([2][2]u16, "nested_array", nested_array);
590 options.addOption([]const []const u16, "nested_slice", nested_slice);
591 options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
592 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
593 options.addOption(NormalEnum, "normal1_enum", NormalEnum.foo);
594 options.addOption(NormalEnum, "normal2_enum", NormalEnum.bar);
595 options.addOption(NormalStruct, "normal1_struct", NormalStruct{
596 .hello = "foo",
597 });
598 options.addOption(NormalStruct, "normal2_struct", NormalStruct{
599 .hello = null,
600 .world = false,
601 });
602 options.addOption(NestedStruct, "nested_struct", NestedStruct{
603 .normal_struct = .{ .hello = "bar" },
604 });
605
606 try std.testing.expectEqualStrings(
607 \\pub const option1: usize = 1;
608 \\pub const option2: ?usize = null;
609 \\pub const option3: ?usize = 3;
610 \\pub const option4: comptime_int = 4;
611 \\pub const option5: comptime_float = 5.01;
612 \\pub const string: []const u8 = "zigisthebest";
613 \\pub const optional_string: ?[]const u8 = null;
614 \\pub const nested_array: [2][2]u16 = [2][2]u16 {
615 \\ [2]u16 {
616 \\ 300,
617 \\ 200,
618 \\ },
619 \\ [2]u16 {
620 \\ 300,
621 \\ 200,
622 \\ },
623 \\};
624 \\pub const nested_slice: []const []const u16 = &[_][]const u16 {
625 \\ &[_]u16 {
626 \\ 300,
627 \\ 200,
628 \\ },
629 \\ &[_]u16 {
630 \\ 300,
631 \\ 200,
632 \\ },
633 \\};
634 \\pub const @"Build.Step.Options.decltest.Options.KeywordEnum" = enum (u0) {
635 \\ @"0.8.1" = 0,
636 \\};
637 \\pub const keyword_enum: @"Build.Step.Options.decltest.Options.KeywordEnum" = .@"0.8.1";
638 \\pub const semantic_version: @import("std").SemanticVersion = .{
639 \\ .major = 0,
640 \\ .minor = 1,
641 \\ .patch = 2,
642 \\ .pre = "foo",
643 \\ .build = "bar",
644 \\};
645 \\pub const @"Build.Step.Options.decltest.Options.NormalEnum" = enum (u1) {
646 \\ foo = 0,
647 \\ bar = 1,
648 \\};
649 \\pub const normal1_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .foo;
650 \\pub const normal2_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .bar;
651 \\pub const @"Build.Step.Options.decltest.Options.NormalStruct" = struct {
652 \\ hello: ?[]const u8,
653 \\ world: bool = true,
654 \\};
655 \\pub const normal1_struct: @"Build.Step.Options.decltest.Options.NormalStruct" = .{
656 \\ .hello = "foo",
657 \\ .world = true,
658 \\};
659 \\pub const normal2_struct: @"Build.Step.Options.decltest.Options.NormalStruct" = .{
660 \\ .hello = null,
661 \\ .world = false,
662 \\};
663 \\pub const @"Build.Step.Options.decltest.Options.NestedStruct" = struct {
664 \\ normal_struct: @"Build.Step.Options.decltest.Options.NormalStruct",
665 \\ normal_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .foo,
666 \\};
667 \\pub const nested_struct: @"Build.Step.Options.decltest.Options.NestedStruct" = .{
668 \\ .normal_struct = .{
669 \\ .hello = "bar",
670 \\ .world = true,
671 \\ },
672 \\ .normal_enum = .foo,
673 \\};
674 \\
675 , options.contents.items);
676
677 _ = try std.zig.Ast.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(arena.allocator(), 0), .zig);
678}