Skip to content

Commit

Permalink
build: parallel secondary entry-points building
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
devversion committed Sep 3, 2017
1 parent 1b6b270 commit b26ce62
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 32 deletions.
34 changes: 22 additions & 12 deletions tools/package-tools/build-package.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import {join} from 'path';
import {main as tsc} from '@angular/tsc-wrapped';
import {buildConfig} from './build-config';
import {getSecondaryEntryPointsForPackage} from './secondary-entry-points';
import {buildPrimaryEntryPointBundles, buildSecondaryEntryPointBundles} from './build-bundles';

import {main as ngc} from '@angular/tsc-wrapped';

const {packagesDir, outputDir} = buildConfig;

Expand Down Expand Up @@ -31,15 +30,21 @@ export class BuildPackage {
private tsconfigTests: string;

/** Secondary entry points for the package. */
get secondaryEntryPoints(): string[] {
get secondaryEntryPoints(): string[][] {
if (!this._secondaryEntryPoints) {
this._secondaryEntryPoints = getSecondaryEntryPointsForPackage(this);
}

return this._secondaryEntryPoints;
}
private _secondaryEntryPoints: string[][];

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

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

// Walk through every secondary entry point and build the TypeScript sources sequentially.
for (const entryPoint of this.secondaryEntryPoints) {
await this._compileEntryPoint(buildTsconfigName, entryPoint);
// Secondary entry points are built in batches. Meaning that some packages will be built
// in parallel while other packages have to be built sequentially.
for (const entryPointsLevel of this.secondaryEntryPoints) {
await Promise.all(entryPointsLevel.map(entryPoint => {
return this._compileEntryPoint(buildTsconfigName, entryPoint);
}));
}
}

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

for (const entryPoint of this.secondaryEntryPoints) {
const entryPointEntryFilePath = join(this.packageOut, entryPoint, 'index.js');
await buildSecondaryEntryPointBundles(entryPointEntryFilePath, this.packageName, entryPoint);
for (const entryPointLevel of this.secondaryEntryPoints) {
await Promise.all(entryPointLevel.map(entryPoint => {
const entryPointEntryPath = join(this.packageOut, entryPoint, 'index.js');
return buildSecondaryEntryPointBundles(entryPointEntryPath, this.packageName, entryPoint);
}));
}
}

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

await tsc(entryPointTsconfigPath, {basePath: entryPointPath});
return ngc(entryPointTsconfigPath, {basePath: entryPointPath});
}
}
3 changes: 1 addition & 2 deletions tools/package-tools/build-release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {replaceVersionPlaceholders} from './version-placeholders';
import {inlinePackageMetadataFiles} from './metadata-inlining';
import {createTypingsReexportFile} from './typings-reexport';
import {createMetadataReexportFile} from './metadata-reexport';
import {getSecondaryEntryPointsForPackage} from './secondary-entry-points';
import {createEntryPointPackageJson} from './entry-point-package-json';
import {buildConfig} from './build-config';
import {BuildPackage} from './build-package';
Expand Down Expand Up @@ -47,7 +46,7 @@ export function composeRelease(buildPackage: BuildPackage) {
function createFilesForSecondaryEntryPoint(buildPackage: BuildPackage, releasePath: string) {
const {packageName, packageOut} = buildPackage;

getSecondaryEntryPointsForPackage(buildPackage).forEach(entryPointName => {
buildPackage.flatSecondaryEntryPoints.forEach(entryPointName => {
// Create a directory in the root of the package for this entry point that contains
// * A package.json that lists the different bundle locations
// * An index.d.ts file that re-exports the index.d.ts from the typings/ directory
Expand Down
46 changes: 28 additions & 18 deletions tools/package-tools/secondary-entry-points.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,36 @@ export function getSecondaryEntryPointsForPackage(pkg: BuildPackage) {
.map(depName => nodeLookup.get(depName)!) || [];
});

// Concatenate the build order for each node into one global build order.
// Duplicates are automatically omitted by getBuildOrder.
return buildNodes.reduce((order: string[], node) => {
return [...order, ...getBuildOrder(node)];
}, []);
}
const buildOrder: string[][] = [];

/** Gets the build order for a given node with DFS. */
function getBuildOrder(node: BuildNode): string[] {
if (node.visited) {
return [];
}
const addNodeToLevel = (node: BuildNode, level: number): number => {
node.visited = true;
node.level = level;

let buildOrder: string[] = [];
for (const dep of node.deps) {
buildOrder = [...buildOrder, ...getBuildOrder(dep)];
}
buildOrder[level] = [...buildOrder[level] || [], node.name];

return level;
};

const sortDependencies = (node: BuildNode): number => {
if (node.visited) {
return node.level || 0;
} else if (!node.deps.length) {
return addNodeToLevel(node, 0);
}

node.visited = true;
return [...buildOrder, node.name];
const dependencyDepth = 1 + node.deps
.map(dep => sortDependencies(dep))
.sort((a, b) => a - b).slice(-1)[0];

addNodeToLevel(node, dependencyDepth);

return dependencyDepth;
};

buildNodes.forEach(node => sortDependencies(node));

return buildOrder;
}

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


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

0 comments on commit b26ce62

Please sign in to comment.