fix(packaging): prune non-target native binaries (#12174)
This commit is contained in:
parent
e08eba674c
commit
128e3e335e
|
|
@ -44,6 +44,14 @@ const PARCEL_WATCHER_PLATFORM_PREFIX_BY_PLATFORM = {
|
||||||
linux: 'watcher-linux',
|
linux: 'watcher-linux',
|
||||||
win32: 'watcher-win32'
|
win32: 'watcher-win32'
|
||||||
}
|
}
|
||||||
|
const ELECTRON_ARCHITECTURE_BY_ENUM = {
|
||||||
|
0: 'ia32',
|
||||||
|
1: 'x64',
|
||||||
|
2: 'arm',
|
||||||
|
3: 'arm64',
|
||||||
|
4: 'universal'
|
||||||
|
}
|
||||||
|
const PACKAGED_NATIVE_ARCHITECTURES = new Set(['ia32', 'x64', 'arm', 'arm64'])
|
||||||
const TYPE_DECLARATION_ARTIFACT_RE = /\.d\.(?:c|m)?ts(?:\.map)?$/
|
const TYPE_DECLARATION_ARTIFACT_RE = /\.d\.(?:c|m)?ts(?:\.map)?$/
|
||||||
const VERSIONED_ONNXRUNTIME_DYLIB_RE = /^libonnxruntime\.\d[\d.]*\.dylib$/
|
const VERSIONED_ONNXRUNTIME_DYLIB_RE = /^libonnxruntime\.\d[\d.]*\.dylib$/
|
||||||
|
|
||||||
|
|
@ -170,7 +178,7 @@ function collectPackagedRuntimePackages(electronPlatformName = process.platform)
|
||||||
// optionalDependency (e.g. @parcel/watcher-linux-x64-glibc) that the
|
// optionalDependency (e.g. @parcel/watcher-linux-x64-glibc) that the
|
||||||
// dependencies graph above never reaches. Include the ones installed for the
|
// dependencies graph above never reaches. Include the ones installed for the
|
||||||
// build's supported architectures; afterPack pruning trims non-target
|
// build's supported architectures; afterPack pruning trims non-target
|
||||||
// platforms. Without this the packaged main bundle's import of
|
// platform/architecture variants. Without this the packaged main bundle's import of
|
||||||
// '@parcel/watcher' resolves at runtime but throws loading its binary.
|
// '@parcel/watcher' resolves at runtime but throws loading its binary.
|
||||||
const parcelWatcherDir = packages.get('@parcel/watcher')
|
const parcelWatcherDir = packages.get('@parcel/watcher')
|
||||||
if (parcelWatcherDir) {
|
if (parcelWatcherDir) {
|
||||||
|
|
@ -246,13 +254,44 @@ function verifyPackagedMainRuntimeDeps(resourcesDir, asar = require('@electron/a
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeNodePtyWindowsArch(electronArch) {
|
function normalizeNodePtyWindowsArch(electronArch) {
|
||||||
if (electronArch === 'x64' || electronArch === 1) {
|
const architecture = normalizeElectronArchitecture(electronArch)
|
||||||
return 'x64'
|
if (architecture !== 'x64' && architecture !== 'arm64') {
|
||||||
|
throw new Error(`Unsupported packaged node-pty Windows architecture: ${architecture}`)
|
||||||
}
|
}
|
||||||
if (electronArch === 'arm64' || electronArch === 3) {
|
return architecture
|
||||||
return 'arm64'
|
}
|
||||||
|
|
||||||
|
function normalizeElectronArchitecture(electronArch) {
|
||||||
|
const architecture =
|
||||||
|
typeof electronArch === 'number'
|
||||||
|
? ELECTRON_ARCHITECTURE_BY_ENUM[electronArch]
|
||||||
|
: electronArch === 'armv7l'
|
||||||
|
? 'arm'
|
||||||
|
: electronArch
|
||||||
|
if (!PACKAGED_NATIVE_ARCHITECTURES.has(architecture)) {
|
||||||
|
throw new Error(`Unsupported packaged runtime architecture: ${String(electronArch)}`)
|
||||||
|
}
|
||||||
|
return architecture
|
||||||
|
}
|
||||||
|
|
||||||
|
function pruneNodePtyNativeDirectories(directory, platformPrefix, electronArch, allowsSuffix) {
|
||||||
|
if (!existsSync(directory)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const architecture = normalizeElectronArchitecture(electronArch)
|
||||||
|
const targetPrefix = `${platformPrefix}${architecture}`
|
||||||
|
const platformPrefixes = Object.values(NODE_PTY_PREBUILD_PREFIX_BY_PLATFORM)
|
||||||
|
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
||||||
|
if (!entry.isDirectory() || !platformPrefixes.some((prefix) => entry.name.startsWith(prefix))) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const matchesTarget =
|
||||||
|
entry.name.startsWith(platformPrefix) &&
|
||||||
|
(entry.name === targetPrefix || (allowsSuffix && entry.name.startsWith(`${targetPrefix}-`)))
|
||||||
|
if (!matchesTarget) {
|
||||||
|
rmSync(join(directory, entry.name), { recursive: true, force: true })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return process.arch === 'arm64' ? 'arm64' : 'x64'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function findNodePtyConptySourceDir(nodePtyDir, windowsArch) {
|
function findNodePtyConptySourceDir(nodePtyDir, windowsArch) {
|
||||||
|
|
@ -308,14 +347,19 @@ function prunePackagedNodePty(resourcesDir, electronPlatformName, electronArch)
|
||||||
|
|
||||||
const allowedPrebuildPrefix = NODE_PTY_PREBUILD_PREFIX_BY_PLATFORM[electronPlatformName]
|
const allowedPrebuildPrefix = NODE_PTY_PREBUILD_PREFIX_BY_PLATFORM[electronPlatformName]
|
||||||
if (allowedPrebuildPrefix) {
|
if (allowedPrebuildPrefix) {
|
||||||
const prebuildsDir = join(nodePtyDir, 'prebuilds')
|
pruneNodePtyNativeDirectories(
|
||||||
if (existsSync(prebuildsDir)) {
|
join(nodePtyDir, 'prebuilds'),
|
||||||
for (const entry of readdirSync(prebuildsDir, { withFileTypes: true })) {
|
allowedPrebuildPrefix,
|
||||||
if (entry.isDirectory() && !entry.name.startsWith(allowedPrebuildPrefix)) {
|
electronArch,
|
||||||
rmSync(join(prebuildsDir, entry.name), { recursive: true, force: true })
|
false
|
||||||
}
|
)
|
||||||
}
|
// Why: sequential cross-arch rebuilds accumulate ABI-tagged outputs here.
|
||||||
}
|
pruneNodePtyNativeDirectories(
|
||||||
|
join(nodePtyDir, 'bin'),
|
||||||
|
allowedPrebuildPrefix,
|
||||||
|
electronArch,
|
||||||
|
true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (electronPlatformName === 'win32') {
|
if (electronPlatformName === 'win32') {
|
||||||
|
|
@ -328,7 +372,7 @@ function prunePackagedNodePty(resourcesDir, electronPlatformName, electronArch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function prunePackagedParcelWatcher(resourcesDir, electronPlatformName) {
|
function prunePackagedParcelWatcher(resourcesDir, electronPlatformName, electronArch) {
|
||||||
const parcelDir = join(resourcesDir, 'node_modules', '@parcel')
|
const parcelDir = join(resourcesDir, 'node_modules', '@parcel')
|
||||||
if (!existsSync(parcelDir)) {
|
if (!existsSync(parcelDir)) {
|
||||||
return
|
return
|
||||||
|
|
@ -336,9 +380,11 @@ function prunePackagedParcelWatcher(resourcesDir, electronPlatformName) {
|
||||||
|
|
||||||
// Why: we package every installed @parcel/watcher-<platform> optional
|
// Why: we package every installed @parcel/watcher-<platform> optional
|
||||||
// subpackage (supportedArchitectures fetches all), but each build only needs
|
// subpackage (supportedArchitectures fetches all), but each build only needs
|
||||||
// its own platform's binary. Keep the core package and the matching platform
|
// its own platform/architecture binaries. Keep the core package and matching
|
||||||
// subpackages; drop the rest so a Linux serve doesn't ship macOS/Windows .node.
|
// native variants; drop the rest.
|
||||||
const keepPrefix = PARCEL_WATCHER_PLATFORM_PREFIX_BY_PLATFORM[electronPlatformName]
|
const keepPrefix = PARCEL_WATCHER_PLATFORM_PREFIX_BY_PLATFORM[electronPlatformName]
|
||||||
|
const architecture = normalizeElectronArchitecture(electronArch)
|
||||||
|
const targetPrefix = keepPrefix ? `${keepPrefix}-${architecture}` : null
|
||||||
for (const entry of readdirSync(parcelDir, { withFileTypes: true })) {
|
for (const entry of readdirSync(parcelDir, { withFileTypes: true })) {
|
||||||
if (!entry.isDirectory() || entry.name === 'watcher') {
|
if (!entry.isDirectory() || entry.name === 'watcher') {
|
||||||
continue
|
continue
|
||||||
|
|
@ -348,7 +394,11 @@ function prunePackagedParcelWatcher(resourcesDir, electronPlatformName) {
|
||||||
if (!entry.name.startsWith('watcher-')) {
|
if (!entry.name.startsWith('watcher-')) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (keepPrefix && entry.name.startsWith(keepPrefix)) {
|
if (
|
||||||
|
keepPrefix &&
|
||||||
|
entry.name.startsWith(keepPrefix) &&
|
||||||
|
(entry.name === targetPrefix || entry.name.startsWith(`${targetPrefix}-`))
|
||||||
|
) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rmSync(join(parcelDir, entry.name), { recursive: true, force: true })
|
rmSync(join(parcelDir, entry.name), { recursive: true, force: true })
|
||||||
|
|
@ -395,8 +445,9 @@ function prunePackagedZodSources(resourcesDir) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function prunePackagedRuntimeNodeModules(resourcesDir, electronPlatformName, electronArch) {
|
function prunePackagedRuntimeNodeModules(resourcesDir, electronPlatformName, electronArch) {
|
||||||
prunePackagedNodePty(resourcesDir, electronPlatformName, electronArch)
|
const architecture = normalizeElectronArchitecture(electronArch)
|
||||||
prunePackagedParcelWatcher(resourcesDir, electronPlatformName)
|
prunePackagedNodePty(resourcesDir, electronPlatformName, architecture)
|
||||||
|
prunePackagedParcelWatcher(resourcesDir, electronPlatformName, architecture)
|
||||||
prunePackagedRuntimeTypeDeclarations(resourcesDir)
|
prunePackagedRuntimeTypeDeclarations(resourcesDir)
|
||||||
prunePackagedSherpaOnnx(resourcesDir, electronPlatformName)
|
prunePackagedSherpaOnnx(resourcesDir, electronPlatformName)
|
||||||
prunePackagedZodSources(resourcesDir)
|
prunePackagedZodSources(resourcesDir)
|
||||||
|
|
|
||||||
|
|
@ -420,40 +420,42 @@ describe('electron-builder config', () => {
|
||||||
expect(findAsarEntry(['/out/main/index.js'], 'out/main/index.js')).toBe('/out/main/index.js')
|
expect(findAsarEntry(['/out/main/index.js'], 'out/main/index.js')).toBe('/out/main/index.js')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('prunes non-target node-pty prebuilds from packaged runtime resources', async () => {
|
it('prunes non-target node-pty architecture outputs from packaged runtime resources', async () => {
|
||||||
const resourcesDir = await mkdtemp(join(tmpdir(), 'orca-node-pty-prune-'))
|
const resourcesDir = await mkdtemp(join(tmpdir(), 'orca-node-pty-prune-'))
|
||||||
try {
|
try {
|
||||||
const prebuildsDir = join(resourcesDir, 'node_modules', 'node-pty', 'prebuilds')
|
const nodePtyDir = join(resourcesDir, 'node_modules', 'node-pty')
|
||||||
|
const prebuildsDir = join(nodePtyDir, 'prebuilds')
|
||||||
|
const binDir = join(nodePtyDir, 'bin')
|
||||||
await mkdir(join(prebuildsDir, 'darwin-arm64'), { recursive: true })
|
await mkdir(join(prebuildsDir, 'darwin-arm64'), { recursive: true })
|
||||||
await mkdir(join(prebuildsDir, 'darwin-x64'), { recursive: true })
|
await mkdir(join(prebuildsDir, 'darwin-x64'), { recursive: true })
|
||||||
await mkdir(join(prebuildsDir, 'linux-x64'), { recursive: true })
|
await mkdir(join(prebuildsDir, 'linux-x64'), { recursive: true })
|
||||||
await mkdir(join(prebuildsDir, 'win32-x64'), { recursive: true })
|
await mkdir(join(prebuildsDir, 'win32-x64'), { recursive: true })
|
||||||
await mkdir(join(resourcesDir, 'node_modules', 'node-pty', 'third_party', 'conpty'), {
|
await mkdir(join(binDir, 'darwin-arm64-148'), { recursive: true })
|
||||||
recursive: true
|
await mkdir(join(binDir, 'darwin-x64-148'), { recursive: true })
|
||||||
})
|
await mkdir(join(nodePtyDir, 'third_party', 'conpty'), {
|
||||||
await mkdir(join(resourcesDir, 'node_modules', 'node-pty', 'deps', 'winpty'), {
|
|
||||||
recursive: true
|
recursive: true
|
||||||
})
|
})
|
||||||
|
await mkdir(join(nodePtyDir, 'deps', 'winpty'), { recursive: true })
|
||||||
|
|
||||||
prunePackagedNodePty(resourcesDir, 'darwin')
|
prunePackagedNodePty(resourcesDir, 'darwin', 3)
|
||||||
|
|
||||||
await expect(readdir(prebuildsDir).then((entries) => entries.sort())).resolves.toEqual([
|
await expect(readdir(prebuildsDir)).resolves.toEqual(['darwin-arm64'])
|
||||||
'darwin-arm64',
|
await expect(readdir(binDir)).resolves.toEqual(['darwin-arm64-148'])
|
||||||
'darwin-x64'
|
await expect(readdir(join(nodePtyDir, 'third_party'))).resolves.toEqual([])
|
||||||
])
|
await expect(readdir(join(nodePtyDir, 'deps'))).resolves.toEqual([])
|
||||||
await expect(
|
expect(() => prunePackagedNodePty(resourcesDir, 'darwin', 4)).toThrow(
|
||||||
readdir(join(resourcesDir, 'node_modules', 'node-pty', 'third_party'))
|
'Unsupported packaged runtime architecture: 4'
|
||||||
).resolves.toEqual([])
|
)
|
||||||
await expect(
|
|
||||||
readdir(join(resourcesDir, 'node_modules', 'node-pty', 'deps'))
|
|
||||||
).resolves.toEqual([])
|
|
||||||
} finally {
|
} finally {
|
||||||
await rm(resourcesDir, { recursive: true, force: true })
|
await rm(resourcesDir, { recursive: true, force: true })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('copies the Windows node-pty ConPTY runtime beside the rebuilt addon', async () => {
|
it('copies the Windows node-pty ConPTY runtime beside the rebuilt addon', async () => {
|
||||||
for (const arch of ['x64', 'arm64']) {
|
for (const [arch, electronArch] of [
|
||||||
|
['x64', 1],
|
||||||
|
['arm64', 3]
|
||||||
|
]) {
|
||||||
const resourcesDir = await mkdtemp(join(tmpdir(), `orca-node-pty-conpty-${arch}-`))
|
const resourcesDir = await mkdtemp(join(tmpdir(), `orca-node-pty-conpty-${arch}-`))
|
||||||
try {
|
try {
|
||||||
const nodePtyDir = join(resourcesDir, 'node_modules', 'node-pty')
|
const nodePtyDir = join(resourcesDir, 'node_modules', 'node-pty')
|
||||||
|
|
@ -472,7 +474,7 @@ describe('electron-builder config', () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
prunePackagedNodePty(resourcesDir, 'win32', arch)
|
prunePackagedNodePty(resourcesDir, 'win32', electronArch)
|
||||||
|
|
||||||
await expect(readFile(join(releaseDir, 'conpty', 'conpty.dll'), 'utf8')).resolves.toBe(
|
await expect(readFile(join(releaseDir, 'conpty', 'conpty.dll'), 'utf8')).resolves.toBe(
|
||||||
`dll payload ${arch}`
|
`dll payload ${arch}`
|
||||||
|
|
@ -500,7 +502,7 @@ describe('electron-builder config', () => {
|
||||||
).toBe(true)
|
).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('prunes non-target @parcel/watcher platform subpackages from packaged runtime resources', async () => {
|
it('prunes non-target @parcel/watcher architecture subpackages', async () => {
|
||||||
const resourcesDir = await mkdtemp(join(tmpdir(), 'orca-parcel-watcher-prune-'))
|
const resourcesDir = await mkdtemp(join(tmpdir(), 'orca-parcel-watcher-prune-'))
|
||||||
try {
|
try {
|
||||||
const parcelDir = join(resourcesDir, 'node_modules', '@parcel')
|
const parcelDir = join(resourcesDir, 'node_modules', '@parcel')
|
||||||
|
|
@ -511,13 +513,15 @@ describe('electron-builder config', () => {
|
||||||
await mkdir(join(parcelDir, 'watcher-linux-arm64-glibc'), { recursive: true })
|
await mkdir(join(parcelDir, 'watcher-linux-arm64-glibc'), { recursive: true })
|
||||||
await mkdir(join(parcelDir, 'watcher-win32-x64'), { recursive: true })
|
await mkdir(join(parcelDir, 'watcher-win32-x64'), { recursive: true })
|
||||||
|
|
||||||
prunePackagedParcelWatcher(resourcesDir, 'linux')
|
prunePackagedParcelWatcher(resourcesDir, 'linux', 'arm64')
|
||||||
|
|
||||||
await expect(readdir(parcelDir).then((entries) => entries.sort())).resolves.toEqual([
|
await expect(readdir(parcelDir).then((entries) => entries.sort())).resolves.toEqual([
|
||||||
'watcher',
|
'watcher',
|
||||||
'watcher-linux-arm64-glibc',
|
'watcher-linux-arm64-glibc'
|
||||||
'watcher-linux-x64-glibc'
|
|
||||||
])
|
])
|
||||||
|
expect(() => prunePackagedParcelWatcher(resourcesDir, 'linux', 'universal')).toThrow(
|
||||||
|
'Unsupported packaged runtime architecture: universal'
|
||||||
|
)
|
||||||
} finally {
|
} finally {
|
||||||
await rm(resourcesDir, { recursive: true, force: true })
|
await rm(resourcesDir, { recursive: true, force: true })
|
||||||
}
|
}
|
||||||
|
|
@ -533,7 +537,7 @@ describe('electron-builder config', () => {
|
||||||
// A hypothetical future @parcel/* runtime dep that is NOT a watcher subpackage.
|
// A hypothetical future @parcel/* runtime dep that is NOT a watcher subpackage.
|
||||||
await mkdir(join(parcelDir, 'transformer-js'), { recursive: true })
|
await mkdir(join(parcelDir, 'transformer-js'), { recursive: true })
|
||||||
|
|
||||||
prunePackagedParcelWatcher(resourcesDir, 'linux')
|
prunePackagedParcelWatcher(resourcesDir, 'linux', 1)
|
||||||
|
|
||||||
await expect(readdir(parcelDir).then((entries) => entries.sort())).resolves.toEqual([
|
await expect(readdir(parcelDir).then((entries) => entries.sort())).resolves.toEqual([
|
||||||
'transformer-js',
|
'transformer-js',
|
||||||
|
|
@ -653,7 +657,8 @@ describe('electron-builder config', () => {
|
||||||
|
|
||||||
await electronBuilderConfig.afterPack({
|
await electronBuilderConfig.afterPack({
|
||||||
appOutDir: join(root, 'linux-unpacked'),
|
appOutDir: join(root, 'linux-unpacked'),
|
||||||
electronPlatformName: 'linux'
|
electronPlatformName: 'linux',
|
||||||
|
arch: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
expect((await stat(launcherPath)).mode & 0o111).not.toBe(0)
|
expect((await stat(launcherPath)).mode & 0o111).not.toBe(0)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue