概要
本文档介绍了“build 变体”的实现,build 变体是 Android Studio 的一项功能, Fuchsia 构建系统允许构建插桩或 特别优化的主机和设备二进制文件版本。
读者必须熟悉 GN toolchain() 实例 并应已阅读以下文档:
build 变体概览
Fuchsia build 定义了几种类型的 build 变体,例如:
asan
和ubsan
变体用于通过 Clang 的 Address Sanitizer 和 分别为未定义的行为排错程序。 甚至还有一个结合了这两者的asan-ubsan
变体。coverage
变体用于通过 Clang 的 启用了基于插桩的分析,以支持代码覆盖率 。profile
变体也用于构建插桩代码, 而是支持配置文件引导的优化。thinlto
和lto
变体用于通过如下方式构建二进制文件: 关联时优化。gcc
变体用于构建 Zircon 的某些部件 使用 GCC 编译器而不是 Clang 从而消除了 对内核进行重要调用)。release
和debug
变体,用于替换 默认编译模式,这由args.gn
中的is_debug
build 配置变量。可满足特定需求的一些其他变体,这些变体均已定义 放在
//build/config/BUILDCONFIG.gn
文件中,并使用惯例 具体说明。
一般来说,单个 build 变体模型如下:
一组用于定义编译器、汇编程序或链接器标记的额外配置 在构建变体二进制文件及其依赖项时应用的政策。
一组要添加到最终 build 图中的变体二进制目标(即可执行文件、可加载文件) 模块,有时甚至是共享库)。
例如,假设“asan”build 变体,用于 启用 Clang 的 Address Sanitizer 支持。在 使用 Address Sanitizer 构建 Fuchsia 可执行程序 需要(至少):
将
-fsanitize=address
标志同时传递到 Clang 编译器和链接器 在构建可执行文件及其所有依赖项(包括 C 库(如果是 Fuchsia)。Asan 运行时 (
libclang_rt.asan.so
) 可在运行时使用, 以及自己的依赖项(即libc++.so
、libc++abi.so
和libunwind.so
二进制文件)。
基础工具链和变体工具链
build 变体始终应用于特定的“基础”工具链
提供由变体本身增强的默认设置。
这将创建一个新的 GN toolchain() 实例,
称为“变体工具链”,它具有自己的 root_out_dir
。例如:
//build/toolchain:host_x64
是用于构建的基本工具链 主机二进制文件,其root_out_dir
为${root_build_dir}/host_x64
。//build/toolchain:host_x64-ubsan
是创建的变体工具链 方法是应用ubsan
变体,并且其root_out_dir
为${root_build_dir}/host_x64-ubsan
。//build/toolchain/fuchsia:x64
是默认工具链(以 基于 x64 的设备),用于构建 Fuchsia 用户级二进制文件。因为这是 默认值,其root_out_dir
与root_build_dir
相同。//build/toolchain/fuchsia:x64-asan
是由 将asan
变体应用于默认工具链。现在是root_out_dir
将为${root_build_dir}/x64-asan
。
一般来说,//${tc_dir}:${tc_name}-${variant}
将是
通过将 ${variant}
变体应用于基础映像而创建的变体工具链
名为 //${tc_dir}:${tc_name}
的工具链,其 root_out_dir
将
始终为 ${root_build_dir}/${tc_name}-${variant}
。
如果基础工具链具有 shlib 工具链, 那么它的所有变体工具链也将有一个变体。最后,一个变体 可应用于多个基础工具链。
例如 //build/toolchain:host_x64-asan
和
//build/toolchain/fuchsia:x64-asan
是已创建的变体工具链
将同一 asan
变体应用于用于
build 主机和 Fuchsia 设备二进制文件。
后者还可以使用 //build/toolchain/fuchsia:x64-asan-shared
,作为
shlib 工具链,用于生成基于 ELF 的共享库。
必须使用 clang_toolchain_suite()
在 build 中定义基础工具链
或 zircon_toolchain_suite()
。两个模板最终都调用 variant_toolchain_suite()
它实现了在需要时自动创建变体工具链的神奇功能。
工具链和变体标记
Fuchsia build 中的每个基本工具链都可以具有多个标记,
是自由格式字符串,用于描述工具链的属性。例如:
"kernel"
标记用于指示使用工具链构建内核
工件(这很重要,因为没有 C 库,也没有标准 C++
对某些目标市场具有重要意义的其他限制条件
定义)。
下面列出了所有有效的工具链标记
//build/toolchain/toolchain_tags.gni
。
同样,每个变体定义都有若干标记,用于描述属性。
变体。例如,"instrumentation"
标记用于
表明该变体会创建机器代码,
插桩(例如,Sanitizer 或 Profiler)
如需查看所有有效的变体标记及其文档的列表,请访问
//build/toolchain/variant_tags.gni
。
创建变体工具链时,系统会将全局 toolchain_variant.tags
值设为
将包含从基本工具链继承的标记,以及从基础工具链继承的标记
。
工具链变体实例化
构建系统只在需要时创建变体工具链。
存在大量可能的“工具链+变体”组合,
并同时创建所有此类请求会大大降低 gn gen
的速度。
构建系统不会急于创建所有变体,而是决定哪些变体 根据以下条件创建的工具链变体:
变体选择器列表显示在
select_variant
全局变量。变体描述符名称列表,显示为 的
enable_variants
参数,variant_toolchain_suite()
模板。很少用于强制启用 即使select_variant
为空,也很少会发生变体。例如,用于构建 C 库的工具链的 ASan 和 UBSan 变体 始终处于启用状态,因为构建 Core Fuchsia IDK 时需要用到这些内容 (请参阅
//zircon/system/ulib/c/BUILD.gn
)。出现在
exclude_variant_tags
variant_toolchain_suite()
的参数。很少用于排除 变体会应用到给定的基本工具链。例如,引导加载程序会排除带有
"instrumented"
的变体 因为无法运行排错程序或性能分析运行时 (请参阅 `//
变体描述符
变体描述符是用于描述
向构建系统提供给定的 build 变体它们通过
//build/config/BUILDCONFIG.gn
中的 known_variants
变量,以及
每个范围都应遵循以下严格架构:
configs
:GN 配置标签的可选列表,将 自动添加到每个具有此变体的目标中。请注意,对于此列表中的每个配置
${label}
,还必须 是目标${label}_deps
,每个目标在此变体中构建了 大多数情况下 空group()
。remove_common_configs
:GN 配置标签的可选列表 如果存在,则应从使用 此变体。如果某些默认 不应将构建系统为二进制文件设置的配置 特定变体remove_shared_configs
:GN 配置标签的可选列表。 与remove_common_configs
类似,但仅当 构建shared_library()
目标及其依赖项。deps
:GN 目标标签的可选列表, 作为隐式依赖项添加到任何可链接目标中 使用此变体构建而成。name
:对变体描述符进行唯一命名的字符串,如 通常用于select_variant
。如果省略name
,则configs
不能为空,并且将用于派生名称(通过联接 名称(带短划线)。tags
:用于描述属性的自由格式字符串的可选列表 变体的效果(请参阅 工具链和变体标记。toolchain_args
:可选范围,其中每个变量都定义 替换工具链上下文中的构建参数 该变体的特征。host_only
和target_only
:可选范围, 包含上述任何字段。这些值仅用于主机或 分别是目标(即设备)工具链。任何字段 也不应包含在外部作用域内。
示例如下:
具有单个配置的变体描述符示例
{
configs = [ "//build/config/lto" ]
tags = [ "lto" ]
}
上述范围定义了一个名为 "lto"
的变体描述符(因为
范围内没有 name
键,名称是从
configs
中的值,而此处只包含一件商品)。
应用此变体将添加 //build/config/lto:lto
配置,
在 //build/config/lto/BUILD.gn
中定义的,该文件还应
包含 //build/config/lto:lto_deps
空组(如果此类
配置没有隐式依赖项。例如:
# //build/config/lto/BUILD.gn
config("lto") {
cflags = [ "-flto" ]
asmflags = cflags
ldflags = cflags
rustflags = [ "-Clto=fat" ]
}
group("lto_deps") {
# Implicit dependencies for "lto" config.
# This is an empty group since there are none.
}
此描述符使用 "lto"
标记来指明:
变体执行了链接时优化。此代码还可以
由 "thinlto"
描述符使用,该描述符将使用
不同的配置
包含多个配置的变体描述符示例
{
configs = [
"//build/config/sanitizers:ubsan",
"//build/config/sanitizers:sancov",
]
remove_common_configs = [ "//build/config:no_rtti" ]
tags = [
"instrumented",
"instrumentation-runtime",
"kernel-excluded",
"sancov",
"ubsan",
]
}
这定义了一个名为 "ubsan-sancov"
的变体描述符(
名称通过联接配置从 configs
列表派生而来
用于构建机器代码,以检测
未定义行为,并收集代码覆盖率
信息。
请注意,此操作还需要 //build/config/sanitizers:ubsan_deps
和 //build/config/sanitizers:sancov_deps
,以列表形式
隐式依赖项
这会占用remove_common_config
,因为//build/config:no_rtti
是许多基础工具链默认配置的一部分,但 RTTI
必须启用 UBSan 插桩才能正常工作。
所用标记的列表也更为广泛。请注意
"kernel-excluded"
标记,用于防止出现此变体
应用到任何内核机器码
包含 toolchain_args
的变体描述符示例
{
name = "release"
toolchain_args = {
is_debug = false
}
}
此变体描述符会明确指定,
添加任何配置或依赖项另一方面
全局 build 配置变量 is_debug
将
设置为 false,这会更改默认配置的数量
在相应的变体工具链上下文中定义。
通用变体
构建系统中鲜为人知的功能称为“通用变体”。这些 是与其他已知变体组合的其他变体描述符, 其工作原理如下:
如果在
args.gn
中设置了is_debug=false
,则意味着所有二进制文件都应 采用最大限度的优化进行构建,则"debug"
变体描述符 由 build 定义如果存在以下情况,允许在调试模式下构建特定目标: 。同样,如果
is_debug=true
(默认值),则"release"
变体 描述符由 build 定义。这样,您就可以使用 必要时进行全面优化。此外,上述通用变体会与所有其他已知变体组合起来, 变体描述符。例如:如果为
is_debug=false
, 那么构建还将创建"asan-debug"
、"ubsan-debug"
、"thinlto-debug"
等。如果为is_debug=true
,则将定义"asan-release"
,"ubsan-release"
、"thinlto-release"
等。
请注意,这些变体描述符由 build 有条件地定义,
基于 is_debug
的值。例如,没有 "release"
变体,
is_debug=false
,并且没有 "debug"
变体及其
is_debug=true
!
toolchain_variant
全局变量
在 BUILD.gn
或 *.gni
文件中时,全局 toolchain_variant
变量
可用于检索 current_toolchain
的变体相关信息。
这是一个具有以下架构的范围:
name
:build 变体描述符的名称。此字段是 基本工具链的上下文,或者是变体描述符的名称, 否则用于创建当前的 GNtoolchain()
实例。各种工具链上下文的名称示例:
//build/toolchain/fuchsia:x64 "" //build/toolchain/fuchsia:x64-shared "" //build/toolchain/fuchsia:x64-asan "asan" //build/toolchain/fuchsia:x64-asan-shared "asan"
base
: 当前状态。请注意,对于某个工具链变体的 shlib 工具链, 它指向最终的基础工具链。示例://build/toolchain/fuchsia:x64 //build/toolchain/fuchsia:x64 //build/toolchain/fuchsia:x64-asan //build/toolchain/fuchsia:x64 //build/toolchain/fuchsia:x64-shared //build/toolchain/fuchsia:x64 //build/toolchain/fuchsia:x64-asan-shared //build/toolchain/fuchsia:x64
tags
:自由格式的字符串列表,每个字符串描述 当前工具链实例及其变体。这就是 工具链和变体标记。instrumented
:一个布尔标志,在且仅当tags
列表时,该标志将设置为 true 包含"instrumentation"
标记值,提供此值是为了方便您替换 类似于 GN 的复杂测试指令:if (toolchain_variant.tags + [ "instrumentation" ] - [ "instrumentation" ] != toolchain_variant.tags) { # toolchain is instrumented ... }
替换为:
if (toolchain_variant.instrumented) { # toolchain is instrumented ... }
is_pic_default
:在可构建 ELF 位置无关代码 (PIC)。这意味着 shlib 工具链 (例如//build/toolchain/fuchsia:x64-shared
)或基础工具链, 直接生成此类代码(例如//zircon/kernel/lib/userabi/userboot:userboot_arm64
)。with_shared
:一个布尔值,如果当前工具链具有 shlib,则该布尔值为 true 用于构建 ELF 共享库的工具链(例如//build/toolchain/fuchsia:x64
) 或位于此类工具链中(例如//build/toolchain/fuchsia:x64-shared
)时。configs
、remove_common_configs
、remove_shared_configs
:列表 的 GN 标签映射到config()
项,这些项直接来自当前的 变体描述符(如果有),否则为空列表。deps
:作为依赖项添加的目标的 GN 标签列表 从变体描述符本身继承的任何可关联目标(如果有)。libprefix
:对于插桩变体,这是安装前缀字符串 如果是共享库,则为空字符串。请参阅 完整的工具链变体 libprefix 部分 。exclude_variant_tags
:供变体选择逻辑在内部使用。 继承自clang_toolchain_suite()
或zircon_toolchain_suite()
调用,也可以直接从目标定义调用。它是一个代码列表 排除要应用于基本工具链或目标的变体 。suffix
:这是"-${toolchain_variant.name}"
;如果名称为空,则为""
。 在内部使用,以简化无条件扩展。supports_cpp
:一个布尔值,如果此工具链支持 C/C++,则为true
。supports_rust
:一个布尔值,如果此工具链支持 Rust,则值为true
。is_basic
:一个布尔值,true
basic_toolchain()
模板,因此不使用任何内置 。它只有copy
或action
目标。
此全局变量的内容很少用于目标定义 根据当前工具链上下文更改其配置。这个 多发生于低级目标,如 C 库、内核工件 或插桩运行时支持。
工具链变体 libprefix
为了能够在单个文件中混用插桩和非插桩二进制文件 Fuchsia 软件包中,构建系统必须执行特殊步骤:
使用插桩变体工具链构建的共享库 必须安装到
"lib/<variant>/"
而不是默认的"lib/"
位置。可执行二进制文件必须使用如下链接器参数进行编译:
"-Wl,-dynamic-linker=<variant>/ld.so.1"
,用于替换默认值 ("ld.so.1"
,它在 Fuchsia Clang 预构建工具链二进制文件中进行了硬编码。)一种特殊情况是,模糊测试 build 变体会使用非模糊测试 build 变体 库子目录的名称
toolchain_variant.libprefix
变量的定义方式如下,
轻松支持上述所有功能:
variant name libdir libprefix note
no variant ---> lib/ "" (default target toolchain)
thinlto ---> lib/ "" (uninstrumented)
asan-ubsan ---> lib/asan-ubsan/ "asan-ubsan/" (instrumented)
asan-fuzzer ---> lib/asan/ "asan/" (instrumented + fuzzing)
此属性可用于确定安装位置,格式为 "lib/${toolchain_variant.libprefix}"
。
链接器标记设置为 "-Wl,-dynamic-linker=${toolchain_variant.libprefix}ld.so.1"
。
变体选择
Fuchsia 构建系统支持选择将哪些 build 变体
以及针对哪些具体目标或目标组,
。为此,您需要定义 select_variant
变量,
build 配置文件 (args.gn
)。请参考以下示例:
# From out/default/args.gn
...
select_variant = [
{
label = [ "//src/sys/component_manager:bin" ]
variant = "release"
},
"host_asan",
"thinlto/blobfs",
"ubsan",
]
列表中的每个值都是一个表达式,称为 变体选择器,可以是范围或字符串; 用于配置构建将变体应用到不同目标集的方式。
如果 select_variant
已定义且不是空列表,则其值将
用于确定如何构建可链接目标,例如可执行文件、
构建图中显示的可加载模块和共享库
及其所有依赖项中。
系统会比较 select_variant
中显示的变体选择器
且系统会选择与当前目标匹配的第一个目标。
因此,上面的示例意味着:
//src/sys/component/manager:bin
程序二进制文件及其依赖项 应始终使用release
变体进行构建(注意:此示例 会导致在gn gen
发生错误,原因是is_debug=false
处于args.gn
文件中,因为"release"
变体不存在 在这种情况下,请参阅通用变体了解原因)。主机二进制文件应在
"asan"
变体中构建。 请注意,"host_asan"
不是变体描述符名称,而是 变体快捷方式。blobfs
程序设备二进制文件应始终为 使用执行链接时操作的"thinlto"
变体构建而成 优化。所有其他设备二进制文件都应使用
"ubsan"
变体进行构建。
变体选择器
变体选择器是可以出现在全局 select_variant
build 中的值
配置变量。供构建系统用来控制变体
在基本工具链环境中定义可关联目标时选择此项。
支持以下三种值:
为一组目标定义一组匹配条件的范围。 该范围的格式如下:
variant
:给定变体描述符的名称 当且仅当当前定位条件符合所有条件时,才会使用此参数 定义。label
:如果定义,则必须是符合条件的 GN 标签列表 (带有:
,但不带工具链标签,例如//src/sys/foo:foo
)。name
:如果定义了 GN 标签目标名称(例如名称)//src/sys/foo:bar
目标值为 '"bar"`)。dir
:如果定义了 GN 标签目录路径(例如路径 的//src/sys/foo:bar
目标为"//src/sys/foo"
)。output_name
:如果定义了,则为目标output_name
值的列表 (默认值为target_name
)。target_type
:如果定义了,则为与目标类型匹配的字符串列表。 有效值包括:"executable"
、"test"
、"loadable_module"
"shared_library"
和另外几个人。testonly
:如果已定义,则为一个布尔值。如果为 true,则选择器与目标匹配 尽在testonly=true
。如果为 false,则选择器匹配没有testonly=true
。host
:如果已定义,则为一个布尔值。如果为 true,则选择器与 主机工具链。如果为 false,则选择器会在目标工具链中进行匹配。
包含简单名称(例如
"asan"
)的字符串,名称指向 变体快捷方式,这是现有变体的别名 选择器范围值。例如,
"coverage"
值相当于以下范围:{ variant = "coverage" host = false }
包含变体快捷方式名称和输出名称的字符串,以 目录路径(例如
"thintlo/blobfs"
)。这是一种方便的格式, 可避免编写等效作用域,如上例所示 例如:{ variant = "thinlto" host = false output_name = [ "blobfs" ] }
select_variant
列表中的选择器顺序很重要:第一个选择器
与当前目标匹配的结果胜出,并确定该目标的构建方式。
变体快捷键
除了变体描述符之外,build 还设置了许多“快捷方式”, 是一些硬编码变体选择器范围值的已命名别名。build 添加 几个硬编码变体,并根据已知变体列表创建其他变体:
"host_asan"
快捷方式被定义为使用"asan"
构建主机二进制文件 变体描述符,它在技术上等同于以下 选择器范围值:# Definition for the `host_asan` variant shortcut [ { variant = "asan" host = true } ]
同样,存在
host_asan-ubsan
、host_coverage
、host_profile
以及其他几个工具。每个变体描述符名称都有一个对应的快捷方式,适用于 设备二进制文件。例如,
"ubsan"
快捷键等效于 添加到此列表,其中包含一个选择器范围值:[ { variant = "ubsan" host = false } ]
这就是在
select_variant
中使用变体描述符名称的原因 仅适用于设备二进制文件,如:# Applies the `ubsan` variant to device binaries, not host ones! select_variant = [ "ubsan", ]
同样,还要为每个通用变体及其 联合,这同样仅适用于设备二进制文件。
这意味着,假设
args.gn
中的is_debug=true
, 以下操作会强制在发布版本中构建所有设备二进制文件 模式,而主机应用仍将在调试模式下构建。is_debug = true select_variant = [ "release" ]
这相当于:
is_debug = true select_variant = [ { variant = "release" host = false } ]
强制在发布模式下编译主机二进制文件的方法如下所示: 使用明确的作用域值,因为 例如:
is_debug = true select_variant = [ { variant = "release" host = true } ]
变体定位条件重定向
variant_target()
模板
在 //build/config/BUILDCONFIG.gn
中定义的 variant_target()
模板
实现了核心 build 变体选择机制。
此模板不应直接从 BUILD.gn
文件中调用,而应
由 Fuchsia build 为
executable()
、loadable_module()
、shared_library()
和其他几个人
对应的可关联目标(即使用静态
链接器)。
对于每个目标,它的作用是:在构建的每个工具链上下文中
将 select_variant
的内容与目标的内容进行比较,
属性(即目标类型和一些其他参数)以:
1) 计算“构建器工具链”即 GN 工具链实例,用于构建真正的二进制文件, 及其依赖项
2) 如果当前工具链是构建器工具链,则只构建 与平时一样的目标
3) 否则,请创建一个 group()
或 copy()
目标,
重定向(即公开依赖)构建器工具链中的目标。
这是组还是副本取决于微妙的情况
已在 variant_target()
实现中全面记录,
但请参阅以下小节了解相关说明。
必须指定 copy()
目标才能保留输出位置
而group()
用于
不需要。
大多数情况下,executable()
或 loadable_module()
目标
将需要 copy()
,而 shared_library()
将需要
group()
。
可链接的变体二进制文件的输出位置
GN 配置语言的一个关键设计限制是, 除了少数例外情况,指定的目标定义一无所有, 但其标签除外。这有问题 因为在很多情况下,指定的目标需要知道 其依赖项或者输出的目标类型 依赖项
为了说明这一点,我们来看以下示例:
一个名为
//src/my/program:bin
的executable()
目标,此目标可生成 名为my_program
的 Fuchsia 程序二进制文件。由于构建方式 这样会生成${root_build_dir}/exe.unstripped/my_program
,${root_build_dir}/my_program
,以及一些次要文件(此处已忽略)。用于解析的名为
//src/my/program:verify_binary
的action()
目标 对程序二进制文件进行检查或从中提取信息(比方说, 会验证其导入符号引用)。此目标需要依赖于 还可以找到二进制文件的输出位置,例如:
action("//src/my/program:verify_imports")
script = "check-my-imports.py"
deps = [ "//src/my/program:bin" ]
inputs = [ get_label_info(deps[0], "root_out_dir") + "/my_program" ]
...
|
| deps
|
v
executable("//src/my/program:bin")
output_name = "my_program"
# outputs: [
${root_build_dir}/exe.unstripped/my_program,
${root_build_dir}/my_program,
]
在这里,action()
可以通过以下方式猜测程序二进制文件的位置
使用 get_label_info("<label>", "root_out_dir")
作为其目录,
并在操作本身中对 output_name
值进行硬编码。这违反了
抽象层,但考虑到 GN 的局限性,这是必要的。
启用 build 变体后,二进制文件目标的实际输出位置
因select_variant
而异。如果实现了变体重定向
只需简单 group()
,图表就会变为:
action("//src/my/program:verify_imports")
script = "check-my-imports.py"
deps = [ "//src/my/program:bin" ]
inputs = [ get_label_info(deps[0], "root_out_dir") + "/my_program" ]
...
|
| deps
|
v
group("//src/my/program:bin")
|
| public_deps
|
v
executable("//src/my/program:bin(//build/toolchain/fuchsia:x64-asan")
output_name = "my_program"
# outputs: [
# ${root_build_dir}/x64-asan/exe.unstripped/my_program,
# ${root_build_dir}/x64-asan/my_program,
# ]
问题在于,顶级操作中 inputs
的值没有更改,
因此它的命令会尝试在原位置查找相应的程序二进制文件
(${root_build_dir}/my_program
),而不是新的
(${root_build_dir}/x64-asan/my-program
)。构建将使用过时的工件
或由于缺少文件而失败。
在操作本身中解析 select_variant
的开销太大,因此解决
在这种情况下,对于可执行和可加载的模块目标,需要 copy()
目标(而不是 group()
),以确保将未剥离的二进制文件复制
原始位置图表会变为:
action("//src/my/program:verify_imports")
script = "check-my-imports.py"
deps = [ "//src/my/program:bin" ]
inputs = [ get_label_info(deps[0], "root_out_dir") + "/my_program" ]
...
|
| deps
|
v
copy("//src/my/program:bin")
outputs = [ "${root_build_dir}/my_program" ]
sources = [ "${root_build_dir}/x64-asan/my_program" ]
|
| public_deps
|
v
executable("//src/my/program:bin(//build/toolchain/fuchsia:x64-asan")
output_name = "my_program"
# outputs: [
${root_build_dir}/x64-asan/exe.unstripped/my_program,
${root_build_dir}/x64-asan/my_program,
]
使用此设置时,构建始终会成功,并且操作命令始终有效 处理正确的二进制文件。
这一切都是在 build 中自动完成的。最终效果是 而无需关心其依赖项是否是使用 无论特定变体是否是特定变体,都可以依赖输出位置 稳定,至少对于未剥离的二进制文件路径而言是稳定的。
ELF 共享库的输出位置
TBW
特殊的 novariant
描述符
TBW
特殊全局变量
host_toolchain
和 host_out_dir
全局变量
TBW
zircon_toolchain
变量
TBW
variant()
模板
TBW