教程:Bazel 入门
Bazel 是 Google 开发的开源构建和测试工具。 它旨在管理具有复杂依赖关系的大型代码库,支持多种语言和平台。 Bazel 自动化了编译代码、链接依赖项、运行测试和部署工件的过程。
本教程将帮助您在 IntelliJ IDEA 中开始使用 Bazel。 完成本教程后,您将:
创建新的 Bazel 项目
了解 Bazel 项目结构和术语
重命名存储库
向项目添加 Java 源代码
构建并运行项目
添加包含 Kotlin 源代码的包,并从 Java 代码中调用它
获取 JAR 构建产物
了解如何使用项目视图
前提
对于本教程,您只需要 Bazel 插件。
安装 Bazel 插件
按 Ctrl+Alt+S 打开设置,然后选择 。
打开 Marketplace 选项卡,找到 Bazel 插件,然后点击 安装。
重启 IDE 以激活插件。
创建项目
启动 IntelliJ IDEA。
如果打开欢迎屏幕,请单击 新建项目。
否则,请在主菜单中转到 。
为项目命名,并选择 Bazel 作为构建系统。

(可选)如果您希望对该项目进行版本控制,或稍后将其发布到 GitHub,请选中 创建 Git 仓库。
点击 创建(C)。
IntelliJ IDEA 会生成项目文件。 之后,它会与 Bazel 同步,这可能需要一段时间。
项目结构和术语
如果您是 Bazel 新手,以下是对该项目的快速导览。 其中介绍了定义 Bazel 项目构建方式的主要概念和文件。 在本教程的后续部分中,我们将引用这些概念和文件。
如果您已经熟悉 Bazel,您可以跳过本节。
主存储库
我们正在查看的项目称为 主存储库。 在 Bazel 术语中, 存储库是用于构建软件的文件集合。
由于我们正在构建 Java 应用程序,因此还需要其他存储库,例如编译器可执行文件和代码依赖项。
工作区
工作区是用于指代构建主存储库所需的全部存储库集合的术语。
模块
除了是一个存储库之外,我们的项目还是一个 模块。 模块是在 Bazel 中进行依赖管理的单位。 模块在 MODULE.BAZEL 文件中定义。
在我们的项目中, MODULE.BAZEL 文件以模块声明开头,用于设置该模块的名称和版本:
它将工作区声明为用于 bzlmod (Bazel 的内置依赖管理系统)的 Bazel 模块。 如果某个存储库随后发布到注册表,此声明会为其赋予唯一标识,大致相当于 Maven 的构件坐标。 这样,其他 Bazel 项目就可以将其用作依赖项。
然后, MODULE.BAZEL 指定用于处理 Java 语言以及外部 JVM 依赖项的依赖项:
这两个依赖项都会导入包含所需文件的存储库。 您可以通过引用模块来添加依赖项。 引用某个模块可能会导入单个存储库或一组存储库。
文件的其余部分会加载 Maven 扩展,指向来自 Maven Central 的库,并在工作区中以名称 @maven 公开生成的存储库:
您可以在 排除的目录 视图中找到生成的存储库,包括传递依赖。
包
包 是 Bazel 构建过程的单位。 包通过其目录中的 BUILD.BAZEL 文件来标识。
在我们的项目中,有两个 Bazel 包:一个用于源代码,另一个用于测试:

包包含所有子目录,但具有自己 BUILD.BAZEL 文件的子目录除外。
源代码包的 BUILD.BAZEL 包含以下内容:
load() 语句会从 defs.bzl 文件中导入符号(例如 java_binary)。 defs.bzl 来自我们通过 bazel_dep() 在 MODULE.BAZEL 文件中导入的 @rules_java 外部存储库。
导入符号后,我们便可以在此 BUILD.BAZEL 文件中使用它们。
package 内置函数用于设置包的元数据(目前仅设置可见性)。 在此情况下,包的可见性为 public ,因为测试需要引用该包。
java_binary 规则调用定义了名为 Main 的构建目标。 它会根据包中的 Java 源文件构建一个 JAR 文件。 main_class 属性指定应用程序的 main 类。 srcs 属性指定要包含到 JAR 中的源文件。
用于测试包的 BUILD.BAZEL 与之类似,但它额外添加了两个依赖项:一个用于 JUnit,另一个用于被测包。
在依赖坐标中, @maven 是存储库名称, junit_junit 是构件名称。
查看已导入的存储库
每个导入的模块对应项目中的一个或多个存储库。 您可以在 bazel-{project name}/external 文件夹中查看项目使用的存储库:

如果查看 BUILD.BAZEL 文件,您会发现其中的依赖项比 外部 文件夹中的存储库数量更少。 这是因为每个导入的模块可能还依赖于更多模块。 Bazel 会识别此类传递依赖并为您导入。
运行 Bazel 目标
在对项目进行更改之前,先确认它确实可以构建并运行。 有多种方式可以实现这一点。 在本教程中,我们将使用 Bazel 工具窗口。
在右侧边栏,单击 Bazel 图标。 您也可以从主菜单中选择 。

这将打开 Bazel 工具窗口,其中列出了在 BUILD.BAZEL 文件中定义的目标。
在 Bazel 工具窗口中,右键单击
Main目标并选择 运行。
或者,如果编辑器中当前显示有 main 方法,您可以使用装订区域图标运行它。

重命名存储库并同步更改
正如我们在 概览中所见, install() 调用会为每个已解析的构件生成一个 Bazel 外部存储库:
由于它将 名称 设为 maven ,我们将生成的存储库引用为 @maven ,例如 @maven//:junit_junit。
假设我们希望用另一个名称引用它,例如 第三方。 为此,让我们更改生成的存储库名称。
重命名生成的存储库
在 MODULE.BAZEL 中,更改
install()和use_repo()调用的名称属性。maven.install( # before # name = "maven", name = "third-party", # after artifacts = [ "junit:junit:4.13.2", ], repositories = [ "https://repo1.maven.org/maven2", ], ) # before # use_repo(maven, "maven") use_repo(maven, "third-party") # after在测试包的 BUILD.BAZEL 中,更改标签。 这是唯一引用该存储库名称的包。 否则,还必须更改所有其他引用。
java_test( name = "tests", srcs = glob(["**/*.java"]), test_class = "org.example.MainTest", deps = [ "//src/main/org/example:Main", # before # "@maven//:junit_junit", "@third-party//:junit_junit", # after ], )
更改项目配置后,您需要重新同步项目,以便 Bazel 获取这些更改。
重新同步项目
在 Bazel 工具窗口的工具栏上,单击 重新同步项目 按钮。

为依赖项添加源代码
如果我们转到用于测试包的 assertEquals() 方法的定义 Ctrl+B ,IntelliJ IDEA 会将我们带到该类文件的反编译版本。

这是因为导入的存储库仅包含已编译的类,而不包含其源代码。 让我们通过指示 Bazel 在生成存储库时同时包含源代码来解决此问题。
添加源代码
打开 MODULE.BAZEL 文件。
将
fetch_sources参数添加到install()调用中,如下所示:maven.install( name = "third-party", artifacts = [ "junit:junit:4.13.2", ], repositories = [ "https://repo1.maven.org/maven2", ], fetch_sources = True )
要使更改生效,我们需要再次重新同步项目。
重新同步项目
在 Bazel 工具窗口的工具栏上,单击 重新同步项目 按钮。

让我们再次尝试导航到 assertEquals() 定义 Ctrl+B:

现在这些是 JUnit 类的实际源代码,而不是反编译版本。
添加 Kotlin
作为下一步,让我们添加一个包含使用 Kotlin 的库的单独包。
添加 Kotlin 依赖项
添加构建规则
在 MODULE.BAZEL 文件中,将用于导入 Kotlin 规则(
rules_kotlin 2.1.8)的语句粘贴到文件顶部:module( name = "null", version = "0.1.0", ) bazel_dep(name = "rules_java", version = "8.10.0") bazel_dep(name = "rules_jvm_external", version = "6.7") bazel_dep(name = "rules_kotlin", version = "2.1.8") maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") maven.install( name = "third-party", artifacts = [ "junit:junit:4.13.2", ], repositories = [ "https://repo1.maven.org/maven2", ], fetch_sources = True ) use_repo(maven, "third-party")
添加规则后,我们可以继续添加一个包含 Kotlin 源代码的包来使用这些规则。
添加包含 Kotlin 源代码的包
切换到 项目 工具窗口。 选中根文件夹后,按 Alt+Insert ,或右键单击它并选择 。
为该包命名,例如
kotlin_lib。会出现一个新文件夹,其中包含一个 BUILD.BAZEL 文件。
在 kotlin_lib 文件夹中,创建以下文件夹结构: src/main/com/example/mylib 。
将 BUILD.BAZEL 移动到新创建的 src/main/com/example/mylib 中,并在其中添加以下代码:
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") package(default_visibility = ["//visibility:public"]) kt_jvm_library( name = "MyLib", srcs = ["MyLib.kt"] )与 Java 包类似,此代码:
从 Kotlin 的 Bazel 规则(
rules_kotlin)中导入kt_jvm_library规则。 这使 Kotlin JVM 库规则可在此 BUILD 文件中使用。将此包中目标的默认可见性设置为 public,这意味着项目中的任何其他 Bazel 包都可以依赖它们。
在 Bazel 中声明一个名为
MyLib的 Kotlin JVM 库目标。 该目标会将 MyLib.kt 编译为 JAR。
在 src/main/com/example/mylib 中,创建名为 MyLib.kt 的文件,并填入以下内容:
package com.example.mylib fun hello() = println("Hello from Kotlin")
同样,我们需要重新同步项目以应用更改。
重新同步项目
在 Bazel 工具窗口的工具栏上,单击 重新同步项目 按钮。

从 Java 调用 Kotlin
要从 Java 包调用该 Kotlin 库,我们需要在相应的 BUILD.BAZEL 文件中为其添加依赖项。
为 Kotlin 包添加依赖项
打开 项目 工具窗口 Alt+1。
在 src/main/org/example/BUILD.bazel 文件中,将
deps参数添加到java_binary调用中,如下所示:java_binary( name = "Main", main_class = "org.example.Main", srcs = glob(["**/*.java"]), deps = ["//kotlin_lib/src/main/com/example/mylib:MyLib"] )
然后,我们可以从 Java 包中调用 hello() 方法:
更新 Java 代码
在 src/main/org/example/Main.java 文件中,添加
导入语句以及库函数调用,如下所示:package org.example; import com.example.mylib.MyLibKt; // import the library public class Main { public static void main(String[] args) { System.out.println("Hello World!"); MyLibKt.hello(); // call the library function } public static int constant4() { return 4; } }
然后,再次重新同步项目:
重新同步项目
在 Bazel 工具窗口的工具栏上,单击 重新同步项目 按钮。

让我们运行该项目,并检查 Main Java 目标能否正确构建并运行。
构建并运行
在右侧边栏,单击 Bazel 图标。 您也可以从主菜单中选择 。
在 Bazel 工具窗口中,右键单击
Main目标并选择 运行。
运行 工具窗口将打开并显示以下输出:
获取 JAR
到目前为止,我们一直直接在 IntelliJ IDEA 中运行该项目。 让我们找到可用于在其他位置运行该项目的 JAR 文件。
打开 项目 工具窗口 Alt+1。
导航到 排除的目录 ,然后在 bazel-bin 下,找到与项目包结构相对应的目录。 在那里,您可以找到 Kotlin 库和主 Java 应用程序包的 JAR 文件。

更改项目视图
此示例项目较小。 但是,如果您日后使用 Bazel 构建更大的项目,那么有一项功能在这种情况下会很有用。
Bazel 的 项目视图允许您隐藏不相关的包,仅处理当前需要的包。 这使源代码更易于导航,并减少对开发环境的负载,这在巨型 monorepos 中很重要。
让我们假设当前正在处理 Kotlin 库,并希望隐藏 Java 包。 这为我们提供了一个机会,来检验该功能的实际效果。
首先,我们需要创建一个 .bazelproject 文件。 这种类型的文件用于定义 Bazel 的项目视图包含的内容。
创建 .bazelproject 文件
打开 项目 工具窗口 Alt+1。
在 .bazelbsp 文件夹下,找到 .bazelproject 文件。

其旁边的 已选择 标签表明这是当前正在使用的项目视图。
复制 .bazelbsp/.bazelproject 文件,并将其粘贴到项目根目录。
在新创建的 .bazelproject 文件中,将
目录属性更改为指向 kotlin_lib 文件夹,如下所示:derive_targets_from_directories: true directories: kotlin_lib在 项目 工具窗口中,右键单击此文件并选择 加载项目视图。

项目 工具窗口现在仅显示 kotlin_lib 包。