zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob e22fbcbf (26692B) - Raw


      1 //! Contains all the same data as `Target`, additionally introducing the
      2 //! concept of "the native target". The purpose of this abstraction is to
      3 //! provide meaningful and unsurprising defaults. This struct does reference
      4 //! any resources and it is copyable.
      5 
      6 /// `null` means native.
      7 cpu_arch: ?Target.Cpu.Arch = null,
      8 
      9 cpu_model: CpuModel = CpuModel.determined_by_cpu_arch,
     10 
     11 /// Sparse set of CPU features to add to the set from `cpu_model`.
     12 cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
     13 
     14 /// Sparse set of CPU features to remove from the set from `cpu_model`.
     15 cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
     16 
     17 /// `null` means native.
     18 os_tag: ?Target.Os.Tag = null,
     19 
     20 /// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native)
     21 /// then `null` for this field means native.
     22 os_version_min: ?OsVersion = null,
     23 
     24 /// When cross compiling, `null` means default (latest known OS version).
     25 /// When `os_tag` is native, `null` means equal to the native OS version.
     26 os_version_max: ?OsVersion = null,
     27 
     28 /// `null` means default when cross compiling, or native when os_tag is native.
     29 /// If `isGnuLibC()` is `false`, this must be `null` and is ignored.
     30 glibc_version: ?SemanticVersion = null,
     31 
     32 /// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI.
     33 abi: ?Target.Abi = null,
     34 
     35 /// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path
     36 /// based on the `os_tag`.
     37 dynamic_linker: Target.DynamicLinker = Target.DynamicLinker.none,
     38 
     39 /// `null` means default for the cpu/arch/os combo.
     40 ofmt: ?Target.ObjectFormat = null,
     41 
     42 pub const CpuModel = union(enum) {
     43     /// Always native
     44     native,
     45 
     46     /// Always baseline
     47     baseline,
     48 
     49     /// If CPU Architecture is native, then the CPU model will be native. Otherwise,
     50     /// it will be baseline.
     51     determined_by_cpu_arch,
     52 
     53     explicit: *const Target.Cpu.Model,
     54 
     55     pub fn eql(a: CpuModel, b: CpuModel) bool {
     56         const Tag = @typeInfo(CpuModel).@"union".tag_type.?;
     57         const a_tag: Tag = a;
     58         const b_tag: Tag = b;
     59         if (a_tag != b_tag) return false;
     60         return switch (a) {
     61             .native, .baseline, .determined_by_cpu_arch => true,
     62             .explicit => |a_model| a_model == b.explicit,
     63         };
     64     }
     65 };
     66 
     67 pub const OsVersion = union(enum) {
     68     none: void,
     69     semver: SemanticVersion,
     70     windows: Target.Os.WindowsVersion,
     71 
     72     pub fn eql(a: OsVersion, b: OsVersion) bool {
     73         const Tag = @typeInfo(OsVersion).@"union".tag_type.?;
     74         const a_tag: Tag = a;
     75         const b_tag: Tag = b;
     76         if (a_tag != b_tag) return false;
     77         return switch (a) {
     78             .none => true,
     79             .semver => |a_semver| a_semver.order(b.semver) == .eq,
     80             .windows => |a_windows| a_windows == b.windows,
     81         };
     82     }
     83 
     84     pub fn eqlOpt(a: ?OsVersion, b: ?OsVersion) bool {
     85         if (a == null and b == null) return true;
     86         if (a == null or b == null) return false;
     87         return OsVersion.eql(a.?, b.?);
     88     }
     89 };
     90 
     91 pub const SemanticVersion = std.SemanticVersion;
     92 
     93 pub fn fromTarget(target: Target) Query {
     94     var result: Query = .{
     95         .cpu_arch = target.cpu.arch,
     96         .cpu_model = .{ .explicit = target.cpu.model },
     97         .os_tag = target.os.tag,
     98         .os_version_min = undefined,
     99         .os_version_max = undefined,
    100         .abi = target.abi,
    101         .glibc_version = if (target.isGnuLibC())
    102             target.os.version_range.linux.glibc
    103         else
    104             null,
    105     };
    106     result.updateOsVersionRange(target.os);
    107 
    108     const all_features = target.cpu.arch.allFeaturesList();
    109     var cpu_model_set = target.cpu.model.features;
    110     cpu_model_set.populateDependencies(all_features);
    111     {
    112         // The "add" set is the full set with the CPU Model set removed.
    113         const add_set = &result.cpu_features_add;
    114         add_set.* = target.cpu.features;
    115         add_set.removeFeatureSet(cpu_model_set);
    116     }
    117     {
    118         // The "sub" set is the features that are on in CPU Model set and off in the full set.
    119         const sub_set = &result.cpu_features_sub;
    120         sub_set.* = cpu_model_set;
    121         sub_set.removeFeatureSet(target.cpu.features);
    122     }
    123     return result;
    124 }
    125 
    126 fn updateOsVersionRange(self: *Query, os: Target.Os) void {
    127     self.os_version_min, self.os_version_max = switch (os.tag.getVersionRangeTag()) {
    128         .none => .{ .{ .none = {} }, .{ .none = {} } },
    129         .semver => .{
    130             .{ .semver = os.version_range.semver.min },
    131             .{ .semver = os.version_range.semver.max },
    132         },
    133         .linux => .{
    134             .{ .semver = os.version_range.linux.range.min },
    135             .{ .semver = os.version_range.linux.range.max },
    136         },
    137         .windows => .{
    138             .{ .windows = os.version_range.windows.min },
    139             .{ .windows = os.version_range.windows.max },
    140         },
    141     };
    142 }
    143 
    144 pub const ParseOptions = struct {
    145     /// This is sometimes called a "triple". It looks roughly like this:
    146     ///     riscv64-linux-musl
    147     /// The fields are, respectively:
    148     /// * CPU Architecture
    149     /// * Operating System (and optional version range)
    150     /// * C ABI (optional, with optional glibc version)
    151     /// The string "native" can be used for CPU architecture as well as Operating System.
    152     /// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted.
    153     arch_os_abi: []const u8 = "native",
    154 
    155     /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e"
    156     /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features
    157     /// to remove from the set.
    158     /// The following special strings are recognized for CPU Model name:
    159     /// * "baseline" - The "default" set of CPU features for cross-compiling. A conservative set
    160     ///                of features that is expected to be supported on most available hardware.
    161     /// * "native"   - The native CPU model is to be detected when compiling.
    162     /// If this field is not provided (`null`), then the value will depend on the
    163     /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".
    164     cpu_features: ?[]const u8 = null,
    165 
    166     /// Absolute path to dynamic linker, to override the default, which is either a natively
    167     /// detected path, or a standard path.
    168     dynamic_linker: ?[]const u8 = null,
    169 
    170     object_format: ?[]const u8 = null,
    171 
    172     /// If this is provided, the function will populate some information about parsing failures,
    173     /// so that user-friendly error messages can be delivered.
    174     diagnostics: ?*Diagnostics = null,
    175 
    176     pub const Diagnostics = struct {
    177         /// If the architecture was determined, this will be populated.
    178         arch: ?Target.Cpu.Arch = null,
    179 
    180         /// If the OS name was determined, this will be populated.
    181         os_name: ?[]const u8 = null,
    182 
    183         /// If the OS tag was determined, this will be populated.
    184         os_tag: ?Target.Os.Tag = null,
    185 
    186         /// If the ABI was determined, this will be populated.
    187         abi: ?Target.Abi = null,
    188 
    189         /// If the CPU name was determined, this will be populated.
    190         cpu_name: ?[]const u8 = null,
    191 
    192         /// If error.UnknownCpuFeature is returned, this will be populated.
    193         unknown_feature_name: ?[]const u8 = null,
    194     };
    195 };
    196 
    197 pub fn parse(args: ParseOptions) !Query {
    198     var dummy_diags: ParseOptions.Diagnostics = undefined;
    199     const diags = args.diagnostics orelse &dummy_diags;
    200 
    201     var result: Query = .{
    202         .dynamic_linker = Target.DynamicLinker.init(args.dynamic_linker),
    203     };
    204 
    205     var it = mem.splitScalar(u8, args.arch_os_abi, '-');
    206     const arch_name = it.first();
    207     const arch_is_native = mem.eql(u8, arch_name, "native");
    208     if (!arch_is_native) {
    209         result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse
    210             return error.UnknownArchitecture;
    211     }
    212     const arch = result.cpu_arch orelse builtin.cpu.arch;
    213     diags.arch = arch;
    214 
    215     if (it.next()) |os_text| {
    216         try parseOs(&result, diags, os_text);
    217     } else if (!arch_is_native) {
    218         return error.MissingOperatingSystem;
    219     }
    220 
    221     const opt_abi_text = it.next();
    222     if (opt_abi_text) |abi_text| {
    223         var abi_it = mem.splitScalar(u8, abi_text, '.');
    224         const abi = std.meta.stringToEnum(Target.Abi, abi_it.first()) orelse
    225             return error.UnknownApplicationBinaryInterface;
    226         result.abi = abi;
    227         diags.abi = abi;
    228 
    229         const abi_ver_text = abi_it.rest();
    230         if (abi_it.next() != null) {
    231             const tag = result.os_tag orelse builtin.os.tag;
    232             if (tag.isGnuLibC(abi)) {
    233                 result.glibc_version = parseVersion(abi_ver_text) catch |err| switch (err) {
    234                     error.Overflow => return error.InvalidAbiVersion,
    235                     error.InvalidVersion => return error.InvalidAbiVersion,
    236                 };
    237             } else {
    238                 return error.InvalidAbiVersion;
    239             }
    240         }
    241     }
    242 
    243     if (it.next() != null) return error.UnexpectedExtraField;
    244 
    245     if (args.cpu_features) |cpu_features| {
    246         const all_features = arch.allFeaturesList();
    247         var index: usize = 0;
    248         while (index < cpu_features.len and
    249             cpu_features[index] != '+' and
    250             cpu_features[index] != '-')
    251         {
    252             index += 1;
    253         }
    254         const cpu_name = cpu_features[0..index];
    255         diags.cpu_name = cpu_name;
    256 
    257         const add_set = &result.cpu_features_add;
    258         const sub_set = &result.cpu_features_sub;
    259         if (mem.eql(u8, cpu_name, "native")) {
    260             result.cpu_model = .native;
    261         } else if (mem.eql(u8, cpu_name, "baseline")) {
    262             result.cpu_model = .baseline;
    263         } else {
    264             result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) };
    265         }
    266 
    267         while (index < cpu_features.len) {
    268             const op = cpu_features[index];
    269             const set = switch (op) {
    270                 '+' => add_set,
    271                 '-' => sub_set,
    272                 else => unreachable,
    273             };
    274             index += 1;
    275             const start = index;
    276             while (index < cpu_features.len and
    277                 cpu_features[index] != '+' and
    278                 cpu_features[index] != '-')
    279             {
    280                 index += 1;
    281             }
    282             const feature_name = cpu_features[start..index];
    283             for (all_features, 0..) |feature, feat_index_usize| {
    284                 const feat_index = @as(Target.Cpu.Feature.Set.Index, @intCast(feat_index_usize));
    285                 if (mem.eql(u8, feature_name, feature.name)) {
    286                     set.addFeature(feat_index);
    287                     break;
    288                 }
    289             } else {
    290                 diags.unknown_feature_name = feature_name;
    291                 return error.UnknownCpuFeature;
    292             }
    293         }
    294     }
    295 
    296     if (args.object_format) |ofmt_name| {
    297         result.ofmt = std.meta.stringToEnum(Target.ObjectFormat, ofmt_name) orelse
    298             return error.UnknownObjectFormat;
    299     }
    300 
    301     return result;
    302 }
    303 
    304 /// Similar to `parse` except instead of fully parsing, it only determines the CPU
    305 /// architecture and returns it if it can be determined, and returns `null` otherwise.
    306 /// This is intended to be used if the API user of Query needs to learn the
    307 /// target CPU architecture in order to fully populate `ParseOptions`.
    308 pub fn parseCpuArch(args: ParseOptions) ?Target.Cpu.Arch {
    309     var it = mem.splitScalar(u8, args.arch_os_abi, '-');
    310     const arch_name = it.first();
    311     const arch_is_native = mem.eql(u8, arch_name, "native");
    312     if (arch_is_native) {
    313         return builtin.cpu.arch;
    314     } else {
    315         return std.meta.stringToEnum(Target.Cpu.Arch, arch_name);
    316     }
    317 }
    318 
    319 /// Similar to `SemanticVersion.parse`, but with following changes:
    320 /// * Leading zeroes are allowed.
    321 /// * Supports only 2 or 3 version components (major, minor, [patch]). If 3-rd component is omitted, it will be 0.
    322 pub fn parseVersion(ver: []const u8) error{ InvalidVersion, Overflow }!SemanticVersion {
    323     const parseVersionComponentFn = (struct {
    324         fn parseVersionComponentInner(component: []const u8) error{ InvalidVersion, Overflow }!usize {
    325             return std.fmt.parseUnsigned(usize, component, 10) catch |err| switch (err) {
    326                 error.InvalidCharacter => return error.InvalidVersion,
    327                 error.Overflow => return error.Overflow,
    328             };
    329         }
    330     }).parseVersionComponentInner;
    331     var version_components = mem.splitScalar(u8, ver, '.');
    332     const major = version_components.first();
    333     const minor = version_components.next() orelse return error.InvalidVersion;
    334     const patch = version_components.next() orelse "0";
    335     if (version_components.next() != null) return error.InvalidVersion;
    336     return .{
    337         .major = try parseVersionComponentFn(major),
    338         .minor = try parseVersionComponentFn(minor),
    339         .patch = try parseVersionComponentFn(patch),
    340     };
    341 }
    342 
    343 test parseVersion {
    344     try std.testing.expectError(error.InvalidVersion, parseVersion("1"));
    345     try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 0 }, try parseVersion("1.2"));
    346     try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 3 }, try parseVersion("1.2.3"));
    347     try std.testing.expectError(error.InvalidVersion, parseVersion("1.2.3.4"));
    348 }
    349 
    350 pub fn isNativeCpu(self: Query) bool {
    351     return self.cpu_arch == null and
    352         (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and
    353         self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty();
    354 }
    355 
    356 pub fn isNativeOs(self: Query) bool {
    357     return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and
    358         self.dynamic_linker.get() == null and self.glibc_version == null;
    359 }
    360 
    361 pub fn isNativeAbi(self: Query) bool {
    362     return self.os_tag == null and self.abi == null;
    363 }
    364 
    365 pub fn isNativeTriple(self: Query) bool {
    366     return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi();
    367 }
    368 
    369 pub fn isNative(self: Query) bool {
    370     return self.isNativeTriple() and self.ofmt == null;
    371 }
    372 
    373 pub fn canDetectLibC(self: Query) bool {
    374     if (self.isNativeOs()) return true;
    375     if (self.os_tag) |os| {
    376         if (builtin.os.tag == .macos and os.isDarwin()) return true;
    377         if (os == .linux and self.abi.isAndroid()) return true;
    378     }
    379     return false;
    380 }
    381 
    382 /// Formats a version with the patch component omitted if it is zero,
    383 /// unlike SemanticVersion.format which formats all its version components regardless.
    384 fn formatVersion(version: SemanticVersion, writer: anytype) !void {
    385     if (version.patch == 0) {
    386         try writer.print("{d}.{d}", .{ version.major, version.minor });
    387     } else {
    388         try writer.print("{d}.{d}.{d}", .{ version.major, version.minor, version.patch });
    389     }
    390 }
    391 
    392 pub fn zigTriple(self: Query, allocator: Allocator) Allocator.Error![]u8 {
    393     if (self.isNativeTriple())
    394         return allocator.dupe(u8, "native");
    395 
    396     const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";
    397     const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native";
    398 
    399     var result = std.ArrayList(u8).init(allocator);
    400     defer result.deinit();
    401 
    402     try result.writer().print("{s}-{s}", .{ arch_name, os_name });
    403 
    404     // The zig target syntax does not allow specifying a max os version with no min, so
    405     // if either are present, we need the min.
    406     if (self.os_version_min) |min| {
    407         switch (min) {
    408             .none => {},
    409             .semver => |v| {
    410                 try result.writer().writeAll(".");
    411                 try formatVersion(v, result.writer());
    412             },
    413             .windows => |v| {
    414                 try result.writer().print("{s}", .{v});
    415             },
    416         }
    417     }
    418     if (self.os_version_max) |max| {
    419         switch (max) {
    420             .none => {},
    421             .semver => |v| {
    422                 try result.writer().writeAll("...");
    423                 try formatVersion(v, result.writer());
    424             },
    425             .windows => |v| {
    426                 // This is counting on a custom format() function defined on `WindowsVersion`
    427                 // to add a prefix '.' and make there be a total of three dots.
    428                 try result.writer().print("..{s}", .{v});
    429             },
    430         }
    431     }
    432 
    433     if (self.glibc_version) |v| {
    434         const name = if (self.abi) |abi| @tagName(abi) else "gnu";
    435         try result.ensureUnusedCapacity(name.len + 2);
    436         result.appendAssumeCapacity('-');
    437         result.appendSliceAssumeCapacity(name);
    438         result.appendAssumeCapacity('.');
    439         try formatVersion(v, result.writer());
    440     } else if (self.abi) |abi| {
    441         const name = @tagName(abi);
    442         try result.ensureUnusedCapacity(name.len + 1);
    443         result.appendAssumeCapacity('-');
    444         result.appendSliceAssumeCapacity(name);
    445     }
    446 
    447     return result.toOwnedSlice();
    448 }
    449 
    450 /// Renders the query into a textual representation that can be parsed via the
    451 /// `-mcpu` flag passed to the Zig compiler.
    452 /// Appends the result to `buffer`.
    453 pub fn serializeCpu(q: Query, buffer: *std.ArrayList(u8)) Allocator.Error!void {
    454     try buffer.ensureUnusedCapacity(8);
    455     switch (q.cpu_model) {
    456         .native => {
    457             buffer.appendSliceAssumeCapacity("native");
    458         },
    459         .baseline => {
    460             buffer.appendSliceAssumeCapacity("baseline");
    461         },
    462         .determined_by_cpu_arch => {
    463             if (q.cpu_arch == null) {
    464                 buffer.appendSliceAssumeCapacity("native");
    465             } else {
    466                 buffer.appendSliceAssumeCapacity("baseline");
    467             }
    468         },
    469         .explicit => |model| {
    470             try buffer.appendSlice(model.name);
    471         },
    472     }
    473 
    474     if (q.cpu_features_add.isEmpty() and q.cpu_features_sub.isEmpty()) {
    475         // The CPU name alone is sufficient.
    476         return;
    477     }
    478 
    479     const cpu_arch = q.cpu_arch orelse builtin.cpu.arch;
    480     const all_features = cpu_arch.allFeaturesList();
    481 
    482     for (all_features, 0..) |feature, i_usize| {
    483         const i: Target.Cpu.Feature.Set.Index = @intCast(i_usize);
    484         try buffer.ensureUnusedCapacity(feature.name.len + 1);
    485         if (q.cpu_features_sub.isEnabled(i)) {
    486             buffer.appendAssumeCapacity('-');
    487             buffer.appendSliceAssumeCapacity(feature.name);
    488         } else if (q.cpu_features_add.isEnabled(i)) {
    489             buffer.appendAssumeCapacity('+');
    490             buffer.appendSliceAssumeCapacity(feature.name);
    491         }
    492     }
    493 }
    494 
    495 pub fn serializeCpuAlloc(q: Query, ally: Allocator) Allocator.Error![]u8 {
    496     var buffer = std.ArrayList(u8).init(ally);
    497     try serializeCpu(q, &buffer);
    498     return buffer.toOwnedSlice();
    499 }
    500 
    501 pub fn allocDescription(self: Query, allocator: Allocator) ![]u8 {
    502     // TODO is there anything else worthy of the description that is not
    503     // already captured in the triple?
    504     return self.zigTriple(allocator);
    505 }
    506 
    507 pub fn setGnuLibCVersion(self: *Query, major: u32, minor: u32, patch: u32) void {
    508     self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch };
    509 }
    510 
    511 fn parseOs(result: *Query, diags: *ParseOptions.Diagnostics, text: []const u8) !void {
    512     var it = mem.splitScalar(u8, text, '.');
    513     const os_name = it.first();
    514     diags.os_name = os_name;
    515     const os_is_native = mem.eql(u8, os_name, "native");
    516     if (!os_is_native) {
    517         result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse
    518             return error.UnknownOperatingSystem;
    519     }
    520     const tag = result.os_tag orelse builtin.os.tag;
    521     diags.os_tag = tag;
    522 
    523     const version_text = it.rest();
    524     if (version_text.len > 0) switch (tag.getVersionRangeTag()) {
    525         .none => return error.InvalidOperatingSystemVersion,
    526         .semver, .linux => range: {
    527             var range_it = mem.splitSequence(u8, version_text, "...");
    528             result.os_version_min = .{
    529                 .semver = parseVersion(range_it.first()) catch |err| switch (err) {
    530                     error.Overflow => return error.InvalidOperatingSystemVersion,
    531                     error.InvalidVersion => return error.InvalidOperatingSystemVersion,
    532                 },
    533             };
    534             result.os_version_max = .{
    535                 .semver = parseVersion(range_it.next() orelse break :range) catch |err| switch (err) {
    536                     error.Overflow => return error.InvalidOperatingSystemVersion,
    537                     error.InvalidVersion => return error.InvalidOperatingSystemVersion,
    538                 },
    539             };
    540         },
    541         .windows => range: {
    542             var range_it = mem.splitSequence(u8, version_text, "...");
    543             result.os_version_min = .{
    544                 .windows = try Target.Os.WindowsVersion.parse(range_it.first()),
    545             };
    546             result.os_version_max = .{
    547                 .windows = try Target.Os.WindowsVersion.parse(range_it.next() orelse break :range),
    548             };
    549         },
    550     };
    551 }
    552 
    553 pub fn eql(a: Query, b: Query) bool {
    554     if (a.cpu_arch != b.cpu_arch) return false;
    555     if (!a.cpu_model.eql(b.cpu_model)) return false;
    556     if (!a.cpu_features_add.eql(b.cpu_features_add)) return false;
    557     if (!a.cpu_features_sub.eql(b.cpu_features_sub)) return false;
    558     if (a.os_tag != b.os_tag) return false;
    559     if (!OsVersion.eqlOpt(a.os_version_min, b.os_version_min)) return false;
    560     if (!OsVersion.eqlOpt(a.os_version_max, b.os_version_max)) return false;
    561     if (!versionEqualOpt(a.glibc_version, b.glibc_version)) return false;
    562     if (a.abi != b.abi) return false;
    563     if (!a.dynamic_linker.eql(b.dynamic_linker)) return false;
    564     if (a.ofmt != b.ofmt) return false;
    565 
    566     return true;
    567 }
    568 
    569 fn versionEqualOpt(a: ?SemanticVersion, b: ?SemanticVersion) bool {
    570     if (a == null and b == null) return true;
    571     if (a == null or b == null) return false;
    572     return SemanticVersion.order(a.?, b.?) == .eq;
    573 }
    574 
    575 const Query = @This();
    576 const std = @import("../std.zig");
    577 const builtin = @import("builtin");
    578 const assert = std.debug.assert;
    579 const Target = std.Target;
    580 const mem = std.mem;
    581 const Allocator = std.mem.Allocator;
    582 
    583 test parse {
    584     if (builtin.target.isGnuLibC()) {
    585         var query = try Query.parse(.{});
    586         query.setGnuLibCVersion(2, 1, 1);
    587 
    588         const text = try query.zigTriple(std.testing.allocator);
    589         defer std.testing.allocator.free(text);
    590 
    591         try std.testing.expectEqualSlices(u8, "native-native-gnu.2.1.1", text);
    592     }
    593     {
    594         const query = try Query.parse(.{
    595             .arch_os_abi = "aarch64-linux",
    596             .cpu_features = "native",
    597         });
    598 
    599         try std.testing.expect(query.cpu_arch.? == .aarch64);
    600         try std.testing.expect(query.cpu_model == .native);
    601     }
    602     {
    603         const query = try Query.parse(.{ .arch_os_abi = "native" });
    604 
    605         try std.testing.expect(query.cpu_arch == null);
    606         try std.testing.expect(query.isNative());
    607 
    608         const text = try query.zigTriple(std.testing.allocator);
    609         defer std.testing.allocator.free(text);
    610         try std.testing.expectEqualSlices(u8, "native", text);
    611     }
    612     {
    613         const query = try Query.parse(.{
    614             .arch_os_abi = "x86_64-linux-gnu",
    615             .cpu_features = "x86_64-sse-sse2-avx-cx8",
    616         });
    617         const target = try std.zig.system.resolveTargetQuery(query);
    618 
    619         try std.testing.expect(target.os.tag == .linux);
    620         try std.testing.expect(target.abi == .gnu);
    621         try std.testing.expect(target.cpu.arch == .x86_64);
    622         try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse));
    623         try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx));
    624         try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8));
    625         try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov));
    626         try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr));
    627 
    628         try std.testing.expect(Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx, .cmov }));
    629         try std.testing.expect(!Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx }));
    630         try std.testing.expect(Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87 }));
    631         try std.testing.expect(!Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87, .sse }));
    632 
    633         const text = try query.zigTriple(std.testing.allocator);
    634         defer std.testing.allocator.free(text);
    635         try std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text);
    636     }
    637     {
    638         const query = try Query.parse(.{
    639             .arch_os_abi = "arm-linux-musleabihf",
    640             .cpu_features = "generic+v8a",
    641         });
    642         const target = try std.zig.system.resolveTargetQuery(query);
    643 
    644         try std.testing.expect(target.os.tag == .linux);
    645         try std.testing.expect(target.abi == .musleabihf);
    646         try std.testing.expect(target.cpu.arch == .arm);
    647         try std.testing.expect(target.cpu.model == &Target.arm.cpu.generic);
    648         try std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a));
    649 
    650         const text = try query.zigTriple(std.testing.allocator);
    651         defer std.testing.allocator.free(text);
    652         try std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text);
    653     }
    654     {
    655         const query = try Query.parse(.{
    656             .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
    657             .cpu_features = "generic+v8a",
    658         });
    659         const target = try std.zig.system.resolveTargetQuery(query);
    660 
    661         try std.testing.expect(target.cpu.arch == .aarch64);
    662         try std.testing.expect(target.os.tag == .linux);
    663         try std.testing.expect(target.os.version_range.linux.range.min.major == 3);
    664         try std.testing.expect(target.os.version_range.linux.range.min.minor == 10);
    665         try std.testing.expect(target.os.version_range.linux.range.min.patch == 0);
    666         try std.testing.expect(target.os.version_range.linux.range.max.major == 4);
    667         try std.testing.expect(target.os.version_range.linux.range.max.minor == 4);
    668         try std.testing.expect(target.os.version_range.linux.range.max.patch == 1);
    669         try std.testing.expect(target.os.version_range.linux.glibc.major == 2);
    670         try std.testing.expect(target.os.version_range.linux.glibc.minor == 27);
    671         try std.testing.expect(target.os.version_range.linux.glibc.patch == 0);
    672         try std.testing.expect(target.abi == .gnu);
    673 
    674         const text = try query.zigTriple(std.testing.allocator);
    675         defer std.testing.allocator.free(text);
    676         try std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-gnu.2.27", text);
    677     }
    678 }