mirror of
https://github.com/oven-sh/bun
synced 2026-02-13 20:39:05 +00:00
### What does this PR do? This PRs adjusts the "arch" string for Linux-musl variant to make sure it can be installed on ARM64 platforms using `npm`. Without this fix, installing bun on Alpine Linux on arm64 fails because the native binary cannot be found. #### Why it fails Bun attempts to find/download the native binaries during the postinstall phase (see [install.ts](https://github.com/oven-sh/bun/blob/bun-v1.1.42/packages/bun-release/src/npm/install.ts)). The platform matching logic lives in [platform.ts](https://github.com/oven-sh/bun/blob/bun-v1.1.42/packages/bun-release/src/platform.ts). Note how the "musl" variant is marked [as "aarch64"](https://github.com/oven-sh/bun/blob/bun-v1.1.42/packages/bun-release/src/platform.ts#L63-L69), while the regular "glibc" variant is marked [as "arm64"](https://github.com/oven-sh/bun/blob/bun-v1.1.42/packages/bun-release/src/platform.ts#L44-L49). On Alpine Linux distributions (or when using "node-alpine" docker image) we're supposed to be using the "musl" binary. However, since bun marks it as "aarch64" while the matching logic relies on `process.arch`, it never gets matched. Node.js uses "arm64", _not_ "aarch64" (see ["process.arch" docs](https://nodejs.org/docs/latest-v22.x/api/process.html#processarch)). In short - a mismatch between the expected arch ("aarch64") and the actual reported arch ("arm64") prevents bun from finding the right binary when installing with npm/pnpm. ### How did you verify your code works? Verified by running the installer on Alpine Linux on arm64. cc @magus
161 lines
3.3 KiB
TypeScript
161 lines
3.3 KiB
TypeScript
import { debug } from "./console";
|
|
import { exists, read } from "./fs";
|
|
import { spawn } from "./spawn";
|
|
|
|
export const os = process.platform;
|
|
|
|
export const arch = os === "darwin" && process.arch === "x64" && isRosetta2() ? "arm64" : process.arch;
|
|
|
|
export const avx2 =
|
|
arch === "x64" &&
|
|
((os === "linux" && isLinuxAVX2()) || (os === "darwin" && isDarwinAVX2()) || (os === "win32" && isWindowsAVX2()));
|
|
|
|
export const abi = os === "linux" && isLinuxMusl() ? "musl" : undefined;
|
|
|
|
export type Platform = {
|
|
os: string;
|
|
arch: string;
|
|
abi?: "musl";
|
|
avx2?: boolean;
|
|
bin: string;
|
|
exe: string;
|
|
};
|
|
|
|
export const platforms: Platform[] = [
|
|
{
|
|
os: "darwin",
|
|
arch: "arm64",
|
|
bin: "bun-darwin-aarch64",
|
|
exe: "bin/bun",
|
|
},
|
|
{
|
|
os: "darwin",
|
|
arch: "x64",
|
|
avx2: true,
|
|
bin: "bun-darwin-x64",
|
|
exe: "bin/bun",
|
|
},
|
|
{
|
|
os: "darwin",
|
|
arch: "x64",
|
|
bin: "bun-darwin-x64-baseline",
|
|
exe: "bin/bun",
|
|
},
|
|
{
|
|
os: "linux",
|
|
arch: "arm64",
|
|
bin: "bun-linux-aarch64",
|
|
exe: "bin/bun",
|
|
},
|
|
{
|
|
os: "linux",
|
|
arch: "x64",
|
|
avx2: true,
|
|
bin: "bun-linux-x64",
|
|
exe: "bin/bun",
|
|
},
|
|
{
|
|
os: "linux",
|
|
arch: "x64",
|
|
bin: "bun-linux-x64-baseline",
|
|
exe: "bin/bun",
|
|
},
|
|
{
|
|
os: "linux",
|
|
arch: "arm64",
|
|
abi: "musl",
|
|
bin: "bun-linux-aarch64-musl",
|
|
exe: "bin/bun",
|
|
},
|
|
{
|
|
os: "linux",
|
|
arch: "x64",
|
|
abi: "musl",
|
|
avx2: true,
|
|
bin: "bun-linux-x64-musl",
|
|
exe: "bin/bun",
|
|
},
|
|
{
|
|
os: "linux",
|
|
arch: "x64",
|
|
abi: "musl",
|
|
bin: "bun-linux-x64-musl-baseline",
|
|
exe: "bin/bun",
|
|
},
|
|
{
|
|
os: "win32",
|
|
arch: "x64",
|
|
avx2: true,
|
|
bin: "bun-windows-x64",
|
|
exe: "bin/bun.exe",
|
|
},
|
|
{
|
|
os: "win32",
|
|
arch: "x64",
|
|
bin: "bun-windows-x64-baseline",
|
|
exe: "bin/bun.exe",
|
|
},
|
|
];
|
|
|
|
export const supportedPlatforms: Platform[] = platforms
|
|
.filter(
|
|
platform =>
|
|
platform.os === os &&
|
|
platform.arch === arch &&
|
|
(!platform.avx2 || avx2) &&
|
|
(!platform.abi || abi === platform.abi),
|
|
)
|
|
.sort((a, b) => (a.avx2 === b.avx2 ? 0 : a.avx2 ? -1 : 1));
|
|
|
|
function isLinuxMusl(): boolean {
|
|
try {
|
|
return exists("/etc/alpine-release");
|
|
} catch (error) {
|
|
debug("isLinuxMusl failed", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function isLinuxAVX2(): boolean {
|
|
try {
|
|
return read("/proc/cpuinfo").includes("avx2");
|
|
} catch (error) {
|
|
debug("isLinuxAVX2 failed", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function isDarwinAVX2(): boolean {
|
|
try {
|
|
const { exitCode, stdout } = spawn("sysctl", ["-n", "machdep.cpu"]);
|
|
return exitCode === 0 && stdout.includes("AVX2");
|
|
} catch (error) {
|
|
debug("isDarwinAVX2 failed", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function isRosetta2(): boolean {
|
|
try {
|
|
const { exitCode, stdout } = spawn("sysctl", ["-n", "sysctl.proc_translated"]);
|
|
return exitCode === 0 && stdout.includes("1");
|
|
} catch (error) {
|
|
debug("isRosetta2 failed", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function isWindowsAVX2(): boolean {
|
|
try {
|
|
return (
|
|
spawn("powershell", [
|
|
"-c",
|
|
`(Add-Type -MemberDefinition '[DllImport("kernel32.dll")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);' -Name 'Kernel32' -Namespace 'Win32' -PassThru)::IsProcessorFeaturePresent(40);`,
|
|
]).stdout.trim() === "True"
|
|
);
|
|
} catch (error) {
|
|
debug("isWindowsAVX2 failed", error);
|
|
return false;
|
|
}
|
|
}
|