GN 快速入门指北

内容

运行 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 用法 。

相关推荐
熬夜学编程的小王几秒前
【C++篇】解锁C++模板的魔法:从万能钥匙到精准雕刻
c++·进阶模版·c++模版·类模版实例化·函数模版实例化
Octopus207710 分钟前
【C++】读取数量不定的输入数据
开发语言·c++·笔记·学习
忘梓.11 分钟前
C嘎嘎探索篇:栈与队列的交响:C++中的结构艺术
c语言·开发语言·c++·
十五年专注C++开发23 分钟前
C++中的链式操作原理与应用(一)
开发语言·c++·设计模式
PaLu-LI34 分钟前
ORB-SLAM2源码学习:Initializer.cc:Initializer::CheckFundamental地图初始化——检查基础矩阵F并评分
c++·opencv·学习·线性代数·ubuntu·计算机视觉·矩阵
9毫米的幻想39 分钟前
【Linux系统】—— 基本指令(四)
linux·c语言·c++·学习
waves浪游44 分钟前
类和对象(中)
c语言·开发语言·数据结构·c++·算法·链表
做人不要太理性1 小时前
【算法一周目】滑动窗口(2)
c++·算法·leetcode·哈希算法·散列表·滑动窗口
汤姆和杰瑞在瑞士吃糯米粑粑1 小时前
【优先算法学习】双指针--结合题目讲解学习
c++·学习·算法
chian-ocean1 小时前
从零开始:Linux 环境下的 C/C++ 编译教程
linux·c语言·c++