revert: "revert: "feat(dev-infra): exposed new rule 'component_benchmark' via dev_infra (#36434)" (#36798)" (#36800)
This reverts commit ad8c4cdd75
.
PR Close #36800
This commit is contained in:
12
dev-infra/benchmark/BUILD.bazel
Normal file
12
dev-infra/benchmark/BUILD.bazel
Normal file
@ -0,0 +1,12 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
# Make source files available for distribution via pkg_npm
|
||||
filegroup(
|
||||
name = "files",
|
||||
srcs = glob(["*"]) + [
|
||||
"//dev-infra/benchmark/brotli-cli:files",
|
||||
"//dev-infra/benchmark/browsers:files",
|
||||
"//dev-infra/benchmark/component_benchmark:files",
|
||||
"//dev-infra/benchmark/ng_rollup_bundle:files",
|
||||
],
|
||||
)
|
19
dev-infra/benchmark/brotli-cli/BUILD.bazel
Normal file
19
dev-infra/benchmark/brotli-cli/BUILD.bazel
Normal file
@ -0,0 +1,19 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
|
||||
|
||||
nodejs_binary(
|
||||
name = "brotli-cli",
|
||||
data = [
|
||||
"cli.js",
|
||||
"@npm//brotli",
|
||||
],
|
||||
entry_point = ":cli.js",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
# Make source files available for distribution via pkg_npm
|
||||
filegroup(
|
||||
name = "files",
|
||||
srcs = glob(["*"]),
|
||||
)
|
21
dev-infra/benchmark/brotli-cli/cli.js
Normal file
21
dev-infra/benchmark/brotli-cli/cli.js
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @license
|
||||
* 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
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const compress = require('brotli/compress');
|
||||
|
||||
function main(args) {
|
||||
const output = args[0].substring('--output='.length);
|
||||
const input = args[1];
|
||||
const buffer = fs.readFileSync(input);
|
||||
fs.writeFileSync(output, compress(buffer, {mode: 0, quality: 11}));
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main(process.argv.slice(2));
|
||||
}
|
78
dev-infra/benchmark/browsers/BUILD.bazel
Normal file
78
dev-infra/benchmark/browsers/BUILD.bazel
Normal file
@ -0,0 +1,78 @@
|
||||
# Copyright Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
################################################################################
|
||||
#
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@io_bazel_rules_webtesting//web:web.bzl", "browser", "web_test_archive")
|
||||
|
||||
# Override of chromium web_test_archive so that the archive is selected based on platform
|
||||
web_test_archive(
|
||||
name = "chromium_archive",
|
||||
testonly = True,
|
||||
archive = select({
|
||||
"@io_bazel_rules_webtesting//common/conditions:linux": "@org_chromium_chromium_amd64//file",
|
||||
"@io_bazel_rules_webtesting//common/conditions:mac": "@org_chromium_chromium_macos//file",
|
||||
"@io_bazel_rules_webtesting//common/conditions:windows": "@org_chromium_chromium_windows//file",
|
||||
}),
|
||||
extract = "build",
|
||||
named_files = select({
|
||||
"@io_bazel_rules_webtesting//common/conditions:linux": {"CHROMIUM": "chrome-linux/chrome"},
|
||||
"@io_bazel_rules_webtesting//common/conditions:mac": {"CHROMIUM": "chrome-mac/Chromium.app/Contents/MacOS/chromium"},
|
||||
"@io_bazel_rules_webtesting//common/conditions:windows": {"CHROMIUM": "chrome-win/chrome.exe"},
|
||||
}),
|
||||
visibility = ["//dev-infra/benchmark/browsers:__subpackages__"],
|
||||
)
|
||||
|
||||
# Override of chromedriver web_test_archive so that the archive is selected based on platform
|
||||
web_test_archive(
|
||||
name = "chromedriver_archive",
|
||||
testonly = True,
|
||||
archive = select({
|
||||
"@io_bazel_rules_webtesting//common/conditions:linux": "@org_chromium_chromedriver_amd64//file",
|
||||
"@io_bazel_rules_webtesting//common/conditions:mac": "@org_chromium_chromedriver_macos//file",
|
||||
"@io_bazel_rules_webtesting//common/conditions:windows": "@org_chromium_chromedriver_windows//file",
|
||||
}),
|
||||
extract = "build",
|
||||
named_files = select({
|
||||
"@io_bazel_rules_webtesting//common/conditions:linux": {
|
||||
"CHROMEDRIVER": "chromedriver_linux64/chromedriver",
|
||||
},
|
||||
"@io_bazel_rules_webtesting//common/conditions:mac": {
|
||||
"CHROMEDRIVER": "chromedriver_mac64/chromedriver",
|
||||
},
|
||||
"@io_bazel_rules_webtesting//common/conditions:windows": {
|
||||
"CHROMEDRIVER": "chromedriver_win32/chromedriver.exe",
|
||||
},
|
||||
}),
|
||||
visibility = ["//dev-infra/benchmark/browsers:__subpackages__"],
|
||||
)
|
||||
|
||||
browser(
|
||||
name = "chromium",
|
||||
metadata = "chromium.json",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":chromedriver_archive",
|
||||
":chromium_archive",
|
||||
"@io_bazel_rules_webtesting//go/wsl",
|
||||
],
|
||||
)
|
||||
|
||||
# Make source files available for distribution via pkg_npm
|
||||
filegroup(
|
||||
name = "files",
|
||||
srcs = glob(["*"]),
|
||||
)
|
87
dev-infra/benchmark/browsers/browser_repositories.bzl
Normal file
87
dev-infra/benchmark/browsers/browser_repositories.bzl
Normal file
@ -0,0 +1,87 @@
|
||||
# Copyright 2018 The Bazel Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Pinned browser versions.
|
||||
|
||||
This function is here to make browser repositories work with cross-platform RBE.
|
||||
Unlike the rules_webtesting browser_repositories, this function defines
|
||||
separate repositories for each platform
|
||||
"""
|
||||
|
||||
load("@io_bazel_rules_webtesting//web/internal:platform_http_file.bzl", _platform_http_file = "platform_http_file")
|
||||
|
||||
def platform_http_file(name, licenses, sha256, urls):
|
||||
"""Platform spepcific browser repository.
|
||||
|
||||
This works around a dificiency in io_bazel_rules_webtesting platform_http_file in that
|
||||
it selects the platform when the repository rule is executed. This limits browsers
|
||||
tests to run on the local user platform only. For cross-platform RBE we want a repository
|
||||
to be defined per platform so the correct one can be selected.
|
||||
"""
|
||||
|
||||
_platform_http_file(
|
||||
name = name,
|
||||
amd64_sha256 = sha256,
|
||||
amd64_urls = urls,
|
||||
licenses = licenses,
|
||||
macos_sha256 = sha256,
|
||||
macos_urls = urls,
|
||||
windows_sha256 = sha256,
|
||||
windows_urls = urls,
|
||||
)
|
||||
|
||||
def browser_repositories():
|
||||
"""Load pinned rules_webtesting browser versions."""
|
||||
|
||||
platform_http_file(
|
||||
name = "org_chromium_chromium_amd64",
|
||||
licenses = ["notice"], # BSD 3-clause (maybe more?)
|
||||
sha256 = "b1e30c4dec8a451f8fe10d1f2d3c71e491d0333425f32247fe5c80a0a354303d",
|
||||
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/664981/chrome-linux.zip"],
|
||||
)
|
||||
|
||||
platform_http_file(
|
||||
name = "org_chromium_chromium_macos",
|
||||
licenses = ["notice"], # BSD 3-clause (maybe more?)
|
||||
sha256 = "7c0ba93616f44a421330b1c1262e8899fbdf7916bed8b04c775e0426f6f35ec6",
|
||||
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/665002/chrome-mac.zip"],
|
||||
)
|
||||
|
||||
platform_http_file(
|
||||
name = "org_chromium_chromium_windows",
|
||||
licenses = ["notice"], # BSD 3-clause (maybe more?)
|
||||
sha256 = "f2facd0066270078d0e8999e684595274c359cac3946299a1ceedba2a5de1c63",
|
||||
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/664999/chrome-win.zip"],
|
||||
)
|
||||
|
||||
platform_http_file(
|
||||
name = "org_chromium_chromedriver_amd64",
|
||||
licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT
|
||||
sha256 = "0ead02145854b60a3317b59031205b362fb4cfdb680fef20e95c89582e6e38be",
|
||||
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/664981/chromedriver_linux64.zip"],
|
||||
)
|
||||
|
||||
platform_http_file(
|
||||
name = "org_chromium_chromedriver_macos",
|
||||
licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT
|
||||
sha256 = "8dd159e27b13b16262afa6993b15321e736c3b484da363c0e03bb050d72522c9",
|
||||
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/665002/chromedriver_mac64.zip"],
|
||||
)
|
||||
|
||||
platform_http_file(
|
||||
name = "org_chromium_chromedriver_windows",
|
||||
licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT
|
||||
sha256 = "1cc881364974102182257a5c5c2b9cfed513689dee28924ca44df082bdf9fd60",
|
||||
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/664999/chromedriver_win32.zip"],
|
||||
)
|
20
dev-infra/benchmark/browsers/chromium.json
Normal file
20
dev-infra/benchmark/browsers/chromium.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"environment" : "local",
|
||||
"capabilities" : {
|
||||
"browserName" : "chrome",
|
||||
"goog:chromeOptions" : {
|
||||
"binary" : "%FILE:CHROMIUM%",
|
||||
"args" : [
|
||||
"--headless",
|
||||
"--use-gl=swiftshader-webgl"
|
||||
]
|
||||
},
|
||||
"google:wslConfig": {
|
||||
"binary": "%FILE:CHROMEDRIVER%",
|
||||
"port":"%WSLPORT:WSL%",
|
||||
"args": ["--port=%WSLPORT:WSL%"],
|
||||
"status": true,
|
||||
"shutdown": true
|
||||
}
|
||||
}
|
||||
}
|
12
dev-infra/benchmark/component_benchmark/BUILD.bazel
Normal file
12
dev-infra/benchmark/component_benchmark/BUILD.bazel
Normal file
@ -0,0 +1,12 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files([
|
||||
"protractor-perf.conf.js",
|
||||
"start-server.js",
|
||||
])
|
||||
|
||||
# Make source files available for distribution via pkg_npm
|
||||
filegroup(
|
||||
name = "files",
|
||||
srcs = glob(["*"]) + ["//dev-infra/benchmark/component_benchmark/defaults:files"],
|
||||
)
|
27
dev-infra/benchmark/component_benchmark/benchmark_test.bzl
Normal file
27
dev-infra/benchmark/component_benchmark/benchmark_test.bzl
Normal file
@ -0,0 +1,27 @@
|
||||
load("@npm_bazel_protractor//:index.bzl", "protractor_web_test_suite")
|
||||
|
||||
"""
|
||||
Macro that can be used to define a benchmark test. This differentiates from
|
||||
a normal Protractor test suite because we specify a custom "perf" configuration
|
||||
that sets up "@angular/benchpress". Benchmark test targets will not run on CI
|
||||
unless explicitly requested.
|
||||
"""
|
||||
|
||||
def benchmark_test(name, server, tags = [], **kwargs):
|
||||
protractor_web_test_suite(
|
||||
name = name,
|
||||
browsers = ["//dev-infra/benchmark/browsers:chromium"],
|
||||
configuration = "//dev-infra/benchmark/component_benchmark:protractor-perf.conf.js",
|
||||
on_prepare = "//dev-infra/benchmark/component_benchmark:start-server.js",
|
||||
server = server,
|
||||
# Benchmark targets should not run on CI by default.
|
||||
tags = tags + [
|
||||
"manual",
|
||||
"no-remote-exec",
|
||||
],
|
||||
test_suite_tags = [
|
||||
"manual",
|
||||
"no-remote-exec",
|
||||
],
|
||||
**kwargs
|
||||
)
|
145
dev-infra/benchmark/component_benchmark/component_benchmark.bzl
Normal file
145
dev-infra/benchmark/component_benchmark/component_benchmark.bzl
Normal file
@ -0,0 +1,145 @@
|
||||
load("//dev-infra/benchmark/ng_rollup_bundle:ng_rollup_bundle.bzl", "ng_rollup_bundle")
|
||||
load("//tools:defaults.bzl", "ng_module")
|
||||
load("@npm_bazel_typescript//:index.bzl", "ts_devserver", "ts_library")
|
||||
load(":benchmark_test.bzl", "benchmark_test")
|
||||
|
||||
def copy_default_file(origin, destination):
|
||||
"""
|
||||
Copies a file from /defaults to the destination.
|
||||
|
||||
Args:
|
||||
origin: The name of a file in benchpress/defaults to be copied.
|
||||
destination: Where the original file will be clopied to.
|
||||
"""
|
||||
native.genrule(
|
||||
name = "copy_default_" + origin + "_file_genrule",
|
||||
srcs = ["//dev-infra/benchmark/component_benchmark/defaults:" + origin],
|
||||
outs = [destination],
|
||||
cmd = "cat $(SRCS) >> $@",
|
||||
)
|
||||
|
||||
def component_benchmark(
|
||||
name,
|
||||
prefix,
|
||||
driver,
|
||||
driver_deps,
|
||||
ng_srcs,
|
||||
ng_deps,
|
||||
assets = None,
|
||||
styles = None,
|
||||
entry_point = None,
|
||||
entry_point_deps = [
|
||||
"//packages/core",
|
||||
"//packages/platform-browser",
|
||||
]):
|
||||
"""
|
||||
Runs a benchmark test against the given angular app using the given driver.
|
||||
|
||||
This rule was created with the intention of reducing the amount of
|
||||
duplicate/boilderplate code, while also allowing you to be as verbose with
|
||||
your app as you'd like. The goal being that if you just want to test a
|
||||
simple component, the only thing you'd need to provide are the component
|
||||
(via ng_srcs) and driver.
|
||||
|
||||
** USAGE NOTES **
|
||||
|
||||
(assets/styles): The default index.html imports a stylesheet named
|
||||
"styles.css". This allows the use of the default index.html with a custom
|
||||
stylesheet through the styles arg by providing either a styles.css in the
|
||||
prefix directory or by providing a css binary named styles.css.
|
||||
|
||||
(assets): The default index.html expects that the root selector for
|
||||
the benchmark app is "app-root".
|
||||
|
||||
(entry_point): The default entry_point expects a file named "app.module" to export
|
||||
the root NgModule for the benchmark application. It also expects that the
|
||||
root NgModule is named "AppModule".
|
||||
|
||||
TIP: The server is named `name + "_server"` so that you can view/debug the
|
||||
app.
|
||||
|
||||
Args:
|
||||
name: The name of the benchmark_test to be run
|
||||
prefix: The relative path to the root directory of the benchmark app
|
||||
driver: The ts driver for running the benchmark
|
||||
driver_deps: Driver's dependencies
|
||||
ng_srcs: All of the ts srcs for the angular app
|
||||
ng_deps: Dependencies for the angular app
|
||||
assets: Static files
|
||||
styles: Stylesheets
|
||||
entry_point: Main entry point for the angular app
|
||||
entry_point_deps: Entry point's dependencies
|
||||
"""
|
||||
app_lib = name + "_app_lib"
|
||||
app_main = name + "_app_main"
|
||||
benchmark_driver = name + "_driver"
|
||||
server = name + "_server"
|
||||
|
||||
# If the user doesn't provide assets, entry_point, or styles, we use a
|
||||
# default version.
|
||||
# Note that we copy the default files to the same directory as what is used
|
||||
# by the app for three reasons:
|
||||
# 1. To avoid having the entry point be defined in a different package from
|
||||
# where this macro is called.
|
||||
# 2. So that we can use relative paths for imports in entry point.
|
||||
# 3. To make using default static files as seamless as possible.
|
||||
|
||||
if not entry_point:
|
||||
entry_point = prefix + "default_index.ts"
|
||||
ng_srcs.append(entry_point)
|
||||
copy_default_file("index.ts", entry_point)
|
||||
|
||||
if not assets:
|
||||
html = prefix + "index.html"
|
||||
assets = [html]
|
||||
copy_default_file("index.html", html)
|
||||
|
||||
if not styles:
|
||||
css = prefix + "styles.css"
|
||||
styles = [css]
|
||||
copy_default_file("styles.css", css)
|
||||
|
||||
# Bootstraps the application and creates
|
||||
# additional files to be imported by the entry_point file.
|
||||
ng_module(
|
||||
name = app_lib,
|
||||
srcs = ng_srcs,
|
||||
# Creates ngFactory and ngSummary to be imported by the app's entry point.
|
||||
generate_ve_shims = True,
|
||||
deps = ng_deps,
|
||||
tsconfig = "//dev-infra/benchmark/component_benchmark:tsconfig-e2e.json",
|
||||
)
|
||||
|
||||
# Bundle the application (needed by ts_devserver).
|
||||
ng_rollup_bundle(
|
||||
name = app_main,
|
||||
entry_point = entry_point,
|
||||
deps = [":" + app_lib] + entry_point_deps,
|
||||
)
|
||||
|
||||
# The ts_library for the driver that runs tests against the benchmark app.
|
||||
ts_library(
|
||||
name = benchmark_driver,
|
||||
tsconfig = "//dev-infra/benchmark/component_benchmark:tsconfig-e2e.json",
|
||||
testonly = True,
|
||||
srcs = [driver],
|
||||
deps = driver_deps,
|
||||
)
|
||||
|
||||
# The server for our application.
|
||||
ts_devserver(
|
||||
name = server,
|
||||
bootstrap = ["//packages/zone.js/dist:zone.js"],
|
||||
port = 4200,
|
||||
static_files = assets + styles,
|
||||
deps = [":" + app_main + ".min_debug.es2015.js"],
|
||||
additional_root_paths = ["//dev-infra/benchmark/component_benchmark/defaults"],
|
||||
serving_path = "/app_bundle.js",
|
||||
)
|
||||
|
||||
# Runs a protractor test that's set up to use @angular/benchpress.
|
||||
benchmark_test(
|
||||
name = name,
|
||||
server = ":" + server,
|
||||
deps = [":" + benchmark_driver],
|
||||
)
|
13
dev-infra/benchmark/component_benchmark/defaults/BUILD.bazel
Normal file
13
dev-infra/benchmark/component_benchmark/defaults/BUILD.bazel
Normal file
@ -0,0 +1,13 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
# Make source files available for distribution via pkg_npm
|
||||
filegroup(
|
||||
name = "files",
|
||||
srcs = glob(["*"]),
|
||||
)
|
||||
|
||||
exports_files([
|
||||
"index.html",
|
||||
"index.ts",
|
||||
"styles.css",
|
||||
])
|
14
dev-infra/benchmark/component_benchmark/defaults/index.html
Normal file
14
dev-infra/benchmark/component_benchmark/defaults/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Benchmark Test</title>
|
||||
<!-- Prevent favicon.ico requests -->
|
||||
<link rel="icon" href="data:,">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<app-root id="root">Loading...</app-root>
|
||||
<script src="/app_bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
19
dev-infra/benchmark/component_benchmark/defaults/index.ts
Normal file
19
dev-infra/benchmark/component_benchmark/defaults/index.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @license
|
||||
* 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
|
||||
*/
|
||||
|
||||
// @ts-ignore Cannot find module
|
||||
import {enableProdMode} from '@angular/core';
|
||||
|
||||
// @ts-ignore Cannot find module
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
// @ts-ignore Cannot find module
|
||||
import {AppModuleNgFactory} from './app.module.ngfactory';
|
||||
|
||||
enableProdMode();
|
||||
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
|
@ -0,0 +1,7 @@
|
||||
/*
|
||||
* This file exists so that if the default index.html is used a 404 will not
|
||||
* throw.
|
||||
*
|
||||
* We leave an import for "styles.css" in the default index.html for the case
|
||||
* where someone wants to use index.html and provide their own styles.
|
||||
*/
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @license
|
||||
* 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
|
||||
*/
|
||||
|
||||
const CHROME_OPTIONS = {
|
||||
'args': ['--js-flags=--expose-gc', '--no-sandbox', '--headless', '--disable-dev-shm-usage'],
|
||||
'perfLoggingPrefs': {
|
||||
'traceCategories':
|
||||
'v8,blink.console,devtools.timeline,disabled-by-default-devtools.timeline,blink.user_timing'
|
||||
}
|
||||
};
|
||||
|
||||
exports.config = {
|
||||
onPrepare: function() {
|
||||
beforeEach(function() {
|
||||
browser.ignoreSynchronization = false;
|
||||
});
|
||||
},
|
||||
restartBrowserBetweenTests: true,
|
||||
allScriptsTimeout: 11000,
|
||||
capabilities: {
|
||||
'browserName': 'chrome',
|
||||
chromeOptions: CHROME_OPTIONS,
|
||||
loggingPrefs: {
|
||||
performance: 'ALL',
|
||||
browser: 'ALL',
|
||||
}
|
||||
},
|
||||
directConnect: true,
|
||||
framework: 'jasmine2',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 90000,
|
||||
print: function(msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
},
|
||||
useAllAngular2AppRoots: true
|
||||
};
|
18
dev-infra/benchmark/component_benchmark/start-server.js
Normal file
18
dev-infra/benchmark/component_benchmark/start-server.js
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @license
|
||||
* 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
|
||||
*/
|
||||
|
||||
const protractorUtils = require('@bazel/protractor/protractor-utils');
|
||||
const protractor = require('protractor');
|
||||
|
||||
module.exports = async function(config) {
|
||||
const {port} = await protractorUtils.runServer(config.workspace, config.server, '-port', []);
|
||||
const processedConfig = await protractor.browser.getProcessedConfig();
|
||||
const serverUrl = `http://localhost:${port}`;
|
||||
|
||||
return processedConfig.baseUrl = protractor.browser.baseUrl = serverUrl;
|
||||
};
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["es2015", "dom"],
|
||||
"types": ["node", "jasmine"]
|
||||
}
|
||||
}
|
20
dev-infra/benchmark/driver-utilities/BUILD.bazel
Normal file
20
dev-infra/benchmark/driver-utilities/BUILD.bazel
Normal file
@ -0,0 +1,20 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@npm_bazel_typescript//:index.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "driver-utilities",
|
||||
srcs = glob(["*.ts"]),
|
||||
module_name = "@angular/dev-infra/benchmark/driver-utilities",
|
||||
tsconfig = "//dev-infra/benchmark/component_benchmark:tsconfig-e2e.json",
|
||||
deps = [
|
||||
"//packages/benchpress",
|
||||
"@npm//@types/fs-extra",
|
||||
"@npm//@types/node",
|
||||
"@npm//@types/selenium-webdriver",
|
||||
"@npm//fs-extra",
|
||||
"@npm//node-uuid",
|
||||
"@npm//protractor",
|
||||
"@npm//selenium-webdriver",
|
||||
],
|
||||
)
|
50
dev-infra/benchmark/driver-utilities/e2e_util.ts
Normal file
50
dev-infra/benchmark/driver-utilities/e2e_util.ts
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @license
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* tslint:disable:no-console */
|
||||
import {browser} from 'protractor';
|
||||
import * as webdriver from 'selenium-webdriver';
|
||||
|
||||
declare var expect: any;
|
||||
|
||||
export function openBrowser(config: {
|
||||
url: string,
|
||||
params?: {name: string, value: any}[],
|
||||
ignoreBrowserSynchronization?: boolean
|
||||
}) {
|
||||
if (config.ignoreBrowserSynchronization) {
|
||||
browser.ignoreSynchronization = true;
|
||||
}
|
||||
const urlParams: string[] = [];
|
||||
if (config.params) {
|
||||
config.params.forEach((param) => urlParams.push(param.name + '=' + param.value));
|
||||
}
|
||||
const url = encodeURI(config.url + '?' + urlParams.join('&'));
|
||||
browser.get(url);
|
||||
if (config.ignoreBrowserSynchronization) {
|
||||
browser.sleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental This API will be moved to Protractor.
|
||||
*/
|
||||
export function verifyNoBrowserErrors() {
|
||||
// TODO(tbosch): Bug in ChromeDriver: Need to execute at least one command
|
||||
// so that the browser logs can be read out!
|
||||
browser.executeScript('1+1');
|
||||
browser.manage().logs().get('browser').then(function(browserLog: any) {
|
||||
const filteredLog = browserLog.filter(function(logEntry: any) {
|
||||
if (logEntry.level.value >= webdriver.logging.Level.INFO.value) {
|
||||
console.log('>> ' + logEntry.message);
|
||||
}
|
||||
return logEntry.level.value > webdriver.logging.Level.WARNING.value;
|
||||
});
|
||||
expect(filteredLog).toEqual([]);
|
||||
});
|
||||
}
|
9
dev-infra/benchmark/driver-utilities/index.ts
Normal file
9
dev-infra/benchmark/driver-utilities/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
export {openBrowser, verifyNoBrowserErrors} from './e2e_util';
|
||||
export {runBenchmark} from './perf_util';
|
75
dev-infra/benchmark/driver-utilities/perf_util.ts
Normal file
75
dev-infra/benchmark/driver-utilities/perf_util.ts
Normal file
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @license
|
||||
* 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
|
||||
*/
|
||||
export {verifyNoBrowserErrors} from './e2e_util';
|
||||
|
||||
const nodeUuid = require('node-uuid');
|
||||
import * as fs from 'fs-extra';
|
||||
|
||||
import {SeleniumWebDriverAdapter, Options, JsonFileReporter, Validator, RegressionSlopeValidator, ConsoleReporter, SizeValidator, MultiReporter, MultiMetric, Runner, StaticProvider} from '@angular/benchpress';
|
||||
import {openBrowser} from './e2e_util';
|
||||
|
||||
// Note: Keep the `modules/benchmarks/README.md` file in sync with the supported options.
|
||||
const globalOptions = {
|
||||
sampleSize: process.env.PERF_SAMPLE_SIZE || 20,
|
||||
forceGc: process.env.PERF_FORCE_GC === 'true',
|
||||
dryRun: process.env.PERF_DRYRUN === 'true',
|
||||
};
|
||||
|
||||
const runner = createBenchpressRunner();
|
||||
|
||||
export function runBenchmark(config: {
|
||||
id: string,
|
||||
url: string,
|
||||
params: {name: string, value: any}[],
|
||||
ignoreBrowserSynchronization?: boolean,
|
||||
microMetrics?: {[key: string]: string},
|
||||
work?: () => void,
|
||||
prepare?: () => void,
|
||||
setup?: () => void
|
||||
}): Promise<any> {
|
||||
openBrowser(config);
|
||||
if (config.setup) {
|
||||
config.setup();
|
||||
}
|
||||
const description: {[key: string]: any} = {};
|
||||
config.params.forEach((param) => description[param.name] = param.value);
|
||||
return runner.sample({
|
||||
id: config.id,
|
||||
execute: config.work,
|
||||
prepare: config.prepare,
|
||||
microMetrics: config.microMetrics,
|
||||
providers: [{provide: Options.SAMPLE_DESCRIPTION, useValue: {}}]
|
||||
});
|
||||
}
|
||||
|
||||
function createBenchpressRunner(): Runner {
|
||||
let runId = nodeUuid.v1();
|
||||
if (process.env.GIT_SHA) {
|
||||
runId = process.env.GIT_SHA + ' ' + runId;
|
||||
}
|
||||
const resultsFolder = './dist/benchmark_results';
|
||||
fs.ensureDirSync(resultsFolder);
|
||||
const providers: StaticProvider[] = [
|
||||
SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
|
||||
{provide: Options.FORCE_GC, useValue: globalOptions.forceGc},
|
||||
{provide: Options.DEFAULT_DESCRIPTION, useValue: {'runId': runId}}, JsonFileReporter.PROVIDERS,
|
||||
{provide: JsonFileReporter.PATH, useValue: resultsFolder}
|
||||
];
|
||||
if (!globalOptions.dryRun) {
|
||||
providers.push({provide: Validator, useExisting: RegressionSlopeValidator});
|
||||
providers.push(
|
||||
{provide: RegressionSlopeValidator.SAMPLE_SIZE, useValue: globalOptions.sampleSize});
|
||||
providers.push(MultiReporter.provideWith([ConsoleReporter, JsonFileReporter]));
|
||||
} else {
|
||||
providers.push({provide: Validator, useExisting: SizeValidator});
|
||||
providers.push({provide: SizeValidator.SAMPLE_SIZE, useValue: 1});
|
||||
providers.push(MultiReporter.provideWith([]));
|
||||
providers.push(MultiMetric.provideWith([]));
|
||||
}
|
||||
return new Runner(providers);
|
||||
}
|
26
dev-infra/benchmark/ng_rollup_bundle/BUILD.bazel
Normal file
26
dev-infra/benchmark/ng_rollup_bundle/BUILD.bazel
Normal file
@ -0,0 +1,26 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
|
||||
|
||||
exports_files([
|
||||
"rollup.config.js",
|
||||
"terser_config.json",
|
||||
])
|
||||
|
||||
nodejs_binary(
|
||||
name = "rollup_with_build_optimizer",
|
||||
data = [
|
||||
"@npm//@angular-devkit/build-optimizer",
|
||||
"@npm//rollup",
|
||||
"@npm//rollup-plugin-commonjs",
|
||||
"@npm//rollup-plugin-node-resolve",
|
||||
"@npm//rollup-plugin-sourcemaps",
|
||||
],
|
||||
entry_point = "@npm//:node_modules/rollup/dist/bin/rollup",
|
||||
)
|
||||
|
||||
# Make source files available for distribution via pkg_npm
|
||||
filegroup(
|
||||
name = "files",
|
||||
srcs = glob(["*"]),
|
||||
)
|
471
dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl
Normal file
471
dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl
Normal file
@ -0,0 +1,471 @@
|
||||
# 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
|
||||
|
||||
"""Rollup with Build Optimizer
|
||||
|
||||
This provides a variant of the [rollup_bundle] rule that works better for Angular apps.
|
||||
|
||||
It registers `@angular-devkit/build-optimizer` as a rollup plugin, to get
|
||||
better optimization. It also uses ESM5 format inputs, as this is what
|
||||
build-optimizer is hard-coded to look for and transform.
|
||||
|
||||
[rollup_bundle]: https://bazelbuild.github.io/rules_nodejs/rollup/rollup_bundle.html
|
||||
"""
|
||||
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "npm_package_bin")
|
||||
load("@build_bazel_rules_nodejs//:providers.bzl", "JSEcmaScriptModuleInfo", "NpmPackageInfo", "node_modules_aspect")
|
||||
load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5")
|
||||
load("@npm_bazel_terser//:index.bzl", "terser_minified")
|
||||
|
||||
_NG_ROLLUP_BUNDLE_OUTPUTS = {
|
||||
"bundle": "%{name}.js",
|
||||
"sourcemap": "%{name}.js.map",
|
||||
}
|
||||
|
||||
_NG_ROLLUP_MODULE_MAPPINGS_ATTR = "ng_rollup_module_mappings"
|
||||
|
||||
def _ng_rollup_module_mappings_aspect_impl(target, ctx):
|
||||
mappings = dict()
|
||||
for dep in ctx.rule.attr.deps:
|
||||
if hasattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR):
|
||||
for k, v in getattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR).items():
|
||||
if k in mappings and mappings[k] != v:
|
||||
fail(("duplicate module mapping at %s: %s maps to both %s and %s" %
|
||||
(target.label, k, mappings[k], v)), "deps")
|
||||
mappings[k] = v
|
||||
if ((hasattr(ctx.rule.attr, "module_name") and ctx.rule.attr.module_name) or
|
||||
(hasattr(ctx.rule.attr, "module_root") and ctx.rule.attr.module_root)):
|
||||
mn = ctx.rule.attr.module_name
|
||||
if not mn:
|
||||
mn = target.label.name
|
||||
mr = target.label.package
|
||||
if target.label.workspace_root:
|
||||
mr = "%s/%s" % (target.label.workspace_root, mr)
|
||||
if ctx.rule.attr.module_root and ctx.rule.attr.module_root != ".":
|
||||
if ctx.rule.attr.module_root.endswith(".ts"):
|
||||
# This is the type-checking module mapping. Strip the trailing .d.ts
|
||||
# as it doesn't belong in TypeScript's path mapping.
|
||||
mr = "%s/%s" % (mr, ctx.rule.attr.module_root.replace(".d.ts", ""))
|
||||
else:
|
||||
mr = "%s/%s" % (mr, ctx.rule.attr.module_root)
|
||||
if mn in mappings and mappings[mn] != mr:
|
||||
fail(("duplicate module mapping at %s: %s maps to both %s and %s" %
|
||||
(target.label, mn, mappings[mn], mr)), "deps")
|
||||
mappings[mn] = mr
|
||||
return struct(ng_rollup_module_mappings = mappings)
|
||||
|
||||
ng_rollup_module_mappings_aspect = aspect(
|
||||
_ng_rollup_module_mappings_aspect_impl,
|
||||
attr_aspects = ["deps"],
|
||||
)
|
||||
|
||||
_NG_ROLLUP_BUNDLE_DEPS_ASPECTS = [esm5_outputs_aspect, ng_rollup_module_mappings_aspect, node_modules_aspect]
|
||||
|
||||
_NG_ROLLUP_BUNDLE_ATTRS = {
|
||||
"build_optimizer": attr.bool(
|
||||
doc = """Use build optimizer plugin
|
||||
|
||||
Only used if sources are esm5 which depends on value of esm5_sources.""",
|
||||
default = True,
|
||||
),
|
||||
"esm5_sources": attr.bool(
|
||||
doc = """Use esm5 input sources""",
|
||||
default = True,
|
||||
),
|
||||
"srcs": attr.label_list(
|
||||
doc = """JavaScript source files from the workspace.
|
||||
These can use ES2015 syntax and ES Modules (import/export)""",
|
||||
allow_files = True,
|
||||
),
|
||||
"entry_point": attr.label(
|
||||
doc = """The starting point of the application, passed as the `--input` flag to rollup.
|
||||
|
||||
If the entry JavaScript file belongs to the same package (as the BUILD file),
|
||||
you can simply reference it by its relative name to the package directory:
|
||||
|
||||
```
|
||||
ng_rollup_bundle(
|
||||
name = "bundle",
|
||||
entry_point = ":main.js",
|
||||
)
|
||||
```
|
||||
|
||||
You can specify the entry point as a typescript file so long as you also include
|
||||
the ts_library target in deps:
|
||||
|
||||
```
|
||||
ts_library(
|
||||
name = "main",
|
||||
srcs = ["main.ts"],
|
||||
)
|
||||
|
||||
ng_rollup_bundle(
|
||||
name = "bundle",
|
||||
deps = [":main"]
|
||||
entry_point = ":main.ts",
|
||||
)
|
||||
```
|
||||
|
||||
The rule will use the corresponding `.js` output of the ts_library rule as the entry point.
|
||||
|
||||
If the entry point target is a rule, it should produce a single JavaScript entry file that will be passed to the nodejs_binary rule.
|
||||
For example:
|
||||
|
||||
```
|
||||
filegroup(
|
||||
name = "entry_file",
|
||||
srcs = ["main.js"],
|
||||
)
|
||||
|
||||
ng_rollup_bundle(
|
||||
name = "bundle",
|
||||
entry_point = ":entry_file",
|
||||
)
|
||||
```
|
||||
""",
|
||||
mandatory = True,
|
||||
allow_single_file = True,
|
||||
),
|
||||
"deps": attr.label_list(
|
||||
doc = """Other targets that provide JavaScript files.
|
||||
Typically this will be `ts_library` or `ng_module` targets.""",
|
||||
aspects = _NG_ROLLUP_BUNDLE_DEPS_ASPECTS,
|
||||
),
|
||||
"format": attr.string(
|
||||
doc = """"Specifies the format of the generated bundle. One of the following:
|
||||
|
||||
- `amd`: Asynchronous Module Definition, used with module loaders like RequireJS
|
||||
- `cjs`: CommonJS, suitable for Node and other bundlers
|
||||
- `esm`: Keep the bundle as an ES module file, suitable for other bundlers and inclusion as a `<script type=module>` tag in modern browsers
|
||||
- `iife`: A self-executing function, suitable for inclusion as a `<script>` tag. (If you want to create a bundle for your application, you probably want to use this.)
|
||||
- `umd`: Universal Module Definition, works as amd, cjs and iife all in one
|
||||
- `system`: Native format of the SystemJS loader
|
||||
""",
|
||||
values = ["amd", "cjs", "esm", "iife", "umd", "system"],
|
||||
default = "esm",
|
||||
),
|
||||
"global_name": attr.string(
|
||||
doc = """A name given to this package when referenced as a global variable.
|
||||
This name appears in the bundle module incantation at the beginning of the file,
|
||||
and governs the global symbol added to the global context (e.g. `window`) as a side-
|
||||
effect of loading the UMD/IIFE JS bundle.
|
||||
|
||||
Rollup doc: "The variable name, representing your iife/umd bundle, by which other scripts on the same page can access it."
|
||||
|
||||
This is passed to the `output.name` setting in Rollup.""",
|
||||
),
|
||||
"globals": attr.string_dict(
|
||||
doc = """A dict of symbols that reference external scripts.
|
||||
The keys are variable names that appear in the program,
|
||||
and the values are the symbol to reference at runtime in a global context (UMD bundles).
|
||||
For example, a program referencing @angular/core should use ng.core
|
||||
as the global reference, so Angular users should include the mapping
|
||||
`"@angular/core":"ng.core"` in the globals.""",
|
||||
default = {},
|
||||
),
|
||||
"license_banner": attr.label(
|
||||
doc = """A .txt file passed to the `banner` config option of rollup.
|
||||
The contents of the file will be copied to the top of the resulting bundles.
|
||||
Note that you can replace a version placeholder in the license file, by using
|
||||
the special version `0.0.0-PLACEHOLDER`. See the section on stamping in the README.""",
|
||||
allow_single_file = [".txt"],
|
||||
),
|
||||
"_rollup": attr.label(
|
||||
executable = True,
|
||||
cfg = "host",
|
||||
default = Label("//dev-infra/benchmark/ng_rollup_bundle:rollup_with_build_optimizer"),
|
||||
),
|
||||
"_rollup_config_tmpl": attr.label(
|
||||
default = Label("//dev-infra/benchmark/ng_rollup_bundle:rollup.config.js"),
|
||||
allow_single_file = True,
|
||||
),
|
||||
}
|
||||
|
||||
def _compute_node_modules_root(ctx):
|
||||
"""Computes the node_modules root from the node_modules and deps attributes.
|
||||
|
||||
Args:
|
||||
ctx: the skylark execution context
|
||||
|
||||
Returns:
|
||||
The node_modules root as a string
|
||||
"""
|
||||
node_modules_root = None
|
||||
for d in ctx.attr.deps:
|
||||
if NpmPackageInfo in d:
|
||||
possible_root = "/".join(["external", d[NpmPackageInfo].workspace, "node_modules"])
|
||||
if not node_modules_root:
|
||||
node_modules_root = possible_root
|
||||
elif node_modules_root != possible_root:
|
||||
fail("All npm dependencies need to come from a single workspace. Found '%s' and '%s'." % (node_modules_root, possible_root))
|
||||
if not node_modules_root:
|
||||
# there are no fine grained deps but we still need a node_modules_root even if its empty
|
||||
node_modules_root = "external/npm/node_modules"
|
||||
return node_modules_root
|
||||
|
||||
# Avoid using non-normalized paths (workspace/../other_workspace/path)
|
||||
def _to_manifest_path(ctx, file):
|
||||
if file.short_path.startswith("../"):
|
||||
return file.short_path[3:]
|
||||
else:
|
||||
return ctx.workspace_name + "/" + file.short_path
|
||||
|
||||
# Expand entry_point into runfiles and strip the file extension
|
||||
def _esm5_entry_point_path(ctx):
|
||||
return _to_manifest_path(ctx, ctx.file.entry_point)[:-(len(ctx.file.entry_point.extension) + 1)]
|
||||
|
||||
def _no_ext(f):
|
||||
return f.short_path[:-len(f.extension) - 1]
|
||||
|
||||
def _resolve_js_input(f, inputs):
|
||||
if f.extension == "js" or f.extension == "mjs":
|
||||
return f
|
||||
|
||||
# look for corresponding js file in inputs
|
||||
no_ext = _no_ext(f)
|
||||
for i in inputs:
|
||||
if i.extension == "js" or i.extension == "mjs":
|
||||
if _no_ext(i) == no_ext:
|
||||
return i
|
||||
fail("Could not find corresponding javascript entry point for %s. Add the %s.js to your deps." % (f.path, no_ext))
|
||||
|
||||
def _write_rollup_config(ctx, root_dir, build_optimizer, filename = "_%s.rollup.conf.js"):
|
||||
"""Generate a rollup config file.
|
||||
|
||||
Args:
|
||||
ctx: Bazel rule execution context
|
||||
root_dir: root directory for module resolution
|
||||
build_optimizer: whether to enable Build Optimizer plugin
|
||||
filename: output filename pattern (defaults to `_%s.rollup.conf.js`)
|
||||
|
||||
Returns:
|
||||
The rollup config file. See https://rollupjs.org/guide/en#configuration-files
|
||||
"""
|
||||
config = ctx.actions.declare_file(filename % ctx.label.name)
|
||||
|
||||
mappings = dict()
|
||||
all_deps = ctx.attr.deps + ctx.attr.srcs
|
||||
for dep in all_deps:
|
||||
if hasattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR):
|
||||
for k, v in getattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR).items():
|
||||
if k in mappings and mappings[k] != v:
|
||||
fail(("duplicate module mapping at %s: %s maps to both %s and %s" %
|
||||
(dep.label, k, mappings[k], v)), "deps")
|
||||
mappings[k] = v
|
||||
|
||||
globals = {}
|
||||
external = []
|
||||
if ctx.attr.globals:
|
||||
globals = ctx.attr.globals.items()
|
||||
external = ctx.attr.globals.keys()
|
||||
|
||||
ctx.actions.expand_template(
|
||||
output = config,
|
||||
template = ctx.file._rollup_config_tmpl,
|
||||
substitutions = {
|
||||
"TMPL_banner_file": "\"%s\"" % ctx.file.license_banner.path if ctx.file.license_banner else "undefined",
|
||||
"TMPL_build_optimizer": "true" if build_optimizer else "false",
|
||||
"TMPL_module_mappings": str(mappings),
|
||||
"TMPL_node_modules_root": _compute_node_modules_root(ctx),
|
||||
"TMPL_root_dir": root_dir,
|
||||
"TMPL_stamp_data": "\"%s\"" % ctx.version_file.path if ctx.version_file else "undefined",
|
||||
"TMPL_workspace_name": ctx.workspace_name,
|
||||
"TMPL_external": ", ".join(["'%s'" % e for e in external]),
|
||||
"TMPL_globals": ", ".join(["'%s': '%s'" % g for g in globals]),
|
||||
"TMPL_ivy_enabled": "true" if ctx.var.get("angular_ivy_enabled", None) == "True" else "false",
|
||||
},
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
def _filter_js_inputs(all_inputs):
|
||||
all_inputs_list = all_inputs.to_list() if type(all_inputs) == type(depset()) else all_inputs
|
||||
return [
|
||||
f
|
||||
for f in all_inputs_list
|
||||
if f.path.endswith(".js") or f.path.endswith(".mjs") or f.path.endswith(".json")
|
||||
]
|
||||
|
||||
def _run_rollup(ctx, entry_point_path, sources, config):
|
||||
args = ctx.actions.args()
|
||||
args.add("--config", config.path)
|
||||
args.add("--input", entry_point_path)
|
||||
args.add("--output.file", ctx.outputs.bundle)
|
||||
args.add("--output.name", ctx.attr.global_name if ctx.attr.global_name else ctx.label.name)
|
||||
args.add("--output.format", ctx.attr.format)
|
||||
args.add("--output.sourcemap")
|
||||
args.add("--output.sourcemapFile", ctx.outputs.sourcemap)
|
||||
|
||||
# We will produce errors as needed. Anything else is spammy: a well-behaved
|
||||
# bazel rule prints nothing on success.
|
||||
args.add("--silent")
|
||||
|
||||
args.add("--preserveSymlinks")
|
||||
|
||||
direct_inputs = [config]
|
||||
|
||||
# Also include files from npm fine grained deps as inputs.
|
||||
# These deps are identified by the NpmPackageInfo provider.
|
||||
for d in ctx.attr.deps:
|
||||
if NpmPackageInfo in d:
|
||||
# Note: we can't avoid calling .to_list() on sources
|
||||
direct_inputs.extend(_filter_js_inputs(d[NpmPackageInfo].sources.to_list()))
|
||||
|
||||
if ctx.file.license_banner:
|
||||
direct_inputs.append(ctx.file.license_banner)
|
||||
if ctx.version_file:
|
||||
direct_inputs.append(ctx.version_file)
|
||||
|
||||
ctx.actions.run(
|
||||
progress_message = "Bundling JavaScript %s [rollup]" % ctx.outputs.bundle.short_path,
|
||||
executable = ctx.executable._rollup,
|
||||
inputs = depset(direct_inputs, transitive = [sources]),
|
||||
outputs = [ctx.outputs.bundle, ctx.outputs.sourcemap],
|
||||
arguments = [args],
|
||||
)
|
||||
|
||||
def _ng_rollup_bundle_impl(ctx):
|
||||
if ctx.attr.esm5_sources:
|
||||
# Use esm5 sources and build optimzier if ctx.attr.build_optimizer is set
|
||||
rollup_config = _write_rollup_config(ctx, build_optimizer = ctx.attr.build_optimizer, root_dir = "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]))
|
||||
_run_rollup(ctx, _esm5_entry_point_path(ctx), flatten_esm5(ctx), rollup_config)
|
||||
else:
|
||||
# Use esm2015 sources and no build optimzier
|
||||
rollup_config = _write_rollup_config(ctx, build_optimizer = False, root_dir = ctx.bin_dir.path)
|
||||
esm2015_files_depsets = []
|
||||
for dep in ctx.attr.deps:
|
||||
if JSEcmaScriptModuleInfo in dep:
|
||||
esm2015_files_depsets.append(dep[JSEcmaScriptModuleInfo].sources)
|
||||
esm2015_files = depset(transitive = esm2015_files_depsets)
|
||||
entry_point_path = _to_manifest_path(ctx, _resolve_js_input(ctx.file.entry_point, esm2015_files.to_list()))
|
||||
_run_rollup(ctx, entry_point_path, esm2015_files, rollup_config)
|
||||
|
||||
return DefaultInfo(files = depset([ctx.outputs.bundle, ctx.outputs.sourcemap]))
|
||||
|
||||
_ng_rollup_bundle = rule(
|
||||
implementation = _ng_rollup_bundle_impl,
|
||||
attrs = _NG_ROLLUP_BUNDLE_ATTRS,
|
||||
outputs = _NG_ROLLUP_BUNDLE_OUTPUTS,
|
||||
)
|
||||
"""
|
||||
Run [Rollup] with the [Build Optimizer] plugin and use esm5 inputs.
|
||||
|
||||
[Rollup]: https://rollupjs.org/
|
||||
[Build Optimizer]: https://www.npmjs.com/package/@angular-devkit/build-optimizer
|
||||
"""
|
||||
|
||||
def ng_rollup_bundle(name, **kwargs):
|
||||
"""Rollup with Build Optimizer on esm5 inputs.
|
||||
|
||||
This provides a variant of the [legacy rollup_bundle] rule that works better for Angular apps.
|
||||
|
||||
Runs [rollup], [terser_minified] and [brotli] to produce a number of output bundles.
|
||||
|
||||
es5 : "%{name}.js"
|
||||
es5 minified : "%{name}.min.js"
|
||||
es5 minified (compressed) : "%{name}.min.js.br",
|
||||
es5 minified (debug) : "%{name}.min_debug.js"
|
||||
es2015 : "%{name}.es2015.js"
|
||||
es2015 minified : "%{name}.min.es2015.js"
|
||||
es2015 minified (compressed) : "%{name}.min.js.es2015.br",
|
||||
es2015 minified (debug) : "%{name}.min_debug.es2015.js"
|
||||
|
||||
It registers `@angular-devkit/build-optimizer` as a rollup plugin, to get
|
||||
better optimization. It also uses ESM5 format inputs, as this is what
|
||||
build-optimizer is hard-coded to look for and transform.
|
||||
|
||||
[legacy rollup_bundle]: https://github.com/bazelbuild/rules_nodejs/blob/0.38.3/internal/rollup/rollup_bundle.bzl
|
||||
[rollup]: https://rollupjs.org/guide/en/
|
||||
[terser_minified]: https://bazelbuild.github.io/rules_nodejs/Terser.html
|
||||
[brotli]: https://brotli.org/
|
||||
"""
|
||||
format = kwargs.pop("format", "iife")
|
||||
build_optimizer = kwargs.pop("build_optimizer", True)
|
||||
visibility = kwargs.pop("visibility", None)
|
||||
|
||||
# Common arguments for all terser_minified targets
|
||||
common_terser_args = {
|
||||
# As of terser 4.3.4 license comments are preserved by default. See
|
||||
# https://github.com/terser/terser/blob/master/CHANGELOG.md. We want to
|
||||
# maintain the comments off behavior. We pass the --comments flag with
|
||||
# a regex that always evaluates to false to do this.
|
||||
"args": ["--comments", "/bogus_string_to_suppress_all_comments^/"],
|
||||
"config_file": "//dev-infra/benchmark/ng_rollup_bundle:terser_config.json",
|
||||
"sourcemap": False,
|
||||
}
|
||||
|
||||
# TODO(gregmagolan): reduce this macro to just use the new @bazel/rollup rollup_bundle
|
||||
# once esm5 inputs are no longer needed. _ng_rollup_bundle is just here for esm5 support
|
||||
# and once that requirement is removed for Angular 10 then there is nothing that rule is doing
|
||||
# that the new @bazel/rollup rollup_bundle rule can't do.
|
||||
_ng_rollup_bundle(
|
||||
name = name,
|
||||
build_optimizer = build_optimizer,
|
||||
format = format,
|
||||
visibility = visibility,
|
||||
**kwargs
|
||||
)
|
||||
terser_minified(name = name + ".min", src = name, visibility = visibility, **common_terser_args)
|
||||
native.filegroup(name = name + ".min.js", srcs = [name + ".min"], visibility = visibility)
|
||||
terser_minified(name = name + ".min_debug", src = name, debug = True, visibility = visibility, **common_terser_args)
|
||||
native.filegroup(name = name + ".min_debug.js", srcs = [name + ".min_debug"], visibility = visibility)
|
||||
npm_package_bin(
|
||||
name = "_%s_brotli" % name,
|
||||
tool = "//dev-infra/benchmark/brotli-cli",
|
||||
data = [name + ".min.js"],
|
||||
outs = [name + ".min.js.br"],
|
||||
args = [
|
||||
"--output=$(execpath %s.min.js.br)" % name,
|
||||
"$(execpath %s.min.js)" % name,
|
||||
],
|
||||
visibility = visibility,
|
||||
)
|
||||
|
||||
_ng_rollup_bundle(
|
||||
name = name + ".es2015",
|
||||
esm5_sources = False,
|
||||
format = format,
|
||||
visibility = visibility,
|
||||
**kwargs
|
||||
)
|
||||
terser_minified(name = name + ".min.es2015", src = name + ".es2015", visibility = visibility, **common_terser_args)
|
||||
native.filegroup(name = name + ".min.es2015.js", srcs = [name + ".min.es2015"], visibility = visibility)
|
||||
terser_minified(name = name + ".min_debug.es2015", src = name + ".es2015", debug = True, visibility = visibility, **common_terser_args)
|
||||
native.filegroup(name = name + ".min_debug.es2015.js", srcs = [name + ".min_debug.es2015"], visibility = visibility)
|
||||
npm_package_bin(
|
||||
name = "_%s_es2015_brotli" % name,
|
||||
tool = "//dev-infra/benchmark/brotli-cli",
|
||||
data = [name + ".min.es2015.js"],
|
||||
outs = [name + ".min.es2015.js.br"],
|
||||
args = [
|
||||
"--output=$(execpath %s.min.es2015.js.br)" % name,
|
||||
"$(execpath %s.min.es2015.js)" % name,
|
||||
],
|
||||
visibility = visibility,
|
||||
)
|
||||
|
||||
def ls_rollup_bundle(name, **kwargs):
|
||||
"""A variant of ng_rollup_bundle for the language-service bundle
|
||||
|
||||
ls_rollup_bundle uses esm5 inputs, outputs AMD and does not use the build optimizer.
|
||||
"""
|
||||
visibility = kwargs.pop("visibility", None)
|
||||
|
||||
# Note: the output file is called "umd.js" because of historical reasons.
|
||||
# The format is actually AMD and not UMD, but we are afraid to rename
|
||||
# the file because that would likely break the IDE and other integrations that
|
||||
# have the path hardcoded in them.
|
||||
ng_rollup_bundle(
|
||||
name = name + ".umd",
|
||||
build_optimizer = False,
|
||||
format = "amd",
|
||||
visibility = visibility,
|
||||
**kwargs
|
||||
)
|
||||
native.alias(
|
||||
name = name,
|
||||
actual = name + ".umd",
|
||||
visibility = visibility,
|
||||
)
|
212
dev-infra/benchmark/ng_rollup_bundle/rollup.config.js
Normal file
212
dev-infra/benchmark/ng_rollup_bundle/rollup.config.js
Normal file
@ -0,0 +1,212 @@
|
||||
/**
|
||||
* @license
|
||||
* 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
|
||||
*/
|
||||
|
||||
// Rollup configuration
|
||||
// GENERATED BY Bazel
|
||||
|
||||
const buildOptimizer = require(
|
||||
'npm/node_modules/@angular-devkit/build-optimizer/src/build-optimizer/rollup-plugin.js');
|
||||
const nodeResolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
const commonjs = require('rollup-plugin-commonjs');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
function log_verbose(...m) {
|
||||
// This is a template file so we use __filename to output the actual filename
|
||||
if (!!process.env['VERBOSE_LOGS']) console.error(`[${path.basename(__filename)}]`, ...m);
|
||||
}
|
||||
|
||||
const workspaceName = 'TMPL_workspace_name';
|
||||
const useBuildOptimzier = TMPL_build_optimizer;
|
||||
const rootDir = 'TMPL_root_dir';
|
||||
const bannerFile = TMPL_banner_file;
|
||||
const stampData = TMPL_stamp_data;
|
||||
const moduleMappings = TMPL_module_mappings;
|
||||
const nodeModulesRoot = 'TMPL_node_modules_root';
|
||||
const ivyEnabled = TMPL_ivy_enabled;
|
||||
|
||||
log_verbose(`running with
|
||||
cwd: ${process.cwd()}
|
||||
workspaceName: ${workspaceName}
|
||||
useBuildOptimzier: ${useBuildOptimzier}
|
||||
rootDir: ${rootDir}
|
||||
bannerFile: ${bannerFile}
|
||||
stampData: ${stampData}
|
||||
moduleMappings: ${JSON.stringify(moduleMappings)}
|
||||
nodeModulesRoot: ${nodeModulesRoot}
|
||||
ivyEnabled: ${ivyEnabled}
|
||||
`);
|
||||
|
||||
function fileExists(filePath) {
|
||||
try {
|
||||
return fs.statSync(filePath).isFile();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// This resolver mimics the TypeScript Path Mapping feature, which lets us resolve
|
||||
// modules based on a mapping of short names to paths.
|
||||
function resolveBazel(
|
||||
importee, importer, baseDir = process.cwd(), resolve = require.resolve, root = rootDir) {
|
||||
log_verbose(`resolving '${importee}' from ${importer}`);
|
||||
|
||||
function resolveInRootDir(importee) {
|
||||
function tryImportee(importee) {
|
||||
var candidate = path.join(baseDir, root, importee);
|
||||
log_verbose(`try to resolve '${importee}' at '${candidate}'`);
|
||||
try {
|
||||
var result = resolve(candidate);
|
||||
return result;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
// Attempt in the following order {importee}.mjs, {importee}/index.mjs,
|
||||
// {importee}, {importee}.js, {importee}/index.js. If an .mjs file is
|
||||
// available it should be resolved as rollup cannot handle the .js files
|
||||
// generated by ts_library as they are not esm. When rolling up esm5 files
|
||||
// these are re-rooted so it is not an issue.
|
||||
// TODO(gregmagolan): clean this up in the future as the .mjs es2015 outputs
|
||||
// along side the .js es5 outputs from ts_library creates this unusual situation for
|
||||
// which we can't rely on standard node module resolution to do the right thing.
|
||||
// In the future ts_library (or equivalent) should only produce a single flavor of
|
||||
// output and ng_rollup_bundle should also just use the use the vanilla rollup_bundle
|
||||
// rule without the need for a custom bazel resolver.
|
||||
return tryImportee(`${importee}.mjs`) || tryImportee(`${importee}/index.mjs`) ||
|
||||
tryImportee(importee) || tryImportee(`${importee}.js`) ||
|
||||
tryImportee(`${importee}/index.js`);
|
||||
}
|
||||
|
||||
// Since mappings are always in POSIX paths, when comparing the importee to mappings
|
||||
// we should normalize the importee.
|
||||
// Having it normalized is also useful to determine relative paths.
|
||||
const normalizedImportee = importee.replace(/\\/g, '/');
|
||||
|
||||
// If import is fully qualified then resolve it directly
|
||||
if (fileExists(importee)) {
|
||||
log_verbose(`resolved fully qualified '${importee}'`);
|
||||
return importee;
|
||||
}
|
||||
|
||||
var resolved;
|
||||
if (normalizedImportee.startsWith('./') || normalizedImportee.startsWith('../')) {
|
||||
// relative import
|
||||
if (importer) {
|
||||
let importerRootRelative = path.dirname(importer);
|
||||
const relative = path.relative(path.join(baseDir, root), importerRootRelative);
|
||||
if (!relative.startsWith('.')) {
|
||||
importerRootRelative = relative;
|
||||
}
|
||||
resolved = path.join(importerRootRelative, importee);
|
||||
} else {
|
||||
throw new Error('cannot resolve relative paths without an importer');
|
||||
}
|
||||
if (resolved) resolved = resolveInRootDir(resolved);
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
// possible workspace import or external import if importee matches a module
|
||||
// mapping
|
||||
for (const k in moduleMappings) {
|
||||
if (normalizedImportee == k || normalizedImportee.startsWith(k + '/')) {
|
||||
// replace the root module name on a mappings match
|
||||
// note that the module_root attribute is intended to be used for type-checking
|
||||
// so it uses eg. "index.d.ts". At runtime, we have only index.js, so we strip the
|
||||
// .d.ts suffix and let node require.resolve do its thing.
|
||||
var v = moduleMappings[k].replace(/\.d\.ts$/, '');
|
||||
const mappedImportee = path.join(v, normalizedImportee.slice(k.length + 1));
|
||||
log_verbose(`module mapped '${importee}' to '${mappedImportee}'`);
|
||||
resolved = resolveInRootDir(mappedImportee);
|
||||
if (resolved) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
// workspace import
|
||||
const userWorkspacePath = path.relative(workspaceName, importee);
|
||||
resolved = resolveInRootDir(userWorkspacePath.startsWith('..') ? importee : userWorkspacePath);
|
||||
}
|
||||
|
||||
if (resolved) {
|
||||
log_verbose(`resolved to ${resolved}`);
|
||||
} else {
|
||||
log_verbose(`allowing rollup to resolve '${importee}' with node module resolution`);
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// We make mainFields match what was the default for plugin-node-resolve <4.2.0
|
||||
// when mainFields was introduced. Resolving to 'browser' or 'es2015' first breaks
|
||||
// some of the benchmarks such as `//modules/benchmarks/src/expanding_rows:perf_chromium-local`.
|
||||
// See https://app.circleci.com/jobs/github/angular/angular/507444 &&
|
||||
// https://app.circleci.com/jobs/github/angular/angular/507442 for affected tests.
|
||||
const mainFields = ['module', 'main'];
|
||||
const ngccMainFields = mainFields.map(f => `${f}_ivy_ngcc`);
|
||||
|
||||
let plugins = [
|
||||
{
|
||||
name: 'resolveBazel',
|
||||
resolveId: resolveBazel,
|
||||
},
|
||||
nodeResolve({
|
||||
// If Ivy is enabled, we need to make sure that the module resolution prioritizes ngcc
|
||||
// processed entry-point fields. Ngcc adds special fields to `package.json` files of
|
||||
// modules that have been processed. Prioritizing these fields matches the Angular CLIs
|
||||
// behavior for supporting Ivy. We need to support ngcc because `ng_rollup_bundle` rule is
|
||||
// shared with other repositories that consume Angular from NPM (w/ ngcc).
|
||||
// https://github.com/angular/angular-cli/blob/1a1ceb609b9a87c4021cce3a6f0fc6d167cd09d2/packages/ngtools/webpack/src/angular_compiler_plugin.ts#L918-L920
|
||||
mainFields: ivyEnabled ? [...ngccMainFields, ...mainFields] : mainFields,
|
||||
jail: process.cwd(),
|
||||
customResolveOptions: {moduleDirectory: nodeModulesRoot}
|
||||
}),
|
||||
commonjs({ignoreGlobal: true}),
|
||||
sourcemaps(),
|
||||
];
|
||||
|
||||
if (useBuildOptimzier) {
|
||||
plugins = [
|
||||
buildOptimizer.default({
|
||||
sideEffectFreeModules: [
|
||||
'.esm5/packages/core/src',
|
||||
'.esm5/packages/common/src',
|
||||
'.esm5/packages/compiler/src',
|
||||
'.esm5/packages/platform-browser/src',
|
||||
]
|
||||
}),
|
||||
].concat(plugins);
|
||||
}
|
||||
|
||||
let banner = '';
|
||||
if (bannerFile) {
|
||||
banner = fs.readFileSync(bannerFile, {encoding: 'utf-8'});
|
||||
if (stampData) {
|
||||
const versionTag = fs.readFileSync(stampData, {encoding: 'utf-8'})
|
||||
.split('\n')
|
||||
.find(s => s.startsWith('BUILD_SCM_VERSION'));
|
||||
// Don't assume BUILD_SCM_VERSION exists
|
||||
if (versionTag) {
|
||||
const version = versionTag.split(' ')[1].trim();
|
||||
banner = banner.replace(/0.0.0-PLACEHOLDER/, version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const config = {
|
||||
plugins,
|
||||
external: [TMPL_external],
|
||||
output: {
|
||||
globals: {TMPL_globals},
|
||||
banner,
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
12
dev-infra/benchmark/ng_rollup_bundle/terser_config.json
Normal file
12
dev-infra/benchmark/ng_rollup_bundle/terser_config.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compress": {
|
||||
"global_defs": {"ngDevMode": false, "ngI18nClosureMode": false, "ngJitMode": false},
|
||||
"keep_fnames": "bazel_no_debug",
|
||||
"passes": 3,
|
||||
"pure_getters": true,
|
||||
"reduce_funcs": "bazel_no_debug",
|
||||
"reduce_vars": "bazel_no_debug",
|
||||
"sequences": "bazel_no_debug"
|
||||
},
|
||||
"mangle": "bazel_no_debug"
|
||||
}
|
12
dev-infra/tools/BUILD.bazel
Normal file
12
dev-infra/tools/BUILD.bazel
Normal file
@ -0,0 +1,12 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
# Make source files available for distribution via pkg_npm
|
||||
filegroup(
|
||||
name = "files",
|
||||
srcs = glob(["*"]) + [
|
||||
"//dev-infra/benchmark/brotli-cli:files",
|
||||
"//dev-infra/benchmark/browsers:files",
|
||||
"//dev-infra/benchmark/component_benchmark:files",
|
||||
"//dev-infra/benchmark/ng_rollup_bundle:files",
|
||||
],
|
||||
)
|
Reference in New Issue
Block a user