Bazel 会根据整理在名为工作区的目录树中的源代码构建软件。工作区中的源文件以嵌套的软件包层次结构进行整理,其中每个软件包都是一个目录,其中包含一组相关的源文件和一个 BUILD
文件。BUILD
文件指定可以从源代码构建哪些软件输出。
工作区
工作区是文件系统上的目录树,其中包含要构建的软件的源文件。每个工作区都有一个名为 WORKSPACE
的文本文件,该文件可能为空,也可能包含对构建输出所需的外部依赖项的引用。
包含名为 WORKSPACE
的文件的目录被视为工作区的根目录。因此,如果工作区中的目录树位于包含 WORKSPACE
文件的子目录中,则 Bazel 会忽略工作区中的所有目录树,因为它们会形成另一个工作区。
Bazel 还支持将 WORKSPACE.bazel
文件用作 WORKSPACE
文件的别名。如果这两个文件都存在,则系统会使用 WORKSPACE.bazel
。
代码库
代码以repositories的形式进行整理。包含 WORKSPACE
文件的目录是主代码库的根目录,也称为 @
。其他(外部)代码库使用工作区规则在 WORKSPACE
文件中定义,或者根据 Bzlmod 系统中的模块和扩展程序生成。如需了解详情,请参阅外部依赖项概览。
如需了解与 Bazel 捆绑在一起的工作区规则,请参阅构建百科全书的工作区规则部分,以及关于嵌入式 Starlark 代码库规则的文档。
由于外部代码库本身就是代码库,因此它们通常也包含 WORKSPACE
文件。不过,Bazel 会忽略这些额外的 WORKSPACE
文件。特别是,以传递方式依赖的代码库不会自动添加。
软件包
代码库中代码组织的主要单元是软件包。软件包是一组相关文件,以及关于如何使用这些文件生成输出工件的规范。
软件包定义为包含名为 BUILD
或 BUILD.bazel
的 BUILD
文件的目录。软件包包含其目录中的所有文件,以及其下的所有子目录(本身包含 BUILD
文件的子目录除外)。根据此定义,任何文件或目录都不能同时属于两个不同的软件包。
例如,在以下目录树中,有两个软件包 my/app
和子软件包 my/app/tests
。请注意,my/app/data
不是软件包,而是属于软件包 my/app
的目录。
src/my/app/BUILD
src/my/app/app.cc
src/my/app/data/input.txt
src/my/app/tests/BUILD
src/my/app/tests/test.cc
目标
软件包是目标的容器,目标在软件包的 BUILD
文件中定义。大多数目标属于两种主要类型之一,即文件和规则。
文件进一步细分为两种。源文件通常由人员编写并签入代码库。生成的文件(有时称为派生文件或输出文件)不会被提交,而是从源文件生成。
第二种目标是使用规则声明的。每个规则实例都指定一组输入文件与一组输出文件之间的关系。规则的输入可以是源文件,但也可以是其他规则的输出。
在大多数情况下,规则的输入是源文件还是生成的文件并不重要;重要的是该文件的内容。因此,您可以轻松地将复杂的源文件替换为由规则生成的文件,例如,当手动维护高度结构化的文件变得太过繁琐时,有人编写了程序来派生该文件。该文件的使用者无需进行任何更改。反之,生成的文件可以轻松替换为仅包含本地更改的源文件。
规则的输入还可能包括其他规则。此类关系的确切含义通常非常复杂,并且取决于语言或规则,但直观上很简单:C++ 库规则 A 可能将另一个 C++ 库规则 B 作为输入。这种依赖项的影响是,在编译期间,A 可以使用 B 的头文件;在链接期间,A 可以使用 B 的符号;在执行期间,A 可以使用 B 的运行时数据。
所有规则的一个不变性是,由规则生成的文件始终与规则本身属于同一软件包;无法将文件生成到其他软件包中。不过,规则的输入来自另一个软件包的情况并不少见。
软件包组是一系列软件包,其目的是限制某些规则的可访问性。软件包组由 package_group
函数定义。它们具有三个属性:它们包含的软件包列表、其名称,以及它们包含的其他软件包组。唯一允许的方式是通过规则的 visibility
属性或 package
函数的 default_visibility
属性引用它们;它们不会生成或使用文件。如需了解详情,请参阅 package_group
文档。