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 ":node_assets", 41 ":readme", 42 ":style_scss", 43 ] 44} 45 46# Runs a nodejs script using the hermetic node toolchain. 47# Args: 48# * script: The .js script to execute 49# * inputs 50# * outputs 51# * deps 52# * depfile 53template("nodejs_script") { 54 assert(defined(invoker.script), "Need script in $target_name") 55 56 action(target_name) { 57 forward_variables_from(invoker, 58 [ 59 "outputs", 60 "depfile", 61 ]) 62 deps = [ ":node_modules" ] 63 if (defined(invoker.deps)) { 64 deps += invoker.deps 65 } 66 script = "../../gn/standalone/build_tool_wrapper.py" 67 inputs = [ invoker.script ] 68 inputs += invoker.inputs 69 args = [ 70 nodejs_bin, 71 rebase_path(invoker.script, root_build_dir), 72 ] 73 args += invoker.args 74 } 75} 76 77# Installs the node modules specified in package.json 78action("node_modules") { 79 script = "../../gn/standalone/build_tool_wrapper.py" 80 stamp_file = "$target_out_dir/.$target_name.stamp" 81 cur_dir = rebase_path(".", root_build_dir) 82 args = [ 83 "--stamp", 84 rebase_path(stamp_file, root_build_dir), 85 "--chdir=$cur_dir", 86 rebase_path("../../tools/pnpm", root_build_dir), 87 "install", 88 "--shamefully-hoist", 89 "--frozen-lockfile", 90 ] 91 inputs = [ 92 "../../tools/npm", 93 "package.json", 94 "pnpm-lock.yaml", 95 ] 96 outputs = [ stamp_file ] 97} 98 99# Renders a markdown file into html. 100# Args: 101# * markdown: Optional. The source markdown file 102# * html_template: Optional. The html template to use 103# * out_html: The generated html, relative to `perfetto_website_out_dir` 104# * deps 105template("md_to_html") { 106 assert(defined(invoker.out_html), "Need out_html in $target_name") 107 assert(defined(invoker.html_template) || defined(invoker.markdown), 108 "Need html_template or markdown in $target_name") 109 nodejs_script(target_name) { 110 forward_variables_from(invoker, [ "deps" ]) 111 script = "src/markdown_render.js" 112 inputs = [] 113 if (defined(invoker.markdown)) { 114 inputs += [ invoker.markdown ] 115 } 116 depfile = "${target_gen_dir}/$target_name.d" 117 if (defined(invoker.html_template)) { 118 inputs += [ invoker.html_template ] 119 } 120 outputs = [ "${perfetto_website_out_dir}/${invoker.out_html}" ] 121 args = [ 122 "--odir", 123 rebase_path(perfetto_website_out_dir, root_build_dir), 124 "-o", 125 rebase_path("${perfetto_website_out_dir}/${invoker.out_html}", 126 root_build_dir), 127 "--depfile", 128 rebase_path(depfile, root_build_dir), 129 ] 130 if (defined(invoker.markdown)) { 131 args += [ 132 "-i", 133 rebase_path(invoker.markdown, root_build_dir), 134 ] 135 } 136 if (defined(invoker.html_template)) { 137 args += [ 138 "-t", 139 rebase_path(invoker.html_template, root_build_dir), 140 ] 141 } 142 } 143} 144 145md_to_html("gen_toc") { 146 markdown = "${src_doc_dir}/toc.md" 147 out_html = "docs/_nav.html" 148} 149 150md_to_html("gen_index") { 151 html_template = "src/template_index.html" 152 deps = [ ":gen_toc" ] 153 out_html = "index.html" 154} 155 156nodejs_script("style_scss") { 157 script = "node_modules/sass/sass.js" 158 input = "src/assets/style.scss" 159 inputs = [ input ] 160 output = "${perfetto_website_out_dir}/assets/style.css" 161 outputs = [ output ] 162 args = [ 163 "--quiet", 164 rebase_path(input, root_build_dir), 165 rebase_path(output, root_build_dir), 166 ] 167 deps = [ ":node_modules" ] 168} 169 170sql_stats_md = "${target_gen_dir}/sql-stats.md" 171 172nodejs_script("gen_sql_stats_md") { 173 script = "src/gen_stats_reference.js" 174 input = "../../src/trace_processor/storage/stats.h" 175 inputs = [ input ] 176 outputs = [ sql_stats_md ] 177 args = [ 178 "-i", 179 rebase_path(input, root_build_dir), 180 "-o", 181 rebase_path(sql_stats_md, root_build_dir), 182 ] 183} 184 185md_to_html("gen_sql_stats_html") { 186 markdown = sql_stats_md 187 html_template = "src/template_markdown.html" 188 deps = [ 189 ":gen_sql_stats_md", 190 ":gen_toc", 191 ] 192 out_html = "docs/analysis/sql-stats" 193} 194 195# Generates a html reference for a proto 196# Args: 197# * proto: The path to a .proto file 198# * message_name: The proto message name 199# * out_html 200template("proto_reference") { 201 sql_stats_md = "${target_gen_dir}/${target_name}.md" 202 nodejs_script("${target_name}_md") { 203 script = "src/gen_proto_reference.js" 204 inputs = [ invoker.proto ] 205 outputs = [ sql_stats_md ] 206 args = [ 207 "-i", 208 rebase_path(invoker.proto, root_build_dir), 209 "-p", 210 invoker.message_name, 211 "-o", 212 rebase_path(sql_stats_md, root_build_dir), 213 ] 214 } 215 216 md_to_html(target_name) { 217 markdown = sql_stats_md 218 html_template = "src/template_markdown.html" 219 deps = [ 220 ":${target_name}_md", 221 ":gen_toc", 222 ] 223 out_html = invoker.out_html 224 } 225} 226 227proto_reference("gen_trace_config_proto") { 228 proto = "../../protos/perfetto/config/trace_config.proto" 229 message_name = "perfetto.protos.TraceConfig" 230 out_html = "docs/reference/trace-config-proto" 231} 232 233proto_reference("gen_trace_packet_proto") { 234 proto = "../../protos/perfetto/trace/trace_packet.proto" 235 message_name = "perfetto.protos.TracePacket" 236 out_html = "docs/reference/trace-packet-proto" 237} 238 239# WARNING: this does globbing at generation time. Incremental builds are not 240# going to work properly if files are added/removed. `gn gen` needs to be 241# rerun. 242sql_tables = 243 exec_script("../../gn/standalone/glob.py", 244 [ 245 "--root=" + rebase_path("../../src/trace_processor/tables", 246 root_build_dir), 247 "--filter=*.h", 248 ], 249 "list lines") 250 251src_sql_tables = [] 252 253foreach(i, sql_tables) { 254 src_sql_tables += [ rebase_path(i, ".", root_build_dir) ] 255} 256 257sql_tables_md = "${target_gen_dir}/sql-tables.md" 258stdlib_docs_md = "${target_gen_dir}/stdlib_docs.md" 259 260action("gen_stdlib_docs_md") { 261 script = "src/gen_stdlib_docs_md.py" 262 label_info = get_label_info( 263 "../../src/trace_processor/perfetto_sql/stdlib:stdlib_json_docs", 264 "target_gen_dir") 265 absolute_input_path = label_info + "/stdlib_docs.json" 266 deps = [ 267 "../../python:trace_processor_stdlib_docs", 268 "../../src/trace_processor/perfetto_sql/stdlib:stdlib_json_docs", 269 ] 270 outputs = [ stdlib_docs_md ] 271 args = [ 272 "--input", 273 rebase_path(absolute_input_path, root_build_dir), 274 "--output", 275 rebase_path(stdlib_docs_md, root_build_dir), 276 ] 277} 278 279md_to_html("gen_stdlib_docs_html") { 280 markdown = stdlib_docs_md 281 html_template = "src/template_markdown.html" 282 deps = [ 283 ":gen_stdlib_docs_md", 284 ":gen_toc", 285 ] 286 out_html = "docs/analysis/stdlib-docs" 287} 288 289nodejs_script("gen_sql_tables_md") { 290 python_label = "../../src/trace_processor/tables:tables_python_docs" 291 python_docs_json = get_label_info(python_label, "target_gen_dir") + "/" + 292 get_label_info(python_label, "name") + ".json" 293 294 script = "src/gen_sql_tables_reference.js" 295 inputs = src_sql_tables 296 deps = [ python_label ] 297 outputs = [ sql_tables_md ] 298 args = [ 299 "-o", 300 rebase_path(sql_tables_md, root_build_dir), 301 ] 302 foreach(file, src_sql_tables) { 303 args += [ 304 "-i", 305 rebase_path(file, root_build_dir), 306 ] 307 } 308 args += [ 309 "-j", 310 rebase_path(python_docs_json, root_build_dir), 311 ] 312} 313 314md_to_html("gen_sql_tables_html") { 315 markdown = sql_tables_md 316 html_template = "src/template_markdown.html" 317 deps = [ 318 ":gen_sql_tables_md", 319 ":gen_toc", 320 ] 321 out_html = "docs/analysis/sql-tables" 322} 323 324md_to_html("readme") { 325 markdown = "${src_doc_dir}/README.md" 326 html_template = "src/template_markdown.html" 327 out_html = "docs/index.html" 328 deps = [ ":gen_toc" ] 329} 330 331# WARNING: this does globbing at generation time. Incremental builds are not 332# going to work properly if files are added/removed. `gn gen` needs to be 333# rerun. 334mdfiles = exec_script("../../gn/standalone/glob.py", 335 [ 336 "--root=" + rebase_path(src_doc_dir, root_build_dir), 337 "--filter=*.md", 338 ], 339 "list lines") 340 341mdfiles -= [ 342 rebase_path("../../docs/README.md", root_build_dir), 343 rebase_path("../../docs/toc.md", root_build_dir), 344] 345 346mdtargets = [] 347 348foreach(source, mdfiles) { 349 filename = rebase_path(string_replace(source, ".md", ""), 350 rebase_path("../../docs", root_build_dir)) 351 352 md_to_html("mdfile_${source}") { 353 markdown = rebase_path(source, ".", root_build_dir) 354 html_template = "src/template_markdown.html" 355 out_html = "docs/${filename}" 356 deps = [ ":gen_toc" ] 357 } 358 mdtargets += [ ":mdfile_${source}" ] 359} 360 361# Files which have been removed/renamed/moved and now have HTTP redirections in 362# src/assets/script.js 363removed_renamed_moved_files = [ "analysis/common-queries.md" ] 364 365foreach(source, removed_renamed_moved_files) { 366 filename = rebase_path(string_replace(source, ".md", ""), 367 rebase_path("../../docs", root_build_dir)) 368 md_to_html("mdfile_${source}") { 369 markdown = "src/empty.md" 370 html_template = "src/template_markdown.html" 371 out_html = "docs/${filename}" 372 deps = [ ":gen_toc" ] 373 } 374 mdtargets += [ ":mdfile_${source}" ] 375} 376 377group("all_mdfiles") { 378 deps = mdtargets 379} 380 381copy("node_assets") { 382 sources = [ 383 "node_modules/highlight.js/styles/tomorrow-night.css", 384 "node_modules/mermaid/dist/mermaid.min.js", 385 ] 386 deps = [ ":node_modules" ] 387 388 outputs = [ "${perfetto_website_out_dir}/assets/{{source_file_part}}" ] 389} 390 391# WARNING: this does globbing at generation time. Incremental builds are not 392# going to work properly if files are added/removed. `gn gen` needs to be 393# rerun. 394assets = exec_script("../../gn/standalone/glob.py", 395 [ 396 "--root=" + rebase_path("src/assets", root_build_dir), 397 "--filter=*.png", 398 "--filter=*.js", 399 ], 400 "list lines") 401 402src_assets = [] 403 404foreach(i, assets) { 405 src_assets += [ rebase_path(i, ".", root_build_dir) ] 406} 407 408copy("assets") { 409 sources = src_assets 410 outputs = [ "${perfetto_website_out_dir}/assets/{{source_file_part}}" ] 411} 412