build: emit performance JSON file for each ng_module() (#41125)

A previous commit implemented a streamlined performance metric reporting
system for the compiler-cli, controlled via the compiler option
`tracePerformance`.

This commit adds a custom Bazel flag rule //packages/compiler-cli:ng_perf
to the repository, and wires it through to the `ng_module` implementation
such that if the flag is set, `ng_module` will produce perf results as part
of the build. The underlying mechanism of `//:ng_perf` is not exported from
`@angular/bazel` as a public rule that consumers can use, so there is little
risk of accidental dependency on the contents of these perf traces.

An alias is added so that `--ng_perf` is a Bazel flag which works in our
repository.

PR Close #41125
This commit is contained in:
Alex Rickabaugh 2021-03-15 16:33:48 -07:00
parent 48fec08c95
commit b61c009c54
6 changed files with 83 additions and 3 deletions

View File

@ -140,6 +140,13 @@ build:remote --remote_executor=remotebuildexecution.googleapis.com
# retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times.
test:saucelabs --flaky_test_attempts=1
################
# Flag Aliases #
################
# --ng_perf will ask the Ivy compiler to produce performance results for each build.
build --flag_alias=ng_perf=//packages/compiler-cli:ng_perf
####################################################
# User bazel configuration
# NOTE: This needs to be the *last* entry in the config.

View File

@ -12,5 +12,6 @@ load("//packages/bazel/src:ng_module.bzl", _ng_module = "ng_module_macro")
ng_module = _ng_module
ng_package = _ng_package
# DO NOT ADD PUBLIC API without including in the documentation generation
# Run `yarn bazel build //packages/bazel/docs` to verify

View File

@ -26,9 +26,19 @@ load(
"tsc_wrapped_tsconfig",
)
# enable_perf_logging controls whether Ivy's performance tracing system will be enabled for any
# compilation which includes this provider.
NgPerfInfo = provider(fields = ["enable_perf_logging"])
_FLAT_DTS_FILE_SUFFIX = ".bundle.d.ts"
_R3_SYMBOLS_DTS_FILE = "src/r3_symbols.d.ts"
def is_perf_requested(ctx):
enable_perf_logging = ctx.attr.perf_flag != None and ctx.attr.perf_flag[NgPerfInfo].enable_perf_logging == True
if enable_perf_logging and not is_ivy_enabled(ctx):
fail("Angular View Engine does not support performance tracing")
return enable_perf_logging
def is_ivy_enabled(ctx):
"""Determine if the ivy compiler should be used to by the ng_module.
@ -278,6 +288,15 @@ def _expected_outs(ctx):
else:
i18n_messages_files = []
dev_perf_files = []
prod_perf_files = []
# In Ivy mode, dev and prod builds both produce a .json output containing performance metrics
# from the compiler for that build.
if is_perf_requested(ctx):
dev_perf_files = [ctx.actions.declare_file(ctx.label.name + "_perf_dev.json")]
prod_perf_files = [ctx.actions.declare_file(ctx.label.name + "_perf_prod.json")]
return struct(
closure_js = closure_js_files,
devmode_js = devmode_js_files,
@ -288,6 +307,8 @@ def _expected_outs(ctx):
dts_bundles = dts_bundles,
bundle_index_typings = bundle_index_typings,
i18n_messages = i18n_messages_files,
dev_perf_files = dev_perf_files,
prod_perf_files = prod_perf_files,
)
# Determines if we need to generate View Engine shims (.ngfactory and .ngsummary files)
@ -336,6 +357,15 @@ def _ngc_tsconfig(ctx, files, srcs, **kwargs):
"_useManifestPathsAsModuleName": (not _is_bazel()),
}
if is_perf_requested(ctx):
# In Ivy mode, set the `tracePerformance` Angular compiler option to enable performance
# metric output.
if "devmode_manifest" in kwargs:
perf_path = outs.dev_perf_files[0].path
else:
perf_path = outs.prod_perf_files[0].path
angular_compiler_options["tracePerformance"] = perf_path
if _should_produce_flat_module_outs(ctx):
angular_compiler_options["flatModuleId"] = ctx.attr.module_name
angular_compiler_options["flatModuleOutFile"] = _flat_module_out_file(ctx)
@ -519,6 +549,7 @@ def _compile_action(
outputs,
dts_bundles_out,
messages_out,
perf_out,
tsconfig_file,
node_opts,
compile_mode):
@ -563,12 +594,12 @@ def _compile_action(
def _prodmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
return _compile_action(ctx, inputs, outputs + outs.closure_js, None, outs.i18n_messages, tsconfig_file, node_opts, "prodmode")
return _compile_action(ctx, inputs, outputs + outs.closure_js + outs.prod_perf_files, None, outs.i18n_messages, outs.prod_perf_files, tsconfig_file, node_opts, "prodmode")
def _devmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
compile_action_outputs = outputs + outs.devmode_js + outs.declarations + outs.summaries + outs.metadata
_compile_action(ctx, inputs, compile_action_outputs, outs.dts_bundles, None, tsconfig_file, node_opts, "devmode")
compile_action_outputs = outputs + outs.devmode_js + outs.declarations + outs.summaries + outs.metadata + outs.dev_perf_files
_compile_action(ctx, inputs, compile_action_outputs, outs.dts_bundles, None, outs.dev_perf_files, tsconfig_file, node_opts, "devmode")
def _ts_expected_outs(ctx, label, srcs_files = []):
# rules_typescript expects a function with two or more arguments, but our
@ -711,6 +742,16 @@ NG_MODULE_ATTRIBUTES = {
executable = True,
cfg = "host",
),
# In the angular/angular monorepo, //tools:defaults.bzl wraps the ng_module rule in a macro
# which sets this attribute to the //packages/compiler-cli:ng_perf flag.
# This is done to avoid exposing the flag to user projects, which would require:
# * defining the flag within @angular/bazel and referencing it correctly here, and
# * committing to the flag and its semantics (including the format of perf JSON files)
# as something users can depend upon.
"perf_flag": attr.label(
providers = [NgPerfInfo],
doc = "Private API to control production of performance metric JSON files",
),
"_supports_workers": attr.bool(default = True),
}

View File

@ -0,0 +1,20 @@
# Copyright Google LLC 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(":ng_module.bzl", "NgPerfInfo")
def _ng_perf_flag_impl(ctx):
return NgPerfInfo(enable_perf_logging = ctx.build_setting_value)
# `ng_perf_flag` is a special `build_setting` rule which ultimately enables a command-line boolean
# flag to control whether the `ng_module` rule produces performance tracing JSON files (in Ivy mode)
# as declared outputs.
#
# It does this via the `NgPerfInfo` provider and the `perf_flag` attriubute on `ng_module`. For more
# details, see: https://docs.bazel.build/versions/master/skylark/config.html
ng_perf_flag = rule(
implementation = _ng_perf_flag_impl,
build_setting = config.bool(flag = True),
)

View File

@ -2,6 +2,10 @@ package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "pkg_npm", "ts_api_guardian_test", "ts_config", "ts_library")
# Load ng_perf_flag explicitly from ng_perf.bzl as it's private API, and not exposed to other
# consumers of @angular/bazel.
load("//packages/bazel/src:ng_perf.bzl", "ng_perf_flag")
ts_config(
name = "tsconfig",
src = "tsconfig-build.json",
@ -88,3 +92,9 @@ ts_api_guardian_test(
],
golden = "angular/goldens/public-api/compiler-cli/compiler_options.d.ts",
)
# Controls whether the Ivy compiler produces performance traces as part of each build
ng_perf_flag(
name = "ng_perf",
build_setting_default = False,
)

View File

@ -156,6 +156,7 @@ def ng_module(name, tsconfig = None, entry_point = None, testonly = False, deps
api_extractor = _INTERNAL_NG_MODULE_API_EXTRACTOR,
ng_xi18n = _INTERNAL_NG_MODULE_XI18N,
module_name = module_name,
perf_flag = "//packages/compiler-cli:ng_perf",
**kwargs
)