xref: /aosp_15_r20/external/perfetto/infra/perfetto.dev/build.js (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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// This script builds the perfetto.dev docs website.
18*6dbdd20aSAndroid Build Coastguard Worker
19*6dbdd20aSAndroid Build Coastguard Workerconst argparse = require('argparse');
20*6dbdd20aSAndroid Build Coastguard Workerconst child_process = require('child_process');
21*6dbdd20aSAndroid Build Coastguard Workerconst fs = require('fs');
22*6dbdd20aSAndroid Build Coastguard Workerconst http = require('http');
23*6dbdd20aSAndroid Build Coastguard Workerconst path = require('path');
24*6dbdd20aSAndroid Build Coastguard Workerconst pjoin = path.join;
25*6dbdd20aSAndroid Build Coastguard Worker
26*6dbdd20aSAndroid Build Coastguard Workerconst ROOT_DIR = path.dirname(path.dirname(__dirname));  // The repo root.
27*6dbdd20aSAndroid Build Coastguard Worker
28*6dbdd20aSAndroid Build Coastguard Workerconst cfg = {
29*6dbdd20aSAndroid Build Coastguard Worker  watch: false,
30*6dbdd20aSAndroid Build Coastguard Worker  verbose: false,
31*6dbdd20aSAndroid Build Coastguard Worker  startHttpServer: false,
32*6dbdd20aSAndroid Build Coastguard Worker
33*6dbdd20aSAndroid Build Coastguard Worker  outDir: pjoin(ROOT_DIR, 'out/perfetto.dev'),
34*6dbdd20aSAndroid Build Coastguard Worker};
35*6dbdd20aSAndroid Build Coastguard Worker
36*6dbdd20aSAndroid Build Coastguard Workerfunction main() {
37*6dbdd20aSAndroid Build Coastguard Worker  const parser = new argparse.ArgumentParser();
38*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--out', {help: 'Output directory'});
39*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--watch', '-w', {action: 'store_true'});
40*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--serve', '-s', {action: 'store_true'});
41*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--verbose', '-v', {action: 'store_true'});
42*6dbdd20aSAndroid Build Coastguard Worker
43*6dbdd20aSAndroid Build Coastguard Worker  const args = parser.parse_args();
44*6dbdd20aSAndroid Build Coastguard Worker  cfg.outDir = path.resolve(ensureDir(args.out || cfg.outDir, /*clean=*/ true));
45*6dbdd20aSAndroid Build Coastguard Worker  cfg.watch = !!args.watch;
46*6dbdd20aSAndroid Build Coastguard Worker  cfg.verbose = !!args.verbose;
47*6dbdd20aSAndroid Build Coastguard Worker  cfg.startHttpServer = args.serve;
48*6dbdd20aSAndroid Build Coastguard Worker
49*6dbdd20aSAndroid Build Coastguard Worker  // Check that deps are current before starting.
50*6dbdd20aSAndroid Build Coastguard Worker  const installBuildDeps = pjoin(ROOT_DIR, 'tools/install-build-deps');
51*6dbdd20aSAndroid Build Coastguard Worker
52*6dbdd20aSAndroid Build Coastguard Worker  // --filter=nodejs --filter=pnpm --filter=gn --filter=ninja is to match what
53*6dbdd20aSAndroid Build Coastguard Worker  // cloud_build_entrypoint.sh passes to install-build-deps. It doesn't bother
54*6dbdd20aSAndroid Build Coastguard Worker  // installing the full toolchains because, unlike the Perfetto UI, it doesn't
55*6dbdd20aSAndroid Build Coastguard Worker  // need Wasm.
56*6dbdd20aSAndroid Build Coastguard Worker  const depsArgs = [
57*6dbdd20aSAndroid Build Coastguard Worker    '--check-only=/dev/null',
58*6dbdd20aSAndroid Build Coastguard Worker    '--ui',
59*6dbdd20aSAndroid Build Coastguard Worker    '--filter=nodejs',
60*6dbdd20aSAndroid Build Coastguard Worker    '--filter=pnpm',
61*6dbdd20aSAndroid Build Coastguard Worker    '--filter=gn',
62*6dbdd20aSAndroid Build Coastguard Worker    '--filter=ninja'
63*6dbdd20aSAndroid Build Coastguard Worker  ];
64*6dbdd20aSAndroid Build Coastguard Worker  exec(installBuildDeps, depsArgs);
65*6dbdd20aSAndroid Build Coastguard Worker
66*6dbdd20aSAndroid Build Coastguard Worker  ninjaBuild();
67*6dbdd20aSAndroid Build Coastguard Worker
68*6dbdd20aSAndroid Build Coastguard Worker  if (args.watch) {
69*6dbdd20aSAndroid Build Coastguard Worker    watchDir('docs');
70*6dbdd20aSAndroid Build Coastguard Worker    watchDir('infra/perfetto.dev/src/assets');
71*6dbdd20aSAndroid Build Coastguard Worker    watchDir('protos');
72*6dbdd20aSAndroid Build Coastguard Worker    watchDir('python');
73*6dbdd20aSAndroid Build Coastguard Worker    watchDir('src/trace_processor/tables');
74*6dbdd20aSAndroid Build Coastguard Worker  }
75*6dbdd20aSAndroid Build Coastguard Worker  if (args.serve) {
76*6dbdd20aSAndroid Build Coastguard Worker    startServer();
77*6dbdd20aSAndroid Build Coastguard Worker  }
78*6dbdd20aSAndroid Build Coastguard Worker}
79*6dbdd20aSAndroid Build Coastguard Worker
80*6dbdd20aSAndroid Build Coastguard Workerfunction ninjaBuild() {
81*6dbdd20aSAndroid Build Coastguard Worker  exec(
82*6dbdd20aSAndroid Build Coastguard Worker      pjoin(ROOT_DIR, 'tools/gn'),
83*6dbdd20aSAndroid Build Coastguard Worker      ['gen', cfg.outDir, '--args=enable_perfetto_site=true']);
84*6dbdd20aSAndroid Build Coastguard Worker  exec(pjoin(ROOT_DIR, 'tools/ninja'), ['-C', cfg.outDir, 'site']);
85*6dbdd20aSAndroid Build Coastguard Worker}
86*6dbdd20aSAndroid Build Coastguard Worker
87*6dbdd20aSAndroid Build Coastguard Workerfunction startServer() {
88*6dbdd20aSAndroid Build Coastguard Worker  const port = 8082;
89*6dbdd20aSAndroid Build Coastguard Worker  console.log(`Starting HTTP server on http://localhost:${port}`)
90*6dbdd20aSAndroid Build Coastguard Worker  const serveDir = path.join(cfg.outDir, 'site');
91*6dbdd20aSAndroid Build Coastguard Worker  http.createServer(function(req, res) {
92*6dbdd20aSAndroid Build Coastguard Worker        console.debug(req.method, req.url);
93*6dbdd20aSAndroid Build Coastguard Worker        let uri = req.url.split('?', 1)[0];
94*6dbdd20aSAndroid Build Coastguard Worker        uri += uri.endsWith('/') ? 'index.html' : '';
95*6dbdd20aSAndroid Build Coastguard Worker
96*6dbdd20aSAndroid Build Coastguard Worker        // Disallow serving anything outside out directory.
97*6dbdd20aSAndroid Build Coastguard Worker        const absPath = path.normalize(path.join(serveDir, uri));
98*6dbdd20aSAndroid Build Coastguard Worker        const relative = path.relative(serveDir, absPath);
99*6dbdd20aSAndroid Build Coastguard Worker        if (relative.startsWith('..')) {
100*6dbdd20aSAndroid Build Coastguard Worker          res.writeHead(404);
101*6dbdd20aSAndroid Build Coastguard Worker          res.end();
102*6dbdd20aSAndroid Build Coastguard Worker          return;
103*6dbdd20aSAndroid Build Coastguard Worker        }
104*6dbdd20aSAndroid Build Coastguard Worker
105*6dbdd20aSAndroid Build Coastguard Worker        fs.readFile(absPath, function(err, data) {
106*6dbdd20aSAndroid Build Coastguard Worker          if (err) {
107*6dbdd20aSAndroid Build Coastguard Worker            res.writeHead(404);
108*6dbdd20aSAndroid Build Coastguard Worker            res.end(JSON.stringify(err));
109*6dbdd20aSAndroid Build Coastguard Worker            return;
110*6dbdd20aSAndroid Build Coastguard Worker          }
111*6dbdd20aSAndroid Build Coastguard Worker          const mimeMap = {
112*6dbdd20aSAndroid Build Coastguard Worker            'css': 'text/css',
113*6dbdd20aSAndroid Build Coastguard Worker            'png': 'image/png',
114*6dbdd20aSAndroid Build Coastguard Worker            'svg': 'image/svg+xml',
115*6dbdd20aSAndroid Build Coastguard Worker            'js': 'application/javascript',
116*6dbdd20aSAndroid Build Coastguard Worker          };
117*6dbdd20aSAndroid Build Coastguard Worker          const contentType = mimeMap[uri.split('.').pop()] || 'text/html';
118*6dbdd20aSAndroid Build Coastguard Worker          const head = {
119*6dbdd20aSAndroid Build Coastguard Worker            'Content-Type': contentType,
120*6dbdd20aSAndroid Build Coastguard Worker            'Content-Length': data.length,
121*6dbdd20aSAndroid Build Coastguard Worker            'Cache-Control': 'no-cache',
122*6dbdd20aSAndroid Build Coastguard Worker          };
123*6dbdd20aSAndroid Build Coastguard Worker          res.writeHead(200, head);
124*6dbdd20aSAndroid Build Coastguard Worker          res.end(data);
125*6dbdd20aSAndroid Build Coastguard Worker        });
126*6dbdd20aSAndroid Build Coastguard Worker      })
127*6dbdd20aSAndroid Build Coastguard Worker      .listen(port, 'localhost');
128*6dbdd20aSAndroid Build Coastguard Worker}
129*6dbdd20aSAndroid Build Coastguard Worker
130*6dbdd20aSAndroid Build Coastguard Workerfunction watchDir(dir) {
131*6dbdd20aSAndroid Build Coastguard Worker  const absDir = path.isAbsolute(dir) ? dir : pjoin(ROOT_DIR, dir);
132*6dbdd20aSAndroid Build Coastguard Worker  // Add a fs watch if in watch mode.
133*6dbdd20aSAndroid Build Coastguard Worker  if (cfg.watch) {
134*6dbdd20aSAndroid Build Coastguard Worker    fs.watch(absDir, {recursive: true}, (_eventType, filePath) => {
135*6dbdd20aSAndroid Build Coastguard Worker      if (cfg.verbose) {
136*6dbdd20aSAndroid Build Coastguard Worker        console.log('File change detected', _eventType, filePath);
137*6dbdd20aSAndroid Build Coastguard Worker      }
138*6dbdd20aSAndroid Build Coastguard Worker      ninjaBuild();
139*6dbdd20aSAndroid Build Coastguard Worker    });
140*6dbdd20aSAndroid Build Coastguard Worker  }
141*6dbdd20aSAndroid Build Coastguard Worker}
142*6dbdd20aSAndroid Build Coastguard Worker
143*6dbdd20aSAndroid Build Coastguard Workerfunction exec(cmd, args, opts) {
144*6dbdd20aSAndroid Build Coastguard Worker  opts = opts || {};
145*6dbdd20aSAndroid Build Coastguard Worker  opts.stdout = opts.stdout || 'inherit';
146*6dbdd20aSAndroid Build Coastguard Worker  if (cfg.verbose) console.log(`${cmd} ${args.join(' ')}\n`);
147*6dbdd20aSAndroid Build Coastguard Worker  const spwOpts = {cwd: cfg.outDir, stdio: ['ignore', opts.stdout, 'inherit']};
148*6dbdd20aSAndroid Build Coastguard Worker  const checkExitCode = (code, signal) => {
149*6dbdd20aSAndroid Build Coastguard Worker    if (signal === 'SIGINT' || signal === 'SIGTERM') return;
150*6dbdd20aSAndroid Build Coastguard Worker    if (code !== 0 && !opts.noErrCheck) {
151*6dbdd20aSAndroid Build Coastguard Worker      console.error(`${cmd} ${args.join(' ')} failed with code ${code}`);
152*6dbdd20aSAndroid Build Coastguard Worker      process.exit(1);
153*6dbdd20aSAndroid Build Coastguard Worker    }
154*6dbdd20aSAndroid Build Coastguard Worker  };
155*6dbdd20aSAndroid Build Coastguard Worker  const spawnRes = child_process.spawnSync(cmd, args, spwOpts);
156*6dbdd20aSAndroid Build Coastguard Worker  checkExitCode(spawnRes.status, spawnRes.signal);
157*6dbdd20aSAndroid Build Coastguard Worker  return spawnRes;
158*6dbdd20aSAndroid Build Coastguard Worker}
159*6dbdd20aSAndroid Build Coastguard Worker
160*6dbdd20aSAndroid Build Coastguard Workerfunction ensureDir(dirPath, clean) {
161*6dbdd20aSAndroid Build Coastguard Worker  const exists = fs.existsSync(dirPath);
162*6dbdd20aSAndroid Build Coastguard Worker  if (exists && clean) {
163*6dbdd20aSAndroid Build Coastguard Worker    if (cfg.verbose) console.log('rm', dirPath);
164*6dbdd20aSAndroid Build Coastguard Worker    fs.rmSync(dirPath, {recursive: true});
165*6dbdd20aSAndroid Build Coastguard Worker  }
166*6dbdd20aSAndroid Build Coastguard Worker  if (!exists || clean) fs.mkdirSync(dirPath, {recursive: true});
167*6dbdd20aSAndroid Build Coastguard Worker  return dirPath;
168*6dbdd20aSAndroid Build Coastguard Worker}
169*6dbdd20aSAndroid Build Coastguard Worker
170*6dbdd20aSAndroid Build Coastguard Workermain();
171