Skip to content

Commit 311797f

Browse files
authored
Rework build system build_options API (ziglang#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
1 parent 04cafd8 commit 311797f

File tree

3 files changed

+297
-224
lines changed

3 files changed

+297
-224
lines changed

build.zig

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 10 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub const WriteFileStep = @import("build/WriteFileStep.zig");
2323
pub const RunStep = @import("build/RunStep.zig");
2424
pub const CheckFileStep = @import("build/CheckFileStep.zig");
2525
pub const InstallRawStep = @import("build/InstallRawStep.zig");
26+
pub const OptionsStep = @import("build/OptionsStep.zig");
2627

2728
pub 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

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-
13881383
pub 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

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-
33523161
test "LibExeObjStep.addPackage" {
33533162
if (builtin.os.tag == .wasi) return error.SkipZigTest;
33543163

0 commit comments

Comments
 (0)