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
 
最佳答案

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

by
很好,有没有某个地方记载了这方面的例子?或者有已知的开源项目采用了这种模式,我可以从中读取?

此 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)
    ...
原因:caused by: java.lang.IllegalAccessError: indexing-reader? does not exist
    在 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)
原因:java.lang.IllegalAccessError: whitespace? does not exist
    在 clojure.lang.Compiler.load(Compiler.java:7652)
    在 clojure.lang.RT.loadResourceScript(RT.java:381)
    ...
    在 clojure.core$refer.invokeStatic(core.clj:4237)
    在 clojure.core$refer.invokeStatic(core.clj:4237)
    在 clojure.core$refer.doInvoke(core.clj:4205)
```
by
这是一个代码加载的竞态条件问题。

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

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