函数

报告问题 查看源代码

目录

package

package(default_deprecation, default_package_metadata, default_testonly, default_visibility, features)

此函数声明适用于软件包中每条规则的元数据。它在软件包(BUILD 文件)中最多使用一次。

对于声明应用于整个代码库中每条规则的元数据的对应项,请使用代码库根目录下 REPO.bazel 文件中的 repo() 函数。repo() 函数采用与 package() 完全相同的参数。

应在文件顶部所有 load() 语句之后、任何规则之前调用 package() 函数。

参数

属性 说明
default_applicable_licenses

default_package_metadata 的别名。

default_visibility

标签列表;默认值为 []

此软件包中规则的默认可见性。

除非在规则的 visibility 属性中另行指定,否则此软件包中的每个规则都具有此属性中指定的可见性。如需详细了解此属性的语法,请参阅可见性文档。 软件包默认的可见性不适用于 exports_files(默认情况下是公开的)。

default_deprecation

字符串;默认值为 ""

为此软件包中的所有规则设置默认的 deprecation 消息。

default_package_metadata

标签列表;默认值为 []

设置元数据目标的默认列表,这些目标将应用于软件包中的所有其他目标。 这些目标通常是与 OSS 软件包和许可声明相关的目标。有关示例,请参阅 rules_license

default_testonly

布尔值;除非另有说明,否则默认值为 False

为此软件包中的所有规则设置默认的 testonly 属性。

javatests 下的软件包中,默认值为 True

features

列出字符串;默认值为 []

设置影响此 BUILD 文件语义的各种标记。

此功能主要由构建系统的工作人员使用,用于标记需要进行某种特殊处理的软件包。除非构建系统中的人员明确请求,否则请勿使用此属性。

示例

以下声明声明此软件包中的规则仅对软件包组 //foo:target 的成员可见。规则的各个可见性声明(如果存在)会覆盖此规范。
package(default_visibility = ["//foo:target"])

package_group

package_group(name, packages, includes)

此函数定义一组软件包,并将标签与该集相关联。可以在 visibility 属性中引用该标签。

软件包组主要用于控制可见性。源代码树中的每个软件包都可以引用公开可见的目标。非公开可见目标只能在其自己的软件包(而非子软件包)中引用。在这些极端情况之间,目标可能允许访问自己的软件包以及一个或多个软件包组描述的任何软件包。如需详细了解可见性系统,请参阅 visibility 属性。

如果给定软件包与 packages 属性匹配,或已包含在 includes 属性中提及的其他软件包组中,该软件包就会被视为在该组中。

从技术上讲,软件包组是目标,而不是由规则创建的,并且本身不具有任何可见性保护。

参数

属性 说明
name

名称(必填)

此目标的唯一名称。

packages

字符串列表;默认值为 []

零个或多个软件包规范的列表。

每个软件包规范字符串都可以采用以下格式之一:

  1. 软件包的完整名称,不包括其代码库,以双斜杠开头。例如,//foo/bar 指定具有该名称且与软件包组位于同一代码库中的软件包。
  2. 如上所述,但带有尾随 /...。例如, //foo/... 指定 //foo 集及其所有子软件包。//... 指定当前代码库中的所有软件包。
  3. 字符串 publicprivate,分别指定每个软件包或不指定软件包。(此表单要求设置 --incompatible_package_group_has_public_syntax 标志。)

此外,前两种软件包规范还可以带有 - 前缀,以表示它们已被否定。

软件包组中包含与至少一个肯定规范及其所有否定规范匹配的任何软件包。例如,值 [//foo/..., -//foo/tests/...] 包含所有既不是 //foo/tests 子软件包的 //foo 子软件包。(包含 //foo 本身,但不包含 //foo/tests 本身。)

除了公开可见之外,没有办法直接在当前代码库之外指定软件包。

如果缺少此属性,等同于将其设置为空列表,也等同于将其设置为仅包含 private 的列表。

注意:在 Bazel 6.0 之前,规范 //... 的旧版行为与 public 相同。启用 --incompatible_fix_package_group_reporoot_syntax(这是 Bazel 6.0 之后的默认设置)后,此行为得到修复。

注意:在 Bazel 6.0 之前,当此属性作为 bazel query --output=proto(或 --output=xml)的一部分进行序列化时,前导斜杠会被省略。例如,//pkg/foo/... 将输出为 \"pkg/foo/...\"。启用 --incompatible_package_group_includes_double_slash(这是 Bazel 6.0 之后的默认设置)后,此行为得到修复。

includes

标签列表;默认值为 []

此软件包中包含的其他软件包组。

此属性中的标签必须引用其他软件包组。引用的软件包组中的软件包将被视为此软件包组的一部分。这将具有传递性 - 如果软件包组 a 包含软件包组 b,而 b 包含软件包组 c,则 c 中的每个软件包也将是 a 的成员。

请注意,当与否定软件包规范结合使用时,系统会先独立计算每个组的软件包集,然后将结果联合在一起。也就是说,一个组中的否定规范不会影响另一个组中的规范。

示例

以下 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() 指定属于此软件包且导出到其他软件包的文件列表。

软件包的 BUILD 文件只能直接引用属于其他软件包的源文件,前提是这些文件已通过 exports_files() 语句显式导出。详细了解文件可见性

作为旧版行为,在规则的输入中提及的文件还会以默认可见性导出,直到标志 --incompatible_no_implicit_file_export 被翻转。不过,您不应依赖此行为,并主动迁移此类行为。

参数

该参数是当前软件包中的文件名称列表。您还可以指定可见性声明;在这种情况下,文件将对指定的目标可见。如果未指定可见性,则文件对每个软件包都可见,即使在 package 函数中指定了软件包默认可见性也是如此。您也可以指定许可

示例

以下示例会导出 golden.txt(来自 test_data 软件包的文本文件),以便其他软件包使用它,例如在测试的 data 属性中。

# from //test_data/BUILD

exports_files(["golden.txt"])

glob

glob(include, exclude=[], exclude_directories=1, allow_empty=True)

Glob 是一个辅助函数,可查找与特定路径模式匹配的所有文件,并返回其路径的可变且已排序的新列表。glob 只会搜索自己软件包中的文件,并且只会查找源文件(而不是生成的文件或其他目标)。

如果源文件的软件包相对路径与任何 include 格式匹配,而与任何 exclude 格式均不匹配,则结果中会包含源文件的标签。

includeexclude 列表包含相对于当前软件包的路径模式。每个模式可以包含一个或多个路径段。与往常一样使用 Unix 路径时,这些段以 / 分隔。格式中的线段与路径中的线段进行匹配。片段可能包含 * 通配符:匹配路径段中的任何子字符串(即使是空子字符串),不包括目录分隔符 /。此通配符可以在一个路径段中多次使用。此外,** 通配符可以匹配零个或零个以上完整的路径段,但必须声明为独立的路径段。

示例:
  • foo/bar.txt 与此软件包中的 foo/bar.txt 文件完全匹配(除非 foo/ 是子软件包)
  • 如果文件以 .txt 结尾,则 foo/*.txt 会匹配 foo/ 目录中的每个文件(除非 foo/ 是子软件包)
  • foo/a*.htm* 会匹配 foo/ 目录中以 a 开头,然后具有任意字符串(可以为空),再包含 .htm,并以另一个任意字符串结尾(除非 foo/ 是子软件包)的每个文件;例如 foo/axx.htmfoo/a.htmlfoo/axxx.html
  • foo/* 会匹配 foo/ 目录中的每个文件(除非 foo/ 是子软件包);它不匹配 foo 目录本身,即使 exclude_directories 设置为 0 也是如此
  • foo/** 匹配软件包的第一级子目录 foo/ 下每个非子软件包子目录中的每个文件;如果 exclude_directories 设置为 0,foo 目录本身也与该模式匹配;在这种情况下,** 被视为匹配零路径段
  • **/a.txt 会匹配此软件包目录中的 a.txt 文件以及非子软件包子目录。
  • 如果生成的路径上至少有一个目录称为 bar,例如 xxx/bar/yyy/zzz/a.txtbar/a.txt(请注意,** 也匹配零个段)或 bar/zzz/a.txt,则 **/bar/**/*.txt 会匹配此软件包的每个非子软件包子目录中的每个 .txt 文件
  • ** 与此软件包的每个非子软件包子目录中的每个文件匹配
  • foo**/a.txt 是无效的格式,因为 ** 必须独立作为一个片段
  • foo/ 是无效模式,因为在 / 之后定义的第二个段是空字符串

如果 exclude_directories 参数已启用(设置为 1),结果中将省略目录类型的文件(默认值为 1)。

如果 allow_empty 参数设置为 False,且结果为空列表,则 glob 函数将出错。

以下是一些重要的限制和注意事项:

  1. 由于 glob() 在 BUILD 文件评估期间运行,因此 glob() 只会匹配源代码树中的文件,而不会匹配生成的文件。如果要构建同时需要源文件和生成的文件的目标,则必须将生成的文件明确列表附加到 glob。请参阅下面的 :mylib:gen_java_srcs示例

  2. 如果某条规则与匹配的源文件同名,则该规则将“覆盖”该文件。

    为理解这一点,请记住 glob() 会返回一个路径列表,因此在其他规则的属性(例如 srcs = glob(["*.cc"]))中使用 glob() 与明确列出匹配的路径的效果相同。例如,如果 glob() 生成 ["Foo.java", "bar/Baz.java"],但软件包中还有一个名为“Foo.java”的规则(这是允许的,但 Bazel 会发出警告),则 glob() 的使用方将使用“Foo.java”规则(其输出),而不是“Foo.java”文件。如需了解详情,请参阅 GitHub 问题 10395

  3. glob 可能会匹配子目录中的文件。子目录名称可使用通配符。但是...
  4. 标签不能跨越软件包边界,并且 glob 与子软件包中的文件不匹配。

    例如,如果 x/y 作为软件包存在(作为 x/y/BUILD 或软件包路径上的其他位置),则软件包 x 中的 glob 表达式 **/*.cc 不会包含 x/y/z.cc。这意味着 glob 表达式的结果实际上取决于 BUILD 文件的存在,也就是说,如果没有名为 x/y 的软件包,或使用 --deleted_packages 标志将该软件包标记为已删除,则同一 glob 表达式将包含 x/y/z.cc

  5. 上述限制适用于所有 glob 表达式,无论它们使用何种通配符。
  6. 文件名以 . 开头的隐藏文件由 *** 通配符完全匹配。如果要将隐藏文件与复合模式相匹配,您的模式需要以 . 开头。例如,*.*.txt 将与 .foo.txt 匹配,但 *.txt 不匹配。 隐藏目录的匹配方式也相同。隐藏目录可能包含不需要作为输入的文件,并且可能会增加不必要的全球化文件数量和内存消耗。如需排除隐藏目录,请将其添加到“排除”列表参数中。
  7. “**”通配符有一个极端情况:模式 "**" 与软件包的目录路径不匹配。也就是说,glob(["**"], exclude_directories = 0) 会严格以传递方式匹配当前软件包目录下的所有文件和目录(当然不会进入子软件包的目录 - 请参阅前文对此的说明)。

一般情况下,您应该尝试提供适当的扩展名(例如 *.html),而不是为 glob 模式使用裸“*”。更明确的名称既是自记录文档,又能确保您不会意外匹配备份文件或 emacs/vi/... 自动保存的文件。

编写构建规则时,您可以枚举 glob 的元素。例如,这样就可以为每个输入生成单独的规则。请参阅下面的展开的 glob 示例部分。

Glob 示例

创建一个根据此目录中的所有 java 文件以及 :gen_java_srcs 规则生成的所有文件构建的 Java 库。

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"]),
)

创建一个根据此目录中以及所有子目录(路径包含名为“testing”的目录除外)中的所有 java 文件构建的库。如果可能,应避免使用这种模式,因为它会减少构建增量,从而增加构建时间。

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 的常规标签列表分配替换为将配置条件映射到匹配值的 select 调用,使得 sh_binarysrcs 属性可配置。每个条件都是对 config_settingconstraint_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 的“subpackages”模块,而不是直接调用此函数。