From 5c213e5474fc8abee19746220d91f720c3c00181 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Mon, 19 Aug 2019 17:10:09 +0300 Subject: [PATCH] refactor(ngcc): abstract work orchestration/execution behind an interface (#32427) This change does not alter the current behavior, but makes it easier to introduce new types of `Executors` , for example to do the required work in parallel (on multiple processes). Inspired by/Based on @alxhub's prototype: alxhub/angular@cb631bdb1 PR Close #32427 --- .../compiler-cli/ngcc/src/execution/api.ts | 17 +++-- .../src/execution/single_process_executor.ts | 46 ++++++++++++++ .../compiler-cli/ngcc/src/execution/utils.ts | 59 ++++++++++++++++++ packages/compiler-cli/ngcc/src/main.ts | 62 ++++--------------- 4 files changed, 129 insertions(+), 55 deletions(-) create mode 100644 packages/compiler-cli/ngcc/src/execution/single_process_executor.ts create mode 100644 packages/compiler-cli/ngcc/src/execution/utils.ts diff --git a/packages/compiler-cli/ngcc/src/execution/api.ts b/packages/compiler-cli/ngcc/src/execution/api.ts index 11149101d1..e1a9eb6731 100644 --- a/packages/compiler-cli/ngcc/src/execution/api.ts +++ b/packages/compiler-cli/ngcc/src/execution/api.ts @@ -20,12 +20,21 @@ export type CompileFn = (task: Task) => void; /** The type of the function that creates the `CompileFn` function used to process tasks. */ export type CreateCompileFn = (onTaskCompleted: TaskCompletedCallback) => CompileFn; +/** Options related to the orchestration/execution of tasks. */ +export interface ExecutionOptions { + compileAllFormats: boolean; + propertiesToConsider: string[]; +} + /** - * The type of the function that orchestrates and executes the required work (i.e. analyzes the - * entry-points, processes the resulting tasks, does book-keeping and validates the final outcome). + * A class that orchestrates and executes the required work (i.e. analyzes the entry-points, + * processes the resulting tasks, does book-keeping and validates the final outcome). */ -export type ExecuteFn = - (analyzeEntryPoints: AnalyzeEntryPointsFn, createCompileFn: CreateCompileFn) => void; +export interface Executor { + execute( + analyzeEntryPoints: AnalyzeEntryPointsFn, createCompileFn: CreateCompileFn, + options: ExecutionOptions): void; +} /** Represents metadata related to the processing of an entry-point. */ export interface EntryPointProcessingMetadata { diff --git a/packages/compiler-cli/ngcc/src/execution/single_process_executor.ts b/packages/compiler-cli/ngcc/src/execution/single_process_executor.ts new file mode 100644 index 0000000000..0d54b678cc --- /dev/null +++ b/packages/compiler-cli/ngcc/src/execution/single_process_executor.ts @@ -0,0 +1,46 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Logger} from '../logging/logger'; +import {PackageJsonUpdater} from '../writing/package_json_updater'; + +import {AnalyzeEntryPointsFn, CreateCompileFn, ExecutionOptions, Executor} from './api'; +import {checkForUnprocessedEntryPoints, onTaskCompleted} from './utils'; + + +/** + * An `Executor` that processes all tasks serially and completes synchronously. + */ +export class SingleProcessExecutor implements Executor { + constructor(private logger: Logger, private pkgJsonUpdater: PackageJsonUpdater) {} + + execute( + analyzeEntryPoints: AnalyzeEntryPointsFn, createCompileFn: CreateCompileFn, + options: ExecutionOptions): void { + this.logger.debug(`Running ngcc on ${this.constructor.name}.`); + + const {processingMetadataPerEntryPoint, tasks} = analyzeEntryPoints(); + const compile = createCompileFn( + (task, outcome) => + onTaskCompleted(this.pkgJsonUpdater, processingMetadataPerEntryPoint, task, outcome)); + + // Process all tasks. + for (const task of tasks) { + const processingMeta = processingMetadataPerEntryPoint.get(task.entryPoint.path) !; + + // If we only need one format processed and we already have one for the corresponding + // entry-point, skip the task. + if (!options.compileAllFormats && processingMeta.hasAnyProcessedFormat) continue; + + compile(task); + } + + // Check for entry-points for which we could not process any format at all. + checkForUnprocessedEntryPoints(processingMetadataPerEntryPoint, options.propertiesToConsider); + } +} diff --git a/packages/compiler-cli/ngcc/src/execution/utils.ts b/packages/compiler-cli/ngcc/src/execution/utils.ts new file mode 100644 index 0000000000..c5f6e0f064 --- /dev/null +++ b/packages/compiler-cli/ngcc/src/execution/utils.ts @@ -0,0 +1,59 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {resolve} from '../../../src/ngtsc/file_system'; +import {markAsProcessed} from '../packages/build_marker'; +import {PackageJsonFormatProperties} from '../packages/entry_point'; +import {PackageJsonUpdater} from '../writing/package_json_updater'; + +import {EntryPointProcessingMetadata, Task, TaskProcessingOutcome} from './api'; + + +/** + * A helper function for checking for unprocessed entry-points (i.e. entry-points for which we could + * not process any format at all). + */ +export const checkForUnprocessedEntryPoints = + (processingMetadataPerEntryPoint: Map, + propertiesToConsider: string[]): void => { + const unprocessedEntryPointPaths = + Array.from(processingMetadataPerEntryPoint.entries()) + .filter(([, processingMeta]) => !processingMeta.hasAnyProcessedFormat) + .map(([entryPointPath]) => `\n - ${entryPointPath}`) + .join(''); + + if (unprocessedEntryPointPaths) { + throw new Error( + 'Failed to compile any formats for the following entry-points (tried ' + + `${propertiesToConsider.join(', ')}): ${unprocessedEntryPointPaths}`); + } + }; + +/** A helper function for handling a task's being completed. */ +export const onTaskCompleted = + (pkgJsonUpdater: PackageJsonUpdater, + processingMetadataPerEntryPoint: Map, task: Task, + outcome: TaskProcessingOutcome, ): void => { + const {entryPoint, formatPropertiesToMarkAsProcessed, processDts} = task; + const processingMeta = processingMetadataPerEntryPoint.get(entryPoint.path) !; + processingMeta.hasAnyProcessedFormat = true; + + if (outcome === TaskProcessingOutcome.Processed) { + const packageJsonPath = resolve(entryPoint.path, 'package.json'); + const propsToMarkAsProcessed: PackageJsonFormatProperties[] = + [...formatPropertiesToMarkAsProcessed]; + + if (processDts) { + processingMeta.hasProcessedTypings = true; + propsToMarkAsProcessed.push('typings'); + } + + markAsProcessed( + pkgJsonUpdater, entryPoint.packageJson, packageJsonPath, propsToMarkAsProcessed); + } + }; diff --git a/packages/compiler-cli/ngcc/src/main.ts b/packages/compiler-cli/ngcc/src/main.ts index 9f76ad0990..a8e00da5b7 100644 --- a/packages/compiler-cli/ngcc/src/main.ts +++ b/packages/compiler-cli/ngcc/src/main.ts @@ -16,12 +16,13 @@ import {ModuleResolver} from './dependencies/module_resolver'; import {UmdDependencyHost} from './dependencies/umd_dependency_host'; import {DirectoryWalkerEntryPointFinder} from './entry_point_finder/directory_walker_entry_point_finder'; import {TargetedEntryPointFinder} from './entry_point_finder/targeted_entry_point_finder'; -import {AnalyzeEntryPointsFn, CreateCompileFn, EntryPointProcessingMetadata, ExecuteFn, Task, TaskProcessingOutcome} from './execution/api'; +import {AnalyzeEntryPointsFn, CreateCompileFn, EntryPointProcessingMetadata, Executor, Task, TaskProcessingOutcome} from './execution/api'; +import {SingleProcessExecutor} from './execution/single_process_executor'; import {ConsoleLogger, LogLevel} from './logging/console_logger'; import {Logger} from './logging/logger'; import {hasBeenProcessed, markAsProcessed} from './packages/build_marker'; import {NgccConfiguration} from './packages/configuration'; -import {EntryPoint, EntryPointJsonProperty, EntryPointPackageJson, PackageJsonFormatProperties, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point'; +import {EntryPoint, EntryPointJsonProperty, EntryPointPackageJson, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point'; import {makeEntryPointBundle} from './packages/entry_point_bundle'; import {Transformer} from './packages/transformer'; import {PathMappings} from './utils'; @@ -189,56 +190,11 @@ export function mainNgcc( }; }; - // The function for actually planning and getting the work done. - const execute: ExecuteFn = - (analyzeEntryPoints: AnalyzeEntryPointsFn, createCompileFn: CreateCompileFn) => { - const {processingMetadataPerEntryPoint, tasks} = analyzeEntryPoints(); - const compile = createCompileFn((task, outcome) => { - const {entryPoint, formatPropertiesToMarkAsProcessed, processDts} = task; - const processingMeta = processingMetadataPerEntryPoint.get(entryPoint.path) !; - processingMeta.hasAnyProcessedFormat = true; + // The executor for actually planning and getting the work done. + const executor = getExecutor(logger, pkgJsonUpdater); + const execOpts = {compileAllFormats, propertiesToConsider}; - if (outcome === TaskProcessingOutcome.Processed) { - const packageJsonPath = fileSystem.resolve(entryPoint.path, 'package.json'); - const propsToMarkAsProcessed: PackageJsonFormatProperties[] = - [...formatPropertiesToMarkAsProcessed]; - - if (processDts) { - processingMeta.hasProcessedTypings = true; - propsToMarkAsProcessed.push('typings'); - } - - markAsProcessed( - pkgJsonUpdater, entryPoint.packageJson, packageJsonPath, propsToMarkAsProcessed); - } - }); - - // Process all tasks. - for (const task of tasks) { - const processingMeta = processingMetadataPerEntryPoint.get(task.entryPoint.path) !; - - // If we only need one format processed and we already have one for the corresponding - // entry-point, skip the task. - if (!compileAllFormats && processingMeta.hasAnyProcessedFormat) continue; - - compile(task); - } - - // Check for entry-points for which we could not process any format at all. - const unprocessedEntryPointPaths = - Array.from(processingMetadataPerEntryPoint.entries()) - .filter(([, processingMeta]) => !processingMeta.hasAnyProcessedFormat) - .map(([entryPointPath]) => `\n - ${entryPointPath}`) - .join(''); - - if (unprocessedEntryPointPaths) { - throw new Error( - 'Failed to compile any formats for the following entry-points (tried ' + - `${propertiesToConsider.join(', ')}): ${unprocessedEntryPointPaths}`); - } - }; - - return execute(analyzeEntryPoints, createCompileFn); + return executor.execute(analyzeEntryPoints, createCompileFn, execOpts); } function ensureSupportedProperties(properties: string[]): EntryPointJsonProperty[] { @@ -270,6 +226,10 @@ function getFileWriter( new InPlaceFileWriter(fs); } +function getExecutor(logger: Logger, pkgJsonUpdater: PackageJsonUpdater): Executor { + return new SingleProcessExecutor(logger, pkgJsonUpdater); +} + function getEntryPoints( fs: FileSystem, pkgJsonUpdater: PackageJsonUpdater, logger: Logger, resolver: DependencyResolver, config: NgccConfiguration, basePath: AbsoluteFsPath,