Skip to content

Commit c96fff7

Browse files
committed
build: parallel secondary entry-points building
All secondary entry-points of the CDK are no longer built sequentially. Secondary entry-points will be resolved in batches that can be compiled in parallel. For example: ``` [ [ 'coercion', 'platform', 'rxjs', 'keycodes', 'bidi', 'collections', 'portal' ], [ 'a11y', 'observers', 'scrolling', 'stepper', 'table' ], [ 'overlay' ] ``` This should improve the build time of the CDK package.
1 parent 1b6b270 commit c96fff7

File tree

3 files changed

+51
-31
lines changed

3 files changed

+51
-31
lines changed

tools/package-tools/build-package.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import {join} from 'path';
2-
import {main as tsc} from '@angular/tsc-wrapped';
32
import {buildConfig} from './build-config';
43
import {getSecondaryEntryPointsForPackage} from './secondary-entry-points';
54
import {buildPrimaryEntryPointBundles, buildSecondaryEntryPointBundles} from './build-bundles';
6-
5+
import {main as ngc} from '@angular/tsc-wrapped';
76

87
const {packagesDir, outputDir} = buildConfig;
98

@@ -31,15 +30,21 @@ export class BuildPackage {
3130
private tsconfigTests: string;
3231

3332
/** Secondary entry points for the package. */
34-
get secondaryEntryPoints(): string[] {
33+
get secondaryEntryPoints(): string[][] {
3534
if (!this._secondaryEntryPoints) {
3635
this._secondaryEntryPoints = getSecondaryEntryPointsForPackage(this);
3736
}
3837

3938
return this._secondaryEntryPoints;
4039
}
40+
private _secondaryEntryPoints: string[][];
4141

42-
private _secondaryEntryPoints: string[];
42+
/** Flat list of secondary entry-points for the build package. */
43+
get flatSecondaryEntryPoints(): string[] {
44+
return this.secondaryEntryPoints.reduce((entryPoints: string[], entryPointLevel: string[]) => {
45+
return [...entryPoints, ...entryPointLevel];
46+
}, []);
47+
}
4348

4449
constructor(public packageName: string, public dependencies: BuildPackage[] = []) {
4550
this.packageRoot = join(packagesDir, packageName);
@@ -55,9 +60,12 @@ export class BuildPackage {
5560
async compile() {
5661
await this._compileEntryPoint(buildTsconfigName);
5762

58-
// Walk through every secondary entry point and build the TypeScript sources sequentially.
59-
for (const entryPoint of this.secondaryEntryPoints) {
60-
await this._compileEntryPoint(buildTsconfigName, entryPoint);
63+
// Secondary entry points are built in batches. Meaning that some packages will be built
64+
// in parallel while other packages have to be built sequentially.
65+
for (const entryPointsLevel of this.secondaryEntryPoints) {
66+
await Promise.all(entryPointsLevel.map(entryPoint => {
67+
return this._compileEntryPoint(buildTsconfigName, entryPoint);
68+
}));
6169
}
6270
}
6371

@@ -70,17 +78,19 @@ export class BuildPackage {
7078
async createBundles() {
7179
await buildPrimaryEntryPointBundles(this.entryFilePath, this.packageName);
7280

73-
for (const entryPoint of this.secondaryEntryPoints) {
74-
const entryPointEntryFilePath = join(this.packageOut, entryPoint, 'index.js');
75-
await buildSecondaryEntryPointBundles(entryPointEntryFilePath, this.packageName, entryPoint);
81+
for (const entryPointLevel of this.secondaryEntryPoints) {
82+
await Promise.all(entryPointLevel.map(entryPoint => {
83+
const entryPointEntryPath = join(this.packageOut, entryPoint, 'index.js');
84+
return buildSecondaryEntryPointBundles(entryPointEntryPath, this.packageName, entryPoint);
85+
}));
7686
}
7787
}
7888

7989
/** Compiles the TypeScript sources of a primary or secondary entry point. */
80-
private async _compileEntryPoint(tsconfigName: string, secondaryEntryPoint?: string) {
90+
private _compileEntryPoint(tsconfigName: string, secondaryEntryPoint?: string) {
8191
const entryPointPath = join(this.packageRoot, secondaryEntryPoint || '');
8292
const entryPointTsconfigPath = join(entryPointPath, tsconfigName);
8393

84-
await tsc(entryPointTsconfigPath, {basePath: entryPointPath});
94+
return ngc(entryPointTsconfigPath, {basePath: entryPointPath});
8595
}
8696
}

tools/package-tools/build-release.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function composeRelease(buildPackage: BuildPackage) {
4747
function createFilesForSecondaryEntryPoint(buildPackage: BuildPackage, releasePath: string) {
4848
const {packageName, packageOut} = buildPackage;
4949

50-
getSecondaryEntryPointsForPackage(buildPackage).forEach(entryPointName => {
50+
buildPackage.flatSecondaryEntryPoints.forEach(entryPointName => {
5151
// Create a directory in the root of the package for this entry point that contains
5252
// * A package.json that lists the different bundle locations
5353
// * An index.d.ts file that re-exports the index.d.ts from the typings/ directory

tools/package-tools/secondary-entry-points.ts

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,36 @@ export function getSecondaryEntryPointsForPackage(pkg: BuildPackage) {
5252
.map(depName => nodeLookup.get(depName)!) || [];
5353
});
5454

55-
// Concatenate the build order for each node into one global build order.
56-
// Duplicates are automatically omitted by getBuildOrder.
57-
return buildNodes.reduce((order: string[], node) => {
58-
return [...order, ...getBuildOrder(node)];
59-
}, []);
60-
}
55+
const buildOrder: string[][] = [];
6156

62-
/** Gets the build order for a given node with DFS. */
63-
function getBuildOrder(node: BuildNode): string[] {
64-
if (node.visited) {
65-
return [];
66-
}
57+
const addNodeToLevel = (node: BuildNode, level: number): number => {
58+
node.visited = true;
59+
node.level = level;
6760

68-
let buildOrder: string[] = [];
69-
for (const dep of node.deps) {
70-
buildOrder = [...buildOrder, ...getBuildOrder(dep)];
71-
}
61+
buildOrder[level] = [...buildOrder[level] || [], node.name];
62+
63+
return level;
64+
};
65+
66+
const sortDependencies = (node: BuildNode): number => {
67+
if (node.visited) {
68+
return node.level || 0;
69+
} else if (!node.deps.length) {
70+
return addNodeToLevel(node, 0);
71+
}
7272

73-
node.visited = true;
74-
return [...buildOrder, node.name];
73+
const dependencyDepth = 1 + node.deps
74+
.map(dep => sortDependencies(dep))
75+
.sort((a, b) => a - b).slice(-1)[0];
76+
77+
addNodeToLevel(node, dependencyDepth);
78+
79+
return dependencyDepth;
80+
};
81+
82+
buildNodes.forEach(node => sortDependencies(node));
83+
84+
return buildOrder;
7585
}
7686

7787
/** Gets the names of all subdirectories for a given path. */
@@ -84,9 +94,9 @@ interface BuildNode {
8494
name: string;
8595
deps: BuildNode[];
8696
visited?: boolean;
97+
level?: number;
8798
}
8899

89-
90100
/** Builds the command that will be executed to find all import statements for a package. */
91101
function buildPackageImportStatementFindCommand(searchDirectory: string, packageName: string) {
92102
if (platform() === 'win32') {

0 commit comments

Comments
 (0)