166 lines
5.2 KiB
JavaScript
166 lines
5.2 KiB
JavaScript
// Copyright (C) 2021 The Android Open Source Project
|
|
//
|
|
// 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.
|
|
|
|
'use strict';
|
|
|
|
// This script builds the perfetto.dev docs website.
|
|
|
|
const argparse = require('argparse');
|
|
const child_process = require('child_process');
|
|
const fs = require('fs');
|
|
const http = require('http');
|
|
const path = require('path');
|
|
const fswatch = require('node-watch'); // Like fs.watch(), but works on Linux.
|
|
const pjoin = path.join;
|
|
|
|
const ROOT_DIR = path.dirname(path.dirname(__dirname)); // The repo root.
|
|
|
|
const cfg = {
|
|
watch: false,
|
|
verbose: false,
|
|
startHttpServer: false,
|
|
|
|
outDir: pjoin(ROOT_DIR, 'out/perfetto.dev'),
|
|
};
|
|
|
|
function main() {
|
|
const parser = new argparse.ArgumentParser();
|
|
parser.add_argument('--out', {help: 'Output directory'});
|
|
parser.add_argument('--watch', '-w', {action: 'store_true'});
|
|
parser.add_argument('--serve', '-s', {action: 'store_true'});
|
|
parser.add_argument('--verbose', '-v', {action: 'store_true'});
|
|
|
|
const args = parser.parse_args();
|
|
cfg.outDir = path.resolve(ensureDir(args.out || cfg.outDir, /*clean=*/ true));
|
|
cfg.watch = !!args.watch;
|
|
cfg.verbose = !!args.verbose;
|
|
cfg.startHttpServer = args.serve;
|
|
|
|
// Check that deps are current before starting.
|
|
const installBuildDeps = pjoin(ROOT_DIR, 'tools/install-build-deps');
|
|
|
|
// --filter=nodejs --filter=gn --filter=ninja is to match what
|
|
// cloud_build_entrypoint.sh passes to install-build-deps. It doesn't bother
|
|
// installing the full toolchains because, unlike the Perfetto UI, it doesn't
|
|
// need Wasm.
|
|
const depsArgs = ['--check-only=/dev/null', '--ui', '--filter=nodejs',
|
|
'--filter=gn', '--filter=ninja'];
|
|
exec(installBuildDeps, depsArgs);
|
|
|
|
ninjaBuild();
|
|
|
|
if (args.watch) {
|
|
watchDir('docs');
|
|
watchDir('infra/perfetto.dev/src/assets');
|
|
watchDir('protos');
|
|
watchDir('python');
|
|
watchDir('src/trace_processor/tables');
|
|
}
|
|
if (args.serve) {
|
|
startServer();
|
|
}
|
|
}
|
|
|
|
function ninjaBuild() {
|
|
exec(
|
|
pjoin(ROOT_DIR, 'tools/gn'),
|
|
['gen', cfg.outDir, '--args=enable_perfetto_site=true']);
|
|
exec(pjoin(ROOT_DIR, 'tools/ninja'), ['-C', cfg.outDir, 'site']);
|
|
}
|
|
|
|
function startServer() {
|
|
const port = 8082;
|
|
console.log(`Starting HTTP server on http://localhost:${port}`)
|
|
const serveDir = path.join(cfg.outDir, 'site');
|
|
http.createServer(function(req, res) {
|
|
console.debug(req.method, req.url);
|
|
let uri = req.url.split('?', 1)[0];
|
|
uri += uri.endsWith('/') ? 'index.html' : '';
|
|
|
|
// Disallow serving anything outside out directory.
|
|
const absPath = path.normalize(path.join(serveDir, uri));
|
|
const relative = path.relative(serveDir, absPath);
|
|
if (relative.startsWith('..')) {
|
|
res.writeHead(404);
|
|
res.end();
|
|
return;
|
|
}
|
|
|
|
fs.readFile(absPath, function(err, data) {
|
|
if (err) {
|
|
res.writeHead(404);
|
|
res.end(JSON.stringify(err));
|
|
return;
|
|
}
|
|
const mimeMap = {
|
|
'css': 'text/css',
|
|
'png': 'image/png',
|
|
'svg': 'image/svg+xml',
|
|
'js': 'application/javascript',
|
|
};
|
|
const contentType = mimeMap[uri.split('.').pop()] || 'text/html';
|
|
const head = {
|
|
'Content-Type': contentType,
|
|
'Content-Length': data.length,
|
|
'Cache-Control': 'no-cache',
|
|
};
|
|
res.writeHead(200, head);
|
|
res.end(data);
|
|
});
|
|
})
|
|
.listen(port, 'localhost');
|
|
}
|
|
|
|
function watchDir(dir) {
|
|
const absDir = path.isAbsolute(dir) ? dir : pjoin(ROOT_DIR, dir);
|
|
// Add a fs watch if in watch mode.
|
|
if (cfg.watch) {
|
|
fswatch(absDir, {recursive: true}, (_eventType, filePath) => {
|
|
if (cfg.verbose) {
|
|
console.log('File change detected', _eventType, filePath);
|
|
}
|
|
ninjaBuild();
|
|
});
|
|
}
|
|
}
|
|
|
|
function exec(cmd, args, opts) {
|
|
opts = opts || {};
|
|
opts.stdout = opts.stdout || 'inherit';
|
|
if (cfg.verbose) console.log(`${cmd} ${args.join(' ')}\n`);
|
|
const spwOpts = {cwd: cfg.outDir, stdio: ['ignore', opts.stdout, 'inherit']};
|
|
const checkExitCode = (code, signal) => {
|
|
if (signal === 'SIGINT' || signal === 'SIGTERM') return;
|
|
if (code !== 0 && !opts.noErrCheck) {
|
|
console.error(`${cmd} ${args.join(' ')} failed with code ${code}`);
|
|
process.exit(1);
|
|
}
|
|
};
|
|
const spawnRes = child_process.spawnSync(cmd, args, spwOpts);
|
|
checkExitCode(spawnRes.status, spawnRes.signal);
|
|
return spawnRes;
|
|
}
|
|
|
|
function ensureDir(dirPath, clean) {
|
|
const exists = fs.existsSync(dirPath);
|
|
if (exists && clean) {
|
|
if (cfg.verbose) console.log('rm', dirPath);
|
|
fs.rmSync(dirPath, {recursive: true});
|
|
}
|
|
if (!exists || clean) fs.mkdirSync(dirPath, {recursive: true});
|
|
return dirPath;
|
|
}
|
|
|
|
main();
|