From cfd8d3dc507eda258f6bb9166ea1bec0864c1ca2 Mon Sep 17 00:00:00 2001 From: Tadas Dailyda Date: Thu, 15 Dec 2022 12:18:08 +0200 Subject: [PATCH] cli: add 'universal' command --- cli/src/index.ts | 2 ++ cli/src/parse-triple.ts | 4 +++ cli/src/universal.ts | 79 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 cli/src/universal.ts diff --git a/cli/src/index.ts b/cli/src/index.ts index d4cd6c80..c6d4fc7c 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -11,6 +11,7 @@ import { HelpCommand } from './help' import { NewProjectCommand } from './new' import { PrePublishCommand } from './pre-publish' import { RenameCommand } from './rename' +import { UniversalCommand } from './universal' import { VersionCommand } from './version' const cli = new Cli({ @@ -23,6 +24,7 @@ cli.register(BuildCommand) cli.register(CreateNpmDirCommand) cli.register(PrePublishCommand) cli.register(VersionCommand) +cli.register(UniversalCommand) cli.register(NewProjectCommand) cli.register(RenameCommand) cli.register(HelpCommand) diff --git a/cli/src/parse-triple.ts b/cli/src/parse-triple.ts index fef1372c..6d42620c 100644 --- a/cli/src/parse-triple.ts +++ b/cli/src/parse-triple.ts @@ -29,6 +29,10 @@ const SysToNodePlatform: { [index: string]: NodeJS.Platform } = { windows: 'win32', } +export const UniArchsByPlatform: Record = { + darwin: ['x64', 'arm64'], +} + export interface PlatformDetail { platform: NodeJS.Platform platformArchABI: string diff --git a/cli/src/universal.ts b/cli/src/universal.ts new file mode 100644 index 00000000..2bafc3cc --- /dev/null +++ b/cli/src/universal.ts @@ -0,0 +1,79 @@ +import { spawnSync } from 'child_process' +import { join } from 'path' + +import chalk from 'chalk' +import { Command, Option } from 'clipanion' + +import { getNapiConfig } from './consts' +import { debugFactory } from './debug' +import { UniArchsByPlatform } from './parse-triple' +import { fileExists } from './utils' + +const debug = debugFactory('universal') + +export class UniversalCommand extends Command { + static usage = Command.Usage({ + description: 'Combine built binaries to universal binaries', + }) + + static paths = [['universal']] + + sourceDir = Option.String('-d,--dir', 'artifacts') + + distDir = Option.String('--dist', '.') + + configFileName?: string = Option.String('-c,--config') + + buildUniversal: Record< + keyof typeof UniArchsByPlatform, + (binName: string, srcFiles: string[]) => string + > = { + darwin: (binName, srcFiles) => { + const outPath = join( + this.distDir, + `${binName}.${process.platform}-universal.node`, + ) + const srcPaths = srcFiles.map((f) => join(this.sourceDir, f)) + spawnSync('lipo', ['-create', '-output', outPath, ...srcPaths]) + return outPath + }, + } + + async execute() { + const { platforms, binaryName } = getNapiConfig(this.configFileName) + + const targetPlatform = platforms.find( + (p) => p.platform === process.platform && p.arch === 'universal', + ) + if (!targetPlatform) { + throw new TypeError( + `'universal' arch for platform '${process.platform}' not found in config!`, + ) + } + + const srcFiles = UniArchsByPlatform[process.platform]?.map( + (a) => `${binaryName}.${process.platform}-${a}.node`, + ) + if (!srcFiles) { + throw new TypeError( + `'universal' arch for platform '${process.platform}' not supported.`, + ) + } + + debug( + `Looking up source binaries to combine: ${chalk.yellowBright(srcFiles)}`, + ) + const srcFileLookup = await Promise.all( + srcFiles.map((f) => fileExists(join(this.sourceDir, f))), + ) + const notFoundFiles = srcFiles.filter((_f, i) => !srcFileLookup[i]) + if (notFoundFiles.length > 0) { + throw new TypeError( + `Some binary files were not found: ${JSON.stringify(notFoundFiles)}`, + ) + } + + const outPath = this.buildUniversal[process.platform](binaryName, srcFiles) + debug(`Produced universal binary: ${outPath}`) + } +}