GN has extensive built-in help, so you can run gn help
, but you can also see all of the help on the reference page. See also the quick start guide and the language and operation details.
You can generate skeleton (or wrapper) projects for Xcode, Visual Studio, QTCreator, and Eclipse that will list the files and targets in the build, but use Ninja to do the actual build. You cannot generate “real” projects that look and compile like native ones.
Run gn help gen
for more details.
GN has robust support for doing cross compiles and building things for multiple architectures in a single build.
See GNCrossCompiles for more info.
Yes! If you create a group target called “default” in the top-level (root) build file, i.e., “//:default”, GN will tell Ninja to build that by default, rather than building everything.
There's at least one, from 2015. There haven't been big changes since then apart from moving it to a standalone repo, so it should still be relevant.
The final values of compiler flags, linker flags, defines, and include directories are collected from various sources by GN. The ordering is defined as:
configs
on the target in order that the configs appear in the list.all_dependent_configs
on the target in order that the configs appear in the list.public_configs
on the target in order that those configs appear in the list.all_dependent_configs
pulled from dependencies, in the order of the deps
list. This is done recursively. If a config appears more than once, only the first occurrence will be used.public_configs
pulled from dependencies, in the order of the deps
list. If a dependency is public, they will be applied recursively.If you need a specific relative ordering of values you may need to put those flags in a config and prepend or append that config in a way that produces the desired result.
The main way that information flows up the dependency graph is via public_configs
. This allows a target to add preprocessor defines, compiler flags, and linker flags to targets that depend on it. A typical example is a library that requires include_dirs
and defines
to be set in a certain way for its headers to work. It would put its values in a config:
config("icu_config") { include_dirs = [ "//third_party/icu" ] defines = [ "U_USING_ICU_NAMESPACE=0" ] }
The library would then reference that as a public_config
which will apply it to any target that directly depends on the icu
target:
shared_library("icu") { sources = [ ... ] deps = [ ... ] public_configs = [ ":icu_config" ] # Label of config defined above. }
A public_config
applies only to direct dependencies of the target. If a target wants to “republish” the public_configs
from its dependencies, it would list those dependencies in its public_deps
. In this example, a “browser” library might use ICU headers in its own headers, so anything that depends on it also needs to get the ICU configuration:
shared_library("browser") { ... # Anything that depends on this "browser" library will also get ICU's settings. public_deps = [ "//third_party/icu" ] }
Another way apply settings up the dependency graph is with all_dependent_configs
which works like public_configs
except that it is applied to all dependent targets regardless of deps
/public_deps
. Use of this feature is discouraged because it is easy to accumulate lots of unnecessary settings in a large project. Ideally all targets can define which information their dependencies need and can control this explicitly with public_deps
.
The last way that information can be collected across the dependency graph is with the metadata feature. This allows data (see gn help metadata
) to be collected from targets to be written to disk (see gn help generated_file
) for the build to use. Computed metadata values are written after all BUILD.gn files are processed and are not available to the GN script.
Sometimes people want to write conditional GN code based on values of a dependency. This is not possible: GN has no defined order for loading BUILD.gn files (this allows pararellism) so GN may not have even loaded the file containing a dependency when you might want information about it. The only way information flows around the dependency graph is if GN itself is propagating that data after the targets are defined.
Sometimes you might have a dependency graph A 🠲 Z or a longer chain A 🠲 B 🠲 C 🠲 Z and want to control some aspect of Z when used from A. This is not possible in GN: information only flows up the dependency chain.
Every label in GN is compiled once per toolchain. This means that every target that depends on B gets the same version of B. If you need different variants of B there are only two options:
Explicitly define two similar but differently named targets encoding the variations you need. This can be done without specifying everything twice using a template or by writing things like the sources to a variable and using that variable in each version of the target.
Use different toolchains. This is commonly used to encode “host” versus “target” differences or to compile parts of a project with something like ASAN. It is possible to use toolchains to encode any variation you might desire but this can be difficult to manage and might be impossible or discoraged depending on how your project is set up (Chrome and Fuchsia use toolchains for specific purposes only).
Sometimes people want to write a build action that expresses copying all files (possibly recursively, possily not) from a source directory without specifying all files in that directory in a BUILD file. This is not possible to express: correct builds must list all inputs. Most approaches people try to work around this break in some way for incremental builds, either the build step is run every time (the build is always “dirty”), file modifications will be missed, or file additions will be missed.
One thing people try is to write an action that declares an input directory and an output directory and have it copy all files from the source to the destination. But incremental builds are likely going to be incorrect if you do this. Ninja determines if an output is in need of rebuilding by comparing the last modified date of the source to the last modified date of the destination. Since almost no filesystems propagate the last modified date of files to their directory, modifications to files in the source will not trigger an incremental rebuild.
Beware when testing this: most filesystems update the last modified date of the parent directory (but not recursive parents) when adding to or removing a file from that directory so this will appear to work in many cases. But no modern production filesystems propagate modification times of the contents of the files to any directories because it would be very slow. The result will be that modifications to the source files will not be reflected in the output when doing incremental builds.
Another thing people try is to write all of the source files to a “depfile” (see gn help depfile
) and to write a single stamp file that tracks the modified date of the output. This approach also may appear to work but is subtly wrong: the additions of new files to the source directory will not trigger the build step and that addition will not be reflected in an incremental build.
The “gn check” feature (see gn help check
) validates that the source code's use of header files follows the requirements set up in the build. It can be a very useful tool for ensuring build correctness.
GN scans the source code for #include
directives and checks that the included files are allowed given the specification of the build. But it is relatively simplistic and does not understand the preprocessor. This means that some headers that are correctly included for a different build variant might be flagged by GN. To disable checking of an include, append a “nogncheck” annotation to the include line:
#if defined(OS_ANDROID) #include "src/android/foo/bar.h" // nogncheck #endif
Correctly handling these cases requires a full preprocessor implementation because many preprocessor conditions depend on values set by other headers. Implementing this would require new code and complexity to define the toolchain and run the preprocessor, and also means that a full build be done before doing the check (since some headers will be generated at build-time). So far, the complexity and disadvantages have outweighed the advantages of a perfectly correct “gn check” implementation.
The “gn check” feature (see previous question) only checks for headers that have been declared in the current toolchain. So if your header never appears in a sources
or public
list, any file will be able to include it without “gn check” failing. As a result, targets should always list all headers they contain even though listing them does not affect the build.
Sometimes a feature request is made to flag unknown headers so that people will know they should be added to the build. But the silent omission of headers outside of the current toolchain is an important feature that limits the necessity of “nogncheck” annotations (see previous question).
In a large project like Chrome, many platform-specific headers will only be defined in that platform‘s build (for example, Android-specific headers would only be listed in the build when compiling for Android). Because the checking doesn’t understand the preprocessor, checking unknown files would flag uses of these headers even if they were properly guarded by platform conditionals. By ignoring headers outside of the current toolchain, the “nogncheck” annotations can be omitted for most platform-specific files.