目录
包裹
package(default_deprecation, default_package_metadata, default_testonly, default_visibility, features)
此函数声明适用于软件包中每个规则的元数据。在软件包(BUILD 文件)中最多使用一次。
对于声明适用于整个代码库中每条规则的元数据的对等项,请使用代码库根目录中 REPO.bazel
文件中的 repo()
函数。repo()
函数所采用的参数与 package()
完全相同。
package() 函数应在文件顶部所有 load() 语句之后、任何规则之前调用。
参数
属性 | 说明 |
---|---|
default_applicable_licenses |
|
default_visibility |
标签列表;默认值为 相应软件包中顶级规则目标和符号宏的默认可见性,即未在符号宏内声明的目标和符号宏。如果目标或宏指定了 如需详细了解此属性的语法,请参阅 可见性文档。软件包默认可见性不适用于 exports_files,后者默认处于公开状态。 |
default_deprecation |
字符串;默认值为 为相应软件包中的所有规则设置默认的
|
default_package_metadata |
标签列表;默认值为 设置适用于软件包中所有其他目标的默认元数据目标列表。 这些目标通常与 OSS 软件包和许可声明相关。 如需查看示例,请参阅 rules_license。 |
default_testonly |
布尔值;默认值为 为相应软件包中的所有规则设置默认
在 |
features |
列出字符串;默认值为 设置影响相应 BUILD 文件语义的各种标志。 此功能主要供构建系统方面的人员使用,用于标记需要某种特殊处理的软件包。除非构建系统方面的人员明确要求,否则请勿使用此标志。 |
示例
以下声明声明了相应软件包中的规则仅对软件包组//foo:target
的成员可见。如果规则中存在单独的公开范围声明,则该声明会覆盖此规范。
package(default_visibility = ["//foo:target"])
package_group
package_group(name, packages, includes)
此函数定义了一组软件包,并将标签与该组相关联。可以在 visibility
属性中引用该标签。
软件包组主要用于控制可见性。公开可见的目标可被源树中的每个软件包引用。私密可见的目标只能在其自己的软件包(而非子软件包)中引用。介于这两个极端之间的是,目标可以允许访问其自己的软件包以及一个或多个软件包组描述的任何软件包。如需详细了解公开范围系统,请参阅公开范围属性。
如果某个给定软件包与 packages
属性匹配,或者已包含在 includes
属性中提及的其他某个软件包组中,则该软件包将被视为属于相应组。
从技术上讲,软件包组是目标,但不是通过规则创建的,并且本身没有任何可见性保护。
参数
属性 | 说明 |
---|---|
name |
名称;必需 相应目标的唯一名称。 |
packages |
字符串列表;默认值为 包含零个或多个软件包规范的列表。 每个软件包规范字符串都可以采用以下形式之一:
此外,前两种软件包规范还可以添加 软件包组包含符合至少一个正向规范且不符合任何负向规范的所有软件包。例如,值 除了公开可见性之外,没有其他方法可以直接指定当前代码库之外的软件包。 如果缺少此属性,则等同于将其设置为空列表,也等同于将其设置为仅包含 注意:在 Bazel 6.0 之前,规范 注意:在 Bazel 6.0 之前,当此属性作为 |
includes |
标签列表;默认值为 此软件包组中包含的其他软件包组。 此属性中的标签必须引用其他软件包组。
被引用软件包组中的软件包被视为此软件包组的一部分。这是传递性的 - 如果软件包组 如果与否定软件包规范一起使用,请注意,系统会先独立计算每个组的软件包集,然后将结果合并在一起。这意味着,一个组中的否定指定不会对另一个组中的指定产生影响。 |
示例
以下 package_group
声明指定了一个名为“tropical”的软件包组,其中包含热带水果。
package_group( name = "tropical", packages = [ "//fruits/mango", "//fruits/orange", "//fruits/papaya/...", ], )
以下声明指定了虚构应用的软件包组:
package_group( name = "fooapp", includes = [ ":controller", ":model", ":view", ], ) package_group( name = "model", packages = ["//fooapp/database"], ) package_group( name = "view", packages = [ "//fooapp/swingui", "//fooapp/webui", ], ) package_group( name = "controller", packages = ["//fooapp/algorithm"], )
exports_files
exports_files([label, ...], visibility, licenses)
exports_files()
指定属于此软件包且导出到其他软件包的文件列表。
只有通过 exports_files()
语句明确导出的源文件,软件包的 BUILD 文件才能直接引用属于其他软件包的源文件。详细了解文件的公开范围。
作为旧版行为,提及为规则输入的相应文件也会以默认可见性导出,直到 --incompatible_no_implicit_file_export
标志翻转为止。不过,不应依赖此行为,而应主动迁移。
参数
该实参是当前软件包中文件名称的列表。还可以指定可见性声明;在这种情况下,文件将对指定的目标可见。如果未指定任何可见性,则文件对每个软件包都可见,即使在 package
函数中指定了软件包默认可见性也是如此。您还可以指定许可。
示例
以下示例导出 test_data
软件包中的文本文件 golden.txt
,以便其他软件包可以使用它,例如在测试的 data
属性中使用它。
# from //test_data/BUILD exports_files(["golden.txt"])
glob
glob(include, exclude=[], exclude_directories=1, allow_empty=True)
Glob 是一种辅助函数,用于查找与特定路径模式匹配的所有文件,并返回一个包含这些文件路径的新可变排序列表。Glob 仅搜索其自身软件包中的文件,并且仅查找源文件(而非生成的文件或其他目标)。
如果源文件的软件包相对路径与任何 include
模式匹配,且与任何 exclude
模式都不匹配,则该源文件的标签会包含在结果中。
include
和 exclude
列表包含相对于当前软件包的路径模式。每个模式可以包含一个或多个路径段。与 Unix 路径一样,这些段由 /
分隔。模式中的各个段与路径的各个段进行匹配。段可能包含 *
通配符:这会匹配路径段中的任何子字符串(甚至包括空子字符串),但不包括目录分隔符 /
。此通配符可在单个路径段中多次使用。此外,**
通配符可以匹配零个或多个完整路径段,但必须声明为独立路径段。
foo/bar.txt
完全匹配相应软件包中的foo/bar.txt
文件(除非foo/
是子软件包)- 如果文件以
.txt
结尾,foo/*.txt
会匹配foo/
目录中的每个文件(除非foo/
是子软件包) foo/a*.htm*
匹配foo/
目录中以a
开头、后跟任意字符串(可以为空)、再后跟.htm
并以另一个任意字符串结尾(除非foo/
是子软件包)的每个文件;例如foo/axx.htm
和foo/a.html
或foo/axxx.html
foo/*
匹配foo/
目录中的每个文件(除非foo/
是子软件包);即使exclude_directories
设置为 0,它也不会匹配foo
目录本身foo/**
与软件包的第一级子目录foo/
下的每个非子软件包子目录中的每个文件匹配;如果exclude_directories
设置为 0,foo
目录本身也与该模式匹配;在这种情况下,**
被视为与零个路径段匹配**/a.txt
匹配此软件包目录中的a.txt
文件以及非子软件包子目录。**/bar/**/*.txt
与此软件包的每个非子软件包子目录中的每个.txt
文件匹配,前提是结果路径上至少有一个目录名为bar
,例如xxx/bar/yyy/zzz/a.txt
或bar/a.txt
(请注意,**
也与零个段匹配)或bar/zzz/a.txt
**
匹配此软件包的每个非子软件包子目录中的每个文件foo**/a.txt
是无效的模式,因为**
必须单独作为一段foo/
是无效的模式,因为在/
之后定义的第二个段是空字符串
如果启用 exclude_directories
实参(设置为 1),则结果中将省略目录类型的文件(默认值为 1)。
如果 allow_empty
实参设置为 False
,则如果结果为空列表,glob
函数将出错。
以下是一些重要的限制和注意事项:
-
由于
glob()
在 BUILD 文件评估期间运行,因此glob()
仅匹配源树中的文件,绝不匹配生成的文件。如果您要构建需要同时包含源文件和生成文件的目标,则必须将生成文件的明确列表附加到 glob。请参阅下文中的示例,其中包含:mylib
和:gen_java_srcs
。 -
如果某条规则与匹配的源文件同名,则该规则会“遮盖”相应文件。
为了理解这一点,请记住
glob()
会返回路径列表,因此在其他规则的属性(例如srcs = glob(["*.cc"])
)中使用glob()
与明确列出匹配的路径具有相同的效果。例如,如果glob()
生成["Foo.java", "bar/Baz.java"]
,但软件包中也有一条名为“Foo.java”的规则(这是允许的,但 Bazel 会发出相关警告),那么glob()
的使用者将使用“Foo.java”规则(及其输出)而不是“Foo.java”文件。如需了解详情,请参阅 GitHub 问题 #10395。 - Glob 可以匹配子目录中的文件。子目录名称可以包含通配符。不过…
-
标签不得跨越软件包边界,并且 glob 不匹配子软件包中的文件。
例如,如果
x/y
作为软件包存在(无论是作为x/y/BUILD
还是在软件包路径中的其他位置),则软件包x
中的 glob 表达式**/*.cc
不包含x/y/z.cc
。这意味着 glob 表达式的结果实际上取决于 BUILD 文件的存在与否,也就是说,如果没有名为x/y
的软件包或该软件包已使用 --deleted_packages 标志标记为已删除,则同一 glob 表达式将包含x/y/z.cc
。 - 上述限制适用于所有 glob 表达式,无论它们使用哪个通配符。
-
文件名以
.
开头的隐藏文件与**
和*
通配符完全匹配。如果您想使用复合模式匹配隐藏文件,则模式需要以.
开头。例如,*
和.*.txt
将匹配.foo.txt
,但*.txt
不会。 隐藏目录也会以相同的方式进行匹配。隐藏目录可能包含不需要作为输入的文件,并且可能会增加不必要的文件 glob 数量和内存消耗。如需排除隐藏目录,请将其添加到“exclude”列表实参中。 -
“**”通配符有一个特殊情况:模式
"**"
与软件包的目录路径不匹配。也就是说,glob(["**"], exclude_directories = 0)
会匹配当前软件包目录下的所有文件和目录(但当然不会进入子软件包的目录 - 请参阅上文中的相关注意事项)。
一般来说,您应该尝试提供适当的扩展名(例如 *.html),而不是使用裸 '*' 作为 glob 模式。更明确的名称既可以自我记录,又可以确保您不会意外匹配备份文件或 emacs/vi/... 自动保存文件。
编写 build 规则时,您可以枚举 glob 的元素。这样一来,系统便可为每个输入生成单独的规则,例如。请参阅下文的扩展 glob 示例部分。
glob 示例
创建一个 Java 库,该库由相应目录中的所有 Java 文件以及 :gen_java_srcs
规则生成的所有文件构建而成。
java_library( name = "mylib", srcs = glob(["*.java"]) + [":gen_java_srcs"], deps = "...", ) genrule( name = "gen_java_srcs", outs = [ "Foo.java", "Bar.java", ], ... )
包含 testdata 目录中的所有 txt 文件,但 experimental.txt 除外。 请注意,系统不会包含 testdata 子目录中的文件。如果您希望包含这些文件,请使用递归 glob (**)。
sh_test( name = "mytest", srcs = ["mytest.sh"], data = glob( ["testdata/*.txt"], exclude = ["testdata/experimental.txt"], ), )
递归 glob 示例
使测试依赖于 testdata 目录及其任何子目录(以及它们的子目录,依此类推)中的所有 txt 文件。 包含 BUILD 文件的子目录会被忽略。(请参阅上文中的限制和注意事项。)
sh_test( name = "mytest", srcs = ["mytest.sh"], data = glob(["testdata/**/*.txt"]), )
创建一个库,该库由相应目录及其所有子目录中的所有 Java 文件构建而成,但路径中包含名为 testing 的目录的文件除外。应尽可能避免使用此模式,因为它会降低构建增量,从而增加构建时间。
java_library( name = "mylib", srcs = glob( ["**/*.java"], exclude = ["**/testing/**"], ), )
展开的 Glob 示例
在当前目录中为 *_test.cc 创建一个单独的 genrule,用于统计文件中的行数。
# Conveniently, the build language supports list comprehensions. [genrule( name = "count_lines_" + f[:-3], # strip ".cc" srcs = [f], outs = ["%s-linecount.txt" % f[:-3]], cmd = "wc -l $< >$@", ) for f in glob(["*_test.cc"])]
如果上述 BUILD 文件位于软件包 //foo 中,并且该软件包包含三个匹配的文件(a_test.cc、b_test.cc 和 c_test.cc),那么运行 bazel query '//foo:all'
将列出生成的所有规则:
$ bazel query '//foo:all' | sort //foo:count_lines_a_test //foo:count_lines_b_test //foo:count_lines_c_test
选择
select( {conditionA: valuesA, conditionB: valuesB, ...}, no_match_error = "custom message" )
select()
是一个辅助函数,用于使规则属性可配置。它可以替换几乎所有属性赋值的右侧,因此其值取决于命令行 Bazel 标志。
例如,您可以使用此功能来定义特定于平台的依赖项,或者根据规则是在“开发者”模式还是“发布”模式下构建来嵌入不同的资源。
基本用法如下:
sh_binary( name = "mytarget", srcs = select({ ":conditionA": ["mytarget_a.sh"], ":conditionB": ["mytarget_b.sh"], "//conditions:default": ["mytarget_default.sh"] }) )
这使得 sh_binary
的 srcs
属性可配置,方法是将它的常规标签列表分配替换为 select
调用,该调用将配置条件映射到匹配的值。每个条件都是对 config_setting
或 constraint_value
的标签引用,如果目标的配置与一组预期值匹配,则表示“匹配”。然后,mytarget#srcs
的值会变为与当前调用匹配的任何标签列表。
注意:
- 在任何调用中,都只选择一个条件。
- 如果多个条件匹配,且其中一个条件是其他条件的专业化版本,则优先考虑专业化版本。如果条件 B 具有与条件 A 相同的所有标志和限制值,以及一些额外的标志或限制值,则认为条件 B 是条件 A 的专业化。这也意味着,专业化解析并非旨在创建排序,如下面的示例 2 所示。
- 如果多个条件匹配,但其中一个条件不是所有其他条件的特例,则 Bazel 会因错误而失败,除非所有条件都解析为相同的值。
- 如果没有其他条件匹配,则特殊伪标签
//conditions:default
会被视为匹配。如果省略此条件,则必须匹配其他规则,以免出错。 select
可以嵌入到较大的属性赋值中。因此,srcs = ["common.sh"] + select({ ":conditionA": ["myrule_a.sh"], ...})
和srcs = select({ ":conditionA": ["a.sh"]}) + select({ ":conditionB": ["b.sh"]})
是有效的表达式。select
适用于大多数属性,但并非所有属性。不兼容的属性会在其文档中标记为nonconfigurable
。子软件包
subpackages(include, exclude=[], allow_empty=True)
subpackages()
是一个辅助函数,类似于glob()
,但它列出的是子软件包,而不是文件和目录。它使用与glob()
相同的路径模式,并且可以匹配当前正在加载的 BUILD 文件的任何直接后代子软件包。如需详细了解包含和排除模式,请参阅 glob。返回的子软件包列表按排序顺序排列,包含相对于当前加载软件包的路径,这些路径与
include
中给定的模式匹配,但不与exclude
中的模式匹配。示例
以下示例列出了软件包
foo/BUILD
的所有直接子软件包# The following BUILD files exist: # foo/BUILD # foo/bar/baz/BUILD # foo/bar/but/bad/BUILD # foo/sub/BUILD # foo/sub/deeper/BUILD # # In foo/BUILD a call to subs1 = subpackages(include = ["**"]) # results in subs1 == ["sub", "bar/baz", "bar/but/bad"] # # 'sub/deeper' is not included because it is a subpackage of 'foo/sub' not of # 'foo' subs2 = subpackages(include = ["bar/*"]) # results in subs2 = ["bar/baz"] # # Since 'bar' is not a subpackage itself, this looks for any subpackages under # all first level subdirectories of 'bar'. subs3 = subpackages(include = ["bar/**"]) # results in subs3 = ["bar/baz", "bar/but/bad"] # # Since bar is not a subpackage itself, this looks for any subpackages which are # (1) under all subdirectories of 'bar' which can be at any level, (2) not a # subpackage of another subpackages. subs4 = subpackages(include = ["sub"]) subs5 = subpackages(include = ["sub/*"]) subs6 = subpackages(include = ["sub/**"]) # results in subs4 and subs6 being ["sub"] # results in subs5 = []. # # In subs4, expression "sub" checks whether 'foo/sub' is a package (i.e. is a # subpackage of 'foo'). # In subs5, "sub/*" looks for subpackages under directory 'foo/sub'. Since # 'foo/sub' is already a subpackage itself, the subdirectories will not be # traversed anymore. # In subs6, 'foo/sub' is a subpackage itself and matches pattern "sub/**", so it # is returned. But the subdirectories of 'foo/sub' will not be traversed # anymore.
一般来说,建议用户使用 skylib 的“子软件包”模块,而不是直接调用此函数。