Bazel的WORKSPACE与Bzlmod:外部依赖管理的比较

Bazel是一个开源的构建和测试工具,它提供了一种统一、人性化和高效的方式来管理项目的构建和测试。在Bazel中,有两种主要的方式来管理外部依赖:WORKSPACE和Bzlmod。这两种方式都有其优点和缺点,本文将对它们进行详细的比较。

WORKSPACE

WORKSPACE是Bazel最初的外部依赖管理系统。在WORKSPACE文件中,你可以声明你的项目依赖哪些外部存储库,并指定如何获取它们。WORKSPACE文件基本上是一系列的加载语句、存储库声明和函数调用。
然而,WORKSPACE有一些缺点。首先,它是逐行评估的,这意味着存储库的声明顺序可能会影响最终使用的版本。其次,WORKSPACE文件在外部存储库中不会递归评估,所以你需要声明你的直接依赖和间接依赖。此外,如果同一个存储库被多次声明,那么最后一个声明将胜出。

Bzlmod

由于WORKSPACE的这些缺点,Bazel引入了一个新的外部依赖管理系统:Bzlmod。Bzlmod在2022年12月发布的Bazel 6.0中正式推出,并计划在2023年底发布的Bazel 7.0中默认启用。

与WORKSPACE相比,Bzlmod提供了更多的功能和更好的用户体验。首先,Bzlmod自动解析传递依赖,使项目能够在保持快速和资源高效的同时进行扩展。其次,Bzlmod支持锁定文件(MODULE.bazel.lock),该文件包含解析后的模块依赖图和扩展评估结果的信息。此外,Bzlmod还引入了一个新命令(mod),允许用户查看外部依赖图,并详细查看模块、存储库和扩展。

然而,尽管Bzlmod提供了许多改进,但它仍然有一些挑战。例如,由于Bzlmod是新引入的,因此可能需要一些时间来适应新的语法和工作流程。此外,虽然Bazel团队正在努力改进Bzlmod,并计划在未来几年内逐步淘汰WORKSPACE,但这个过程可能会带来一些不确定性。

接下来对bzlmod的一种场景使用场景做一个说明

场景1bazel_dep,用于直接描述对外部bazel工程的依赖,并且外部工程也必须符合bzlmod方式

module(
    name = "my_awesome_project",
    version = "0.1.0",
    author = "A happy Bazel user",
    description = "Demo project",
    compatibility_level = "1",
    bazel_compatibility = ">=2.0.0;<=3.6.0;-2.2.0;-3.1.0",
)
bazel_dep(name = "guava", version = "29.0")
bazel_dep(name = "protobuf", version = "3.13.0",
          repo_name = "com_google_protobuf")
bazel_dep(name = "grpc", version = "1.27.0",
          repo_name = "com_github_grpc_grpc")
bazel_dep(name = "abseil-cpp", version = "20200225.2")
bazel_dep(name = "bazel_skylib", version = "1.0.3")

场景2:集成第三方包管理器

使用
bazel_dep(name = "rules_jvm_external", version = "1.0")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") 
maven.dep(coord="org.junit:junit:3.0")
maven.dep(coord="com.google.guava:guava:1.2")
maven.pom(pom_xml="//:pom.xml") 
use_repo(
    maven,
    "org_junit_junit",
    guava="com_google_guava_guava",
) 


定义:
# @rules_jvm_external//:extensions.bzl
load("//:repo_rules.bzl", "maven_single_jar")
def _maven_impl(ctx):
  coords = []
  for mod in ctx.modules:
    coords += [dep.coord for dep in mod.tags.dep]
  output = ctx.execute(["coursier", "resolve", coords])  # hypothetical call
  repo_attrs = process_coursier(output)
  [maven_single_jar(**attrs) for attrs in repo_attrs] 

maven_dep = tag_class(attrs = {"coord": attr.string()})
maven_pom = tag_class(attrs = {"pom_xml": attr.label()})
maven = module_extension(
    implementation=_maven_impl,
    tag_classes={"dep": maven_dep, "pom": maven_pom},
) 

场景3重写/patch现有外部依赖

# Depend on module lib_a at a local path.
# The bazel_dep statement is necessary because the root module must have visibility on lib_a.
bazel_dep(name = "lib_a", version="")
local_path_override(
    module_name = "lib_a",
    path = "./lib_a",
)
# Patch bazel skylib 1.2.0 with a local patch file.
bazel_dep(name = "bazel_skylib", version = "1.2.0")
= 1,
)
single_version_override(
    module_name = "bazel_skylib",
    patches = ["//:bazel_skylib.patch"],
    patch_strip 

场景4:自定义扩展

使用
librarian_extension = use_extension("@librarian//:librarian.bzl", "librarian_extension")

# Whether we should allow macros in MODULE.bazel is still in debate,
# but currently it works and helps simplifying `use_repo` usages.
def book(name, edition):
    librarian_extension.book(name=name, edition=edition)
    use_repo(librarian_extension, name)

book(name="the_great_gatsby", edition="1995.12")
book(name="hamlet", edition="2005.1")

librarian.bzl 定义
……
fetch_book = repository_rule(
    implementation = _fetch_book_impl,
    attrs = {
        "edition": attr.string(),
    },
)
……
book = tag_class(attrs={
    "name": attr.string(),
    "edition": attr.string(),
})
librarian_extension = module_extension(
    implementation = _librarian_extension_impl,
    tag_classes = {"book": book},
)

结论

总的来说,WORKSPACE和Bzlmod都是Bazel中管理外部依赖的有效方式。然而,由于各自的优点和缺点,它们可能会更适合不同类型或规模的项目。对于小型或简单项目来说,WORKSPACE可能已经足够满足需求。然而,对于大型或复杂项目来说,Bzlmod可能会提供更好的性能和可扩展性。

无论选择哪种方式,都需要注意遵循最佳实践,并确保理解所使用工具的工作原理和限制。通过这样做,可以确保项目构建和测试过程既高效又可靠。

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值