|  | # Best practices for writing GN templates | 
|  |  | 
|  | ## Overview {#overview} | 
|  |  | 
|  | In GN, templates provide a way to add on to GN’s built-in target types. Basically, | 
|  | templates are GN’s primary way to build reusable functions. Template definitions go | 
|  | in `.gni` (GN import) files that can be imported into target `.gn` files. | 
|  |  | 
|  | This document details the best practices for creating GN templates, and each best | 
|  | practice includes an example. These best practices are in addition to the best | 
|  | practices outlined in [Fuchsia build system policies](policies.md). | 
|  |  | 
|  | Run `gn help template` for more information and more complete examples, and | 
|  | [GN Language and Operation](https://gn.googlesource.com/gn/+/HEAD/docs/language.md#templates) | 
|  | for more information on GN features. | 
|  |  | 
|  | ##  Templates {#templates} | 
|  |  | 
|  | ### Define templates in `.gni`, targets in `BUILD.gn` {#define-templates-in-gni-targets-in-build-gn} | 
|  |  | 
|  | Technically, it’s possible to import both `.gni` and `BUILD.gn` files. The best | 
|  | practice, however, is to define templates in `.gni` files, and | 
|  | targets in `.gn` files. This makes it clear to users what’s a template. Users | 
|  | want to import templates so they can use them, and never want to import targets. | 
|  |  | 
|  | ### Document templates and args {#document-templates-and-args} | 
|  |  | 
|  | Document both your templates and args, including: | 
|  |  | 
|  | *   A general explanation of the template’s purpose and concepts introduced. A practical usage example is recommended. | 
|  | *   All parameters should be documented. Parameters that are common and simply forwarded (such as `deps` or `visibility`), where the meaning is consistent with their meaning on built-in GN rules, can be listed with no additional information. | 
|  | *   If a template generates `metadata,` then `data_keys` should be listed. | 
|  |  | 
|  | To document your template, insert a comment block in front of your template definition | 
|  | to specify your public contract. | 
|  |  | 
|  | ```gn | 
|  | declare_args() { | 
|  | # The amount of bytes to allocate when creating a disk image. | 
|  | disk_image_size_bytes = 1024 | 
|  | } | 
|  |  | 
|  | # Defines a disk image file. | 
|  | # | 
|  | # Disk image files are used to boot the bar virtual machine. | 
|  | # | 
|  | # Example: | 
|  | # ``` | 
|  | # disk_image("my_image") { | 
|  | #   sources = [ "boot.img", "kernel.img" ] | 
|  | #   sdk = false | 
|  | # } | 
|  | # ``` | 
|  | # | 
|  | # Parameters | 
|  | # | 
|  | #  sources (required) | 
|  | #    List of source files to include in the image. | 
|  | #    Type: list(path) | 
|  | # | 
|  | #  sdk (optional) | 
|  | #    This image is exported to the SDK. | 
|  | #    Type: bool | 
|  | #    Default: false | 
|  | # | 
|  | #  data_deps | 
|  | #  deps | 
|  | #  public_deps | 
|  | #  testonly | 
|  | #  visibility | 
|  | # | 
|  | # Metadata | 
|  | # | 
|  | #  files | 
|  | #    Filenames present in this image. | 
|  | template("disk_image") { | 
|  | ... | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ### Wrap tools with a single action template {#wrap-tools-with-a-single-action-template} | 
|  |  | 
|  | For every tool, have a canonical template that wraps it with an `action`. | 
|  | This template’s job is to turn GN parameters into `args` for the tool, and | 
|  | that’s it. This sets an encapsulation boundary around the tool for details | 
|  | such as translating parameters to args. | 
|  |  | 
|  | Note that in this example we define the `executable()` in one file and the | 
|  | `template()` in another, because | 
|  | [templates and targets should be separated](#define-templates-in-gni-targets-in-build-gn). | 
|  |  | 
|  | ```gn | 
|  | # //src/developer_tools/BUILD.gn | 
|  | executable("copy_to_target_bin") { | 
|  | ... | 
|  | } | 
|  |  | 
|  | # //src/developer_tools/cli.gni | 
|  | template("copy_to_target") { | 
|  | compiled_action(target_name) { | 
|  | forward_variables_from(invoker, [ | 
|  | "data_deps", | 
|  | "deps", | 
|  | "public_deps", | 
|  | "testonly", | 
|  | "visibility" | 
|  | ]) | 
|  | assert(defined(invoker.sources), "Must specify sources") | 
|  | assert(defined(invoker.destinations), "Must specify destinations") | 
|  | tool = "//src/developer_tools:copy_to_target_bin" | 
|  | args = [ "--sources" ] | 
|  | foreach(source, sources) { | 
|  | args += [ rebase_path(source, root_build_dir) ] | 
|  | } | 
|  | args += [ "--destinations" ] | 
|  | foreach(destination, destinations) { | 
|  | args += [ rebase_path(destination, root_build_dir) ] | 
|  | } | 
|  | } | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ### Consider making templates private {#consider-making-templates-private} | 
|  |  | 
|  | Templates and variables whose name begins with an underscore (e.g. `template("_private")`) | 
|  | are considered private and won’t be visible to other files that `import()` them, but can be | 
|  | used in the same file that they’re defined. This is useful for internal helper templates or | 
|  | “local global variables” that you might define for instance to share logic between two templates, | 
|  | where the helper is not useful to the user. | 
|  |  | 
|  | ```gn | 
|  | template("coffee") { | 
|  | # Take coffee parameters like roast and sugar | 
|  | ... | 
|  | _beverage(target_name) { | 
|  | # Express in beverage terms like ingredients and temperature | 
|  | ... | 
|  | } | 
|  | } | 
|  |  | 
|  | template("tea") { | 
|  | # Take tea parameters like loose leaf and cream | 
|  | ... | 
|  | _beverage(target_name) { | 
|  | # Express in beverage terms like ingredients and temperature | 
|  | ... | 
|  | } | 
|  | } | 
|  |  | 
|  | # We don't want people directly defining new beverages. | 
|  | # For instance they might add both sugar and salt to the ingredients list. | 
|  | template("_beverage") { | 
|  | ... | 
|  | } | 
|  | ``` | 
|  |  | 
|  | Sometimes you can’t make a template private because it actually needs to be used | 
|  | from different files, but you’d still like to hide it because it’s not meant to | 
|  | be used directly. In situations like this you can swap enforcement for signaling, by | 
|  | putting your template in a file under a path such as `//build/internal/`. | 
|  |  | 
|  | ### Test your templates {#test-your-templates} | 
|  |  | 
|  | Write tests that use your templates to build, or use files generated by your | 
|  | templates in the course of the test. | 
|  |  | 
|  | You should not rely on other people’s builds and tests to test your template. | 
|  | Having your own tests makes your template more maintainable, since it’s faster | 
|  | to validate future changes to your template and it’s easier to isolate faults. | 
|  |  | 
|  | ```gn | 
|  | # //src/drinks/coffee.gni | 
|  | template("coffee") { | 
|  | ... | 
|  | } | 
|  |  | 
|  | # //src/drinks/tests/BUILD.gni | 
|  | coffee("coffee_for_test") { | 
|  | ... | 
|  | } | 
|  |  | 
|  | test("coffee_test") { | 
|  | sources = [ "taste_coffee.cc" ] | 
|  | data_deps = [ ":coffee_for_test" ] | 
|  | ... | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ## Parameters {#parameters} | 
|  |  | 
|  | ### Assert on required parameters {#assert-on-required-parameters} | 
|  |  | 
|  | If you have required parameters in your template, `assert` that they’re defined. | 
|  |  | 
|  | If a user forgets to specify a required parameter, and there’s no assert defined, | 
|  | they won’t get a clear explanation for their error. Using an assert allows you to | 
|  | provide a useful error message. | 
|  |  | 
|  | ```gn | 
|  | template("my_template") { | 
|  | forward_variables_from(invoker, [ "sources", "testonly", "visibility" ]) | 
|  | assert(defined(sources), | 
|  | "A `sources` argument was missing when calling my_template($target_name)") | 
|  | } | 
|  |  | 
|  | template("my_other_template") { | 
|  | forward_variables_from(invoker, [ "inputs", "testonly", "visibility" ]) | 
|  | assert(defined(inputs) && inputs != [], | 
|  | "An `input` argument must be present and non-empty " + | 
|  | "when calling my_template($target_name)") | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ### Always forward `testonly` {#always-forward-testonly} | 
|  |  | 
|  | Setting `testonly` on a target guards it against being used by non-test targets. | 
|  | If your template doesn’t forward `testonly` to inner targets then: | 
|  |  | 
|  | 1. Your inner targets might fail to build, because your users might pass you `testonly` dependencies. | 
|  | 2. You’ll surprise your users when they find that their `testonly` artifacts end up in production artifacts. | 
|  |  | 
|  | The following example shows how to forward `testonly`: | 
|  |  | 
|  | ```gn | 
|  | template("my_template") { | 
|  | action(target_name) { | 
|  | forward_variables_from(invoker, [ "testonly", "deps" ]) | 
|  | ... | 
|  | } | 
|  | } | 
|  |  | 
|  | my_template("my_target") { | 
|  | visibility = [ ... ] | 
|  | testonly = true | 
|  | ... | 
|  | } | 
|  | ``` | 
|  |  | 
|  | Note that if the parent scope for the inner action defines `testonly` | 
|  | then `forward_variables_from(invoker, "*")` won’t forward it, as it | 
|  | avoids clobbering variables. Here are some patterns to work around this: | 
|  |  | 
|  | ```gn | 
|  | # Broken, doesn't forward `testonly` | 
|  | template("my_template") { | 
|  | testonly = ... | 
|  | action(target_name) { | 
|  | forward_variables_from(invoker, "*") | 
|  | ... | 
|  | } | 
|  | } | 
|  |  | 
|  | # Works | 
|  | template("my_template") { | 
|  | testonly = ... | 
|  | action(target_name) { | 
|  | forward_variables_from(invoker, "*") | 
|  | testonly = testonly | 
|  | ... | 
|  | } | 
|  | } | 
|  |  | 
|  | # Works | 
|  | template("my_template") { | 
|  | testonly = ... | 
|  | action(target_name) { | 
|  | forward_variables_from(invoker, "*", [ "testonly" ]) | 
|  | forward_variables_from(invoker, [ "testonly" ]) | 
|  | ... | 
|  | } | 
|  | } | 
|  | ``` | 
|  |  | 
|  | The one exception to this are templates that hard-code `testonly = true` because | 
|  | they should never be used in production targets. For example: | 
|  |  | 
|  | ```gn | 
|  | template("a_test_template") { | 
|  | testonly = true | 
|  | ... | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ### Forward `visibility` to the main target and hide inner targets {#forward-visibility-to-the-main-target-hide-inner-targets} | 
|  |  | 
|  | GN users expect to be able to set `visibility` on any target. | 
|  |  | 
|  | This advice is similar to [always forward testonly](#heading=h.fk6w1as9tkpx), except that | 
|  | it only applies to the main target (the target named `target_name`). Other targets should | 
|  | have their `visibility` restricted, so that your users can’t depend on your inner targets | 
|  | that are not part of your contract. | 
|  |  | 
|  | ```gn | 
|  | template("my_template") { | 
|  | action("${target_name}_helper") { | 
|  | forward_variables_from(invoker, [ "testonly", "deps" ]) | 
|  | visibility = [ ":*" ] | 
|  | ... | 
|  | } | 
|  |  | 
|  | action(target_name) { | 
|  | forward_variables_from(invoker, [ "testonly", "visibility" ]) | 
|  | deps = [ ":${target_name}_helper" ] | 
|  | ... | 
|  | } | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ### If forwarding `deps`, also forward `public_deps` and `data_deps` {#if-forwarding-deps-also-forward-public_deps-and-data_deps} | 
|  |  | 
|  | All built-in rules that take `deps` take `public_deps` and `data_deps`. | 
|  | Some built-in rules don’t differentiate between types of deps (e.g. `action()` | 
|  | treats `deps` and `public_deps` equally). But dependants on your generated | 
|  | targets might (e.g. an `executable()` that deps on your generated `action()` | 
|  | treats transitive `deps` and `public_deps` differently). | 
|  |  | 
|  | ```gn | 
|  | template("my_template") { | 
|  | action(target_name) { | 
|  | forward_variables_from(invoker, [ | 
|  | "data_deps", | 
|  | "deps", | 
|  | "public_deps", | 
|  | "testonly", | 
|  | "Visibility" | 
|  | ]) | 
|  | ... | 
|  | } | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ## Target Names {#target-names} | 
|  |  | 
|  | ### Define an inner target named `target_name` {#define-an-inner-target-named-target_name} | 
|  |  | 
|  | Your template should define at least one target that is named `target_name`. | 
|  | This allows your users to invoke your template with a name, and then use that | 
|  | name in their deps. | 
|  |  | 
|  | ```gn | 
|  | # //build/image.gni | 
|  | template("image") { | 
|  | action(target_name) { | 
|  | ... | 
|  | } | 
|  | } | 
|  |  | 
|  | # //src/some/project/BUILD.gn | 
|  | import("//build/image.gni") | 
|  |  | 
|  | image("my_image") { | 
|  | ... | 
|  | } | 
|  |  | 
|  | group("images") { | 
|  | deps = [ ":my_image", ... ] | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ### `target_name` is a good default for an output name, but offer an override {#target_name-is-a-good-default-for-an-output-name-but-offer-an-override} | 
|  |  | 
|  | If your template produces a single output then using the target name to select | 
|  | the output name is good default behavior. However, target names must be unique | 
|  | in a directory, so your users won’t always be able to use the name that they | 
|  | want both for the target and the output. | 
|  |  | 
|  | It’s a good best practice to offer users an override: | 
|  |  | 
|  | ```gn | 
|  | template("image") { | 
|  | forward_variables_from(invoker, [ "output_name", ... ]) | 
|  | if (!defined(output_name)) { | 
|  | output_name = target_name | 
|  | } | 
|  | ... | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ### Prefix internal target names with `$target_name` {#prefix-internal-target-names-with-$target_name} | 
|  |  | 
|  | GN labels must be unique, or else you’ll get a gen-time error. If everyone on | 
|  | the same project follows the same naming convention then collisions are less | 
|  | likely to happen and it becomes easier to associate internal target names | 
|  | with the targets that created them. | 
|  |  | 
|  | ```gn | 
|  | template("boot_image") { | 
|  | generate_boot_manifest_action = "${target_name}_generate_boot_manifest" | 
|  | action(generate_boot_manifest_action) { | 
|  | ... | 
|  | } | 
|  |  | 
|  | image(target_name) { | 
|  | ... | 
|  | deps += [ ":$generate_boot_manifest_action" ] | 
|  | } | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ### Do not infer output names from target labels {#do-not-infer-output-names-from-target-labels} | 
|  |  | 
|  | It’s tempting to assume a relationship between target names and output names. | 
|  | For instance, the following example will work: | 
|  |  | 
|  | ```gn | 
|  | executable("bin") { | 
|  | ... | 
|  | } | 
|  |  | 
|  | template("bin_runner") { | 
|  | compiled_action(target_name) { | 
|  | forward_variables_from(invoker, [ "testonly", "visibility" ]) | 
|  | assert(defined(invoker.bin), "Must specify bin") | 
|  | deps = [ invoker.bin ] | 
|  | tool = root_out_dir + "/" + get_label_info(invoker.foo, "name") | 
|  | ... | 
|  | } | 
|  | } | 
|  |  | 
|  | bin_runner("this_will_work") { | 
|  | bin = ":bin" | 
|  | } | 
|  | ``` | 
|  |  | 
|  | However this example will product a gen-time error: | 
|  |  | 
|  | ```gn | 
|  | executable("bin") { | 
|  | output_name = "my_binary" | 
|  | ... | 
|  | } | 
|  |  | 
|  | template("bin_runner") { | 
|  | compiled_action(target_name) { | 
|  | forward_variables_from(invoker, [ "testonly", "visibility" ]) | 
|  | assert(defined(invoker.bin), "Must specify bin") | 
|  | tool = root_out_dir + "/" + get_label_info(invoker.bin, "name") | 
|  | ... | 
|  | } | 
|  | } | 
|  |  | 
|  | # This will produce a gen-time error saying that a file ".../bin" is needed | 
|  | # by ":this_will_fail" with no rule to generate it. | 
|  | bin_runner("this_will_fail") { | 
|  | bin = ":bin" | 
|  | } | 
|  | ``` | 
|  |  | 
|  | Here’s one way of fixing this problem: | 
|  |  | 
|  | ```gn | 
|  | executable("bin") { | 
|  | output_name = "my_binary" | 
|  | ... | 
|  | } | 
|  |  | 
|  | template("bin_runner") { | 
|  | compiled_action(target_name) { | 
|  | forward_variables_from(invoker, [ "testonly", "visibility" ]) | 
|  | assert(defined(invoker.bin), "Must specify bin") | 
|  | tool = bin | 
|  | ... | 
|  | } | 
|  | } | 
|  |  | 
|  | bin_runner("this_will_work") { | 
|  | bin = "$root_out_dir/my_binary" | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ## GN functions and generation {#gn-functions-and-generation} | 
|  |  | 
|  | ### Only use `read_file()` with source files {#only-use-read_file-with-source-files} | 
|  |  | 
|  | `read_file()` occurs during generation and can not be safely used to read from generated | 
|  | files or build outputs. It can be used to read source files, for example to read | 
|  | a manifest file or a json file with which to populate build dependencies. | 
|  | Notably `read_file()` can not be used with `generated_file()` or `write_file()`. | 
|  |  | 
|  | ### Prefer `generated_file()` over `write_file()` {#prefer-generated_file-over-write_file} | 
|  |  | 
|  | In general, it’s recommended that you use `generated_file()` over `write_file()`. | 
|  | `generated_file()` provides additional features and addresses some of the challenges | 
|  | of `write_file()`. For instance, `generated_file()` can be executed in parallel, | 
|  | while `write_file()` is done serially at gen time. | 
|  |  | 
|  | The structure of both commands is very similar. For instance, you can turn | 
|  | this instance of `write_file()`: | 
|  |  | 
|  | ```gn | 
|  | write_file("my_file", "My file contents") | 
|  | ``` | 
|  |  | 
|  | Into this instance of `generated_file()`: | 
|  |  | 
|  | ```gn | 
|  | generated_file("my_file") { | 
|  | outputs = [ "my_file" ] | 
|  | contents = "My file contents" | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ## Patterns and anti-patterns {#patterns-and-anti-patterns} | 
|  |  | 
|  | ### Target outputs {#target-outputs} | 
|  |  | 
|  | When working with `get_target_outputs()` to extract a single element, GN won’t | 
|  | let you subscript a list before assignment. To work around this issue, | 
|  | you can use the less than elegant workaround below: | 
|  |  | 
|  | ```gn | 
|  | # Appending to a list is elegant | 
|  | deps += get_target_outputs(":some_target") | 
|  |  | 
|  | # Extracting a single element to use in variable substitution - ugly but reliable | 
|  | _outputs = get_target_outputs(":other_target") | 
|  | output = _outputs[0] | 
|  | message = "My favorite output is $output" | 
|  |  | 
|  | # This expression is invalid: `output = get_target_outputs(":other_target")[0]` | 
|  | # GN won't let you subscript an rvalue. | 
|  | ``` | 
|  |  | 
|  | ### Set operations {#set-operations} | 
|  |  | 
|  | GN offers lists and scopes as aggregate data types, but not associative | 
|  | types like maps or sets. Sometimes lists are used instead of sets. The | 
|  | example below has a list of build variants, and checks if one of them | 
|  | is the “profile” variant: | 
|  |  | 
|  | ```gn | 
|  | if (variants + [ "profile" ] - [ "profile" ] != variants) { | 
|  | # Do something special for profile builds | 
|  | ... | 
|  | } | 
|  | ``` | 
|  |  | 
|  | This is an anti-pattern. Rather, variants could be defined as follows: | 
|  |  | 
|  | ```gn | 
|  | variants = { | 
|  | profile = true | 
|  | asan = false | 
|  | ... | 
|  | } | 
|  |  | 
|  | if (variants.profile) { | 
|  | # Do something special for profile builds | 
|  | ... | 
|  | } | 
|  | ``` | 
|  |  | 
|  | ### Forwarding `"*"` {#forwarding-*} | 
|  |  | 
|  | `forward_variables_from()` copies specified variables to the current | 
|  | scope from the given scope _or any enclosing scope_. Unless you | 
|  | specify `"*"`, in which case it will only directly copy variables | 
|  | from the given scope. And it will never clobber a variable that’s | 
|  | already in your scope - that’s a gen-time error. | 
|  |  | 
|  | Sometimes you want to copy everything from the invoker, except for | 
|  | a particular variable that you want to copy from any enclosing | 
|  | scope. You’ll encounter this pattern: | 
|  |  | 
|  | ```gn | 
|  | forward_variables_from(invoker, "*", [ "visibility" ]) | 
|  | forward_variables_from(invoker, [ "visibility" ]) | 
|  | ``` | 
|  |  | 
|  | ### `exec_script()` {#exec-script} | 
|  |  | 
|  | GN's built-in function | 
|  | [exec_script](https://gn.googlesource.com/gn/+/HEAD/docs/reference.md#func_exec_script) | 
|  | is a powerful tool for augmenting GN's abilities. Like `action()`, | 
|  | `exec_script()` can invoke an external tool. Unlike `action()`, `exec_script()` | 
|  | can invoke the tool **synchronously** with build generation, meaning that you | 
|  | can use the output of the tool in your `BUILD.gn` logic. | 
|  |  | 
|  | Since this creates a performance bottleneck in gen time (i.e. `fx set` takes | 
|  | longer), this feature must be used with care. | 
|  | For more information, refer to | 
|  | [this writeup](https://chromium.googlesource.com/chromium/src/+/ab1c69b1814d3c905fdab7b0d177b478eecf40a3/.gn#291) | 
|  | by the Chromium team. | 
|  |  | 
|  | An allowlist has been set up in `//.gn`. Please consult `OWNERS` for changes | 
|  | made to this allowlist. |