naiveproxy/tools/gn/docs/language.md

541 lines
19 KiB
Markdown
Raw Permalink Normal View History

2018-02-02 13:49:39 +03:00
# GN Language and Operation
[TOC]
## Introduction
This page describes many of the language details and behaviors.
### Use the built-in help!
GN has an extensive built-in help system which provides a reference for
every function and built-in variable. This page is more high-level.
```
gn help
```
You can also see the
[slides](https://docs.google.com/presentation/d/15Zwb53JcncHfEwHpnG_PoIbbzQ3GQi_cpujYwbpcbZo/edit?usp=sharing)
from a March, 2016 introduction to GN. The speaker notes contain the full
content.
### Design philosophy
* Writing build files should not be a creative endeavour. Ideally two
people should produce the same buildfile given the same
requirements. There should be no flexibility unless it's absolutely
needed. As many things should be fatal errors as possible.
* The definition should read more like code than rules. I don't want
to write or debug Prolog. But everybody on our team can write and
debug C++ and Python.
* The build language should be opinionated as to how the build should
work. It should not necessarily be easy or even possible to express
arbitrary things. We should be changing source and tooling to make
the build simpler rather than making everything more complicated to
conform to external requirements (within reason).
* Be like Blaze when it makes sense (see "Differences and similarities
to Blaze" below).
## Language
GN uses an extremely simple, dynamically typed language. The types are:
* Boolean (`true`, `false`).
* 64-bit signed integers.
* Strings.
* Lists (of any other types).
* Scopes (sort of like a dictionary, only for built-in stuff).
There are some built-in variables whose values depend on the current
environment. See `gn help` for more.
There are purposefully many omissions in the language. There are no
user-defined function calls, for example (templates are the closest thing). As
per the above design philosophy, if you need this kind of thing you're probably
doing it wrong.
The variable `sources` has a special rule: when assigning to it, a list
of exclusion patterns is applied to it. This is designed to
automatically filter out some types of files. See `gn help
set_sources_assignment_filter` and `gn help label_pattern` for more.
The full grammar for language nerds is available in `gn help grammar`.
### Strings
Strings are enclosed in double-quotes and use backslash as the escape
character. The only escape sequences supported are:
* `\"` (for literal quote)
* `\$` (for literal dollars sign)
* `\\` (for literal backslash)
Any other use of a backslash is treated as a literal backslash. So, for
example, `\b` used in patterns does not need to be escaped, nor do most Windows
paths like `"C:\foo\bar.h"`.
Simple variable substitution is supported via `$`, where the word
following the dollars sign is replaced with the value of the variable.
You can optionally surround the name with `{}` if there is not a
non-variable-name character to terminate the variable name. More complex
expressions are not supported, only variable name substitution.
```
a = "mypath"
b = "$a/foo.cc" # b -> "mypath/foo.cc"
c = "foo${a}bar.cc" # c -> "foomypathbar.cc"
```
You can encode 8-bit characters using "$0xFF" syntax, so a string with newlines
(hex 0A) would `"look$0x0Alike$0x0Athis"`.
### Lists
There is no way to get the length of a list. If you find yourself
wanting to do this kind of thing, you're trying to do too much work in
the build.
Lists support appending:
```
a = [ "first" ]
a += [ "second" ] # [ "first", "second" ]
a += [ "third", "fourth" ] # [ "first", "second", "third", "fourth" ]
b = a + [ "fifth" ] # [ "first", "second", "third", "fourth", "fifth" ]
```
Appending a list to another list appends the items in the second list
rather than appending the list as a nested member.
You can remove items from a list:
```
a = [ "first", "second", "third", "first" ]
b = a - [ "first" ] # [ "second", "third" ]
a -= [ "second" ] # [ "first", "third", "fourth" ]
```
The - operator on a list searches for matches and removes all matching
items. Subtracting a list from another list will remove each item in the
second list.
If no matching items are found, an error will be thrown, so you need to
know in advance that the item is there before removing it. Given that
there is no way to test for inclusion, the main use-case is to set up a
master list of files or flags, and to remove ones that don't apply to
the current build based on various conditions.
Stylistically, prefer to only add to lists and have each source file or
dependency appear once. This is the opposite of the advice Chrome-team used to
give for GYP (GYP would prefer to list all files, and then remove the ones you
didn't want in conditionals).
Lists support zero-based subscripting to extract values:
```
a = [ "first", "second", "third" ]
b = a[1] # -> "second"
```
The \[\] operator is read-only and can not be used to mutate the
list. The primary use-case of this is when an external script returns
several known values and you want to extract them.
There are some cases where it's easy to overwrite a list when you mean
to append to it instead. To help catch this case, it is an error to
assign a nonempty list to a variable containing an existing nonempty
list. If you want to get around this restriction, first assign the
destination variable to the empty list.
```
a = [ "one" ]
a = [ "two" ] # Error: overwriting nonempty list with a nonempty list.
a = [] # OK
a = [ "two" ] # OK
```
Note that execution of the build script is done without intrinsic
knowledge of the meaning of the underlying data. This means that it
doesn't know that `sources` is a list of file names, for example. So if
you remove an item, it must match the literal string rather than
specifying a different name that will resolve to the same file name.
### Conditionals
Conditionals look like C:
```
if (is_linux || (is_win && target_cpu == "x86")) {
sources -= [ "something.cc" ]
} else if (...) {
...
} else {
...
}
```
You can use them in most places, even around entire targets if the
target should only be declared in certain circumstances.
### Looping
You can iterate over a list with `foreach`. This is discouraged. Most things
the build should do can normally be expressed without doing this, and if you
find it necessary it may be an indication you're doing too much work in the
metabuild.
```
foreach(i, mylist) {
print(i) # Note: i is a copy of each element, not a reference to it.
}
```
### Function calls
Simple function calls look like most other languages:
```
print("hello, world")
assert(is_win, "This should only be executed on Windows")
```
Such functions are built-in and the user can not define new ones.
Some functions take a block of code enclosed by `{ }` following them:
```
static_library("mylibrary") {
sources = [ "a.cc" ]
}
```
Most of these define targets. The user can define new functions like this
with the template mechanism discussed below.
Precisely, this expression means that the block becomes an argument to the
function for the function to execute. Most of the block-style functions execute
the block and treat the resulting scope as a dictionary of variables to read.
### Scoping and execution
Files and function calls followed by `{ }` blocks introduce new scopes. Scopes
are nested. When you read a variable, the containing scopes will be searched in
reverse order until a matching name is found. Variable writes always go to the
innermost scope.
There is no way to modify any enclosing scope other than the innermost
one. This means that when you define a target, for example, nothing you
do inside of the block will "leak out" into the rest of the file.
`if`/`else`/`foreach` statements, even though they use `{ }`, do not introduce
a new scope so changes will persist outside of the statement.
## Naming things
### File and directory names
File and directory names are strings and are interpreted as relative to
the current build file's directory. There are three possible forms:
Relative names:
```
"foo.cc"
"src/foo.cc"
"../src/foo.cc"
```
Source-tree absolute names:
```
"//net/foo.cc"
"//base/test/foo.cc"
```
System absolute names (rare, normally used for include directories):
```
"/usr/local/include/"
"/C:/Program Files/Windows Kits/Include"
```
## Build configuration
## Targets
A target is a node in the build graph. It usually represents some kind
of executable or library file that will be generated. Targets depend on
other targets. The built-in target types (see `gn help <targettype>` for
more help) are:
* `action`: Run a script to generate a file.
* `action_foreach`: Run a script once for each source file.
* `bundle_data`: Declare data to go into a Mac/iOS bundle.
* `create_bundle`: Creates a Mac/iOS bundle.
* `executable`: Generates an executable file.
* `group`: A virtual dependency node that refers to one or more other
targets.
* `shared_library`: A .dll or .so.
* `loadable_module`: A .dll or .so loadable only at runtime.
* `source_set`: A lightweight virtual static library (usually
preferrable over a real static library since it will build faster).
* `static_library`: A .lib or .a file (normally you'll want a
`source_set` instead).
You can extend this to make custom target types using templates (see below). In
Chrome some of the more commonly-used templates are:
* `component`: Either a source set or shared library, depending on the
build type.
* `test`: A test executable. On mobile this will create the appropriate
native app type for tests.
* `app`: Executable or Mac/iOS application.
* `android_apk`: Make an APK. There are a _lot_ of other Android ones, see
`//build/config/android/rules.gni`.
## Configs
Configs are named objects that specify sets of flags, include
directories, and defines. They can be applied to a target and pushed to
dependent targets.
To define a config:
```
config("myconfig") {
includes = [ "src/include" ]
defines = [ "ENABLE_DOOM_MELON" ]
}
```
To apply a config to a target:
```
executable("doom_melon") {
configs = [ ":myconfig" ]
}
```
It is common for the build config file to specify target defaults that
set a default list of configs. Targets can add or remove to this list as
needed. So in practice you would usually use `configs += ":myconfig"` to
append to the list of defaults.
See `gn help config` for more information about how configs are declared
and applied.
### Public configs
A target can apply settings to other targets that depend on it. The most
common example is a third party target that requires some defines or
include directories for its headers to compile properly. You want these
settings to apply both to the compile of the third party library itself,
as well as all targets that use the library.
To do this, you write a config with the settings you want to apply:
```
config("my_external_library_config") {
includes = "."
defines = [ "DISABLE_JANK" ]
}
```
Then this config is added to the target as a "public" config. It will
apply both to the target as well as targets that directly depend on it.
```
shared_library("my_external_library") {
...
# Targets that depend on this get this config applied.
public_configs = [ ":my_external_library_config" ]
}
```
Dependent targets can in turn forward this up the dependency tree
another level by adding your target as a "public" dependency.
```
static_library("intermediate_library") {
...
# Targets that depend on this one also get the configs from "my external library".
public_deps = [ ":my_external_library" ]
}
```
A target can forward a config to all dependents until a link boundary is
reached by setting it as an `all_dependent_config`. This is strongly
discouraged as it can spray flags and defines over more of the build than
necessary. Instead, use public_deps to control which flags apply where.
In Chrome, prefer the build flag header system (`build/buildflag_header.gni`)
for defines which prevents most screw-ups with compiler defines.
## Templates
Templates are GN's primary way to re-use code. Typically, a template
would expand to one or more other target types.
```
# Declares a script that compiles IDL files to source, and then compiles those
# source files.
template("idl") {
# Always base helper targets on target_name so they're unique. Target name
# will be the string passed as the name when the template is invoked.
idl_target_name = "${target_name}_generate"
action_foreach(idl_target_name) {
...
}
# Your template should always define a target with the name target_name.
# When other targets depend on your template invocation, this will be the
# destination of that dependency.
source_set(target_name) {
...
deps = [ ":$idl_target_name" ] # Require the sources to be compiled.
}
}
```
Typically your template definition would go in a `.gni` file and users
would import that file to see the template definition:
```
import("//tools/idl_compiler.gni")
idl("my_interfaces") {
sources = [ "a.idl", "b.idl" ]
}
```
Declaring a template creates a closure around the variables in scope at
that time. When the template is invoked, the magic variable `invoker` is
used to read variables out of the invoking scope. The template would
generally copy the values its interested in into its own scope:
```
template("idl") {
source_set(target_name) {
sources = invoker.sources
}
}
```
The current directory when a template executes will be that of the
invoking build file rather than the template source file. This is so
files passed in from the template invoker will be correct (this
generally accounts for most file handling in a template). However, if
the template has files itself (perhaps it generates an action that runs
a script), you will want to use absolute paths ("//foo/...") to refer to
these files to account for the fact that the current directory will be
unpredictable during invocation. See `gn help template` for more
information and more complete examples.
## Other features
### Imports
You can import `.gni` files into the current scope with the `import`
function. This is _not_ an include in the C++ sense. The imported file is
executed independently and the resulting scope is copied into the current file
(C++ executes the included file in the current context of when the
include directive appeared). This allows the results of the import to be
cached, and also prevents some of the more "creative" uses of includes like
multiply-included files.
Typically, a `.gni` would define build arguments and templates. See `gn
help import` for more.
Your `.gni` file can define temporary variables that are not exported files
that include it by using a preceding underscore in the name like `_this`.
### Path processing
Often you will want to make a file name or a list of file names relative
to a different directory. This is especially common when running
scripts, which are executed with the build output directory as the
current directory, while build files usually refer to files relative to
their containing directory.
You can use `rebase_path` to convert directories. See `gn help
rebase_path` for more help and examples. Typical usage to convert a file
name relative to the current directory to be relative to the root build
directory would be: ``` new_paths = rebase_path("myfile.c",
root_build_dir) ```
### Patterns
Patterns are used to generate the output file names for a given set of
inputs for custom target types, and to automatically remove files from
the `sources` variable (see `gn help set_sources_assignment_filter`).
They are like simple regular expressions. See `gn help label_pattern`
for more.
### Executing scripts
There are two ways to execute scripts. All external scripts in GN are in
Python. The first way is as a build step. Such a script would take some
input and generate some output as part of the build. Targets that invoke
scripts are declared with the "action" target type (see `gn help
action`).
The second way to execute scripts is synchronously during build file
execution. This is necessary in some cases to determine the set of files
to compile, or to get certain system configurations that the build file
might depend on. The build file can read the stdout of the script and
act on it in different ways.
Synchronous script execution is done by the `exec_script` function (see
`gn help exec_script` for details and examples). Because synchronously
executing a script requires that the current buildfile execution be
suspended until a Python process completes execution, relying on
external scripts is slow and should be minimized.
To prevent abuse, files permitted to call `exec_script` can be whitelisted in
the toplevel `.gn` file. Chrome does this to require additional code review
for such additions. See `gn help dotfile`.
You can synchronously read and write files which is discouraged but
occasionally necessary when synchronously running scripts. The typical use-case
would be to pass a list of file names longer than the command-line limits of
the current platform. See `gn help read_file` and `gn help write_file` for how
to read and write files. These functions should be avoided if at all possible.
Actions that exceed command-line length limits can use response files to
get around this limitation without synchronously writing files. See
`gn help response_file_contents`.
# Differences and similarities to Blaze
Blaze is Google's internal build system, now publicly released as
[Bazel](http://bazel.io/). It has inspired a number of other systems such as
[Pants](http://www.pantsbuild.org/) and [Buck](http://facebook.github.io/buck/).
In Google's homogeneous environment, the need for conditionals is very
low and they can get by with a few hacks (`abi_deps`). Chrome uses
conditionals all over the place and the need to add these is the main
reason for the files looking different.
GN also adds the concept of "configs" to manage some of the trickier
dependency and configuration problems which likewise don't arise on the
server. Blaze has a concept of a "configuration" which is like a GN
toolchain, but built into the tool itself. The way that toolchains work
in GN is a result of trying to separate this concept out into the build
files in a clean way.
GN keeps some GYP concept like "all dependent" settings which work a bit
differently in Blaze. This is partially to make conversion from the existing
GYP code easier, and the GYP constructs generally offer more fine-grained
control (which is either good or bad, depending on the situation).
GN also uses GYP names like "sources" instead of "srcs" since
abbreviating this seems needlessly obscure, although it uses Blaze's
"deps" since "dependencies" is so hard to type. Chromium also compiles
multiple languages in one target so specifying the language type on the
target name prefix was dropped (e.g. from `cc_library`).