Commit 3e2f8233a8

Jakub Konka <kubkon@jakubkonka.com>
2021-11-10 16:40:52
Make Rosetta a new variant in ExternalExecutor enum
When running, check if Rosetta is available, otherwise, pass the tests.
1 parent 77c5208
Changed files (4)
lib/std/zig/cross_target.zig
@@ -596,6 +596,7 @@ pub const CrossTarget = struct {
 
     pub const Executor = union(enum) {
         native,
+        rosetta,
         qemu: []const u8,
         wine: []const u8,
         wasmtime: []const u8,
@@ -626,10 +627,13 @@ pub const CrossTarget = struct {
                 return .native;
             }
         }
-        // If the OS match and OS is macOS and CPU is arm64, treat always as native
-        // since we'll be running the foreign architecture tests using Rosetta2.
+        // If the OS match and OS is macOS and CPU is arm64, we can use Rosetta 2
+        // to emulate the foreign architecture.
         if (os_match and os_tag == .macos and builtin.cpu.arch == .aarch64) {
-            return .native;
+            return switch (cpu_arch) {
+                .x86_64 => .rosetta,
+                else => .unavailable,
+            };
         }
 
         // If the OS matches, we can use QEMU to emulate a foreign architecture.
lib/std/zig/CrossTarget.zig
@@ -643,10 +643,13 @@ pub fn getExternalExecutor(self: CrossTarget) Executor {
             return .native;
         }
     }
-    // If the OS match and OS is macOS and CPU is arm64, treat always as native
-    // since we'll be running the foreign architecture tests using Rosetta2.
+    // If the OS match and OS is macOS and CPU is arm64, we can use Rosetta 2
+    // to emulate the foreign architecture.
     if (os_match and os_tag == .macos and builtin.cpu.arch == .aarch64) {
-        return .native;
+        return switch (cpu_arch) {
+            .x86_64 => .rosetta,
+            else => .unavailable,
+        };
     }
 
     // If the OS matches, we can use QEMU to emulate a foreign architecture.
lib/std/build.zig
@@ -2529,7 +2529,7 @@ pub const LibExeObjStep = struct {
                 }
             }
         } else switch (self.target.getExternalExecutor()) {
-            .native, .unavailable => {},
+            .native, .rosetta, .unavailable => {},
             .qemu => |bin_name| if (self.enable_qemu) qemu: {
                 const need_cross_glibc = self.target.isGnuLibC() and self.is_linking_libc;
                 const glibc_dir_arg = if (need_cross_glibc)
src/test.zig
@@ -1132,6 +1132,29 @@ pub const TestContext = struct {
                             .native => try argv.append(exe_path),
                             .unavailable => return, // Pass test.
 
+                            .rosetta => if (builtin.os.tag == .macos) {
+                                // Check based on official Apple docs.
+                                // If sysctlbyname returns errno.ENOENT, then we are running a native process.
+                                // Otherwise, if an error occurs then we are not native and there is no Rosetta available.
+                                // Finally if OK, we are running a translated process via Rosetta.
+                                // https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment
+                                var ret: c_int = 0;
+                                var size: usize = @sizeOf(c_int);
+                                std.os.sysctlbynameZ(
+                                    "sysctl.proc_translated",
+                                    &ret,
+                                    &size,
+                                    null,
+                                    0,
+                                ) catch |err| switch (err) {
+                                    error.UnknownName => unreachable, // Native process, we should never trigger it as .rosetta.
+                                    else => return, // No Rosetta available, pass test.
+                                };
+                                try argv.append(exe_path);
+                            } else {
+                                return; // Rosetta not available, pass test.
+                            },
+
                             .qemu => |qemu_bin_name| if (enable_qemu) {
                                 // TODO Ability for test cases to specify whether to link libc.
                                 const need_cross_glibc = false; // target.isGnuLibC() and self.is_linking_libc;