Java 软件包指南
32 位 – CLR – CMake – Cross – DKMS – Eclipse – Electron – Font – Free Pascal – GNOME – Go – Haskell – Java – KDE – 内核模块 – Lisp – Meson – MinGW – Node.js – Nonfree – OCaml – Perl – PHP – Python – R – Ruby – Rust - Security – 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。