mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
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
This commit is contained in:
parent
04cafd8137
commit
311797f686
53
build.zig
53
build.zig
@ -109,9 +109,13 @@ pub fn build(b: *Builder) !void {
|
|||||||
b.default_step.dependOn(&exe.step);
|
b.default_step.dependOn(&exe.step);
|
||||||
exe.single_threaded = single_threaded;
|
exe.single_threaded = single_threaded;
|
||||||
|
|
||||||
exe.addBuildOption(u32, "mem_leak_frames", mem_leak_frames);
|
const exe_options = b.addOptions();
|
||||||
exe.addBuildOption(bool, "skip_non_native", skip_non_native);
|
exe.addOptions("build_options", exe_options);
|
||||||
exe.addBuildOption(bool, "have_llvm", enable_llvm);
|
|
||||||
|
exe_options.addOption(u32, "mem_leak_frames", mem_leak_frames);
|
||||||
|
exe_options.addOption(bool, "skip_non_native", skip_non_native);
|
||||||
|
exe_options.addOption(bool, "have_llvm", enable_llvm);
|
||||||
|
|
||||||
if (enable_llvm) {
|
if (enable_llvm) {
|
||||||
const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option);
|
const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option);
|
||||||
|
|
||||||
@ -218,15 +222,15 @@ pub fn build(b: *Builder) !void {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
exe.addBuildOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
|
exe_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
|
||||||
|
|
||||||
const semver = try std.SemanticVersion.parse(version);
|
const semver = try std.SemanticVersion.parse(version);
|
||||||
exe.addBuildOption(std.SemanticVersion, "semver", semver);
|
exe_options.addOption(std.SemanticVersion, "semver", semver);
|
||||||
|
|
||||||
exe.addBuildOption(bool, "enable_logging", enable_logging);
|
exe_options.addOption(bool, "enable_logging", enable_logging);
|
||||||
exe.addBuildOption(bool, "enable_tracy", tracy != null);
|
exe_options.addOption(bool, "enable_tracy", tracy != null);
|
||||||
exe.addBuildOption(bool, "is_stage1", is_stage1);
|
exe_options.addOption(bool, "is_stage1", is_stage1);
|
||||||
exe.addBuildOption(bool, "omit_stage2", omit_stage2);
|
exe_options.addOption(bool, "omit_stage2", omit_stage2);
|
||||||
if (tracy) |tracy_path| {
|
if (tracy) |tracy_path| {
|
||||||
const client_cpp = fs.path.join(
|
const client_cpp = fs.path.join(
|
||||||
b.allocator,
|
b.allocator,
|
||||||
@ -248,20 +252,23 @@ pub fn build(b: *Builder) !void {
|
|||||||
const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false;
|
const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false;
|
||||||
const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");
|
const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");
|
||||||
|
|
||||||
test_stage2.addBuildOption(bool, "enable_logging", enable_logging);
|
const test_stage2_options = b.addOptions();
|
||||||
test_stage2.addBuildOption(bool, "skip_non_native", skip_non_native);
|
test_stage2.addOptions("build_options", test_stage2_options);
|
||||||
test_stage2.addBuildOption(bool, "skip_compile_errors", skip_compile_errors);
|
|
||||||
test_stage2.addBuildOption(bool, "is_stage1", is_stage1);
|
test_stage2_options.addOption(bool, "enable_logging", enable_logging);
|
||||||
test_stage2.addBuildOption(bool, "omit_stage2", omit_stage2);
|
test_stage2_options.addOption(bool, "skip_non_native", skip_non_native);
|
||||||
test_stage2.addBuildOption(bool, "have_llvm", enable_llvm);
|
test_stage2_options.addOption(bool, "skip_compile_errors", skip_compile_errors);
|
||||||
test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled);
|
test_stage2_options.addOption(bool, "is_stage1", is_stage1);
|
||||||
test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled);
|
test_stage2_options.addOption(bool, "omit_stage2", omit_stage2);
|
||||||
test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled);
|
test_stage2_options.addOption(bool, "have_llvm", enable_llvm);
|
||||||
test_stage2.addBuildOption(u32, "mem_leak_frames", mem_leak_frames * 2);
|
test_stage2_options.addOption(bool, "enable_qemu", is_qemu_enabled);
|
||||||
test_stage2.addBuildOption(bool, "enable_darling", is_darling_enabled);
|
test_stage2_options.addOption(bool, "enable_wine", is_wine_enabled);
|
||||||
test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir);
|
test_stage2_options.addOption(bool, "enable_wasmtime", is_wasmtime_enabled);
|
||||||
test_stage2.addBuildOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
|
test_stage2_options.addOption(u32, "mem_leak_frames", mem_leak_frames * 2);
|
||||||
test_stage2.addBuildOption(std.SemanticVersion, "semver", semver);
|
test_stage2_options.addOption(bool, "enable_darling", is_darling_enabled);
|
||||||
|
test_stage2_options.addOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir);
|
||||||
|
test_stage2_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
|
||||||
|
test_stage2_options.addOption(std.SemanticVersion, "semver", semver);
|
||||||
|
|
||||||
const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
|
const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
|
||||||
test_stage2_step.dependOn(&test_stage2.step);
|
test_stage2_step.dependOn(&test_stage2.step);
|
||||||
|
|||||||
@ -23,6 +23,7 @@ pub const WriteFileStep = @import("build/WriteFileStep.zig");
|
|||||||
pub const RunStep = @import("build/RunStep.zig");
|
pub const RunStep = @import("build/RunStep.zig");
|
||||||
pub const CheckFileStep = @import("build/CheckFileStep.zig");
|
pub const CheckFileStep = @import("build/CheckFileStep.zig");
|
||||||
pub const InstallRawStep = @import("build/InstallRawStep.zig");
|
pub const InstallRawStep = @import("build/InstallRawStep.zig");
|
||||||
|
pub const OptionsStep = @import("build/OptionsStep.zig");
|
||||||
|
|
||||||
pub const Builder = struct {
|
pub const Builder = struct {
|
||||||
install_tls: TopLevelStep,
|
install_tls: TopLevelStep,
|
||||||
@ -247,6 +248,10 @@ pub const Builder = struct {
|
|||||||
return LibExeObjStep.createExecutable(builder, name, root_src);
|
return LibExeObjStep.createExecutable(builder, name, root_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn addOptions(self: *Builder) *OptionsStep {
|
||||||
|
return OptionsStep.create(self);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
|
pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
|
||||||
return addObjectSource(self, name, convertOptionalPathToFileSource(root_src));
|
return addObjectSource(self, name, convertOptionalPathToFileSource(root_src));
|
||||||
}
|
}
|
||||||
@ -1375,16 +1380,6 @@ pub const FileSource = union(enum) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const BuildOptionArtifactArg = struct {
|
|
||||||
name: []const u8,
|
|
||||||
artifact: *LibExeObjStep,
|
|
||||||
};
|
|
||||||
|
|
||||||
const BuildOptionFileSourceArg = struct {
|
|
||||||
name: []const u8,
|
|
||||||
source: FileSource,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const LibExeObjStep = struct {
|
pub const LibExeObjStep = struct {
|
||||||
pub const base_id = .lib_exe_obj;
|
pub const base_id = .lib_exe_obj;
|
||||||
|
|
||||||
@ -1434,9 +1429,6 @@ pub const LibExeObjStep = struct {
|
|||||||
out_lib_filename: []const u8,
|
out_lib_filename: []const u8,
|
||||||
out_pdb_filename: []const u8,
|
out_pdb_filename: []const u8,
|
||||||
packages: ArrayList(Pkg),
|
packages: ArrayList(Pkg),
|
||||||
build_options_contents: std.ArrayList(u8),
|
|
||||||
build_options_artifact_args: std.ArrayList(BuildOptionArtifactArg),
|
|
||||||
build_options_file_source_args: std.ArrayList(BuildOptionFileSourceArg),
|
|
||||||
|
|
||||||
object_src: []const u8,
|
object_src: []const u8,
|
||||||
|
|
||||||
@ -1603,9 +1595,6 @@ pub const LibExeObjStep = struct {
|
|||||||
.rpaths = ArrayList([]const u8).init(builder.allocator),
|
.rpaths = ArrayList([]const u8).init(builder.allocator),
|
||||||
.framework_dirs = ArrayList([]const u8).init(builder.allocator),
|
.framework_dirs = ArrayList([]const u8).init(builder.allocator),
|
||||||
.object_src = undefined,
|
.object_src = undefined,
|
||||||
.build_options_contents = std.ArrayList(u8).init(builder.allocator),
|
|
||||||
.build_options_artifact_args = std.ArrayList(BuildOptionArtifactArg).init(builder.allocator),
|
|
||||||
.build_options_file_source_args = std.ArrayList(BuildOptionFileSourceArg).init(builder.allocator),
|
|
||||||
.c_std = Builder.CStd.C99,
|
.c_std = Builder.CStd.C99,
|
||||||
.override_lib_dir = null,
|
.override_lib_dir = null,
|
||||||
.main_pkg_path = null,
|
.main_pkg_path = null,
|
||||||
@ -2038,119 +2027,6 @@ pub const LibExeObjStep = struct {
|
|||||||
self.linkLibraryOrObject(obj);
|
self.linkLibraryOrObject(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
|
|
||||||
const out = self.build_options_contents.writer();
|
|
||||||
switch (T) {
|
|
||||||
[]const []const u8 => {
|
|
||||||
out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
|
|
||||||
for (value) |slice| {
|
|
||||||
out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
|
|
||||||
}
|
|
||||||
out.writeAll("};\n") catch unreachable;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
[:0]const u8 => {
|
|
||||||
out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
[]const u8 => {
|
|
||||||
out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
?[:0]const u8 => {
|
|
||||||
out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
|
|
||||||
if (value) |payload| {
|
|
||||||
out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
|
|
||||||
} else {
|
|
||||||
out.writeAll("null;\n") catch unreachable;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
?[]const u8 => {
|
|
||||||
out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
|
|
||||||
if (value) |payload| {
|
|
||||||
out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
|
|
||||||
} else {
|
|
||||||
out.writeAll("null;\n") catch unreachable;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
std.builtin.Version => {
|
|
||||||
out.print(
|
|
||||||
\\pub const {}: @import("std").builtin.Version = .{{
|
|
||||||
\\ .major = {d},
|
|
||||||
\\ .minor = {d},
|
|
||||||
\\ .patch = {d},
|
|
||||||
\\}};
|
|
||||||
\\
|
|
||||||
, .{
|
|
||||||
std.zig.fmtId(name),
|
|
||||||
|
|
||||||
value.major,
|
|
||||||
value.minor,
|
|
||||||
value.patch,
|
|
||||||
}) catch unreachable;
|
|
||||||
},
|
|
||||||
std.SemanticVersion => {
|
|
||||||
out.print(
|
|
||||||
\\pub const {}: @import("std").SemanticVersion = .{{
|
|
||||||
\\ .major = {d},
|
|
||||||
\\ .minor = {d},
|
|
||||||
\\ .patch = {d},
|
|
||||||
\\
|
|
||||||
, .{
|
|
||||||
std.zig.fmtId(name),
|
|
||||||
|
|
||||||
value.major,
|
|
||||||
value.minor,
|
|
||||||
value.patch,
|
|
||||||
}) catch unreachable;
|
|
||||||
if (value.pre) |some| {
|
|
||||||
out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
|
|
||||||
}
|
|
||||||
if (value.build) |some| {
|
|
||||||
out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
|
|
||||||
}
|
|
||||||
out.writeAll("};\n") catch unreachable;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
switch (@typeInfo(T)) {
|
|
||||||
.Enum => |enum_info| {
|
|
||||||
out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
|
|
||||||
inline for (enum_info.fields) |field| {
|
|
||||||
out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
|
|
||||||
}
|
|
||||||
out.writeAll("};\n") catch unreachable;
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
out.print("pub const {}: {s} = {};\n", .{ std.zig.fmtId(name), @typeName(T), value }) catch unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The value is the path in the cache dir.
|
|
||||||
/// Adds a dependency automatically.
|
|
||||||
pub fn addBuildOptionArtifact(self: *LibExeObjStep, name: []const u8, artifact: *LibExeObjStep) void {
|
|
||||||
self.build_options_artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
|
|
||||||
self.step.dependOn(&artifact.step);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The value is the path in the cache dir.
|
|
||||||
/// Adds a dependency automatically.
|
|
||||||
/// basename refers to the basename of the WriteFileStep
|
|
||||||
pub fn addBuildOptionFileSource(
|
|
||||||
self: *LibExeObjStep,
|
|
||||||
name: []const u8,
|
|
||||||
source: FileSource,
|
|
||||||
) void {
|
|
||||||
self.build_options_file_source_args.append(.{
|
|
||||||
.name = name,
|
|
||||||
.source = source.dupe(self.builder),
|
|
||||||
}) catch unreachable;
|
|
||||||
source.addStepDependencies(&self.step);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void {
|
pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void {
|
||||||
self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
|
self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
|
||||||
}
|
}
|
||||||
@ -2176,6 +2052,10 @@ pub const LibExeObjStep = struct {
|
|||||||
self.addRecursiveBuildDeps(package);
|
self.addRecursiveBuildDeps(package);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
|
||||||
|
self.addPackage(options.getPackage(package_name));
|
||||||
|
}
|
||||||
|
|
||||||
fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
|
fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
|
||||||
package.path.addStepDependencies(&self.step);
|
package.path.addStepDependencies(&self.step);
|
||||||
if (package.dependencies) |deps| {
|
if (package.dependencies) |deps| {
|
||||||
@ -2393,41 +2273,6 @@ pub const LibExeObjStep = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.build_options_contents.items.len > 0 or
|
|
||||||
self.build_options_artifact_args.items.len > 0 or
|
|
||||||
self.build_options_file_source_args.items.len > 0)
|
|
||||||
{
|
|
||||||
// Render build artifact and write file options at the last minute, now that the path is known.
|
|
||||||
//
|
|
||||||
// Note that pathFromRoot uses resolve path, so this will have
|
|
||||||
// correct behavior even if getOutputPath is already absolute.
|
|
||||||
for (self.build_options_artifact_args.items) |item| {
|
|
||||||
self.addBuildOption(
|
|
||||||
[]const u8,
|
|
||||||
item.name,
|
|
||||||
self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for (self.build_options_file_source_args.items) |item| {
|
|
||||||
self.addBuildOption(
|
|
||||||
[]const u8,
|
|
||||||
item.name,
|
|
||||||
item.source.getPath(self.builder),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const build_options_file = try fs.path.join(
|
|
||||||
builder.allocator,
|
|
||||||
&[_][]const u8{ builder.cache_root, builder.fmt("{s}_build_options.zig", .{self.name}) },
|
|
||||||
);
|
|
||||||
const path_from_root = builder.pathFromRoot(build_options_file);
|
|
||||||
try fs.cwd().writeFile(path_from_root, self.build_options_contents.items);
|
|
||||||
try zig_args.append("--pkg-begin");
|
|
||||||
try zig_args.append("build_options");
|
|
||||||
try zig_args.append(path_from_root);
|
|
||||||
try zig_args.append("--pkg-end");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.image_base) |image_base| {
|
if (self.image_base) |image_base| {
|
||||||
try zig_args.append("--image-base");
|
try zig_args.append("--image-base");
|
||||||
try zig_args.append(builder.fmt("0x{x}", .{image_base}));
|
try zig_args.append(builder.fmt("0x{x}", .{image_base}));
|
||||||
@ -3141,6 +2986,7 @@ pub const Step = struct {
|
|||||||
run,
|
run,
|
||||||
check_file,
|
check_file,
|
||||||
install_raw,
|
install_raw,
|
||||||
|
options,
|
||||||
custom,
|
custom,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3312,43 +3158,6 @@ test "Builder.dupePkg()" {
|
|||||||
try std.testing.expect(dupe_deps[0].path.path.ptr != pkg_dep.path.path.ptr);
|
try std.testing.expect(dupe_deps[0].path.path.ptr != pkg_dep.path.path.ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "LibExeObjStep.addBuildOption" {
|
|
||||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
|
||||||
|
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
||||||
defer arena.deinit();
|
|
||||||
var builder = try Builder.create(
|
|
||||||
&arena.allocator,
|
|
||||||
"test",
|
|
||||||
"test",
|
|
||||||
"test",
|
|
||||||
"test",
|
|
||||||
);
|
|
||||||
defer builder.destroy();
|
|
||||||
|
|
||||||
var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
|
|
||||||
exe.addBuildOption(usize, "option1", 1);
|
|
||||||
exe.addBuildOption(?usize, "option2", null);
|
|
||||||
exe.addBuildOption([]const u8, "string", "zigisthebest");
|
|
||||||
exe.addBuildOption(?[]const u8, "optional_string", null);
|
|
||||||
exe.addBuildOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
|
|
||||||
|
|
||||||
try std.testing.expectEqualStrings(
|
|
||||||
\\pub const option1: usize = 1;
|
|
||||||
\\pub const option2: ?usize = null;
|
|
||||||
\\pub const string: []const u8 = "zigisthebest";
|
|
||||||
\\pub const optional_string: ?[]const u8 = null;
|
|
||||||
\\pub const semantic_version: @import("std").SemanticVersion = .{
|
|
||||||
\\ .major = 0,
|
|
||||||
\\ .minor = 1,
|
|
||||||
\\ .patch = 2,
|
|
||||||
\\ .pre = "foo",
|
|
||||||
\\ .build = "bar",
|
|
||||||
\\};
|
|
||||||
\\
|
|
||||||
, exe.build_options_contents.items);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "LibExeObjStep.addPackage" {
|
test "LibExeObjStep.addPackage" {
|
||||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||||
|
|
||||||
|
|||||||
257
lib/std/build/OptionsStep.zig
Normal file
257
lib/std/build/OptionsStep.zig
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
const std = @import("../std.zig");
|
||||||
|
const build = std.build;
|
||||||
|
const fs = std.fs;
|
||||||
|
const Step = build.Step;
|
||||||
|
const Builder = build.Builder;
|
||||||
|
const GeneratedFile = build.GeneratedFile;
|
||||||
|
const LibExeObjStep = build.LibExeObjStep;
|
||||||
|
const FileSource = build.FileSource;
|
||||||
|
|
||||||
|
const OptionsStep = @This();
|
||||||
|
|
||||||
|
step: Step,
|
||||||
|
generated_file: GeneratedFile,
|
||||||
|
builder: *Builder,
|
||||||
|
|
||||||
|
contents: std.ArrayList(u8),
|
||||||
|
artifact_args: std.ArrayList(OptionArtifactArg),
|
||||||
|
file_source_args: std.ArrayList(OptionFileSourceArg),
|
||||||
|
|
||||||
|
pub fn create(builder: *Builder) *OptionsStep {
|
||||||
|
const self = builder.allocator.create(OptionsStep) catch unreachable;
|
||||||
|
self.* = .{
|
||||||
|
.builder = builder,
|
||||||
|
.step = Step.init(.options, "options", builder.allocator, make),
|
||||||
|
.generated_file = undefined,
|
||||||
|
.contents = std.ArrayList(u8).init(builder.allocator),
|
||||||
|
.artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
|
||||||
|
.file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
|
||||||
|
};
|
||||||
|
self.generated_file = .{ .step = &self.step };
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void {
|
||||||
|
const out = self.contents.writer();
|
||||||
|
switch (T) {
|
||||||
|
[]const []const u8 => {
|
||||||
|
out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
|
||||||
|
for (value) |slice| {
|
||||||
|
out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
|
||||||
|
}
|
||||||
|
out.writeAll("};\n") catch unreachable;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
[:0]const u8 => {
|
||||||
|
out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
[]const u8 => {
|
||||||
|
out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
?[:0]const u8 => {
|
||||||
|
out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
|
||||||
|
if (value) |payload| {
|
||||||
|
out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
|
||||||
|
} else {
|
||||||
|
out.writeAll("null;\n") catch unreachable;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
?[]const u8 => {
|
||||||
|
out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
|
||||||
|
if (value) |payload| {
|
||||||
|
out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
|
||||||
|
} else {
|
||||||
|
out.writeAll("null;\n") catch unreachable;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
std.builtin.Version => {
|
||||||
|
out.print(
|
||||||
|
\\pub const {}: @import("std").builtin.Version = .{{
|
||||||
|
\\ .major = {d},
|
||||||
|
\\ .minor = {d},
|
||||||
|
\\ .patch = {d},
|
||||||
|
\\}};
|
||||||
|
\\
|
||||||
|
, .{
|
||||||
|
std.zig.fmtId(name),
|
||||||
|
|
||||||
|
value.major,
|
||||||
|
value.minor,
|
||||||
|
value.patch,
|
||||||
|
}) catch unreachable;
|
||||||
|
},
|
||||||
|
std.SemanticVersion => {
|
||||||
|
out.print(
|
||||||
|
\\pub const {}: @import("std").SemanticVersion = .{{
|
||||||
|
\\ .major = {d},
|
||||||
|
\\ .minor = {d},
|
||||||
|
\\ .patch = {d},
|
||||||
|
\\
|
||||||
|
, .{
|
||||||
|
std.zig.fmtId(name),
|
||||||
|
|
||||||
|
value.major,
|
||||||
|
value.minor,
|
||||||
|
value.patch,
|
||||||
|
}) catch unreachable;
|
||||||
|
if (value.pre) |some| {
|
||||||
|
out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
|
||||||
|
}
|
||||||
|
if (value.build) |some| {
|
||||||
|
out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
|
||||||
|
}
|
||||||
|
out.writeAll("};\n") catch unreachable;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
switch (@typeInfo(T)) {
|
||||||
|
.Enum => |enum_info| {
|
||||||
|
out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
|
||||||
|
inline for (enum_info.fields) |field| {
|
||||||
|
out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
|
||||||
|
}
|
||||||
|
out.writeAll("};\n") catch unreachable;
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
out.print("pub const {}: {s} = {};\n", .{ std.zig.fmtId(name), @typeName(T), value }) catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The value is the path in the cache dir.
|
||||||
|
/// Adds a dependency automatically.
|
||||||
|
pub fn addOptionFileSource(
|
||||||
|
self: *OptionsStep,
|
||||||
|
name: []const u8,
|
||||||
|
source: FileSource,
|
||||||
|
) void {
|
||||||
|
self.file_source_args.append(.{
|
||||||
|
.name = name,
|
||||||
|
.source = source.dupe(self.builder),
|
||||||
|
}) catch unreachable;
|
||||||
|
source.addStepDependencies(&self.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The value is the path in the cache dir.
|
||||||
|
/// Adds a dependency automatically.
|
||||||
|
pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void {
|
||||||
|
self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
|
||||||
|
self.step.dependOn(&artifact.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getPackage(self: OptionsStep, package_name: []const u8) build.Pkg {
|
||||||
|
return .{ .name = package_name, .path = self.getSource() };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getSource(self: OptionsStep) FileSource {
|
||||||
|
return .{ .generated = &self.generated_file };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make(step: *Step) !void {
|
||||||
|
const self = @fieldParentPtr(OptionsStep, "step", step);
|
||||||
|
|
||||||
|
for (self.artifact_args.items) |item| {
|
||||||
|
self.addOption(
|
||||||
|
[]const u8,
|
||||||
|
item.name,
|
||||||
|
self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (self.file_source_args.items) |item| {
|
||||||
|
self.addOption(
|
||||||
|
[]const u8,
|
||||||
|
item.name,
|
||||||
|
item.source.getPath(self.builder),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const options_directory = self.builder.pathFromRoot(
|
||||||
|
try fs.path.join(
|
||||||
|
self.builder.allocator,
|
||||||
|
&[_][]const u8{ self.builder.cache_root, "options" },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
try fs.cwd().makePath(options_directory);
|
||||||
|
|
||||||
|
const options_file = try fs.path.join(
|
||||||
|
self.builder.allocator,
|
||||||
|
&[_][]const u8{ options_directory, &self.hashContentsToFileName() },
|
||||||
|
);
|
||||||
|
|
||||||
|
try fs.cwd().writeFile(options_file, self.contents.items);
|
||||||
|
|
||||||
|
self.generated_file.path = options_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
|
||||||
|
// This implementation is copied from `WriteFileStep.make`
|
||||||
|
|
||||||
|
var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
|
||||||
|
|
||||||
|
// Random bytes to make OptionsStep unique. Refresh this with
|
||||||
|
// new random bytes when OptionsStep implementation is modified
|
||||||
|
// in a non-backwards-compatible way.
|
||||||
|
hash.update("yL0Ya4KkmcCjBlP8");
|
||||||
|
hash.update(self.contents.items);
|
||||||
|
|
||||||
|
var digest: [48]u8 = undefined;
|
||||||
|
hash.final(&digest);
|
||||||
|
var hash_basename: [64]u8 = undefined;
|
||||||
|
_ = fs.base64_encoder.encode(&hash_basename, &digest);
|
||||||
|
return hash_basename;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OptionArtifactArg = struct {
|
||||||
|
name: []const u8,
|
||||||
|
artifact: *LibExeObjStep,
|
||||||
|
};
|
||||||
|
|
||||||
|
const OptionFileSourceArg = struct {
|
||||||
|
name: []const u8,
|
||||||
|
source: FileSource,
|
||||||
|
};
|
||||||
|
|
||||||
|
test "OptionsStep" {
|
||||||
|
if (std.builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||||
|
|
||||||
|
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
var builder = try Builder.create(
|
||||||
|
&arena.allocator,
|
||||||
|
"test",
|
||||||
|
"test",
|
||||||
|
"test",
|
||||||
|
"test",
|
||||||
|
);
|
||||||
|
defer builder.destroy();
|
||||||
|
|
||||||
|
const options = builder.addOptions();
|
||||||
|
|
||||||
|
options.addOption(usize, "option1", 1);
|
||||||
|
options.addOption(?usize, "option2", null);
|
||||||
|
options.addOption([]const u8, "string", "zigisthebest");
|
||||||
|
options.addOption(?[]const u8, "optional_string", null);
|
||||||
|
options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
|
||||||
|
|
||||||
|
try std.testing.expectEqualStrings(
|
||||||
|
\\pub const option1: usize = 1;
|
||||||
|
\\pub const option2: ?usize = null;
|
||||||
|
\\pub const string: []const u8 = "zigisthebest";
|
||||||
|
\\pub const optional_string: ?[]const u8 = null;
|
||||||
|
\\pub const semantic_version: @import("std").SemanticVersion = .{
|
||||||
|
\\ .major = 0,
|
||||||
|
\\ .minor = 1,
|
||||||
|
\\ .patch = 2,
|
||||||
|
\\ .pre = "foo",
|
||||||
|
\\ .build = "bar",
|
||||||
|
\\};
|
||||||
|
\\
|
||||||
|
, options.contents.items);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user