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