import { realpathSync } from 'node:fs'; import { arch, platform } from 'node:os'; import { join, relative, resolve } from 'node:path'; import { pathToFileURL } from 'node:url'; import { PI_RUNTIME_PACKAGE, defaultPiBundleTarget, stagePiRuntimeBundle, } from './lib/pi-runtime-bundle.mjs'; function parseTarget(value) { const separator = value.lastIndexOf('-'); if (separator <= 0 || separator === value.length - 1) { throw new Error(`Invalid Pi runtime target: ${value}`); } const target = { platform: value.slice(0, separator), arch: value.slice(separator + 1), }; if (!['win32', 'darwin', 'linux'].includes(target.platform)) { throw new Error(`Unsupported Pi runtime platform: ${target.platform}`); } if (!['x64', 'arm64'].includes(target.arch)) { throw new Error(`Unsupported Pi runtime architecture: ${target.arch}`); } return target.platform === 'linux' ? { ...target, libc: 'glibc' } : target; } export function parseBundleArgs(argv) { const options = { outputRoot: resolve('build/pi-runtime'), targets: [] }; for (let index = 0; index < argv.length; index += 1) { const argument = argv[index]; if (argument === '--output') { const value = argv[index + 1]; if (!value || value.startsWith('--')) throw new Error('--output requires a value'); options.outputRoot = resolve(value); index += 1; } else if (argument === '--target') { const value = argv[index + 1]; if (!value || value.startsWith('--')) throw new Error('--target requires a value'); options.targets.push(parseTarget(value)); index += 1; } else if (argument === '--release-targets') { const currentPlatform = platform(); const arches = currentPlatform === 'win32' ? ['x64'] : ['x64', 'arm64']; options.targets.push(...arches.map((targetArch) => parseTarget(`${currentPlatform}-${targetArch}`))); } else if (argument === '--help') options.help = true; else throw new Error(`Unknown argument: ${argument}`); } if (options.targets.length === 0) options.targets.push(defaultPiBundleTarget()); const unique = new Map(options.targets.map((target) => [ `${target.platform}-${target.arch}`, target, ])); options.targets = [...unique.values()]; for (const target of options.targets) { if (target.platform !== platform()) { throw new Error( `Pi runtime ${target.platform}-${target.arch} must be staged on ${target.platform}, current host is ${platform()}-${arch()}`, ); } } return options; } function printHelp() { process.stdout.write('Usage: node scripts/bundle-pi-runtime.mjs [options]\n\n'); process.stdout.write(' --target Stage one runtime target (repeatable)\n'); process.stdout.write(' --release-targets Stage every configured architecture for the host OS\n'); process.stdout.write(' --output Output root (default: build/pi-runtime)\n'); } export async function bundlePiRuntime(options, projectRoot = process.cwd()) { const resolvedProjectRoot = resolve(projectRoot); const sourcePackageRoot = realpathSync(join( resolvedProjectRoot, 'node_modules', ...PI_RUNTIME_PACKAGE.split('/'), )); const results = []; for (const target of options.targets) { const destination = join(options.outputRoot, `${target.platform}-${target.arch}`); const destinationRelative = relative(options.outputRoot, destination); if (destinationRelative.startsWith('..') || destinationRelative === '') { throw new Error(`Unsafe Pi runtime destination: ${destination}`); } const manifest = await stagePiRuntimeBundle({ projectRoot: resolvedProjectRoot, sourcePackageRoot, destination, target, }); results.push({ destination, manifest }); } return results; } async function main() { const options = parseBundleArgs(process.argv.slice(2)); if (options.help) { printHelp(); return; } const results = await bundlePiRuntime(options); process.stdout.write(`${JSON.stringify(results.map(({ destination, manifest }) => ({ destination, target: manifest.target, packageCount: manifest.productionPackages.length, assetCount: manifest.runtimeAssets.length, })), null, 2)}\n`); } const isMain = process.argv[1] && pathToFileURL(resolve(process.argv[1])).href === import.meta.url; if (isMain) { main().catch((error) => { process.stderr.write(`${error.stack ?? error.message}\n`); process.exitCode = 1; }); }