请在 2024 年 Clojure 调查 中分享您的想法!

欢迎!请参阅 关于 页面以了解此工作的更多信息。

+1
tools.build

我在理解如何使用 tools.build 控制 uberjar 的内容上有困难。在这种情况下,运行时环境(一个 Spark 集群)提供了一些依赖项,因此这些依赖项不应该在 uberjar 中。让我们假设我有两个类路径(表示为别名)

  • :compile 编译所需的所有依赖和类的类路径。

  • :uber 最终 jar 中应包含的依赖和类的类路径。

理论上,如果你从 :compile 类路径中减去 :uber 类路径,我们应该得到由运行时环境提供的类的集合。

考虑以下简化的依赖图

dep graph

我们只需要蓝色依赖项在 jar 中。白色 "provided" 依赖项需要在编译期间使用,但不应该出现在最终的 uberjar 中。

我查看了 compile-clj:filter-nses 选项,但它在瞬态依赖项上产生了问题。

  • 如果我们只过滤到 my.project,我们会丢失 some.other/depsome.transient/dep 的重要依赖。
  • 如果我们排除 org.apache(不可能使用 :filter-nses 实现,但在理论上可以做到),我们仍然会有 org.scala-lang/scala-library 留下来。

我还尝试了在编译和 jar 构建期间使用不同的 create-basis 调用。我不确定这是否有意义。

能否利用依赖图来控制 1) 什么被编译,或者 2) 什么类的放入 uberjar?任何建议都将非常感谢。

1 个回答

+2

选中
 
最佳答案

使用多次调用 create-basis 是正确的做法 - 一个用于编译,一个用于创建 uberjar。

很好的了解。有没有某处记录了这个例子?或者有已知开源项目使用这种模式,我可以从中阅读?

这个 gist 包含我尝试使用多个基础结构的方法,但是它抛出了我无法解释的异常
https://gist.github.com/erp12/7ea123fe4d623f61dcda94e3cbd03845#file-build-clj

有趣的是,根本原因异常并不一致。以下有两个示例。
甚至更奇怪的是,即使没有任何入口函数引用 `jar-basis`,这些异常也会发生。这表明多次调用 `create-basis` 就会导致异常。

```
线程 "Thread-7" 出现异常 java.lang.ExceptionInInitializerError
    在 clojure.tools.deps.alpha.util.S3TransporterFactory.triggerLoad(S3TransporterFactory.java:44)
    在 clojure.tools.deps.alpha.util.S3TransporterFactory.access$100(S3TransporterFactory.java:29)
    在 clojure.tools.deps.alpha.util.S3TransporterFactory$1.run(S3TransporterFactory.java:49)
    在 java.lang.Thread.run(Thread.java:748)
由:在 (clojure/tools/reader.clj:1:1) 编译时出现语法错误
    在 clojure.lang COMPILER.load(Compiler.java:7652)
    在 clojure.lang.RT.loadResourceScript(RT.java:381)
    ...
由: java.lang.IllegalAccessError: indexing-reader? 不存在
    在 clojure.core$refer.invokeStatic(core.clj:4237)
    在 clojure.core$refer.doInvoke(core.clj:4205)
```

另一个错误版本。

```
线程 "Thread-7" 出现异常 java.lang.ExceptionInInitializerError
    在 clojure.tools.deps.alpha.util.S3TransporterFactory.triggerLoad(S3TransporterFactory.java:44)
    在 clojure.tools.deps.alpha.util.S3TransporterFactory.access$100(S3TransporterFactory.java:29)
    在 clojure.tools.deps.alpha.util.S3TransporterFactory$1.run(S3TransporterFactory.java:49)
    在 java.lang.Thread.run(Thread.java:748)
由:在 (clojure/tools/reader/reader_types.clj:1:1) 编译时出现语法错误
    在 clojure.lang COMPILER.load(Compiler.java:7652)
    在 clojure.lang.RT.loadResourceScript(RT.java:381)
    ...
由:java.lang.IllegalAccessError: whitespace? 不存在
    在 clojure.core$refer.invokeStatic(core.clj:4237)
    在 clojure.core$refer.doInvoke(core.clj:4205)
```
这是代码加载的竞争条件。

调用create-basis最终会导致Maven代码启动另一个线程,如果尚未加载,就会加载一些代码。这种代码加载与在您的build.clj中发生的其他代码加载竞争,导致这些错误。

如果您的build.clj顶部需要clojure.tools.deps.alpha.util.s3-transporter命名空间,它应该预加载代码并消除竞争。但这样会使您的脚本启动变慢。
谢谢对你问题解释!这非常有帮助。
还遇到了与 https://github.com/clj-easy/graal-build-time 的竞争条件。
这里加载时的竞争条件应该在tools.build的最新版本中得到修复。
...