authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-11-09 10:31:51+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-02 00:22:05+01:00
log77c5208c77f8e91786192dfc4e5945d40d67312a
treef3f6eae1eb4cc52614b7f3755bf6c764c16ff1ee
parent0714832c21169493a0a857237fbface612e3d5e0

Treat x86_64 tests as native under the Rosetta 2 on M1 Macs


2 files changed, 905 insertions(+), 0 deletions(-)

lib/std/zig/CrossTarget.zig+6
...@@ -612,6 +612,7 @@ pub fn vcpkgTriplet(self: CrossTarget, allocator: mem.Allocator, linkage: VcpkgL...@@ -612,6 +612,7 @@ pub fn vcpkgTriplet(self: CrossTarget, allocator: mem.Allocator, linkage: VcpkgL
612612
613pub const Executor = union(enum) {613pub const Executor = union(enum) {
614 native,614 native,
615 rosetta,
615 qemu: []const u8,616 qemu: []const u8,
616 wine: []const u8,617 wine: []const u8,
617 wasmtime: []const u8,618 wasmtime: []const u8,
...@@ -642,6 +643,11 @@ pub fn getExternalExecutor(self: CrossTarget) Executor {...@@ -642,6 +643,11 @@ pub fn getExternalExecutor(self: CrossTarget) Executor {
642 return .native;643 return .native;
643 }644 }
644 }645 }
646 // If the OS match and OS is macOS and CPU is arm64, treat always as native
647 // since we'll be running the foreign architecture tests using Rosetta2.
648 if (os_match and os_tag == .macos and builtin.cpu.arch == .aarch64) {
649 return .native;
650 }
645651
646 // If the OS matches, we can use QEMU to emulate a foreign architecture.652 // If the OS matches, we can use QEMU to emulate a foreign architecture.
647 if (os_match) {653 if (os_match) {
lib/std/zig/cross_target.zig created+899
...@@ -0,0 +1,899 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const Target = std.Target;
5const mem = std.mem;
6
7/// Contains all the same data as `Target`, additionally introducing the concept of "the native target".
8/// The purpose of this abstraction is to provide meaningful and unsurprising defaults.
9/// This struct does reference any resources and it is copyable.
10pub const CrossTarget = struct {
11 /// `null` means native.
12 cpu_arch: ?Target.Cpu.Arch = null,
13
14 cpu_model: CpuModel = CpuModel.determined_by_cpu_arch,
15
16 /// Sparse set of CPU features to add to the set from `cpu_model`.
17 cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
18
19 /// Sparse set of CPU features to remove from the set from `cpu_model`.
20 cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
21
22 /// `null` means native.
23 os_tag: ?Target.Os.Tag = null,
24
25 /// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native)
26 /// then `null` for this field means native.
27 os_version_min: ?OsVersion = null,
28
29 /// When cross compiling, `null` means default (latest known OS version).
30 /// When `os_tag` is native, `null` means equal to the native OS version.
31 os_version_max: ?OsVersion = null,
32
33 /// `null` means default when cross compiling, or native when os_tag is native.
34 /// If `isGnuLibC()` is `false`, this must be `null` and is ignored.
35 glibc_version: ?SemVer = null,
36
37 /// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI.
38 abi: ?Target.Abi = null,
39
40 /// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path
41 /// based on the `os_tag`.
42 dynamic_linker: DynamicLinker = DynamicLinker{},
43
44 pub const CpuModel = union(enum) {
45 /// Always native
46 native,
47
48 /// Always baseline
49 baseline,
50
51 /// If CPU Architecture is native, then the CPU model will be native. Otherwise,
52 /// it will be baseline.
53 determined_by_cpu_arch,
54
55 explicit: *const Target.Cpu.Model,
56 };
57
58 pub const OsVersion = union(enum) {
59 none: void,
60 semver: SemVer,
61 windows: Target.Os.WindowsVersion,
62 };
63
64 pub const SemVer = std.builtin.Version;
65
66 pub const DynamicLinker = Target.DynamicLinker;
67
68 pub fn fromTarget(target: Target) CrossTarget {
69 var result: CrossTarget = .{
70 .cpu_arch = target.cpu.arch,
71 .cpu_model = .{ .explicit = target.cpu.model },
72 .os_tag = target.os.tag,
73 .os_version_min = undefined,
74 .os_version_max = undefined,
75 .abi = target.abi,
76 .glibc_version = if (target.isGnuLibC())
77 target.os.version_range.linux.glibc
78 else
79 null,
80 };
81 result.updateOsVersionRange(target.os);
82
83 const all_features = target.cpu.arch.allFeaturesList();
84 var cpu_model_set = target.cpu.model.features;
85 cpu_model_set.populateDependencies(all_features);
86 {
87 // The "add" set is the full set with the CPU Model set removed.
88 const add_set = &result.cpu_features_add;
89 add_set.* = target.cpu.features;
90 add_set.removeFeatureSet(cpu_model_set);
91 }
92 {
93 // The "sub" set is the features that are on in CPU Model set and off in the full set.
94 const sub_set = &result.cpu_features_sub;
95 sub_set.* = cpu_model_set;
96 sub_set.removeFeatureSet(target.cpu.features);
97 }
98 return result;
99 }
100
101 fn updateOsVersionRange(self: *CrossTarget, os: Target.Os) void {
102 switch (os.tag) {
103 .freestanding,
104 .ananas,
105 .cloudabi,
106 .fuchsia,
107 .kfreebsd,
108 .lv2,
109 .solaris,
110 .zos,
111 .haiku,
112 .minix,
113 .rtems,
114 .nacl,
115 .aix,
116 .cuda,
117 .nvcl,
118 .amdhsa,
119 .ps4,
120 .elfiamcu,
121 .mesa3d,
122 .contiki,
123 .amdpal,
124 .hermit,
125 .hurd,
126 .wasi,
127 .emscripten,
128 .uefi,
129 .opencl,
130 .glsl450,
131 .vulkan,
132 .plan9,
133 .other,
134 => {
135 self.os_version_min = .{ .none = {} };
136 self.os_version_max = .{ .none = {} };
137 },
138
139 .freebsd,
140 .macos,
141 .ios,
142 .tvos,
143 .watchos,
144 .netbsd,
145 .openbsd,
146 .dragonfly,
147 => {
148 self.os_version_min = .{ .semver = os.version_range.semver.min };
149 self.os_version_max = .{ .semver = os.version_range.semver.max };
150 },
151
152 .linux => {
153 self.os_version_min = .{ .semver = os.version_range.linux.range.min };
154 self.os_version_max = .{ .semver = os.version_range.linux.range.max };
155 },
156
157 .windows => {
158 self.os_version_min = .{ .windows = os.version_range.windows.min };
159 self.os_version_max = .{ .windows = os.version_range.windows.max };
160 },
161 }
162 }
163
164 /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
165 pub fn toTarget(self: CrossTarget) Target {
166 return .{
167 .cpu = self.getCpu(),
168 .os = self.getOs(),
169 .abi = self.getAbi(),
170 };
171 }
172
173 pub const ParseOptions = struct {
174 /// This is sometimes called a "triple". It looks roughly like this:
175 /// riscv64-linux-musl
176 /// The fields are, respectively:
177 /// * CPU Architecture
178 /// * Operating System (and optional version range)
179 /// * C ABI (optional, with optional glibc version)
180 /// The string "native" can be used for CPU architecture as well as Operating System.
181 /// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted.
182 arch_os_abi: []const u8 = "native",
183
184 /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e"
185 /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features
186 /// to remove from the set.
187 /// The following special strings are recognized for CPU Model name:
188 /// * "baseline" - The "default" set of CPU features for cross-compiling. A conservative set
189 /// of features that is expected to be supported on most available hardware.
190 /// * "native" - The native CPU model is to be detected when compiling.
191 /// If this field is not provided (`null`), then the value will depend on the
192 /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".
193 cpu_features: ?[]const u8 = null,
194
195 /// Absolute path to dynamic linker, to override the default, which is either a natively
196 /// detected path, or a standard path.
197 dynamic_linker: ?[]const u8 = null,
198
199 /// If this is provided, the function will populate some information about parsing failures,
200 /// so that user-friendly error messages can be delivered.
201 diagnostics: ?*Diagnostics = null,
202
203 pub const Diagnostics = struct {
204 /// If the architecture was determined, this will be populated.
205 arch: ?Target.Cpu.Arch = null,
206
207 /// If the OS name was determined, this will be populated.
208 os_name: ?[]const u8 = null,
209
210 /// If the OS tag was determined, this will be populated.
211 os_tag: ?Target.Os.Tag = null,
212
213 /// If the ABI was determined, this will be populated.
214 abi: ?Target.Abi = null,
215
216 /// If the CPU name was determined, this will be populated.
217 cpu_name: ?[]const u8 = null,
218
219 /// If error.UnknownCpuFeature is returned, this will be populated.
220 unknown_feature_name: ?[]const u8 = null,
221 };
222 };
223
224 pub fn parse(args: ParseOptions) !CrossTarget {
225 var dummy_diags: ParseOptions.Diagnostics = undefined;
226 const diags = args.diagnostics orelse &dummy_diags;
227
228 var result: CrossTarget = .{
229 .dynamic_linker = DynamicLinker.init(args.dynamic_linker),
230 };
231
232 var it = mem.split(u8, args.arch_os_abi, "-");
233 const arch_name = it.next().?;
234 const arch_is_native = mem.eql(u8, arch_name, "native");
235 if (!arch_is_native) {
236 result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse
237 return error.UnknownArchitecture;
238 }
239 const arch = result.getCpuArch();
240 diags.arch = arch;
241
242 if (it.next()) |os_text| {
243 try parseOs(&result, diags, os_text);
244 } else if (!arch_is_native) {
245 return error.MissingOperatingSystem;
246 }
247
248 const opt_abi_text = it.next();
249 if (opt_abi_text) |abi_text| {
250 var abi_it = mem.split(u8, abi_text, ".");
251 const abi = std.meta.stringToEnum(Target.Abi, abi_it.next().?) orelse
252 return error.UnknownApplicationBinaryInterface;
253 result.abi = abi;
254 diags.abi = abi;
255
256 const abi_ver_text = abi_it.rest();
257 if (abi_it.next() != null) {
258 if (result.isGnuLibC()) {
259 result.glibc_version = SemVer.parse(abi_ver_text) catch |err| switch (err) {
260 error.Overflow => return error.InvalidAbiVersion,
261 error.InvalidCharacter => return error.InvalidAbiVersion,
262 error.InvalidVersion => return error.InvalidAbiVersion,
263 };
264 } else {
265 return error.InvalidAbiVersion;
266 }
267 }
268 }
269
270 if (it.next() != null) return error.UnexpectedExtraField;
271
272 if (args.cpu_features) |cpu_features| {
273 const all_features = arch.allFeaturesList();
274 var index: usize = 0;
275 while (index < cpu_features.len and
276 cpu_features[index] != '+' and
277 cpu_features[index] != '-')
278 {
279 index += 1;
280 }
281 const cpu_name = cpu_features[0..index];
282 diags.cpu_name = cpu_name;
283
284 const add_set = &result.cpu_features_add;
285 const sub_set = &result.cpu_features_sub;
286 if (mem.eql(u8, cpu_name, "native")) {
287 result.cpu_model = .native;
288 } else if (mem.eql(u8, cpu_name, "baseline")) {
289 result.cpu_model = .baseline;
290 } else {
291 result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) };
292 }
293
294 while (index < cpu_features.len) {
295 const op = cpu_features[index];
296 const set = switch (op) {
297 '+' => add_set,
298 '-' => sub_set,
299 else => unreachable,
300 };
301 index += 1;
302 const start = index;
303 while (index < cpu_features.len and
304 cpu_features[index] != '+' and
305 cpu_features[index] != '-')
306 {
307 index += 1;
308 }
309 const feature_name = cpu_features[start..index];
310 for (all_features) |feature, feat_index_usize| {
311 const feat_index = @intCast(Target.Cpu.Feature.Set.Index, feat_index_usize);
312 if (mem.eql(u8, feature_name, feature.name)) {
313 set.addFeature(feat_index);
314 break;
315 }
316 } else {
317 diags.unknown_feature_name = feature_name;
318 return error.UnknownCpuFeature;
319 }
320 }
321 }
322
323 return result;
324 }
325
326 /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
327 pub fn getCpu(self: CrossTarget) Target.Cpu {
328 switch (self.cpu_model) {
329 .native => {
330 // This works when doing `zig build` because Zig generates a build executable using
331 // native CPU model & features. However this will not be accurate otherwise, and
332 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
333 return builtin.cpu;
334 },
335 .baseline => {
336 var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
337 self.updateCpuFeatures(&adjusted_baseline.features);
338 return adjusted_baseline;
339 },
340 .determined_by_cpu_arch => if (self.cpu_arch == null) {
341 // This works when doing `zig build` because Zig generates a build executable using
342 // native CPU model & features. However this will not be accurate otherwise, and
343 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
344 return builtin.cpu;
345 } else {
346 var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
347 self.updateCpuFeatures(&adjusted_baseline.features);
348 return adjusted_baseline;
349 },
350 .explicit => |model| {
351 var adjusted_model = model.toCpu(self.getCpuArch());
352 self.updateCpuFeatures(&adjusted_model.features);
353 return adjusted_model;
354 },
355 }
356 }
357
358 pub fn getCpuArch(self: CrossTarget) Target.Cpu.Arch {
359 return self.cpu_arch orelse builtin.cpu.arch;
360 }
361
362 pub fn getCpuModel(self: CrossTarget) *const Target.Cpu.Model {
363 return switch (self.cpu_model) {
364 .explicit => |cpu_model| cpu_model,
365 else => self.getCpu().model,
366 };
367 }
368
369 pub fn getCpuFeatures(self: CrossTarget) Target.Cpu.Feature.Set {
370 return self.getCpu().features;
371 }
372
373 /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
374 pub fn getOs(self: CrossTarget) Target.Os {
375 // `builtin.os` works when doing `zig build` because Zig generates a build executable using
376 // native OS version range. However this will not be accurate otherwise, and
377 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
378 var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange() else builtin.os;
379
380 if (self.os_version_min) |min| switch (min) {
381 .none => {},
382 .semver => |semver| switch (self.getOsTag()) {
383 .linux => adjusted_os.version_range.linux.range.min = semver,
384 else => adjusted_os.version_range.semver.min = semver,
385 },
386 .windows => |win_ver| adjusted_os.version_range.windows.min = win_ver,
387 };
388
389 if (self.os_version_max) |max| switch (max) {
390 .none => {},
391 .semver => |semver| switch (self.getOsTag()) {
392 .linux => adjusted_os.version_range.linux.range.max = semver,
393 else => adjusted_os.version_range.semver.max = semver,
394 },
395 .windows => |win_ver| adjusted_os.version_range.windows.max = win_ver,
396 };
397
398 if (self.glibc_version) |glibc| {
399 assert(self.isGnuLibC());
400 adjusted_os.version_range.linux.glibc = glibc;
401 }
402
403 return adjusted_os;
404 }
405
406 pub fn getOsTag(self: CrossTarget) Target.Os.Tag {
407 return self.os_tag orelse builtin.os.tag;
408 }
409
410 /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
411 pub fn getOsVersionMin(self: CrossTarget) OsVersion {
412 if (self.os_version_min) |version_min| return version_min;
413 var tmp: CrossTarget = undefined;
414 tmp.updateOsVersionRange(self.getOs());
415 return tmp.os_version_min.?;
416 }
417
418 /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
419 pub fn getOsVersionMax(self: CrossTarget) OsVersion {
420 if (self.os_version_max) |version_max| return version_max;
421 var tmp: CrossTarget = undefined;
422 tmp.updateOsVersionRange(self.getOs());
423 return tmp.os_version_max.?;
424 }
425
426 /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
427 pub fn getAbi(self: CrossTarget) Target.Abi {
428 if (self.abi) |abi| return abi;
429
430 if (self.os_tag == null) {
431 // This works when doing `zig build` because Zig generates a build executable using
432 // native CPU model & features. However this will not be accurate otherwise, and
433 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
434 return builtin.abi;
435 }
436
437 return Target.Abi.default(self.getCpuArch(), self.getOs());
438 }
439
440 pub fn isFreeBSD(self: CrossTarget) bool {
441 return self.getOsTag() == .freebsd;
442 }
443
444 pub fn isDarwin(self: CrossTarget) bool {
445 return self.getOsTag().isDarwin();
446 }
447
448 pub fn isNetBSD(self: CrossTarget) bool {
449 return self.getOsTag() == .netbsd;
450 }
451
452 pub fn isOpenBSD(self: CrossTarget) bool {
453 return self.getOsTag() == .openbsd;
454 }
455
456 pub fn isUefi(self: CrossTarget) bool {
457 return self.getOsTag() == .uefi;
458 }
459
460 pub fn isDragonFlyBSD(self: CrossTarget) bool {
461 return self.getOsTag() == .dragonfly;
462 }
463
464 pub fn isLinux(self: CrossTarget) bool {
465 return self.getOsTag() == .linux;
466 }
467
468 pub fn isWindows(self: CrossTarget) bool {
469 return self.getOsTag() == .windows;
470 }
471
472 pub fn exeFileExt(self: CrossTarget) [:0]const u8 {
473 return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag());
474 }
475
476 pub fn staticLibSuffix(self: CrossTarget) [:0]const u8 {
477 return Target.staticLibSuffix_os_abi(self.getOsTag(), self.getAbi());
478 }
479
480 pub fn dynamicLibSuffix(self: CrossTarget) [:0]const u8 {
481 return self.getOsTag().dynamicLibSuffix();
482 }
483
484 pub fn libPrefix(self: CrossTarget) [:0]const u8 {
485 return Target.libPrefix_os_abi(self.getOsTag(), self.getAbi());
486 }
487
488 pub fn isNativeCpu(self: CrossTarget) bool {
489 return self.cpu_arch == null and
490 (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and
491 self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty();
492 }
493
494 pub fn isNativeOs(self: CrossTarget) bool {
495 return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and
496 self.dynamic_linker.get() == null and self.glibc_version == null;
497 }
498
499 pub fn isNativeAbi(self: CrossTarget) bool {
500 return self.os_tag == null and self.abi == null;
501 }
502
503 pub fn isNative(self: CrossTarget) bool {
504 return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi();
505 }
506
507 pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![]u8 {
508 if (self.isNative()) {
509 return allocator.dupe(u8, "native");
510 }
511
512 const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";
513 const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native";
514
515 var result = std.ArrayList(u8).init(allocator);
516 defer result.deinit();
517
518 try result.writer().print("{s}-{s}", .{ arch_name, os_name });
519
520 // The zig target syntax does not allow specifying a max os version with no min, so
521 // if either are present, we need the min.
522 if (self.os_version_min != null or self.os_version_max != null) {
523 switch (self.getOsVersionMin()) {
524 .none => {},
525 .semver => |v| try result.writer().print(".{}", .{v}),
526 .windows => |v| try result.writer().print("{s}", .{v}),
527 }
528 }
529 if (self.os_version_max) |max| {
530 switch (max) {
531 .none => {},
532 .semver => |v| try result.writer().print("...{}", .{v}),
533 .windows => |v| try result.writer().print("..{s}", .{v}),
534 }
535 }
536
537 if (self.glibc_version) |v| {
538 try result.writer().print("-{s}.{}", .{ @tagName(self.getAbi()), v });
539 } else if (self.abi) |abi| {
540 try result.writer().print("-{s}", .{@tagName(abi)});
541 }
542
543 return result.toOwnedSlice();
544 }
545
546 pub fn allocDescription(self: CrossTarget, allocator: *mem.Allocator) ![]u8 {
547 // TODO is there anything else worthy of the description that is not
548 // already captured in the triple?
549 return self.zigTriple(allocator);
550 }
551
552 pub fn linuxTriple(self: CrossTarget, allocator: *mem.Allocator) ![]u8 {
553 return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi());
554 }
555
556 pub fn wantSharedLibSymLinks(self: CrossTarget) bool {
557 return self.getOsTag() != .windows;
558 }
559
560 pub const VcpkgLinkage = std.builtin.LinkMode;
561
562 /// Returned slice must be freed by the caller.
563 pub fn vcpkgTriplet(self: CrossTarget, allocator: *mem.Allocator, linkage: VcpkgLinkage) ![]u8 {
564 const arch = switch (self.getCpuArch()) {
565 .i386 => "x86",
566 .x86_64 => "x64",
567
568 .arm,
569 .armeb,
570 .thumb,
571 .thumbeb,
572 .aarch64_32,
573 => "arm",
574
575 .aarch64,
576 .aarch64_be,
577 => "arm64",
578
579 else => return error.UnsupportedVcpkgArchitecture,
580 };
581
582 const os = switch (self.getOsTag()) {
583 .windows => "windows",
584 .linux => "linux",
585 .macos => "macos",
586 else => return error.UnsupportedVcpkgOperatingSystem,
587 };
588
589 const static_suffix = switch (linkage) {
590 .Static => "-static",
591 .Dynamic => "",
592 };
593
594 return std.fmt.allocPrint(allocator, "{s}-{s}{s}", .{ arch, os, static_suffix });
595 }
596
597 pub const Executor = union(enum) {
598 native,
599 qemu: []const u8,
600 wine: []const u8,
601 wasmtime: []const u8,
602 darling: []const u8,
603 unavailable,
604 };
605
606 /// Note that even a `CrossTarget` which returns `false` for `isNative` could still be natively executed.
607 /// For example `-target arm-native` running on an aarch64 host.
608 pub fn getExternalExecutor(self: CrossTarget) Executor {
609 const cpu_arch = self.getCpuArch();
610 const os_tag = self.getOsTag();
611 const os_match = os_tag == builtin.os.tag;
612
613 // If the OS and CPU arch match, the binary can be considered native.
614 // TODO additionally match the CPU features. This `getExternalExecutor` function should
615 // be moved to std.Target and match any chosen target against the native target.
616 if (os_match and cpu_arch == builtin.cpu.arch) {
617 // However, we also need to verify that the dynamic linker path is valid.
618 if (self.os_tag == null) {
619 return .native;
620 }
621 // TODO here we call toTarget, a deprecated function, because of the above TODO about moving
622 // this code to std.Target.
623 const opt_dl = self.dynamic_linker.get() orelse self.toTarget().standardDynamicLinkerPath().get();
624 if (opt_dl) |dl| blk: {
625 std.fs.cwd().access(dl, .{}) catch break :blk;
626 return .native;
627 }
628 }
629 // If the OS match and OS is macOS and CPU is arm64, treat always as native
630 // since we'll be running the foreign architecture tests using Rosetta2.
631 if (os_match and os_tag == .macos and builtin.cpu.arch == .aarch64) {
632 return .native;
633 }
634
635 // If the OS matches, we can use QEMU to emulate a foreign architecture.
636 if (os_match) {
637 return switch (cpu_arch) {
638 .aarch64 => Executor{ .qemu = "qemu-aarch64" },
639 .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" },
640 .arm => Executor{ .qemu = "qemu-arm" },
641 .armeb => Executor{ .qemu = "qemu-armeb" },
642 .i386 => Executor{ .qemu = "qemu-i386" },
643 .mips => Executor{ .qemu = "qemu-mips" },
644 .mipsel => Executor{ .qemu = "qemu-mipsel" },
645 .mips64 => Executor{ .qemu = "qemu-mips64" },
646 .mips64el => Executor{ .qemu = "qemu-mips64el" },
647 .powerpc => Executor{ .qemu = "qemu-ppc" },
648 .powerpc64 => Executor{ .qemu = "qemu-ppc64" },
649 .powerpc64le => Executor{ .qemu = "qemu-ppc64le" },
650 .riscv32 => Executor{ .qemu = "qemu-riscv32" },
651 .riscv64 => Executor{ .qemu = "qemu-riscv64" },
652 .s390x => Executor{ .qemu = "qemu-s390x" },
653 .sparc => Executor{ .qemu = "qemu-sparc" },
654 .x86_64 => Executor{ .qemu = "qemu-x86_64" },
655 else => return .unavailable,
656 };
657 }
658
659 switch (os_tag) {
660 .windows => switch (cpu_arch.ptrBitWidth()) {
661 32 => return Executor{ .wine = "wine" },
662 64 => return Executor{ .wine = "wine64" },
663 else => return .unavailable,
664 },
665 .wasi => switch (cpu_arch.ptrBitWidth()) {
666 32 => return Executor{ .wasmtime = "wasmtime" },
667 else => return .unavailable,
668 },
669 .macos => {
670 // TODO loosen this check once upstream adds QEMU-based emulation
671 // layer for non-host architectures:
672 // https://github.com/darlinghq/darling/issues/863
673 if (cpu_arch != builtin.cpu.arch) {
674 return .unavailable;
675 }
676 return Executor{ .darling = "darling" };
677 },
678 else => return .unavailable,
679 }
680 }
681
682 pub fn isGnuLibC(self: CrossTarget) bool {
683 return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi());
684 }
685
686 pub fn setGnuLibCVersion(self: *CrossTarget, major: u32, minor: u32, patch: u32) void {
687 assert(self.isGnuLibC());
688 self.glibc_version = SemVer{ .major = major, .minor = minor, .patch = patch };
689 }
690
691 pub fn getObjectFormat(self: CrossTarget) Target.ObjectFormat {
692 return Target.getObjectFormatSimple(self.getOsTag(), self.getCpuArch());
693 }
694
695 pub fn updateCpuFeatures(self: CrossTarget, set: *Target.Cpu.Feature.Set) void {
696 set.removeFeatureSet(self.cpu_features_sub);
697 set.addFeatureSet(self.cpu_features_add);
698 set.populateDependencies(self.getCpuArch().allFeaturesList());
699 set.removeFeatureSet(self.cpu_features_sub);
700 }
701
702 fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const u8) !void {
703 var it = mem.split(u8, text, ".");
704 const os_name = it.next().?;
705 diags.os_name = os_name;
706 const os_is_native = mem.eql(u8, os_name, "native");
707 if (!os_is_native) {
708 result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse
709 return error.UnknownOperatingSystem;
710 }
711 const tag = result.getOsTag();
712 diags.os_tag = tag;
713
714 const version_text = it.rest();
715 if (it.next() == null) return;
716
717 switch (tag) {
718 .freestanding,
719 .ananas,
720 .cloudabi,
721 .fuchsia,
722 .kfreebsd,
723 .lv2,
724 .solaris,
725 .zos,
726 .haiku,
727 .minix,
728 .rtems,
729 .nacl,
730 .aix,
731 .cuda,
732 .nvcl,
733 .amdhsa,
734 .ps4,
735 .elfiamcu,
736 .mesa3d,
737 .contiki,
738 .amdpal,
739 .hermit,
740 .hurd,
741 .wasi,
742 .emscripten,
743 .uefi,
744 .opencl,
745 .glsl450,
746 .vulkan,
747 .plan9,
748 .other,
749 => return error.InvalidOperatingSystemVersion,
750
751 .freebsd,
752 .macos,
753 .ios,
754 .tvos,
755 .watchos,
756 .netbsd,
757 .openbsd,
758 .linux,
759 .dragonfly,
760 => {
761 var range_it = mem.split(u8, version_text, "...");
762
763 const min_text = range_it.next().?;
764 const min_ver = SemVer.parse(min_text) catch |err| switch (err) {
765 error.Overflow => return error.InvalidOperatingSystemVersion,
766 error.InvalidCharacter => return error.InvalidOperatingSystemVersion,
767 error.InvalidVersion => return error.InvalidOperatingSystemVersion,
768 };
769 result.os_version_min = .{ .semver = min_ver };
770
771 const max_text = range_it.next() orelse return;
772 const max_ver = SemVer.parse(max_text) catch |err| switch (err) {
773 error.Overflow => return error.InvalidOperatingSystemVersion,
774 error.InvalidCharacter => return error.InvalidOperatingSystemVersion,
775 error.InvalidVersion => return error.InvalidOperatingSystemVersion,
776 };
777 result.os_version_max = .{ .semver = max_ver };
778 },
779
780 .windows => {
781 var range_it = mem.split(u8, version_text, "...");
782
783 const min_text = range_it.next().?;
784 const min_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, min_text) orelse
785 return error.InvalidOperatingSystemVersion;
786 result.os_version_min = .{ .windows = min_ver };
787
788 const max_text = range_it.next() orelse return;
789 const max_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, max_text) orelse
790 return error.InvalidOperatingSystemVersion;
791 result.os_version_max = .{ .windows = max_ver };
792 },
793 }
794 }
795};
796
797test "CrossTarget.parse" {
798 if (builtin.target.isGnuLibC()) {
799 var cross_target = try CrossTarget.parse(.{});
800 cross_target.setGnuLibCVersion(2, 1, 1);
801
802 const text = try cross_target.zigTriple(std.testing.allocator);
803 defer std.testing.allocator.free(text);
804
805 var buf: [256]u8 = undefined;
806 const triple = std.fmt.bufPrint(
807 buf[0..],
808 "native-native-{s}.2.1.1",
809 .{@tagName(builtin.abi)},
810 ) catch unreachable;
811
812 try std.testing.expectEqualSlices(u8, triple, text);
813 }
814 {
815 const cross_target = try CrossTarget.parse(.{
816 .arch_os_abi = "aarch64-linux",
817 .cpu_features = "native",
818 });
819
820 try std.testing.expect(cross_target.cpu_arch.? == .aarch64);
821 try std.testing.expect(cross_target.cpu_model == .native);
822 }
823 {
824 const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" });
825
826 try std.testing.expect(cross_target.cpu_arch == null);
827 try std.testing.expect(cross_target.isNative());
828
829 const text = try cross_target.zigTriple(std.testing.allocator);
830 defer std.testing.allocator.free(text);
831 try std.testing.expectEqualSlices(u8, "native", text);
832 }
833 {
834 const cross_target = try CrossTarget.parse(.{
835 .arch_os_abi = "x86_64-linux-gnu",
836 .cpu_features = "x86_64-sse-sse2-avx-cx8",
837 });
838 const target = cross_target.toTarget();
839
840 try std.testing.expect(target.os.tag == .linux);
841 try std.testing.expect(target.abi == .gnu);
842 try std.testing.expect(target.cpu.arch == .x86_64);
843 try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse));
844 try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx));
845 try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8));
846 try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov));
847 try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr));
848
849 try std.testing.expect(Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx, .cmov }));
850 try std.testing.expect(!Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx }));
851 try std.testing.expect(Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87 }));
852 try std.testing.expect(!Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87, .sse }));
853
854 const text = try cross_target.zigTriple(std.testing.allocator);
855 defer std.testing.allocator.free(text);
856 try std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text);
857 }
858 {
859 const cross_target = try CrossTarget.parse(.{
860 .arch_os_abi = "arm-linux-musleabihf",
861 .cpu_features = "generic+v8a",
862 });
863 const target = cross_target.toTarget();
864
865 try std.testing.expect(target.os.tag == .linux);
866 try std.testing.expect(target.abi == .musleabihf);
867 try std.testing.expect(target.cpu.arch == .arm);
868 try std.testing.expect(target.cpu.model == &Target.arm.cpu.generic);
869 try std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a));
870
871 const text = try cross_target.zigTriple(std.testing.allocator);
872 defer std.testing.allocator.free(text);
873 try std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text);
874 }
875 {
876 const cross_target = try CrossTarget.parse(.{
877 .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
878 .cpu_features = "generic+v8a",
879 });
880 const target = cross_target.toTarget();
881
882 try std.testing.expect(target.cpu.arch == .aarch64);
883 try std.testing.expect(target.os.tag == .linux);
884 try std.testing.expect(target.os.version_range.linux.range.min.major == 3);
885 try std.testing.expect(target.os.version_range.linux.range.min.minor == 10);
886 try std.testing.expect(target.os.version_range.linux.range.min.patch == 0);
887 try std.testing.expect(target.os.version_range.linux.range.max.major == 4);
888 try std.testing.expect(target.os.version_range.linux.range.max.minor == 4);
889 try std.testing.expect(target.os.version_range.linux.range.max.patch == 1);
890 try std.testing.expect(target.os.version_range.linux.glibc.major == 2);
891 try std.testing.expect(target.os.version_range.linux.glibc.minor == 27);
892 try std.testing.expect(target.os.version_range.linux.glibc.patch == 0);
893 try std.testing.expect(target.abi == .gnu);
894
895 const text = try cross_target.zigTriple(std.testing.allocator);
896 defer std.testing.allocator.free(text);
897 try std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-gnu.2.27", text);
898 }
899}