内容
运行 GN
GN 是基于命令行运行的,大型项目中,GN 可与源代码一起进行版本控制和分发。
- 在 Chromium 和基于 Chromium 的项目中,GN 编译构建所需相关工具已被封装在
depot_tools
工具包中。因此,为了使我们自己项目具备 GN 能力,首先要安装depot_tools
工具包 (里面包含gclient、gcl、gn和ninja等工具),然后将其目录路径添加到我们机器的环境变量 PATH 中,之后即可在包含当前目录的源代码树中使用 GN 工具对项目进行编译构建(通过执行源码树中层层 .gn 文件进行)。 - 对于其他项目,请参阅您的项目文档。
建立一个构建
与其他一些构建系统不同,GN 可以让你按需设置所需要的构建目录,这可以方便你根据需要并行维护尽可能多的不同构建。 一旦创建好了构建目录,当您在该目录中编译构建时,如果因为代码修改导致 Ninja 文件已过期,它们也可以自动重新生成,因此您可以不必重新运行 GN 命令。 要创建构建目录,可以执行以下命令:
bash
gn gen out/my_build
这样就在当前目录下生成了构建目录** out/my_build**。
传递构建参数
你还可以在创建构建目录时按需设置构建参数:
bash
gn args out/my_build
这样会打开一个编辑器,你可以在该文件中键入所需的构建参数 (build args),例如:
ini
is_component_build = true
is_debug = false
可设参数变量取决于您自己的构建(上述示例来自 Chromium)。您可以以下命令来查看所在项目中的可设参数及其默认值的列表:
bash
gn args --list out/my_build
注意,使用上述 list 命令时您必须指定构建目录,因为可用参数可能会因不同构建而改变。 Chrome 开发人员可以阅读 Chrome 特定的构建配置指引 以获取更多信息。
一步一步来
首先克隆这个 repo:
bash
git clone https://gn.googlesource.com/gn
添加构建文件 clone 完成后,进入目录 examples/simple_build
这是这个 GN 示例的根目录,我们将在这个目录下进行下面的编译构建。 在该目录中有一个 tutorial
目录,在其下已经有一个还添加到当前 GN 构建层次的文件 tutorial.cc
,该目录下我们可以手动创建一个新的 BUILD.gn
文件,以指定我们需要编译的新目标** tutorial**,文件内容如下:
javascript
executable("tutorial") {
sources = [
"tutorial.cc",
]
}
这样我们就定义了一个编译目标(是一个可执行文件 ** tutorial**),下一步我们开始编译构建这个目标。 打开上一层父目录simple_build
中的BUILD.gn
文件。GN 会首先加载这个根文件,然后从这里加载我们所有的依赖项,所以我们只需要在这个文件中添加一个对我们定义的上述新目标的引用。
我们可以将这个新目标作为一个依赖添加为 simple_build/BUILD.gn
文件中现有目标之一的依赖项,但注意,将可执行文件作为另一个可执行文件的依赖项通常没有多大意义(它们不能链接)。因此,让我们创建一个 名为 "tools" 的 group (在 GN 中,"group" 指的是未编译或链接的依赖项集合,tools 是自定义的名字):
javascript
group("tools") {
deps = [
# 这将自动扩展为 "//tutorial:tutorial",即上述新目标的全名。
"//tutorial",
]
}
测试您的添加
在 simple_build
目录下运行以下命令:
csharp
gn gen out
ninja -C out tutorial
这样,我们就完成编译了一个可执行文件** tutorial**。执行该文件,你应该可以看到 "Hello from the tutorial." 输出到控制台:
bash
out/tutorial
注:GN 鼓励静态库的目标名称不必要保证全局唯一,如果要构建其中之一,可以指定特定的路径(不包括前导"//")进行 ninja 编译:
sql
ninja -C out some/path/to/target:my_target
声明依赖
可以看到,在 examples/simple_build/BUILD.gn
中定义的目标中,有一个静态库定义了一个函数 GetStaticText()
:
javascript
static_library("hello_static") {
sources = [
"hello_static.cc",
"hello_static.h",
]
}
还有一个动态库定义了一个函数GetSharedText()
:
ini
shared_library("hello_shared") {
sources = [
"hello_shared.cc",
"hello_shared.h",
]
defines = [ "HELLO_SHARED_IMPLEMENTATION" ]
}
上面代码也展示了如何为一个编译目标设置预编译宏(即上述 defines
内容),如果需要设置多个宏以及为其设定默认值,可以使用如下形式:
ini
defines = [
"HELLO_SHARED_IMPLEMENTATION",
"ENABLE_DOOM_MELON=0",
]
现在让我们看看需要依赖上述两个库进行编译的可执行目标 hello:
ini
executable("hello") {
sources = [
"hello.cc",
]
deps = [
":hello_shared",
":hello_static",
]
}
可以看到,编译此目标依赖一个源文件 hello.cc 以及前述两个库。注意,deps 中的冒号表示的是后面目标是在当前 BUILD.gn 文件已经定义了。
测试二进制文件
在 simple_build
目录下运行以下命令:
csharp
ninja -C out hello
out/hello
请注意,您并不需要重新运行 gn 命令,当任何编译构建所需文件发生更改时,GN 将自动重建 ninja 文件(正如执行开始时 ninja 打印的 [1/1] Regenerating ninja files
所指示的)。
将设置放到 config 中
库的用户通常需要设置一些编译器 flags、defines 和 include 目录,我们可以把所有此类设置放入一个 "config" ,它是一个自命名的配置集合(不是源或依赖项):
ini
config("my_lib_config") {
defines = [ "ENABLE_DOOM_MELON" ]
include_dirs = [ "//third_party/something" ]
}
要将配置好的 config 应用于编译构建目标,可以将其添加到 configs 列表中:
javascript
static_library("hello_shared") {
...
# 注意这里通常需要"+=",请参阅下面的"默认配置"。
configs += [
":my_lib_config",
]
}
此外,通过将其放在 public_configs
列表中,可以将所设置的配置应用于依赖于当前目标的所有目标的编译构建中:
javascript
static_library("hello_shared") {
...
public_configs = [
":my_lib_config",
]
}
public_configs
同样适用于当前的目标,所以没有必要同时在两个地方列出同一个配置。
默认配置
默认情况下,构建配置将设置一些适用于每个目标的设置,这些通常会被设置为默认的配置列表,您可以使用 print 命令来查看这些配置(这对调试很有用):
scss
executable("hello") {
print(configs)
}
运行 GN ,则将打印如下内容:
csharp
$ gn gen out
["//build:compiler_defaults", "//build:executable_ldconfig"]
Done. Made 5 targets from 5 files in 9ms
在编译定义目标中,可以修改此列表以更改其默认值。例如,构建设置可能会通过添加 no_exceptions
配置默认关闭异常,但在目标中我们可以通过将其替换为不同的配置来重新启用它们:
ini
executable("hello") {
...
configs -= [ "//build:no_exceptions" ] # 删除 no_exceptions 全局默认配置
configs += [ "//build:exceptions" ] # 替换为不同的配置
}
我们上面的打印命令也可以使用字符串插值来表达。这是一种将值转换为字符串的方法。它使用符号 $ 来指代一个变量:
bash
print("The configs for the target $target_name are $configs")
添加一个新的构建参数
通过此命令,您可以通过 declare_args
声明接受哪些参数并为它们指定默认值:
ini
declare_args() {
enable_teleporter = true
enable_doom_melon = false
}
在给定范围内多次声明给定参数是错误的,因此在确定范围和命名参数时应小心。
不知道正在发生什么?
您可以在 verbose 模式下运行 GN 命令查看关于正在执行的操作的消息(增加使用 -v 即可)。
"desc" 命令
您可以运行 gn desc <build_dir> <targetname>
以获取编译目标的有关信息:
sql
gn desc out/Default //foo/bar:say_hello
以上命令将打印出关于 //foo/bar:say_hello
目标的所有信息。
您也可以只打印一部分。比如想知道 TWO_PEOPLE
宏定义来自 say_hello
目标的何处,下面命令会打印出 //foo/bar:say_hello
的宏定义集,它会展示每个宏定义来自来自哪里:
csharp
> gn desc out/Default //foo/bar:say_hello defines --blame
...lots of other stuff omitted...
From //foo/bar:hello_config
(Added by //foo/bar/BUILD.gn:12)
TWO_PEOPLE
另一种打印方式:
sql
gn desc out/Default //base:base_i18n deps --tree
将打印出 base_i18n
目标在当前目录中的依赖关系树信息。 可以执行 gn help desc
获取更多 desc 用法 。