fix(desktop): degrade get-windows on Windows ARM64

(cherry picked from commit 6997b5a97e72aa1ffc98533aaa100e84857b1fbe)
This commit is contained in:
fangliquanflq
2026-08-09 15:22:00 +08:00
committed by brooklyn!
parent 58e4bb04d6
commit 689a4be6f2
3 changed files with 58 additions and 19 deletions
+4 -5
View File
@@ -144,11 +144,10 @@ export default async function beforePack(context) {
await stageNodePty({ platform, arch: archName })
console.log(`[before-pack] re-staged node-pty for target ${platform}-${archName}`)
}
// get-windows' native payload is per-platform, not per-arch (the macOS
// helper is universal, Windows stages every prebuilt binding dir), so
// it re-stages for the universal target too.
stageGetWindows({ platform })
console.log(`[before-pack] re-staged get-windows for target ${platform}`)
// The macOS helper is universal, while Windows bindings are arch-specific.
// Pass the target arch so an ARM64 package never stages an x64 binding.
stageGetWindows({ platform, arch: archName })
console.log(`[before-pack] re-staged get-windows for target ${platform}-${archName}`)
}
} catch (err) {
// This one SHOULD fail the build — a missing/wrong native binary for the
+20 -7
View File
@@ -437,7 +437,7 @@ const GET_WINDOWS_VERSION = '9.3.0'
export function stageGetWindowsInto(
srcRoot,
destRoot,
{ platform = process.platform, rebuild } = {}
{ platform = process.platform, arch = process.arch, rebuild } = {}
) {
// The STAGED_WINDOWS_JS rewrite mirrors this exact version's export surface.
// A version bump must fail the build here until the rewrite is re-verified —
@@ -490,11 +490,20 @@ export function stageGetWindowsInto(
? readdirSync(bindingRoot).filter(
(dir) =>
dir.includes(`-${platform}-`) &&
dir.endsWith(`-${arch}`) &&
existsSync(join(bindingRoot, dir, 'node-get-windows.node'))
)
: []
let bindingDirs = scanBindingDirs()
if (bindingDirs.length === 0 && typeof rebuild === 'function') {
if (bindingDirs.length === 0 && arch === 'arm64') {
// get-windows 9.3.0 publishes win32 prebuilds for ia32/x64 only.
// The staged windows.js deliberately fails soft when binding/ is absent,
// so preserve the desktop build and disable only window enumeration.
console.warn(
'[stage-native-deps] get-windows has no win32-arm64 prebuilt binding; ' +
'staging the fail-soft JS surface without native window enumeration.'
)
} else if (bindingDirs.length === 0 && typeof rebuild === 'function') {
// A plain `npm install` won't re-run an install script for a package
// that is already on disk, so every checkout that installed while
// get-windows was missing from allowScripts stays bricked even after
@@ -505,9 +514,9 @@ export function stageGetWindowsInto(
rebuild()
bindingDirs = scanBindingDirs()
}
if (bindingDirs.length === 0) {
if (bindingDirs.length === 0 && arch !== 'arm64') {
throw new Error(
'[stage-native-deps] get-windows has no win32 prebuilt binding under lib/binding. ' +
`[stage-native-deps] get-windows has no win32-${arch} prebuilt binding under lib/binding. ` +
'Recover from the checkout root with:\n' +
' npm install-scripts approve get-windows\n' +
' npm rebuild get-windows'
@@ -546,7 +555,11 @@ function rebuildGetWindowsViaNpm() {
}
export function stageGetWindows(
{ platform = process.platform, resolveRoot = resolveGetWindowsRoot } = {}
{
platform = process.platform,
arch = process.arch,
resolveRoot = resolveGetWindowsRoot
} = {}
) {
const srcRoot = resolveRoot()
const destRoot = resolve(projectRoot, 'dist/node_modules/get-windows')
@@ -575,12 +588,12 @@ export function stageGetWindows(
// has nothing to gain from the rebuild.
const rebuild =
platform === 'win32' && process.platform === 'win32' ? rebuildGetWindowsViaNpm : undefined
return stageGetWindowsInto(srcRoot, destRoot, { platform, rebuild })
return stageGetWindowsInto(srcRoot, destRoot, { platform, arch, rebuild })
}
// Allow direct CLI invocation: node scripts/stage-native-deps.mjs [platform] [arch]
if (isMain(import.meta.url)) {
const [platform, arch] = process.argv.slice(2)
stageNodePty({ platform, arch })
stageGetWindows({ platform })
stageGetWindows({ platform, arch })
}
@@ -391,7 +391,7 @@ test('win32 staging skips the darwin binding the tarball bundles on every platfo
]
})
stageGetWindowsInto(srcRoot, destRoot, { platform: 'win32' })
stageGetWindowsInto(srcRoot, destRoot, { platform: 'win32', arch: 'x64' })
assert.ok(existsSync(join(destRoot, 'lib', 'binding', 'napi-9-win32-unknown-x64', 'node-get-windows.node')))
assert.ok(!existsSync(join(destRoot, 'lib', 'binding', 'napi-9-darwin-unknown-arm64')))
@@ -411,7 +411,7 @@ test('win32 staging rejects a binding dir that claims win32 but holds a foreign
})
assert.throws(
() => stageGetWindowsInto(srcRoot, destRoot, { platform: 'win32' }),
() => stageGetWindowsInto(srcRoot, destRoot, { platform: 'win32', arch: 'x64' }),
/expected win32, got darwin/
)
} finally {
@@ -419,7 +419,7 @@ test('win32 staging rejects a binding dir that claims win32 but holds a foreign
}
})
test('win32 staging fails when only foreign bindings exist', () => {
test('win32-x64 staging fails when only foreign bindings exist', () => {
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
try {
const srcRoot = join(tmp, 'get-windows')
@@ -430,14 +430,36 @@ test('win32 staging fails when only foreign bindings exist', () => {
})
assert.throws(
() => stageGetWindowsInto(srcRoot, destRoot, { platform: 'win32' }),
/no win32 prebuilt binding/
() => stageGetWindowsInto(srcRoot, destRoot, { platform: 'win32', arch: 'x64' }),
/no win32-x64 prebuilt binding/
)
} finally {
fs.rmSync(tmp, { recursive: true, force: true })
}
})
test('win32-arm64 staging omits incompatible bindings and keeps the fail-soft JS surface', () => {
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
try {
const srcRoot = join(tmp, 'get-windows')
const destRoot = join(tmp, 'dest')
makeFakeGetWindows(srcRoot, {
bindings: [
{ dir: 'napi-9-darwin-unknown-arm64', platform: 'darwin' },
{ dir: 'napi-9-win32-unknown-x64', platform: 'win32' }
]
})
stageGetWindowsInto(srcRoot, destRoot, { platform: 'win32', arch: 'arm64' })
assert.ok(existsSync(join(destRoot, 'lib', 'windows.js')))
assert.ok(!existsSync(join(destRoot, 'lib', 'binding')))
} finally {
fs.rmSync(tmp, { recursive: true, force: true })
}
})
test('win32 staging self-heals through the rebuild hook when the binding is missing', () => {
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
try {
@@ -457,7 +479,7 @@ test('win32 staging self-heals through the rebuild hook when the binding is miss
)
}
stageGetWindowsInto(srcRoot, destRoot, { platform: 'win32', rebuild })
stageGetWindowsInto(srcRoot, destRoot, { platform: 'win32', arch: 'x64', rebuild })
assert.equal(calls, 1)
assert.ok(
@@ -477,7 +499,12 @@ test('win32 staging reports the recovery steps when the rebuild hook produces no
makeFakeGetWindows(srcRoot, { bindings: [] })
assert.throws(
() => stageGetWindowsInto(srcRoot, destRoot, { platform: 'win32', rebuild: () => {} }),
() =>
stageGetWindowsInto(srcRoot, destRoot, {
platform: 'win32',
arch: 'x64',
rebuild: () => {}
}),
/npm rebuild get-windows/
)
} finally {