1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2021 The Android Open Source Project 2*6dbdd20aSAndroid Build Coastguard Worker// 3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*6dbdd20aSAndroid Build Coastguard Worker// 7*6dbdd20aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*6dbdd20aSAndroid Build Coastguard Worker// 9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License. 14*6dbdd20aSAndroid Build Coastguard Worker 15*6dbdd20aSAndroid Build Coastguard Worker'use strict'; 16*6dbdd20aSAndroid Build Coastguard Worker 17*6dbdd20aSAndroid Build Coastguard Worker 18*6dbdd20aSAndroid Build Coastguard Worker// This script takes care of: 19*6dbdd20aSAndroid Build Coastguard Worker// - The build process for the whole UI and the chrome extension. 20*6dbdd20aSAndroid Build Coastguard Worker// - The HTTP dev-server with live-reload capabilities. 21*6dbdd20aSAndroid Build Coastguard Worker// The reason why this is a hand-rolled script rather than a conventional build 22*6dbdd20aSAndroid Build Coastguard Worker// system is keeping incremental build fast and maintaining the set of 23*6dbdd20aSAndroid Build Coastguard Worker// dependencies contained. 24*6dbdd20aSAndroid Build Coastguard Worker// The only way to keep incremental build fast (i.e. O(seconds) for the 25*6dbdd20aSAndroid Build Coastguard Worker// edit-one-line -> reload html cycles) is to run both the TypeScript compiler 26*6dbdd20aSAndroid Build Coastguard Worker// and the rollup bundler in --watch mode. Any other attempt, leads to O(10s) 27*6dbdd20aSAndroid Build Coastguard Worker// incremental-build times. 28*6dbdd20aSAndroid Build Coastguard Worker// This script allows mixing build tools that support --watch mode (tsc and 29*6dbdd20aSAndroid Build Coastguard Worker// rollup) and auto-triggering-on-file-change rules via fs.watch. 30*6dbdd20aSAndroid Build Coastguard Worker// When invoked without any argument (e.g., for production builds), this script 31*6dbdd20aSAndroid Build Coastguard Worker// just runs all the build tasks serially. It doesn't to do any mtime-based 32*6dbdd20aSAndroid Build Coastguard Worker// check, it always re-runs all the tasks. 33*6dbdd20aSAndroid Build Coastguard Worker// When invoked with --watch, it mounts a pipeline of tasks based on fs.watch 34*6dbdd20aSAndroid Build Coastguard Worker// and runs them together with tsc --watch and rollup --watch. 35*6dbdd20aSAndroid Build Coastguard Worker// The output directory structure is carefully crafted so that any change to UI 36*6dbdd20aSAndroid Build Coastguard Worker// sources causes cascading triggers of the next steps. 37*6dbdd20aSAndroid Build Coastguard Worker// The overall build graph looks as follows: 38*6dbdd20aSAndroid Build Coastguard Worker// +----------------+ +-----------------------------+ 39*6dbdd20aSAndroid Build Coastguard Worker// | protos/*.proto |----->| pbjs out/tsc/gen/protos.js |--+ 40*6dbdd20aSAndroid Build Coastguard Worker// +----------------+ +-----------------------------+ | 41*6dbdd20aSAndroid Build Coastguard Worker// +-----------------------------+ | 42*6dbdd20aSAndroid Build Coastguard Worker// | pbts out/tsc/gen/protos.d.ts|<-+ 43*6dbdd20aSAndroid Build Coastguard Worker// +-----------------------------+ 44*6dbdd20aSAndroid Build Coastguard Worker// | 45*6dbdd20aSAndroid Build Coastguard Worker// V +-------------------------+ 46*6dbdd20aSAndroid Build Coastguard Worker// +---------+ +-----+ | out/tsc/frontend/*.js | 47*6dbdd20aSAndroid Build Coastguard Worker// | ui/*.ts |------------->| tsc |-> +-------------------------+ +--------+ 48*6dbdd20aSAndroid Build Coastguard Worker// +---------+ +-----+ | out/tsc/controller/*.js |-->| rollup | 49*6dbdd20aSAndroid Build Coastguard Worker// ^ +-------------------------+ +--------+ 50*6dbdd20aSAndroid Build Coastguard Worker// +------------+ | out/tsc/engine/*.js | | 51*6dbdd20aSAndroid Build Coastguard Worker// +-----------+ |*.wasm.js | +-------------------------+ | 52*6dbdd20aSAndroid Build Coastguard Worker// |ninja *.cc |->|*.wasm.d.ts | | 53*6dbdd20aSAndroid Build Coastguard Worker// +-----------+ |*.wasm |-----------------+ | 54*6dbdd20aSAndroid Build Coastguard Worker// +------------+ | | 55*6dbdd20aSAndroid Build Coastguard Worker// V V 56*6dbdd20aSAndroid Build Coastguard Worker// +-----------+ +------+ +------------------------------------------------+ 57*6dbdd20aSAndroid Build Coastguard Worker// | ui/*.scss |->| scss |--->| Final out/dist/ dir | 58*6dbdd20aSAndroid Build Coastguard Worker// +-----------+ +------+ +------------------------------------------------+ 59*6dbdd20aSAndroid Build Coastguard Worker// +----------------------+ | +----------+ +---------+ +--------------------+| 60*6dbdd20aSAndroid Build Coastguard Worker// | src/assets/*.png | | | assets/ | |*.wasm.js| | frontend_bundle.js || 61*6dbdd20aSAndroid Build Coastguard Worker// +----------------------+ | | *.css | |*.wasm | +--------------------+| 62*6dbdd20aSAndroid Build Coastguard Worker// | buildtools/typefaces |-->| | *.png | +---------+ | engine_bundle.js || 63*6dbdd20aSAndroid Build Coastguard Worker// +----------------------+ | | *.woff2 | +--------------------+| 64*6dbdd20aSAndroid Build Coastguard Worker// | buildtools/legacy_tv | | | tv.html | |traceconv_bundle.js || 65*6dbdd20aSAndroid Build Coastguard Worker// +----------------------+ | +----------+ +--------------------+| 66*6dbdd20aSAndroid Build Coastguard Worker// +------------------------------------------------+ 67*6dbdd20aSAndroid Build Coastguard Worker 68*6dbdd20aSAndroid Build Coastguard Workerconst argparse = require('argparse'); 69*6dbdd20aSAndroid Build Coastguard Workerconst childProcess = require('child_process'); 70*6dbdd20aSAndroid Build Coastguard Workerconst crypto = require('crypto'); 71*6dbdd20aSAndroid Build Coastguard Workerconst fs = require('fs'); 72*6dbdd20aSAndroid Build Coastguard Workerconst http = require('http'); 73*6dbdd20aSAndroid Build Coastguard Workerconst path = require('path'); 74*6dbdd20aSAndroid Build Coastguard Workerconst pjoin = path.join; 75*6dbdd20aSAndroid Build Coastguard Worker 76*6dbdd20aSAndroid Build Coastguard Workerconst ROOT_DIR = path.dirname(__dirname); // The repo root. 77*6dbdd20aSAndroid Build Coastguard Workerconst VERSION_SCRIPT = pjoin(ROOT_DIR, 'tools/write_version_header.py'); 78*6dbdd20aSAndroid Build Coastguard Workerconst GEN_IMPORTS_SCRIPT = pjoin(ROOT_DIR, 'tools/gen_ui_imports'); 79*6dbdd20aSAndroid Build Coastguard Worker 80*6dbdd20aSAndroid Build Coastguard Workerconst cfg = { 81*6dbdd20aSAndroid Build Coastguard Worker minifyJs: '', 82*6dbdd20aSAndroid Build Coastguard Worker watch: false, 83*6dbdd20aSAndroid Build Coastguard Worker verbose: false, 84*6dbdd20aSAndroid Build Coastguard Worker debug: false, 85*6dbdd20aSAndroid Build Coastguard Worker bigtrace: false, 86*6dbdd20aSAndroid Build Coastguard Worker startHttpServer: false, 87*6dbdd20aSAndroid Build Coastguard Worker httpServerListenHost: '127.0.0.1', 88*6dbdd20aSAndroid Build Coastguard Worker httpServerListenPort: 10000, 89*6dbdd20aSAndroid Build Coastguard Worker wasmModules: ['trace_processor', 'traceconv', 'trace_config_utils'], 90*6dbdd20aSAndroid Build Coastguard Worker crossOriginIsolation: false, 91*6dbdd20aSAndroid Build Coastguard Worker testFilter: '', 92*6dbdd20aSAndroid Build Coastguard Worker noOverrideGnArgs: false, 93*6dbdd20aSAndroid Build Coastguard Worker 94*6dbdd20aSAndroid Build Coastguard Worker // The fields below will be changed by main() after cmdline parsing. 95*6dbdd20aSAndroid Build Coastguard Worker // Directory structure: 96*6dbdd20aSAndroid Build Coastguard Worker // out/xxx/ -> outDir : Root build dir, for both ninja/wasm and UI. 97*6dbdd20aSAndroid Build Coastguard Worker // ui/ -> outUiDir : UI dir. All outputs from this script. 98*6dbdd20aSAndroid Build Coastguard Worker // tsc/ -> outTscDir : Transpiled .ts -> .js. 99*6dbdd20aSAndroid Build Coastguard Worker // gen/ -> outGenDir : Auto-generated .ts/.js (e.g. protos). 100*6dbdd20aSAndroid Build Coastguard Worker // dist/ -> outDistRootDir : Only index.html and service_worker.js 101*6dbdd20aSAndroid Build Coastguard Worker // v1.2/ -> outDistDir : JS bundles and assets 102*6dbdd20aSAndroid Build Coastguard Worker // chrome_extension/ : Chrome extension. 103*6dbdd20aSAndroid Build Coastguard Worker outDir: pjoin(ROOT_DIR, 'out/ui'), 104*6dbdd20aSAndroid Build Coastguard Worker version: '', // v1.2.3, derived from the CHANGELOG + git. 105*6dbdd20aSAndroid Build Coastguard Worker outUiDir: '', 106*6dbdd20aSAndroid Build Coastguard Worker outUiTestArtifactsDir: '', 107*6dbdd20aSAndroid Build Coastguard Worker outDistRootDir: '', 108*6dbdd20aSAndroid Build Coastguard Worker outTscDir: '', 109*6dbdd20aSAndroid Build Coastguard Worker outGenDir: '', 110*6dbdd20aSAndroid Build Coastguard Worker outDistDir: '', 111*6dbdd20aSAndroid Build Coastguard Worker outExtDir: '', 112*6dbdd20aSAndroid Build Coastguard Worker outBigtraceDistDir: '', 113*6dbdd20aSAndroid Build Coastguard Worker outOpenPerfettoTraceDistDir: '', 114*6dbdd20aSAndroid Build Coastguard Worker}; 115*6dbdd20aSAndroid Build Coastguard Worker 116*6dbdd20aSAndroid Build Coastguard Workerconst RULES = [ 117*6dbdd20aSAndroid Build Coastguard Worker {r: /ui\/src\/assets\/index.html/, f: copyIndexHtml}, 118*6dbdd20aSAndroid Build Coastguard Worker {r: /ui\/src\/assets\/bigtrace.html/, f: copyBigtraceHtml}, 119*6dbdd20aSAndroid Build Coastguard Worker {r: /ui\/src\/open_perfetto_trace\/index.html/, f: copyOpenPerfettoTraceHtml}, 120*6dbdd20aSAndroid Build Coastguard Worker {r: /ui\/src\/assets\/((.*)[.]png)/, f: copyAssets}, 121*6dbdd20aSAndroid Build Coastguard Worker {r: /buildtools\/typefaces\/(.+[.]woff2)/, f: copyAssets}, 122*6dbdd20aSAndroid Build Coastguard Worker {r: /buildtools\/catapult_trace_viewer\/(.+(js|html))/, f: copyAssets}, 123*6dbdd20aSAndroid Build Coastguard Worker {r: /ui\/src\/assets\/.+[.]scss/, f: compileScss}, 124*6dbdd20aSAndroid Build Coastguard Worker {r: /ui\/src\/chrome_extension\/.*/, f: copyExtensionAssets}, 125*6dbdd20aSAndroid Build Coastguard Worker {r: /.*\/dist\/.+\/(?!manifest\.json).*/, f: genServiceWorkerManifestJson}, 126*6dbdd20aSAndroid Build Coastguard Worker {r: /.*\/dist\/.*[.](js|html|css|wasm)$/, f: notifyLiveServer}, 127*6dbdd20aSAndroid Build Coastguard Worker]; 128*6dbdd20aSAndroid Build Coastguard Worker 129*6dbdd20aSAndroid Build Coastguard Workerconst tasks = []; 130*6dbdd20aSAndroid Build Coastguard Workerlet tasksTot = 0; 131*6dbdd20aSAndroid Build Coastguard Workerlet tasksRan = 0; 132*6dbdd20aSAndroid Build Coastguard Workerconst httpWatches = []; 133*6dbdd20aSAndroid Build Coastguard Workerconst tStart = Date.now(); 134*6dbdd20aSAndroid Build Coastguard Workerconst subprocesses = []; 135*6dbdd20aSAndroid Build Coastguard Worker 136*6dbdd20aSAndroid Build Coastguard Workerasync function main() { 137*6dbdd20aSAndroid Build Coastguard Worker const parser = new argparse.ArgumentParser(); 138*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--out', {help: 'Output directory'}); 139*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--minify-js', { 140*6dbdd20aSAndroid Build Coastguard Worker help: 'Minify js files', 141*6dbdd20aSAndroid Build Coastguard Worker choices: ['preserve_comments', 'all'], 142*6dbdd20aSAndroid Build Coastguard Worker }); 143*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--watch', '-w', {action: 'store_true'}); 144*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--serve', '-s', {action: 'store_true'}); 145*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--serve-host', {help: '--serve bind host'}); 146*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--serve-port', {help: '--serve bind port', type: 'int'}); 147*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--verbose', '-v', {action: 'store_true'}); 148*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--no-build', '-n', {action: 'store_true'}); 149*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--no-wasm', '-W', {action: 'store_true'}); 150*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--run-unittests', '-t', {action: 'store_true'}); 151*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--debug', '-d', {action: 'store_true'}); 152*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--bigtrace', {action: 'store_true'}); 153*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--open-perfetto-trace', {action: 'store_true'}); 154*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--interactive', '-i', {action: 'store_true'}); 155*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--rebaseline', '-r', {action: 'store_true'}); 156*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--no-depscheck', {action: 'store_true'}); 157*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--cross-origin-isolation', {action: 'store_true'}); 158*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--test-filter', '-f', { 159*6dbdd20aSAndroid Build Coastguard Worker help: 'filter Jest tests by regex, e.g. \'chrome_render\'', 160*6dbdd20aSAndroid Build Coastguard Worker }); 161*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--no-override-gn-args', {action: 'store_true'}); 162*6dbdd20aSAndroid Build Coastguard Worker 163*6dbdd20aSAndroid Build Coastguard Worker const args = parser.parse_args(); 164*6dbdd20aSAndroid Build Coastguard Worker const clean = !args.no_build; 165*6dbdd20aSAndroid Build Coastguard Worker cfg.outDir = path.resolve(ensureDir(args.out || cfg.outDir)); 166*6dbdd20aSAndroid Build Coastguard Worker cfg.outUiDir = ensureDir(pjoin(cfg.outDir, 'ui'), clean); 167*6dbdd20aSAndroid Build Coastguard Worker cfg.outUiTestArtifactsDir = ensureDir(pjoin(cfg.outDir, 'ui-test-artifacts')); 168*6dbdd20aSAndroid Build Coastguard Worker cfg.outExtDir = ensureDir(pjoin(cfg.outUiDir, 'chrome_extension')); 169*6dbdd20aSAndroid Build Coastguard Worker cfg.outDistRootDir = ensureDir(pjoin(cfg.outUiDir, 'dist')); 170*6dbdd20aSAndroid Build Coastguard Worker const proc = exec('python3', [VERSION_SCRIPT, '--stdout'], {stdout: 'pipe'}); 171*6dbdd20aSAndroid Build Coastguard Worker cfg.version = proc.stdout.toString().trim(); 172*6dbdd20aSAndroid Build Coastguard Worker cfg.outDistDir = ensureDir(pjoin(cfg.outDistRootDir, cfg.version)); 173*6dbdd20aSAndroid Build Coastguard Worker cfg.outTscDir = ensureDir(pjoin(cfg.outUiDir, 'tsc')); 174*6dbdd20aSAndroid Build Coastguard Worker cfg.outGenDir = ensureDir(pjoin(cfg.outUiDir, 'tsc/gen')); 175*6dbdd20aSAndroid Build Coastguard Worker cfg.testFilter = args.test_filter || ''; 176*6dbdd20aSAndroid Build Coastguard Worker cfg.watch = !!args.watch; 177*6dbdd20aSAndroid Build Coastguard Worker cfg.verbose = !!args.verbose; 178*6dbdd20aSAndroid Build Coastguard Worker cfg.debug = !!args.debug; 179*6dbdd20aSAndroid Build Coastguard Worker cfg.bigtrace = !!args.bigtrace; 180*6dbdd20aSAndroid Build Coastguard Worker cfg.openPerfettoTrace = !!args.open_perfetto_trace; 181*6dbdd20aSAndroid Build Coastguard Worker cfg.startHttpServer = args.serve; 182*6dbdd20aSAndroid Build Coastguard Worker cfg.noOverrideGnArgs = !!args.no_override_gn_args; 183*6dbdd20aSAndroid Build Coastguard Worker if (args.minify_js) { 184*6dbdd20aSAndroid Build Coastguard Worker cfg.minifyJs = args.minify_js; 185*6dbdd20aSAndroid Build Coastguard Worker } 186*6dbdd20aSAndroid Build Coastguard Worker if (args.bigtrace) { 187*6dbdd20aSAndroid Build Coastguard Worker cfg.outBigtraceDistDir = ensureDir(pjoin(cfg.outDistDir, 'bigtrace')); 188*6dbdd20aSAndroid Build Coastguard Worker } 189*6dbdd20aSAndroid Build Coastguard Worker if (cfg.openPerfettoTrace) { 190*6dbdd20aSAndroid Build Coastguard Worker cfg.outOpenPerfettoTraceDistDir = ensureDir(pjoin(cfg.outDistRootDir, 191*6dbdd20aSAndroid Build Coastguard Worker 'open_perfetto_trace')); 192*6dbdd20aSAndroid Build Coastguard Worker } 193*6dbdd20aSAndroid Build Coastguard Worker if (args.serve_host) { 194*6dbdd20aSAndroid Build Coastguard Worker cfg.httpServerListenHost = args.serve_host; 195*6dbdd20aSAndroid Build Coastguard Worker } 196*6dbdd20aSAndroid Build Coastguard Worker if (args.serve_port) { 197*6dbdd20aSAndroid Build Coastguard Worker cfg.httpServerListenPort = args.serve_port; 198*6dbdd20aSAndroid Build Coastguard Worker } 199*6dbdd20aSAndroid Build Coastguard Worker if (args.interactive) { 200*6dbdd20aSAndroid Build Coastguard Worker process.env.PERFETTO_UI_TESTS_INTERACTIVE = '1'; 201*6dbdd20aSAndroid Build Coastguard Worker } 202*6dbdd20aSAndroid Build Coastguard Worker if (args.rebaseline) { 203*6dbdd20aSAndroid Build Coastguard Worker process.env.PERFETTO_UI_TESTS_REBASELINE = '1'; 204*6dbdd20aSAndroid Build Coastguard Worker } 205*6dbdd20aSAndroid Build Coastguard Worker if (args.cross_origin_isolation) { 206*6dbdd20aSAndroid Build Coastguard Worker cfg.crossOriginIsolation = true; 207*6dbdd20aSAndroid Build Coastguard Worker } 208*6dbdd20aSAndroid Build Coastguard Worker 209*6dbdd20aSAndroid Build Coastguard Worker process.on('SIGINT', () => { 210*6dbdd20aSAndroid Build Coastguard Worker console.log('\nSIGINT received. Killing all child processes and exiting'); 211*6dbdd20aSAndroid Build Coastguard Worker for (const proc of subprocesses) { 212*6dbdd20aSAndroid Build Coastguard Worker if (proc) proc.kill('SIGINT'); 213*6dbdd20aSAndroid Build Coastguard Worker } 214*6dbdd20aSAndroid Build Coastguard Worker process.exit(130); // 130 -> Same behavior of bash when killed by SIGINT. 215*6dbdd20aSAndroid Build Coastguard Worker }); 216*6dbdd20aSAndroid Build Coastguard Worker 217*6dbdd20aSAndroid Build Coastguard Worker if (!args.no_depscheck) { 218*6dbdd20aSAndroid Build Coastguard Worker // Check that deps are current before starting. 219*6dbdd20aSAndroid Build Coastguard Worker const installBuildDeps = pjoin(ROOT_DIR, 'tools/install-build-deps'); 220*6dbdd20aSAndroid Build Coastguard Worker const checkDepsPath = pjoin(cfg.outDir, '.check_deps'); 221*6dbdd20aSAndroid Build Coastguard Worker let args = [installBuildDeps, `--check-only=${checkDepsPath}`, '--ui']; 222*6dbdd20aSAndroid Build Coastguard Worker 223*6dbdd20aSAndroid Build Coastguard Worker if (process.platform === 'darwin') { 224*6dbdd20aSAndroid Build Coastguard Worker const result = childProcess.spawnSync('arch', ['-arm64', 'true']); 225*6dbdd20aSAndroid Build Coastguard Worker const isArm64Capable = result.status === 0; 226*6dbdd20aSAndroid Build Coastguard Worker if (isArm64Capable) { 227*6dbdd20aSAndroid Build Coastguard Worker const archArgs = [ 228*6dbdd20aSAndroid Build Coastguard Worker 'arch', 229*6dbdd20aSAndroid Build Coastguard Worker '-arch', 230*6dbdd20aSAndroid Build Coastguard Worker 'arm64', 231*6dbdd20aSAndroid Build Coastguard Worker ]; 232*6dbdd20aSAndroid Build Coastguard Worker args = archArgs.concat(args); 233*6dbdd20aSAndroid Build Coastguard Worker } 234*6dbdd20aSAndroid Build Coastguard Worker } 235*6dbdd20aSAndroid Build Coastguard Worker const cmd = args.shift(); 236*6dbdd20aSAndroid Build Coastguard Worker exec(cmd, args); 237*6dbdd20aSAndroid Build Coastguard Worker } 238*6dbdd20aSAndroid Build Coastguard Worker 239*6dbdd20aSAndroid Build Coastguard Worker console.log('Entering', cfg.outDir); 240*6dbdd20aSAndroid Build Coastguard Worker process.chdir(cfg.outDir); 241*6dbdd20aSAndroid Build Coastguard Worker 242*6dbdd20aSAndroid Build Coastguard Worker // Enqueue empty task. This is needed only for --no-build --serve. The HTTP 243*6dbdd20aSAndroid Build Coastguard Worker // server is started when the task queue reaches quiescence, but it takes at 244*6dbdd20aSAndroid Build Coastguard Worker // least one task for that. 245*6dbdd20aSAndroid Build Coastguard Worker addTask(() => {}); 246*6dbdd20aSAndroid Build Coastguard Worker 247*6dbdd20aSAndroid Build Coastguard Worker if (!args.no_build) { 248*6dbdd20aSAndroid Build Coastguard Worker updateSymlinks(); // Links //ui/out -> //out/xxx/ui/ 249*6dbdd20aSAndroid Build Coastguard Worker 250*6dbdd20aSAndroid Build Coastguard Worker buildWasm(args.no_wasm); 251*6dbdd20aSAndroid Build Coastguard Worker generateImports('ui/src/core_plugins', 'all_core_plugins'); 252*6dbdd20aSAndroid Build Coastguard Worker generateImports('ui/src/plugins', 'all_plugins'); 253*6dbdd20aSAndroid Build Coastguard Worker scanDir('ui/src/assets'); 254*6dbdd20aSAndroid Build Coastguard Worker scanDir('ui/src/chrome_extension'); 255*6dbdd20aSAndroid Build Coastguard Worker scanDir('buildtools/typefaces'); 256*6dbdd20aSAndroid Build Coastguard Worker scanDir('buildtools/catapult_trace_viewer'); 257*6dbdd20aSAndroid Build Coastguard Worker compileProtos(); 258*6dbdd20aSAndroid Build Coastguard Worker genVersion(); 259*6dbdd20aSAndroid Build Coastguard Worker generateStdlibDocs(); 260*6dbdd20aSAndroid Build Coastguard Worker 261*6dbdd20aSAndroid Build Coastguard Worker const tsProjects = [ 262*6dbdd20aSAndroid Build Coastguard Worker 'ui', 263*6dbdd20aSAndroid Build Coastguard Worker 'ui/src/service_worker' 264*6dbdd20aSAndroid Build Coastguard Worker ]; 265*6dbdd20aSAndroid Build Coastguard Worker if (cfg.bigtrace) tsProjects.push('ui/src/bigtrace'); 266*6dbdd20aSAndroid Build Coastguard Worker if (cfg.openPerfettoTrace) { 267*6dbdd20aSAndroid Build Coastguard Worker scanDir('ui/src/open_perfetto_trace'); 268*6dbdd20aSAndroid Build Coastguard Worker tsProjects.push('ui/src/open_perfetto_trace'); 269*6dbdd20aSAndroid Build Coastguard Worker } 270*6dbdd20aSAndroid Build Coastguard Worker 271*6dbdd20aSAndroid Build Coastguard Worker 272*6dbdd20aSAndroid Build Coastguard Worker for (const prj of tsProjects) { 273*6dbdd20aSAndroid Build Coastguard Worker transpileTsProject(prj); 274*6dbdd20aSAndroid Build Coastguard Worker } 275*6dbdd20aSAndroid Build Coastguard Worker 276*6dbdd20aSAndroid Build Coastguard Worker if (cfg.watch) { 277*6dbdd20aSAndroid Build Coastguard Worker for (const prj of tsProjects) { 278*6dbdd20aSAndroid Build Coastguard Worker transpileTsProject(prj, {watch: cfg.watch}); 279*6dbdd20aSAndroid Build Coastguard Worker } 280*6dbdd20aSAndroid Build Coastguard Worker } 281*6dbdd20aSAndroid Build Coastguard Worker 282*6dbdd20aSAndroid Build Coastguard Worker bundleJs('rollup.config.js'); 283*6dbdd20aSAndroid Build Coastguard Worker genServiceWorkerManifestJson(); 284*6dbdd20aSAndroid Build Coastguard Worker 285*6dbdd20aSAndroid Build Coastguard Worker // Watches the /dist. When changed: 286*6dbdd20aSAndroid Build Coastguard Worker // - Notifies the HTTP live reload clients. 287*6dbdd20aSAndroid Build Coastguard Worker // - Regenerates the ServiceWorker file map. 288*6dbdd20aSAndroid Build Coastguard Worker scanDir(cfg.outDistRootDir); 289*6dbdd20aSAndroid Build Coastguard Worker } 290*6dbdd20aSAndroid Build Coastguard Worker 291*6dbdd20aSAndroid Build Coastguard Worker // We should enter the loop only in watch mode, where tsc and rollup are 292*6dbdd20aSAndroid Build Coastguard Worker // asynchronous because they run in watch mode. 293*6dbdd20aSAndroid Build Coastguard Worker if (args.no_build && !isDistComplete()) { 294*6dbdd20aSAndroid Build Coastguard Worker console.log('No build was requested, but artifacts are not available.'); 295*6dbdd20aSAndroid Build Coastguard Worker console.log('In case of execution error, re-run without --no-build.'); 296*6dbdd20aSAndroid Build Coastguard Worker } 297*6dbdd20aSAndroid Build Coastguard Worker if (!args.no_build) { 298*6dbdd20aSAndroid Build Coastguard Worker const tStart = Date.now(); 299*6dbdd20aSAndroid Build Coastguard Worker while (!isDistComplete()) { 300*6dbdd20aSAndroid Build Coastguard Worker const secs = Math.ceil((Date.now() - tStart) / 1000); 301*6dbdd20aSAndroid Build Coastguard Worker process.stdout.write( 302*6dbdd20aSAndroid Build Coastguard Worker `\t\tWaiting for first build to complete... ${secs} s\r`); 303*6dbdd20aSAndroid Build Coastguard Worker await new Promise((r) => setTimeout(r, 500)); 304*6dbdd20aSAndroid Build Coastguard Worker } 305*6dbdd20aSAndroid Build Coastguard Worker } 306*6dbdd20aSAndroid Build Coastguard Worker if (cfg.watch) console.log('\nFirst build completed!'); 307*6dbdd20aSAndroid Build Coastguard Worker 308*6dbdd20aSAndroid Build Coastguard Worker if (cfg.startHttpServer) { 309*6dbdd20aSAndroid Build Coastguard Worker startServer(); 310*6dbdd20aSAndroid Build Coastguard Worker } 311*6dbdd20aSAndroid Build Coastguard Worker if (args.run_unittests) { 312*6dbdd20aSAndroid Build Coastguard Worker runTests('jest.unittest.config.js'); 313*6dbdd20aSAndroid Build Coastguard Worker } 314*6dbdd20aSAndroid Build Coastguard Worker} 315*6dbdd20aSAndroid Build Coastguard Worker 316*6dbdd20aSAndroid Build Coastguard Worker// ----------- 317*6dbdd20aSAndroid Build Coastguard Worker// Build rules 318*6dbdd20aSAndroid Build Coastguard Worker// ----------- 319*6dbdd20aSAndroid Build Coastguard Worker 320*6dbdd20aSAndroid Build Coastguard Workerfunction runTests(cfgFile) { 321*6dbdd20aSAndroid Build Coastguard Worker const args = [ 322*6dbdd20aSAndroid Build Coastguard Worker '--rootDir', 323*6dbdd20aSAndroid Build Coastguard Worker cfg.outTscDir, 324*6dbdd20aSAndroid Build Coastguard Worker '--verbose', 325*6dbdd20aSAndroid Build Coastguard Worker '--runInBand', 326*6dbdd20aSAndroid Build Coastguard Worker '--detectOpenHandles', 327*6dbdd20aSAndroid Build Coastguard Worker '--forceExit', 328*6dbdd20aSAndroid Build Coastguard Worker '--projects', 329*6dbdd20aSAndroid Build Coastguard Worker pjoin(ROOT_DIR, 'ui/config', cfgFile), 330*6dbdd20aSAndroid Build Coastguard Worker ]; 331*6dbdd20aSAndroid Build Coastguard Worker if (cfg.testFilter.length > 0) { 332*6dbdd20aSAndroid Build Coastguard Worker args.push('-t', cfg.testFilter); 333*6dbdd20aSAndroid Build Coastguard Worker } 334*6dbdd20aSAndroid Build Coastguard Worker if (cfg.watch) { 335*6dbdd20aSAndroid Build Coastguard Worker args.push('--watchAll'); 336*6dbdd20aSAndroid Build Coastguard Worker addTask(execModule, ['jest', args, {async: true}]); 337*6dbdd20aSAndroid Build Coastguard Worker } else { 338*6dbdd20aSAndroid Build Coastguard Worker addTask(execModule, ['jest', args]); 339*6dbdd20aSAndroid Build Coastguard Worker } 340*6dbdd20aSAndroid Build Coastguard Worker} 341*6dbdd20aSAndroid Build Coastguard Worker 342*6dbdd20aSAndroid Build Coastguard Workerfunction cpHtml(src, filename) { 343*6dbdd20aSAndroid Build Coastguard Worker let html = fs.readFileSync(src).toString(); 344*6dbdd20aSAndroid Build Coastguard Worker // First copy the html as-is into the dist/v1.2.3/ directory. This is 345*6dbdd20aSAndroid Build Coastguard Worker // only used for archival purporses, so one can open 346*6dbdd20aSAndroid Build Coastguard Worker // ui.perfetto.dev/v1.2.3/ to skip the auto-update and channel logic. 347*6dbdd20aSAndroid Build Coastguard Worker fs.writeFileSync(pjoin(cfg.outDistDir, filename), html); 348*6dbdd20aSAndroid Build Coastguard Worker 349*6dbdd20aSAndroid Build Coastguard Worker // Then copy it into the dist/ root by patching the version code. 350*6dbdd20aSAndroid Build Coastguard Worker // TODO(primiano): in next CLs, this script should take a 351*6dbdd20aSAndroid Build Coastguard Worker // --release_map=xxx.json argument, to populate this with multiple channels. 352*6dbdd20aSAndroid Build Coastguard Worker const versionMap = JSON.stringify({'stable': cfg.version}); 353*6dbdd20aSAndroid Build Coastguard Worker const bodyRegex = /data-perfetto_version='[^']*'/; 354*6dbdd20aSAndroid Build Coastguard Worker html = html.replace(bodyRegex, `data-perfetto_version='${versionMap}'`); 355*6dbdd20aSAndroid Build Coastguard Worker fs.writeFileSync(pjoin(cfg.outDistRootDir, filename), html); 356*6dbdd20aSAndroid Build Coastguard Worker} 357*6dbdd20aSAndroid Build Coastguard Worker 358*6dbdd20aSAndroid Build Coastguard Workerfunction copyIndexHtml(src) { 359*6dbdd20aSAndroid Build Coastguard Worker addTask(cpHtml, [src, 'index.html']); 360*6dbdd20aSAndroid Build Coastguard Worker} 361*6dbdd20aSAndroid Build Coastguard Worker 362*6dbdd20aSAndroid Build Coastguard Workerfunction copyBigtraceHtml(src) { 363*6dbdd20aSAndroid Build Coastguard Worker if (cfg.bigtrace) { 364*6dbdd20aSAndroid Build Coastguard Worker addTask(cpHtml, [src, 'bigtrace.html']); 365*6dbdd20aSAndroid Build Coastguard Worker } 366*6dbdd20aSAndroid Build Coastguard Worker} 367*6dbdd20aSAndroid Build Coastguard Worker 368*6dbdd20aSAndroid Build Coastguard Workerfunction copyOpenPerfettoTraceHtml(src) { 369*6dbdd20aSAndroid Build Coastguard Worker if (cfg.openPerfettoTrace) { 370*6dbdd20aSAndroid Build Coastguard Worker addTask(cp, [src, pjoin(cfg.outOpenPerfettoTraceDistDir, 'index.html')]); 371*6dbdd20aSAndroid Build Coastguard Worker } 372*6dbdd20aSAndroid Build Coastguard Worker} 373*6dbdd20aSAndroid Build Coastguard Worker 374*6dbdd20aSAndroid Build Coastguard Workerfunction copyAssets(src, dst) { 375*6dbdd20aSAndroid Build Coastguard Worker addTask(cp, [src, pjoin(cfg.outDistDir, 'assets', dst)]); 376*6dbdd20aSAndroid Build Coastguard Worker if (cfg.bigtrace) { 377*6dbdd20aSAndroid Build Coastguard Worker addTask(cp, [src, pjoin(cfg.outBigtraceDistDir, 'assets', dst)]); 378*6dbdd20aSAndroid Build Coastguard Worker } 379*6dbdd20aSAndroid Build Coastguard Worker} 380*6dbdd20aSAndroid Build Coastguard Worker 381*6dbdd20aSAndroid Build Coastguard Workerfunction copyUiTestArtifactsAssets(src, dst) { 382*6dbdd20aSAndroid Build Coastguard Worker addTask(cp, [src, pjoin(cfg.outUiTestArtifactsDir, dst)]); 383*6dbdd20aSAndroid Build Coastguard Worker} 384*6dbdd20aSAndroid Build Coastguard Worker 385*6dbdd20aSAndroid Build Coastguard Workerfunction compileScss() { 386*6dbdd20aSAndroid Build Coastguard Worker const src = pjoin(ROOT_DIR, 'ui/src/assets/perfetto.scss'); 387*6dbdd20aSAndroid Build Coastguard Worker const dst = pjoin(cfg.outDistDir, 'perfetto.css'); 388*6dbdd20aSAndroid Build Coastguard Worker // In watch mode, don't exit(1) if scss fails. It can easily happen by 389*6dbdd20aSAndroid Build Coastguard Worker // having a typo in the css. It will still print an error. 390*6dbdd20aSAndroid Build Coastguard Worker const noErrCheck = !!cfg.watch; 391*6dbdd20aSAndroid Build Coastguard Worker const args = [src, dst]; 392*6dbdd20aSAndroid Build Coastguard Worker if (!cfg.verbose) { 393*6dbdd20aSAndroid Build Coastguard Worker args.unshift('--quiet'); 394*6dbdd20aSAndroid Build Coastguard Worker } 395*6dbdd20aSAndroid Build Coastguard Worker addTask(execModule, ['sass', args, {noErrCheck}]); 396*6dbdd20aSAndroid Build Coastguard Worker if (cfg.bigtrace) { 397*6dbdd20aSAndroid Build Coastguard Worker addTask(cp, [dst, pjoin(cfg.outBigtraceDistDir, 'perfetto.css')]); 398*6dbdd20aSAndroid Build Coastguard Worker } 399*6dbdd20aSAndroid Build Coastguard Worker} 400*6dbdd20aSAndroid Build Coastguard Worker 401*6dbdd20aSAndroid Build Coastguard Workerfunction compileProtos() { 402*6dbdd20aSAndroid Build Coastguard Worker const dstJs = pjoin(cfg.outGenDir, 'protos.js'); 403*6dbdd20aSAndroid Build Coastguard Worker const dstTs = pjoin(cfg.outGenDir, 'protos.d.ts'); 404*6dbdd20aSAndroid Build Coastguard Worker const inputs = [ 405*6dbdd20aSAndroid Build Coastguard Worker 'protos/perfetto/ipc/consumer_port.proto', 406*6dbdd20aSAndroid Build Coastguard Worker 'protos/perfetto/ipc/wire_protocol.proto', 407*6dbdd20aSAndroid Build Coastguard Worker 'protos/perfetto/trace/perfetto/perfetto_metatrace.proto', 408*6dbdd20aSAndroid Build Coastguard Worker 'protos/perfetto/trace_processor/trace_processor.proto', 409*6dbdd20aSAndroid Build Coastguard Worker ]; 410*6dbdd20aSAndroid Build Coastguard Worker // Can't put --no-comments here - The comments are load bearing for 411*6dbdd20aSAndroid Build Coastguard Worker // the pbts invocation which follows. 412*6dbdd20aSAndroid Build Coastguard Worker const pbjsArgs = [ 413*6dbdd20aSAndroid Build Coastguard Worker '--no-beautify', 414*6dbdd20aSAndroid Build Coastguard Worker '--force-number', 415*6dbdd20aSAndroid Build Coastguard Worker '--no-delimited', 416*6dbdd20aSAndroid Build Coastguard Worker '--no-verify', 417*6dbdd20aSAndroid Build Coastguard Worker '-t', 418*6dbdd20aSAndroid Build Coastguard Worker 'static-module', 419*6dbdd20aSAndroid Build Coastguard Worker '-w', 420*6dbdd20aSAndroid Build Coastguard Worker 'commonjs', 421*6dbdd20aSAndroid Build Coastguard Worker '-p', 422*6dbdd20aSAndroid Build Coastguard Worker ROOT_DIR, 423*6dbdd20aSAndroid Build Coastguard Worker '-o', 424*6dbdd20aSAndroid Build Coastguard Worker dstJs, 425*6dbdd20aSAndroid Build Coastguard Worker ].concat(inputs); 426*6dbdd20aSAndroid Build Coastguard Worker addTask(execModule, ['pbjs', pbjsArgs]); 427*6dbdd20aSAndroid Build Coastguard Worker 428*6dbdd20aSAndroid Build Coastguard Worker // Note: If you are looking into slowness of pbts it is not pbts 429*6dbdd20aSAndroid Build Coastguard Worker // itself that is slow. It invokes jsdoc to parse the comments out of 430*6dbdd20aSAndroid Build Coastguard Worker // the |dstJs| with https://github.com/hegemonic/catharsis which is 431*6dbdd20aSAndroid Build Coastguard Worker // pinning a CPU core the whole time. 432*6dbdd20aSAndroid Build Coastguard Worker const pbtsArgs = ['--no-comments', '-p', ROOT_DIR, '-o', dstTs, dstJs]; 433*6dbdd20aSAndroid Build Coastguard Worker addTask(execModule, ['pbts', pbtsArgs]); 434*6dbdd20aSAndroid Build Coastguard Worker} 435*6dbdd20aSAndroid Build Coastguard Worker 436*6dbdd20aSAndroid Build Coastguard Workerfunction generateImports(dir, name) { 437*6dbdd20aSAndroid Build Coastguard Worker // We have to use the symlink (ui/src/gen) rather than cfg.outGenDir 438*6dbdd20aSAndroid Build Coastguard Worker // below since we want to generate the correct relative imports. For example: 439*6dbdd20aSAndroid Build Coastguard Worker // ui/src/frontend/foo.ts 440*6dbdd20aSAndroid Build Coastguard Worker // import '../gen/all_plugins.ts'; 441*6dbdd20aSAndroid Build Coastguard Worker // ui/src/gen/all_plugins.ts (aka ui/out/tsc/gen/all_plugins.ts) 442*6dbdd20aSAndroid Build Coastguard Worker // import '../frontend/some_plugin.ts'; 443*6dbdd20aSAndroid Build Coastguard Worker const dstTs = pjoin(ROOT_DIR, 'ui/src/gen', name); 444*6dbdd20aSAndroid Build Coastguard Worker const inputDir = pjoin(ROOT_DIR, dir); 445*6dbdd20aSAndroid Build Coastguard Worker const args = [GEN_IMPORTS_SCRIPT, inputDir, '--out', dstTs]; 446*6dbdd20aSAndroid Build Coastguard Worker addTask(exec, ['python3', args]); 447*6dbdd20aSAndroid Build Coastguard Worker} 448*6dbdd20aSAndroid Build Coastguard Worker 449*6dbdd20aSAndroid Build Coastguard Worker// Generates a .ts source that defines the VERSION and SCM_REVISION constants. 450*6dbdd20aSAndroid Build Coastguard Workerfunction genVersion() { 451*6dbdd20aSAndroid Build Coastguard Worker const cmd = 'python3'; 452*6dbdd20aSAndroid Build Coastguard Worker const args = 453*6dbdd20aSAndroid Build Coastguard Worker [VERSION_SCRIPT, '--ts_out', pjoin(cfg.outGenDir, 'perfetto_version.ts')]; 454*6dbdd20aSAndroid Build Coastguard Worker addTask(exec, [cmd, args]); 455*6dbdd20aSAndroid Build Coastguard Worker} 456*6dbdd20aSAndroid Build Coastguard Worker 457*6dbdd20aSAndroid Build Coastguard Workerfunction generateStdlibDocs() { 458*6dbdd20aSAndroid Build Coastguard Worker const cmd = pjoin(ROOT_DIR, 'tools/gen_stdlib_docs_json.py'); 459*6dbdd20aSAndroid Build Coastguard Worker const stdlibDir = pjoin(ROOT_DIR, 'src/trace_processor/perfetto_sql/stdlib'); 460*6dbdd20aSAndroid Build Coastguard Worker 461*6dbdd20aSAndroid Build Coastguard Worker const stdlibFiles = 462*6dbdd20aSAndroid Build Coastguard Worker listFilesRecursive(stdlibDir) 463*6dbdd20aSAndroid Build Coastguard Worker .filter((filePath) => path.extname(filePath) === '.sql'); 464*6dbdd20aSAndroid Build Coastguard Worker 465*6dbdd20aSAndroid Build Coastguard Worker addTask(exec, [ 466*6dbdd20aSAndroid Build Coastguard Worker cmd, 467*6dbdd20aSAndroid Build Coastguard Worker [ 468*6dbdd20aSAndroid Build Coastguard Worker '--json-out', 469*6dbdd20aSAndroid Build Coastguard Worker pjoin(cfg.outDistDir, 'stdlib_docs.json'), 470*6dbdd20aSAndroid Build Coastguard Worker '--minify', 471*6dbdd20aSAndroid Build Coastguard Worker ...stdlibFiles, 472*6dbdd20aSAndroid Build Coastguard Worker ], 473*6dbdd20aSAndroid Build Coastguard Worker ]); 474*6dbdd20aSAndroid Build Coastguard Worker} 475*6dbdd20aSAndroid Build Coastguard Worker 476*6dbdd20aSAndroid Build Coastguard Workerfunction updateSymlinks() { 477*6dbdd20aSAndroid Build Coastguard Worker // /ui/out -> /out/ui. 478*6dbdd20aSAndroid Build Coastguard Worker mklink(cfg.outUiDir, pjoin(ROOT_DIR, 'ui/out')); 479*6dbdd20aSAndroid Build Coastguard Worker 480*6dbdd20aSAndroid Build Coastguard Worker // /ui/src/gen -> /out/ui/ui/tsc/gen) 481*6dbdd20aSAndroid Build Coastguard Worker mklink(cfg.outGenDir, pjoin(ROOT_DIR, 'ui/src/gen')); 482*6dbdd20aSAndroid Build Coastguard Worker 483*6dbdd20aSAndroid Build Coastguard Worker // /out/ui/test/data -> /test/data (For UI tests). 484*6dbdd20aSAndroid Build Coastguard Worker mklink( 485*6dbdd20aSAndroid Build Coastguard Worker pjoin(ROOT_DIR, 'test/data'), 486*6dbdd20aSAndroid Build Coastguard Worker pjoin(ensureDir(pjoin(cfg.outDir, 'test')), 'data')); 487*6dbdd20aSAndroid Build Coastguard Worker 488*6dbdd20aSAndroid Build Coastguard Worker // Creates a out/dist_version -> out/dist/v1.2.3 symlink, so rollup config 489*6dbdd20aSAndroid Build Coastguard Worker // can point to that without having to know the current version number. 490*6dbdd20aSAndroid Build Coastguard Worker mklink( 491*6dbdd20aSAndroid Build Coastguard Worker path.relative(cfg.outUiDir, cfg.outDistDir), 492*6dbdd20aSAndroid Build Coastguard Worker pjoin(cfg.outUiDir, 'dist_version')); 493*6dbdd20aSAndroid Build Coastguard Worker 494*6dbdd20aSAndroid Build Coastguard Worker mklink( 495*6dbdd20aSAndroid Build Coastguard Worker pjoin(ROOT_DIR, 'ui/node_modules'), pjoin(cfg.outTscDir, 'node_modules')); 496*6dbdd20aSAndroid Build Coastguard Worker} 497*6dbdd20aSAndroid Build Coastguard Worker 498*6dbdd20aSAndroid Build Coastguard Worker// Invokes ninja for building the {trace_processor, traceconv} Wasm modules. 499*6dbdd20aSAndroid Build Coastguard Worker// It copies the .wasm directly into the out/dist/ dir, and the .js/.ts into 500*6dbdd20aSAndroid Build Coastguard Worker// out/tsc/, so the typescript compiler and the bundler can pick them up. 501*6dbdd20aSAndroid Build Coastguard Workerfunction buildWasm(skipWasmBuild) { 502*6dbdd20aSAndroid Build Coastguard Worker if (!skipWasmBuild) { 503*6dbdd20aSAndroid Build Coastguard Worker if (!cfg.noOverrideGnArgs) { 504*6dbdd20aSAndroid Build Coastguard Worker let gnVars = `is_debug=${cfg.debug}`; 505*6dbdd20aSAndroid Build Coastguard Worker if (childProcess.spawnSync('which', ['ccache']).status === 0) { 506*6dbdd20aSAndroid Build Coastguard Worker gnVars += ` cc_wrapper="ccache"`; 507*6dbdd20aSAndroid Build Coastguard Worker } 508*6dbdd20aSAndroid Build Coastguard Worker const gnArgs = ['gen', `--args=${gnVars}`, cfg.outDir]; 509*6dbdd20aSAndroid Build Coastguard Worker addTask(exec, [pjoin(ROOT_DIR, 'tools/gn'), gnArgs]); 510*6dbdd20aSAndroid Build Coastguard Worker } 511*6dbdd20aSAndroid Build Coastguard Worker 512*6dbdd20aSAndroid Build Coastguard Worker const ninjaArgs = ['-C', cfg.outDir]; 513*6dbdd20aSAndroid Build Coastguard Worker ninjaArgs.push(...cfg.wasmModules.map((x) => `${x}_wasm`)); 514*6dbdd20aSAndroid Build Coastguard Worker addTask(exec, [pjoin(ROOT_DIR, 'tools/ninja'), ninjaArgs]); 515*6dbdd20aSAndroid Build Coastguard Worker } 516*6dbdd20aSAndroid Build Coastguard Worker 517*6dbdd20aSAndroid Build Coastguard Worker const wasmOutDir = pjoin(cfg.outDir, 'wasm'); 518*6dbdd20aSAndroid Build Coastguard Worker for (const wasmMod of cfg.wasmModules) { 519*6dbdd20aSAndroid Build Coastguard Worker // The .wasm file goes directly into the dist dir (also .map in debug) 520*6dbdd20aSAndroid Build Coastguard Worker for (const ext of ['.wasm'].concat(cfg.debug ? ['.wasm.map'] : [])) { 521*6dbdd20aSAndroid Build Coastguard Worker const src = `${wasmOutDir}/${wasmMod}${ext}`; 522*6dbdd20aSAndroid Build Coastguard Worker addTask(cp, [src, pjoin(cfg.outDistDir, wasmMod + ext)]); 523*6dbdd20aSAndroid Build Coastguard Worker } 524*6dbdd20aSAndroid Build Coastguard Worker // The .js / .ts go into intermediates, they will be bundled by rollup. 525*6dbdd20aSAndroid Build Coastguard Worker for (const ext of ['.js', '.d.ts']) { 526*6dbdd20aSAndroid Build Coastguard Worker const fname = `${wasmMod}${ext}`; 527*6dbdd20aSAndroid Build Coastguard Worker addTask(cp, [pjoin(wasmOutDir, fname), pjoin(cfg.outGenDir, fname)]); 528*6dbdd20aSAndroid Build Coastguard Worker } 529*6dbdd20aSAndroid Build Coastguard Worker } 530*6dbdd20aSAndroid Build Coastguard Worker} 531*6dbdd20aSAndroid Build Coastguard Worker 532*6dbdd20aSAndroid Build Coastguard Worker// This transpiles all the sources (frontend, controller, engine, extension) in 533*6dbdd20aSAndroid Build Coastguard Worker// one go. The only project that has a dedicated invocation is service_worker. 534*6dbdd20aSAndroid Build Coastguard Workerfunction transpileTsProject(project, options) { 535*6dbdd20aSAndroid Build Coastguard Worker const args = ['--project', pjoin(ROOT_DIR, project)]; 536*6dbdd20aSAndroid Build Coastguard Worker 537*6dbdd20aSAndroid Build Coastguard Worker if (options !== undefined && options.watch) { 538*6dbdd20aSAndroid Build Coastguard Worker args.push('--watch', '--preserveWatchOutput'); 539*6dbdd20aSAndroid Build Coastguard Worker addTask(execModule, ['tsc', args, {async: true}]); 540*6dbdd20aSAndroid Build Coastguard Worker } else { 541*6dbdd20aSAndroid Build Coastguard Worker addTask(execModule, ['tsc', args]); 542*6dbdd20aSAndroid Build Coastguard Worker } 543*6dbdd20aSAndroid Build Coastguard Worker} 544*6dbdd20aSAndroid Build Coastguard Worker 545*6dbdd20aSAndroid Build Coastguard Worker// Creates the three {frontend, controller, engine}_bundle.js in one invocation. 546*6dbdd20aSAndroid Build Coastguard Workerfunction bundleJs(cfgName) { 547*6dbdd20aSAndroid Build Coastguard Worker const rcfg = pjoin(ROOT_DIR, 'ui/config', cfgName); 548*6dbdd20aSAndroid Build Coastguard Worker const args = ['-c', rcfg, '--no-indent']; 549*6dbdd20aSAndroid Build Coastguard Worker if (cfg.bigtrace) { 550*6dbdd20aSAndroid Build Coastguard Worker args.push('--environment', 'ENABLE_BIGTRACE:true'); 551*6dbdd20aSAndroid Build Coastguard Worker } 552*6dbdd20aSAndroid Build Coastguard Worker if (cfg.openPerfettoTrace) { 553*6dbdd20aSAndroid Build Coastguard Worker args.push('--environment', 'ENABLE_OPEN_PERFETTO_TRACE:true'); 554*6dbdd20aSAndroid Build Coastguard Worker } 555*6dbdd20aSAndroid Build Coastguard Worker if (cfg.minifyJs) { 556*6dbdd20aSAndroid Build Coastguard Worker args.push('--environment', `MINIFY_JS:${cfg.minifyJs}`); 557*6dbdd20aSAndroid Build Coastguard Worker } 558*6dbdd20aSAndroid Build Coastguard Worker args.push(...(cfg.verbose ? [] : ['--silent'])); 559*6dbdd20aSAndroid Build Coastguard Worker if (cfg.watch) { 560*6dbdd20aSAndroid Build Coastguard Worker // --waitForBundleInput is sadly quite busted so it is required ts 561*6dbdd20aSAndroid Build Coastguard Worker // has build at least once before invoking this. 562*6dbdd20aSAndroid Build Coastguard Worker args.push('--watch', '--no-watch.clearScreen'); 563*6dbdd20aSAndroid Build Coastguard Worker addTask(execModule, ['rollup', args, {async: true}]); 564*6dbdd20aSAndroid Build Coastguard Worker } else { 565*6dbdd20aSAndroid Build Coastguard Worker addTask(execModule, ['rollup', args]); 566*6dbdd20aSAndroid Build Coastguard Worker } 567*6dbdd20aSAndroid Build Coastguard Worker} 568*6dbdd20aSAndroid Build Coastguard Worker 569*6dbdd20aSAndroid Build Coastguard Workerfunction genServiceWorkerManifestJson() { 570*6dbdd20aSAndroid Build Coastguard Worker function makeManifest() { 571*6dbdd20aSAndroid Build Coastguard Worker const manifest = {resources: {}}; 572*6dbdd20aSAndroid Build Coastguard Worker // When building the subresource manifest skip source maps, the manifest 573*6dbdd20aSAndroid Build Coastguard Worker // itself and the copy of the index.html which is copied under /v1.2.3/. 574*6dbdd20aSAndroid Build Coastguard Worker // The root /index.html will be fetched by service_worker.js separately. 575*6dbdd20aSAndroid Build Coastguard Worker const skipRegex = /(\.map|manifest\.json|index.html)$/; 576*6dbdd20aSAndroid Build Coastguard Worker walk(cfg.outDistDir, (absPath) => { 577*6dbdd20aSAndroid Build Coastguard Worker const contents = fs.readFileSync(absPath); 578*6dbdd20aSAndroid Build Coastguard Worker const relPath = path.relative(cfg.outDistDir, absPath); 579*6dbdd20aSAndroid Build Coastguard Worker const b64 = crypto.createHash('sha256').update(contents).digest('base64'); 580*6dbdd20aSAndroid Build Coastguard Worker manifest.resources[relPath] = 'sha256-' + b64; 581*6dbdd20aSAndroid Build Coastguard Worker }, skipRegex); 582*6dbdd20aSAndroid Build Coastguard Worker const manifestJson = JSON.stringify(manifest, null, 2); 583*6dbdd20aSAndroid Build Coastguard Worker fs.writeFileSync(pjoin(cfg.outDistDir, 'manifest.json'), manifestJson); 584*6dbdd20aSAndroid Build Coastguard Worker } 585*6dbdd20aSAndroid Build Coastguard Worker addTask(makeManifest, []); 586*6dbdd20aSAndroid Build Coastguard Worker} 587*6dbdd20aSAndroid Build Coastguard Worker 588*6dbdd20aSAndroid Build Coastguard Workerfunction startServer() { 589*6dbdd20aSAndroid Build Coastguard Worker const host = cfg.httpServerListenHost == '127.0.0.1' ? 'localhost' : cfg.httpServerListenHost; 590*6dbdd20aSAndroid Build Coastguard Worker console.log( 591*6dbdd20aSAndroid Build Coastguard Worker 'Starting HTTP server on', 592*6dbdd20aSAndroid Build Coastguard Worker `http://${host}:${cfg.httpServerListenPort}`); 593*6dbdd20aSAndroid Build Coastguard Worker http.createServer(function(req, res) { 594*6dbdd20aSAndroid Build Coastguard Worker console.debug(req.method, req.url); 595*6dbdd20aSAndroid Build Coastguard Worker let uri = req.url.split('?', 1)[0]; 596*6dbdd20aSAndroid Build Coastguard Worker if (uri.endsWith('/')) { 597*6dbdd20aSAndroid Build Coastguard Worker uri += 'index.html'; 598*6dbdd20aSAndroid Build Coastguard Worker } 599*6dbdd20aSAndroid Build Coastguard Worker 600*6dbdd20aSAndroid Build Coastguard Worker if (uri === '/live_reload') { 601*6dbdd20aSAndroid Build Coastguard Worker // Implements the Server-Side-Events protocol. 602*6dbdd20aSAndroid Build Coastguard Worker const head = { 603*6dbdd20aSAndroid Build Coastguard Worker 'Content-Type': 'text/event-stream', 604*6dbdd20aSAndroid Build Coastguard Worker 'Connection': 'keep-alive', 605*6dbdd20aSAndroid Build Coastguard Worker 'Cache-Control': 'no-cache', 606*6dbdd20aSAndroid Build Coastguard Worker }; 607*6dbdd20aSAndroid Build Coastguard Worker res.writeHead(200, head); 608*6dbdd20aSAndroid Build Coastguard Worker const arrayIdx = httpWatches.length; 609*6dbdd20aSAndroid Build Coastguard Worker // We never remove from the array, the delete leaves an undefined item 610*6dbdd20aSAndroid Build Coastguard Worker // around. It makes keeping track of the index easier at the cost of a 611*6dbdd20aSAndroid Build Coastguard Worker // small leak. 612*6dbdd20aSAndroid Build Coastguard Worker httpWatches.push(res); 613*6dbdd20aSAndroid Build Coastguard Worker req.on('close', () => delete httpWatches[arrayIdx]); 614*6dbdd20aSAndroid Build Coastguard Worker return; 615*6dbdd20aSAndroid Build Coastguard Worker } 616*6dbdd20aSAndroid Build Coastguard Worker 617*6dbdd20aSAndroid Build Coastguard Worker let absPath = path.normalize(path.join(cfg.outDistRootDir, uri)); 618*6dbdd20aSAndroid Build Coastguard Worker // We want to be able to use the data in '/test/' for e2e tests. 619*6dbdd20aSAndroid Build Coastguard Worker // However, we don't want do create a symlink into the 'dist/' dir, 620*6dbdd20aSAndroid Build Coastguard Worker // because 'dist/' gets shipped on the production server. 621*6dbdd20aSAndroid Build Coastguard Worker if (uri.startsWith('/test/')) { 622*6dbdd20aSAndroid Build Coastguard Worker absPath = pjoin(ROOT_DIR, uri); 623*6dbdd20aSAndroid Build Coastguard Worker } 624*6dbdd20aSAndroid Build Coastguard Worker 625*6dbdd20aSAndroid Build Coastguard Worker // Don't serve contents outside of the project root (b/221101533). 626*6dbdd20aSAndroid Build Coastguard Worker if (path.relative(ROOT_DIR, absPath).startsWith('..')) { 627*6dbdd20aSAndroid Build Coastguard Worker res.writeHead(403); 628*6dbdd20aSAndroid Build Coastguard Worker res.end('403 Forbidden - Request path outside of the repo root'); 629*6dbdd20aSAndroid Build Coastguard Worker return; 630*6dbdd20aSAndroid Build Coastguard Worker } 631*6dbdd20aSAndroid Build Coastguard Worker 632*6dbdd20aSAndroid Build Coastguard Worker fs.readFile(absPath, function(err, data) { 633*6dbdd20aSAndroid Build Coastguard Worker if (err) { 634*6dbdd20aSAndroid Build Coastguard Worker res.writeHead(404); 635*6dbdd20aSAndroid Build Coastguard Worker res.end(JSON.stringify(err)); 636*6dbdd20aSAndroid Build Coastguard Worker return; 637*6dbdd20aSAndroid Build Coastguard Worker } 638*6dbdd20aSAndroid Build Coastguard Worker 639*6dbdd20aSAndroid Build Coastguard Worker const mimeMap = { 640*6dbdd20aSAndroid Build Coastguard Worker 'html': 'text/html', 641*6dbdd20aSAndroid Build Coastguard Worker 'css': 'text/css', 642*6dbdd20aSAndroid Build Coastguard Worker 'js': 'application/javascript', 643*6dbdd20aSAndroid Build Coastguard Worker 'wasm': 'application/wasm', 644*6dbdd20aSAndroid Build Coastguard Worker }; 645*6dbdd20aSAndroid Build Coastguard Worker const ext = uri.split('.').pop(); 646*6dbdd20aSAndroid Build Coastguard Worker const cType = mimeMap[ext] || 'octect/stream'; 647*6dbdd20aSAndroid Build Coastguard Worker const head = { 648*6dbdd20aSAndroid Build Coastguard Worker 'Content-Type': cType, 649*6dbdd20aSAndroid Build Coastguard Worker 'Content-Length': data.length, 650*6dbdd20aSAndroid Build Coastguard Worker 'Last-Modified': fs.statSync(absPath).mtime.toUTCString(), 651*6dbdd20aSAndroid Build Coastguard Worker 'Cache-Control': 'no-cache', 652*6dbdd20aSAndroid Build Coastguard Worker }; 653*6dbdd20aSAndroid Build Coastguard Worker if (cfg.crossOriginIsolation) { 654*6dbdd20aSAndroid Build Coastguard Worker head['Cross-Origin-Opener-Policy'] = 'same-origin'; 655*6dbdd20aSAndroid Build Coastguard Worker head['Cross-Origin-Embedder-Policy'] = 'require-corp'; 656*6dbdd20aSAndroid Build Coastguard Worker } 657*6dbdd20aSAndroid Build Coastguard Worker res.writeHead(200, head); 658*6dbdd20aSAndroid Build Coastguard Worker res.write(data); 659*6dbdd20aSAndroid Build Coastguard Worker res.end(); 660*6dbdd20aSAndroid Build Coastguard Worker }); 661*6dbdd20aSAndroid Build Coastguard Worker }) 662*6dbdd20aSAndroid Build Coastguard Worker .listen(cfg.httpServerListenPort, cfg.httpServerListenHost); 663*6dbdd20aSAndroid Build Coastguard Worker} 664*6dbdd20aSAndroid Build Coastguard Worker 665*6dbdd20aSAndroid Build Coastguard Workerfunction isDistComplete() { 666*6dbdd20aSAndroid Build Coastguard Worker const requiredArtifacts = [ 667*6dbdd20aSAndroid Build Coastguard Worker 'frontend_bundle.js', 668*6dbdd20aSAndroid Build Coastguard Worker 'engine_bundle.js', 669*6dbdd20aSAndroid Build Coastguard Worker 'traceconv_bundle.js', 670*6dbdd20aSAndroid Build Coastguard Worker 'trace_processor.wasm', 671*6dbdd20aSAndroid Build Coastguard Worker 'perfetto.css', 672*6dbdd20aSAndroid Build Coastguard Worker ]; 673*6dbdd20aSAndroid Build Coastguard Worker const relPaths = new Set(); 674*6dbdd20aSAndroid Build Coastguard Worker walk(cfg.outDistDir, (absPath) => { 675*6dbdd20aSAndroid Build Coastguard Worker relPaths.add(path.relative(cfg.outDistDir, absPath)); 676*6dbdd20aSAndroid Build Coastguard Worker }); 677*6dbdd20aSAndroid Build Coastguard Worker for (const fName of requiredArtifacts) { 678*6dbdd20aSAndroid Build Coastguard Worker if (!relPaths.has(fName)) return false; 679*6dbdd20aSAndroid Build Coastguard Worker } 680*6dbdd20aSAndroid Build Coastguard Worker return true; 681*6dbdd20aSAndroid Build Coastguard Worker} 682*6dbdd20aSAndroid Build Coastguard Worker 683*6dbdd20aSAndroid Build Coastguard Worker// Called whenever a change in the out/dist directory is detected. It sends a 684*6dbdd20aSAndroid Build Coastguard Worker// Server-Side-Event to the live_reload.ts script. 685*6dbdd20aSAndroid Build Coastguard Workerfunction notifyLiveServer(changedFile) { 686*6dbdd20aSAndroid Build Coastguard Worker for (const cli of httpWatches) { 687*6dbdd20aSAndroid Build Coastguard Worker if (cli === undefined) continue; 688*6dbdd20aSAndroid Build Coastguard Worker cli.write( 689*6dbdd20aSAndroid Build Coastguard Worker 'data: ' + path.relative(cfg.outDistRootDir, changedFile) + '\n\n'); 690*6dbdd20aSAndroid Build Coastguard Worker } 691*6dbdd20aSAndroid Build Coastguard Worker} 692*6dbdd20aSAndroid Build Coastguard Worker 693*6dbdd20aSAndroid Build Coastguard Workerfunction copyExtensionAssets() { 694*6dbdd20aSAndroid Build Coastguard Worker addTask(cp, [ 695*6dbdd20aSAndroid Build Coastguard Worker pjoin(ROOT_DIR, 'ui/src/assets/logo-128.png'), 696*6dbdd20aSAndroid Build Coastguard Worker pjoin(cfg.outExtDir, 'logo-128.png'), 697*6dbdd20aSAndroid Build Coastguard Worker ]); 698*6dbdd20aSAndroid Build Coastguard Worker addTask(cp, [ 699*6dbdd20aSAndroid Build Coastguard Worker pjoin(ROOT_DIR, 'ui/src/chrome_extension/manifest.json'), 700*6dbdd20aSAndroid Build Coastguard Worker pjoin(cfg.outExtDir, 'manifest.json'), 701*6dbdd20aSAndroid Build Coastguard Worker ]); 702*6dbdd20aSAndroid Build Coastguard Worker} 703*6dbdd20aSAndroid Build Coastguard Worker 704*6dbdd20aSAndroid Build Coastguard Worker// ----------------------- 705*6dbdd20aSAndroid Build Coastguard Worker// Task chaining functions 706*6dbdd20aSAndroid Build Coastguard Worker// ----------------------- 707*6dbdd20aSAndroid Build Coastguard Worker 708*6dbdd20aSAndroid Build Coastguard Workerfunction addTask(func, args) { 709*6dbdd20aSAndroid Build Coastguard Worker const task = new Task(func, args); 710*6dbdd20aSAndroid Build Coastguard Worker for (const t of tasks) { 711*6dbdd20aSAndroid Build Coastguard Worker if (t.identity === task.identity) { 712*6dbdd20aSAndroid Build Coastguard Worker return; 713*6dbdd20aSAndroid Build Coastguard Worker } 714*6dbdd20aSAndroid Build Coastguard Worker } 715*6dbdd20aSAndroid Build Coastguard Worker tasks.push(task); 716*6dbdd20aSAndroid Build Coastguard Worker setTimeout(runTasks, 0); 717*6dbdd20aSAndroid Build Coastguard Worker} 718*6dbdd20aSAndroid Build Coastguard Worker 719*6dbdd20aSAndroid Build Coastguard Workerfunction runTasks() { 720*6dbdd20aSAndroid Build Coastguard Worker const snapTasks = tasks.splice(0); // snap = std::move(tasks). 721*6dbdd20aSAndroid Build Coastguard Worker tasksTot += snapTasks.length; 722*6dbdd20aSAndroid Build Coastguard Worker for (const task of snapTasks) { 723*6dbdd20aSAndroid Build Coastguard Worker const DIM = '\u001b[2m'; 724*6dbdd20aSAndroid Build Coastguard Worker const BRT = '\u001b[37m'; 725*6dbdd20aSAndroid Build Coastguard Worker const RST = '\u001b[0m'; 726*6dbdd20aSAndroid Build Coastguard Worker const ms = (new Date(Date.now() - tStart)).toISOString().slice(17, -1); 727*6dbdd20aSAndroid Build Coastguard Worker const ts = `[${DIM}${ms}${RST}]`; 728*6dbdd20aSAndroid Build Coastguard Worker const descr = task.description.substr(0, 80); 729*6dbdd20aSAndroid Build Coastguard Worker console.log(`${ts} ${BRT}${++tasksRan}/${tasksTot}${RST}\t${descr}`); 730*6dbdd20aSAndroid Build Coastguard Worker task.func.apply(/* this=*/ undefined, task.args); 731*6dbdd20aSAndroid Build Coastguard Worker } 732*6dbdd20aSAndroid Build Coastguard Worker} 733*6dbdd20aSAndroid Build Coastguard Worker 734*6dbdd20aSAndroid Build Coastguard Worker// Executes all the RULES that match the given |absPath|. 735*6dbdd20aSAndroid Build Coastguard Workerfunction scanFile(absPath) { 736*6dbdd20aSAndroid Build Coastguard Worker console.assert(fs.existsSync(absPath)); 737*6dbdd20aSAndroid Build Coastguard Worker console.assert(path.isAbsolute(absPath)); 738*6dbdd20aSAndroid Build Coastguard Worker const normPath = path.relative(ROOT_DIR, absPath); 739*6dbdd20aSAndroid Build Coastguard Worker for (const rule of RULES) { 740*6dbdd20aSAndroid Build Coastguard Worker const match = rule.r.exec(normPath); 741*6dbdd20aSAndroid Build Coastguard Worker if (!match || match[0] !== normPath) continue; 742*6dbdd20aSAndroid Build Coastguard Worker const captureGroup = match.length > 1 ? match[1] : undefined; 743*6dbdd20aSAndroid Build Coastguard Worker rule.f(absPath, captureGroup); 744*6dbdd20aSAndroid Build Coastguard Worker } 745*6dbdd20aSAndroid Build Coastguard Worker} 746*6dbdd20aSAndroid Build Coastguard Worker 747*6dbdd20aSAndroid Build Coastguard Worker// Walks the passed |dir| recursively and, for each file, invokes the matching 748*6dbdd20aSAndroid Build Coastguard Worker// RULES. If --watch is used, it also installs a fswatch() and re-triggers the 749*6dbdd20aSAndroid Build Coastguard Worker// matching RULES on each file change. 750*6dbdd20aSAndroid Build Coastguard Workerfunction scanDir(dir, regex) { 751*6dbdd20aSAndroid Build Coastguard Worker const filterFn = regex ? (absPath) => regex.test(absPath) : () => true; 752*6dbdd20aSAndroid Build Coastguard Worker const absDir = path.isAbsolute(dir) ? dir : pjoin(ROOT_DIR, dir); 753*6dbdd20aSAndroid Build Coastguard Worker // Add a fs watch if in watch mode. 754*6dbdd20aSAndroid Build Coastguard Worker if (cfg.watch) { 755*6dbdd20aSAndroid Build Coastguard Worker fs.watch(absDir, {recursive: true}, (_eventType, relFilePath) => { 756*6dbdd20aSAndroid Build Coastguard Worker const filePath = pjoin(absDir, relFilePath); 757*6dbdd20aSAndroid Build Coastguard Worker if (!filterFn(filePath)) return; 758*6dbdd20aSAndroid Build Coastguard Worker if (cfg.verbose) { 759*6dbdd20aSAndroid Build Coastguard Worker console.log('File change detected', _eventType, filePath); 760*6dbdd20aSAndroid Build Coastguard Worker } 761*6dbdd20aSAndroid Build Coastguard Worker if (fs.existsSync(filePath)) { 762*6dbdd20aSAndroid Build Coastguard Worker scanFile(filePath, filterFn); 763*6dbdd20aSAndroid Build Coastguard Worker } 764*6dbdd20aSAndroid Build Coastguard Worker }); 765*6dbdd20aSAndroid Build Coastguard Worker } 766*6dbdd20aSAndroid Build Coastguard Worker walk(absDir, (f) => { 767*6dbdd20aSAndroid Build Coastguard Worker if (filterFn(f)) scanFile(f); 768*6dbdd20aSAndroid Build Coastguard Worker }); 769*6dbdd20aSAndroid Build Coastguard Worker} 770*6dbdd20aSAndroid Build Coastguard Worker 771*6dbdd20aSAndroid Build Coastguard Workerfunction exec(cmd, args, opts) { 772*6dbdd20aSAndroid Build Coastguard Worker opts = opts || {}; 773*6dbdd20aSAndroid Build Coastguard Worker opts.stdout = opts.stdout || 'inherit'; 774*6dbdd20aSAndroid Build Coastguard Worker if (cfg.verbose) console.log(`${cmd} ${args.join(' ')}\n`); 775*6dbdd20aSAndroid Build Coastguard Worker const spwOpts = {cwd: cfg.outDir, stdio: ['ignore', opts.stdout, 'inherit']}; 776*6dbdd20aSAndroid Build Coastguard Worker const checkExitCode = (code, signal) => { 777*6dbdd20aSAndroid Build Coastguard Worker if (signal === 'SIGINT' || signal === 'SIGTERM') return; 778*6dbdd20aSAndroid Build Coastguard Worker if (code !== 0 && !opts.noErrCheck) { 779*6dbdd20aSAndroid Build Coastguard Worker console.error(`${cmd} ${args.join(' ')} failed with code ${code}`); 780*6dbdd20aSAndroid Build Coastguard Worker process.exit(1); 781*6dbdd20aSAndroid Build Coastguard Worker } 782*6dbdd20aSAndroid Build Coastguard Worker }; 783*6dbdd20aSAndroid Build Coastguard Worker if (opts.async) { 784*6dbdd20aSAndroid Build Coastguard Worker const proc = childProcess.spawn(cmd, args, spwOpts); 785*6dbdd20aSAndroid Build Coastguard Worker const procIndex = subprocesses.length; 786*6dbdd20aSAndroid Build Coastguard Worker subprocesses.push(proc); 787*6dbdd20aSAndroid Build Coastguard Worker return new Promise((resolve, _reject) => { 788*6dbdd20aSAndroid Build Coastguard Worker proc.on('exit', (code, signal) => { 789*6dbdd20aSAndroid Build Coastguard Worker delete subprocesses[procIndex]; 790*6dbdd20aSAndroid Build Coastguard Worker checkExitCode(code, signal); 791*6dbdd20aSAndroid Build Coastguard Worker resolve(); 792*6dbdd20aSAndroid Build Coastguard Worker }); 793*6dbdd20aSAndroid Build Coastguard Worker }); 794*6dbdd20aSAndroid Build Coastguard Worker } else { 795*6dbdd20aSAndroid Build Coastguard Worker const spawnRes = childProcess.spawnSync(cmd, args, spwOpts); 796*6dbdd20aSAndroid Build Coastguard Worker checkExitCode(spawnRes.status, spawnRes.signal); 797*6dbdd20aSAndroid Build Coastguard Worker return spawnRes; 798*6dbdd20aSAndroid Build Coastguard Worker } 799*6dbdd20aSAndroid Build Coastguard Worker} 800*6dbdd20aSAndroid Build Coastguard Worker 801*6dbdd20aSAndroid Build Coastguard Workerfunction execModule(module, args, opts) { 802*6dbdd20aSAndroid Build Coastguard Worker const modPath = pjoin(ROOT_DIR, 'ui/node_modules/.bin', module); 803*6dbdd20aSAndroid Build Coastguard Worker return exec(modPath, args || [], opts); 804*6dbdd20aSAndroid Build Coastguard Worker} 805*6dbdd20aSAndroid Build Coastguard Worker 806*6dbdd20aSAndroid Build Coastguard Worker// ------------------------------------------ 807*6dbdd20aSAndroid Build Coastguard Worker// File system & subprocess utility functions 808*6dbdd20aSAndroid Build Coastguard Worker// ------------------------------------------ 809*6dbdd20aSAndroid Build Coastguard Worker 810*6dbdd20aSAndroid Build Coastguard Workerclass Task { 811*6dbdd20aSAndroid Build Coastguard Worker constructor(func, args) { 812*6dbdd20aSAndroid Build Coastguard Worker this.func = func; 813*6dbdd20aSAndroid Build Coastguard Worker this.args = args || []; 814*6dbdd20aSAndroid Build Coastguard Worker // |identity| is used to dedupe identical tasks in the queue. 815*6dbdd20aSAndroid Build Coastguard Worker this.identity = JSON.stringify([this.func.name, this.args]); 816*6dbdd20aSAndroid Build Coastguard Worker } 817*6dbdd20aSAndroid Build Coastguard Worker 818*6dbdd20aSAndroid Build Coastguard Worker get description() { 819*6dbdd20aSAndroid Build Coastguard Worker const ret = this.func.name.startsWith('exec') ? [] : [this.func.name]; 820*6dbdd20aSAndroid Build Coastguard Worker const flattenedArgs = [].concat(...this.args); 821*6dbdd20aSAndroid Build Coastguard Worker for (const arg of flattenedArgs) { 822*6dbdd20aSAndroid Build Coastguard Worker const argStr = `${arg}`; 823*6dbdd20aSAndroid Build Coastguard Worker if (argStr.startsWith('/')) { 824*6dbdd20aSAndroid Build Coastguard Worker ret.push(path.relative(cfg.outDir, arg)); 825*6dbdd20aSAndroid Build Coastguard Worker } else { 826*6dbdd20aSAndroid Build Coastguard Worker ret.push(argStr); 827*6dbdd20aSAndroid Build Coastguard Worker } 828*6dbdd20aSAndroid Build Coastguard Worker } 829*6dbdd20aSAndroid Build Coastguard Worker return ret.join(' '); 830*6dbdd20aSAndroid Build Coastguard Worker } 831*6dbdd20aSAndroid Build Coastguard Worker} 832*6dbdd20aSAndroid Build Coastguard Worker 833*6dbdd20aSAndroid Build Coastguard Workerfunction walk(dir, callback, skipRegex) { 834*6dbdd20aSAndroid Build Coastguard Worker for (const child of fs.readdirSync(dir)) { 835*6dbdd20aSAndroid Build Coastguard Worker const childPath = pjoin(dir, child); 836*6dbdd20aSAndroid Build Coastguard Worker const stat = fs.lstatSync(childPath); 837*6dbdd20aSAndroid Build Coastguard Worker if (skipRegex !== undefined && skipRegex.test(child)) continue; 838*6dbdd20aSAndroid Build Coastguard Worker if (stat.isDirectory()) { 839*6dbdd20aSAndroid Build Coastguard Worker walk(childPath, callback, skipRegex); 840*6dbdd20aSAndroid Build Coastguard Worker } else if (!stat.isSymbolicLink()) { 841*6dbdd20aSAndroid Build Coastguard Worker callback(childPath); 842*6dbdd20aSAndroid Build Coastguard Worker } 843*6dbdd20aSAndroid Build Coastguard Worker } 844*6dbdd20aSAndroid Build Coastguard Worker} 845*6dbdd20aSAndroid Build Coastguard Worker 846*6dbdd20aSAndroid Build Coastguard Worker// Recursively build a list of files in a given directory and return a list of 847*6dbdd20aSAndroid Build Coastguard Worker// file paths, similar to `find -type f`. 848*6dbdd20aSAndroid Build Coastguard Workerfunction listFilesRecursive(dir) { 849*6dbdd20aSAndroid Build Coastguard Worker const fileList = []; 850*6dbdd20aSAndroid Build Coastguard Worker 851*6dbdd20aSAndroid Build Coastguard Worker walk(dir, (filePath) => { 852*6dbdd20aSAndroid Build Coastguard Worker fileList.push(filePath); 853*6dbdd20aSAndroid Build Coastguard Worker }); 854*6dbdd20aSAndroid Build Coastguard Worker 855*6dbdd20aSAndroid Build Coastguard Worker return fileList; 856*6dbdd20aSAndroid Build Coastguard Worker} 857*6dbdd20aSAndroid Build Coastguard Worker 858*6dbdd20aSAndroid Build Coastguard Workerfunction ensureDir(dirPath, clean) { 859*6dbdd20aSAndroid Build Coastguard Worker const exists = fs.existsSync(dirPath); 860*6dbdd20aSAndroid Build Coastguard Worker if (exists && clean) { 861*6dbdd20aSAndroid Build Coastguard Worker console.log('rm', dirPath); 862*6dbdd20aSAndroid Build Coastguard Worker fs.rmSync(dirPath, {recursive: true}); 863*6dbdd20aSAndroid Build Coastguard Worker } 864*6dbdd20aSAndroid Build Coastguard Worker if (!exists || clean) fs.mkdirSync(dirPath, {recursive: true}); 865*6dbdd20aSAndroid Build Coastguard Worker return dirPath; 866*6dbdd20aSAndroid Build Coastguard Worker} 867*6dbdd20aSAndroid Build Coastguard Worker 868*6dbdd20aSAndroid Build Coastguard Workerfunction cp(src, dst) { 869*6dbdd20aSAndroid Build Coastguard Worker ensureDir(path.dirname(dst)); 870*6dbdd20aSAndroid Build Coastguard Worker if (cfg.verbose) { 871*6dbdd20aSAndroid Build Coastguard Worker console.log( 872*6dbdd20aSAndroid Build Coastguard Worker 'cp', path.relative(ROOT_DIR, src), '->', path.relative(ROOT_DIR, dst)); 873*6dbdd20aSAndroid Build Coastguard Worker } 874*6dbdd20aSAndroid Build Coastguard Worker fs.copyFileSync(src, dst); 875*6dbdd20aSAndroid Build Coastguard Worker} 876*6dbdd20aSAndroid Build Coastguard Worker 877*6dbdd20aSAndroid Build Coastguard Workerfunction mklink(src, dst) { 878*6dbdd20aSAndroid Build Coastguard Worker // If the symlink already points to the right place don't touch it. This is 879*6dbdd20aSAndroid Build Coastguard Worker // to avoid changing the mtime of the ui/ dir when unnecessary. 880*6dbdd20aSAndroid Build Coastguard Worker if (fs.existsSync(dst)) { 881*6dbdd20aSAndroid Build Coastguard Worker if (fs.lstatSync(dst).isSymbolicLink() && fs.readlinkSync(dst) === src) { 882*6dbdd20aSAndroid Build Coastguard Worker return; 883*6dbdd20aSAndroid Build Coastguard Worker } else { 884*6dbdd20aSAndroid Build Coastguard Worker fs.unlinkSync(dst); 885*6dbdd20aSAndroid Build Coastguard Worker } 886*6dbdd20aSAndroid Build Coastguard Worker } 887*6dbdd20aSAndroid Build Coastguard Worker fs.symlinkSync(src, dst); 888*6dbdd20aSAndroid Build Coastguard Worker} 889*6dbdd20aSAndroid Build Coastguard Worker 890*6dbdd20aSAndroid Build Coastguard Workermain(); 891