blob: 81186e7279400110bc45bdf69deb571b73d238c9 [file] [log] [blame] [view]
# GN in Zircon
This discussion assumes basic familiarity with GN syntax and concepts.
[This introduction to GN](intro.md) can provide that background.
GN uses a templating structure to abstract many of the build details away from
the end user. Below are a subset of the templates the Zircon GN defines,
focusing on the ones with which Zircon hackers are most likely to interact.
## `$zx/` prefix
As discussed in [the introduction](intro.md), GN uses "source-absolute" paths
that look like `//a/b/c`. In the Zircon GN files, we **never** use `//`.
Instead, use `$zx/foo` to refer to `//zircon/foo`,
e.g. `"$zx/system/ulib/zircon"`.
## `executable()` and `test()`
The primary target type in producing a binary is `executable()`. This produces
an executable binary from the listed sources. The Zircon build also provides a
means to indicate the location in the image wherein that binary should be
installed via the `install_path` variable in the target scope.
`install_path` can be:
* a string: the path relative to the root of the BOOTFS (with no leading `/`)
* omitted: use the default path of `bin/<binary_name>`
* `false`: do not install this file at all
The build also provides a `test()` target, which is identical to
`executable()` except that it sets `testonly = true` and that its default
`install_path` is `test/<binary_name>` instead of `bin/<binary_name>`.
`test()` can be used for a test program that runs on Zircon or for a test
program that runs on the host side. In fact, the same `test()` target can
serve to build the same test program for both situations with no extra work
required. (It's just what dependency paths reach that target that will
determine whether it's built for host or for Zircon or for both.)
## `library()`
The `library()` template is for any kind of "library" in the Zircon tradition,
whether for the kernel, Zircon user code, or host-side code. The basic thing
it means to be a "library" is that there is an `include/` subdirectory of
public header files. Dependents that list this `library()` target in their
`deps` will automatically get `-I` switches for that `include/` directory.
The default case with the most concise syntax is a static-only userland
library. Making a library available as a shared library just requires adding
the line `shared = true`. Likewise, making a library available for host-side
use just requires adding the line `host = true`. These are in addition to the
default `static = true` that makes the library available for userland static
linking. For a library that should *never* be statically linked (aside from
host-side or kernel uses), you can override the default with `static = false`.
For a library in the kernel, set `kernel = true`. This is the same whether
it's a kernel-only library, or is code shared between kernel and user (and/or
host). Setting `kernel = true` changes the default to `static = false`, so if
a library can be used either in the kernel or in userland, then you must set
`static = true` explicitly alongside `kernel = true` (unless you set `shared =
true` and want to prohibit static linking of that library in userland).
Note: For kernel modules that do not provide an `include/` subdirectory,
use [`source_set()`](#source_set) instead of `library()`.
Here’s an exemplar showing all the essential options. Most actual targets
will be little more than a `sources` list and a `deps` list.
```gn
library("foo") {
# Builds "libfoo.a" when static, "libfoo.so" when shared.
static = true # default, omitted unless kernel = true: build userland libfoo.a
shared = true # false if omitted: build userland libfoo.so
kernel = true # false if omitted: can be used from kernel
host = true # false if omitted: can be used in host tools
sources = [
"foo.c",
"bar.cpp",
]
deps = [
# Can refer here to `source_set()` or other `library()` targets defined
# locally.
":foo_minimal", # Defined in this same BUILD.gn file.
"foobar_subsystem", # Defined in foobar_subsystem/BUILD.gn relative to here.
# Explicitly link in static libbar.a even if libbar.so is available.
"$zx/system/ulib/bar:static",
# Be explicit about getting libbaz.so as a shared library.
"$zx/system/ulib/baz:shared",
# Compile with -Isystem/ulib/bozo/include, but don't link anything in.
# This should usually not be used in `deps`, but only in `public_deps`.
# See below.
"$zx/system/ulib/bozo:headers",
# Let system/ulib/quux/BUILD.gn decide whether static or shared is the
# norm for that library. (So far the defining `library()` will always
# prefer the shared library if it's enabled; it would be easy to add the
# option to build shared but default to static if that's ever useful.)
"$zx/system/ulib/quux",
# `library("quextras")` appears in system/ulib/quux/BUILD.gn because quux
# and quextras want to share some private source code or for whatever
# reason we've decided putting them in a single directory is right.
# Because we're not using the target with the name of its directory,
# the `:name` syntax selects the specific target within that BUILD.gn file.
# For the derived target names, we use `.` before the suffix.
# In fact, "quux:headers" is just an alias for "quux:quux.headers", etc.
"$zx/system/ulib/quux:quextras",
"$zx/system/ulib/quux:quextras_more.static",
"$zx/system/ulib/quux:quextras_way_more.shared",
# This is a `library()` that will set `static=false shared=true`
# so `zircon:static` here wouldn't work but `zircon:shared` would work.
"$zx/system/ulib/zircon",
]
# Per-module compilation flags are always optional.
# *Note*: For cases where the flag order matters, it may be necessary
# to use a config() instead.
cflags = [ "-Wfoo", "-fbar" ]
cflags_cc = [ "-fonly-for-c++" ]
cflags_c = [ "-fonly-for-c" ]
asmflags = [ "-Wa,--some-as-switch" ]
ldflags = [ "-Wl,--only-affects-shlib-link" ]
}
```
A heavily abridged real-world example of a kernel module:
```gn
# deps = [ "$zx/kernel/object" ] gets -Ikernel/object/include
library("object") {
kernel = true
sources = [
"buffer_chain.cpp",
"process_dispatcher.cpp",
]
deps = [
"$zx/kernel/dev/interrupt",
"$zx/system/ulib/fbl",
]
}
```
Note `system/ulib/fbl` is not `kernel/lib/fbl`: the one `fbl` serves
all. Here's a heavily abridged example for that case:
```gn
library("fbl") {
kernel = true
static = true
sources = [
"alloc_checker.cpp",
]
if (is_kernel) {
sources += [
"arena.cpp",
"arena_tests.cpp",
]
} else {
sources += [ "string.cpp" ]
}
}
```
The actual `fbl` is a bad example because it has other complications, but this
demonstrates how a library of shared code can be maintained in one place with
one `BUILD.gn` file using one library target to describe both the kernel and
userland incarnations. They share everything, but can differ as needed based
on `is_kernel` conditionals.
Libraries define a standard set of targets (if relevant):
* `$target_name.headers`
is always provided, for just getting the headers and not linking it in
* `$target_name.static`
is provided if `static = true` (the default)
* `$target_name.shared`
is provided if `shared = true`
If the library is the main target in the file (e.g. `$zx/foo:foo`)--the common
case--the `static`, `shared`, and `headers` sub-targets are aliased into
`$zx/foo:static`, `$zx/foo:shared`, and `$zx/foo:headers`.
### `public_deps` for header dependencies
In addition to `deps` and `data_deps`, GN also has `public_deps`. This is used
when a target exposes a dependency in its public header files and needs to
forward that dependency's settings up the dependency chain. Every use of
`public_deps` should have a comment explaining why it's needed:
For example, `library("async-loop")` contains this:
```gn
public_deps = [
# <lib/async-loop/loop.h> has #include <lib/async/dispatcher.h>.
"$zx/system/ulib/async:headers",
]
```
## `source_set()` and `static_library()`
Some code that doesn't have an include directory can just use the
native GN `source_set()` or `static_library()` targets.
A source set (see `gn help source_set`) is a way to create a logical grouping
of files or to scope compilation switches narrowly. The object files will be
linked directly into final binaries without going through any intermediate
libraries. In contrast, the files in a static library are only pulled in
as-needed to resolve symbols.
* Code in the kernel itself should always use `source_set`. Static libraries
currently interact poorly with inline assembly.
* A `source_set` *must* be used when creating groups of tests since the
test harness depends on static initializers while the static library
linking rules will strip the tests. All kernel code.
* A `static_library` should be used for higher-level things that looks like
libraries or a part of one. The dead code stripping is more efficient which can
produce faster links and smaller binaries in cases where some code isn't
needed.
```gn
source_set("some_code") {
sources = [
"this.c",
"that.cpp",
]
}
```
## `loadable_module()`
This is not really used in the Zircon build so far, but could be. A loadable
module is a shared object that's not linked directly but rather loaded
dynamically via `dlopen()` or the like.
`loadable_module()` takes the `install_path` parameter like `executable()`
does. But it has no default path, so it's like `install_path = false` unless
you supply a path explicitly.
Zircon device drivers are loadable modules, but they have their own special
templates that should be used instead of `loadable_module()`.
## `driver()` and `test_driver()`
Drivers are loadable modules with some special support and constraints.
* They get a default `install_path` appropriate for drivers, so they will be
found by `devmgr`.
* They implicitly depend on `libdriver` so it shouldn't be listed in `deps`.
* They implicitly use the static C++ standard library.
`test_driver()` is to `driver()` as `test()` is to `executable()`.
```gn
driver("fvm") {
sources = [
"fvm.cpp",
]
deps = [
"$zx/system/ulib/ddktl",
"$zx/system/ulib/fs",
"$zx/system/ulib/zircon",
]
}
```
### `resources()` and `firmware()`
A `resource()` target declares some file that might be needed in the BOOTFS
image, but doesn’t directly cause anything to happen in the build. The style
of the rule is as if it’s a copy from a source file to an output file in the
build; it’s modelled on GN’s native `copy()` rule, and `gn help copy` explains
why its syntax is exactly the way it is. `outputs` is single-element list
containing a path in the BOOTFS.
```gn
import("$zx/public/gn/resource.gni")
resource("tables") {
sources = [
"data.tbl",
]
outputs = [
"data/some_lib/data_v1.tbl",
]
}
```
The purpose of `resource()` is to be listed in the `data_deps` of the target
that uses the data:
```gn
library("uses_tables") {
sources = [
"read_table.cc",
]
data_deps = [
":tables",
]
}
```
This can be a `library()`, an `executable()`, a `source_set()`, etc. Good
practice is to put the `data_deps` in the finest-grained target that holds the
code that uses the file at runtime. Doing so ensures that the relevant
resource will be available at runtime.
If the resource is generated by the build, then the path in the `sources` list
identifies its location in the build directory, usually using
`$target_out_dir` or `$target_gen_dir`. In that case, the `resource()` must
also have a `deps` list that includes the target that generates that file.
The build also allows for a special type of resource that is generated from
the dependency graph. Using `generated_resource()` creates a resource file
that is intended for use in `data_deps`, as in a normal `resource()`, but
instead of using an existing source file it will generate a file at `gn gen`
time with fixed contents or based on a metadata collection (see `gn help
generated_file` for details).
`firmware()` is a special-case variant of `resource()`, intended for drivers.
It places the resource in `/lib/firmware/$path`, where `$path` is a relative
path to the resource in the `/lib/firmware` root. This mimics the calling
convention in `devhost`, where a driver calls `load_firmware(...)` on a
relative path.
## `fidl_library()`
This template allows the definition of a FIDL library and its associated
bindings. Declaring a `fidl_library()` target will cause the build to
generate bindings for all supported languages.
Note: To use this template, you must import the `fidl.gni` file scope.
```gn
import("$zx/public/gn/fidl.gni")
# Defined in $zx/system/fidl/fuchsia-io/BUILD.gn
fidl_library("fuchsia-io") {
sources = [
"io.fidl",
]
public_deps = [
"$zx/system/fidl/fuchsia-mem",
]
}
```
Note the use of [`public_deps`](#public_deps). When a FIDL library's source
files have `using other_library;` that's equivalent to a C/C++ library using
`#include <other_library/header>` in its public headers. Since this is very
common for FIDL (and Banjo) libraries, we don't require comments on every case
when it follows this simple pattern.
Depending on which bindings are defined, the above example will generate a set
of targets of the form `$zx/system/fidl/fuchsia-io:fuchsia-io.<language>`, or,
in the case where the target name is the same as the directory name as above,
`$zx/system/fidl/fuchsia-io:<language>`.
The common case today is `"$zx/system/fidl/fuchsia-io:c"`.
## `banjo_library()`
The definition of Banjo libraries is similar to that of FIDL libraries. A
`banjo_libary()` target will generate bindings for all supported languages,
though the set of supported languages will be different from that of FIDL.
```gn
import("$zx/public/gn/banjo.gni")
banjo_library("ddk-driver") {
sources = [
"driver.banjo",
]
}
```
Currently, listing the plain target with no `:<language>` suffix in `deps`
gets both the C and C++ bindings. This will probably change in the near
future to more closely follow the FIDL model: specify exactly which bindings
you depend on.
See above about `public_deps`. Its use in `banjo_library()` is exactly like
its use in `fidl_library()`.