aboutsummaryrefslogtreecommitdiff
path: root/lib/std/Target/Query.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-12-04 12:35:04 -0700
committerAndrew Kelley <andrew@ziglang.org>2024-01-01 17:51:18 -0700
commit3179f58c414b5e4845b9bf3acdf276fe8e2b88a0 (patch)
tree23d8f051385f7948aa8c97ece358fdbe35b86803 /lib/std/Target/Query.zig
parent67d48b94d601521e15dd44c8789b1f528d09f10c (diff)
downloadzig-3179f58c414b5e4845b9bf3acdf276fe8e2b88a0.tar.gz
zig-3179f58c414b5e4845b9bf3acdf276fe8e2b88a0.zip
rename std.zig.CrossTarget to std.Target.Query
Diffstat (limited to 'lib/std/Target/Query.zig')
-rw-r--r--lib/std/Target/Query.zig855
1 files changed, 855 insertions, 0 deletions
diff --git a/lib/std/Target/Query.zig b/lib/std/Target/Query.zig
new file mode 100644
index 0000000000..617d141d39
--- /dev/null
+++ b/lib/std/Target/Query.zig
@@ -0,0 +1,855 @@
+//! Contains all the same data as `Target`, additionally introducing the
+//! concept of "the native target". The purpose of this abstraction is to
+//! provide meaningful and unsurprising defaults. This struct does reference
+//! any resources and it is copyable.
+
+/// `null` means native.
+cpu_arch: ?Target.Cpu.Arch = null,
+
+cpu_model: CpuModel = CpuModel.determined_by_cpu_arch,
+
+/// Sparse set of CPU features to add to the set from `cpu_model`.
+cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
+
+/// Sparse set of CPU features to remove from the set from `cpu_model`.
+cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
+
+/// `null` means native.
+os_tag: ?Target.Os.Tag = null,
+
+/// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native)
+/// then `null` for this field means native.
+os_version_min: ?OsVersion = null,
+
+/// When cross compiling, `null` means default (latest known OS version).
+/// When `os_tag` is native, `null` means equal to the native OS version.
+os_version_max: ?OsVersion = null,
+
+/// `null` means default when cross compiling, or native when os_tag is native.
+/// If `isGnuLibC()` is `false`, this must be `null` and is ignored.
+glibc_version: ?SemanticVersion = null,
+
+/// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI.
+abi: ?Target.Abi = null,
+
+/// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path
+/// based on the `os_tag`.
+dynamic_linker: DynamicLinker = DynamicLinker{},
+
+/// `null` means default for the cpu/arch/os combo.
+ofmt: ?Target.ObjectFormat = null,
+
+pub const CpuModel = union(enum) {
+ /// Always native
+ native,
+
+ /// Always baseline
+ baseline,
+
+ /// If CPU Architecture is native, then the CPU model will be native. Otherwise,
+ /// it will be baseline.
+ determined_by_cpu_arch,
+
+ explicit: *const Target.Cpu.Model,
+};
+
+pub const OsVersion = union(enum) {
+ none: void,
+ semver: SemanticVersion,
+ windows: Target.Os.WindowsVersion,
+};
+
+pub const SemanticVersion = std.SemanticVersion;
+
+pub const DynamicLinker = Target.DynamicLinker;
+
+pub fn fromTarget(target: Target) Query {
+ var result: Query = .{
+ .cpu_arch = target.cpu.arch,
+ .cpu_model = .{ .explicit = target.cpu.model },
+ .os_tag = target.os.tag,
+ .os_version_min = undefined,
+ .os_version_max = undefined,
+ .abi = target.abi,
+ .glibc_version = if (target.isGnuLibC())
+ target.os.version_range.linux.glibc
+ else
+ null,
+ };
+ result.updateOsVersionRange(target.os);
+
+ const all_features = target.cpu.arch.allFeaturesList();
+ var cpu_model_set = target.cpu.model.features;
+ cpu_model_set.populateDependencies(all_features);
+ {
+ // The "add" set is the full set with the CPU Model set removed.
+ const add_set = &result.cpu_features_add;
+ add_set.* = target.cpu.features;
+ add_set.removeFeatureSet(cpu_model_set);
+ }
+ {
+ // The "sub" set is the features that are on in CPU Model set and off in the full set.
+ const sub_set = &result.cpu_features_sub;
+ sub_set.* = cpu_model_set;
+ sub_set.removeFeatureSet(target.cpu.features);
+ }
+ return result;
+}
+
+fn updateOsVersionRange(self: *Query, os: Target.Os) void {
+ switch (os.tag) {
+ .freestanding,
+ .ananas,
+ .cloudabi,
+ .fuchsia,
+ .kfreebsd,
+ .lv2,
+ .solaris,
+ .illumos,
+ .zos,
+ .haiku,
+ .minix,
+ .rtems,
+ .nacl,
+ .aix,
+ .cuda,
+ .nvcl,
+ .amdhsa,
+ .ps4,
+ .ps5,
+ .elfiamcu,
+ .mesa3d,
+ .contiki,
+ .amdpal,
+ .hermit,
+ .hurd,
+ .wasi,
+ .emscripten,
+ .driverkit,
+ .shadermodel,
+ .liteos,
+ .uefi,
+ .opencl,
+ .glsl450,
+ .vulkan,
+ .plan9,
+ .other,
+ => {
+ self.os_version_min = .{ .none = {} };
+ self.os_version_max = .{ .none = {} };
+ },
+
+ .freebsd,
+ .macos,
+ .ios,
+ .tvos,
+ .watchos,
+ .netbsd,
+ .openbsd,
+ .dragonfly,
+ => {
+ self.os_version_min = .{ .semver = os.version_range.semver.min };
+ self.os_version_max = .{ .semver = os.version_range.semver.max };
+ },
+
+ .linux => {
+ self.os_version_min = .{ .semver = os.version_range.linux.range.min };
+ self.os_version_max = .{ .semver = os.version_range.linux.range.max };
+ },
+
+ .windows => {
+ self.os_version_min = .{ .windows = os.version_range.windows.min };
+ self.os_version_max = .{ .windows = os.version_range.windows.max };
+ },
+ }
+}
+
+/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+pub fn toTarget(self: Query) Target {
+ return .{
+ .cpu = self.getCpu(),
+ .os = self.getOs(),
+ .abi = self.getAbi(),
+ .ofmt = self.getObjectFormat(),
+ };
+}
+
+pub const ParseOptions = struct {
+ /// This is sometimes called a "triple". It looks roughly like this:
+ /// riscv64-linux-musl
+ /// The fields are, respectively:
+ /// * CPU Architecture
+ /// * Operating System (and optional version range)
+ /// * C ABI (optional, with optional glibc version)
+ /// The string "native" can be used for CPU architecture as well as Operating System.
+ /// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted.
+ arch_os_abi: []const u8 = "native",
+
+ /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e"
+ /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features
+ /// to remove from the set.
+ /// The following special strings are recognized for CPU Model name:
+ /// * "baseline" - The "default" set of CPU features for cross-compiling. A conservative set
+ /// of features that is expected to be supported on most available hardware.
+ /// * "native" - The native CPU model is to be detected when compiling.
+ /// If this field is not provided (`null`), then the value will depend on the
+ /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".
+ cpu_features: ?[]const u8 = null,
+
+ /// Absolute path to dynamic linker, to override the default, which is either a natively
+ /// detected path, or a standard path.
+ dynamic_linker: ?[]const u8 = null,
+
+ object_format: ?[]const u8 = null,
+
+ /// If this is provided, the function will populate some information about parsing failures,
+ /// so that user-friendly error messages can be delivered.
+ diagnostics: ?*Diagnostics = null,
+
+ pub const Diagnostics = struct {
+ /// If the architecture was determined, this will be populated.
+ arch: ?Target.Cpu.Arch = null,
+
+ /// If the OS name was determined, this will be populated.
+ os_name: ?[]const u8 = null,
+
+ /// If the OS tag was determined, this will be populated.
+ os_tag: ?Target.Os.Tag = null,
+
+ /// If the ABI was determined, this will be populated.
+ abi: ?Target.Abi = null,
+
+ /// If the CPU name was determined, this will be populated.
+ cpu_name: ?[]const u8 = null,
+
+ /// If error.UnknownCpuFeature is returned, this will be populated.
+ unknown_feature_name: ?[]const u8 = null,
+ };
+};
+
+pub fn parse(args: ParseOptions) !Query {
+ var dummy_diags: ParseOptions.Diagnostics = undefined;
+ const diags = args.diagnostics orelse &dummy_diags;
+
+ var result: Query = .{
+ .dynamic_linker = DynamicLinker.init(args.dynamic_linker),
+ };
+
+ var it = mem.splitScalar(u8, args.arch_os_abi, '-');
+ const arch_name = it.first();
+ const arch_is_native = mem.eql(u8, arch_name, "native");
+ if (!arch_is_native) {
+ result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse
+ return error.UnknownArchitecture;
+ }
+ const arch = result.getCpuArch();
+ diags.arch = arch;
+
+ if (it.next()) |os_text| {
+ try parseOs(&result, diags, os_text);
+ } else if (!arch_is_native) {
+ return error.MissingOperatingSystem;
+ }
+
+ const opt_abi_text = it.next();
+ if (opt_abi_text) |abi_text| {
+ var abi_it = mem.splitScalar(u8, abi_text, '.');
+ const abi = std.meta.stringToEnum(Target.Abi, abi_it.first()) orelse
+ return error.UnknownApplicationBinaryInterface;
+ result.abi = abi;
+ diags.abi = abi;
+
+ const abi_ver_text = abi_it.rest();
+ if (abi_it.next() != null) {
+ if (result.isGnuLibC()) {
+ result.glibc_version = parseVersion(abi_ver_text) catch |err| switch (err) {
+ error.Overflow => return error.InvalidAbiVersion,
+ error.InvalidVersion => return error.InvalidAbiVersion,
+ };
+ } else {
+ return error.InvalidAbiVersion;
+ }
+ }
+ }
+
+ if (it.next() != null) return error.UnexpectedExtraField;
+
+ if (args.cpu_features) |cpu_features| {
+ const all_features = arch.allFeaturesList();
+ var index: usize = 0;
+ while (index < cpu_features.len and
+ cpu_features[index] != '+' and
+ cpu_features[index] != '-')
+ {
+ index += 1;
+ }
+ const cpu_name = cpu_features[0..index];
+ diags.cpu_name = cpu_name;
+
+ const add_set = &result.cpu_features_add;
+ const sub_set = &result.cpu_features_sub;
+ if (mem.eql(u8, cpu_name, "native")) {
+ result.cpu_model = .native;
+ } else if (mem.eql(u8, cpu_name, "baseline")) {
+ result.cpu_model = .baseline;
+ } else {
+ result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) };
+ }
+
+ while (index < cpu_features.len) {
+ const op = cpu_features[index];
+ const set = switch (op) {
+ '+' => add_set,
+ '-' => sub_set,
+ else => unreachable,
+ };
+ index += 1;
+ const start = index;
+ while (index < cpu_features.len and
+ cpu_features[index] != '+' and
+ cpu_features[index] != '-')
+ {
+ index += 1;
+ }
+ const feature_name = cpu_features[start..index];
+ for (all_features, 0..) |feature, feat_index_usize| {
+ const feat_index = @as(Target.Cpu.Feature.Set.Index, @intCast(feat_index_usize));
+ if (mem.eql(u8, feature_name, feature.name)) {
+ set.addFeature(feat_index);
+ break;
+ }
+ } else {
+ diags.unknown_feature_name = feature_name;
+ return error.UnknownCpuFeature;
+ }
+ }
+ }
+
+ if (args.object_format) |ofmt_name| {
+ result.ofmt = std.meta.stringToEnum(Target.ObjectFormat, ofmt_name) orelse
+ return error.UnknownObjectFormat;
+ }
+
+ return result;
+}
+
+/// Similar to `parse` except instead of fully parsing, it only determines the CPU
+/// architecture and returns it if it can be determined, and returns `null` otherwise.
+/// This is intended to be used if the API user of Query needs to learn the
+/// target CPU architecture in order to fully populate `ParseOptions`.
+pub fn parseCpuArch(args: ParseOptions) ?Target.Cpu.Arch {
+ var it = mem.splitScalar(u8, args.arch_os_abi, '-');
+ const arch_name = it.first();
+ const arch_is_native = mem.eql(u8, arch_name, "native");
+ if (arch_is_native) {
+ return builtin.cpu.arch;
+ } else {
+ return std.meta.stringToEnum(Target.Cpu.Arch, arch_name);
+ }
+}
+
+/// Similar to `SemanticVersion.parse`, but with following changes:
+/// * Leading zeroes are allowed.
+/// * Supports only 2 or 3 version components (major, minor, [patch]). If 3-rd component is omitted, it will be 0.
+pub fn parseVersion(ver: []const u8) error{ InvalidVersion, Overflow }!SemanticVersion {
+ const parseVersionComponentFn = (struct {
+ fn parseVersionComponentInner(component: []const u8) error{ InvalidVersion, Overflow }!usize {
+ return std.fmt.parseUnsigned(usize, component, 10) catch |err| switch (err) {
+ error.InvalidCharacter => return error.InvalidVersion,
+ error.Overflow => return error.Overflow,
+ };
+ }
+ }).parseVersionComponentInner;
+ var version_components = mem.splitScalar(u8, ver, '.');
+ const major = version_components.first();
+ const minor = version_components.next() orelse return error.InvalidVersion;
+ const patch = version_components.next() orelse "0";
+ if (version_components.next() != null) return error.InvalidVersion;
+ return .{
+ .major = try parseVersionComponentFn(major),
+ .minor = try parseVersionComponentFn(minor),
+ .patch = try parseVersionComponentFn(patch),
+ };
+}
+
+test parseVersion {
+ try std.testing.expectError(error.InvalidVersion, parseVersion("1"));
+ try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 0 }, try parseVersion("1.2"));
+ try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 3 }, try parseVersion("1.2.3"));
+ try std.testing.expectError(error.InvalidVersion, parseVersion("1.2.3.4"));
+}
+
+/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+pub fn getCpu(self: Query) Target.Cpu {
+ switch (self.cpu_model) {
+ .native => {
+ // This works when doing `zig build` because Zig generates a build executable using
+ // native CPU model & features. However this will not be accurate otherwise, and
+ // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
+ return builtin.cpu;
+ },
+ .baseline => {
+ var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
+ self.updateCpuFeatures(&adjusted_baseline.features);
+ return adjusted_baseline;
+ },
+ .determined_by_cpu_arch => if (self.cpu_arch == null) {
+ // This works when doing `zig build` because Zig generates a build executable using
+ // native CPU model & features. However this will not be accurate otherwise, and
+ // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
+ return builtin.cpu;
+ } else {
+ var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
+ self.updateCpuFeatures(&adjusted_baseline.features);
+ return adjusted_baseline;
+ },
+ .explicit => |model| {
+ var adjusted_model = model.toCpu(self.getCpuArch());
+ self.updateCpuFeatures(&adjusted_model.features);
+ return adjusted_model;
+ },
+ }
+}
+
+pub fn getCpuArch(self: Query) Target.Cpu.Arch {
+ return self.cpu_arch orelse builtin.cpu.arch;
+}
+
+pub fn getCpuModel(self: Query) *const Target.Cpu.Model {
+ return switch (self.cpu_model) {
+ .explicit => |cpu_model| cpu_model,
+ else => self.getCpu().model,
+ };
+}
+
+pub fn getCpuFeatures(self: Query) Target.Cpu.Feature.Set {
+ return self.getCpu().features;
+}
+
+/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+pub fn getOs(self: Query) Target.Os {
+ // `builtin.os` works when doing `zig build` because Zig generates a build executable using
+ // native OS version range. However this will not be accurate otherwise, and
+ // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
+ var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange(self.getCpuArch()) else builtin.os;
+
+ if (self.os_version_min) |min| switch (min) {
+ .none => {},
+ .semver => |semver| switch (self.getOsTag()) {
+ .linux => adjusted_os.version_range.linux.range.min = semver,
+ else => adjusted_os.version_range.semver.min = semver,
+ },
+ .windows => |win_ver| adjusted_os.version_range.windows.min = win_ver,
+ };
+
+ if (self.os_version_max) |max| switch (max) {
+ .none => {},
+ .semver => |semver| switch (self.getOsTag()) {
+ .linux => adjusted_os.version_range.linux.range.max = semver,
+ else => adjusted_os.version_range.semver.max = semver,
+ },
+ .windows => |win_ver| adjusted_os.version_range.windows.max = win_ver,
+ };
+
+ if (self.glibc_version) |glibc| {
+ assert(self.isGnuLibC());
+ adjusted_os.version_range.linux.glibc = glibc;
+ }
+
+ return adjusted_os;
+}
+
+pub fn getOsTag(self: Query) Target.Os.Tag {
+ return self.os_tag orelse builtin.os.tag;
+}
+
+/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+pub fn getOsVersionMin(self: Query) OsVersion {
+ if (self.os_version_min) |version_min| return version_min;
+ var tmp: Query = undefined;
+ tmp.updateOsVersionRange(self.getOs());
+ return tmp.os_version_min.?;
+}
+
+/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+pub fn getOsVersionMax(self: Query) OsVersion {
+ if (self.os_version_max) |version_max| return version_max;
+ var tmp: Query = undefined;
+ tmp.updateOsVersionRange(self.getOs());
+ return tmp.os_version_max.?;
+}
+
+/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+pub fn getAbi(self: Query) Target.Abi {
+ if (self.abi) |abi| return abi;
+
+ if (self.os_tag == null) {
+ // This works when doing `zig build` because Zig generates a build executable using
+ // native CPU model & features. However this will not be accurate otherwise, and
+ // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
+ return builtin.abi;
+ }
+
+ return Target.Abi.default(self.getCpuArch(), self.getOs());
+}
+
+pub fn isFreeBSD(self: Query) bool {
+ return self.getOsTag() == .freebsd;
+}
+
+pub fn isDarwin(self: Query) bool {
+ return self.getOsTag().isDarwin();
+}
+
+pub fn isNetBSD(self: Query) bool {
+ return self.getOsTag() == .netbsd;
+}
+
+pub fn isOpenBSD(self: Query) bool {
+ return self.getOsTag() == .openbsd;
+}
+
+pub fn isUefi(self: Query) bool {
+ return self.getOsTag() == .uefi;
+}
+
+pub fn isDragonFlyBSD(self: Query) bool {
+ return self.getOsTag() == .dragonfly;
+}
+
+pub fn isLinux(self: Query) bool {
+ return self.getOsTag() == .linux;
+}
+
+pub fn isWindows(self: Query) bool {
+ return self.getOsTag() == .windows;
+}
+
+pub fn exeFileExt(self: Query) [:0]const u8 {
+ return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag());
+}
+
+pub fn staticLibSuffix(self: Query) [:0]const u8 {
+ return Target.staticLibSuffix_os_abi(self.getOsTag(), self.getAbi());
+}
+
+pub fn dynamicLibSuffix(self: Query) [:0]const u8 {
+ return self.getOsTag().dynamicLibSuffix();
+}
+
+pub fn libPrefix(self: Query) [:0]const u8 {
+ return Target.libPrefix_os_abi(self.getOsTag(), self.getAbi());
+}
+
+pub fn isNativeCpu(self: Query) bool {
+ return self.cpu_arch == null and
+ (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and
+ self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty();
+}
+
+pub fn isNativeOs(self: Query) bool {
+ return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and
+ self.dynamic_linker.get() == null and self.glibc_version == null;
+}
+
+pub fn isNativeAbi(self: Query) bool {
+ return self.os_tag == null and self.abi == null;
+}
+
+pub fn isNative(self: Query) bool {
+ return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi();
+}
+
+/// Formats a version with the patch component omitted if it is zero,
+/// unlike SemanticVersion.format which formats all its version components regardless.
+fn formatVersion(version: SemanticVersion, writer: anytype) !void {
+ if (version.patch == 0) {
+ try writer.print("{d}.{d}", .{ version.major, version.minor });
+ } else {
+ try writer.print("{d}.{d}.{d}", .{ version.major, version.minor, version.patch });
+ }
+}
+
+pub fn zigTriple(self: Query, allocator: mem.Allocator) error{OutOfMemory}![]u8 {
+ if (self.isNative()) {
+ return allocator.dupe(u8, "native");
+ }
+
+ const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";
+ const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native";
+
+ var result = std.ArrayList(u8).init(allocator);
+ defer result.deinit();
+
+ try result.writer().print("{s}-{s}", .{ arch_name, os_name });
+
+ // The zig target syntax does not allow specifying a max os version with no min, so
+ // if either are present, we need the min.
+ if (self.os_version_min != null or self.os_version_max != null) {
+ switch (self.getOsVersionMin()) {
+ .none => {},
+ .semver => |v| {
+ try result.writer().writeAll(".");
+ try formatVersion(v, result.writer());
+ },
+ .windows => |v| try result.writer().print("{s}", .{v}),
+ }
+ }
+ if (self.os_version_max) |max| {
+ switch (max) {
+ .none => {},
+ .semver => |v| {
+ try result.writer().writeAll("...");
+ try formatVersion(v, result.writer());
+ },
+ .windows => |v| try result.writer().print("..{s}", .{v}),
+ }
+ }
+
+ if (self.glibc_version) |v| {
+ try result.writer().print("-{s}.", .{@tagName(self.getAbi())});
+ try formatVersion(v, result.writer());
+ } else if (self.abi) |abi| {
+ try result.writer().print("-{s}", .{@tagName(abi)});
+ }
+
+ return result.toOwnedSlice();
+}
+
+pub fn allocDescription(self: Query, allocator: mem.Allocator) ![]u8 {
+ // TODO is there anything else worthy of the description that is not
+ // already captured in the triple?
+ return self.zigTriple(allocator);
+}
+
+pub fn linuxTriple(self: Query, allocator: mem.Allocator) ![]u8 {
+ return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi());
+}
+
+pub fn isGnuLibC(self: Query) bool {
+ return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi());
+}
+
+pub fn setGnuLibCVersion(self: *Query, major: u32, minor: u32, patch: u32) void {
+ assert(self.isGnuLibC());
+ self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch };
+}
+
+pub fn getObjectFormat(self: Query) Target.ObjectFormat {
+ return self.ofmt orelse Target.ObjectFormat.default(self.getOsTag(), self.getCpuArch());
+}
+
+pub fn updateCpuFeatures(self: Query, set: *Target.Cpu.Feature.Set) void {
+ set.removeFeatureSet(self.cpu_features_sub);
+ set.addFeatureSet(self.cpu_features_add);
+ set.populateDependencies(self.getCpuArch().allFeaturesList());
+ set.removeFeatureSet(self.cpu_features_sub);
+}
+
+fn parseOs(result: *Query, diags: *ParseOptions.Diagnostics, text: []const u8) !void {
+ var it = mem.splitScalar(u8, text, '.');
+ const os_name = it.first();
+ diags.os_name = os_name;
+ const os_is_native = mem.eql(u8, os_name, "native");
+ if (!os_is_native) {
+ result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse
+ return error.UnknownOperatingSystem;
+ }
+ const tag = result.getOsTag();
+ diags.os_tag = tag;
+
+ const version_text = it.rest();
+ if (it.next() == null) return;
+
+ switch (tag) {
+ .freestanding,
+ .ananas,
+ .cloudabi,
+ .fuchsia,
+ .kfreebsd,
+ .lv2,
+ .solaris,
+ .illumos,
+ .zos,
+ .haiku,
+ .minix,
+ .rtems,
+ .nacl,
+ .aix,
+ .cuda,
+ .nvcl,
+ .amdhsa,
+ .ps4,
+ .ps5,
+ .elfiamcu,
+ .mesa3d,
+ .contiki,
+ .amdpal,
+ .hermit,
+ .hurd,
+ .wasi,
+ .emscripten,
+ .uefi,
+ .opencl,
+ .glsl450,
+ .vulkan,
+ .plan9,
+ .driverkit,
+ .shadermodel,
+ .liteos,
+ .other,
+ => return error.InvalidOperatingSystemVersion,
+
+ .freebsd,
+ .macos,
+ .ios,
+ .tvos,
+ .watchos,
+ .netbsd,
+ .openbsd,
+ .linux,
+ .dragonfly,
+ => {
+ var range_it = mem.splitSequence(u8, version_text, "...");
+
+ const min_text = range_it.next().?;
+ const min_ver = parseVersion(min_text) catch |err| switch (err) {
+ error.Overflow => return error.InvalidOperatingSystemVersion,
+ error.InvalidVersion => return error.InvalidOperatingSystemVersion,
+ };
+ result.os_version_min = .{ .semver = min_ver };
+
+ const max_text = range_it.next() orelse return;
+ const max_ver = parseVersion(max_text) catch |err| switch (err) {
+ error.Overflow => return error.InvalidOperatingSystemVersion,
+ error.InvalidVersion => return error.InvalidOperatingSystemVersion,
+ };
+ result.os_version_max = .{ .semver = max_ver };
+ },
+
+ .windows => {
+ var range_it = mem.splitSequence(u8, version_text, "...");
+
+ const min_text = range_it.first();
+ const min_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, min_text) orelse
+ return error.InvalidOperatingSystemVersion;
+ result.os_version_min = .{ .windows = min_ver };
+
+ const max_text = range_it.next() orelse return;
+ const max_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, max_text) orelse
+ return error.InvalidOperatingSystemVersion;
+ result.os_version_max = .{ .windows = max_ver };
+ },
+ }
+}
+
+const Query = @This();
+const std = @import("../std.zig");
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+const Target = std.Target;
+const mem = std.mem;
+
+test parse {
+ if (builtin.target.isGnuLibC()) {
+ var query = try Query.parse(.{});
+ query.setGnuLibCVersion(2, 1, 1);
+
+ const text = try query.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+
+ var buf: [256]u8 = undefined;
+ const triple = std.fmt.bufPrint(
+ buf[0..],
+ "native-native-{s}.2.1.1",
+ .{@tagName(builtin.abi)},
+ ) catch unreachable;
+
+ try std.testing.expectEqualSlices(u8, triple, text);
+ }
+ {
+ const query = try Query.parse(.{
+ .arch_os_abi = "aarch64-linux",
+ .cpu_features = "native",
+ });
+
+ try std.testing.expect(query.cpu_arch.? == .aarch64);
+ try std.testing.expect(query.cpu_model == .native);
+ }
+ {
+ const query = try Query.parse(.{ .arch_os_abi = "native" });
+
+ try std.testing.expect(query.cpu_arch == null);
+ try std.testing.expect(query.isNative());
+
+ const text = try query.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ try std.testing.expectEqualSlices(u8, "native", text);
+ }
+ {
+ const query = try Query.parse(.{
+ .arch_os_abi = "x86_64-linux-gnu",
+ .cpu_features = "x86_64-sse-sse2-avx-cx8",
+ });
+ const target = query.toTarget();
+
+ try std.testing.expect(target.os.tag == .linux);
+ try std.testing.expect(target.abi == .gnu);
+ try std.testing.expect(target.cpu.arch == .x86_64);
+ try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse));
+ try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx));
+ try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8));
+ try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov));
+ try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr));
+
+ try std.testing.expect(Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx, .cmov }));
+ try std.testing.expect(!Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx }));
+ try std.testing.expect(Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87 }));
+ try std.testing.expect(!Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87, .sse }));
+
+ const text = try query.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ try std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text);
+ }
+ {
+ const query = try Query.parse(.{
+ .arch_os_abi = "arm-linux-musleabihf",
+ .cpu_features = "generic+v8a",
+ });
+ const target = query.toTarget();
+
+ try std.testing.expect(target.os.tag == .linux);
+ try std.testing.expect(target.abi == .musleabihf);
+ try std.testing.expect(target.cpu.arch == .arm);
+ try std.testing.expect(target.cpu.model == &Target.arm.cpu.generic);
+ try std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a));
+
+ const text = try query.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ try std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text);
+ }
+ {
+ const query = try Query.parse(.{
+ .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
+ .cpu_features = "generic+v8a",
+ });
+ const target = query.toTarget();
+
+ try std.testing.expect(target.cpu.arch == .aarch64);
+ try std.testing.expect(target.os.tag == .linux);
+ try std.testing.expect(target.os.version_range.linux.range.min.major == 3);
+ try std.testing.expect(target.os.version_range.linux.range.min.minor == 10);
+ try std.testing.expect(target.os.version_range.linux.range.min.patch == 0);
+ try std.testing.expect(target.os.version_range.linux.range.max.major == 4);
+ try std.testing.expect(target.os.version_range.linux.range.max.minor == 4);
+ try std.testing.expect(target.os.version_range.linux.range.max.patch == 1);
+ try std.testing.expect(target.os.version_range.linux.glibc.major == 2);
+ try std.testing.expect(target.os.version_range.linux.glibc.minor == 27);
+ try std.testing.expect(target.os.version_range.linux.glibc.patch == 0);
+ try std.testing.expect(target.abi == .gnu);
+
+ const text = try query.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ try std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-gnu.2.27", text);
+ }
+}