GN and Ninja are documented here:
[TOC]
List all files read (or executed) by an action as inputs
.
action_with_pydeps
to ensure all dependent Python files are captured
as inputs.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.
inputs
.
«gn analyze» is used by bots to run only affected tests and build only affected targets. Try it out locally via:
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
inputs
to a
dependent target, analyze will be correct.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.inputs
to a
dependent target, a few options exist:jinja_template()
does this.read_file()
to read
them in.
action_with_pydeps()
does this.exec_script()
to compute them when
compute_inputs_for_analyze
is set.
grit()
does this.Do not list files as outputs
unless they are important. Outputs are important
if they are:
Example:
Rationale:
Option 1: To make outputs visible in codesearch (e.g. generated sources):
$target_gen_dir/$target_name.$EXTENSION
.Option 2: Otherwise (for binary files):
$target_out_dir/$target_name.$EXTENSION
.Option 3: For outputs that are required at runtime (e.g. runtime_deps), options 1 & 2 do not work because they are not archived in builder/tester bot configurations. In this case:
$root_out_dir/gen.runtime
or $root_out_dir/obj.runtime
.Example:
# 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:
$root_build_dir/YOUR_NAME_HERE/$target_name
.Examples:
# 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"
Outputs should be atomic and take advantage of restat=1
.
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..build_utils.AtomicOutput()
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.
Chromium GN files follow GN’s Style Guide with a few additions.
Bad:
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:
template("generate_zipped_sources") {
action(target_name) {
script = "generate_and_zip.py"
...
outputs = [ invoker.output_zip ]
}
}
Targets that are not relevant to users of your template should be named as:
${target_name}__$something
.
Example:
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.
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.
Example:
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" ]
...
}
}
Prefix variables within templates and targets with an underscore. For example:
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.
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:
template("hello") {
script = "..."
action(target_name) {
# This action will see "script" from the enclosing scope.
}
}
Good:
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:
template("hello") {
testonly = true # Applies to all nested targets.
action(target_name) {
script = "..."
}
}
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:
TESTONLY_AND_VISIBILITY = [ "testonly", "visibility" ]
Example usage:
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:
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.
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.