Skip to content

build: parallel secondary entry-points building #6822

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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