| # 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()`. |