blob: d7d67a8a862c4fd115ab8130dfb06250fd827da5 [file] [log] [blame]
# Copyright (C) 2018 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.
import("../gn/perfetto.gni")
import("../gn/wasm.gni")
import("../protos/perfetto/trace_processor/proto_files.gni")
# Prevent that this file is accidentally included in embedder builds.
assert(enable_perfetto_ui)
ui_dir = "$root_build_dir/ui"
chrome_extension_dir = "$root_build_dir/chrome_extension"
ui_gen_dir = "$target_out_dir/gen"
nodejs_root = "../buildtools/nodejs"
nodejs_bin = rebase_path("$nodejs_root/bin", root_build_dir)
# +----------------------------------------------------------------------------+
# | The outer "ui" target to just ninja -C out/xxx ui |
# +----------------------------------------------------------------------------+
group("ui") {
deps = [
":chrome_extension_assets_dist",
":chrome_extension_bundle_dist",
":dist",
":gen_dist_file_map",
":service_worker_bundle_dist",
":test_scripts",
# IMPORTANT: Only add deps here if they are NOT part of the production UI
# (e.g., tests, extensions, ...). Any UI dep should go in the
# |ui_dist_targets| list below. The only exception is the service worker
# target, that depends on that list.
]
}
# The list of targets that produces dist/ files for the UI. This list is used
# also by the gen_dist_file_map to generate the map of hashes of all UI files,
# which is turn used by the service worker code for the offline caching.
ui_dist_targets = [
":assets_dist",
":catapult_dist",
":controller_bundle_dist",
":engine_bundle_dist",
":frontend_bundle_dist",
":index_dist",
":scss",
":typefaces_dist",
":wasm_dist",
]
# Buils the ui, but not service worker, tests and extensions.
group("dist") {
deps = ui_dist_targets
}
# A minimal page to profile the WASM engine without the all UI.
group("query") {
deps = [
":query_bundle_dist",
":query_dist",
":ui",
]
}
# +----------------------------------------------------------------------------+
# | Template used to run node binaries using the hermetic node toolchain. |
# +----------------------------------------------------------------------------+
template("node_bin") {
action(target_name) {
forward_variables_from(invoker,
[
"inputs",
"outputs",
"depfile",
])
deps = [
":node_modules",
]
if (defined(invoker.deps)) {
deps += invoker.deps
}
script = "../gn/standalone/build_tool_wrapper.py"
_node_cmd = invoker.node_cmd
args = []
if (defined(invoker.suppress_stdout) && invoker.suppress_stdout) {
args += [ "--suppress_stdout" ]
}
if (defined(invoker.suppress_stderr) && invoker.suppress_stderr) {
args += [ "--suppress_stderr" ]
}
args += [
"--path=$nodejs_bin",
"node",
rebase_path("node_modules/.bin/$_node_cmd", root_build_dir),
] + invoker.args
}
}
# +----------------------------------------------------------------------------+
# | Template for "sorcery" the source map resolver. |
# +----------------------------------------------------------------------------+
template("sorcery") {
node_bin(target_name) {
assert(defined(invoker.input))
assert(defined(invoker.output))
forward_variables_from(invoker, [ "deps" ])
inputs = [
invoker.input,
]
outputs = [
invoker.output,
invoker.output + ".map",
]
node_cmd = "sorcery"
args = [
"-i",
rebase_path(invoker.input, root_build_dir),
"-o",
rebase_path(invoker.output, root_build_dir),
]
}
}
# +----------------------------------------------------------------------------+
# | Template for bundling js |
# +----------------------------------------------------------------------------+
template("bundle") {
node_bin(target_name) {
assert(defined(invoker.input))
assert(defined(invoker.output))
forward_variables_from(invoker, [ "deps" ])
inputs = [
invoker.input,
"rollup.config.js",
]
outputs = [
invoker.output,
invoker.output + ".map",
]
node_cmd = "rollup"
args = [
"-c",
rebase_path("rollup.config.js", root_build_dir),
rebase_path(invoker.input, root_build_dir),
"-o",
rebase_path(invoker.output, root_build_dir),
"-f",
"iife",
"-m",
"--silent",
]
}
}
# +----------------------------------------------------------------------------+
# | Bundles all *.js files together resolving CommonJS require() deps. |
# +----------------------------------------------------------------------------+
# Bundle together all js sources into a bundle.js file, that will ultimately be
# included by the .html files.
bundle("frontend_bundle") {
deps = [
":transpile_all_ts",
]
input = "$target_out_dir/frontend/index.js"
output = "$target_out_dir/frontend_bundle.js"
}
bundle("chrome_extension_bundle") {
deps = [
":transpile_all_ts",
]
input = "$target_out_dir/chrome_extension/index.js"
output = "$target_out_dir/chrome_extension_bundle.js"
}
bundle("controller_bundle") {
deps = [
":transpile_all_ts",
]
input = "$target_out_dir/controller/index.js"
output = "$target_out_dir/controller_bundle.js"
}
bundle("engine_bundle") {
deps = [
":transpile_all_ts",
]
input = "$target_out_dir/engine/index.js"
output = "$target_out_dir/engine_bundle.js"
}
bundle("service_worker_bundle") {
deps = [
":transpile_service_worker_ts",
]
input = "$target_out_dir/service_worker/service_worker.js"
output = "$target_out_dir/service_worker.js"
}
bundle("query_bundle") {
deps = [
":transpile_all_ts",
]
input = "$target_out_dir/query/index.js"
output = "$target_out_dir/query_bundle.js"
}
# +----------------------------------------------------------------------------+
# | Protobuf: gen rules to create .js and .d.ts files from protos. |
# +----------------------------------------------------------------------------+
node_bin("protos_to_js") {
inputs = []
foreach(proto, trace_processor_protos) {
inputs += [ "../protos/perfetto/trace_processor/$proto.proto" ]
}
inputs += [
"../protos/perfetto/config/perfetto_config.proto",
"../protos/perfetto/ipc/consumer_port.proto",
"../protos/perfetto/ipc/wire_protocol.proto",
]
outputs = [
"$ui_gen_dir/protos.js",
]
node_cmd = "pbjs"
args = [
"-t",
"static-module",
"-w",
"commonjs",
"-p",
rebase_path("..", root_build_dir),
"-o",
rebase_path(outputs[0], root_build_dir),
] + rebase_path(inputs, root_build_dir)
}
# Protobuf.js requires to first generate .js files from the .proto and then
# create .ts definitions for them.
node_bin("protos_to_ts") {
deps = [
":protos_to_js",
]
inputs = [
"$ui_gen_dir/protos.js",
]
outputs = [
"$ui_gen_dir/protos.d.ts",
]
node_cmd = "pbts"
args = [
"-p",
rebase_path("..", root_build_dir),
"-o",
rebase_path(outputs[0], root_build_dir),
rebase_path(inputs[0], root_build_dir),
]
}
# +----------------------------------------------------------------------------+
# | TypeScript: transpiles all *.ts into .js |
# +----------------------------------------------------------------------------+
# Builds all .ts sources in the repo under |src|.
node_bin("transpile_all_ts") {
deps = [
":dist_symlink",
":protos_to_ts",
":wasm_gen",
]
inputs = [
"tsconfig.json",
]
outputs = [
"$target_out_dir/frontend/index.js",
"$target_out_dir/engine/index.js",
"$target_out_dir/controller/index.js",
"$target_out_dir/query/index.js",
"$target_out_dir/chrome_extension/index.js",
]
depfile = root_out_dir + "/tsc.d"
exec_script("../gn/standalone/glob.py",
[
"--root=" + rebase_path(".", root_build_dir),
"--filter=*.ts",
"--exclude=node_modules",
"--exclude=dist",
"--exclude=service_worker",
"--deps=obj/ui/frontend/index.js",
"--output=" + rebase_path(depfile),
],
"")
node_cmd = "tsc"
args = [
"--project",
rebase_path(".", root_build_dir),
"--outDir",
rebase_path(target_out_dir, root_build_dir),
]
}
node_bin("transpile_service_worker_ts") {
deps = [
":dist_symlink",
":gen_dist_file_map",
]
inputs = [
"tsconfig.json",
"src/service_worker/service_worker.ts",
]
outputs = [
"$target_out_dir/service_worker/service_worker.js",
]
node_cmd = "tsc"
args = [
"--project",
rebase_path("src/service_worker", root_build_dir),
"--outDir",
rebase_path(target_out_dir, root_build_dir),
]
}
# +----------------------------------------------------------------------------+
# | Build css. |
# +----------------------------------------------------------------------------+
scss_root = "src/assets/perfetto.scss"
scss_srcs = [
"src/assets/typefaces.scss",
"src/assets/sidebar.scss",
"src/assets/topbar.scss",
"src/assets/record.scss",
"src/assets/common.scss",
"src/assets/modal.scss",
"src/assets/details.scss",
]
# Build css.
node_bin("scss") {
deps = [
":dist_symlink",
]
inputs = [ scss_root ] + scss_srcs
outputs = [
"$ui_dir/perfetto.css",
]
node_cmd = "node-sass"
args = [
"--quiet",
rebase_path(scss_root, root_build_dir),
rebase_path(outputs[0], root_build_dir),
]
}
# +----------------------------------------------------------------------------+
# | Copy rules: create the final output directory. |
# +----------------------------------------------------------------------------+
copy("index_dist") {
sources = [
"index.html",
]
outputs = [
"$ui_dir/index.html",
]
}
copy("typefaces_dist") {
sources = [
"../buildtools/typefaces/MaterialIcons.woff2",
"../buildtools/typefaces/Raleway-Regular.woff2",
"../buildtools/typefaces/Raleway-Thin.woff2",
"../buildtools/typefaces/RobotoCondensed-Light.woff2",
"../buildtools/typefaces/RobotoCondensed-Regular.woff2",
"../buildtools/typefaces/RobotoMono-Regular.woff2",
]
outputs = [
"$ui_dir/assets/{{source_file_part}}",
]
}
copy("query_dist") {
sources = [
"query.html",
]
outputs = [
"$ui_dir/query.html",
]
}
copy("assets_dist") {
sources = [
"src/assets/heap_profiler.png",
"src/assets/logo-3d.png",
"src/assets/logo.png",
"src/assets/rec_atrace.png",
"src/assets/rec_battery_counters.png",
"src/assets/rec_board_voltage.png",
"src/assets/rec_cpu_coarse.png",
"src/assets/rec_cpu_fine.png",
"src/assets/rec_cpu_freq.png",
"src/assets/rec_cpu_voltage.png",
"src/assets/rec_cpu_wakeup.png",
"src/assets/rec_ftrace.png",
"src/assets/rec_lmk.png",
"src/assets/rec_logcat.png",
"src/assets/rec_long_trace.png",
"src/assets/rec_mem_hifreq.png",
"src/assets/rec_meminfo.png",
"src/assets/rec_one_shot.png",
"src/assets/rec_ps_stats.png",
"src/assets/rec_ring_buf.png",
"src/assets/rec_vmstat.png",
] + [ scss_root ] + scss_srcs
outputs = [
"$ui_dir/assets/{{source_file_part}}",
]
}
copy("chrome_extension_assets_dist") {
sources = [
"src/assets/logo-128.png",
"src/assets/logo.png",
"src/chrome_extension/manifest.json",
]
outputs = [
"$chrome_extension_dir/{{source_file_part}}",
]
}
sorcery("frontend_bundle_dist") {
deps = [
":frontend_bundle",
]
input = "$target_out_dir/frontend_bundle.js"
output = "$ui_dir/frontend_bundle.js"
}
sorcery("chrome_extension_bundle_dist") {
deps = [
":chrome_extension_bundle",
]
input = "$target_out_dir/chrome_extension_bundle.js"
output = "$chrome_extension_dir/chrome_extension_bundle.js"
}
sorcery("controller_bundle_dist") {
deps = [
":controller_bundle",
]
input = "$target_out_dir/controller_bundle.js"
output = "$ui_dir/controller_bundle.js"
}
sorcery("engine_bundle_dist") {
deps = [
":engine_bundle",
]
input = "$target_out_dir/engine_bundle.js"
output = "$ui_dir/engine_bundle.js"
}
sorcery("service_worker_bundle_dist") {
deps = [
":service_worker_bundle",
]
input = "$target_out_dir/service_worker.js"
output = "$ui_dir/service_worker.js"
}
sorcery("query_bundle_dist") {
deps = [
":query_bundle",
]
input = "$target_out_dir/query_bundle.js"
output = "$ui_dir/query_bundle.js"
}
copy("wasm_dist") {
deps = [
"//src/trace_processor:trace_processor.wasm($wasm_toolchain)",
"//tools/trace_to_text:trace_to_text.wasm($wasm_toolchain)",
]
sources = [
"$root_build_dir/wasm/trace_processor.wasm",
"$root_build_dir/wasm/trace_to_text.wasm",
]
outputs = [
"$ui_dir/{{source_file_part}}",
]
}
copy("wasm_gen") {
deps = [
":dist_symlink",
# trace_processor
"//src/trace_processor:trace_processor.d.ts($wasm_toolchain)",
"//src/trace_processor:trace_processor.js($wasm_toolchain)",
"//src/trace_processor:trace_processor.wasm($wasm_toolchain)",
# trace_to_text
"//tools/trace_to_text:trace_to_text.d.ts($wasm_toolchain)",
"//tools/trace_to_text:trace_to_text.js($wasm_toolchain)",
"//tools/trace_to_text:trace_to_text.wasm($wasm_toolchain)",
]
sources = [
# trace_processor
"$root_build_dir/wasm/trace_processor.d.ts",
"$root_build_dir/wasm/trace_processor.js",
"$root_build_dir/wasm/trace_processor.wasm",
# trace_to_text
"$root_build_dir/wasm/trace_to_text.d.ts",
"$root_build_dir/wasm/trace_to_text.js",
"$root_build_dir/wasm/trace_to_text.wasm",
]
if (is_debug) {
sources += [
"$root_build_dir/wasm/trace_processor.wasm.map",
"$root_build_dir/wasm/trace_to_text.wasm.map",
]
}
outputs = [
"$ui_gen_dir/{{source_file_part}}",
]
}
# Copy over the vulcanized legacy trace viewer.
copy("catapult_dist") {
sources = [
"../buildtools/catapult_trace_viewer/catapult_trace_viewer.html",
"../buildtools/catapult_trace_viewer/catapult_trace_viewer.js",
]
outputs = [
"$ui_dir/assets/{{source_file_part}}",
]
}
# +----------------------------------------------------------------------------+
# | Node JS: Creates a symlink in the out directory to node_modules. |
# +----------------------------------------------------------------------------+
action("check_node_exists") {
script = "../gn/standalone/check_buildtool_exists.py"
args = [
nodejs_bin,
"--touch",
rebase_path("$target_out_dir/node_exists", ""),
]
inputs = []
outputs = [
"$target_out_dir/node_exists",
]
}
# Creates a symlink from out/xxx/ui/node_modules -> ../../../ui/node_modules.
# This allows to run rollup and other node tools from the out/xxx directory.
action("node_modules_symlink") {
deps = [
":check_node_exists",
]
script = "../gn/standalone/build_tool_wrapper.py"
stamp_file = "$target_out_dir/.$target_name.stamp"
args = [
"--stamp",
rebase_path(stamp_file, root_build_dir),
"/bin/ln",
"-fns",
rebase_path("node_modules", target_out_dir),
rebase_path("$target_out_dir/node_modules", root_build_dir),
]
outputs = [
stamp_file,
]
}
group("node_modules") {
deps = [
":node_modules_symlink",
]
}
# Creates a symlink from //ui/dist -> ../../out/xxx/ui. Used only for
# autocompletion in IDEs. The problem this is solving is that in tsconfig.json
# we can't possibly know the path to ../../out/xxx for outDir. Instead, we set
# outDir to "./dist" and create a symlink on the first build.
action("dist_symlink") {
script = "../gn/standalone/build_tool_wrapper.py"
stamp_file = "$target_out_dir/.$target_name.stamp"
args = [
"--stamp",
rebase_path(stamp_file, root_build_dir),
"/bin/ln",
"-fns",
rebase_path(target_out_dir, "."),
rebase_path("dist", root_build_dir),
]
inputs = [
"$root_build_dir",
]
outputs = [
stamp_file,
]
}
group("test_scripts") {
deps = [
":copy_tests_script",
":copy_unittests_script",
]
}
copy("copy_unittests_script") {
sources = [
"config/ui_unittests_template",
]
outputs = [
"$root_build_dir/ui_unittests",
]
}
copy("copy_tests_script") {
sources = [
"config/ui_tests_template",
]
outputs = [
"$root_build_dir/ui_tests",
]
}
# This target generates an map containing all the UI subresources and their
# hashes. This is used by the service worker code for offline caching.
# This taarget needs to be kept at the end of the BUILD.gn file, because of the
# get_target_outputs() call (fails otherwise due to GN's evaluation order).
action("gen_dist_file_map") {
out_file_path = "$ui_gen_dir/dist_file_map.ts"
dist_files = []
foreach(target, ui_dist_targets) {
foreach(dist_file, get_target_outputs(target)) {
dist_files += [ rebase_path(dist_file, root_build_dir) ]
}
}
deps = [
":dist",
]
script = "../gn/standalone/write_ui_dist_file_map.py"
inputs = []
outputs = [
out_file_path,
]
args = [
"--out",
rebase_path(out_file_path, root_build_dir),
"--strip",
rebase_path(ui_dir, root_build_dir),
] + dist_files
}