authorgravatar for leecannon@leecannon.xyzLee Cannon <leecannon@leecannon.xyz> 2021-08-27 00:53:23+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-08-26 19:53:23-04:00
log311797f68619f3de66cbdecabc2af7d2022e8874
tree55986dfef14c5dbec0ac7634f1a4e56805019c7f
parent04cafd81377b9c57006a28407b0d97bc1164345e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Rework build system `build_options` API (#9623)

* rework `build_options` to integrate with the FileSource abstraction * support mapping as an arbitrarily named package * support mapping to multiple different artifacts * use hash of contents for options filename

3 files changed, 297 insertions(+), 224 deletions(-)

build.zig+30-23
......@@ -109,9 +109,13 @@ pub fn build(b: *Builder) !void {
109109 b.default_step.dependOn(&exe.step);
110110 exe.single_threaded = single_threaded;
111111
112 exe.addBuildOption(u32, "mem_leak_frames", mem_leak_frames);
113 exe.addBuildOption(bool, "skip_non_native", skip_non_native);
114 exe.addBuildOption(bool, "have_llvm", enable_llvm);
112 const exe_options = b.addOptions();
113 exe.addOptions("build_options", exe_options);
114
115 exe_options.addOption(u32, "mem_leak_frames", mem_leak_frames);
116 exe_options.addOption(bool, "skip_non_native", skip_non_native);
117 exe_options.addOption(bool, "have_llvm", enable_llvm);
118
115119 if (enable_llvm) {
116120 const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option);
117121
......@@ -218,15 +222,15 @@ pub fn build(b: *Builder) !void {
218222 },
219223 }
220224 };
221 exe.addBuildOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
225 exe_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
222226
223227 const semver = try std.SemanticVersion.parse(version);
224 exe.addBuildOption(std.SemanticVersion, "semver", semver);
228 exe_options.addOption(std.SemanticVersion, "semver", semver);
225229
226 exe.addBuildOption(bool, "enable_logging", enable_logging);
227 exe.addBuildOption(bool, "enable_tracy", tracy != null);
228 exe.addBuildOption(bool, "is_stage1", is_stage1);
229 exe.addBuildOption(bool, "omit_stage2", omit_stage2);
230 exe_options.addOption(bool, "enable_logging", enable_logging);
231 exe_options.addOption(bool, "enable_tracy", tracy != null);
232 exe_options.addOption(bool, "is_stage1", is_stage1);
233 exe_options.addOption(bool, "omit_stage2", omit_stage2);
230234 if (tracy) |tracy_path| {
231235 const client_cpp = fs.path.join(
232236 b.allocator,
......@@ -248,20 +252,23 @@ pub fn build(b: *Builder) !void {
248252 const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false;
249253 const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");
250254
251 test_stage2.addBuildOption(bool, "enable_logging", enable_logging);
252 test_stage2.addBuildOption(bool, "skip_non_native", skip_non_native);
253 test_stage2.addBuildOption(bool, "skip_compile_errors", skip_compile_errors);
254 test_stage2.addBuildOption(bool, "is_stage1", is_stage1);
255 test_stage2.addBuildOption(bool, "omit_stage2", omit_stage2);
256 test_stage2.addBuildOption(bool, "have_llvm", enable_llvm);
257 test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled);
258 test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled);
259 test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled);
260 test_stage2.addBuildOption(u32, "mem_leak_frames", mem_leak_frames * 2);
261 test_stage2.addBuildOption(bool, "enable_darling", is_darling_enabled);
262 test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir);
263 test_stage2.addBuildOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
264 test_stage2.addBuildOption(std.SemanticVersion, "semver", semver);
255 const test_stage2_options = b.addOptions();
256 test_stage2.addOptions("build_options", test_stage2_options);
257
258 test_stage2_options.addOption(bool, "enable_logging", enable_logging);
259 test_stage2_options.addOption(bool, "skip_non_native", skip_non_native);
260 test_stage2_options.addOption(bool, "skip_compile_errors", skip_compile_errors);
261 test_stage2_options.addOption(bool, "is_stage1", is_stage1);
262 test_stage2_options.addOption(bool, "omit_stage2", omit_stage2);
263 test_stage2_options.addOption(bool, "have_llvm", enable_llvm);
264 test_stage2_options.addOption(bool, "enable_qemu", is_qemu_enabled);
265 test_stage2_options.addOption(bool, "enable_wine", is_wine_enabled);
266 test_stage2_options.addOption(bool, "enable_wasmtime", is_wasmtime_enabled);
267 test_stage2_options.addOption(u32, "mem_leak_frames", mem_leak_frames * 2);
268 test_stage2_options.addOption(bool, "enable_darling", is_darling_enabled);
269 test_stage2_options.addOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir);
270 test_stage2_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
271 test_stage2_options.addOption(std.SemanticVersion, "semver", semver);
265272
266273 const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
267274 test_stage2_step.dependOn(&test_stage2.step);
lib/std/build.zig+10-201
......@@ -23,6 +23,7 @@ pub const WriteFileStep = @import("build/WriteFileStep.zig");
2323pub const RunStep = @import("build/RunStep.zig");
2424pub const CheckFileStep = @import("build/CheckFileStep.zig");
2525pub const InstallRawStep = @import("build/InstallRawStep.zig");
26pub const OptionsStep = @import("build/OptionsStep.zig");
2627
2728pub const Builder = struct {
2829 install_tls: TopLevelStep,
......@@ -247,6 +248,10 @@ pub const Builder = struct {
247248 return LibExeObjStep.createExecutable(builder, name, root_src);
248249 }
249250
251 pub fn addOptions(self: *Builder) *OptionsStep {
252 return OptionsStep.create(self);
253 }
254
250255 pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
251256 return addObjectSource(self, name, convertOptionalPathToFileSource(root_src));
252257 }
......@@ -1375,16 +1380,6 @@ pub const FileSource = union(enum) {
13751380 }
13761381};
13771382
1378const BuildOptionArtifactArg = struct {
1379 name: []const u8,
1380 artifact: *LibExeObjStep,
1381};
1382
1383const BuildOptionFileSourceArg = struct {
1384 name: []const u8,
1385 source: FileSource,
1386};
1387
13881383pub const LibExeObjStep = struct {
13891384 pub const base_id = .lib_exe_obj;
13901385
......@@ -1434,9 +1429,6 @@ pub const LibExeObjStep = struct {
14341429 out_lib_filename: []const u8,
14351430 out_pdb_filename: []const u8,
14361431 packages: ArrayList(Pkg),
1437 build_options_contents: std.ArrayList(u8),
1438 build_options_artifact_args: std.ArrayList(BuildOptionArtifactArg),
1439 build_options_file_source_args: std.ArrayList(BuildOptionFileSourceArg),
14401432
14411433 object_src: []const u8,
14421434
......@@ -1603,9 +1595,6 @@ pub const LibExeObjStep = struct {
16031595 .rpaths = ArrayList([]const u8).init(builder.allocator),
16041596 .framework_dirs = ArrayList([]const u8).init(builder.allocator),
16051597 .object_src = undefined,
1606 .build_options_contents = std.ArrayList(u8).init(builder.allocator),
1607 .build_options_artifact_args = std.ArrayList(BuildOptionArtifactArg).init(builder.allocator),
1608 .build_options_file_source_args = std.ArrayList(BuildOptionFileSourceArg).init(builder.allocator),
16091598 .c_std = Builder.CStd.C99,
16101599 .override_lib_dir = null,
16111600 .main_pkg_path = null,
......@@ -2038,119 +2027,6 @@ pub const LibExeObjStep = struct {
20382027 self.linkLibraryOrObject(obj);
20392028 }
20402029
2041 pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
2042 const out = self.build_options_contents.writer();
2043 switch (T) {
2044 []const []const u8 => {
2045 out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
2046 for (value) |slice| {
2047 out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
2048 }
2049 out.writeAll("};\n") catch unreachable;
2050 return;
2051 },
2052 [:0]const u8 => {
2053 out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
2054 return;
2055 },
2056 []const u8 => {
2057 out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
2058 return;
2059 },
2060 ?[:0]const u8 => {
2061 out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
2062 if (value) |payload| {
2063 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
2064 } else {
2065 out.writeAll("null;\n") catch unreachable;
2066 }
2067 return;
2068 },
2069 ?[]const u8 => {
2070 out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
2071 if (value) |payload| {
2072 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
2073 } else {
2074 out.writeAll("null;\n") catch unreachable;
2075 }
2076 return;
2077 },
2078 std.builtin.Version => {
2079 out.print(
2080 \\pub const {}: @import("std").builtin.Version = .{{
2081 \\ .major = {d},
2082 \\ .minor = {d},
2083 \\ .patch = {d},
2084 \\}};
2085 \\
2086 , .{
2087 std.zig.fmtId(name),
2088
2089 value.major,
2090 value.minor,
2091 value.patch,
2092 }) catch unreachable;
2093 },
2094 std.SemanticVersion => {
2095 out.print(
2096 \\pub const {}: @import("std").SemanticVersion = .{{
2097 \\ .major = {d},
2098 \\ .minor = {d},
2099 \\ .patch = {d},
2100 \\
2101 , .{
2102 std.zig.fmtId(name),
2103
2104 value.major,
2105 value.minor,
2106 value.patch,
2107 }) catch unreachable;
2108 if (value.pre) |some| {
2109 out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
2110 }
2111 if (value.build) |some| {
2112 out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
2113 }
2114 out.writeAll("};\n") catch unreachable;
2115 return;
2116 },
2117 else => {},
2118 }
2119 switch (@typeInfo(T)) {
2120 .Enum => |enum_info| {
2121 out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
2122 inline for (enum_info.fields) |field| {
2123 out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
2124 }
2125 out.writeAll("};\n") catch unreachable;
2126 },
2127 else => {},
2128 }
2129 out.print("pub const {}: {s} = {};\n", .{ std.zig.fmtId(name), @typeName(T), value }) catch unreachable;
2130 }
2131
2132 /// The value is the path in the cache dir.
2133 /// Adds a dependency automatically.
2134 pub fn addBuildOptionArtifact(self: *LibExeObjStep, name: []const u8, artifact: *LibExeObjStep) void {
2135 self.build_options_artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
2136 self.step.dependOn(&artifact.step);
2137 }
2138
2139 /// The value is the path in the cache dir.
2140 /// Adds a dependency automatically.
2141 /// basename refers to the basename of the WriteFileStep
2142 pub fn addBuildOptionFileSource(
2143 self: *LibExeObjStep,
2144 name: []const u8,
2145 source: FileSource,
2146 ) void {
2147 self.build_options_file_source_args.append(.{
2148 .name = name,
2149 .source = source.dupe(self.builder),
2150 }) catch unreachable;
2151 source.addStepDependencies(&self.step);
2152 }
2153
21542030 pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void {
21552031 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
21562032 }
......@@ -2176,6 +2052,10 @@ pub const LibExeObjStep = struct {
21762052 self.addRecursiveBuildDeps(package);
21772053 }
21782054
2055 pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
2056 self.addPackage(options.getPackage(package_name));
2057 }
2058
21792059 fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
21802060 package.path.addStepDependencies(&self.step);
21812061 if (package.dependencies) |deps| {
......@@ -2393,41 +2273,6 @@ pub const LibExeObjStep = struct {
23932273 }
23942274 }
23952275
2396 if (self.build_options_contents.items.len > 0 or
2397 self.build_options_artifact_args.items.len > 0 or
2398 self.build_options_file_source_args.items.len > 0)
2399 {
2400 // Render build artifact and write file options at the last minute, now that the path is known.
2401 //
2402 // Note that pathFromRoot uses resolve path, so this will have
2403 // correct behavior even if getOutputPath is already absolute.
2404 for (self.build_options_artifact_args.items) |item| {
2405 self.addBuildOption(
2406 []const u8,
2407 item.name,
2408 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
2409 );
2410 }
2411 for (self.build_options_file_source_args.items) |item| {
2412 self.addBuildOption(
2413 []const u8,
2414 item.name,
2415 item.source.getPath(self.builder),
2416 );
2417 }
2418
2419 const build_options_file = try fs.path.join(
2420 builder.allocator,
2421 &[_][]const u8{ builder.cache_root, builder.fmt("{s}_build_options.zig", .{self.name}) },
2422 );
2423 const path_from_root = builder.pathFromRoot(build_options_file);
2424 try fs.cwd().writeFile(path_from_root, self.build_options_contents.items);
2425 try zig_args.append("--pkg-begin");
2426 try zig_args.append("build_options");
2427 try zig_args.append(path_from_root);
2428 try zig_args.append("--pkg-end");
2429 }
2430
24312276 if (self.image_base) |image_base| {
24322277 try zig_args.append("--image-base");
24332278 try zig_args.append(builder.fmt("0x{x}", .{image_base}));
......@@ -3141,6 +2986,7 @@ pub const Step = struct {
31412986 run,
31422987 check_file,
31432988 install_raw,
2989 options,
31442990 custom,
31452991 };
31462992
......@@ -3312,43 +3158,6 @@ test "Builder.dupePkg()" {
33123158 try std.testing.expect(dupe_deps[0].path.path.ptr != pkg_dep.path.path.ptr);
33133159}
33143160
3315test "LibExeObjStep.addBuildOption" {
3316 if (builtin.os.tag == .wasi) return error.SkipZigTest;
3317
3318 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
3319 defer arena.deinit();
3320 var builder = try Builder.create(
3321 &arena.allocator,
3322 "test",
3323 "test",
3324 "test",
3325 "test",
3326 );
3327 defer builder.destroy();
3328
3329 var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
3330 exe.addBuildOption(usize, "option1", 1);
3331 exe.addBuildOption(?usize, "option2", null);
3332 exe.addBuildOption([]const u8, "string", "zigisthebest");
3333 exe.addBuildOption(?[]const u8, "optional_string", null);
3334 exe.addBuildOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
3335
3336 try std.testing.expectEqualStrings(
3337 \\pub const option1: usize = 1;
3338 \\pub const option2: ?usize = null;
3339 \\pub const string: []const u8 = "zigisthebest";
3340 \\pub const optional_string: ?[]const u8 = null;
3341 \\pub const semantic_version: @import("std").SemanticVersion = .{
3342 \\ .major = 0,
3343 \\ .minor = 1,
3344 \\ .patch = 2,
3345 \\ .pre = "foo",
3346 \\ .build = "bar",
3347 \\};
3348 \\
3349 , exe.build_options_contents.items);
3350}
3351
33523161test "LibExeObjStep.addPackage" {
33533162 if (builtin.os.tag == .wasi) return error.SkipZigTest;
33543163
lib/std/build/OptionsStep.zig created+257
......@@ -0,0 +1,257 @@
1const std = @import("../std.zig");
2const build = std.build;
3const fs = std.fs;
4const Step = build.Step;
5const Builder = build.Builder;
6const GeneratedFile = build.GeneratedFile;
7const LibExeObjStep = build.LibExeObjStep;
8const FileSource = build.FileSource;
9
10const OptionsStep = @This();
11
12step: Step,
13generated_file: GeneratedFile,
14builder: *Builder,
15
16contents: std.ArrayList(u8),
17artifact_args: std.ArrayList(OptionArtifactArg),
18file_source_args: std.ArrayList(OptionFileSourceArg),
19
20pub fn create(builder: *Builder) *OptionsStep {
21 const self = builder.allocator.create(OptionsStep) catch unreachable;
22 self.* = .{
23 .builder = builder,
24 .step = Step.init(.options, "options", builder.allocator, make),
25 .generated_file = undefined,
26 .contents = std.ArrayList(u8).init(builder.allocator),
27 .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
28 .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
29 };
30 self.generated_file = .{ .step = &self.step };
31
32 return self;
33}
34
35pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void {
36 const out = self.contents.writer();
37 switch (T) {
38 []const []const u8 => {
39 out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
40 for (value) |slice| {
41 out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
42 }
43 out.writeAll("};\n") catch unreachable;
44 return;
45 },
46 [:0]const u8 => {
47 out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
48 return;
49 },
50 []const u8 => {
51 out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
52 return;
53 },
54 ?[:0]const u8 => {
55 out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
56 if (value) |payload| {
57 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
58 } else {
59 out.writeAll("null;\n") catch unreachable;
60 }
61 return;
62 },
63 ?[]const u8 => {
64 out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
65 if (value) |payload| {
66 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
67 } else {
68 out.writeAll("null;\n") catch unreachable;
69 }
70 return;
71 },
72 std.builtin.Version => {
73 out.print(
74 \\pub const {}: @import("std").builtin.Version = .{{
75 \\ .major = {d},
76 \\ .minor = {d},
77 \\ .patch = {d},
78 \\}};
79 \\
80 , .{
81 std.zig.fmtId(name),
82
83 value.major,
84 value.minor,
85 value.patch,
86 }) catch unreachable;
87 },
88 std.SemanticVersion => {
89 out.print(
90 \\pub const {}: @import("std").SemanticVersion = .{{
91 \\ .major = {d},
92 \\ .minor = {d},
93 \\ .patch = {d},
94 \\
95 , .{
96 std.zig.fmtId(name),
97
98 value.major,
99 value.minor,
100 value.patch,
101 }) catch unreachable;
102 if (value.pre) |some| {
103 out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
104 }
105 if (value.build) |some| {
106 out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
107 }
108 out.writeAll("};\n") catch unreachable;
109 return;
110 },
111 else => {},
112 }
113 switch (@typeInfo(T)) {
114 .Enum => |enum_info| {
115 out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
116 inline for (enum_info.fields) |field| {
117 out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
118 }
119 out.writeAll("};\n") catch unreachable;
120 },
121 else => {},
122 }
123 out.print("pub const {}: {s} = {};\n", .{ std.zig.fmtId(name), @typeName(T), value }) catch unreachable;
124}
125
126/// The value is the path in the cache dir.
127/// Adds a dependency automatically.
128pub fn addOptionFileSource(
129 self: *OptionsStep,
130 name: []const u8,
131 source: FileSource,
132) void {
133 self.file_source_args.append(.{
134 .name = name,
135 .source = source.dupe(self.builder),
136 }) catch unreachable;
137 source.addStepDependencies(&self.step);
138}
139
140/// The value is the path in the cache dir.
141/// Adds a dependency automatically.
142pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void {
143 self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
144 self.step.dependOn(&artifact.step);
145}
146
147pub fn getPackage(self: OptionsStep, package_name: []const u8) build.Pkg {
148 return .{ .name = package_name, .path = self.getSource() };
149}
150
151pub fn getSource(self: OptionsStep) FileSource {
152 return .{ .generated = &self.generated_file };
153}
154
155fn make(step: *Step) !void {
156 const self = @fieldParentPtr(OptionsStep, "step", step);
157
158 for (self.artifact_args.items) |item| {
159 self.addOption(
160 []const u8,
161 item.name,
162 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
163 );
164 }
165
166 for (self.file_source_args.items) |item| {
167 self.addOption(
168 []const u8,
169 item.name,
170 item.source.getPath(self.builder),
171 );
172 }
173
174 const options_directory = self.builder.pathFromRoot(
175 try fs.path.join(
176 self.builder.allocator,
177 &[_][]const u8{ self.builder.cache_root, "options" },
178 ),
179 );
180
181 try fs.cwd().makePath(options_directory);
182
183 const options_file = try fs.path.join(
184 self.builder.allocator,
185 &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
186 );
187
188 try fs.cwd().writeFile(options_file, self.contents.items);
189
190 self.generated_file.path = options_file;
191}
192
193fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
194 // This implementation is copied from `WriteFileStep.make`
195
196 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
197
198 // Random bytes to make OptionsStep unique. Refresh this with
199 // new random bytes when OptionsStep implementation is modified
200 // in a non-backwards-compatible way.
201 hash.update("yL0Ya4KkmcCjBlP8");
202 hash.update(self.contents.items);
203
204 var digest: [48]u8 = undefined;
205 hash.final(&digest);
206 var hash_basename: [64]u8 = undefined;
207 _ = fs.base64_encoder.encode(&hash_basename, &digest);
208 return hash_basename;
209}
210
211const OptionArtifactArg = struct {
212 name: []const u8,
213 artifact: *LibExeObjStep,
214};
215
216const OptionFileSourceArg = struct {
217 name: []const u8,
218 source: FileSource,
219};
220
221test "OptionsStep" {
222 if (std.builtin.os.tag == .wasi) return error.SkipZigTest;
223
224 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
225 defer arena.deinit();
226 var builder = try Builder.create(
227 &arena.allocator,
228 "test",
229 "test",
230 "test",
231 "test",
232 );
233 defer builder.destroy();
234
235 const options = builder.addOptions();
236
237 options.addOption(usize, "option1", 1);
238 options.addOption(?usize, "option2", null);
239 options.addOption([]const u8, "string", "zigisthebest");
240 options.addOption(?[]const u8, "optional_string", null);
241 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
242
243 try std.testing.expectEqualStrings(
244 \\pub const option1: usize = 1;
245 \\pub const option2: ?usize = null;
246 \\pub const string: []const u8 = "zigisthebest";
247 \\pub const optional_string: ?[]const u8 = null;
248 \\pub const semantic_version: @import("std").SemanticVersion = .{
249 \\ .major = 0,
250 \\ .minor = 1,
251 \\ .patch = 2,
252 \\ .pre = "foo",
253 \\ .build = "bar",
254 \\};
255 \\
256 , options.contents.items);
257}