2024 Clojure 状况调查中分享您的想法!

欢迎!有关该工作方式的更多信息,请参阅关于页面。

0
工具
编辑

背景

  1. 我的构建在一个容器中运行,我在其中无法访问临时文件(无论构建成功与否,容器完成时会丢弃它们)。因此,我为 Clojure 命令的所有调用添加了 --report stderr 选项。
  2. 我的构建使用 Depstar 构建 uberjar,这需要使用 -X:... 选项。
  3. 我还使用 -Srepro 选项。

问题

当 Clojure 命令行包括 -X 选项时,似乎没有方法同时包含 --report stderr 选项。以下是一些示例输出

$ clojure --report stderr -Srepro -X:uberjar
WARNING: Use of -A with clojure.main is deprecated, use -M instead
{:clojure.main/message
 "Execution error (FileNotFoundException) at java.io.FileInputStream/open0 (FileInputStream.java:-2).\n-Srepro (No such file or directory)\n",
 :clojure.main/triage
 {:clojure.error/class java.io.FileNotFoundException,
  :clojure.error/line -2,
  :clojure.error/cause "-Srepro (No such file or directory)",
  :clojure.error/symbol java.io.FileInputStream/open0,
  :clojure.error/source "FileInputStream.java",
  :clojure.error/phase :execution},
 :clojure.main/trace
 {:via
  [{:type java.io.FileNotFoundException,
    :message "-Srepro (No such file or directory)",
    :at [java.io.FileInputStream open0 "FileInputStream.java" -2]}],
  :trace
  [[java.io.FileInputStream open0 "FileInputStream.java" -2]
   [java.io.FileInputStream open "FileInputStream.java" 219]
   [java.io.FileInputStream <init> "FileInputStream.java" 157]
   [java.io.FileInputStream <init> "FileInputStream.java" 112]
   [clojure.lang.Compiler loadFile "Compiler.java" 7575]
   [clojure.main$load_script invokeStatic "main.clj" 475]
   [clojure.main$script_opt invokeStatic "main.clj" 535]
   [clojure.main$script_opt invoke "main.clj" 530]
   [clojure.main$main invokeStatic "main.clj" 664]
   [clojure.main$main doInvoke "main.clj" 616]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.main main "main.java" 40]],
  :cause "-Srepro (No such file or directory)"}}

Execution error (FileNotFoundException) at java.io.FileInputStream/open0 (FileInputStream.java:-2).
-Srepro (No such file or directory)

前两个选项已交换

$ clojure -Srepro --report stderr -X:uberjar
WARNING: Use of -A with clojure.main is deprecated, use -M instead
{:clojure.main/message
 "Execution error (FileNotFoundException) at java.io.FileInputStream/open0 (FileInputStream.java:-2).\n-X:uberjar (No such file or directory)\n",
 :clojure.main/triage
 {:clojure.error/class java.io.FileNotFoundException,
  :clojure.error/line -2,
  :clojure.error/cause "-X:uberjar (No such file or directory)",
  :clojure.error/symbol java.io.FileInputStream/open0,
  :clojure.error/source "FileInputStream.java",
  :clojure.error/phase :execution},
 :clojure.main/trace
 {:via
  [{:type java.io.FileNotFoundException,
    :message "-X:uberjar (No such file or directory)",
    :at [java.io.FileInputStream open0 "FileInputStream.java" -2]}],
  :trace
  [[java.io.FileInputStream open0 "FileInputStream.java" -2]
   [java.io.FileInputStream open "FileInputStream.java" 219]
   [java.io.FileInputStream <init> "FileInputStream.java" 157]
   [java.io.FileInputStream <init> "FileInputStream.java" 112]
   [clojure.lang.Compiler loadFile "Compiler.java" 7575]
   [clojure.main$load_script invokeStatic "main.clj" 475]
   [clojure.main$script_opt invokeStatic "main.clj" 535]
   [clojure.main$script_opt invoke "main.clj" 530]
   [clojure.main$main invokeStatic "main.clj" 664]
   [clojure.main$main doInvoke "main.clj" 616]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.main main "main.java" 40]],
  :cause "-X:uberjar (No such file or directory)"}}

Execution error (FileNotFoundException) at java.io.FileInputStream/open0 (FileInputStream.java:-2).
-X:uberjar (No such file or directory)

最后,我使用的 CLI 工具版本

$ clojure --version
Clojure CLI version 1.10.3.855

所以问题是:我如何确保来自 -X 函数(在这种情况下为 Depstar,但可能是任何东西)的任何未捕获异常都被写入 stdout 或 stderr,而不会写入我无法访问的临时文件?难道 -X 放弃(令人烦恼)默认行为,将异常详细信息写入临时文件吗?

2 个回答

+1

“--report”选项是clojure.main(而非clojure CLI)的功能。(顺便提一下,clojure.main中也有用Java系统属性完成此操作的等效方法 - 请参阅https://clojure.org/reference/repl_and_main#_as_launcher

使用clj -X,您并不是使用clojure.main,因此此选项不存在。另外,-X不应执行写入临时文件的操作,所以我搞不清楚为什么您会用-X?

by
这让人们摸不着头脑,因为他们不知道(也许坦白地说,他们也不在乎)-X、-M、-A(过去)、-P等等之间的内部实现差异。这些选项的默认异常处理行为存在(不必要的)差异(一些依赖于clojure.main,其他可能依赖于JVM的默认异常处理机制)。

如果在这里应用“最小惊讶原则”,那就太理想了 - 不论使用哪些其他clj/clojure选项,都始终以相同的方式处理异常处理,并且可以通过某些一致通用的选项来覆盖默认处理机制。
by
考虑一下是否应该让-X的执行承担一些 clojure.main 的错误报告功能可能是个好办法。它们(有意)是不同的,但也许可以有更多的重叠。
by
由于我无法在评论中格式化代码,我将编辑我的答案以解决这个问题。
by
是的,我想不出任何好的理由要为调用的内容及其方式做出不同的响应。这似乎是一种导致混乱的方法。
by
https://clojure.atlassian.net/browse/TDEPS-193 登录以进一步思考这个问题。
by
我正在运行 `clojure -X:sql:dev development/cli-start` ,但是当它失败(由于端口未被占用)时,我得到了完整的报告在:/var/folders/06/46ycckp94db7k29k022nxjp00000gn/T/clojure-8233560807290300750.edn` 并将命令更改为 `env JAVA_OPTS='-Dclojure.main.report=stderr' clojure -X:sql:dev development/cli-start` 使得异常显示在行内。这是使用 `clojure --version` 1.11.1.1189(和Clojure 1.11.1)。因此这似乎直接与你所说的相矛盾,Alex?!
by
正如我在我的回答中所说

使用 -X 执行 clojure.main,但只对 CLI API 模型进行操作,然后调用一个或多个“执行函数”。你不能向它传递 --report,但可以使用 JVM 选项。
by
在此处的实现中已经多次发生变化。在当前版本中,-X/-T将通过 clojure.main 到执行函数。我目前将此视为一个实现细节(但可能是一个将坚持下去的实现细节)。我们有一个 jira,更全面地考虑这个问题,链接为 https://clojure.atlassian.net/browse/TDEPS-193
0
by
编辑了 by

depstar 使用外部命令运行 AOT 编译过程,并确实使用了 clojure.main,但没有简单的方法将 --report 传递给此子进程。然而,您可以将 :jvm-opts 传递给 depstar,它会将这些选项传输给子进程。

:jvm-opts '["-Dclojure.main.report=stderr"]'

depstar 自身会写入 stdout 和 stderr。

(! 650)-> clojure -X:jar :jar test.jar :main-class foo.bar :aot true
[main] WARN hf.depstar.uberjar - :aot is not recommended for a 'thin' JAR!
[main] INFO hf.depstar.uberjar - Compiling foo.bar ...
Execution error (FileNotFoundException) at user/eval136 (REPL:1).
Could not locate foo/bar__init.class, foo/bar.clj or foo/bar.cljc on classpath.

Full report at:
/var/folders/p1/30gnjddx6p193frh670pl8nh0000gn/T/clojure-13601013524882075105.edn

[main] ERROR hf.depstar.uberjar - Compilation of foo.bar failed!
[main] ERROR hf.depstar.uberjar - AOT FAILED

以下是提供了 JVM 选项的相同内容

(! 652)-> clojure -X:jar :jar test.jar :main-class foo.bar :aot true :jvm-opts '["-Dclojure.main.report=stderr"]'
[main] WARN hf.depstar.uberjar - :aot is not recommended for a 'thin' JAR!
[main] INFO hf.depstar.uberjar - Compiling foo.bar ...
{:clojure.main/message
 "Execution error (FileNotFoundException) at user/eval136 (REPL:1).\nCould not locate foo/bar__init.class, foo/bar.clj or foo/bar.cljc on classpath.\n",
 :clojure.main/triage
 {:clojure.error/class java.io.FileNotFoundException,
  :clojure.error/line 1,
  :clojure.error/cause
  "Could not locate foo/bar__init.class, foo/bar.clj or foo/bar.cljc on classpath.",
  :clojure.error/symbol user/eval136,
  :clojure.error/phase :execution},
 :clojure.main/trace
 {:via
  [{:type java.io.FileNotFoundException,
    :message
    "Could not locate foo/bar__init.class, foo/bar.clj or foo/bar.cljc on classpath.",
    :at [clojure.lang.RT load "RT.java" 462]}],
  :trace
  [[clojure.lang.RT load "RT.java" 462]
   [clojure.lang.RT load "RT.java" 424]
   [clojure.core$load$fn__6856 invoke "core.clj" 6115]
   [clojure.core$load invokeStatic "core.clj" 6114]
   [clojure.core$load doInvoke "core.clj" 6098]
   [clojure.lang.RestFn invoke "RestFn.java" 408]
   [clojure.core$load_one invokeStatic "core.clj" 5897]
   [clojure.core$compile$fn__6861 invoke "core.clj" 6125]
   [clojure.core$compile invokeStatic "core.clj" 6125]
   [clojure.core$compile invoke "core.clj" 6117]
   [user$eval136 invokeStatic "NO_SOURCE_FILE" 1]
   [user$eval136 invoke "NO_SOURCE_FILE" 1]
   [clojure.lang.Compiler eval "Compiler.java" 7181]
   [clojure.lang.Compiler eval "Compiler.java" 7136]
   [clojure.core$eval invokeStatic "core.clj" 3202]
   [clojure.main$eval_opt invokeStatic "main.clj" 488]
   [clojure.main$eval_opt invoke "main.clj" 482]
   [clojure.main$initialize invokeStatic "main.clj" 508]
   [clojure.main$null_opt invokeStatic "main.clj" 542]
   [clojure.main$null_opt invoke "main.clj" 539]
   [clojure.main$main invokeStatic "main.clj" 664]
   [clojure.main$main doInvoke "main.clj" 616]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.main main "main.java" 40]],
  :cause
  "Could not locate foo/bar__init.class, foo/bar.clj or foo/bar.cljc on classpath."}}

Execution error (FileNotFoundException) at user/eval136 (REPL:1).
Could not locate foo/bar__init.class, foo/bar.clj or foo/bar.cljc on classpath.


[main] ERROR hf.depstar.uberjar - Compilation of foo.bar failed!
[main] ERROR hf.depstar.uberjar - AOT FAILED

后续是关于 Alex 回答的评论

使用 -X 仅会执行 clojure.main,但这仅限于 CLI API 模拟,然后调用一个或多个 "exec 函数"。您无法将 --report 传递给它,但可以使用 JVM 选项。

(! 667)-> cat src/thrower.clj 
(ns thrower)

(defn bang [_] (throw (ex-info "foo" {})))

(! 668)-> clojure -X thrower/bang
Execution error (ExceptionInfo) at thrower/bang (thrower.clj:3).
foo

Full report at:
/var/folders/p1/30gnjddx6p193frh670pl8nh0000gn/T/clojure-248093423269602852.edn
(! 669)-> clojure -J-Dclojure.main.report=stderr -X thrower/bang
{:clojure.main/message
 "Execution error (ExceptionInfo) at thrower/bang (thrower.clj:3).\nfoo\n",
 :clojure.main/triage
 {:clojure.error/class clojure.lang.ExceptionInfo,
  :clojure.error/line 3,
  :clojure.error/cause "foo",
  :clojure.error/symbol thrower/bang,
  :clojure.error/source "thrower.clj",
  :clojure.error/phase :execution},
 :clojure.main/trace
 {:via
  [{:type clojure.lang.ExceptionInfo,
    :message "foo",
    :data {},
    :at [thrower$bang invokeStatic "thrower.clj" 3]}],
  :trace
  [[thrower$bang invokeStatic "thrower.clj" 3]
   [thrower$bang invoke "thrower.clj" 3]
   [clojure.lang.AFn applyToHelper "AFn.java" 154]
   [clojure.lang.AFn applyTo "AFn.java" 144]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.core$apply invokeStatic "core.clj" 667]
   [clojure.core$apply invoke "core.clj" 662]
   [clojure.run.exec$exec invokeStatic "exec.clj" 40]
   [clojure.run.exec$exec doInvoke "exec.clj" 35]
   [clojure.lang.RestFn invoke "RestFn.java" 423]
   [clojure.run.exec$_main invokeStatic "exec.clj" 169]
   [clojure.run.exec$_main doInvoke "exec.clj" 157]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.core$apply invokeStatic "core.clj" 667]
   [clojure.main$main_opt invokeStatic "main.clj" 514]
   [clojure.main$main_opt invoke "main.clj" 510]
   [clojure.main$main invokeStatic "main.clj" 664]
   [clojure.main$main doInvoke "main.clj" 616]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.main main "main.java" 40]],
  :cause "foo",
  :data {}}}

Execution error (ExceptionInfo) at thrower/bang (thrower.clj:3).
foo
by
谢谢!一般来说,我没有遇到过 depstar 抛出任何异常(例如,我会在之前的步骤中编译代码以确保它的有效性),所以我对它不太担心。但我更担心的是,在六个月后查看我的 bash 构建脚本,试图回忆为什么 `--report stderr` 出现在 `clojure` 几个调用中,而其他情况下不出现。
by
解决方案:在所有这些中都用 -J-Dclojure.main.report=stderr 替代! :)
...