Skip to content

Commit

Permalink
neon add-target creates the template tree for each added target
Browse files Browse the repository at this point in the history
  • Loading branch information
David Herman committed Nov 23, 2023
1 parent e98c206 commit 9ebe1cc
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 38 deletions.
59 changes: 43 additions & 16 deletions pkgs/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12421,38 +12421,39 @@ class SourceManifest extends AbstractManifest {
}
return new BinaryManifest(json);
}
async addTargetPair(node, rust) {
async addTargetPair(pair) {
const { node, rust } = pair;
const targets = this.cfg().targets;
if (targets[node] === rust) {
return false;
return null;
}
targets[node] = rust;
await this.save();
return true;
return pair;
}
async addNodeTarget(target) {
const rt = node2Rust(target);
if (rt.length > 1) {
throw new Error(`multiple Rust targets found for Node target ${target}; please specify one of ${rt.join(', ')}`);
}
return await this.addTargetPair(target, rt[0]);
return await this.addTargetPair({ node: target, rust: rt[0] });
}
async addRustTarget(target) {
return await this.addTargetPair(rust2Node(target), target);
return await this.addTargetPair({ node: rust2Node(target), rust: target });
}
async addTargets(family) {
const targets = this.cfg().targets;
let modified = false;
let modified = [];
for (const [key, value] of Object.entries(family)) {
const node = key;
const rust = value;
if (targets[node] === rust) {
continue;
}
targets[node] = rust;
modified = true;
modified.push({ node, rust });
}
if (modified) {
if (modified.length) {
await this.save();
}
return modified;
Expand Down Expand Up @@ -12653,27 +12654,31 @@ class Tarball {





const add_target_OPTIONS = [
{ name: 'bundle', alias: 'b', type: String, defaultValue: null },
{ name: 'platform', alias: 'p', type: String, defaultValue: null },
{ name: 'arch', alias: 'a', type: String, defaultValue: null },
{ name: 'abi', type: String, defaultValue: null },
{ name: 'out-dir', alias: 'o', type: String, defaultValue: 'npm' },
{ name: 'verbose', alias: 'v', type: Boolean, defaultValue: false }
];
class AddTarget {
static summary() { return 'Add a new build target to package.json.'; }
static syntax() { return 'neon add-target [<target> | -p <plat> -a <arch> [--abi <abi>]] [-b <file>]'; }
static syntax() { return 'neon add-target [<t> | -p <p> -a <arch> [--abi <abi>]] [-o <d>] [-b <f>]'; }
static options() {
return [
{ name: '<target>', summary: 'Full target name, in either Node or Rust convention.' },
{ name: '<t>', summary: 'Full target name, in either Node or Rust convention.' },
{
name: '',
summary: 'This may be a target name in either Node or Rust convention, or one of the Neon target family presets described below. (Default: current target)'
},
{ name: '-p, --platform <plat>', summary: 'Target platform name. (Default: current platform)' },
{ name: '-p, --platform <p>', summary: 'Target platform name. (Default: current platform)' },
{ name: '-a, --arch <arch>', summary: 'Target architecture name. (Default: current arch)' },
{ name: '--abi <abi>', summary: 'Target ABI name. (Default: current ABI)' },
{ name: '-b, --bundle <file>', summary: 'File to generate bundling metadata.' },
{ name: '-o, --out-dir <d>', summary: 'Output directory for target template tree. (Default: npm)' },
{ name: '-b, --bundle <f>', summary: 'File to generate bundling metadata.' },
{
name: '',
summary: 'This generated file ensures support for bundlers (e.g. @vercel/ncc), which rely on static analysis to detect and enable any addons used by the library.'
Expand All @@ -12700,13 +12705,15 @@ class AddTarget {
_arch;
_abi;
_target;
_outDir;
_bundle;
_verbose;
constructor(argv) {
const options = dist_default()(add_target_OPTIONS, { argv, partial: true });
this._platform = options.platform || null;
this._arch = options.arch || null;
this._abi = options.abi || null;
this._outDir = options['out-dir'] || external_node_path_namespaceObject.join(process.cwd(), 'dist');
this._bundle = options.bundle || null;
this._verbose = !!options.verbose;
if (options.platform && !options.arch) {
Expand Down Expand Up @@ -12739,15 +12746,18 @@ class AddTarget {
async addTarget(sourceManifest) {
if (!this._target) {
this.log('adding default system target');
return sourceManifest.addRustTarget(await getCurrentTarget(msg => this.log(msg)));
const pair = await sourceManifest.addRustTarget(await getCurrentTarget(msg => this.log(msg)));
return pair ? [pair] : [];
}
else if (isRustTarget(this._target)) {
this.log(`adding Rust target ${this._target}`);
return sourceManifest.addRustTarget(this._target);
const pair = await sourceManifest.addRustTarget(this._target);
return pair ? [pair] : [];
}
else if (isNodeTarget(this._target)) {
this.log(`adding Node target ${this._target}`);
return sourceManifest.addNodeTarget(this._target);
const pair = await sourceManifest.addNodeTarget(this._target);
return pair ? [pair] : [];
}
else if (isTargetFamilyKey(this._target)) {
return sourceManifest.addTargets(expandTargetFamily(this._target));
Expand All @@ -12756,12 +12766,29 @@ class AddTarget {
throw new Error(`unrecognized target ${this._target}`);
}
}
async createTemplateTree(sourceManifest, pair) {
const { node, rust } = pair;
const binaryManifest = sourceManifest.manifestFor(rust);
this.log(`prebuild manifest: ${binaryManifest.stringify()}`);
const treeDir = external_node_path_namespaceObject.join(this._outDir, node);
this.log(`creating ${treeDir}`);
await promises_namespaceObject.mkdir(treeDir, { recursive: true });
this.log(`created ${treeDir}`);
this.log(`creating ${treeDir}/package.json`);
await binaryManifest.save(treeDir);
this.log(`creating ${treeDir}/README.md`);
await promises_namespaceObject.writeFile(external_node_path_namespaceObject.join(treeDir, "README.md"), `# \`${binaryManifest.name}\`\n\n${binaryManifest.description}\n`);
}
async run() {
this.log(`reading package.json`);
const sourceManifest = await SourceManifest.load();
this.log(`manifest: ${sourceManifest.stringify()}`);
if (await this.addTarget(sourceManifest)) {
const modified = await this.addTarget(sourceManifest);
if (modified.length) {
sourceManifest.updateTargets(msg => this.log(msg), this._bundle);
for (const pair of modified) {
await this.createTemplateTree(sourceManifest, pair);
}
}
}
}
Expand Down
51 changes: 41 additions & 10 deletions src/cli/src/commands/add-target.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import commandLineArgs from 'command-line-args';
import { Command, CommandDetail, CommandSection } from '../command.js';
import { expandTargetFamily, getCurrentTarget, isNodeTarget, isRustTarget, isTargetFamilyKey } from '../target.js';
import { expandTargetFamily, getCurrentTarget, isNodeTarget, isRustTarget, isTargetFamilyKey, NodeTarget, RustTarget, TargetPair } from '../target.js';
import { SourceManifest } from '../manifest.js';

const OPTIONS = [
{ name: 'bundle', alias: 'b', type: String, defaultValue: null },
{ name: 'platform', alias: 'p', type: String, defaultValue: null },
{ name: 'arch', alias: 'a', type: String, defaultValue: null },
{ name: 'abi', type: String, defaultValue: null },
{ name: 'out-dir', alias: 'o', type: String, defaultValue: 'npm' },
{ name: 'verbose', alias: 'v', type: Boolean, defaultValue: false }
];

export default class AddTarget implements Command {
static summary(): string { return 'Add a new build target to package.json.'; }
static syntax(): string { return 'neon add-target [<target> | -p <plat> -a <arch> [--abi <abi>]] [-b <file>]'; }
static syntax(): string { return 'neon add-target [<t> | -p <p> -a <arch> [--abi <abi>]] [-o <d>] [-b <f>]'; }
static options(): CommandDetail[] {
return [
{ name: '<target>', summary: 'Full target name, in either Node or Rust convention.' },
{ name: '<t>', summary: 'Full target name, in either Node or Rust convention.' },
{
name: '',
summary: 'This may be a target name in either Node or Rust convention, or one of the Neon target family presets described below. (Default: current target)'
},
{ name: '-p, --platform <plat>', summary: 'Target platform name. (Default: current platform)' },
{ name: '-p, --platform <p>', summary: 'Target platform name. (Default: current platform)' },
{ name: '-a, --arch <arch>', summary: 'Target architecture name. (Default: current arch)' },
{ name: '--abi <abi>', summary: 'Target ABI name. (Default: current ABI)' },
{ name: '-b, --bundle <file>', summary: 'File to generate bundling metadata.' },
{ name: '-o, --out-dir <d>', summary: 'Output directory for target template tree. (Default: npm)' },
{ name: '-b, --bundle <f>', summary: 'File to generate bundling metadata.' },
{
name: '',
summary: 'This generated file ensures support for bundlers (e.g. @vercel/ncc), which rely on static analysis to detect and enable any addons used by the library.'
Expand Down Expand Up @@ -52,6 +56,7 @@ export default class AddTarget implements Command {
private _arch: string | null;
private _abi: string | null;
private _target: string | null;
private _outDir: string;
private _bundle: string | null;
private _verbose: boolean;

Expand All @@ -61,6 +66,7 @@ export default class AddTarget implements Command {
this._platform = options.platform || null;
this._arch = options.arch || null;
this._abi = options.abi || null;
this._outDir = options['out-dir'] || path.join(process.cwd(), 'dist');
this._bundle = options.bundle || null;
this._verbose = !!options.verbose;

Expand Down Expand Up @@ -96,30 +102,55 @@ export default class AddTarget implements Command {
}
}

async addTarget(sourceManifest: SourceManifest): Promise<boolean> {
async addTarget(sourceManifest: SourceManifest): Promise<TargetPair[]> {
if (!this._target) {
this.log('adding default system target');
return sourceManifest.addRustTarget(await getCurrentTarget(msg => this.log(msg)));
const pair = await sourceManifest.addRustTarget(await getCurrentTarget(msg => this.log(msg)));
return pair ? [pair] : [];
} else if (isRustTarget(this._target)) {
this.log(`adding Rust target ${this._target}`);
return sourceManifest.addRustTarget(this._target);
const pair = await sourceManifest.addRustTarget(this._target);
return pair ? [pair] : [];
} else if (isNodeTarget(this._target)) {
this.log(`adding Node target ${this._target}`);
return sourceManifest.addNodeTarget(this._target);
const pair = await sourceManifest.addNodeTarget(this._target);
return pair ? [pair] : [];
} else if (isTargetFamilyKey(this._target)) {
return sourceManifest.addTargets(expandTargetFamily(this._target));
} else {
throw new Error(`unrecognized target ${this._target}`);
}
}

async createTemplateTree(sourceManifest: SourceManifest, pair: TargetPair): Promise<void> {
const { node, rust } = pair;
const binaryManifest = sourceManifest.manifestFor(rust);
this.log(`prebuild manifest: ${binaryManifest.stringify()}`);

const treeDir = path.join(this._outDir, node);

this.log(`creating ${treeDir}`);
await fs.mkdir(treeDir, { recursive: true });
this.log(`created ${treeDir}`);

this.log(`creating ${treeDir}/package.json`);
await binaryManifest.save(treeDir);

this.log(`creating ${treeDir}/README.md`);
await fs.writeFile(path.join(treeDir, "README.md"), `# \`${binaryManifest.name}\`\n\n${binaryManifest.description}\n`);
}

async run() {
this.log(`reading package.json`);
const sourceManifest = await SourceManifest.load();
this.log(`manifest: ${sourceManifest.stringify()}`);

if (await this.addTarget(sourceManifest)) {
const modified = await this.addTarget(sourceManifest);
if (modified.length) {
sourceManifest.updateTargets(msg => this.log(msg), this._bundle);
for (const pair of modified) {
await this.createTemplateTree(sourceManifest, pair);
}
}
}
}
Expand Down
25 changes: 13 additions & 12 deletions src/cli/src/manifest.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { execa } from 'execa';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { RustTarget, NodeTarget, isRustTarget, isNodeTarget, assertIsRustTarget, assertIsNodeTarget, getTargetDescriptor, node2Rust, rust2Node, TargetMap } from './target.js';
import { RustTarget, NodeTarget, isRustTarget, isNodeTarget, assertIsRustTarget, assertIsNodeTarget, getTargetDescriptor, node2Rust, rust2Node, TargetMap, TargetPair } from './target.js';

export interface BinaryCfg {
type: "binary",
Expand Down Expand Up @@ -372,33 +372,34 @@ export class SourceManifest extends AbstractManifest {
return new BinaryManifest(json);
}

async addTargetPair(node: NodeTarget, rust: RustTarget): Promise<boolean> {
async addTargetPair(pair: TargetPair): Promise<TargetPair | null> {
const { node, rust } = pair;
const targets = this.cfg().targets;

if (targets[node] === rust) {
return false;
return null;
}

targets[node] = rust;
await this.save();
return true;
return pair;
}

async addNodeTarget(target: NodeTarget): Promise<boolean> {
async addNodeTarget(target: NodeTarget): Promise<TargetPair | null> {
const rt = node2Rust(target);
if (rt.length > 1) {
throw new Error(`multiple Rust targets found for Node target ${target}; please specify one of ${rt.join(', ')}`);
}
return await this.addTargetPair(target, rt[0]);
return await this.addTargetPair({ node: target, rust: rt[0] });
}

async addRustTarget(target: RustTarget): Promise<boolean> {
return await this.addTargetPair(rust2Node(target), target);
async addRustTarget(target: RustTarget): Promise<TargetPair | null> {
return await this.addTargetPair({ node: rust2Node(target), rust: target });
}

async addTargets(family: TargetMap): Promise<boolean> {
async addTargets(family: TargetMap): Promise<TargetPair[]> {
const targets = this.cfg().targets;
let modified = false;
let modified = [];

for (const [key, value] of Object.entries(family)) {
const node: NodeTarget = key as NodeTarget;
Expand All @@ -409,10 +410,10 @@ export class SourceManifest extends AbstractManifest {
}

targets[node] = rust;
modified = true;
modified.push({ node, rust });
}

if (modified) {
if (modified.length) {
await this.save();
}

Expand Down
1 change: 1 addition & 0 deletions src/cli/src/target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function assertIsTargetFamilyKey(x: unknown): asserts x is TargetFamilyKe
}
}

export type TargetPair = { node: NodeTarget, rust: RustTarget };
export type TargetMap = { [key in NodeTarget]?: RustTarget };

export type TargetFamily =
Expand Down

0 comments on commit 9ebe1cc

Please sign in to comment.