From b61c009c5479812b061c6837e1fdbf645deea4f1 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Mon, 15 Mar 2021 16:33:48 -0700 Subject: [PATCH] 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 --- .bazelrc | 7 +++++ packages/bazel/index.bzl | 1 + packages/bazel/src/ng_module.bzl | 47 +++++++++++++++++++++++++++++-- packages/bazel/src/ng_perf.bzl | 20 +++++++++++++ packages/compiler-cli/BUILD.bazel | 10 +++++++ tools/defaults.bzl | 1 + 6 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 packages/bazel/src/ng_perf.bzl diff --git a/.bazelrc b/.bazelrc index 615785d2b2..6f7a24ff89 100644 --- a/.bazelrc +++ b/.bazelrc @@ -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. diff --git a/packages/bazel/index.bzl b/packages/bazel/index.bzl index 6f28d7db87..3c43d9f1cc 100644 --- a/packages/bazel/index.bzl +++ b/packages/bazel/index.bzl @@ -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 diff --git a/packages/bazel/src/ng_module.bzl b/packages/bazel/src/ng_module.bzl index 7c18c555dc..d8ad4635bd 100644 --- a/packages/bazel/src/ng_module.bzl +++ b/packages/bazel/src/ng_module.bzl @@ -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), } diff --git a/packages/bazel/src/ng_perf.bzl b/packages/bazel/src/ng_perf.bzl new file mode 100644 index 0000000000..9fb0cc2343 --- /dev/null +++ b/packages/bazel/src/ng_perf.bzl @@ -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), +) diff --git a/packages/compiler-cli/BUILD.bazel b/packages/compiler-cli/BUILD.bazel index 5e57f02cd6..fe4758ee91 100644 --- a/packages/compiler-cli/BUILD.bazel +++ b/packages/compiler-cli/BUILD.bazel @@ -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, +) diff --git a/tools/defaults.bzl b/tools/defaults.bzl index 154e48a9b2..9d7a2bed56 100644 --- a/tools/defaults.bzl +++ b/tools/defaults.bzl @@ -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 )