feat: add Pi process and RPC foundation
This commit is contained in:
123
scripts/bundle-pi-runtime.mjs
Normal file
123
scripts/bundle-pi-runtime.mjs
Normal file
@@ -0,0 +1,123 @@
|
||||
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;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user