Skip to content

FAQ: GN

Vincent Coubard edited this page Feb 1, 2021 · 18 revisions

What is GN ?

GN is a build system from Google which is used to build Chrome, their Fuchsia operating system and pigweed, a set of embedded libraries. It is the main build system used by the CHIP project.

GN is a meta build system that generates ninja (GN) files. The build itself is then handled by ninja

How do I get GN ?

GN can be downloaded from its git repository that contains build instructions at the root. GN is automatically available if you open the CHIP project in a VSCode remote container or use the environment setup script by executing source scripts/activate.sh.

Where is GN documentation ?

The GN repository contains various documents in its docs folder. A quick start guide is available as well as an introductory presentation that covers all GN concepts.

To get help on any topic, the command gn help <topic> can be used.

Since the generic gn help command dumps quite a lot of info, you can simply exec an invalid command, e.g. gn foo, to see just the list of supported commands.

How do I generate ninja build files ?

To generate the ninja files used to build the GN project, the command gn gen <path-to-output> should be used. Build files and associated output will go into the selected path. Therefore to have several build flavor of a project, use a different output path for each of them.

How do I build my project ?

Once ninja files have been generated, ninja is used to build the targets. It is invoked with the command ninja -C <path-to-output> [target-label] where path-to-output is the directory containing the ninja files generated with gn gen and target-label indicates the target to build in the source path.

Note that if any gn build file is modified, ninja files are automatically updated by ninja when it is invoked.

How do I list build targets ?

Build targets can be listed with the command gn ls <path-to-output>.

How do I list arguments of my build ?

Build arguments are described in the build files, they're often assigned to default values. To list all the arguments available for a build, use the following command: gn args --list <path-to-output>. The commands prints the name of build arguments, their description and their value for this output path. To see only the non-default values, use: gn args --list --overrides-only <path-to-output>.

How do I override build arguments ?

Build arguments can be defined for a build using the command gn args <path-to-output>. Overrides of the build arguments are stored in the file args.gn located at the root of the build output folder. The default arg values are provided in declare_args() blocks of BUILD.gn files.

What are GN main concepts ?

GN is a meta build generator that generates Ninja files. GN stands for Generate Ninja.

It exposes several concepts that drive a build.

  • BUILD.gn: The main build files are named BUILD.gn. In a GN project, there is usually one BUILD.gn file for each significant folder to build.
  • target: A target is something that the system can build. An executable or a static libraries are targets. A target is identified with a path from the root of the project starting with // and pointing to the associated build file. Targets inside the BUILD.gn files are identified with a colon : and the target name. For example, the target //libfoo/libbar:test indicates that the target is in the BUILD.gn file at libfoo/libbar and is named test.
  • toolchain: A toolchain describe the set of tools and associated invocation required to build and link source files. For examples, cc, cxx or ld are part of the toolchain definition. GN doesn't come with pre-baked toolchain definition. Every project must build their own. Inspiration can be drawn pigweed's toolchain.
  • args: Arguments provided to the build system are defined in the build files.
  • deps: Each target can have a set of public dependencies or private dependencies identified by their target label.
  • config: It is possible to define a set of configuration (include directories, compiler flags, ...) and apply it to a target. These configurations can be put in .gni files to be reused between targets.
  • group: Group several targets together in a named meta target.

How to define the head of a GN project?

Heads of GN projects are defined with a .gn file. When GN is run it looks at the current and parents directory to find a .gn files that indicates the root of the project.

That .gn file must contain a buildconfig that points to the global project build config. That build config sets the default toolchain.

To get more help: gn help dotfile.

How to build a library?

GN support built-in targets such as:

  • executable, shared_library, static_library
  • loadable_module - like a shared library but loaded at runtime
  • source_set - compiles source files with no intermediate library

Therefore, if you want to build your code as a library, you just need to create a specific target with all dependencies.

Simple example:

static_library(“base”) {
  sources = [
    “a.cc”,
    “b.cc”,
  ]

  deps = [
    “//fancypants”,
    “//foo/bar:baz”,
  ]
}

A source set is a collection of sources that get compiled but are not linked to produce any kind of library. Instead, the resulting object files are implicitly added to the linker line of all targets that depend on the source set. More details: GN source set

Static library details: GN static library or gn help static_library

Shared library details: GN shared library or gn help shared_library

How to create an executable target ?

Executables are defined using the builtin target executable. The built in target accept compile and link flags, define, include dirs and dependency. The full list of variable is available by executing gn help executable.

Executable example
executable("hello") {
  # list of source files 
  sources = [ "hello.cc" ]
  # depends on the hello_shared and hello_static target
  deps = [
    ":hello_shared",
    ":hello_static",
  ]
}

How to add dependencies?

Dependencies are the core of GN. Every BUILD.gn file is loaded conditionally -- there has to be a dependency on a label in that directory. No wildcards are allowed. To get built, every target has to be listed as a dependency of some target. You can use a group target type to collect a set of dependencies into one target (default target is a group).

There are a couple of dependency types that may be added to a target definition:

  • deps -- Specify private dependencies of a target. Private dependencies are propagated up the dependency tree and linked to dependent targets, but do not grant the ability to include headers from the dependency. Public configs are not forwarded. See gn help deps.
  • public_deps -- Public dependencies additionally express that the current target exposes the listed deps as part of its public API. Public condigs are forwarded and public headers in the dependency are usable by dependents (includes do not require a direct dependency or visibility). See gn help public_deps.
  • data -- Data files that are runtime dependencies for a given target. Since these are runtime dependencies, they do not affect which targets are built or when. See gn help data and gn help runtime_deps.
  • data_deps -- Non-linked dependencies. This is normally used for things like plugins or helper programs that a target needs at runtime. See gn help data_deps.
  • inputs -- Additional compile-time dependencies. Inputs are typically only used for action and action_foreach targets (input files to a script). See gn help inputs.
See examples.
# This target can include files from "c" but not from
# "super_secret_implementation_details".
executable("a") {
  deps = [ ":b" ]
}

shared_library("b") {
  deps = [ ":super_secret_implementation_details" ]
  public_deps = [ ":c" ]
}
executable("foo") {
  deps = [ "//base" ]
  # Non-linked dependencies that a target needs at runtime.
  data_deps = [ "//plugins:my_runtime_plugin" ]
}
action("myscript") {
  script = "domything.py"
  # Input files to a script.
  inputs = [ "input.data" ]
}

How do I define the target platform?

You can specify a cross-compile build by setting target_os and target_cpu args. By default the build config files will usually set them to the host's values, so e.g. on an x64 Linux machine gn gen <out_dir> is equivalent to gn gen <out_dir> --args='target_os="linux" target_cpu="x64"'.

To do a 64-bit MIPS Chrome OS cross-compile:

gn gen <out_dir> --args='target_os="chromeos" target_cpu="mips64el"'

See gn help target_cpu and gn help target_os.

If you are defining a new toolchain, you are responsible for making sure that current_cpu is set appropriately in your toolchain definitions; if you are using the stock templates like gcc_toolchain and msvc_toolchain, that means you are responsible for making sure that toolchain_cpu and toolchain_os are set as appropriate in the template invocations.

When writing rules in various BUILD.gn files, current_cpu should be used rather than target_cpu most of the time.

## How do I select my toolchain

GN doesn't come with a set of predefined toolchain for a build. The build script is responsible of selecting the right toolchain for the right tasks. Some builds may depends on artifacts that are generated by several toolchains. Therefore, the mechanism to select a specific toolchain is specific to the project being build.

Usually projects use the args target_cpu and target_os to load the appropriate toolchain on behalf of the user using the builtin function set_default_toolchain. This is what CHIP does.

CHIP also defines a special argument to override the default toolchain: custom_toolchain. This variable should point to the path that contains the custom toolchain to use.

How do I inspect or debug the GN environment?

Use the verbose flag -v to follow the GN as it generates ninja files, gn gen <out_dir> -v.

Use gn ls <out_dir> to list the build targets. Knowing the target name is essential for further investigation.

See example output.
$  gn ls out/host/
...
//examples/shell/shell_common:shell_common
//examples/shell/standalone:chip-shell
//examples/shell/standalone:standalone
...

A summary for the given target (from above) can be displayed with gn desc <out_dir> <target_name>. Add a <what_to_show> param for more fine-grained info, e.g.:

  • gn desc <out_dir> <target_name> cflags --blame to show the cflags and check their origin,

    See example output.
      $  gn desc out/host/ //examples/shell/shell_common:shell_common cflags --blame
      From //build/config/compiler:optimize_default
           (Added by //build/config/defaults.gni:31)
        -O0
      ...
      From //build/chip/linux:glib
           (Added by //build/chip/linux/gdbus_library.gni:99)
        -I/usr/include/gio-unix-2.0
        -I/usr/include/libmount
        -I/usr/include/blkid
        -I/usr/include/glib-2.0
        -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
      ...
    
  • gn desc <out_dir> <target_name> ldflags --blame to show the ldflags,

    See example output.
      $  gn desc out/host/ //examples/shell/shell_common:shell_common ldflags --blame
      ...
      From //build/config/compiler:warnings_default
           (Added by //build/config/defaults.gni:58)
        -Wl,--fatal-warnings
        -Werror
        -Wl,-z,defs
      ...
    
  • gn desc <out_dir> <target_name> defines --blame to show the defines,

    See example output.
      $  gn desc out/host/ //examples/shell/shell_common:shell_common defines --blame
      From //examples/shell/shell_common:shell_common_config
           (Added by //examples/shell/shell_common/BUILD.gn:55)
        ENABLE_CHIP_SHELL
        OPENTHREAD_CONFIG_CLI_TRANSPORT=OT_CLI_TRANSPORT_CONSOLE
      From //src:includes
           (Added by //src/app/BUILD.gn:54)
        CHIP_HAVE_CONFIG_H=1
        CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE=true
      From //src/lwip:lwip_base_config
           (Added by //third_party/lwip/lwip.gni:297)
        LWIP_IPV4=1
        LWIP_IPV6=1
      ...
    
  • gn desc <out_dir> <target_name> deps --tree to show a dependency tree.

    See example output.
      $  gn desc out/host/ //examples/shell/shell_common:shell_common deps
      //src/lib:lib
      //src/lib/shell:shell
      //src/setup_payload:setup_payload
    
      $  gn desc out/host/ //examples/shell/shell_common:shell_common deps --tree
      //src/lib:lib
        //src/app:app
          //src/app:codec_headers
          //src/lib/support:support
            //src/ble:ble_config_header
              //src/ble:ble_buildconfig
                //src/ble:gen_ble_buildconfig
        ...
      //src/lib/shell:shell
        //src/lib/core:core...
        //src/lib/support:support...
      //src/setup_payload:setup_payload...
    

To find all targets that depend on the given target, use gn refs <out_dir> <target_name>.

See example output.
$  gn refs out/host/ //src/lib/shell:shell
//:default
//examples/shell/shell_common:shell_common
//examples/shell/standalone:chip-shell
//src/lib/shell/tests:tests_common

To see all targets that list a given file in their sources, use gn refs <out_dir> <file_name>.

See example output.
$  gn refs out/host/ src/ble/BleConfig.h
//src/ble:ble
//src/ble:ble_config_header

You can also discover the paths of dependencies between two targets. To do that, use gn path <out_dir> <target_one> <target_two>.

See example output.
$  gn path out/host/ //examples/shell/shell_common:shell_common //src/ble:ble_config_header
//examples/shell/shell_common:shell_common --[public]-->
//src/lib:lib --[public]-->
//src/ble:ble --[public]-->
//src/ble:ble_config_header

Showing one of 18 "interesting" non-data paths. 18 of them are public.

Apart from the verbose flag mentioned above, you can use the print() command in any of the BUILD.gn files to dump arbitrary data, e.g.

executable("hello") {
  print(configs)
}