# 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_dir = "$root_build_dir/ui" 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 = [ ":assets_dist", ":controller_bundle_dist", ":engine_bundle_dist", ":frontend_bundle_dist", ":index_dist", ":scss", ":test_scripts", ":wasm_dist", ] } # +----------------------------------------------------------------------------+ # | 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("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" } # +----------------------------------------------------------------------------+ # | 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" ] outputs = [ "$ui_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 = [ "$ui_gen_dir/protos.js", ] outputs = [ "$ui_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 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", ] 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", "--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), ] } # +----------------------------------------------------------------------------+ # | Build css. | # +----------------------------------------------------------------------------+ # Build css. node_bin("scss") { deps = [ ":dist_symlink", ] main_css = "src/assets/perfetto.scss" inputs = [ main_css, "src/assets/sidebar.scss", "src/assets/topbar.scss", ] outputs = [ "$ui_dir/perfetto.css", ] node_cmd = "node-sass" args = [ "--quiet", rebase_path(main_css, 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("assets_dist") { sources = [ "src/assets/flamegraph.svg", "src/assets/logo-3d.png", "src/assets/logo.png", "src/assets/perfetto.scss", "src/assets/sidebar.scss", "src/assets/topbar.scss", ] outputs = [ "$ui_dir/assets/{{source_file_part}}", ] } sorcery("frontend_bundle_dist") { deps = [ ":frontend_bundle", ] input = "$target_out_dir/frontend_bundle.js" output = "$ui_dir/frontend_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" } copy("wasm_dist") { deps = [ "//src/trace_processor:trace_processor.wasm($wasm_toolchain)", ] sources = [ "$root_build_dir/wasm/trace_processor.wasm", ] outputs = [ "$ui_dir/{{source_file_part}}", ] } copy("wasm_gen") { deps = [ ":dist_symlink", "//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)", ] sources = [ "$root_build_dir/wasm/trace_processor.d.ts", "$root_build_dir/wasm/trace_processor.js", "$root_build_dir/wasm/trace_processor.wasm", ] outputs = [ "$ui_gen_dir/{{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", ] }