Java 软件包指南
32 位 – CLR – CMake – 交叉编译 – DKMS – Eclipse 插件 – Electron – 字体 – Free Pascal – GNOME – Go – Haskell – Java – KDE – 内核模块 – Lisp – Meson – MinGW – Node.js – 非自由软件 – OCaml – Perl – PHP – Python – R – Ruby – Rust - 安全 – Shell – VCS – Web 应用程序 – Wine
本文档定义了在 Arch Linux 下打包 Java 程序的建议标准。众所周知,Java 程序很难在没有依赖项重叠的情况下进行干净地打包。本文档描述了一种补救这种情况的方法。这些指南非常灵活,以涵盖处理 Java 应用程序时出现的许多不同情况。
简介
Arch Linux 打包者似乎无法就如何处理 Java 软件包达成一致。在官方和非官方仓库以及 AUR 中的 PKGBUILD 中使用了各种方法。这些解决方案包括将所有内容都放在 /opt
中,并在 /usr/bin
中放置 shell 脚本,或在 /etc/profile
中放置配置文件。其他解决方案则将内容放置在 /usr/share
中的目录中,并在 /usr/bin
中放置脚本。许多解决方案向系统 CLASSPATH
和 PATH
添加了不必要的文件。
典型 Java 应用程序的结构
大多数桌面 Java 应用程序都具有类似的结构。它们是从与系统无关(但与软件包相关!)的安装程序安装的。这通常将所有内容安装在一个目录中,该目录带有名为 bin
、lib
、jar
、conf
等的子目录。通常有一个包含主可执行类的主 jar 文件。通常会提供一个 shell 脚本来运行主类,这样用户就不必直接调用 Java 解释器。这个 shell 脚本通常非常复杂,因为它在各个发行版中是通用的,并且通常包含针对不同系统的特殊情况(例如,Cygwin)。
lib
目录通常包含捆绑的 jar 文件,这些文件满足 Java 应用程序的依赖项。这使用户可以轻松安装程序(包含所有依赖项),但却是软件包开发人员的噩梦。当多个软件包捆绑相同的依赖项时,这是一种空间浪费。过去,当桌面 Java 应用程序和库较少时,这不是一个大问题,而且那些存在的应用程序和库往往都非常大。现在情况不同了...
运行程序所需的其他文件通常存储在与主 jar 文件相同的文件夹中,或其子目录中。由于 Java 程序不知道它们的类是从哪里加载的,因此它们通常需要从该目录中运行(即 shell 脚本应 cd
到该目录中),或者设置一个环境变量来指示目录的位置。
Arch Linux 上的 Java 打包
在 Arch 中打包 Java 应用程序对于打包者来说将比目前需要做更多的工作。但是,这种努力是值得的,它将带来更干净的文件系统和更少的捆绑依赖项(随着越来越多的 Java 库被重构为它们自己的软件包,打包将变得更容易)。在创建 Arch Linux Java 软件包时,应遵循以下准则
- 如果 Java 库具有通用名称,则软件包名称应以标题
java-
开头,以帮助将其与其他库区分开来。对于唯一命名的软件包(如 JUnit)、最终用户程序(如 Eclipse)或可以使用另一个前缀唯一描述的库(如 jakarta-commons-collections 或 apache-ant),则不需要这样做。
- 将随程序分发的所有 jar 文件(且不包含其他文件)放置在
/usr/share/java/myprogram
目录中。这包括随应用程序分发的所有依赖项 jar 文件。但是,应努力将常用或大型依赖库放入其自己的软件包中。只有当程序不依赖于特定版本的依赖库时,这才有可能实现。
- 此规则使得可以迭代地重构依赖项。也就是说,软件包及其所有依赖项最初可以放在一个目录中。在对此进行测试后,可以一次重构一个主要依赖项。请注意,某些应用程序在主 jar 文件中包含捆绑的依赖项。也就是说,它们解压捆绑的依赖项并将其包含在主 jar 中。此类依赖项通常非常小,重构它们没有什么意义。
- 如果程序旨在由用户运行,请编写一个自定义 shell 脚本来运行主 jar 文件。此脚本应放置在
/usr/bin
中。库通常不需要 shell 脚本。从头开始编写 shell 脚本,而不是使用程序捆绑的脚本。删除测试自定义环境(如 Cygwin)的代码,以及尝试确定是否已设置JAVA_HOME
的代码(Arch 不使用JAVA_HOME
,而是使用archlinux-java
来设置/usr/bin/java
符号链接)。
- 对于 jar 文件,此类脚本应如下所示
#!/bin/sh exec /usr/bin/java -jar '/usr/share/java/myprogram/myprogram.jar' "$@"
- 对于单个类文件,此类脚本应如下所示
#!/bin/sh exec /usr/bin/java '/usr/share/java/myprogram/myprogramclassname' "$@"
- 除非有明确的原因不这样做(即:
CLASSPATH
用作插件机制),否则请使用 Java 解释器的-cp
选项设置CLASSPATH
。CLASSPATH
应包括/usr/share/java/myprogram
目录中的所有 jar 文件,以及从已重构到其他目录的依赖库中的 jar 文件。您可以使用如下代码
for name in /usr/share/java/myprogram/*.jar ; do CP=$CP:$name done CP=$CP:/usr/share/java/dep1/dep1.jar java -cp $CP myprogram.java.MainClass
- 确保 shell 脚本是可执行的!
- 随软件包分发的其他文件应存储在
/usr/share
下以软件包命名的目录中。您可能需要在 shell 脚本中设置此目录的位置,例如使用MYPROJECT_HOME
变量。此指南假定程序期望所有文件都位于同一目录中(Java 软件包的标准做法)。如果将配置文件放在其他位置(例如,/var/log
中的日志)看起来更自然,那么请随意这样做。
- 请记住,在某些系统上,
/usr
可能以只读方式挂载。如果共享目录中有需要由应用程序写入的文件,则可能必须将它们重新定位到/etc
、/var
或用户的主目录。
- 与 Arch Linux 软件包的标准一样,如果无法在不进行大量工作的情况下遵守上述标准,则应以其首选方式安装软件包,并将结果目录放置在
/opt
中。这对于捆绑 JRE 或包含自定义版本的依赖项,或执行其他奇怪或痛苦任务的程序很有用。
多种 API 实现
如果您的软件包分发常用 API 实现(如 jdbc 驱动程序),则应将库放置在 /usr/share/java/apiname
下。以便允许用户从各种实现中进行选择的应用程序知道在哪里查找它们。仅对原始库软件包使用此位置。如果此类实现是应用程序分发的一部分,请勿将此 jar 文件放置在公共位置下,而应使用普通软件包结构。
目录结构示例
为了澄清,以下是名为 foo
的假设程序的目录结构示例。由于 foo
是一个常用名称,因此软件包被命名为 java-foo
,但请注意这并未反映在目录结构中
/usr/share/java/foo/
/usr/share/java/foo/foo.jar
/usr/share/java/foo/bar.jar
(java-foo
的包含依赖项)/usr/share/foo/
/usr/share/foo/*.*
(java-foo
所需的一些通用文件)/usr/bin/foo
(可执行 shell 脚本)
依赖
Java 软件包可能会根据其需要指定 java-runtime
或 java-environment
作为依赖项。对于大多数软件包,java-runtime
(Java 运行时环境) 是运行用 Java 编写的软件所需的。java-environment
(Java 开发工具包) 是需要将 Java 源代码编译为字节码的软件包所需的。
有关更多信息,请参阅 Java。