# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/features.gni")
import(
"//tools/code_coverage/js_source_maps/create_js_source_maps/create_js_source_maps.gni")
import(
"//tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.gni")
import("//tools/grit/grit_rule.gni")
import("//tools/grit/preprocess_if_expr.gni")
import("//tools/polymer/css_to_wrapper.gni")
import("//tools/polymer/html_to_wrapper.gni")
import("//tools/typescript/ts_library.gni")
import("//tools/typescript/webui_path_mappings.gni")
import("//ui/webui/resources/tools/bundle_js.gni")
import("//ui/webui/resources/tools/bundle_js_excludes.gni")
import("//ui/webui/resources/tools/generate_grd.gni")
import("//ui/webui/resources/tools/minify_js.gni")
import("//ui/webui/webui_features.gni")
if (use_blink) {
import("//chrome/common/features.gni")
}
# See documentation at https://chromium.googlesource.com/chromium/src/+/HEAD/docs/webui_build_configuration.md#build_webui
template("build_webui") {
not_needed([ "target_name" ])
forward_variables_from(invoker, [ "grd_prefix" ])
preprocess_dir = "${target_gen_dir}/preprocessed"
ts_out_dir = "${target_gen_dir}/tsc"
if (defined(invoker.ts_out_dir)) {
ts_out_dir = invoker.ts_out_dir
}
optimize = optimize_webui
if (defined(invoker.optimize)) {
optimize = invoker.optimize
}
minify = optimize
bundle = optimize && defined(invoker.optimize_webui_in_files)
has_ts_in_files = defined(invoker.web_component_files) ||
defined(invoker.non_web_component_files)
enable_source_maps = enable_webui_inline_sourcemaps && has_ts_in_files
if (defined(invoker.enable_source_maps) && has_ts_in_files) {
assert(!invoker.enable_source_maps || !optimize)
enable_source_maps = invoker.enable_source_maps
}
# Determine whether a :build_grd or :build_grdp target should be defined.
generate_grdp = false
generate_grd_target_name = "build_grd"
if (defined(invoker.generate_grdp) && invoker.generate_grdp) {
generate_grdp = true
generate_grd_target_name = "build_grdp"
}
### Compute the lists of files that are used across multiple targets.
# At least one of `web_component_files`, `css_files`, `mojo_files`, or
# 'non_web_component_files` must be defined. Otherwise, the build rule
# does not actually generate any input for TS compiler.
assert(has_ts_in_files || defined(invoker.css_files) ||
defined(invoker.mojo_files))
ts_files = []
if (defined(invoker.web_component_files)) {
ts_files += invoker.web_component_files
}
if (defined(invoker.non_web_component_files)) {
ts_files += invoker.non_web_component_files
}
if (defined(invoker.web_component_files) ||
defined(invoker.icons_html_files)) {
# Files that are passed as input to html_to_wrapper().
html_files = []
if (defined(invoker.web_component_files)) {
web_component_ts_files =
filter_include(invoker.web_component_files, [ "*.ts" ])
foreach(f, web_component_ts_files) {
html_files += [ string_replace(f, ".ts", ".html") ]
}
web_component_js_files =
filter_include(invoker.web_component_files, [ "*.js" ])
foreach(f, web_component_js_files) {
html_files += [ string_replace(f, ".js", ".html") ]
}
}
if (defined(invoker.icons_html_files)) {
html_files += invoker.icons_html_files
}
# Files that are generated by html_to_wrapper().
html_wrapper_files = []
foreach(f, html_files) {
html_wrapper_files += [ f + ".ts" ]
}
}
if (defined(invoker.css_files)) {
# Files that are generated by css_to_wrapper().
css_wrapper_files = []
foreach(f, invoker.css_files) {
css_wrapper_files += [ f + ".ts" ]
}
}
# Generated Mojo JS files.
if (defined(invoker.mojo_files)) {
assert(defined(invoker.mojo_files_deps))
mojo_files = []
foreach(mojo_file, invoker.mojo_files) {
mojo_files += [ get_path_info(invoker.mojo_files, "file") ]
}
}
if (defined(invoker.static_files)) {
# Compute which static_files should be preprocessed.
non_preprocessed_files_filter = [
"*.jpg",
"*.png",
"*.svg",
]
static_non_preprocessed_files =
filter_include(invoker.static_files, non_preprocessed_files_filter)
static_preprocessed_files =
filter_exclude(invoker.static_files, non_preprocessed_files_filter)
}
preprocessor_defines = []
if (use_blink) {
preprocessor_defines += chrome_grit_defines
}
if (defined(invoker.preprocessor_defines)) {
preprocessor_defines += invoker.preprocessor_defines
}
### Define the various targets that are required by the build pipeline.
# Specifically the order in which these targets are executed is:
#
# 1) preprocess_if_expr()
# 2) create_js_source_maps() (only if |invoker.enable_source_maps| flag is
# true)
# 3) html_to_wrapper(), css_to_wrapper()
# 4) ts_library()
# 5) merge_js_source_maps() (only if the |invoker.enable_source_maps| flag is
# true)
# 6) optimize_webui() (only if invoker.optimize && defined(invoker.optimize_webui_in_files))
# 7) minify_js() (only if invoker.optimize && !defined(invoker.optimize_webui_in_files))
# 8) generate_grd()
# 9) grit()
if (defined(invoker.static_files)) {
preprocess_if_expr("preprocess_static_files") {
visibility = [ ":$generate_grd_target_name" ]
defines = preprocessor_defines
in_folder = "."
out_folder = preprocess_dir
in_files = static_preprocessed_files
out_manifest = "${target_gen_dir}/preprocess_static_files_manifest.json"
}
}
if (has_ts_in_files) {
preprocess_if_expr("preprocess_ts_files") {
visibility = [
":build_ts",
":html_wrapper_files",
]
if (enable_source_maps) {
visibility += [ ":create_source_maps" ]
}
defines = preprocessor_defines
in_folder = "."
out_folder = preprocess_dir
if (enable_source_maps) {
out_folder = "${preprocess_dir}/_tmp"
enable_removal_comments = enable_source_maps
}
in_files = ts_files
}
}
if (enable_source_maps) {
create_js_source_maps("create_source_maps") {
# TODO(dpapad): Make this only visible to ":build_ts" and
# ":html_wrapper_files".
inline_sourcemaps = true
sources = filter_include(
get_target_outputs(":preprocess_ts_files"),
[
"*.ts",
# TODO(crbug.com/40167175): Remove once we no longer pass JS
# files to build_webui.
"*.js",
])
originals = []
outputs = []
foreach(in_file, sources) {
rebased_path = rebase_path(in_file, "${preprocess_dir}/_tmp")
originals += [ rebased_path ]
outputs += [ "${preprocess_dir}/" + rebased_path ]
}
deps = [ ":preprocess_ts_files" ]
}
}
if (defined(html_files) || defined(invoker.css_files)) {
preprocess_if_expr("preprocess_html_css_files") {
visibility = [
":css_wrapper_files",
":html_wrapper_files",
]
defines = preprocessor_defines
in_folder = "."
out_folder = preprocess_dir
in_files = []
if (defined(html_files)) {
in_files += html_files
}
if (defined(invoker.css_files)) {
in_files += invoker.css_files
}
}
}
if (defined(html_files)) {
html_to_wrapper("html_wrapper_files") {
visibility = [ ":build_ts" ]
deps = [ ":preprocess_html_css_files" ]
in_folder = preprocess_dir
out_folder = preprocess_dir
in_files = html_files
minify = optimize
if (defined(invoker.html_to_wrapper_scheme)) {
scheme = invoker.html_to_wrapper_scheme
}
if (defined(invoker.html_to_wrapper_template)) {
template = invoker.html_to_wrapper_template
if (invoker.html_to_wrapper_template == "detect") {
if (enable_source_maps) {
deps += [ ":create_source_maps" ]
} else {
deps += [ ":preprocess_ts_files" ]
}
}
}
}
}
if (defined(invoker.css_files)) {
css_to_wrapper("css_wrapper_files") {
visibility = [ ":build_ts" ]
deps = [ ":preprocess_html_css_files" ]
in_folder = preprocess_dir
out_folder = preprocess_dir
in_files = invoker.css_files
minify = optimize
}
}
if (defined(invoker.mojo_files_deps)) {
mojo_base_path = "."
if (defined(invoker.mojo_base_path)) {
mojo_base_path = invoker.mojo_base_path
}
copy("copy_mojo") {
visibility = [ ":build_ts" ]
deps = invoker.mojo_files_deps
sources = invoker.mojo_files
outputs = [ "${preprocess_dir}/${mojo_base_path}/{{source_file_part}}" ]
}
}
if (defined(invoker.ts_deps)) {
webui_path_mappings("build_path_map") {
ts_deps = invoker.ts_deps
visibility = [ ":build_ts" ]
webui_context_type = "relative"
if (defined(invoker.webui_context_type)) {
webui_context_type = invoker.webui_context_type
}
}
}
ts_library("build_ts") {
root_dir = preprocess_dir
out_dir = ts_out_dir
composite = false
if (defined(invoker.ts_composite)) {
composite = invoker.ts_composite
}
if (!composite) {
visibility = [
":$generate_grd_target_name",
":build_bundle",
":build_min_js",
]
if (enable_source_maps) {
visibility += [ ":merge_source_maps" ]
}
}
if (defined(invoker.ts_tsconfig_base)) {
tsconfig_base = invoker.ts_tsconfig_base
} else if (defined(invoker.ts_deps)) {
# Default tsconfig is set to tsconfig_base_polymer.json for targets
# with a Polymer dependency, set to tsconfig_base_lit.json for
# targets with a Lit dependency and no Polymer dependency, and left
# unset (defaults to //tools/typescript/tsconfig_base.json) for other
# non-Polymer, non-Lit targets.
has_polymer =
filter_include(invoker.ts_deps,
[ "//third_party/polymer/v3_0:library" ]) != []
if (has_polymer) {
tsconfig_base = "//tools/typescript/tsconfig_base_polymer.json"
} else {
has_lit = filter_include(invoker.ts_deps,
[ "//third_party/lit/v3_0:build_ts" ]) != []
if (has_lit) {
tsconfig_base = "//tools/typescript/tsconfig_base_lit.json"
}
}
}
in_files = ts_files
if (defined(invoker.ts_deps)) {
path_mappings_file = "$target_gen_dir/path_mappings_build_path_map.json"
extra_deps = [ ":build_path_map" ]
} else {
extra_deps = []
}
if (enable_source_maps) {
# ts_library()'s |enable_source_maps| param inherited from the outer
# scope's enable_source_maps
extra_deps += [ ":create_source_maps" ]
} else if (has_ts_in_files) {
extra_deps += [ ":preprocess_ts_files" ]
}
if (defined(html_files)) {
in_files += html_wrapper_files
extra_deps += [ ":html_wrapper_files" ]
}
if (defined(invoker.css_files)) {
in_files += css_wrapper_files
extra_deps += [ ":css_wrapper_files" ]
}
if (defined(invoker.ts_deps)) {
deps = invoker.ts_deps
}
if (defined(invoker.ts_definitions)) {
definitions = invoker.ts_definitions
}
if (defined(invoker.ts_path_mappings)) {
path_mappings = invoker.ts_path_mappings
}
if (defined(invoker.mojo_files_deps)) {
assert(defined(invoker.mojo_files))
target_outputs = get_target_outputs(":copy_mojo")
# Add all Mojo JS files produced by `:copy_mojo` as inputs to the TS
# compiler.
foreach(o, target_outputs) {
in_files += [ rebase_path(o, preprocess_dir) ]
}
extra_deps += [ ":copy_mojo" ]
}
if (defined(invoker.ts_extra_deps)) {
extra_deps += invoker.ts_extra_deps
}
}
if (enable_source_maps) {
merge_js_source_maps("merge_source_maps") {
deps = [ ":build_ts" ]
manifest_files =
filter_include(get_target_outputs(":build_ts"), [ "*_manifest.json" ])
js_files = filter_include(get_target_outputs(":build_ts"), [ "*.js" ])
sources = []
outputs = []
out_dir = "$target_gen_dir/merge_source_maps"
foreach(_js_file, js_files) {
sources += [ _js_file ]
outputs += [ string_replace(_js_file, ts_out_dir, out_dir) ]
}
}
}
if (bundle) {
bundle_js("build_bundle") {
visibility = [ ":build_min_js" ]
host = invoker.optimize_webui_host
input = rebase_path(ts_out_dir, root_build_dir)
js_module_in_files = invoker.optimize_webui_in_files
out_folder = "$target_gen_dir/bundled"
excludes = BUNDLE_JS_EXCLUDES
if (defined(invoker.optimize_webui_excludes)) {
excludes += invoker.optimize_webui_excludes
}
if (defined(invoker.optimize_webui_external_paths)) {
external_paths = invoker.optimize_webui_external_paths
}
deps = [ ":build_ts" ]
}
}
if (minify) {
minify_js("build_min_js") {
visibility = [ ":$generate_grd_target_name" ]
in_folder = ts_out_dir
in_target = ":build_ts"
if (bundle) {
in_folder = "$target_gen_dir/bundled"
in_target = ":build_bundle"
}
out_folder = "$target_gen_dir/minified"
js_files = filter_include(get_target_outputs(in_target), [ "*.js" ])
in_files = []
foreach(_js_file, js_files) {
in_files += [ string_replace(_js_file, "$in_folder/", "") ]
}
deps = [ in_target ]
}
}
generate_grd(generate_grd_target_name) {
if (!generate_grdp) {
visibility = [ ":resources_grit" ]
}
grd_prefix = grd_prefix
out_grd = "$target_gen_dir/resources.grd"
if (generate_grdp) {
out_grd = "$target_gen_dir/resources.grdp"
}
deps = []
manifest_files = []
if (defined(invoker.static_files)) {
input_files = static_non_preprocessed_files
input_files_base_dir = rebase_path(".", "//")
deps += [ ":preprocess_static_files" ]
manifest_files +=
[ "${target_gen_dir}/preprocess_static_files_manifest.json" ]
}
if (optimize) {
deps += [ ":build_min_js" ]
manifest_files += filter_include(get_target_outputs(":build_min_js"),
[ "*_manifest.json" ])
if (bundle) {
resource_path_rewrites = []
foreach(f, invoker.optimize_webui_in_files) {
name = get_path_info(f, "name")
output_file = string_replace(f, "${name}.js", "${name}.rollup.js")
resource_path_rewrites += [ "${output_file}|${f}" ]
}
}
} else if (enable_source_maps) {
deps += [ ":merge_source_maps" ]
manifest_files += filter_include(get_target_outputs(":merge_source_maps"),
[ "*_manifest__processed.json" ])
} else {
deps += [ ":build_ts" ]
manifest_files +=
filter_include(get_target_outputs(":build_ts"), [ "*_manifest.json" ])
}
if (defined(invoker.extra_grdp_deps)) {
deps += invoker.extra_grdp_deps
grdp_files = invoker.extra_grdp_files
}
if (defined(invoker.grd_resource_path_prefix)) {
resource_path_prefix = invoker.grd_resource_path_prefix
}
}
if (!generate_grdp) {
grit("resources") {
# These arguments are needed since the grd is generated at build time.
enable_input_discovery_for_gn_analyze = false
source = "$target_gen_dir/resources.grd"
deps = [ ":$generate_grd_target_name" ]
outputs = [
"grit/${grd_prefix}_resources.h",
"grit/${grd_prefix}_resources_map.cc",
"grit/${grd_prefix}_resources_map.h",
"${grd_prefix}_resources.pak",
]
grit_output_dir = "$root_gen_dir/chrome"
if (defined(invoker.grit_output_dir)) {
grit_output_dir = invoker.grit_output_dir
}
output_dir = grit_output_dir
}
}
}