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

欢迎!请访问关于页面获取更多关于这个工作方式的详细信息。

+1
tools.build

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

  • :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) what gets compiled或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,应该预加载代码并消除竞争。但这会使你的脚本启动得更慢。
by
感谢对问题的解释!这非常有帮助。
by
我也遇到了与 https://github.com/clj-easy/graal-build-time 的竞争条件。
by
关于这里加载时的竞争条件,在 tools.build 的最新版本中应该已经修复。
...