Return whether or not the given host is capable of running executables of the other target.
pub fn getExternalExecutor(io: Io, candidate: *const std.Target, options: GetExternalExecutorOptions) Executor
pub fn getExternalExecutor(io: Io, candidate: *const std.Target, options: GetExternalExecutorOptions) Executor {
const host_os_tag = options.host_os_tag;
const host_cpu_arch = options.host_cpu_arch;
const os_match = host_os_tag == candidate.os.tag;
const cpu_ok = cpu_ok: {
if (host_cpu_arch == candidate.cpu.arch)
break :cpu_ok true;
if (host_cpu_arch == .x86_64 and candidate.cpu.arch == .x86)
break :cpu_ok true;
if (host_cpu_arch == .aarch64 and candidate.cpu.arch == .arm)
break :cpu_ok true;
if (host_cpu_arch == .aarch64_be and candidate.cpu.arch == .armeb)
break :cpu_ok true;
// TODO additionally detect incompatible CPU features.
// Note that in some cases the OS kernel will emulate missing CPU features
// when an illegal instruction is encountered.
break :cpu_ok false;
};
var bad_result: Executor = .bad_os_or_cpu;
if (os_match and cpu_ok) native: {
if (options.link_libc) {
if (candidate.dynamic_linker.get()) |candidate_dl| {
Io.Dir.cwd().access(io, candidate_dl, .{}) catch {
bad_result = .{ .bad_dl = candidate_dl };
break :native;
};
}
}
return .native;
}
// If the OS match and OS is macOS and CPU is arm64, we can use Rosetta 2
// to emulate the foreign architecture.
if (options.allow_rosetta and os_match and
(host_os_tag == .maccatalyst or host_os_tag == .macos) and host_cpu_arch == .aarch64)
{
switch (candidate.cpu.arch) {
.x86_64 => return .rosetta,
else => return bad_result,
}
}
// If the OS matches, we can use QEMU to emulate a foreign architecture.
if (options.allow_qemu and os_match and (!cpu_ok or options.qemu_fixes_dl)) {
return switch (candidate.cpu.arch) {
inline .aarch64,
.arm,
.riscv64,
.x86,
.x86_64,
=> |t| switch (candidate.os.tag) {
.linux,
.freebsd,
=> .{ .qemu = switch (t) {
.x86 => "qemu-i386",
.x86_64 => switch (candidate.abi) {
.gnux32, .muslx32, .x32 => return bad_result,
else => "qemu-x86_64",
},
else => "qemu-" ++ @tagName(t),
} },
else => bad_result,
},
inline .aarch64_be,
.alpha,
.armeb,
.hexagon,
.hppa,
.loongarch64,
.m68k,
.microblaze,
.microblazeel,
.mips,
.mipsel,
.mips64,
.mips64el,
.or1k,
.powerpc,
.powerpc64,
.powerpc64le,
.riscv32,
.s390x,
.sh,
.sheb,
.sparc,
.sparc64,
.thumb,
.thumbeb,
.xtensa,
.xtensaeb,
=> |t| switch (candidate.os.tag) {
.linux,
=> .{
.qemu = switch (t) {
.powerpc => "qemu-ppc",
.powerpc64 => "qemu-ppc64",
.powerpc64le => "qemu-ppc64le",
.mips64, .mips64el => switch (candidate.abi) {
.gnuabin32, .muslabin32, .abin32 => if (t == .mips64el) "qemu-mipsn32el" else "qemu-mipsn32",
else => "qemu-" ++ @tagName(t),
},
// TODO: Actually check the SuperH version.
.sh => "qemu-sh4",
.sheb => "qemu-sh4eb",
.sparc => "qemu-sparc32plus",
.thumb => "qemu-arm",
.thumbeb => "qemu-armeb",
else => "qemu-" ++ @tagName(t),
},
},
else => bad_result,
},
else => bad_result,
};
}
if (options.allow_wasmtime and candidate.cpu.arch.isWasm()) {
return .{ .wasmtime = "wasmtime" };
}
switch (candidate.os.tag) {
.windows => {
if (options.allow_wine) {
const wine_supported = switch (candidate.cpu.arch) {
.thumb => switch (host_cpu_arch) {
.arm, .thumb, .aarch64 => true,
else => false,
},
.aarch64 => host_cpu_arch == .aarch64,
.x86 => host_cpu_arch.isX86(),
.x86_64 => host_cpu_arch == .x86_64,
else => false,
};
return if (wine_supported) .{ .wine = "wine" } else bad_result;
}
return bad_result;
},
.driverkit, .macos => {
if (options.allow_darling) {
// This check can be loosened once darling adds a QEMU-based emulation
// layer for non-host architectures:
// https://github.com/darlinghq/darling/issues/863
if (candidate.cpu.arch != host_cpu_arch) {
return bad_result;
}
return .{ .darling = "darling" };
}
return bad_result;
},
else => return bad_result,
}
}