【问题标题】:Datanucleus enhancement with Bazel使用 Bazel 增强 Datanucleus
【发布时间】:2016-10-31 06:15:44
【问题描述】:

我正在尝试将 Maven 项目迁移到 Bazel,但遇到了 Datanucleus 增强方面的问题。

jar-file 构建完成后,Datanucleus 会查看其中并进行一些字节码操作以增强可持久化的类。在 Bazel 中执行此操作的方法是定义一条规则,该规则采用 java_library 规则的 *.jar 输出并创建库的新增强版本。

我遇到的问题是,对于我的规则,我需要来自外部库的 datanucleus-core 包。当我尝试通过$(location //third_party:datanucleus_core)genrule 访问它时,它指向一个没有类的jar:

(genrule) cmd = "echo $(location //third_party:datanucleus_core)"
bazel-out/local-fastbuild/bin/third_party/liborg_datanucleus_datanucleus_core.jar

(genrule) cmd = "jar tf $(location //third_party:datanucleus_core)"
META-INF/
META-INF/MANIFEST.MF

Bazel 在genrule 中从$(location //third_party:datanucleus_core) 解析的jar 文件仅包含具有以下内容的META-INF/MANIFEST.MF

Manifest-Version: 1.0
Created-By: blaze

我尝试使用java_binary 规则将正确的datanucleus_core.jar 添加到类路径中,但Datanucleus 就地增强了我的库并且无法将其更改写入磁盘(重写规则的输入文件)。此外,java_binary 规则不应该用于构建。

所以问题是在运行 Datanucleus 实用程序的 Bazel 中增强 jar 库的最佳方法是什么,该实用程序作为 Maven 存储库中的第三方依赖项提供?

Bazel 构建标签:0.3.2-homebrew,操作系统:OS X El Capitan (10.11.6),java:1.8.0_92

更新

Datanucleus 依赖声明:

# WORKSPACE
maven_jar(
    name = "org_datanucleus_datanucleus_core",
    artifact = "org.datanucleus:datanucleus-core:5.0.3",
)

# third_party/BUILD
java_library(
    name = "org_datanucleus_datanucleus_core",
    visibility = ["//visibility:public"],
    exports = ["@org_datanucleus_datanucleus_core//jar"],
)

(在我的问题中,我将 org_datanucleus_datanucleus_core 缩短为 datanucleus_core

【问题讨论】:

  • 没有增强过程会增强类IN A JAR。如果这就是你正在做的......你必须解包,增强,打包它们。

标签: datanucleus bazel


【解决方案1】:

正如 Neil Stockton 所提到的,您无法增强 jar 中的类。因此,基本策略将是:

  1. 创建 jar。
  2. 解压类文件。
  3. 运行增强功能。
  4. 将其备份。

第 2 步和第 3 步必须合并到第 4 步,因为 Bazel 坚持将所有输入和输出声明为构建规则(并且您不知道 .java 文件会生成哪些 .class 文件,因此 Bazel 总是将它们打包)。

创建一个datanucleus.bzl 文件来声明您的增强规则。它应该类似于:

# Run datastore enhancements on the java_library named "jarname".
def enhance(jarname):
  # src is the name of the jar file your java_library rule generates.
  src = "lib" + jarname + ".jar"
  native.genrule(
      name = jarname + "-enhancement",
      srcs = [
          src, 
          "//third_party:datanucleus_core"
      ],
      outs = [jarname + "-enhanced.jar"],
      cmd = """
# Un-jar the .class files.
jar tf $(location {0})
# Run the enhance.
classes=""
for $$class in $$(find . -name *.class); do
  java -cp {0}:$(location //third_party:datanucleus_core) $$class
  classes="$$classes $$class"
done
# jar them back up.
jar cf $@ $$classes""".format(src),
  )

(我对数据存储不太熟悉,所以cmd 可能需要一些修改,但应该是一般的想法。)

然后,在你的 BUILD 文件中,你会这样做:

java_library(
    name = "my-lib",
    srcs = glob(["*.java"]),
    deps = ["..."],
)

# import the rule you wrote.
load('//:datanucleus.bzl', 'enhance')
enhance("my-lib")

现在你可以这样做了:

bazel build //:my-lib-enhanced.jar

并在其他 java_ 规则中使用 my-lib-enhanced.jar 作为依赖项。

.bzl 文件的更多信息:https://bazel.build/versions/master/docs/skylark/concepts.html


根据 jar 编辑添加更多信息:

有几个选项可以获取包含 datanucleus 内容的 jar。首先,你不需要间接层:你可以说:

      srcs = [
          src, 
          "@datanucleus_core//jar"
      ],

这将为您提供实际的罐子。

如果出于某种原因,您需要将 jar 放在 third_party 中,您可以修改 third_party/BUILD 以创建一个 deploy jar,这是一个 java 二进制文件,它捆绑了它的所有依赖项部署(因为你实际上并不打算将它用作二进制文件,你可以使用任何你想要的主类名):

java_binary(
    name = "datanucleus-core",
    main_class = "whatever",
    runtime_deps = ["@org_datanucleus_datanucleus_core//jar"],
)

genrule(
    name = "your-lib",
    srcs = [":datanucleus-core_deploy.jar", ...],
)

:datanucleus-core_deploy.jar 被称为隐式目标:它仅在请求时构建,但可以从您的 java_binary 声明中生成。

【讨论】:

  • 感谢您的详细回答,@kristina。它真的很有用。我只关心一件事。正如我在问题中提到的,我无法从genrule 访问第三方jar。从$(location //third_party:datanucleus_core) 返回的jar-文件不包含任何类,因此如您的示例中那样将其添加到类路径中是行不通的。它只包含MANIFEST.MF,表示它是Created-By: blaze。您知道为什么会发生这种情况以及我该如何解决吗?我用我的 Datanucleus 依赖声明更新了帖子。
  • 我已经更新了我的答案以包含有关在类路径中获取 jar 的信息。 Java 库目标跟踪它们的依赖关系,但它们不会打包它们(如您所见)。否则 jar 会随着构建树的增加而变得越来越大,因此在“需要”它们之前不会包含它们,例如,在部署二进制文件中。
  • 完美!非常感谢,克里斯蒂娜!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-16
  • 2012-05-26
  • 2013-07-02
  • 1970-01-01
  • 2013-10-04
相关资源
最近更新 更多