请在2024 Clojure状态调查!中分享您的想法。

欢迎!请查阅关于页面以获取更多关于本站运作方式的信息。

+1 投票
tools.build

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

  • :compile 包含编译所需的全部依赖项和类的类路径。

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

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

考虑以下简化的依赖图表

dep graph

我们只需要将蓝色依赖项放到jar中。白色“已提供”依赖项需要在编译时使用,但不应该出现在最终的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 投票
by
被选中 by
 
最佳答案

使用多个创建基础的调用是正确的——一个用于编译,一个用于uberjar。

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

这个片段包含了我尝试使用多个基础版本的尝试,但它抛出了我无法解释的异常
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)
```
by
这是代码加载过程中的一种竞争条件。

调用create-basis最终会导致Maven代码启动一个新线程,如果还未加载某些代码,则会加载它们。这种代码加载与在你的build.clj中发生的其他代码加载发生竞争,从而导致这些错误。

如果你需要在你的build.clj顶部需要状态,那么它将预加载代码并消除竞争。但这会使你的脚本启动速度变慢。
by
感谢对问题的解释!这非常有用。
by
也遇到了与https://github.com/clj-easy/graal-build-time相同的竞争条件。
by
在最新版本的tools.build中修复了加载这里的竞争条件。
...