Skip to content

Commit

Permalink
feat(@angular/cli): initial minimal BEP support
Browse files Browse the repository at this point in the history
  • Loading branch information
clydin authored and kyliau committed Dec 11, 2018
1 parent c83be5e commit f066e99
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 9 deletions.
11 changes: 10 additions & 1 deletion packages/angular/cli/commands/build.json
Expand Up @@ -11,6 +11,15 @@

"allOf": [
{ "$ref": "./definitions.json#/definitions/architect" },
{ "$ref": "./definitions.json#/definitions/base" }
{ "$ref": "./definitions.json#/definitions/base" },
{
"type": "object",
"properties": {
"buildEventLog": {
"type": "string",
"description": "(experimental) Output file path for Build Event Protocol events"
}
}
}
]
}
11 changes: 10 additions & 1 deletion packages/angular/cli/commands/serve.json
Expand Up @@ -12,6 +12,15 @@
"type": "object",
"allOf": [
{ "$ref": "./definitions.json#/definitions/architect" },
{ "$ref": "./definitions.json#/definitions/base" }
{ "$ref": "./definitions.json#/definitions/base" },
{
"type": "object",
"properties": {
"buildEventLog": {
"type": "string",
"description": "(experimental) Output file path for Build Event Protocol events"
}
}
}
]
}
62 changes: 55 additions & 7 deletions packages/angular/cli/models/architect-command.ts
Expand Up @@ -7,11 +7,13 @@
*/
import {
Architect,
BuilderConfiguration,
BuilderContext,
TargetSpecifier,
} from '@angular-devkit/architect';
import { experimental, json, schema, tags } from '@angular-devkit/core';
import { NodeJsSyncHost, createConsoleLogger } from '@angular-devkit/core/node';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import { BepJsonWriter } from '../utilities/bep';
import { parseJsonSchemaToOptions } from '../utilities/json-schema';
import { BaseCommandOptions, Command } from './command';
import { Arguments, Option } from './interface';
Expand Down Expand Up @@ -169,14 +171,46 @@ export abstract class ArchitectCommand<
return await this.runArchitectTarget(options);
}

protected async runSingleTarget(targetSpec: TargetSpecifier, options: string[]) {
protected async runBepTarget<T>(
command: string,
configuration: BuilderConfiguration<T>,
buildEventLog: string,
): Promise<number> {
const bep = new BepJsonWriter(buildEventLog);

// Send start
bep.writeBuildStarted(command);

let last = 1;
let rebuild = false;
await this._architect.run(configuration, { logger: this.logger }).forEach(event => {
last = event.success ? 0 : 1;

if (rebuild) {
// NOTE: This will have an incorrect timestamp but this cannot be fixed
// until builders report additional status events
bep.writeBuildStarted(command);
} else {
rebuild = true;
}

bep.writeBuildFinished(last);
});

return last;
}

protected async runSingleTarget(
targetSpec: TargetSpecifier,
targetOptions: string[],
commandOptions: ArchitectCommandOptions & Arguments) {
// We need to build the builderSpec twice because architect does not understand
// overrides separately (getting the configuration builds the whole project, including
// overrides).
const builderConf = this._architect.getBuilderConfiguration(targetSpec);
const builderDesc = await this._architect.getBuilderDescription(builderConf).toPromise();
const targetOptionArray = await parseJsonSchemaToOptions(this._registry, builderDesc.schema);
const overrides = parseArguments(options, targetOptionArray, this.logger);
const overrides = parseArguments(targetOptions, targetOptionArray, this.logger);

if (overrides['--']) {
(overrides['--'] || []).forEach(additional => {
Expand All @@ -190,9 +224,23 @@ export abstract class ArchitectCommand<
logger: this.logger,
targetSpecifier: targetSpec,
};
const result = await this._architect.run(realBuilderConf, builderContext).toPromise();

return result.success ? 0 : 1;
if (commandOptions.buildEventLog && ['build', 'serve'].includes(this.description.name)) {
// The build/serve commands supports BEP messaging
this.logger.warn('BEP support is experimental and subject to change.');

return this.runBepTarget(
this.description.name,
realBuilderConf,
commandOptions.buildEventLog as string,
);
} else {
const result = await this._architect
.run(realBuilderConf, builderContext)
.toPromise();

return result.success ? 0 : 1;
}
}

protected async runArchitectTarget(
Expand All @@ -207,12 +255,12 @@ export abstract class ArchitectCommand<
// Running them in parallel would jumble the log messages.
let result = 0;
for (const project of this.getProjectNamesByTarget(this.target)) {
result |= await this.runSingleTarget({ ...targetSpec, project }, extra);
result |= await this.runSingleTarget({ ...targetSpec, project }, extra, options);
}

return result;
} else {
return await this.runSingleTarget(targetSpec, extra);
return await this.runSingleTarget(targetSpec, extra, options);
}
} catch (e) {
if (e instanceof schema.SchemaValidationException) {
Expand Down
68 changes: 68 additions & 0 deletions packages/angular/cli/utilities/bep.ts
@@ -0,0 +1,68 @@
/**
* @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 * as fs from 'fs';


export interface BuildEventMessage {
id: {};
[key: string]: {};
}

export class BepGenerator {
private constructor() {}

static createBuildStarted(command: string, time?: number): BuildEventMessage {
return {
id: { started: {} },
started: {
command,
start_time_millis: time == undefined ? Date.now() : time,
},
};
}

static createBuildFinished(code: number, time?: number): BuildEventMessage {
return {
id: { finished: {} },
finished: {
finish_time_millis: time == undefined ? Date.now() : time,
exit_code: { code },
},
};
}
}

export class BepJsonWriter {
private stream = fs.createWriteStream(this.filename);

constructor(public readonly filename: string) {

}

close(): void {
this.stream.close();
}

writeEvent(event: BuildEventMessage): void {
const raw = JSON.stringify(event);

this.stream.write(raw + '\n');
}

writeBuildStarted(command: string, time?: number): void {
const event = BepGenerator.createBuildStarted(command, time);

this.writeEvent(event);
}

writeBuildFinished(code: number, time?: number): void {
const event = BepGenerator.createBuildFinished(code, time);

this.writeEvent(event);
}
}

0 comments on commit f066e99

Please sign in to comment.