| author | |
| committer | |
| log | 311797f68619f3de66cbdecabc2af7d2022e8874 |
| tree | 55986dfef14c5dbec0ac7634f1a4e56805019c7f |
| parent | 04cafd81377b9c57006a28407b0d97bc1164345e |
| signature |
* 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 filename3 files changed, 297 insertions(+), 224 deletions(-)
build.zig+30-23| ... | ... | @@ -109,9 +109,13 @@ pub fn build(b: *Builder) !void { |
| 109 | 109 | b.default_step.dependOn(&exe.step); |
| 110 | 110 | exe.single_threaded = single_threaded; |
| 111 | 111 | |
| 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 | ||
| 115 | 119 | if (enable_llvm) { |
| 116 | 120 | const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option); |
| 117 | 121 | |
| ... | ... | @@ -218,15 +222,15 @@ pub fn build(b: *Builder) !void { |
| 218 | 222 | }, |
| 219 | 223 | } |
| 220 | 224 | }; |
| 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)); | |
| 222 | 226 | |
| 223 | 227 | const semver = try std.SemanticVersion.parse(version); |
| 224 | exe.addBuildOption(std.SemanticVersion, "semver", semver); | |
| 228 | exe_options.addOption(std.SemanticVersion, "semver", semver); | |
| 225 | 229 | |
| 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); | |
| 230 | 234 | if (tracy) |tracy_path| { |
| 231 | 235 | const client_cpp = fs.path.join( |
| 232 | 236 | b.allocator, |
| ... | ... | @@ -248,20 +252,23 @@ pub fn build(b: *Builder) !void { |
| 248 | 252 | const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false; |
| 249 | 253 | const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc"); |
| 250 | 254 | |
| 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); | |
| 265 | 272 | |
| 266 | 273 | const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); |
| 267 | 274 | test_stage2_step.dependOn(&test_stage2.step); |
lib/std/build.zig+10-201| ... | ... | @@ -23,6 +23,7 @@ pub const WriteFileStep = @import("build/WriteFileStep.zig"); |
| 23 | 23 | pub const RunStep = @import("build/RunStep.zig"); |
| 24 | 24 | pub const CheckFileStep = @import("build/CheckFileStep.zig"); |
| 25 | 25 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); |
| 26 | pub const OptionsStep = @import("build/OptionsStep.zig"); | |
| 26 | 27 | |
| 27 | 28 | pub const Builder = struct { |
| 28 | 29 | install_tls: TopLevelStep, |
| ... | ... | @@ -247,6 +248,10 @@ pub const Builder = struct { |
| 247 | 248 | return LibExeObjStep.createExecutable(builder, name, root_src); |
| 248 | 249 | } |
| 249 | 250 | |
| 251 | pub fn addOptions(self: *Builder) *OptionsStep { | |
| 252 | return OptionsStep.create(self); | |
| 253 | } | |
| 254 | ||
| 250 | 255 | pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { |
| 251 | 256 | return addObjectSource(self, name, convertOptionalPathToFileSource(root_src)); |
| 252 | 257 | } |
| ... | ... | @@ -1375,16 +1380,6 @@ pub const FileSource = union(enum) { |
| 1375 | 1380 | } |
| 1376 | 1381 | }; |
| 1377 | 1382 | |
| 1378 | const BuildOptionArtifactArg = struct { | |
| 1379 | name: []const u8, | |
| 1380 | artifact: *LibExeObjStep, | |
| 1381 | }; | |
| 1382 | ||
| 1383 | const BuildOptionFileSourceArg = struct { | |
| 1384 | name: []const u8, | |
| 1385 | source: FileSource, | |
| 1386 | }; | |
| 1387 | ||
| 1388 | 1383 | pub const LibExeObjStep = struct { |
| 1389 | 1384 | pub const base_id = .lib_exe_obj; |
| 1390 | 1385 | |
| ... | ... | @@ -1434,9 +1429,6 @@ pub const LibExeObjStep = struct { |
| 1434 | 1429 | out_lib_filename: []const u8, |
| 1435 | 1430 | out_pdb_filename: []const u8, |
| 1436 | 1431 | 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), | |
| 1440 | 1432 | |
| 1441 | 1433 | object_src: []const u8, |
| 1442 | 1434 | |
| ... | ... | @@ -1603,9 +1595,6 @@ pub const LibExeObjStep = struct { |
| 1603 | 1595 | .rpaths = ArrayList([]const u8).init(builder.allocator), |
| 1604 | 1596 | .framework_dirs = ArrayList([]const u8).init(builder.allocator), |
| 1605 | 1597 | .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), | |
| 1609 | 1598 | .c_std = Builder.CStd.C99, |
| 1610 | 1599 | .override_lib_dir = null, |
| 1611 | 1600 | .main_pkg_path = null, |
| ... | ... | @@ -2038,119 +2027,6 @@ pub const LibExeObjStep = struct { |
| 2038 | 2027 | self.linkLibraryOrObject(obj); |
| 2039 | 2028 | } |
| 2040 | 2029 | |
| 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 | ||
| 2154 | 2030 | pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void { |
| 2155 | 2031 | self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable; |
| 2156 | 2032 | } |
| ... | ... | @@ -2176,6 +2052,10 @@ pub const LibExeObjStep = struct { |
| 2176 | 2052 | self.addRecursiveBuildDeps(package); |
| 2177 | 2053 | } |
| 2178 | 2054 | |
| 2055 | pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void { | |
| 2056 | self.addPackage(options.getPackage(package_name)); | |
| 2057 | } | |
| 2058 | ||
| 2179 | 2059 | fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void { |
| 2180 | 2060 | package.path.addStepDependencies(&self.step); |
| 2181 | 2061 | if (package.dependencies) |deps| { |
| ... | ... | @@ -2393,41 +2273,6 @@ pub const LibExeObjStep = struct { |
| 2393 | 2273 | } |
| 2394 | 2274 | } |
| 2395 | 2275 | |
| 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 | ||
| 2431 | 2276 | if (self.image_base) |image_base| { |
| 2432 | 2277 | try zig_args.append("--image-base"); |
| 2433 | 2278 | try zig_args.append(builder.fmt("0x{x}", .{image_base})); |
| ... | ... | @@ -3141,6 +2986,7 @@ pub const Step = struct { |
| 3141 | 2986 | run, |
| 3142 | 2987 | check_file, |
| 3143 | 2988 | install_raw, |
| 2989 | options, | |
| 3144 | 2990 | custom, |
| 3145 | 2991 | }; |
| 3146 | 2992 | |
| ... | ... | @@ -3312,43 +3158,6 @@ test "Builder.dupePkg()" { |
| 3312 | 3158 | try std.testing.expect(dupe_deps[0].path.path.ptr != pkg_dep.path.path.ptr); |
| 3313 | 3159 | } |
| 3314 | 3160 | |
| 3315 | test "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 | ||
| 3352 | 3161 | test "LibExeObjStep.addPackage" { |
| 3353 | 3162 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 3354 | 3163 |
lib/std/build/OptionsStep.zig created+257| ... | ... | @@ -0,0 +1,257 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const build = std.build; | |
| 3 | const fs = std.fs; | |
| 4 | const Step = build.Step; | |
| 5 | const Builder = build.Builder; | |
| 6 | const GeneratedFile = build.GeneratedFile; | |
| 7 | const LibExeObjStep = build.LibExeObjStep; | |
| 8 | const FileSource = build.FileSource; | |
| 9 | ||
| 10 | const OptionsStep = @This(); | |
| 11 | ||
| 12 | step: Step, | |
| 13 | generated_file: GeneratedFile, | |
| 14 | builder: *Builder, | |
| 15 | ||
| 16 | contents: std.ArrayList(u8), | |
| 17 | artifact_args: std.ArrayList(OptionArtifactArg), | |
| 18 | file_source_args: std.ArrayList(OptionFileSourceArg), | |
| 19 | ||
| 20 | pub 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 | ||
| 35 | pub 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. | |
| 128 | pub 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. | |
| 142 | pub 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 | ||
| 147 | pub fn getPackage(self: OptionsStep, package_name: []const u8) build.Pkg { | |
| 148 | return .{ .name = package_name, .path = self.getSource() }; | |
| 149 | } | |
| 150 | ||
| 151 | pub fn getSource(self: OptionsStep) FileSource { | |
| 152 | return .{ .generated = &self.generated_file }; | |
| 153 | } | |
| 154 | ||
| 155 | fn 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 | ||
| 193 | fn 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 | ||
| 211 | const OptionArtifactArg = struct { | |
| 212 | name: []const u8, | |
| 213 | artifact: *LibExeObjStep, | |
| 214 | }; | |
| 215 | ||
| 216 | const OptionFileSourceArg = struct { | |
| 217 | name: []const u8, | |
| 218 | source: FileSource, | |
| 219 | }; | |
| 220 | ||
| 221 | test "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 | } |