summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/build/docs/writing_gn_templates.md
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/build/docs/writing_gn_templates.md')
-rw-r--r--third_party/libwebrtc/build/docs/writing_gn_templates.md344
1 files changed, 344 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/docs/writing_gn_templates.md b/third_party/libwebrtc/build/docs/writing_gn_templates.md
new file mode 100644
index 0000000000..21a73bb65e
--- /dev/null
+++ b/third_party/libwebrtc/build/docs/writing_gn_templates.md
@@ -0,0 +1,344 @@
+# Writing GN Templates
+GN and Ninja are documented here:
+* GN: https://gn.googlesource.com/gn/+/master/docs/
+* Ninja: https://ninja-build.org/manual.html
+
+[TOC]
+
+## Things to Consider When Writing Templates
+### Inputs and Depfiles
+List all files read (or executed) by an action as `inputs`.
+ * It is not enough to have inputs listed by dependent targets. They must be
+ listed directly by targets that use them, or added by a depfile.
+ * Non-system Python imports are inputs! For scripts that import such modules,
+ use [`action_with_pydeps`] to ensure all dependent Python files are captured
+ as inputs.
+
+[`action_with_pydeps`]: https://cs.chromium.org/chromium/src/build/config/python.gni?rcl=320ee4295eb7fabaa112f08d1aacc88efd1444e5&l=75
+
+To understand *why* actions must list all inputs directly, you need to
+understand ninja's "restat" directive, which is used for all GN `action()`s.
+
+From https://ninja-build.org/manual.html:
+
+> if present, causes Ninja to re-stat the command’s outputs after execution of
+> the command. Each output whose modification time the command did not change
+> will be treated as though it had never needed to be built. This may cause the
+> output’s reverse dependencies to be removed from the list of pending build
+> actions.
+
+So, if your action depends on target "X", and "X" does not change its outputs
+when rebuilt, then ninja will not bother to rebuild your target.
+
+For action inputs that are not computable during "gn gen", actions can write
+depfiles (.d files) to add additional input files as dependencies for
+subsequent builds. They are relevant only for incremental builds since they
+won't exist for the initial build.
+ * Depfiles should not list files that GN already lists as `inputs`.
+ * Besides being redundant, listing them also makes it harder to remove
+ inputs, since removing them from GN does not immediately remove them from
+ depfiles.
+ * Stale paths in depfiles can cause ninja to complain of circular
+ dependencies [in some cases](https://bugs.chromium.org/p/chromium/issues/detail?id=639042).
+
+### Ensuring "gn analyze" Knows About your Inputs
+"gn analyze" is used by bots to run only affected tests and build only affected
+targets. Try it out locally via:
+```bash
+echo "compute_inputs_for_analyze = true" >> out/Debug/args.gn
+gn analyze //out/Debug <(echo '{
+ "files": ["//BUILD.gn"],
+ "test_targets": ["//base"],
+ "additional_compile_targets":[]}') result.txt; cat result.txt
+```
+* For analyze to work properly, GN must know about all inputs.
+* Inputs added by depfiles are *not available* to "gn analyze".
+ * When paths listed in a target's depfile are listed as `inputs` to a
+ dependent target, analyze will be correct.
+ * Example: An `AndroidManifest.xml` file is an input to an
+ `android_library()` and is included in an `android_apk()`'s depfile.
+ `gn analyze` will know that a change to the file will require the APK
+ to be rebuilt, because the file is marked as an input to the library, and
+ the library is a dep of the APK.
+ * When paths listed in a target's depfile are *not* listed as `inputs` to a
+ dependent target, a few options exist:
+ * Rather than putting the inputs in a depfile, force users of your template
+ to list them, and then have your action re-compute them and assert that
+ they were correct.
+ * `jinja_template()` does this.
+ * Rather than putting the inputs in a depfile, compute them beforehand and
+ save them to a text file. Have your template Use `read_file()` to read
+ them in.
+ * `action_with_pydeps()` does this.
+ * Continue using a depfile, but use an `exec_script()` to compute them when
+ [`compute_inputs_for_analyze`](https://cs.chromium.org/chromium/src/build/config/compute_inputs_for_analyze.gni)
+ is set.
+ * `grit()` does this.
+
+### Outputs
+#### What to List as Outputs
+Do not list files as `outputs` unless they are important. Outputs are important
+if they are:
+ * used as an input by another target, or
+ * are roots in the dependency graph (e.g. binaries, apks, etc).
+
+Example:
+* An action runs a binary that creates an output as well as a log file. Do not
+ list the log file as an output.
+
+Rationale:
+* Inputs and outputs are a node's public API on the build graph. Not listing
+ "implementation detail"-style outputs prevents other targets from depending on
+ them as inputs.
+* Not listing them also helps to minimize the size of the build graph (although
+ this would be noticeable only for frequently used templates).
+
+#### Where to Place Outputs
+**Option 1:** To make outputs visible in codesearch (e.g. generated sources):
+* use `$target_gen_dir/$target_name.$EXTENSION`.
+
+**Option 2:** Otherwise (for binary files):
+* use `$target_out_dir/$target_name.$EXTENSION`.
+
+**Option 3:** For outputs that are required at runtime
+(e.g. [runtime_deps](https://gn.googlesource.com/gn/+/master/docs/reference.md#runtime_deps)),
+options 1 & 2 do not work because they are not archived in builder/tester bot
+configurations. In this case:
+* use `$root_out_dir/gen.runtime` or `$root_out_dir/obj.runtime`.
+
+Example:
+```python
+# This .json file is used at runtime and thus cannot go in target_gen_dir.
+_target_dir_name = rebase_path(get_label_info(":$target_name", "dir"), "//")
+_output_path = "$root_out_dir/gen.runtime/$_target_dir_name/$target_name.json"
+```
+
+**Option 4:** For outputs that map 1:1 with executables, and whose paths cannot
+be derived at runtime:
+* use `$root_build_dir/YOUR_NAME_HERE/$target_name`.
+
+Examples:
+```python
+# Wrapper scripts for apks:
+_output_path = "$root_build_dir/bin/$target_name"
+# Metadata for apks. Used by binary size tools.
+_output_path = "$root_build_dir/size-info/${invoker.name}.apk.jar.info"
+```
+
+## Best Practices for Python Actions
+Outputs should be atomic and take advantage of `restat=1`.
+* Make outputs atomic by writing to temporary files and then moving them to
+ their final location.
+ * Rationale: An interrupted write can leave a file with an updated timestamp
+ and corrupt contents. Ninja looks only at timestamps.
+* Do not overwrite an existing output with identical contents.
+ * Rationale: `restat=1` is a ninja feature enabled for all actions that
+ short-circuits a build when output timestamps do not change. This feature is
+ the reason that the total number of build steps sometimes decreases when
+ building..
+* Use [`build_utils.AtomicOutput()`](https://cs.chromium.org/chromium/src/build/android/gyp/util/build_utils.py?rcl=7d6ba28e92bec865a7b7876c35b4621d56fb37d8&l=128)
+ to perform both of these techniques.
+
+Actions should be deterministic in order to avoid hard-to-reproduce bugs.
+Given identical inputs, they should produce byte-for-byte identical outputs.
+* Some common mistakes:
+ * Depending on filesystem iteration order.
+ * Writing timestamps in files (or in zip entries).
+ * Writing absolute paths in outputs.
+
+## Style Guide
+Chromium GN files follow
+[GN's Style Guide](https://gn.googlesource.com/gn/+/master/docs/style_guide.md)
+with a few additions.
+
+### Action Granularity
+ * Prefer writing new Python scripts that do what you want over
+ composing multiple separate actions within a template.
+ * Fewer targets makes for a simpler build graph.
+ * GN logic and build logic winds up much simpler.
+
+Bad:
+```python
+template("generate_zipped_sources") {
+ generate_files("${target_name}__gen") {
+ ...
+ outputs = [ "$target_gen_dir/$target_name.temp" ]
+ }
+ zip(target_name) {
+ deps = [ ":${target_name}__gen" ]
+ inputs = [ "$target_gen_dir/$target_name.temp" ]
+ outputs = [ invoker.output_zip ]
+ }
+}
+```
+
+Good:
+```python
+template("generate_zipped_sources") {
+ action(target_name) {
+ script = "generate_and_zip.py"
+ ...
+ outputs = [ invoker.output_zip ]
+ }
+}
+```
+
+### Naming for Intermediate Targets
+Targets that are not relevant to users of your template should be named as:
+`${target_name}__$something`.
+
+Example:
+```python
+template("my_template") {
+ action("${target_name}__helper") {
+ ...
+ }
+ action(target_name) {
+ deps = [ ":${target_name}__helper" ]
+ ...
+ }
+}
+```
+
+This scheme ensures that subtargets defined in templates do not conflict with
+top-level targets.
+
+### Visibility for Intermediate Targets
+
+You can restrict what targets can depend on one another using [visibility].
+When writing templates, with multiple intermediate targets, `visibility` should
+only be applied to the final target (the one named `target_name`). Applying only
+to the final target ensures that the invoker-provided visibility does not
+prevent intermediate targets from depending on each other.
+
+[visibility]: https://gn.googlesource.com/gn/+/master/docs/reference.md#var_visibility
+
+Example:
+```python
+template("my_template") {
+ # Do not forward visibility here.
+ action("${target_name}__helper") {
+ # Do not forward visibility here.
+ ...
+ }
+ action(target_name) {
+ # Forward visibility here.
+ forward_variables_from(invoker, [ "visibility" ])
+ deps = [ ":${target_name}__helper" ]
+ ...
+ }
+}
+```
+
+### Variables
+Prefix variables within templates and targets with an underscore. For example:
+
+```python
+template("example") {
+ _outer_sources = invoker.extra_sources
+
+ source_set(target_name) {
+ _inner_sources = invoker.sources
+ sources = _outer_sources + _inner_sources
+ }
+}
+```
+
+This convention conveys that `sources` is relevant to `source_set`, while
+`_outer_sources` and `_inner_sources` are not.
+
+### Passing Arguments to Targets
+Pass arguments to targets by assigning them directly within target definitions.
+
+When a GN template goes to resolve `invoker.FOO`, GN will look in all enclosing
+scopes of the target's definition. It is hard to figure out where `invoker.FOO`
+is coming from when it is not assigned directly within the target definition.
+
+Bad:
+```python
+template("hello") {
+ script = "..."
+ action(target_name) {
+ # This action will see "script" from the enclosing scope.
+ }
+}
+```
+
+Good:
+```python
+template("hello") {
+ action(target_name) {
+ script = "..." # This is equivalent, but much more clear.
+ }
+}
+```
+
+**Exception:** `testonly` and `visibility` can be set in the outer scope so that
+they are implicitly passed to all targets within a template.
+
+This is okay:
+```python
+template("hello") {
+ testonly = true # Applies to all nested targets.
+ action(target_name) {
+ script = "..."
+ }
+}
+```
+
+### Using forward_variables_from()
+Using [forward_variables_from()] is encouraged, but special care needs to be
+taken when forwarding `"*"`. The variables `testonly` and `visibility` should
+always be listed explicitly in case they are assigned in an enclosing
+scope.
+See [this bug] for more a full example.
+
+To make this easier, `//build/config/BUILDCONFIG.gn` defines:
+```python
+TESTONLY_AND_VISIBILITY = [ "testonly", "visibility" ]
+```
+
+Example usage:
+```python
+template("action_wrapper") {
+ action(target_name) {
+ forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
+ forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+ ...
+ }
+}
+```
+
+If your template defines multiple targets, be careful to apply `testonly` to
+both, but `visibility` only to the primary one (so that the primary one is not
+prevented from depending on the other ones).
+
+Example:
+```python
+template("template_with_multiple_targets") {
+ action("${target_name}__helper) {
+ forward_variables_from(invoker, [ "testonly" ])
+ ...
+ }
+ action(target_name) {
+ forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+ ...
+ }
+}
+```
+
+An alternative would be to explicitly set `visibility` on all inner targets,
+but doing so tends to be tedious and has little benefit.
+
+[this bug]: https://bugs.chromium.org/p/chromium/issues/detail?id=862232
+[forward_variables_from]: https://gn.googlesource.com/gn/+/master/docs/reference.md#func_forward_variables_from
+
+## Useful Ninja Flags
+Useful ninja flags when developing build rules:
+* `ninja -v` - log the full command-line of every target.
+* `ninja -v -n` - log the full command-line of every target without having
+ to wait for a build.
+* `ninja -w dupbuild=err` - fail if multiple targets have the same output.
+* `ninja -d keeprsp` - prevent ninja from deleting response files.
+* `ninja -n -d explain` - print why ninja thinks a target is dirty.
+* `ninja -j1` - execute only one command at a time.