moz.build 文件¶
moz.build
文件是定义树元数据(特别是构建配置)的机制。
树中的目录包含 moz.build
文件,这些文件声明了它们各自树部分的功能。这包括要编译的 C++ 文件列表、在哪里查找测试等。
moz.build
文件实际上是 Python 脚本。但是,它们的执行受特殊规则约束。这将在下面解释。
moz.build Python 沙箱¶
如上所述,moz.build
文件是 Python 脚本。但是,它们在特殊的 Python 沙箱中执行,该沙箱会显着更改和限制执行环境。环境如此不同,以至于大多数 moz.build
文件如果由普通的 Python 解释器执行(例如 python moz.build
),则可能无法正常执行。
以下属性使 moz.build
文件的执行变得特殊
执行环境公开了一组有限的 Python 子集。
有一组特殊的全局符号和强制的符号命名约定。
某些符号继承自先前执行的
moz.build
文件。
Python 的有限子集实际上是一个极其有限的子集。只有 __builtin__
中的几个符号被公开。这些包括 True
、False
、None
、sorted
、int
和 set
。像 import
、print
和 open
这样的全局函数不可用。没有这些,moz.build
文件能做的事情非常少。这是有意为之的。
执行沙箱对所有 UPPERCASE
变量进行特殊处理。任何 UPPERCASE
变量都必须在脚本执行之前为沙箱所知。任何尝试读取或写入未知 UPPERCASE
变量的操作都将导致引发异常。此外,所有 UPPERCASE
变量的类型都严格执行。尝试将不兼容的类型分配给 UPPERCASE
变量将导致引发异常。
对 UPPERCASE
变量的行为严格性是一个非常有意的设计决策。通过确保严格的行为,任何涉及 UPPERCASE
变量的操作都保证具有明确定义的副作用。以前,当构建配置在 Makefiles
中定义时,对什么也不做的变量的赋值会不被注意到。 moz.build
文件通过消除虚假承诺的可能性解决了此问题。
moz.build
文件执行完成后,仅使用 UPPERCASE
变量来检索状态。
Python 沙箱可用的变量和函数集由 mozbuild.frontend.context
模块定义。此模块中的数据结构由 mozbuild.frontend.reader.MozbuildSandbox
类使用来构建沙箱。有测试可以确保公开给空沙箱的符号集都在 context
模块中定义。此模块还包含每个符号的文档,因此没有任何内容可以偷偷进入沙箱而没有被明确定义和记录。
读取和遍历 moz.build 文件¶
读取 moz.build
文件的过程大致包括
从根
moz.build
(<topsrcdir>/moz.build
)开始。在一个新的沙箱中评估
moz.build
文件。发出从执行的沙箱中获得的主要上下文和任何子上下文。
提取一组要接下来执行的
moz.build
文件。对于每个额外的
moz.build
文件,转到 #2 并重复,直到所有引用的文件都执行完毕。
从消费者的角度来看,读取的输出是一系列 mozbuild.frontend.reader.context.Context
实例。每个 Context
定义了数据的特定方面。消费者迭代这些对象并对其中的数据执行某些操作。每个对象本质上都是在其执行期间填充的所有 UPPERCASE
变量的字典。
注意
历史上,每个 moz.build
文件只有一个 context
。随着 moz.build
文件跟踪的事物数量的增加以及对越来越复杂的处理的需求,有必要将这些上下文拆分为多个逻辑部分。现在,每个 moz.build
文件发出多个上下文很常见。
构建系统读取模式¶
评估 moz.build
文件的传统模式称为构建系统遍历模式。在此模式下,每个 moz.build
沙箱中的 CONFIG
变量都由来自 config.status
的数据填充,该数据由 configure
生成。
在评估期间,moz.build
文件通常会根据构建配置的状态做出决策。例如,仅当启用功能 X 时才编译 foo.cpp。
在此模式下,moz.build
文件的遍历由 DIRS
和 TEST_DIRS
等变量控制。例如,要执行子目录 foo
,您需要将 DIRS += ['foo']
添加到 moz.build
文件中,并且将评估 foo/moz.build
。
文件系统读取模式¶
有一种替代的读取模式,它不涉及构建系统并且不使用 DIRS
变量来控制进入子目录的遍历。此模式称为文件系统读取模式。
在此读取模式下,CONFIG
变量是一个虚拟的、大部分为空的对象。访问除少数几个特殊变量之外的所有变量都将返回空值。这意味着几乎所有 if CONFIG['FOO']:
分支都不会被执行。
读取器控制要评估的文件集,而不是使用已评估的 moz.build
文件中的内容来驱动进入后续 moz.build
文件的遍历。
单个 moz.build
文件不能保证能够独立执行。相反,我们必须首先评估所有父 moz.build
文件。例如,为了评估 /foo/moz.build
,必须执行 /moz.build
并使其状态影响 /foo/moz.build
的执行。
文件系统读取模式用于为 文件元数据 功能提供支持。
技术细节¶
读取 moz.build
文件的代码位于 mozbuild.frontend.reader
中。Python 沙箱评估结果(mozbuild.frontend.context.Context
)被传递到 mozbuild.frontend.emitter
,后者将其转换为 mozbuild.frontend.data
中定义的类。此模块中的每个类都定义了树元数据的特定于域的组件。例如,将有单独的类来表示 JavaScript 文件与编译的 C++ 文件或测试清单。这意味着此数据的下游使用者可以根据类类型进行筛选,只使用他们感兴趣的内容。
在 moz.build
文件实例和从每个文件派生的 mozbuild.frontend.data
类数量之间没有明确的映射。根据 moz.build
文件的内容,可能派生 1 个对象或 100 个对象。
低级沙箱执行和元数据表示之间 emitter
层的目的是促进统一的规范化和验证步骤。 moz.build
派生数据的下游使用者有很多,并且许多使用者将执行相同的操作。此逻辑可能很复杂,因此我们有一个专门用于此的组件。
mozbuild.frontend.reader.BuildReader`
和 mozbuild.frontend.reader.TreeMetadataEmitter`
由于使用了生成器,因此具有基于流的 API。当您正确地将它们连接起来时,在所有 moz.build
文件都读取之前,就会发出 mozbuild.frontend.data
类。这意味着下游错误会在沙箱执行后立即引发。
评估 Python 沙箱的大部分代码都适用于非 Mozilla 系统。理论上,它可以提取到一个独立且通用的包中。但是,在有需要之前,可能存在一些紧密耦合的部分。