refactor(compiler-cli): move ngc-wrapped to packages/bazel (#18733)
See design: https://goo.gl/rAeYWx PR Close #18733
This commit is contained in:
1
packages/bazel/BUILD.bazel
Normal file
1
packages/bazel/BUILD.bazel
Normal file
@ -0,0 +1 @@
|
||||
# Empty marker file, indicating this directory is a Bazel package.
|
21
packages/bazel/WORKSPACE
Normal file
21
packages/bazel/WORKSPACE
Normal file
@ -0,0 +1,21 @@
|
||||
# By convention, the name should "describe the project in reverse-DNS form"
|
||||
# https://docs.bazel.build/versions/master/be/functions.html#workspace
|
||||
# But if we use "io_angular" then the loads used in BUILD files will
|
||||
# be unfamiliar to Angular users who import from '@angular/pkg' in
|
||||
# TypeScript files. We want to reduce the impedance between the Bazel
|
||||
# and node naming schemes.
|
||||
# We take the name "angular" so that users can write
|
||||
# load("@angular//:index.bzl", "ng_module")
|
||||
workspace(name = "angular")
|
||||
|
||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
|
||||
git_repository(
|
||||
name = "build_bazel_rules_typescript",
|
||||
remote = "https://github.com/bazelbuild/rules_typescript.git",
|
||||
tag = "0.0.5",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "node_repositories")
|
||||
|
||||
node_repositories(package_json = ":package.json")
|
9
packages/bazel/index.bzl
Normal file
9
packages/bazel/index.bzl
Normal file
@ -0,0 +1,9 @@
|
||||
# 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
|
||||
""" Public API surface is re-exported here.
|
||||
Users should not load files under "/src"
|
||||
"""
|
||||
|
||||
load("//src:ng_module.bzl", "ng_module")
|
15
packages/bazel/package.json
Normal file
15
packages/bazel/package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "@angular/bazel",
|
||||
"version": "0.0.0-PLACEHOLDER",
|
||||
"description": "Angular - bazel build rules",
|
||||
"author": "angular",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@angular/compiler-cli": "0.0.0-PLACEHOLDER",
|
||||
"typescript": "~2.3"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.git"
|
||||
}
|
||||
}
|
1
packages/bazel/src/BUILD.bazel
Normal file
1
packages/bazel/src/BUILD.bazel
Normal file
@ -0,0 +1 @@
|
||||
# Empty marker file, indicating this directory is a Bazel package.
|
155
packages/bazel/src/ng_module.bzl
Normal file
155
packages/bazel/src/ng_module.bzl
Normal file
@ -0,0 +1,155 @@
|
||||
# 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
|
||||
|
||||
load("@build_bazel_rules_typescript//internal:build_defs.bzl", "tsc_wrapped_tsconfig")
|
||||
|
||||
load(
|
||||
"@build_bazel_rules_typescript//internal:common/compilation.bzl",
|
||||
"COMMON_ATTRIBUTES", "compile_ts", "ts_providers_dict_to_struct"
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_typescript//internal:common/json_marshal.bzl", "json_marshal")
|
||||
|
||||
# Calculate the expected output of the template compiler for every source in
|
||||
# in the library. Most of these will be produced as empty files but it is
|
||||
# unknown, without parsing, which will be empty.
|
||||
def _expected_outs(ctx):
|
||||
result = []
|
||||
|
||||
for src in ctx.files.srcs:
|
||||
if src.short_path.endswith(".ts"):
|
||||
basename = src.short_path[len(ctx.label.package) + 1:-3]
|
||||
result += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in [
|
||||
".ngfactory.js",
|
||||
".ngfactory.d.ts",
|
||||
".ngsummary.js",
|
||||
".ngsummary.d.ts",
|
||||
".ngsummary.json",
|
||||
]]
|
||||
elif src.short_path.endswith(".css"):
|
||||
basename = src.short_path[len(ctx.label.package) + 1:-4]
|
||||
result += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in [
|
||||
".css.shim.ngstyle.js",
|
||||
".css.shim.ngstyle.d.ts",
|
||||
".css.ngstyle.js",
|
||||
".css.ngstyle.d.ts",
|
||||
]]
|
||||
return result
|
||||
|
||||
def _ngc_tsconfig(ctx, files, srcs, **kwargs):
|
||||
return dict(tsc_wrapped_tsconfig(ctx, files, srcs, **kwargs), **{
|
||||
"angularCompilerOptions": {
|
||||
"expectedOut": [o.path for o in _expected_outs(ctx)],
|
||||
}
|
||||
})
|
||||
|
||||
def _compile_action(ctx, inputs, outputs, config_file_path):
|
||||
externs_files = []
|
||||
non_externs_files = []
|
||||
for output in outputs:
|
||||
if output.basename.endswith(".es5.MF"):
|
||||
ctx.file_action(output, content="")
|
||||
else:
|
||||
non_externs_files.append(output)
|
||||
|
||||
# TODO(alexeagle): For now we mock creation of externs files
|
||||
for externs_file in externs_files:
|
||||
ctx.file_action(output=externs_file, content="")
|
||||
|
||||
action_inputs = inputs
|
||||
if hasattr(ctx.attr, "node_modules"):
|
||||
action_inputs += [f for f in ctx.files.node_modules
|
||||
if f.path.endswith(".ts") or f.path.endswith(".json")]
|
||||
if ctx.file.tsconfig:
|
||||
action_inputs += [ctx.file.tsconfig]
|
||||
|
||||
# One at-sign makes this a params-file, enabling the worker strategy.
|
||||
# Two at-signs escapes the argument so it's passed through to ngc
|
||||
# rather than the contents getting expanded.
|
||||
if ctx.attr.supports_workers:
|
||||
arguments = ["@@" + config_file_path]
|
||||
else:
|
||||
arguments = ["-p", config_file_path]
|
||||
|
||||
ctx.action(
|
||||
progress_message = "Compiling Angular templates (ngc) %s" % ctx.label,
|
||||
mnemonic = "AngularTemplateCompile",
|
||||
inputs = action_inputs,
|
||||
outputs = non_externs_files,
|
||||
arguments = arguments,
|
||||
executable = ctx.executable.compiler,
|
||||
execution_requirements = {
|
||||
"supports-workers": str(int(ctx.attr.supports_workers)),
|
||||
},
|
||||
)
|
||||
|
||||
def _devmode_compile_action(ctx, inputs, outputs, config_file_path):
|
||||
# TODO(alexeagle): compile for feeding to Closure Compiler
|
||||
_compile_action(ctx, inputs, outputs + _expected_outs(ctx), config_file_path)
|
||||
|
||||
def _compile_ng(ctx):
|
||||
declarations = []
|
||||
for dep in ctx.attr.deps:
|
||||
if hasattr(dep, "typescript"):
|
||||
declarations += dep.typescript.transitive_declarations
|
||||
|
||||
tsconfig_json = ctx.new_file(ctx.label.name + "_tsconfig.json")
|
||||
ctx.file_action(output=tsconfig_json, content=json_marshal(
|
||||
_ngc_tsconfig(ctx, ctx.files.srcs + declarations, ctx.files.srcs)))
|
||||
|
||||
_devmode_compile_action(ctx, ctx.files.srcs + declarations + [tsconfig_json], [], tsconfig_json.path)
|
||||
|
||||
return {
|
||||
"files": depset(_expected_outs(ctx)),
|
||||
"typescript": {
|
||||
# FIXME: expose the right outputs so this looks like a ts_library
|
||||
"declarations": [],
|
||||
"transitive_declarations": [],
|
||||
"type_blacklisted_declarations": [],
|
||||
},
|
||||
}
|
||||
|
||||
def _ng_module_impl(ctx):
|
||||
if ctx.attr.write_ng_outputs_only:
|
||||
ts_providers = _compile_ng(ctx)
|
||||
else:
|
||||
ts_providers = compile_ts(ctx, is_library=True,
|
||||
compile_action=_compile_action,
|
||||
devmode_compile_action=_devmode_compile_action,
|
||||
tsc_wrapped_tsconfig=_ngc_tsconfig)
|
||||
|
||||
addl_declarations = [o for o in _expected_outs(ctx) if o.path.endswith(".d.ts")]
|
||||
ts_providers["typescript"]["declarations"] += addl_declarations
|
||||
ts_providers["typescript"]["transitive_declarations"] += addl_declarations
|
||||
|
||||
return ts_providers_dict_to_struct(ts_providers)
|
||||
|
||||
|
||||
ng_module = rule(
|
||||
implementation = _ng_module_impl,
|
||||
attrs = dict(COMMON_ATTRIBUTES, **{
|
||||
"srcs": attr.label_list(allow_files = True),
|
||||
|
||||
# To be used only to bootstrap @angular/core compilation,
|
||||
# since we want to compile @angular/core with ngc, but ngc depends on
|
||||
# @angular/core typescript output.
|
||||
"write_ng_outputs_only": attr.bool(default = False),
|
||||
"tsconfig": attr.label(allow_files = True, single_file = True),
|
||||
"no_i18n": attr.bool(default = False),
|
||||
# TODO(alexeagle): enable workers for ngc
|
||||
"supports_workers": attr.bool(default = False),
|
||||
"compiler": attr.label(
|
||||
default = Label("//internal/ngc"),
|
||||
executable = True,
|
||||
cfg = "host",
|
||||
),
|
||||
# @// is special syntax for the "main" repository
|
||||
# The default assumes the user specified a target "node_modules" in their
|
||||
# root BUILD file.
|
||||
"node_modules": attr.label(
|
||||
default = Label("@//:node_modules")
|
||||
),
|
||||
}),
|
||||
)
|
19
packages/bazel/src/ngc-wrapped/BUILD.bazel
Normal file
19
packages/bazel/src/ngc-wrapped/BUILD.bazel
Normal file
@ -0,0 +1,19 @@
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library", "nodejs_binary")
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
ts_library(
|
||||
name = "ngc_lib",
|
||||
srcs = ["index.ts"],
|
||||
deps = [
|
||||
"//packages/compiler-cli",
|
||||
"@build_bazel_rules_typescript//internal/tsc_wrapped"
|
||||
],
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = "ngc-wrapped",
|
||||
entry_point = "__main__/packages/bazel/src/ngc-wrapped/index.js",
|
||||
data = [":ngc_lib"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
3
packages/bazel/src/ngc-wrapped/README.md
Normal file
3
packages/bazel/src/ngc-wrapped/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# ngc-wrapped
|
||||
|
||||
This is a wrapper around @angular/compiler-cli that makes ngc run under Bazel.
|
35
packages/bazel/src/ngc-wrapped/index.ts
Normal file
35
packages/bazel/src/ngc-wrapped/index.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
// TODO(chuckj): Remove the requirement for a fake 'reflect` implementation from
|
||||
// the compiler
|
||||
import 'reflect-metadata';
|
||||
|
||||
import {calcProjectFileAndBasePath, createNgCompilerOptions, formatDiagnostics, performCompilation} from '@angular/compiler-cli';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
// Note, the tsc_wrapped module comes from rules_typescript, not from @angular/tsc-wrapped
|
||||
import {parseTsconfig} from 'tsc_wrapped';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
function main(args: string[]) {
|
||||
const project = args[1];
|
||||
const [{options: tsOptions, bazelOpts, files, config}] = parseTsconfig(project);
|
||||
const {basePath} = calcProjectFileAndBasePath(project);
|
||||
const ngOptions = createNgCompilerOptions(basePath, config, tsOptions);
|
||||
|
||||
const {diagnostics} = performCompilation({rootNames: files, options: ngOptions});
|
||||
if (diagnostics.length) {
|
||||
console.error(formatDiagnostics(ngOptions, diagnostics));
|
||||
}
|
||||
return diagnostics.some(d => d.category === ts.DiagnosticCategory.Error) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
process.exitCode = main(process.argv.slice(2));
|
||||
}
|
@ -24,5 +24,5 @@ ng_module(
|
||||
write_ng_outputs_only = True,
|
||||
module_name = "@angular/core",
|
||||
tsconfig = ":tsconfig-build.json",
|
||||
compiler = "//tools/ngc-wrapped"
|
||||
compiler = "//packages/bazel/src/ngc-wrapped"
|
||||
)
|
@ -25,6 +25,7 @@
|
||||
"types": ["angularjs"]
|
||||
},
|
||||
"exclude": [
|
||||
"bazel",
|
||||
"compiler-cli/integrationtest",
|
||||
"platform-server/integrationtest",
|
||||
"tsc-wrapped",
|
||||
|
Reference in New Issue
Block a user