本页面包含可帮助您将 Bazel 与 Java 项目搭配使用的资源。它链接到了一个教程、构建规则以及其他与使用 Bazel 构建 Java 项目相关的信息。
使用 Bazel
以下资源可帮助您在 Java 项目中使用 Bazel:
迁移到 Bazel
如果您目前使用 Maven 构建 Java 项目,请按照迁移指南中的步骤开始使用 Bazel 构建 Maven 项目:
Java 版本
有两个相关的 Java 版本,它们通过配置标志进行设置:
- 代码库中源文件的版本
- 用于执行代码和测试代码的 Java 运行时版本
配置代码库中源代码的版本
如果没有其他配置,Bazel 会假定代码库中的所有 Java 源文件都是使用单个 Java 版本编写的。如需指定代码库中源代码的版本,请将 build --java_language_version={ver} 添加到 .bazelrc 文件,其中 {ver} 例如为 11。Bazel 代码库所有者应设置此标志,以便 Bazel 及其用户可以引用源代码的 Java 版本号。如需了解详情,请参阅
Java 语言版本标志。
配置用于执行和测试代码的 JVM
Bazel 使用一个 JDK 进行编译,并使用另一个 JVM 来执行和测试代码。
默认情况下,Bazel 使用其下载的 JDK 编译代码,并使用本地计算机上安装的 JVM 执行和测试代码。Bazel 使用 JAVA_HOME 或路径搜索 JVM。
生成的二进制文件与系统库中本地安装的 JVM 兼容,这意味着生成的二进制文件取决于计算机上安装的内容。
如需配置用于执行和测试的 JVM,请使用 --java_runtime_version 标志。默认值为 local_jdk。
封闭测试和编译
如需创建封闭编译,您可以使用命令行标志 --java_runtime_version=remotejdk_11。代码将在从远程代码库下载的 JVM 上进行编译、执行和测试。如需了解详情,请参阅
Java 运行时版本标志。
配置 Java 中构建工具的编译和执行
还有另一对 JDK 和 JVM 用于构建和执行工具,这些工具在构建流程中使用,但不在构建结果中。该 JDK 和 JVM 使用 --tool_java_language_version 和 --tool_java_runtime_version 进行控制。默认值分别为 11 和 remotejdk_11
。
使用本地安装的 JDK 进行编译
Bazel 默认使用远程 JDK 进行编译,因为它会替换 JDK 的内部组件。使用本地安装的 JDK 的编译工具链已配置,但未使用。
如需使用本地安装的 JDK 进行编译,即使用本地 JDK 的编译工具链,请使用其他标志 --extra_toolchains=@local_jdk//:all,但请注意,这可能不适用于任意供应商的 JDK。
如需了解详情,请参阅 配置 Java 工具链。
最佳实践
除了 Bazel 的一般最佳实践 之外,以下是 Java 项目的特定最佳实践。
目录结构
首选 Maven 的标准目录布局(源代码位于 src/main/java 下,测试位于 src/test/java 下)。
BUILD 文件
创建 BUILD 文件时,请遵循以下准则:
每个包含 Java 源代码的目录使用一个
BUILD文件,因为这可以提高构建性能。每个
BUILD文件都应包含一个java_library规则,如下所示:java_library( name = "directory-name", srcs = glob(["*.java"]), deps = [...], )库的名称应为包含
BUILD文件的目录的名称。这会缩短库的标签,即使用"//package"而不是"//package:package"。源代码应为目录中所有 Java 文件的非递归
globof 。测试应位于
src/test下的匹配目录中,并依赖于此库。
为高级 Java 构建创建新规则
注意:创建新规则适用于高级构建和测试场景。开始使用 Bazel 时,您不需要创建新规则。
以下模块、配置片段和提供程序将帮助您 在构建 Java 项目时扩展 Bazel 的功能:
- 主要 Java 模块:
java_common - 主要 Java 提供程序:
JavaInfo - 配置片段:
java 其他模块:
配置 Java 工具链
Bazel 使用两种类型的 Java 工具链:
- 执行:用于执行和测试 Java 二进制文件,通过
--java_runtime_version标志进行控制 - 编译:用于编译 Java 源代码,通过
--java_language_version标志进行控制
配置其他执行工具链
执行工具链是 JVM(本地或来自代码库),其中包含有关其版本、操作系统和 CPU 架构的一些其他信息。
可以使用模块扩展中的 local_java_repository 或 remote_java_repository 代码库规则添加 Java 执行工具链。添加该规则后,可以使用标志来使用 JVM。如果为同一操作系统和 CPU 架构提供了多个定义,则系统会使用第一个定义。
MODULE.bazel 中本地 JVM 的配置示例:
custom_jdk = use_extension("@rules_java//java:extensions.bzl", "java_repository")
custom_jdk.local(
name = "additionaljdk", # Can be used with --java_runtime_version=additionaljdk, --java_runtime_version=11 or --java_runtime_version=additionaljdk_11
version = "11", # Optional, if not set it is autodetected
java_home = "/usr/lib/jdk-15/", # Path to directory containing bin/java
)
use_repo(custom_jdk, "additionaljdk")
register_toolchains("@additionaljdk//:all")
远程 JVM 的配置示例:
custom_jdk = use_extension("@rules_java//java:extensions.bzl", "java_repository")
custom_jdk.remote(
name = "openjdk_canary_linux_arm",
prefix = "openjdk_canary", # Can be used with --java_runtime_version=openjdk_canary_11
version = "11", # or --java_runtime_version=11
target_compatible_with = [ # Specifies constraints this JVM is compatible with
"@platforms//cpu:arm",
"@platforms//os:linux",
],
urls = ..., # Other parameters are from http_repository rule.
sha256 = ...,
strip_prefix = ...
)
use_repo(custom_jdk, "openjdk_canary_linux_arm", "openjdk_canary_linux_arm_toolchain_config_repo")
register_toolchains("@openjdk_canary_linux_arm_toolchain_config_repo//:all")
配置其他编译工具链
编译工具链由 JDK 和 Bazel 在编译期间使用的多个工具组成,这些工具提供其他功能,例如:Error Prone、严格的 Java 依赖项、标头编译、Android 脱糖、覆盖率插桩和 IDE 的 genclass 处理。
JavaBuilder 是 Bazel 捆绑的工具,用于执行编译并提供上述功能。实际编译由 JDK 的内部编译器执行。用于编译的 JDK 由工具链的 java_runtime 属性指定。
Bazel 会替换一些 JDK 内部组件。如果 JDK 版本 > 9,则使用 JDK 的标志 --patch_module 修补 java.compiler 和 jdk.compiler 模块。如果 JDK 版本为 8,则使用 -Xbootclasspath 标志修补 Java 编译器。
VanillaJavaBuilder 是 JavaBuilder 的第二种实现,它不会修改 JDK 的内部编译器,也没有任何其他功能。任何内置工具链都不会使用 VanillaJavaBuilder。
除了 JavaBuilder 之外,Bazel 在编译期间还会使用其他几个工具。
`ijar` 工具会处理 `jar` 文件,以移除除调用
签名之外的所有内容。生成的 jar 称为标头 jar。它们用于提高编译增量,方法是仅在函数正文发生更改时重新编译下游依赖项。
singlejar 工具会将多个 jar 文件打包到一个文件中。
genclass 工具会对 Java 编译的输出进行后处理,并生成一个 jar,其中仅包含由注解处理器生成的源代码的类文件。
JacocoRunner 工具会在检测到的文件上运行 Jacoco,并以 LCOV 格式输出结果。
TestRunner 工具会在受控环境中执行 JUnit 4 测试。
您可以通过将 default_java_toolchain 宏添加到
BUILD 文件并注册该宏来重新配置编译,方法是将 register_toolchains 规则添加到
MODULE.bazel 文件,或使用
--extra_toolchains 标志。
仅当 source_version 属性与 --java_language_version 标志指定的值匹配时,才会使用该工具链。
工具链配置示例:
load(
"@rules_java//toolchains:default_java_toolchain.bzl",
"default_java_toolchain", "DEFAULT_TOOLCHAIN_CONFIGURATION", "BASE_JDK9_JVM_OPTS", "DEFAULT_JAVACOPTS"
)
default_java_toolchain(
name = "repository_default_toolchain",
configuration = DEFAULT_TOOLCHAIN_CONFIGURATION, # One of predefined configurations
# Other parameters are from java_toolchain rule:
java_runtime = "@rules_java//toolchains:remote_jdk11", # JDK to use for compilation and toolchain's tools execution
jvm_opts = BASE_JDK9_JVM_OPTS + ["--enable_preview"], # Additional JDK options
javacopts = DEFAULT_JAVACOPTS + ["--enable_preview"], # Additional javac options
source_version = "9",
)
可以使用 --extra_toolchains=//:repository_default_toolchain_definition
或通过将 register_toolchains("//:repository_default_toolchain_definition")
添加到工作区来使用该工具链。
预定义配置:
DEFAULT_TOOLCHAIN_CONFIGURATION:所有功能,支持 JDK 版本 >= 9VANILLA_TOOLCHAIN_CONFIGURATION:没有其他功能,支持任意供应商的 JDK。PREBUILT_TOOLCHAIN_CONFIGURATION:与默认配置相同,但仅使用预构建工具(ijar、singlejar)NONPREBUILT_TOOLCHAIN_CONFIGURATION:与默认配置相同,但所有工具都是从源代码构建的(这在具有不同 libc 的操作系统上可能很有用)
配置 JVM 和 Java 编译器标志
您可以使用标志或
default_java_toolchain 属性配置 JVM 和 javac 标志。
相关标志包括 --jvmopt、--host_jvmopt、--javacopt 和
--host_javacopt。
相关 default_java_toolchain 属性包括 javacopts、jvm_opts、
javabuilder_jvm_opts 和 turbine_jvm_opts。
软件包专用 Java 编译器标志配置
您可以使用 default_java_toolchain 的 package_configuration 属性为特定源文件配置不同的 Java 编译器标志。
请参阅以下示例。
load("@rules_java//toolchains:default_java_toolchain.bzl", "default_java_toolchain")
# This is a convenience macro that inherits values from Bazel's default java_toolchain
default_java_toolchain(
name = "toolchain",
package_configuration = [
":error_prone",
],
visibility = ["//visibility:public"],
)
# This associates a set of javac flags with a set of packages
java_package_configuration(
name = "error_prone",
javacopts = [
"-Xep:MissingOverride:ERROR",
],
packages = ["error_prone_packages"],
)
# This is a regular package_group, which is used to specify a set of packages to apply flags to
package_group(
name = "error_prone_packages",
packages = [
"//foo/...",
"-//foo/bar/...", # this is an exclusion
],
)
单个代码库中的多个 Java 源代码版本
Bazel 仅支持在构建中编译单个版本的 Java 源代码。这意味着,在构建 Java 测试或应用时,所有 依赖项都是针对同一 Java 版本构建的。
不过,可以使用不同的标志执行单独的构建。
为了更轻松地使用不同的标志,您可以将特定版本的标志集与 .bazelrc 配置分组:
build:java8 --java_language_version=8
build:java8 --java_runtime_version=local_jdk_8
build:java11 --java_language_version=11
build:java11 --java_runtime_version=remotejdk_11
这些配置可以与 --config 标志搭配使用,例如
bazel test --config=java11 //:java11_test。