
Bazel 的原生 Java 规则不支持直接消费 declare_directory 创建的树状目录(tree artifact),导致 java_library 无法识别其中的 .java 文件;正确做法是将生成的源码打包为 .srcjar 格式的 ZIP 归档,Java 规则即可自动解压并编译。
Bazel 的原生 Java 规则不支持直接消费 `declare_directory` 创建的树状目录(tree artifact),导致 `java_library` 无法识别其中的 `.java` 文件;正确做法是将生成的源码打包为 `.srcjar` 格式的 ZIP 归档,Java 规则即可自动解压并编译。
要使 java_library 正确编译由自定义规则生成的 java 源文件,关键在于避免直接传递 directory 类型的输出(即 tree artifact),而应将其封装为 bazel 明确支持的 source jar(.srcjar)。bazel 的 java 规则(如 java_library、java_binary)原生支持 .srcjar 文件:它们会自动解压其中的 .java 文件,并将其纳入编译路径。
✅ 正确实现步骤
-
保留原有代码生成逻辑,仍使用
ctx.actions.declare_directory创建临时目录; -
新增归档步骤:调用
ctx.actions.run或ctx.actions.run_shell,将生成的 Java 文件打包为.srcjar(即扩展名为.srcjar的 ZIP 文件); -
在 rule 返回值中提供该
.srcjar文件(而非目录),供java_library.srcs消费。
以下是修正后的规则示例:
def _generator_java_impl(ctx):
# Step 1: 声明生成目录
src_tree = ctx.actions.declare_directory(ctx.attr.name + "_srcs")
# Step 2: 执行实际代码生成(保持不变)
ctx.actions.run(
outputs = [src_tree],
executable = ctx.executable._generator_tool,
arguments = [src_tree.path],
# ... 其他参数(inputs, tools 等)
)
# Step 3: 将目录打包为 .srcjar(必须使用 .srcjar 后缀!)
srcjar = ctx.actions.declare_file(ctx.attr.name + ".srcjar")
ctx.actions.run_shell(
inputs = [src_tree],
outputs = [srcjar],
command = "zip -r $1 $2 && touch $1", # 确保 zip 非空且时间戳更新
arguments = [srcjar.path, src_tree.path],
)
# ✅ 关键:只返回 .srcjar 文件,不返回目录
return [DefaultInfo(files = depset([srcjar]))]
generator_java = rule(
implementation = _generator_java_impl,
attrs = {
"_generator_tool": attr.label(
executable = True,
cfg = "exec",
allow_files = True,
),
# 其他属性...
},
output_to_genfiles = True, # 推荐设为 True,确保 srcjar 输出到 genfiles 目录
)? BUILD 文件用法(保持简洁)
load("//tools/rules:generator.bzl", "generator_java")
generator_java(
name = "generated_java",
# ... 参数
)
java_library(
name = "mylib",
srcs = [":generated_java"], # ✅ 现在指向 .srcjar,可被正确编译
visibility = ["//visibility:public"],
)⚠️ 注意事项
-
.srcjar必须是合法 ZIP 文件,且内部路径需为标准 Java 包结构(如com/example/Foo.java),否则编译可能失败或包名解析异常; - 不要尝试在
java_library.srcs中混用目录和.srcjar—— Bazel 会忽略目录; - 若生成的 Java 文件含资源(如
resources/),.srcjar仅支持源码;资源需单独通过resources属性或java_library.resources提供; - 可使用
jar tf target.jar验证最终 JAR 是否包含预期类:bazel build //:mylib && jar tf bazel-bin/libmylib.jar。
通过将动态生成的源码标准化为 .srcjar,你既保留了灵活的代码生成能力,又完全兼容 Bazel 的 Java 生态链——编译、IDE 导入(如 bazel run @rules_jvm_external//:export_classpath)、测试与发布均可无缝衔接。


















