diff --git a/CHANGELOG.md b/CHANGELOG.md index 85c966bbd3c..94a7708ed91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ #### :rocket: New Feature +- Support Windows 11 on ARM by installing the x64 binaries, which Windows runs through its x64 emulation. https://github.com/rescript-lang/rescript/pull/8776 - Allow `$` in identifiers, e.g. `let count$ = signal(0)`. https://github.com/rescript-lang/rescript/pull/8791 #### :bug: Bug fix diff --git a/cli/common/bins.cjs b/cli/common/bins.cjs index e5fa517dab8..a99de4b02ab 100644 --- a/cli/common/bins.cjs +++ b/cli/common/bins.cjs @@ -4,22 +4,18 @@ // The lookup is synchronous so that tools.cjs, the CommonJS entry of // `rescript/tools`, can load it; bins.js re-exports these values for ES modules. +const os = require("node:os"); const path = require("node:path"); +const { getTarget } = require("./platform.cjs"); const minimumNodeVersion = "22.0.0"; -const target = `${process.platform}-${process.arch}`; +const target = getTarget(process.platform, process.arch, os.release()); -const supportedPlatforms = [ - "darwin-arm64", - "darwin-x64", - "linux-arm64", - "linux-x64", - "win32-x64", -]; - -if (!supportedPlatforms.includes(target)) { - throw new Error(`Platform ${target} is not supported!`); +if (target === undefined) { + throw new Error( + `Platform ${process.platform}-${process.arch} is not supported!`, + ); } const binPackageName = `@rescript/${target}`; diff --git a/cli/common/platform.cjs b/cli/common/platform.cjs new file mode 100644 index 00000000000..1053611faff --- /dev/null +++ b/cli/common/platform.cjs @@ -0,0 +1,43 @@ +// @ts-check + +// Maps the current platform to the platform package (@rescript/) that +// provides its compiler binaries. Kept free of side effects so it can be tested. + +const supportedTargets = [ + "darwin-arm64", + "darwin-x64", + "linux-arm64", + "linux-x64", + "win32-x64", +]; + +// The first Windows 11 build. Windows 10 on ARM only emulates x86, not x64. +const firstWindows11Build = 22000; + +/** + * @param {string} platform `process.platform` + * @param {string} arch `process.arch` + * @param {string} osRelease `os.release()`, e.g. "10.0.22631" on Windows 11 + * @returns {string | undefined} the target, or undefined if unsupported + */ +function getTarget(platform, arch, osRelease) { + // Windows 11 on ARM runs the x64 toolchain through its x64 emulation. + const binaryArch = + platform === "win32" && + arch === "arm64" && + windowsBuild(osRelease) >= firstWindows11Build + ? "x64" + : arch; + const target = `${platform}-${binaryArch}`; + return supportedTargets.includes(target) ? target : undefined; +} + +/** + * @param {string} osRelease + */ +function windowsBuild(osRelease) { + const build = Number(osRelease.split(".")[2]); + return Number.isInteger(build) ? build : 0; +} + +exports.getTarget = getTarget; diff --git a/packages/@rescript/win32-x64/package.json b/packages/@rescript/win32-x64/package.json index 7755b2ab76e..6ca159ea368 100644 --- a/packages/@rescript/win32-x64/package.json +++ b/packages/@rescript/win32-x64/package.json @@ -45,7 +45,8 @@ "win32" ], "cpu": [ - "x64" + "x64", + "arm64" ], "files": [ "bin.js", diff --git a/packages/artifacts.json b/packages/artifacts.json index 3e39d26b5c9..20ab33ba762 100644 --- a/packages/artifacts.json +++ b/packages/artifacts.json @@ -10,6 +10,7 @@ "cli/bsc.js", "cli/common/bins.cjs", "cli/common/bins.js", + "cli/common/platform.cjs", "cli/common/runtime.js", "cli/common/tools.cjs", "cli/common/tools.js", diff --git a/scripts/test.js b/scripts/test.js index cbf18084779..e5e26839043 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -104,6 +104,7 @@ if (mochaTest) { "10000", "tests/tests/src/**/*_test.mjs", "tests/belt_tests/src/**/*_test.mjs", + "tests/cli_tests/*_test.mjs", // jsx_preserve_test.mjs contains JSX, which Node cannot load; the // file exists to pin the preserved-JSX output. "--ignore", diff --git a/tests/cli_tests/platform_test.mjs b/tests/cli_tests/platform_test.mjs new file mode 100644 index 00000000000..4faa5a1465f --- /dev/null +++ b/tests/cli_tests/platform_test.mjs @@ -0,0 +1,33 @@ +import * as assert from "node:assert/strict"; +import { describe, it } from "mocha"; +import { getTarget } from "../../cli/common/platform.cjs"; + +describe("getTarget", () => { + it("maps supported platforms to their own package", () => { + for (const [platform, arch] of [ + ["darwin", "arm64"], + ["darwin", "x64"], + ["linux", "arm64"], + ["linux", "x64"], + ["win32", "x64"], + ]) { + assert.equal(getTarget(platform, arch, "10.0.0"), `${platform}-${arch}`); + } + }); + + it("uses the x64 package on Windows 11 on ARM", () => { + assert.equal(getTarget("win32", "arm64", "10.0.22000"), "win32-x64"); + assert.equal(getTarget("win32", "arm64", "10.0.26100"), "win32-x64"); + }); + + it("rejects Windows 10 on ARM, which cannot emulate x64", () => { + assert.equal(getTarget("win32", "arm64", "10.0.19045"), undefined); + assert.equal(getTarget("win32", "arm64", "unknown"), undefined); + }); + + it("rejects unsupported platforms", () => { + assert.equal(getTarget("win32", "ia32", "10.0.26100"), undefined); + assert.equal(getTarget("linux", "ia32", "6.0.0"), undefined); + assert.equal(getTarget("freebsd", "x64", "14.0"), undefined); + }); +});