• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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