Pete Bacon Darwin 1790b63a5d refactor(ngcc): expose the TaskDependencies mapping on BaseTaskQueue (#36083)
Later when we implement the ability to continue processing when tasks have
failed to compile, we will also need to avoid processing tasks that depend
upon the failed task.

This refactoring exposes this list of dependent tasks in a way that can be
used to skip processing of tasks that depend upon a failed task.

It also changes the blocking model of the parallel mode of operation so
that non-typings tasks are now blocked on their corresponding typings task.
Previously the non-typings tasks could be triggered to run in parallel to
the typings task, since they do not have a hard dependency on each other,
but this made it difficult to skip task correctly if the typings task failed,
since it was possible that a non-typings task was already in flight when
the typings task failed. The result of this is a small potential degradation
of performance in async parallel processing mode, in the rare cases that
there were not enough unblocked tasks to make use of all the available
workers.

PR Close #36083
2020-03-18 15:56:21 -07:00

78 lines
3.4 KiB
TypeScript

/**
* @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 {DepGraph} from 'dependency-graph';
import {PartiallyOrderedTasks, Task} from '../../src/execution/tasks/api';
import {EntryPoint} from '../../src/packages/entry_point';
/**
* Create a set of tasks and a graph of their interdependencies.
*
* NOTE 1: The first task for each entry-point generates typings (which is similar to what happens
* in the actual code).
* NOTE 2: The `computeTaskDependencies()` implementation relies on the fact that tasks are sorted in such
* a way that a task can only depend upon earlier tasks (i.e. dependencies always come
* before dependents in the list of tasks).
* To preserve this attribute, you need to ensure that entry-points will only depend on
* entry-points with a lower index. Take this into account when defining `entryPointDeps`.
* (Failing to do so, will result in an error.)
*
* @param entryPointCount The number of different entry-points to mock.
* @param tasksPerEntryPointCount The number of tasks to generate per entry-point (i.e. simulating
* processing multiple format properties).
* @param entryPointDeps An object mapping an entry-point to its dependencies. Keys are
* entry-point indices and values are arrays of entry-point indices that the
* entry-point corresponding to the key depends on.
* For example, if entry-point #2 depends on entry-points #0 and #1,
* `entryPointDeps` would be `{2: [0, 1]}`.
* @return An object with the following properties:
* - `tasks`: The (partially ordered) list of generated mock tasks.
* - `graph`: The dependency graph for the generated mock entry-point.
*/
export function createTasksAndGraph(
entryPointCount: number, tasksPerEntryPointCount = 1,
entryPointDeps: {[entryPointIndex: string]: number[]} = {}):
{tasks: PartiallyOrderedTasks, graph: DepGraph<EntryPoint>} {
const entryPoints: EntryPoint[] = [];
const tasks: PartiallyOrderedTasks = [] as any;
const graph = new DepGraph<EntryPoint>();
// Create the entry-points and the associated tasks.
for (let epIdx = 0; epIdx < entryPointCount; epIdx++) {
const entryPoint = {
name: `entry-point-${epIdx}`,
path: `/path/to/entry/point/${epIdx}`,
} as EntryPoint;
entryPoints.push(entryPoint);
graph.addNode(entryPoint.path);
for (let tIdx = 0; tIdx < tasksPerEntryPointCount; tIdx++) {
tasks.push({ entryPoint, formatProperty: `prop-${tIdx}`, processDts: tIdx === 0 } as Task);
}
}
// Define entry-point interdependencies.
for (const epIdx of Object.keys(entryPointDeps).map(strIdx => +strIdx)) {
const fromPath = entryPoints[epIdx].path;
for (const depIdx of entryPointDeps[epIdx]) {
// Ensure that each entry-point only depends on entry-points at a lower index.
if (depIdx >= epIdx) {
throw Error(
'Invalid `entryPointDeps`: Entry-points can only depend on entry-points at a lower ' +
`index, but entry-point #${epIdx} depends on #${depIdx} in: ` +
JSON.stringify(entryPointDeps, null, 2));
}
const toPath = entryPoints[depIdx].path;
graph.addDependency(fromPath, toPath);
}
}
return {tasks, graph};
}