packages/jsii-pacmak/lib/builder.ts (94 lines of code) (raw):

import { RosettaTabletReader } from 'jsii-rosetta'; import * as path from 'path'; import * as logging from './logging'; import { JsiiModule } from './packaging'; import { TargetConstructor, Target } from './target'; import { TargetName } from './targets'; import { Toposorted } from './toposort'; import { Scratch, flatten } from './util'; export interface BuildOptions { /** * Whether to fingerprint the produced artifacts. * @default true */ readonly fingerprint?: boolean; /** * Whether artifacts should be re-build even if their fingerprints look up-to-date. * @default false */ readonly force?: boolean; /** * Arguments provided by the user (how they are used is target-dependent) */ readonly arguments: { readonly [name: string]: any }; /** * Only generate code, don't build */ readonly codeOnly?: boolean; /** * Whether or not to clean */ readonly clean?: boolean; /** * Whether to add an additional subdirectory for the target language */ readonly languageSubdirectory?: boolean; /** * The Rosetta instance to load examples from */ readonly rosetta: RosettaTabletReader; /** * Whether to generate runtime type checking code in places where compile-time * type checking is not possible. */ readonly runtimeTypeChecking: boolean; } /** * Interface for classes that can build language targets * * Building can happen one target at a time, or multiple targets at a time. */ export interface TargetBuilder { buildModules(): Promise<void>; } /** * Base implementation, building the package targets for the given language independently of each other * * Some languages can gain substantial speedup in preparing an "uber project" for all packages * and compiling them all in one go (Those will be implementing a custom Builder). * * For languages where it doesn't matter--or where we haven't figured out how to * do that yet--this class can serve as a base class: it will build each package * independently, taking care to build them in the right order. */ export class IndependentPackageBuilder implements TargetBuilder { public constructor( private readonly targetName: TargetName, private readonly targetConstructor: TargetConstructor, private readonly modules: Toposorted<JsiiModule>, private readonly options: BuildOptions, ) {} public async buildModules(): Promise<void> { if (this.options.codeOnly) { await Promise.all( flatten(this.modules).map((module) => this.generateModuleCode(module, this.options), ), ); return; } for (const modules of this.modules) { // eslint-disable-next-line no-await-in-loop await Promise.all( modules.map((module) => this.buildModule(module, this.options)), ); } } private async generateModuleCode(module: JsiiModule, options: BuildOptions) { const outputDir = this.finalOutputDir(module, options); logging.debug(`Generating ${this.targetName} code into ${outputDir}`); await this.makeTarget(module, options).generateCode( outputDir, module.tarball, ); } private async buildModule(module: JsiiModule, options: BuildOptions) { const target = this.makeTarget(module, options); const outputDir = this.finalOutputDir(module, options); const src = await Scratch.make((tmpdir) => { logging.debug(`Generating ${this.targetName} code into ${tmpdir}`); return target.generateCode(tmpdir, module.tarball); }); try { logging.debug(`Building ${src.directory} into ${outputDir}`); return await target.build(src.directory, outputDir); } catch (err) { logging.warn(`Failed building ${this.targetName}`); // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors return await Promise.reject(err); } finally { if (options.clean) { logging.debug(`Cleaning ${src.directory}`); await src.cleanup(); } else { logging.info( `Generated code for ${this.targetName} retained at ${src.directory}`, ); } } } private makeTarget(module: JsiiModule, options: BuildOptions): Target { return new this.targetConstructor({ arguments: options.arguments, assembly: module.assembly, fingerprint: options.fingerprint, force: options.force, packageDir: module.moduleDirectory, rosetta: options.rosetta, runtimeTypeChecking: options.runtimeTypeChecking, targetName: this.targetName, }); } private finalOutputDir(module: JsiiModule, options: BuildOptions): string { if (options.languageSubdirectory) { return path.join(module.outputDirectory, this.targetName); } return module.outputDirectory; } }