1# Writing GN Templates 2GN and Ninja are documented here: 3* GN: https://gn.googlesource.com/gn/+/main/docs/ 4* Ninja: https://ninja-build.org/manual.html 5 6[TOC] 7 8## Things to Consider When Writing Templates 9### Inputs and Depfiles 10List all files read (or executed) by an action as `inputs`. 11 * It is not enough to have inputs listed by dependent targets. They must be 12 listed directly by targets that use them, or added by a depfile. 13 * Non-system Python imports are inputs! For scripts that import such modules, 14 use [`action_with_pydeps`] to ensure all dependent Python files are captured 15 as inputs. 16 17[`action_with_pydeps`]: https://cs.chromium.org/chromium/src/build/config/python.gni?rcl=320ee4295eb7fabaa112f08d1aacc88efd1444e5&l=75 18 19To understand *why* actions must list all inputs directly, you need to 20understand ninja's "restat" directive, which is used for all GN `action()`s. 21 22From https://ninja-build.org/manual.html: 23 24> if present, causes Ninja to re-stat the command’s outputs after execution of 25> the command. Each output whose modification time the command did not change 26> will be treated as though it had never needed to be built. This may cause the 27> output’s reverse dependencies to be removed from the list of pending build 28> actions. 29 30So, if your action depends on target "X", and "X" does not change its outputs 31when rebuilt, then ninja will not bother to rebuild your target. 32 33For action inputs that are not computable during "gn gen", actions can write 34depfiles (.d files) to add additional input files as dependencies for 35subsequent builds. They are relevant only for incremental builds since they 36won't exist for the initial build. 37 * Depfiles should not list files that GN already lists as `inputs`. 38 * Besides being redundant, listing them also makes it harder to remove 39 inputs, since removing them from GN does not immediately remove them from 40 depfiles. 41 * Stale paths in depfiles can cause ninja to complain of circular 42 dependencies [in some cases](https://bugs.chromium.org/p/chromium/issues/detail?id=639042). 43 * Use [`action_helpers.write_depfile()`] to write these. 44 45[`action_helpers.write_depfile()`]: https://source.chromium.org/chromium/chromium/src/+/main:build/action_helpers.py?q=symbol:%5Cbwrite_depfile 46 47### Ensuring "gn analyze" Knows About your Inputs 48"gn analyze" is used by bots to run only affected tests and build only affected 49targets. Try it out locally via: 50```bash 51echo "compute_inputs_for_analyze = true" >> out/Debug/args.gn 52gn analyze //out/Debug <(echo '{ 53 "files": ["//BUILD.gn"], 54 "test_targets": ["//base"], 55 "additional_compile_targets":[]}') result.txt; cat result.txt 56``` 57* For analyze to work properly, GN must know about all inputs. 58* Inputs added by depfiles are *not available* to "gn analyze". 59 * When paths listed in a target's depfile are listed as `inputs` to a 60 dependent target, analyze will be correct. 61 * Example: An `AndroidManifest.xml` file is an input to an 62 `android_library()` and is included in an `android_apk()`'s depfile. 63 `gn analyze` will know that a change to the file will require the APK 64 to be rebuilt, because the file is marked as an input to the library, and 65 the library is a dep of the APK. 66 * When paths listed in a target's depfile are *not* listed as `inputs` to a 67 dependent target, a few options exist: 68 * Rather than putting the inputs in a depfile, force users of your template 69 to list them, and then have your action re-compute them and assert that 70 they were correct. 71 * `jinja_template()` does this. 72 * Rather than putting the inputs in a depfile, compute them beforehand and 73 save them to a text file. Have your template Use `read_file()` to read 74 them in. 75 * `action_with_pydeps()` does this. 76 * Continue using a depfile, but use an `exec_script()` to compute them when 77 [`compute_inputs_for_analyze`](https://cs.chromium.org/chromium/src/build/config/compute_inputs_for_analyze.gni) 78 is set. 79 * `grit()` does this. 80 81### Outputs 82#### What to List as Outputs 83Do not list files as `outputs` unless they are important. Outputs are important 84if they are: 85 * used as an input by another target, or 86 * are roots in the dependency graph (e.g. binaries, apks, etc). 87 88Example: 89* An action runs a binary that creates an output as well as a log file. Do not 90 list the log file as an output. 91 92Rationale: 93* Inputs and outputs are a node's public API on the build graph. Not listing 94 "implementation detail"-style outputs prevents other targets from depending on 95 them as inputs. 96* Not listing them also helps to minimize the size of the build graph (although 97 this would be noticeable only for frequently used templates). 98 99#### Where to Place Outputs 100**Option 1:** To make outputs visible in codesearch (e.g. generated sources): 101* use `$target_gen_dir/$target_name.$EXTENSION`. 102 103**Option 2:** Otherwise (for binary files): 104* use `$target_out_dir/$target_name.$EXTENSION`. 105 106**Option 3:** For outputs that are required at runtime 107(e.g. [runtime_deps](https://gn.googlesource.com/gn/+/main/docs/reference.md#runtime_deps)), 108options 1 & 2 do not work because they are not archived in builder/tester bot 109configurations. In this case: 110* use `$root_out_dir/gen.runtime` or `$root_out_dir/obj.runtime`. 111 112Example: 113```python 114# This .json file is used at runtime and thus cannot go in target_gen_dir. 115_target_dir_name = rebase_path(get_label_info(":$target_name", "dir"), "//") 116_output_path = "$root_out_dir/gen.runtime/$_target_dir_name/$target_name.json" 117``` 118 119**Option 4:** For outputs that map 1:1 with executables, and whose paths cannot 120be derived at runtime: 121* use `$root_build_dir/YOUR_NAME_HERE/$target_name`. 122 123Examples: 124```python 125# Wrapper scripts for apks: 126_output_path = "$root_build_dir/bin/$target_name" 127# Metadata for apks. Used by binary size tools. 128_output_path = "$root_build_dir/size-info/${invoker.name}.apk.jar.info" 129``` 130 131## Best Practices for Python Actions 132Outputs should be atomic and take advantage of `restat=1`. 133* Make outputs atomic by writing to temporary files and then moving them to 134 their final location. 135 * Rationale: An interrupted write can leave a file with an updated timestamp 136 and corrupt contents. Ninja looks only at timestamps. 137* Do not overwrite an existing output with identical contents. 138 * Rationale: `restat=1` is a ninja feature enabled for all actions that 139 short-circuits a build when output timestamps do not change. This feature is 140 the reason that the total number of build steps sometimes decreases when 141 building.. 142* Use [`action_helpers.atomic_output()`] to perform both of these techniques. 143 144[`action_helpers.atomic_output()`]: https://source.chromium.org/chromium/chromium/src/+/main:build/action_helpers.py?q=symbol:%5Cbatomic_output 145 146Actions should be deterministic in order to avoid hard-to-reproduce bugs. 147Given identical inputs, they should produce byte-for-byte identical outputs. 148* Some common mistakes: 149 * Depending on filesystem iteration order. 150 * Writing absolute paths in outputs. 151 * Writing timestamps in files (or in zip entries). 152 * Tip: Use [`zip_helpers.py`] when writing `.zip` files. 153 154[`zip_helpers.py`]: https://source.chromium.org/chromium/chromium/src/+/main:build/zip_helpers.py 155 156## Style Guide 157Chromium GN files follow 158[GN's Style Guide](https://gn.googlesource.com/gn/+/main/docs/style_guide.md) 159with a few additions. 160 161### Action Granularity 162 * Prefer writing new Python scripts that do what you want over 163 composing multiple separate actions within a template. 164 * Fewer targets makes for a simpler build graph. 165 * GN logic and build logic winds up much simpler. 166 167Bad: 168```python 169template("generate_zipped_sources") { 170 generate_files("${target_name}__gen") { 171 ... 172 outputs = [ "$target_gen_dir/$target_name.temp" ] 173 } 174 zip(target_name) { 175 deps = [ ":${target_name}__gen" ] 176 inputs = [ "$target_gen_dir/$target_name.temp" ] 177 outputs = [ invoker.output_zip ] 178 } 179} 180``` 181 182Good: 183```python 184template("generate_zipped_sources") { 185 action(target_name) { 186 script = "generate_and_zip.py" 187 ... 188 outputs = [ invoker.output_zip ] 189 } 190} 191``` 192 193### Naming for Intermediate Targets 194Targets that are not relevant to users of your template should be named as: 195`${target_name}__$something`. 196 197Example: 198```python 199template("my_template") { 200 action("${target_name}__helper") { 201 ... 202 } 203 action(target_name) { 204 deps = [ ":${target_name}__helper" ] 205 ... 206 } 207} 208``` 209 210This scheme ensures that subtargets defined in templates do not conflict with 211top-level targets. 212 213### Visibility for Intermediate Targets 214 215You can restrict what targets can depend on one another using [visibility]. 216When writing templates, with multiple intermediate targets, `visibility` should 217only be applied to the final target (the one named `target_name`). Applying only 218to the final target ensures that the invoker-provided visibility does not 219prevent intermediate targets from depending on each other. 220 221[visibility]: https://gn.googlesource.com/gn/+/main/docs/reference.md#var_visibility 222 223Example: 224```python 225template("my_template") { 226 # Do not forward visibility here. 227 action("${target_name}__helper") { 228 # Do not forward visibility here. 229 ... 230 } 231 action(target_name) { 232 # Forward visibility here. 233 forward_variables_from(invoker, [ "visibility" ]) 234 deps = [ ":${target_name}__helper" ] 235 ... 236 } 237} 238``` 239 240### Variables 241Prefix variables within templates and targets with an underscore. For example: 242 243```python 244template("example") { 245 _outer_sources = invoker.extra_sources 246 247 source_set(target_name) { 248 _inner_sources = invoker.sources 249 sources = _outer_sources + _inner_sources 250 } 251} 252``` 253 254This convention conveys that `sources` is relevant to `source_set`, while 255`_outer_sources` and `_inner_sources` are not. 256 257### Passing Arguments to Targets 258Pass arguments to targets by assigning them directly within target definitions. 259 260When a GN template goes to resolve `invoker.FOO`, GN will look in all enclosing 261scopes of the target's definition. It is hard to figure out where `invoker.FOO` 262is coming from when it is not assigned directly within the target definition. 263 264Bad: 265```python 266template("hello") { 267 script = "..." 268 action(target_name) { 269 # This action will see "script" from the enclosing scope. 270 } 271} 272``` 273 274Good: 275```python 276template("hello") { 277 action(target_name) { 278 script = "..." # This is equivalent, but much more clear. 279 } 280} 281``` 282 283**Exception:** `testonly` and `visibility` can be set in the outer scope so that 284they are implicitly passed to all targets within a template. 285 286This is okay: 287```python 288template("hello") { 289 testonly = true # Applies to all nested targets. 290 action(target_name) { 291 script = "..." 292 } 293} 294``` 295 296### Using forward_variables_from() 297Using [forward_variables_from()] is encouraged, but special care needs to be 298taken when forwarding `"*"`. The variables `testonly` and `visibility` should 299always be listed explicitly in case they are assigned in an enclosing 300scope. 301See [this bug] for more a full example. 302 303To make this easier, `//build/config/BUILDCONFIG.gn` defines: 304```python 305TESTONLY_AND_VISIBILITY = [ "testonly", "visibility" ] 306``` 307 308Example usage: 309```python 310template("action_wrapper") { 311 action(target_name) { 312 forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) 313 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 314 ... 315 } 316} 317``` 318 319If your template defines multiple targets, be careful to apply `testonly` to 320both, but `visibility` only to the primary one (so that the primary one is not 321prevented from depending on the other ones). 322 323Example: 324```python 325template("template_with_multiple_targets") { 326 action("${target_name}__helper") { 327 forward_variables_from(invoker, [ "testonly" ]) 328 ... 329 } 330 action(target_name) { 331 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 332 ... 333 } 334} 335``` 336 337An alternative would be to explicitly set `visibility` on all inner targets, 338but doing so tends to be tedious and has little benefit. 339 340[this bug]: https://bugs.chromium.org/p/chromium/issues/detail?id=862232 341[forward_variables_from]: https://gn.googlesource.com/gn/+/main/docs/reference.md#func_forward_variables_from 342 343## Useful Ninja Flags 344Useful ninja flags when developing build rules: 345* `ninja -v` - log the full command-line of every target. 346* `ninja -v -n` - log the full command-line of every target without having 347 to wait for a build. 348* `ninja -w dupbuild=err` - fail if multiple targets have the same output. 349* `ninja -d keeprsp` - prevent ninja from deleting response files. 350* `ninja -n -d explain` - print why ninja thinks a target is dirty. 351* `ninja -j1` - execute only one command at a time. 352