Add skeleton for UI (Node + TypeScript + WASM + build integration)
This CL checks in a basic skeleton to develop the UI, introducing
- NodeJS binary, pulled in via tools/install-build-deps --ui
- WASM toolchain
- TypeScript compiler
- Protobuf.js library dependency
- Basic build file plumbing
- ui/run-dev-server
For the moment, it just introduced a hello world TypeScript file
that loads a hello world WASM library.
All it takes to build and bundle everything is:
$ ninja -C out/xxx ui
$ ui/run-dev-server
Bug: 73626618
Change-Id: I44a628c3dc8b36d1e9267f43f25f256761c7e90f
diff --git a/ui/BUILD.gn b/ui/BUILD.gn
new file mode 100644
index 0000000..b5a3ffa
--- /dev/null
+++ b/ui/BUILD.gn
@@ -0,0 +1,286 @@
+# 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")
+
+ui_dist_dir = "$root_build_dir/ui"
+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 = [
+ ":bundle",
+ ":index",
+ ":wasm",
+ ]
+}
+
+# +----------------------------------------------------------------------------+
+# | Template used to run node binaries using the hermetic node toolchain. |
+# +----------------------------------------------------------------------------+
+template("node_bin") {
+ action(target_name) {
+ forward_variables_from(invoker,
+ [
+ "inputs",
+ "outputs",
+ ])
+ deps = [
+ ":node_modules",
+ ]
+ if (defined(invoker.deps)) {
+ deps += invoker.deps
+ }
+ script = "../gn/standalone/build_tool_wrapper.py"
+ _node_cmd = invoker.node_cmd
+ args = [
+ "--path=$nodejs_bin",
+ "node",
+ rebase_path("node_modules/.bin/$_node_cmd", root_build_dir),
+ ] + invoker.args
+ }
+}
+
+# +----------------------------------------------------------------------------+
+# | 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.
+node_bin("bundle") {
+ deps = [
+ ":transpile_all_ts",
+ ]
+ node_cmd = "browserify"
+ inputs = [
+ "$ui_dist_dir/main.js",
+ ]
+ outputs = [
+ "$ui_dist_dir/bundle.js",
+ ]
+ args = [
+ rebase_path(inputs[0], root_build_dir),
+ "-d",
+ "-o",
+ rebase_path(outputs[0], root_build_dir),
+ ]
+}
+
+# +----------------------------------------------------------------------------+
+# | Protobuf: gen rules to create .js and .d.ts files from protos. |
+# +----------------------------------------------------------------------------+
+proto_gen_dir = "$ui_dist_dir/gen"
+
+node_bin("protos_to_js") {
+ inputs = []
+ foreach(proto, trace_processor_protos) {
+ inputs += [ "../protos/perfetto/trace_processor/$proto.proto" ]
+ }
+ outputs = [
+ "$proto_gen_dir/protos.js",
+ ]
+ node_cmd = "pbjs"
+ args = [
+ "-t",
+ "static-module",
+ "-w",
+ "commonjs",
+ "-p",
+ rebase_path("../protos", 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 = [
+ "$proto_gen_dir/protos.js",
+ ]
+ outputs = [
+ "$proto_gen_dir/protos.d.ts",
+ ]
+ node_cmd = "pbts"
+ args = [
+ "-p",
+ rebase_path("../protos", 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 that are reachable from |sources|.
+node_bin("transpile_all_ts") {
+ sources = [
+ "main.ts",
+ ]
+ deps = [
+ ":dist_symlink",
+ ":protos_to_ts",
+ ]
+ inputs = sources + [ "tsconfig.json" ]
+ outputs = [
+ "$ui_dist_dir/main.js",
+ ]
+
+ # Find all *.ts files and pass them as input triggers. This does NOT affect
+ # which files will get built. This defines only the set of files that, when
+ # changes, will re-trigger this rule in ninja. The files that get transpiled
+ # are the transitive dependencies of |sources|.
+ all_ts_files = exec_script("../gn/standalone/glob.py",
+ [
+ "--root=" + rebase_path(".", root_build_dir),
+ "--filter=*.ts",
+ "--exclude=node_modules",
+ "--exclude=dist",
+ ],
+ "list lines",
+ [ "." ])
+ inputs += all_ts_files
+
+ node_cmd = "tsc"
+ args = [
+ "--project",
+ rebase_path(".", root_build_dir),
+ "--outDir",
+ rebase_path(ui_dist_dir, root_build_dir),
+ ]
+}
+
+# +----------------------------------------------------------------------------+
+# | Copy rules: copy assets into the dist directory. |
+# +----------------------------------------------------------------------------+
+copy("index") {
+ sources = [
+ "index.html",
+ ]
+ outputs = [
+ "$ui_dist_dir/index.html",
+ ]
+}
+
+copy("wasm") {
+ deps = [
+ "//src/trace_processor:trace_processor.js($wasm_toolchain)",
+ "//src/trace_processor:trace_processor.wasm($wasm_toolchain)",
+ ]
+ sources = [
+ "$root_build_dir/wasm/trace_processor.js",
+ "$root_build_dir/wasm/trace_processor.wasm",
+ ]
+ outputs = [
+ "$ui_dist_dir/wasm/{{source_file_part}}",
+ ]
+}
+
+# +----------------------------------------------------------------------------+
+# | Node JS: run npm install and creates a symlink in the out directory. |
+# +----------------------------------------------------------------------------+
+
+action("check_node_exists") {
+ script = "../gn/standalone/check_buildtool_exists.py"
+ args = [ nodejs_bin ]
+ inputs = []
+ outputs = [
+ "$target_out_dir/ignored",
+ ]
+}
+
+# Creates a symlink from out/xxx/ui/node_modules -> ../../../ui/node_modules.
+# This allows to run browserify and other node tools from the out/xxx directory.
+action("node_modules_symlink") {
+ deps = [
+ ":check_node_exists",
+ ]
+ inputs = [
+ "node_modules",
+ ]
+ outputs = [
+ "$ui_dist_dir/node_modules",
+ ]
+ script = "../gn/standalone/build_tool_wrapper.py"
+ args = [
+ "/bin/ln",
+ "-fns",
+ rebase_path(inputs[0], ui_dist_dir),
+ rebase_path(outputs[0], root_build_dir),
+ ]
+}
+
+# Runs npm install.
+action("node_modules") {
+ script = "../gn/standalone/build_tool_wrapper.py"
+ stamp_file = "$ui_dist_dir/.$target_name.stamp"
+ args = [
+ "--chdir",
+ rebase_path(".", root_build_dir),
+ "--stamp",
+ rebase_path(stamp_file, root_build_dir),
+ ]
+ args += [
+ "--path=$nodejs_bin",
+ "node",
+ rebase_path("$nodejs_root/bin/npm", "."),
+ "install",
+ "--no-save",
+ "--silent",
+ ]
+ inputs = [
+ "package.json",
+ "package-lock.json",
+ ]
+ outputs = [
+ stamp_file,
+ ]
+ 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 = "$ui_dist_dir/.$target_name.stamp"
+ args = [
+ "--stamp",
+ rebase_path(stamp_file, root_build_dir),
+ "/bin/ln",
+ "-fns",
+ rebase_path(ui_dist_dir, "."),
+ rebase_path("dist", root_build_dir),
+ ]
+ inputs = [
+ "$root_build_dir",
+ ]
+ outputs = [
+ stamp_file,
+ ]
+}