# Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Instantiate grit. This will produce a script target to run grit (named # ${target_name}_grit), and a static library that compiles the .cc files. # # In general, code should depend on the static library. However, if the # generated files are only processed by other actions to generate other # files, it is possible to depend on the script target directly. # # Parameters # # source (required) # Path to .grd file. # # enable_input_discovery_for_gn_analyze (default=true) # Runs grit_info.py via exec_script() when compute_inputs_for_analyze=true # in order to discover all files that affect this target. # Turn this off when the .grd file is generated, or an with # flattenhtml=true points to a generated file. # For "gn analyze" to be correct with this arg disabled, all inputs # must be listed via |inputs|. # # inputs (optional) # List of additional files, required for grit to process source file. # # outputs (required) # List of outputs from grit, relative to the target_gen_dir. Grit will # verify at build time that this list is correct and will fail if there # is a mismatch between the outputs specified by the .grd file and the # outputs list here. # # To get this list, you can look in the .grd file for # nodes of the processed files. # # output_dir (optional) # Directory for generated files. If you specify this, you will often # want to specify output_name if the target name is not particularly # unique, since this can cause files from multiple grit targets to # overwrite each other. # # output_name (optional) # Provide an alternate base name for the generated files, like the .d # files. Normally these are based on the target name and go in the # output_dir, but if multiple targets with the same name end up in # the same output_dir, they can collide. # # configs (optional) # List of additional configs to be applied to the generated target. # # deps (optional) # testonly (optional) # visibility (optional) # Normal meaning. # # # Example # # grit("my_resources") { # # Source and outputs are required. # source = "myfile.grd" # outputs = [ # "foo_strings.h", # "foo_strings.pak", # ] # # grit_flags = [ "-E", "foo=bar" ] # Optional extra flags. # # You can also put deps here if the grit source depends on generated # # files. # } import("//build/config/compiler/compiler.gni") import("//build/config/compute_inputs_for_analyze.gni") import("//build/config/sanitizers/sanitizers.gni") import("//build/toolchain/gcc_toolchain.gni") import("//tools/grit/grit_args.gni") _strip_resource_files = is_android && is_official_build _js_minifier = "//tools/grit/minify_with_uglify.py" _css_minifier = "//tools/grit/minimize_css.py" grit_resource_id_target = "//tools/gritsettings:default_resource_ids" grit_resource_id_file = get_label_info(grit_resource_id_target, "target_gen_dir") + "/default_resource_ids" grit_info_script = "//tools/grit/grit_info.py" # TODO(asvitkine): Add predetermined ids files for other platforms. grit_predetermined_resource_ids_file = "" if (is_mac) { grit_predetermined_resource_ids_file = "//tools/gritsettings/startup_resources_mac.txt" } if (is_win) { grit_predetermined_resource_ids_file = "//tools/gritsettings/startup_resources_win.txt" } template("grit") { if (defined(invoker.output_dir)) { _output_dir = invoker.output_dir } else { _output_dir = target_gen_dir } _grit_outputs = get_path_info(rebase_path(invoker.outputs, ".", _output_dir), "abspath") # Add .info output for all pak files _pak_info_outputs = [] foreach(output, _grit_outputs) { if (get_path_info(output, "extension") == "pak") { _pak_info_outputs += [ output + ".info" ] } } if (defined(invoker.output_name)) { _grit_output_name = invoker.output_name } else { _grit_output_name = target_name } _grit_custom_target = target_name + "_grit" action(_grit_custom_target) { script = "//tools/grit/grit.py" inputs = [ invoker.source ] testonly = defined(invoker.testonly) && invoker.testonly depfile = "$target_gen_dir/$target_name.d" deps = [ "//tools/grit:grit_sources" ] outputs = [ "${depfile}.stamp" ] + _grit_outputs + _pak_info_outputs _grit_flags = grit_args # Add extra defines with -D flags. if (defined(invoker.defines)) { foreach(i, invoker.defines) { _grit_flags += [ "-D", i, ] } } if (defined(invoker.grit_flags)) { _grit_flags += invoker.grit_flags } _rebased_source_path = rebase_path(invoker.source, root_build_dir) _enable_grit_info = !defined(invoker.enable_input_discovery_for_gn_analyze) || invoker.enable_input_discovery_for_gn_analyze if (_enable_grit_info && compute_inputs_for_analyze) { # Only call exec_script when the user has explicitly opted into greater # precision at the expense of performance. _rel_inputs = exec_script("//tools/grit/grit_info.py", [ "--inputs", _rebased_source_path, ] + _grit_flags, "list lines") inputs += rebase_path(_rel_inputs, ".", root_build_dir) } args = [ "-i", _rebased_source_path, "build", "-o", rebase_path(_output_dir, root_build_dir), "--depdir", ".", "--depfile", rebase_path(depfile, root_build_dir), "--write-only-new=1", "--depend-on-stamp", ] + _grit_flags # Add brotli executable if using brotli. if (defined(invoker.use_brotli) && invoker.use_brotli) { _brotli_target = "//third_party/brotli:brotli($host_toolchain)" _brotli_executable = get_label_info(_brotli_target, "root_out_dir") + "/" + get_label_info(_brotli_target, "name") if (host_os == "win") { _brotli_executable += ".exe" } inputs += [ _brotli_executable ] args += [ "--brotli", rebase_path(_brotli_executable, root_build_dir), ] } _resource_ids = grit_resource_id_file if (defined(invoker.resource_ids)) { _resource_ids = invoker.resource_ids } if (_resource_ids != "") { inputs += [ _resource_ids ] args += [ "-f", rebase_path(_resource_ids, root_build_dir), ] if (_resource_ids == grit_resource_id_file) { deps += [ grit_resource_id_target ] } } if (grit_predetermined_resource_ids_file != "") { inputs += [ grit_predetermined_resource_ids_file ] args += [ "-p", rebase_path(grit_predetermined_resource_ids_file, root_build_dir), ] } # We want to make sure the declared outputs actually match what Grit is # writing. We write the list to a file (some of the output lists are long # enough to not fit on a Windows command line) and ask Grit to verify those # are the actual outputs at runtime. _asserted_list_file = "$target_out_dir/${_grit_output_name}_expected_outputs.txt" write_file(_asserted_list_file, rebase_path(invoker.outputs, root_build_dir, _output_dir)) inputs += [ _asserted_list_file ] args += [ "--assert-file-list", rebase_path(_asserted_list_file, root_build_dir), ] if (enable_resource_allowlist_generation) { _rc_grit_outputs = [] foreach(output, _grit_outputs) { if (get_path_info(output, "extension") == "rc") { _rc_grit_outputs += [ output ] } } if (_rc_grit_outputs != []) { # Resource allowlisting cannot be used with .rc files. # Make sure that there aren't any .pak outputs which would require # allowlist annotations. assert(_pak_info_outputs == [], "can't combine .pak and .rc outputs") } else { args += [ "--allowlist-support" ] } } if (_strip_resource_files) { _js_minifier_command = rebase_path(_js_minifier, root_build_dir) _css_minifier_command = rebase_path(_css_minifier, root_build_dir) args += [ "--js-minifier", _js_minifier_command, "--css-minifier", _css_minifier_command, ] inputs += [ _js_minifier, _css_minifier, ] } if (defined(invoker.visibility)) { # This needs to include both what the invoker specified (since they # probably include generated headers from this target), as well as the # generated source set (since there's no guarantee that the visibility # specified by the invoker includes our target). # # Only define visibility at all if the invoker specified it. Otherwise, # we want to keep the public "no visibility specified" default. visibility = [ ":${invoker.target_name}" ] + invoker.visibility } if (defined(invoker.use_brotli) && invoker.use_brotli) { if (is_mac && is_asan) { deps += [ "//tools/grit:brotli_mac_asan_workaround" ] } else { deps += [ "//third_party/brotli:brotli($host_toolchain)" ] } } if (defined(invoker.deps)) { deps += invoker.deps } if (defined(invoker.inputs)) { inputs += invoker.inputs } } # This is the thing that people actually link with, it must be named the # same as the argument the template was invoked with. source_set(target_name) { testonly = defined(invoker.testonly) && invoker.testonly # Since we generate a file, we need to be run before the targets that # depend on us. sources = [] foreach(_output, _grit_outputs) { _extension = get_path_info(_output, "extension") if (_extension != "json" && _extension != "gz" && _extension != "pak" && _extension != "xml") { sources += [ _output ] } } # Deps set on the template invocation will go on the action that runs # grit above rather than this library. This target needs to depend on the # action publicly so other scripts can take the outputs from the grit # script as inputs. public_deps = [ ":$_grit_custom_target" ] deps = [ "//base" ] if (defined(invoker.public_configs)) { public_configs += invoker.public_configs } if (defined(invoker.configs)) { configs += invoker.configs } if (defined(invoker.visibility)) { visibility = invoker.visibility } output_name = _grit_output_name } } if (is_android) { import("//build/config/android/rules.gni") # Declare a target that generates localized strings.xml from a .grd file. # # If this target is included in the deps of an android resources/library/apk, # the strings.xml will be included with that target. # # Variables # deps: Specifies the dependencies of this target. # grd_file: Path to the .grd file to generate strings.xml from. # outputs: Expected grit outputs (see grit rule). # # Example # java_strings_grd("foo_strings_grd") { # grd_file = "foo_strings.grd" # } template("java_strings_grd") { forward_variables_from(invoker, [ "testonly" ]) _resources_zip = "$target_out_dir/$target_name.resources.zip" _grit_target_name = "${target_name}__grit" _grit_output_dir = "$target_gen_dir/${target_name}_grit_output" grit(_grit_target_name) { forward_variables_from(invoker, [ "deps", "defines", ]) grit_flags = [ "-E", "ANDROID_JAVA_TAGGED_ONLY=false", ] output_dir = _grit_output_dir resource_ids = "" source = invoker.grd_file outputs = invoker.outputs } _zip_target_name = "${target_name}__zip" zip(_zip_target_name) { base_dir = _grit_output_dir # No need to depend on the source_set(). _grit_dep = ":${_grit_target_name}_grit" deps = [ _grit_dep ] inputs = filter_exclude(get_target_outputs(_grit_dep), [ "*.stamp" ]) output = _resources_zip } android_generated_resources(target_name) { forward_variables_from(invoker, [ "resource_overlay", "visibility", ]) generating_target = ":$_zip_target_name" generated_resources_zip = _resources_zip } } # Declare a target that packages strings.xml generated from a grd file. # # If this target is included in the deps of an android resources/library/apk, # the strings.xml will be included with that target. # # Variables # grit_output_dir: directory containing grit-generated files. # generated_files: list of android resource files to package. # # Example # java_strings_grd_prebuilt("foo_strings_grd") { # grit_output_dir = "$root_gen_dir/foo/grit" # generated_files = [ # "values/strings.xml" # ] # } template("java_strings_grd_prebuilt") { forward_variables_from(invoker, [ "testonly" ]) _resources_zip = "$target_out_dir/$target_name.resources.zip" _zip_target_name = "${target_name}__zip" zip(_zip_target_name) { base_dir = invoker.grit_output_dir inputs = rebase_path(invoker.generated_files, ".", base_dir) output = _resources_zip if (defined(invoker.deps)) { deps = invoker.deps } } android_generated_resources(target_name) { forward_variables_from(invoker, [ "resource_overlay", "visibility", ]) generating_target = ":$_zip_target_name" generated_resources_zip = _resources_zip } } }