1# Copyright (C) 2022 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import("../../gn/perfetto.gni") 16 17# Prevent that this file is accidentally included in embedder builds. 18assert(enable_perfetto_site) 19 20nodejs_bin = rebase_path("../../tools/node", root_build_dir) 21 22# The destination directory where the website will be built. GN pollutes 23# root_out_dir with all sorts of files, so we use a subdirectory. 24perfetto_website_out_dir = "$root_out_dir/site" 25 26# The directory containing all the markdown sources for the docs. 27src_doc_dir = "../../docs" 28 29group("site") { 30 deps = [ 31 ":all_mdfiles", 32 ":assets", 33 ":gen_index", 34 ":gen_sql_stats_html", 35 ":gen_sql_tables_html", 36 ":gen_stdlib_docs_html", 37 ":gen_toc", 38 ":gen_trace_config_proto", 39 ":gen_trace_packet_proto", 40 ":gen_ui_plugin_api_html", 41 ":node_assets", 42 ":readme", 43 ":style_scss", 44 ] 45} 46 47# Runs a nodejs script using the hermetic node toolchain. 48# Args: 49# * script: The .js script to execute 50# * inputs 51# * outputs 52# * deps 53# * depfile 54template("nodejs_script") { 55 assert(defined(invoker.script), "Need script in $target_name") 56 57 action(target_name) { 58 forward_variables_from(invoker, 59 [ 60 "outputs", 61 "depfile", 62 ]) 63 deps = [ ":node_modules" ] 64 if (defined(invoker.deps)) { 65 deps += invoker.deps 66 } 67 script = "../../gn/standalone/build_tool_wrapper.py" 68 inputs = [ invoker.script ] 69 inputs += invoker.inputs 70 args = [ 71 nodejs_bin, 72 rebase_path(invoker.script, root_build_dir), 73 ] 74 args += invoker.args 75 } 76} 77 78# Installs the node modules specified in package.json 79action("node_modules") { 80 script = "../../gn/standalone/build_tool_wrapper.py" 81 stamp_file = "$target_out_dir/.$target_name.stamp" 82 cur_dir = rebase_path(".", root_build_dir) 83 args = [ 84 "--stamp", 85 rebase_path(stamp_file, root_build_dir), 86 "--chdir=$cur_dir", 87 rebase_path("../../tools/pnpm", root_build_dir), 88 "install", 89 "--shamefully-hoist", 90 "--frozen-lockfile", 91 ] 92 inputs = [ 93 "../../tools/npm", 94 "package.json", 95 "pnpm-lock.yaml", 96 ] 97 outputs = [ stamp_file ] 98} 99 100# Renders a markdown file into html. 101# Args: 102# * markdown: Optional. The source markdown file 103# * html_template: Optional. The html template to use 104# * out_html: The generated html, relative to `perfetto_website_out_dir` 105# * deps 106template("md_to_html") { 107 assert(defined(invoker.out_html), "Need out_html in $target_name") 108 assert(defined(invoker.html_template) || defined(invoker.markdown), 109 "Need html_template or markdown in $target_name") 110 nodejs_script(target_name) { 111 forward_variables_from(invoker, [ "deps" ]) 112 script = "src/markdown_render.js" 113 inputs = [] 114 if (defined(invoker.markdown)) { 115 inputs += [ invoker.markdown ] 116 } 117 depfile = "${target_gen_dir}/$target_name.d" 118 if (defined(invoker.html_template)) { 119 inputs += [ invoker.html_template ] 120 } 121 outputs = [ "${perfetto_website_out_dir}/${invoker.out_html}" ] 122 args = [ 123 "--odir", 124 rebase_path(perfetto_website_out_dir, root_build_dir), 125 "-o", 126 rebase_path("${perfetto_website_out_dir}/${invoker.out_html}", 127 root_build_dir), 128 "--depfile", 129 rebase_path(depfile, root_build_dir), 130 ] 131 if (defined(invoker.markdown)) { 132 args += [ 133 "-i", 134 rebase_path(invoker.markdown, root_build_dir), 135 ] 136 } 137 if (defined(invoker.html_template)) { 138 args += [ 139 "-t", 140 rebase_path(invoker.html_template, root_build_dir), 141 ] 142 } 143 } 144} 145 146md_to_html("gen_toc") { 147 markdown = "${src_doc_dir}/toc.md" 148 out_html = "docs/_nav.html" 149} 150 151md_to_html("gen_index") { 152 html_template = "src/template_index.html" 153 deps = [ ":gen_toc" ] 154 out_html = "index.html" 155} 156 157nodejs_script("style_scss") { 158 script = "node_modules/sass/sass.js" 159 input = "src/assets/style.scss" 160 inputs = [ input ] 161 output = "${perfetto_website_out_dir}/assets/style.css" 162 outputs = [ output ] 163 args = [ 164 "--quiet", 165 rebase_path(input, root_build_dir), 166 rebase_path(output, root_build_dir), 167 ] 168 deps = [ ":node_modules" ] 169} 170 171sql_stats_md = "${target_gen_dir}/sql-stats.md" 172 173nodejs_script("gen_sql_stats_md") { 174 script = "src/gen_stats_reference.js" 175 input = "../../src/trace_processor/storage/stats.h" 176 inputs = [ input ] 177 outputs = [ sql_stats_md ] 178 args = [ 179 "-i", 180 rebase_path(input, root_build_dir), 181 "-o", 182 rebase_path(sql_stats_md, root_build_dir), 183 ] 184} 185 186md_to_html("gen_sql_stats_html") { 187 markdown = sql_stats_md 188 html_template = "src/template_markdown.html" 189 deps = [ 190 ":gen_sql_stats_md", 191 ":gen_toc", 192 ] 193 out_html = "docs/analysis/sql-stats" 194} 195 196ui_plugin_api_md = "${target_gen_dir}/ui-plugin-api.md" 197 198nodejs_script("gen_ui_plugin_api_md") { 199 script = "src/gen_ui_reference.js" 200 input = "../../ui/src/public/index.ts" 201 inputs = [ input ] 202 outputs = [ ui_plugin_api_md ] 203 args = [ 204 "-i", 205 rebase_path(input, root_build_dir), 206 "-o", 207 rebase_path(ui_plugin_api_md, root_build_dir), 208 ] 209} 210 211md_to_html("gen_ui_plugin_api_html") { 212 markdown = ui_plugin_api_md 213 html_template = "src/template_markdown.html" 214 deps = [ 215 ":gen_toc", 216 ":gen_ui_plugin_api_md", 217 ] 218 out_html = "docs/reference/ui-plugin-api" 219} 220 221# Generates a html reference for a proto 222# Args: 223# * proto: The path to a .proto file 224# * message_name: The proto message name 225# * out_html 226template("proto_reference") { 227 sql_stats_md = "${target_gen_dir}/${target_name}.md" 228 nodejs_script("${target_name}_md") { 229 script = "src/gen_proto_reference.js" 230 inputs = [ invoker.proto ] 231 outputs = [ sql_stats_md ] 232 args = [ 233 "-i", 234 rebase_path(invoker.proto, root_build_dir), 235 "-p", 236 invoker.message_name, 237 "-o", 238 rebase_path(sql_stats_md, root_build_dir), 239 ] 240 } 241 242 md_to_html(target_name) { 243 markdown = sql_stats_md 244 html_template = "src/template_markdown.html" 245 deps = [ 246 ":${target_name}_md", 247 ":gen_toc", 248 ] 249 out_html = invoker.out_html 250 } 251} 252 253proto_reference("gen_trace_config_proto") { 254 proto = "../../protos/perfetto/config/trace_config.proto" 255 message_name = "perfetto.protos.TraceConfig" 256 out_html = "docs/reference/trace-config-proto" 257} 258 259proto_reference("gen_trace_packet_proto") { 260 proto = "../../protos/perfetto/trace/trace_packet.proto" 261 message_name = "perfetto.protos.TracePacket" 262 out_html = "docs/reference/trace-packet-proto" 263} 264 265# WARNING: this does globbing at generation time. Incremental builds are not 266# going to work properly if files are added/removed. `gn gen` needs to be 267# rerun. 268sql_tables = 269 exec_script("../../gn/standalone/glob.py", 270 [ 271 "--root=" + rebase_path("../../src/trace_processor/tables", 272 root_build_dir), 273 "--filter=*.h", 274 ], 275 "list lines") 276 277src_sql_tables = [] 278 279foreach(i, sql_tables) { 280 src_sql_tables += [ rebase_path(i, ".", root_build_dir) ] 281} 282 283sql_tables_md = "${target_gen_dir}/sql-tables.md" 284stdlib_docs_md = "${target_gen_dir}/stdlib_docs.md" 285 286action("gen_stdlib_docs_md") { 287 script = "src/gen_stdlib_docs_md.py" 288 label_info = get_label_info( 289 "../../src/trace_processor/perfetto_sql/stdlib:stdlib_json_docs", 290 "target_gen_dir") 291 absolute_input_path = label_info + "/stdlib_docs.json" 292 deps = [ 293 "../../python:trace_processor_stdlib_docs", 294 "../../src/trace_processor/perfetto_sql/stdlib:stdlib_json_docs", 295 ] 296 outputs = [ stdlib_docs_md ] 297 args = [ 298 "--input", 299 rebase_path(absolute_input_path, root_build_dir), 300 "--output", 301 rebase_path(stdlib_docs_md, root_build_dir), 302 ] 303} 304 305md_to_html("gen_stdlib_docs_html") { 306 markdown = stdlib_docs_md 307 html_template = "src/template_markdown.html" 308 deps = [ 309 ":gen_stdlib_docs_md", 310 ":gen_toc", 311 ] 312 out_html = "docs/analysis/stdlib-docs" 313} 314 315nodejs_script("gen_sql_tables_md") { 316 python_label = "../../src/trace_processor/tables:tables_python_docs" 317 python_docs_json = get_label_info(python_label, "target_gen_dir") + "/" + 318 get_label_info(python_label, "name") + ".json" 319 320 script = "src/gen_sql_tables_reference.js" 321 inputs = src_sql_tables 322 deps = [ python_label ] 323 outputs = [ sql_tables_md ] 324 args = [ 325 "-o", 326 rebase_path(sql_tables_md, root_build_dir), 327 ] 328 foreach(file, src_sql_tables) { 329 args += [ 330 "-i", 331 rebase_path(file, root_build_dir), 332 ] 333 } 334 args += [ 335 "-j", 336 rebase_path(python_docs_json, root_build_dir), 337 ] 338} 339 340md_to_html("gen_sql_tables_html") { 341 markdown = sql_tables_md 342 html_template = "src/template_markdown.html" 343 deps = [ 344 ":gen_sql_tables_md", 345 ":gen_toc", 346 ] 347 out_html = "docs/analysis/sql-tables" 348} 349 350md_to_html("readme") { 351 markdown = "${src_doc_dir}/README.md" 352 html_template = "src/template_markdown.html" 353 out_html = "docs/index.html" 354 deps = [ ":gen_toc" ] 355} 356 357# WARNING: this does globbing at generation time. Incremental builds are not 358# going to work properly if files are added/removed. `gn gen` needs to be 359# rerun. 360mdfiles = exec_script("../../gn/standalone/glob.py", 361 [ 362 "--root=" + rebase_path(src_doc_dir, root_build_dir), 363 "--filter=*.md", 364 ], 365 "list lines") 366 367mdfiles -= [ 368 rebase_path("../../docs/README.md", root_build_dir), 369 rebase_path("../../docs/toc.md", root_build_dir), 370] 371 372mdtargets = [] 373 374foreach(source, mdfiles) { 375 filename = rebase_path(string_replace(source, ".md", ""), 376 rebase_path("../../docs", root_build_dir)) 377 378 md_to_html("mdfile_${source}") { 379 markdown = rebase_path(source, ".", root_build_dir) 380 html_template = "src/template_markdown.html" 381 out_html = "docs/${filename}" 382 deps = [ ":gen_toc" ] 383 } 384 mdtargets += [ ":mdfile_${source}" ] 385} 386 387group("all_mdfiles") { 388 deps = mdtargets 389} 390 391copy("node_assets") { 392 sources = [ 393 "node_modules/highlight.js/styles/tomorrow-night.css", 394 "node_modules/mermaid/dist/mermaid.min.js", 395 ] 396 deps = [ ":node_modules" ] 397 398 outputs = [ "${perfetto_website_out_dir}/assets/{{source_file_part}}" ] 399} 400 401# WARNING: this does globbing at generation time. Incremental builds are not 402# going to work properly if files are added/removed. `gn gen` needs to be 403# rerun. 404assets = exec_script("../../gn/standalone/glob.py", 405 [ 406 "--root=" + rebase_path("src/assets", root_build_dir), 407 "--filter=*.png", 408 "--filter=*.js", 409 ], 410 "list lines") 411 412src_assets = [] 413 414foreach(i, assets) { 415 src_assets += [ rebase_path(i, ".", root_build_dir) ] 416} 417 418copy("assets") { 419 sources = src_assets 420 outputs = [ "${perfetto_website_out_dir}/assets/{{source_file_part}}" ] 421} 422