我在 Clojure 中编写了一个程序,我想在命令行上执行它,而不是在命令行上专门调用 java(例如java -jar
)。我想要一个可执行文件,例如myprogram
,接受任何参数并运行我的程序。这里有几件事可能会使这更容易:
- 可以假设 Java 已安装并且
java
在路径中。 - 尽管适用于 Windows 的解决方案将是一个很好的优势,但您可以假设这一切都是在类似 UNIX 的操作系统上完成的,例如 Mac OS X 或 Ubuntu。
- 可以在某种脚本中调用 Java。
- 可以使用用户可能安装或未安装的其他语言,例如 Ruby、Python 或 Perl。All-bash 会很酷,因为我可以假设人们拥有它。
- 如果我必须使用某种工具来构建将执行的二进制文件,那没关系,但我不是在寻找期望使用 GUI 界面操作的 .app 或 .exe 文件(例如,Oracle 的 appbundler 不是什么我在这里找)。
我已经用一种方法在这条路上走得很远,但我不得不想知道是否有更好的方法。
仅供参考:我已经尝试过的
我将在下面描述我的方法,但任何答案都不需要遵循这种方法。
我所做的是创建一个名为 lein 的插件makescript
,该插件生成一个 uberjar,base64 对其进行编码,并将其放在一个 Ruby 脚本中的一个所谓的 heredoc 变量中,如下所示:
# ...ruby script...
BASE64_JAR = <<-JAR_BOUNDARY
# [...base64 encoded file here...]
JAR_BOUNDARY
然后,您应该能够运行 ruby 脚本。它将获取 BASE64_JAR 变量,将其取消编码,将其放置在临时文件中,然后通过调用java -jar <filename>
.
我使用这种方法遇到的问题是 Ruby 的base64
库和 Clojure 的clojure.data.codec.base64
库似乎生成了不同的字符串来表示 jar,如果我使用 Ruby,由 Clojure 编码的字符串不会解码为原始文件。这可能与两种语言之间的字符串本身的编码(UTF-8 相关?也许)有关。以下是说明断开连接的 repl/irb 会话:
repl=> (def jar-contents (slurp "../target/myproj-0.1.0-SNAPSHOT-002-standalone.jar"))
repl=> (count jar-contents) ;; => 9433328
repl=> (def a-str (String. (clojure.data.codec.base64/encode (.getBytes jar-contents)) "UTF-8"))
repl=> (count a-str) ;; => 23265576
irb> f = File.open("target/pwgen-0.1.0-SNAPSHOT-002-standalone.jar", "r").read()
irb> p f.length # => 9657639
irb> b = Base64.encode64(f)
irb> p b.length # => 13564973
请注意,原始大小接近但不相同,但编码版本却大不相同。
虽然这很令人费解,我想知道为什么会发生这种情况,但我想我可以通过让 makescript 生成 uberjar 并将路径传递给另一个 Ruby 脚本,然后将 base64 编码(然后解码,也使用Ruby) 独立的 JAR。问题仍然存在:有没有更好、更简单的方法?我是否遗漏了一些明显的东西,或者这真的像看起来那么难?