124 lines
4.4 KiB
JavaScript
124 lines
4.4 KiB
JavaScript
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 <platform-arch> Stage one runtime target (repeatable)\n');
|
|
process.stdout.write(' --release-targets Stage every configured architecture for the host OS\n');
|
|
process.stdout.write(' --output <directory> 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;
|
|
});
|
|
}
|