Java パッケージガイドライン
32ビット – CLR – クロス – Eclipse – Electron – Free Pascal – GNOME – Go – Haskell – Java – KDE – カーネル – Lisp – MinGW – Node.js – ノンフリー – OCaml – Perl – PHP – Python – R – Ruby – Rust – VCS – ウェブ – Wine
この文章では Arch Linux における Java プログラムのパッケージングについて提唱される標準を定義しています。Java プログラムは依存関係を重ねないようにパッケージ化するのが大変難しいことで有名です。この文章ではそれをなんとか改善する方法を説明します。Java アプリケーションを扱うときは様々な場面に合わせて以下のガイドラインを臨機応変に読み替えてください。
イントロダクション
Java パッケージの処理方法について Arch Linux のパッケージ作成者が合意を取ることは不可能です。公式・非公式リポジトリや AUR に存在する PKGBUILD では様々な手段が使われています。/opt
に全てのファイルを配置して、/usr/bin
にシェルスクリプトを置いたり /etc/profile
にプロファイルを保存する方法もあれば、/usr/share
のディレクトリにファイルを配置して /usr/bin
にスクリプトを置いているのもあります。システムの CLASSPATH
や PATH
には多くの不必要なファイルが追加されています。
典型的な Java アプリケーションの構造
ほとんどの Java のデスクトップアプリケーションは同じような構造をしています。これらのアプリケーションはシステムに依存しない (ただしパッケージに依存する) インストーラーによってインストールされます。大抵は全てを単一のディレクトリにインストールして bin
, lib
, jar
, conf
などのサブディレクトリが作られます。通常、メインの jar ファイルが存在していて、メインの実行クラスが記述されています。また、Java インタプリタを直接実行しなくてもいいように、メインクラスを実行するためのシェルスクリプトも付属しているのが普通です。シェルスクリプトは様々なディストリビューションで共通して用いられ、別の環境 (例: Cygwin) 用の特殊なケースもカバーするようになっている場合が多く、非常に複雑になっています。
lib
ディレクトリには jar ファイルがバンドルされているのが通例で、Java アプリケーションの依存関係を満たすようになっています。(全ての依存パッケージが含まれていることは) プログラムをインストールしたいユーザーにとっては楽ですが、パッケージの開発者にとっては悪夢です。同一の依存パッケージを複数のパッケージがバンドルしていることは無駄に他なりません。Java のデスクトップアプリケーションやライブラリの数が少なかった昔はそこまで大きな問題ではありませんでしたが、Java アプリケーションとライブラリの数が増えるにつれて避けがたい問題になっています。時代は変わったのです。
プログラムを実行するのに必要な他のファイルはメインの jar ファイルと同じフォルダに保存されるか、サブディレクトリに保存されるのが普通です。Java プログラムはどこからクラスがロードされたか関知しないので、jar ファイルがあるディレクトリ内から実行しなくてはなりません (シェルスクリプトでディレクトリに cd
する必要がある)。あるいは、ディレクトリの場所を示す環境変数を設定します。
パッケージング
Packaging Java applications in Arch is going to take quite a bit more work for packagers than it currently does. The effort will be worth it, however, resulting in a cleaner filesystem and fewer bundled dependencies (as more and more Java libraries are refactored into their own packages, packaging will become easier). The following guidelines should be followed in creating an Arch Linux Java package:
- If a Java library has a generic name, the package name should be prepended with the title
java-
to help distinguish it from other libraries. This is not necessary with uniquely named packages (like JUnit), end-user programs (like Eclipse), or libraries that can be uniquely described with another prefix (like jakarta-commons-collections or apache-ant).
- You do not need to compile Java applications from source. Very little optimization goes into the compile process, as with gcc created binaries. If the source package provides an easy way to build from source go ahead and use it, but if its easier to just grab a binary release of a jar file or an installer you may use that as well.
- Place all jar files (and no other files) distributed with the program in a
/usr/share/java/myprogram
directory. This includes all dependency jar files distributed with the application. However, effort should be made to place common or large dependency libraries into their own packages. This can only happen if the program does not depend on a specific version of a dependency library.
This rule makes it possible to iteratively refactor dependencies. That is, the package and all its dependencies can be placed into one directory at first. After this has been tested, major dependencies can be refactored out one at a time. Note that some applications include bundled dependencies inside the main jar file. That is, they unjar the bundled dependencies and include them in the main jar. Such dependencies are usually very small and there is little point in refactoring them.
- If the program is meant to be run by the user, write a custom shell script that runs the main jar file. This script should be placed in
/usr/bin
. Libraries generally do not require shell scripts. Write the shell script from scratch, rather than using one that is bundled with the program. Remove code that tests for custom environments (like Cygwin), and code that tries to determine ifJAVA_HOME
has been set (Arch does not useJAVA_HOME
, it usesarchlinux-java
to set the/usr/bin/java
symlink).
such script should look like this for jar files:
#!/bin/sh exec /usr/bin/java -jar '/usr/share/java/PROGRAMNAME/PROGRAMNAME.jar' "$@"
and like this for single class files:
#!/bin/sh exec /usr/bin/java '/usr/share/java/PROGRAMNAME/PROGRAMCLASSNAME' "$@"
- Set the
CLASSPATH
using the-cp
option to the Java interpreter unless there is an explicit reason not to (ie: theCLASSPATH
is used as a plugin mechanism). TheCLASSPATH
should include all jar files in the/usr/share/java/myprogram
direcory, as well as jar files that are from dependency libraries that have been refactored to other directories. You can use something like the following code:
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
- Make sure the shell script is executable!
- Other files distributed with the package should be stored in a directory named after the package under
/usr/share
. You may need to set the location of this directory in a variable likeMYPROJECT_HOME
inside the shell script. This guideline assumes that the program expects all files to be in the same directory (as is standard with Java packages). If it seems more natural to put a configuration file elsewhere (for example, logs in/var/log
), then feel free to do so.
Bear in mind that /usr
may be mounted as read-only on some systems. If there are files in the shared directory that need to be written by the application, they may have to be relocated to /etc
, /var
, or the user's home directory.
- As is standard with Arch Linux packages, if the above standards cannot be adhered to without a serious amount of work, the package should be installed in its preferred manner, with the resulting directory located in
/opt
. This is useful for programs that bundle JREs or include customized versions of dependencies, or do other strange or painful tasks.
複数の API 実装
If your package distributes commonly used API implementation(like jdbc driver) you should place the library under /usr/share/java/apiname
. So that applications that allow user to select from various implementations
will know where to look for them. Use this location only for raw library packages. If such a implementation is part of distribution of application, do not place this jar file under common location but use ordinary package structure.
ディレクトリ構造の例
To clarify, here is an example directory structure for a hypothetical program called foo
. Since foo
is a common name, the package is named java-foo
, but notice this is not reflected in the directory structure:
/usr/share/java/foo/
/usr/share/java/foo/foo.jar
/usr/share/java/foo/bar.jar
(included dependency ofjava-foo
)/usr/share/foo/
/usr/share/foo/*.*
(some general files required byjava-foo
)/usr/bin/foo
(executable shell script)
依存パッケージ
Java パッケージには、必要に応じて、java-runtime
あるいは java-environment
を依存パッケージとして指定します。
大抵のパッケージでは、Java で書かれたソフトウェアを実行するのに java-runtime
が必要になります。java-runtime
は以下のパッケージに含まれている仮想パッケージです:
- jre7-openjdk (フリー)
- java-gcj-compatAUR (フリー)
- jreAUR (ノンフリー)
Java のソースコードをバイトコードにコンパイルする必要があるパッケージでは java-environment
(例: JDK) が必須になります。java-environment
は以下のパッケージに含まれている仮想パッケージです:
- jdk7-openjdk (フリー)
- jdkAUR (ノンフリー)