| 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,9 +109,13 @@ pub fn build(b: *Builder) !void { |
| 109 | b.default_step.dependOn(&exe.step); | 109 | b.default_step.dependOn(&exe.step); |
| 110 | exe.single_threaded = single_threaded; | 110 | exe.single_threaded = single_threaded; |
| 111 | 111 | ||
| 112 | exe.addBuildOption(u32, "mem_leak_frames", mem_leak_frames); | 112 | const exe_options = b.addOptions(); |
| 113 | exe.addBuildOption(bool, "skip_non_native", skip_non_native); | 113 | exe.addOptions("build_options", exe_options); |
| 114 | exe.addBuildOption(bool, "have_llvm", enable_llvm); | 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 | if (enable_llvm) { | 119 | if (enable_llvm) { |
| 116 | const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option); | 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,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 | const semver = try std.SemanticVersion.parse(version); | 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); | 230 | exe_options.addOption(bool, "enable_logging", enable_logging); |
| 227 | exe.addBuildOption(bool, "enable_tracy", tracy != null); | 231 | exe_options.addOption(bool, "enable_tracy", tracy != null); |
| 228 | exe.addBuildOption(bool, "is_stage1", is_stage1); | 232 | exe_options.addOption(bool, "is_stage1", is_stage1); |
| 229 | exe.addBuildOption(bool, "omit_stage2", omit_stage2); | 233 | exe_options.addOption(bool, "omit_stage2", omit_stage2); |
| 230 | if (tracy) |tracy_path| { | 234 | if (tracy) |tracy_path| { |
| 231 | const client_cpp = fs.path.join( | 235 | const client_cpp = fs.path.join( |
| 232 | b.allocator, | 236 | b.allocator, |
| ... | @@ -248,20 +252,23 @@ pub fn build(b: *Builder) !void { | ... | @@ -248,20 +252,23 @@ pub fn build(b: *Builder) !void { |
| 248 | const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false; | 252 | const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false; |
| 249 | const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc"); | 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); | 255 | const test_stage2_options = b.addOptions(); |
| 252 | test_stage2.addBuildOption(bool, "skip_non_native", skip_non_native); | 256 | test_stage2.addOptions("build_options", test_stage2_options); |
| 253 | test_stage2.addBuildOption(bool, "skip_compile_errors", skip_compile_errors); | 257 | |
| 254 | test_stage2.addBuildOption(bool, "is_stage1", is_stage1); | 258 | test_stage2_options.addOption(bool, "enable_logging", enable_logging); |
| 255 | test_stage2.addBuildOption(bool, "omit_stage2", omit_stage2); | 259 | test_stage2_options.addOption(bool, "skip_non_native", skip_non_native); |
| 256 | test_stage2.addBuildOption(bool, "have_llvm", enable_llvm); | 260 | test_stage2_options.addOption(bool, "skip_compile_errors", skip_compile_errors); |
| 257 | test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled); | 261 | test_stage2_options.addOption(bool, "is_stage1", is_stage1); |
| 258 | test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled); | 262 | test_stage2_options.addOption(bool, "omit_stage2", omit_stage2); |
| 259 | test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled); | 263 | test_stage2_options.addOption(bool, "have_llvm", enable_llvm); |
| 260 | test_stage2.addBuildOption(u32, "mem_leak_frames", mem_leak_frames * 2); | 264 | test_stage2_options.addOption(bool, "enable_qemu", is_qemu_enabled); |
| 261 | test_stage2.addBuildOption(bool, "enable_darling", is_darling_enabled); | 265 | test_stage2_options.addOption(bool, "enable_wine", is_wine_enabled); |
| 262 | test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir); | 266 | test_stage2_options.addOption(bool, "enable_wasmtime", is_wasmtime_enabled); |
| 263 | test_stage2.addBuildOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version)); | 267 | test_stage2_options.addOption(u32, "mem_leak_frames", mem_leak_frames * 2); |
| 264 | test_stage2.addBuildOption(std.SemanticVersion, "semver", semver); | 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 | const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); | 273 | const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); |
| 267 | test_stage2_step.dependOn(&test_stage2.step); | 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,6 +23,7 @@ pub const WriteFileStep = @import("build/WriteFileStep.zig"); |
| 23 | pub const RunStep = @import("build/RunStep.zig"); | 23 | pub const RunStep = @import("build/RunStep.zig"); |
| 24 | pub const CheckFileStep = @import("build/CheckFileStep.zig"); | 24 | pub const CheckFileStep = @import("build/CheckFileStep.zig"); |
| 25 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); | 25 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); |
| 26 | pub const OptionsStep = @import("build/OptionsStep.zig"); | ||
| 26 | 27 | ||
| 27 | pub const Builder = struct { | 28 | pub const Builder = struct { |
| 28 | install_tls: TopLevelStep, | 29 | install_tls: TopLevelStep, |
| ... | @@ -247,6 +248,10 @@ pub const Builder = struct { | ... | @@ -247,6 +248,10 @@ pub const Builder = struct { |
| 247 | return LibExeObjStep.createExecutable(builder, name, root_src); | 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 | pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { | 255 | pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { |
| 251 | return addObjectSource(self, name, convertOptionalPathToFileSource(root_src)); | 256 | return addObjectSource(self, name, convertOptionalPathToFileSource(root_src)); |
| 252 | } | 257 | } |
| ... | @@ -1375,16 +1380,6 @@ pub const FileSource = union(enum) { | ... | @@ -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 | pub const LibExeObjStep = struct { | 1383 | pub const LibExeObjStep = struct { |
| 1389 | pub const base_id = .lib_exe_obj; | 1384 | pub const base_id = .lib_exe_obj; |
| 1390 | 1385 | ||
| ... | @@ -1434,9 +1429,6 @@ pub const LibExeObjStep = struct { | ... | @@ -1434,9 +1429,6 @@ pub const LibExeObjStep = struct { |
| 1434 | out_lib_filename: []const u8, | 1429 | out_lib_filename: []const u8, |
| 1435 | out_pdb_filename: []const u8, | 1430 | out_pdb_filename: []const u8, |
| 1436 | packages: ArrayList(Pkg), | 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 | object_src: []const u8, | 1433 | object_src: []const u8, |
| 1442 | 1434 | ||
| ... | @@ -1603,9 +1595,6 @@ pub const LibExeObjStep = struct { | ... | @@ -1603,9 +1595,6 @@ pub const LibExeObjStep = struct { |
| 1603 | .rpaths = ArrayList([]const u8).init(builder.allocator), | 1595 | .rpaths = ArrayList([]const u8).init(builder.allocator), |
| 1604 | .framework_dirs = ArrayList([]const u8).init(builder.allocator), | 1596 | .framework_dirs = ArrayList([]const u8).init(builder.allocator), |
| 1605 | .object_src = undefined, | 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 | .c_std = Builder.CStd.C99, | 1598 | .c_std = Builder.CStd.C99, |
| 1610 | .override_lib_dir = null, | 1599 | .override_lib_dir = null, |
| 1611 | .main_pkg_path = null, | 1600 | .main_pkg_path = null, |
| ... | @@ -2038,119 +2027,6 @@ pub const LibExeObjStep = struct { | ... | @@ -2038,119 +2027,6 @@ pub const LibExeObjStep = struct { |
| 2038 | self.linkLibraryOrObject(obj); | 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 | pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void { | 2030 | pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void { |
| 2155 | self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable; | 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,6 +2052,10 @@ pub const LibExeObjStep = struct { |
| 2176 | self.addRecursiveBuildDeps(package); | 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 | fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void { | 2059 | fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void { |
| 2180 | package.path.addStepDependencies(&self.step); | 2060 | package.path.addStepDependencies(&self.step); |
| 2181 | if (package.dependencies) |deps| { | 2061 | if (package.dependencies) |deps| { |
| ... | @@ -2393,41 +2273,6 @@ pub const LibExeObjStep = struct { | ... | @@ -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 | if (self.image_base) |image_base| { | 2276 | if (self.image_base) |image_base| { |
| 2432 | try zig_args.append("--image-base"); | 2277 | try zig_args.append("--image-base"); |
| 2433 | try zig_args.append(builder.fmt("0x{x}", .{image_base})); | 2278 | try zig_args.append(builder.fmt("0x{x}", .{image_base})); |
| ... | @@ -3141,6 +2986,7 @@ pub const Step = struct { | ... | @@ -3141,6 +2986,7 @@ pub const Step = struct { |
| 3141 | run, | 2986 | run, |
| 3142 | check_file, | 2987 | check_file, |
| 3143 | install_raw, | 2988 | install_raw, |
| 2989 | options, | ||
| 3144 | custom, | 2990 | custom, |
| 3145 | }; | 2991 | }; |
| 3146 | 2992 | ||
| ... | @@ -3312,43 +3158,6 @@ test "Builder.dupePkg()" { | ... | @@ -3312,43 +3158,6 @@ test "Builder.dupePkg()" { |
| 3312 | try std.testing.expect(dupe_deps[0].path.path.ptr != pkg_dep.path.path.ptr); | 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 | test "LibExeObjStep.addPackage" { | 3161 | test "LibExeObjStep.addPackage" { |
| 3353 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | 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 | } | ||