mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 21:08:36 +00:00
std.build: implement passing options to dependency packages
* introduce the concept of maps to user input options, but don't implement it for command line arg parsing yet. * remove setPreferredReleaseMode and standardReleaseOptions in favor of standardOptimizeOption which has a future-proof options parameter.
This commit is contained in:
parent
ef8f694d77
commit
063888afff
@ -73,8 +73,6 @@ pub const Builder = struct {
|
||||
build_root: []const u8,
|
||||
cache_root: []const u8,
|
||||
global_cache_root: []const u8,
|
||||
release_mode: ?std.builtin.Mode,
|
||||
is_release: bool,
|
||||
/// zig lib dir
|
||||
override_lib_dir: ?[]const u8,
|
||||
vcpkg_root: VcpkgRoot = .unattempted,
|
||||
@ -150,6 +148,7 @@ pub const Builder = struct {
|
||||
flag: void,
|
||||
scalar: []const u8,
|
||||
list: ArrayList([]const u8),
|
||||
map: StringHashMap(*const UserValue),
|
||||
};
|
||||
|
||||
const TypeId = enum {
|
||||
@ -223,8 +222,6 @@ pub const Builder = struct {
|
||||
.step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
|
||||
.description = "Remove build artifacts from prefix path",
|
||||
},
|
||||
.release_mode = null,
|
||||
.is_release = false,
|
||||
.override_lib_dir = null,
|
||||
.install_path = undefined,
|
||||
.args = null,
|
||||
@ -291,8 +288,6 @@ pub const Builder = struct {
|
||||
.build_root = build_root,
|
||||
.cache_root = parent.cache_root,
|
||||
.global_cache_root = parent.global_cache_root,
|
||||
.release_mode = parent.release_mode,
|
||||
.is_release = parent.is_release,
|
||||
.override_lib_dir = parent.override_lib_dir,
|
||||
.debug_log_scopes = parent.debug_log_scopes,
|
||||
.debug_compile_errors = parent.debug_compile_errors,
|
||||
@ -312,10 +307,55 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
fn applyArgs(b: *Builder, args: anytype) !void {
|
||||
// TODO this function is the way that a build.zig file communicates
|
||||
// options to its dependencies. It is the programmatic way to give
|
||||
// command line arguments to a build.zig script.
|
||||
_ = args;
|
||||
inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| {
|
||||
const v = @field(args, field.name);
|
||||
const T = @TypeOf(v);
|
||||
switch (T) {
|
||||
CrossTarget => {
|
||||
try b.user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = try v.zigTriple(b.allocator) },
|
||||
.used = false,
|
||||
});
|
||||
try b.user_input_options.put("cpu", .{
|
||||
.name = "cpu",
|
||||
.value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) },
|
||||
.used = false,
|
||||
});
|
||||
},
|
||||
[]const u8 => {
|
||||
try b.user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = v },
|
||||
.used = false,
|
||||
});
|
||||
},
|
||||
else => switch (@typeInfo(T)) {
|
||||
.Bool => {
|
||||
try b.user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = if (v) "true" else "false" },
|
||||
.used = false,
|
||||
});
|
||||
},
|
||||
.Enum => {
|
||||
try b.user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = @tagName(v) },
|
||||
.used = false,
|
||||
});
|
||||
},
|
||||
.Int => {
|
||||
try b.user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) },
|
||||
.used = false,
|
||||
});
|
||||
},
|
||||
else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
|
||||
},
|
||||
}
|
||||
}
|
||||
const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
|
||||
// Random bytes to make unique. Refresh this with new random bytes when
|
||||
// implementation is modified in a non-backwards-compatible way.
|
||||
@ -679,15 +719,19 @@ pub const Builder = struct {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.list => {
|
||||
log.err("Expected -D{s} to be a boolean, but received a list.\n", .{name});
|
||||
.list, .map => {
|
||||
log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{
|
||||
name, @tagName(option_ptr.value),
|
||||
});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
},
|
||||
.int => switch (option_ptr.value) {
|
||||
.flag => {
|
||||
log.err("Expected -D{s} to be an integer, but received a boolean.\n", .{name});
|
||||
.flag, .list, .map => {
|
||||
log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{
|
||||
name, @tagName(option_ptr.value),
|
||||
});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
@ -706,15 +750,12 @@ pub const Builder = struct {
|
||||
};
|
||||
return n;
|
||||
},
|
||||
.list => {
|
||||
log.err("Expected -D{s} to be an integer, but received a list.\n", .{name});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
},
|
||||
.float => switch (option_ptr.value) {
|
||||
.flag => {
|
||||
log.err("Expected -D{s} to be a float, but received a boolean.\n", .{name});
|
||||
.flag, .map, .list => {
|
||||
log.err("Expected -D{s} to be a float, but received a {s}.\n", .{
|
||||
name, @tagName(option_ptr.value),
|
||||
});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
@ -726,15 +767,12 @@ pub const Builder = struct {
|
||||
};
|
||||
return n;
|
||||
},
|
||||
.list => {
|
||||
log.err("Expected -D{s} to be a float, but received a list.\n", .{name});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
},
|
||||
.@"enum" => switch (option_ptr.value) {
|
||||
.flag => {
|
||||
log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name});
|
||||
.flag, .map, .list => {
|
||||
log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{
|
||||
name, @tagName(option_ptr.value),
|
||||
});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
@ -747,28 +785,22 @@ pub const Builder = struct {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.list => {
|
||||
log.err("Expected -D{s} to be a string, but received a list.\n", .{name});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
},
|
||||
.string => switch (option_ptr.value) {
|
||||
.flag => {
|
||||
log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
.list => {
|
||||
log.err("Expected -D{s} to be a string, but received a list.\n", .{name});
|
||||
.flag, .list, .map => {
|
||||
log.err("Expected -D{s} to be a string, but received a {s}.\n", .{
|
||||
name, @tagName(option_ptr.value),
|
||||
});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
.scalar => |s| return s,
|
||||
},
|
||||
.list => switch (option_ptr.value) {
|
||||
.flag => {
|
||||
log.err("Expected -D{s} to be a list, but received a boolean.\n", .{name});
|
||||
.flag, .map => {
|
||||
log.err("Expected -D{s} to be a list, but received a {s}.\n", .{
|
||||
name, @tagName(option_ptr.value),
|
||||
});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
@ -790,41 +822,24 @@ pub const Builder = struct {
|
||||
return &step_info.step;
|
||||
}
|
||||
|
||||
/// This provides the -Drelease option to the build user and does not give them the choice.
|
||||
pub fn setPreferredReleaseMode(self: *Builder, mode: std.builtin.Mode) void {
|
||||
if (self.release_mode != null) {
|
||||
@panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice");
|
||||
pub const StandardOptimizeOptionOptions = struct {
|
||||
preferred_optimize_mode: ?std.builtin.Mode = null,
|
||||
};
|
||||
|
||||
pub fn standardOptimizeOption(self: *Builder, options: StandardOptimizeOptionOptions) std.builtin.Mode {
|
||||
if (options.preferred_optimize_mode) |mode| {
|
||||
if (self.option(bool, "release", "optimize for end users") orelse false) {
|
||||
return mode;
|
||||
} else {
|
||||
return .Debug;
|
||||
}
|
||||
} else {
|
||||
return self.option(
|
||||
std.builtin.Mode,
|
||||
"optimize",
|
||||
"prioritize performance, safety, or binary size (-O flag)",
|
||||
) orelse .Debug;
|
||||
}
|
||||
const description = self.fmt("Create a release build ({s})", .{@tagName(mode)});
|
||||
self.is_release = self.option(bool, "release", description) orelse false;
|
||||
self.release_mode = if (self.is_release) mode else std.builtin.Mode.Debug;
|
||||
}
|
||||
|
||||
/// If you call this without first calling `setPreferredReleaseMode` then it gives the build user
|
||||
/// the choice of what kind of release.
|
||||
pub fn standardReleaseOptions(self: *Builder) std.builtin.Mode {
|
||||
if (self.release_mode) |mode| return mode;
|
||||
|
||||
const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false;
|
||||
const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false;
|
||||
const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false;
|
||||
|
||||
const mode = if (release_safe and !release_fast and !release_small)
|
||||
std.builtin.Mode.ReleaseSafe
|
||||
else if (release_fast and !release_safe and !release_small)
|
||||
std.builtin.Mode.ReleaseFast
|
||||
else if (release_small and !release_fast and !release_safe)
|
||||
std.builtin.Mode.ReleaseSmall
|
||||
else if (!release_fast and !release_safe and !release_small)
|
||||
std.builtin.Mode.Debug
|
||||
else x: {
|
||||
log.err("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)\n", .{});
|
||||
self.markInvalidUserInput();
|
||||
break :x std.builtin.Mode.Debug;
|
||||
};
|
||||
self.is_release = mode != .Debug;
|
||||
self.release_mode = mode;
|
||||
return mode;
|
||||
}
|
||||
|
||||
pub const StandardTargetOptionsArgs = struct {
|
||||
@ -1004,6 +1019,11 @@ pub const Builder = struct {
|
||||
log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
|
||||
return true;
|
||||
},
|
||||
.map => |*map| {
|
||||
_ = map;
|
||||
log.warn("TODO maps as command line arguments is not implemented yet.", .{});
|
||||
return true;
|
||||
},
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -1026,7 +1046,7 @@ pub const Builder = struct {
|
||||
log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
|
||||
return true;
|
||||
},
|
||||
.list => {
|
||||
.list, .map => {
|
||||
log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
|
||||
return true;
|
||||
},
|
||||
@ -1058,7 +1078,7 @@ pub const Builder = struct {
|
||||
var it = self.user_input_options.iterator();
|
||||
while (it.next()) |entry| {
|
||||
if (!entry.value_ptr.used) {
|
||||
log.err("Invalid option: -D{s}\n", .{entry.key_ptr.*});
|
||||
log.err("Invalid option: -D{s}", .{entry.key_ptr.*});
|
||||
self.markInvalidUserInput();
|
||||
}
|
||||
}
|
||||
@ -1456,6 +1476,11 @@ pub const Builder = struct {
|
||||
) *Dependency {
|
||||
const sub_builder = b.createChild(name, build_root, args) catch unreachable;
|
||||
sub_builder.runBuild(build_zig) catch unreachable;
|
||||
|
||||
if (sub_builder.validateUserInputDidItFail()) {
|
||||
std.debug.dumpCurrentStackTrace(@returnAddress());
|
||||
}
|
||||
|
||||
const dep = b.allocator.create(Dependency) catch unreachable;
|
||||
dep.* = .{ .builder = sub_builder };
|
||||
return dep;
|
||||
@ -1718,6 +1743,34 @@ pub const InstalledFile = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
|
||||
// TODO this logic can disappear if cpu model + features becomes part of the target triple
|
||||
const all_features = cpu.arch.allFeaturesList();
|
||||
var populated_cpu_features = cpu.model.features;
|
||||
populated_cpu_features.populateDependencies(all_features);
|
||||
|
||||
if (populated_cpu_features.eql(cpu.features)) {
|
||||
// The CPU name alone is sufficient.
|
||||
return cpu.model.name;
|
||||
} else {
|
||||
var mcpu_buffer = ArrayList(u8).init(allocator);
|
||||
try mcpu_buffer.appendSlice(cpu.model.name);
|
||||
|
||||
for (all_features) |feature, i_usize| {
|
||||
const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
|
||||
const in_cpu_set = populated_cpu_features.isEnabled(i);
|
||||
const in_actual_set = cpu.features.isEnabled(i);
|
||||
if (in_cpu_set and !in_actual_set) {
|
||||
try mcpu_buffer.writer().print("-{s}", .{feature.name});
|
||||
} else if (!in_cpu_set and in_actual_set) {
|
||||
try mcpu_buffer.writer().print("+{s}", .{feature.name});
|
||||
}
|
||||
}
|
||||
|
||||
return try mcpu_buffer.toOwnedSlice();
|
||||
}
|
||||
}
|
||||
|
||||
test "dupePkg()" {
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
|
||||
@ -1495,37 +1495,10 @@ fn make(step: *Step) !void {
|
||||
}
|
||||
|
||||
if (!self.target.isNative()) {
|
||||
try zig_args.append("-target");
|
||||
try zig_args.append(try self.target.zigTriple(builder.allocator));
|
||||
|
||||
// TODO this logic can disappear if cpu model + features becomes part of the target triple
|
||||
const cross = self.target.toTarget();
|
||||
const all_features = cross.cpu.arch.allFeaturesList();
|
||||
var populated_cpu_features = cross.cpu.model.features;
|
||||
populated_cpu_features.populateDependencies(all_features);
|
||||
|
||||
if (populated_cpu_features.eql(cross.cpu.features)) {
|
||||
// The CPU name alone is sufficient.
|
||||
try zig_args.append("-mcpu");
|
||||
try zig_args.append(cross.cpu.model.name);
|
||||
} else {
|
||||
var mcpu_buffer = ArrayList(u8).init(builder.allocator);
|
||||
|
||||
try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name});
|
||||
|
||||
for (all_features) |feature, i_usize| {
|
||||
const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
|
||||
const in_cpu_set = populated_cpu_features.isEnabled(i);
|
||||
const in_actual_set = cross.cpu.features.isEnabled(i);
|
||||
if (in_cpu_set and !in_actual_set) {
|
||||
try mcpu_buffer.writer().print("-{s}", .{feature.name});
|
||||
} else if (!in_cpu_set and in_actual_set) {
|
||||
try mcpu_buffer.writer().print("+{s}", .{feature.name});
|
||||
}
|
||||
}
|
||||
|
||||
try zig_args.append(try mcpu_buffer.toOwnedSlice());
|
||||
}
|
||||
try zig_args.appendSlice(&.{
|
||||
"-target", try self.target.zigTriple(builder.allocator),
|
||||
"-mcpu", try build.serializeCpu(builder.allocator, self.target.getCpu()),
|
||||
});
|
||||
|
||||
if (self.target.dynamic_linker.get()) |dynamic_linker| {
|
||||
try zig_args.append("--dynamic-linker");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user