请分享您的想法,参与2024年Clojure调查!

欢迎!请参见关于页面以获取更多有关其工作方式的信息。

+1投票
工具建设

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

  • :compile 编译所需的全部依赖/类路径。

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

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

考虑以下简化的依赖图

dep graph

我们只需要blue依赖项包含在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
 
最佳答案

使用多次调用 create-basis 是完全正确的 - 一个用于编译,一个用于 uberjar。

by
有益的知识。有没有哪里记录了这方面的例子?或者有没有可以使用这个模式的开源项目,我可以从中读取信息?

这个片段包含了我尝试使用多个 bases 的代码,但它抛出了我无法解释的异常
https://gist.github.com/erp12/7ea123fe4d623f61dcda94e3cbd03845#file-build-clj

有趣的是,根cause异常并不一致。下面是两个例子。
甚至更奇怪的是,即使 `jar-basis` 永远没有在任何 entry-point 函数中引用,这些异常也会被抛出。这似乎只是多次调用 `create-basis` 就会引发异常。

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

另一个错误的版本。

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

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

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