diff --git a/package.json b/package.json index 135a2a4fe9..6c837f4929 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ }, "// 1": "dependencies are used locally and by bazel", "dependencies": { + "@angular-devkit/architect": "^0.10.6", "@angular-devkit/core": "^7.0.4", "@angular-devkit/schematics": "^7.0.4", "@bazel/typescript": "0.21.0", diff --git a/packages/bazel/BUILD.bazel b/packages/bazel/BUILD.bazel index b1e12f19ec..e8260eaa38 100644 --- a/packages/bazel/BUILD.bazel +++ b/packages/bazel/BUILD.bazel @@ -14,6 +14,7 @@ npm_package( ], tags = ["release-with-framework"], deps = [ + "//packages/bazel/src/builders", "//packages/bazel/src/ng_package:lib", "//packages/bazel/src/ngc-wrapped:ngc_lib", "//packages/bazel/src/protractor/utils", diff --git a/packages/bazel/package.json b/packages/bazel/package.json index 2d126fc971..674abc01d9 100644 --- a/packages/bazel/package.json +++ b/packages/bazel/package.json @@ -12,6 +12,7 @@ }, "typings": "./src/ngc-wrapped/index.d.ts", "dependencies": { + "@angular-devkit/architect": "^0.10.6", "@angular-devkit/core": "^7.0.4", "@angular-devkit/schematics": "^7.0.4", "@bazel/typescript": "^0.21.0", @@ -28,6 +29,7 @@ "type": "git", "url": "https://github.com/angular/angular.git" }, + "builders": "./src/builders/builders.json", "schematics": "./src/schematics/collection.json", "ng-update": { "packageGroup": "NG_UPDATE_PACKAGE_GROUP" diff --git a/packages/bazel/src/builders/BUILD.bazel b/packages/bazel/src/builders/BUILD.bazel new file mode 100644 index 0000000000..0a4c2299d0 --- /dev/null +++ b/packages/bazel/src/builders/BUILD.bazel @@ -0,0 +1,30 @@ +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "package_assets", + srcs = [ + "builders.json", + ], + visibility = ["//packages/bazel:__subpackages__"], +) + +load("//tools:defaults.bzl", "ts_library") + +ts_library( + name = "builders", + srcs = [ + "bazel.ts", + "index.ts", + "schema.d.ts", + ], + data = [ + "schema.json", + ], + deps = [ + "@ngdeps//@angular-devkit/architect", + "@ngdeps//@angular-devkit/core", + "@ngdeps//@types/node", + "@rxjs", + "@rxjs//operators", + ], +) diff --git a/packages/bazel/src/builders/bazel.ts b/packages/bazel/src/builders/bazel.ts new file mode 100644 index 0000000000..24c5369a8d --- /dev/null +++ b/packages/bazel/src/builders/bazel.ts @@ -0,0 +1,43 @@ +/** + * @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 {spawn, spawnSync} from 'child_process'; +import {Observable, Subject} from 'rxjs'; + +export type Executable = 'bazel' | 'ibazel'; +export type Command = 'build' | 'test' | 'run' | 'coverage' | 'query'; + +export function runBazel( + projectDir: string, executable: Executable, command: Command, workspaceTarget: string, + flags: string[]): Observable { + const doneSubject = new Subject(); + const buildProcess = spawn(executable, [command, workspaceTarget, ...flags], { + cwd: projectDir, + stdio: 'inherit', + shell: false, + }); + + buildProcess.once('close', (code: number) => { + if (code === 0) { + doneSubject.next(); + } else { + doneSubject.error(`${executable} failed with code ${code}.`); + } + }); + + return doneSubject.asObservable(); +} + +export function checkInstallation(executable: Executable, projectDir: string) { + const child = spawnSync(executable, ['version'], { + cwd: projectDir, + shell: false, + }); + return child.status === 0; +} diff --git a/packages/bazel/src/builders/builders.json b/packages/bazel/src/builders/builders.json new file mode 100644 index 0000000000..3999f35086 --- /dev/null +++ b/packages/bazel/src/builders/builders.json @@ -0,0 +1,9 @@ +{ + "builders": { + "build": { + "class": "./index#Builder", + "schema": "./schema.json", + "description": "Executes Bazel on a target." + } + } +} diff --git a/packages/bazel/src/builders/index.ts b/packages/bazel/src/builders/index.ts new file mode 100644 index 0000000000..b2d88fe225 --- /dev/null +++ b/packages/bazel/src/builders/index.ts @@ -0,0 +1,40 @@ +/** + * @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 + * + * @fileoverview Bazel bundle builder + */ + +import {BuildEvent, Builder as BuilderInterface, BuilderConfiguration, BuilderContext} from '@angular-devkit/architect'; +import {getSystemPath, resolve} from '@angular-devkit/core'; +import {Observable, of } from 'rxjs'; +import {catchError, map, tap} from 'rxjs/operators'; + +import {checkInstallation, runBazel} from './bazel'; +import {Schema} from './schema'; + +export class Builder implements BuilderInterface { + constructor(private context: BuilderContext) {} + + run(builderConfig: BuilderConfiguration>): Observable { + const projectRoot = getSystemPath(resolve(this.context.workspace.root, builderConfig.root)); + const targetLabel = builderConfig.options.targetLabel; + + const executable = builderConfig.options.watch ? 'ibazel' : 'bazel'; + + if (!checkInstallation(executable, projectRoot)) { + throw new Error( + `Could not run ${executable}. Please make sure that the ` + + `"${executable}" command is available in the $PATH.`); + } + + // TODO: Support passing flags. + return runBazel( + projectRoot, executable, builderConfig.options.bazelCommand !, targetLabel !, + [] /* flags */) + .pipe(map(() => ({success: true})), catchError(() => of ({success: false})), ); + } +} diff --git a/packages/bazel/src/builders/schema.d.ts b/packages/bazel/src/builders/schema.d.ts new file mode 100644 index 0000000000..79426a2eed --- /dev/null +++ b/packages/bazel/src/builders/schema.d.ts @@ -0,0 +1,24 @@ + +// THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE THE +// CORRESPONDING JSON SCHEMA FILE, THEN RUN devkit-admin build (or bazel build ...). + +// tslint:disable:no-global-tslint-disable +// tslint:disable + +/** + * Options for Bazel Builder + */ +export interface Schema { + bazelCommand: BazelCommand; + /** + * Target to be executed under Bazel. + */ + targetLabel: string; + watch?: boolean; +} + +export enum BazelCommand { + Build = 'build', + Run = 'run', + Test = 'test', +} diff --git a/packages/bazel/src/builders/schema.json b/packages/bazel/src/builders/schema.json new file mode 100644 index 0000000000..22bdfa55a9 --- /dev/null +++ b/packages/bazel/src/builders/schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/schema", + "title": "Bazel builder schema", + "description": "Options for Bazel Builder", + "type": "object", + "properties": { + "targetLabel": { + "type": "string", + "description": "Target to be executed under Bazel." + }, + "bazelCommand": { + "type": "string", + "enum": [ + "run", + "build", + "test" + ] + }, + "watch": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false, + "required": [ + "targetLabel", + "bazelCommand" + ] +} diff --git a/tools/npm/yarn.lock b/tools/npm/yarn.lock index 47b51e66a6..b977557f4d 100644 --- a/tools/npm/yarn.lock +++ b/tools/npm/yarn.lock @@ -1693,7 +1693,7 @@ karma-sourcemap-loader@0.3.7: dependencies: graceful-fs "^4.1.2" -"karma@github:alexeagle/karma#fa1a84ac881485b5657cb669e9b4e5da77b79f0a": +karma@alexeagle/karma#fa1a84ac881485b5657cb669e9b4e5da77b79f0a: version "1.7.1" resolved "https://codeload.github.com/alexeagle/karma/tar.gz/fa1a84ac881485b5657cb669e9b4e5da77b79f0a" dependencies: diff --git a/yarn.lock b/yarn.lock index 4ede5d8d10..89ffa63f02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@angular-devkit/architect@^0.10.6": + version "0.10.6" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.10.6.tgz#7007e7591be21eeb478951106c84c83802ca21a4" + integrity sha512-IygpkXNn946vVUFFWKWEDxRqRy888vOAUWcmkZzqPEBYkuwWt7WnLfe8Sjw4fH/+HLWEMS8RXbdSTHiiaP9qOg== + dependencies: + "@angular-devkit/core" "7.0.6" + rxjs "6.3.3" + "@angular-devkit/core@7.0.5", "@angular-devkit/core@^7.0.4": version "7.0.5" resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.0.5.tgz#60866c0a2367cea44b436a359ac9be99a138e180" @@ -13,6 +21,17 @@ rxjs "6.3.3" source-map "0.7.3" +"@angular-devkit/core@7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.0.6.tgz#26c4cd4d271e8cd03f6e50b4ec30cbc606f3346e" + integrity sha512-RPSXUtLrpYDTqAEL0rCyDKxES76EomsPBvUUZTD6UkE2pihoh9ZIxkzhzlE+HU/xdqm28+smQYFhvvEAXFWwSQ== + dependencies: + ajv "6.5.3" + chokidar "2.0.4" + fast-json-stable-stringify "2.0.0" + rxjs "6.3.3" + source-map "0.7.3" + "@angular-devkit/schematics@7.0.5", "@angular-devkit/schematics@^7.0.4": version "7.0.5" resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.0.5.tgz#527bf0af5352172e92c5473a33bc07af44c77796"