概要
- 本記事はFUN Advent Calendar 2022 part2の19日目の記事です。
- 昨日はたろうくんの[かわいいは正義] tiikawaコマンドを作った件 [そのプロセスは殺さない,殺したくない]でした。
- 癒やしは大事ですね。しかし卒論スペースは定期的に片付けましょう。でないと
kill -KILL
でプロセスを殺したくなります。
- 癒やしは大事ですね。しかし卒論スペースは定期的に片付けましょう。でないと
- 明日はkazu8823さんの「二郎の美味しい作り方、そして二郎を食べることによるその効果」です。
- ちなみにPart1、Part3もあります。
- 昨日はたろうくんの[かわいいは正義] tiikawaコマンドを作った件 [そのプロセスは殺さない,殺したくない]でした。
本記事では、公立はこだて未来大学の2年生の講義「情報処理演習I」でEclipseを使いたくない、Javaもインストールしたくない、という人に贈る「dockerのopenjdkを使ってCUIで無理やりJARファイルを作成する」術を紹介します。
お前誰だよ!という人はきっとOB/OGの方ですね。
はじめに
公立はこだて未来大学の2年生の講義「情報処理演習I」はJavaの演習講義です。Javaでプログラミングの勉強をします。
正直に言います。
Eclipse重すぎ。
Eclipse重すぎ。
大事なことなのでチャ○アスなみに何度も言います。
Eclipse重すぎ。
いいマシンをお持ちの皆さんは感じないかもしれませんが、筆者が使っている非力なマシンではEclipseさんはとても重いです。筆者のような残念なマシンを使っている人や、
- 俺はvimで書くぜ
- 至高emacsしか認めない
- とりまvscodeがあればええやろ
という方々は、演習の提出ファイルであるJARファイルの作成にゴニョゴニョする必要があります。面倒なのでEclipseにコピペしてビルドしているのではないでしょうか。筆者もそうでした。vscodeで書いた方が書きやすいのに、Eclipseでビルドしないといけないのが手間でした。
そこで、dockerのopenjdkを使って課題に提出可能なJARファイルを生成することにしました。情報処理演習Iを担当していますが、この成果物のおかげで普段使っているマシンにEclipseをインストールしていません。講義で使うマシンは学生さんに見せるためだけにEclipseがインストールされています。
情報処理演習Iの提出ファイルに関して
情報処理演習Iをすでに受けたことがある方はご存知かとは思いますが、情報処理演習Iでは書いたプログラムをJARファイルとしてエクスポートし、提出します。すると、自動的にプログラムがチェックされて点数が出力されます。
提出用のJARファイルの作成は、Eclipseさんのエクスポート機能で行えます。おおよその手順は以下です。
- パッケージまたはクラスを選択して「エクスポート」
- 出力形式としてJARファイルを選択
- ソースを含める設定
- JARファイル名を指定のものに変更
- メインクラスの指定
- エクスポート
これらを全てキーボードだけで行うのは(できなくはないけど)面倒です。
そこで、
- ソースコードをお好きなエディタで書く
- コマンドを実行してJARファイルを生成
という手順でJARファイルを生成できるようにします。
なお、本記事の手順で生成したJARファイルを提出して0点になっても責任は一切負いかねます。自己責任でご使用ください。と言っても、何度でも提出できるので0点になったら素直にEclipseを使えばよろしいです。
ところで筆者はあまりJavaには詳しくないです。講義でしか使っていませんので、実はもっと簡単な手順があるならぜひ教えて下さい。特に、MANIFEST.MFを1ステップで作成する手順が分からず、スクリプト内では一度作成してからアップデートするという手順を踏んでいます。
準備
以下の手順では情報処理演習I用のディレクトリを作成して、全てその下で作業します。ここでは ~/ipp1/
を作成したものとします。
テンプレートファイルの作成
情報処理演習Iでは署名が無いファイルは自動チェックで0点になります。このため、署名入りのテンプレートファイルを作成します。 YOUR NAME
と bxxxxxxx
の部分は自分の情報を入れておきましょう。これらに加えてconstructorなどを追加しておいてもいいでしょう。
なお、エディタのsnippet機能などで作成しても構いません。
package template;
/**
* @author YOUR NAME bxxxxxxx
*/
public class Template {
public static void main(String[] args) {
}
}
dockerのインストール
dockerをインストールしておきましょう。この記事を見て実際にやろうとしている人はきっとdockerはすでにインストールされていると思いますので、インストール手順は割愛します。
openjdkを使いますのでpullしておきます。
docker pull openjdk
スクリプトの作成
以下のファイルを作ります。パーミッションも適切に設定しておきましょう。
#!/bin/sh
if [ $# -lt 3 ]; then
echo "Usage: $0 <jar_file> <main_class> <path_to_src>"
exit 1
fi
DOCKER_IMG=openjdk
JAR=$1
MAIN=$2
SRC_PATH=$3
docker run --rm -it \
-v $PWD:/app \
-v $(realpath ${SRC_PATH}):/src \
-w /work \
${DOCKER_IMG} \
sh -c "cp -r /src/* /work/ &&
javac **/*.java &&
jar cvf /app/${JAR} ./ &&
echo Main-Class: ${MAIN} > main.mf &&
jar uvfm /app/${JAR} main.mf || /bin/bash"
使い方
情報処理演習Iでは演習の中で新しいパッケージを作成し、その中にクラスファイルを格納していきます。
ここでは sample
パッケージに Main
クラスを作成し、 sample.jar
というJARファイルにエクスポートして提出するという場合を仮定して説明します。
パッケージとなるディレクトリの作成
src
ディレクトリの中にパッケージ名のディレクトリを作成します。
cd ~/ipp1
mkdir -p src/sample
クラスファイルの作成
テンプレートをコピーしてクラスファイルを作成します。
cp Template.java src/sample/Main.java
課題に従ってクラスファイルを書き換えます。
ここでは、Hello!
を出力する課題が出たとします。以下のようになるかと思います。パッケージ名を書き換え忘れることが多いので注意しましょう。
package sample;
/**
* @author YOUR NAME bxxxxxxx
*/
public class Main {
public static void main(String[] args) {
System.out.println("Hello!");
}
}
JARファイルの作成
準備で作成した ipp1-build.sh
を使ってJARファイルを作成します。
JARファイル名、メインクラス名、パッケージのディレクトリが格納された src
ディレクトリへのパスを指定します。
パッケージごと提出する必要があるため、以下の2点が必要です。
- メインクラスの指定がパッケージ名を含む
- JARファイルにはパッケージがディレクトリごと含まれる
このため、 src
ディレクトリの中身を全て格納し、そこに入っている.javaファイルを全てコンパイルするようにしています。また、dockerを使って(手抜きした実装をして)いるため、JARファイルへのパスはカレントディレクトリより下のみを指定可能です。
色々と手抜きをしているため、パスの指定は柔軟ではありません。どうぞ改造して使ってください。
./ipp1-build.sh sample.jar sample.Main src
成功すれば以下のような出力が表示されてカレントディレクトリ(= ~/ipp1
)に sample.jar
が作成されます。
added manifest
adding: sample/(in = 0) (out= 0)(stored 0%)
adding: sample/Main.class(in = 415) (out= 287)(deflated 30%)
adding: sample/Main.java(in = 166) (out= 138)(deflated 16%)
updated manifest
なお、Javaファイルにエラーがあると、docker内のbashで停止します。ソースコードを修正し、コンパイル・実行して確認しながらソースコードを完成させましょう。コンパイルコマンドは書きませんが、きっと皆さん分かりますよね(というか、スクリプトに書いてあります)。
以下の例は Main.java
でクラス名を修正し忘れた場合のエラーです。 exit
すればdockerコンテナを終了できます。
sample/Main.java:6: error: class Template is public, should be declared in a file named Template.java
public class Template {
^
1 error
bash-4.4#
実行
スクリプトを作っていないですが、dockerのopenjdkコンテナ上でjavaコマンドを実行すれば実行できます。
docker run --rm -it -v $PWD:/app -w /app openjdk java -jar sample.jar
Hello!
問題なく実行できるのであれば提出しましょう。
運用上のTips
各週の演習が混ざらないようにする
各週でディレクトリを作って、その中に src/パッケージ名
ディレクトリを作るとよいです。
mkdir ~/ipp1/01
cd !$
mkdir -p src/sample
cp ../Template.java src/sample/Main.java
...
パッケージをコピーして使用する場合は前の週からコピーしましょう。
mkdir ~/ipp1/02/src
cd ~/ipp1/02
cp -r ../01/src/sample src/
パッケージ名を変更する場合には手動で変更する必要があります。割愛しますが、sedなどで書き換ると良いです。
提供されたテンプレートファイルの使用
テンプレートとして .jar ファイルが提供される場合があります。その場合には手動で展開して使います。
mkdir ~/ipp1/xx/src
cd ~/ipp1/xx
docker run --rm -it -v $PWD/src:/app -v $(realpath ~/Downloads):/down -w /app openjdk /bin/bash
bash-4.4# jar xvf /down/hoge.jar
created: hoge/
inflated: hoge/Hoge.java
inflated: hoge/Fuga.java
bash-4.4# exit
生成されたJARファイルの中身
JARファイルはアーカイブファイルであり、その中身はこんな感じになっています。
.
├── META-INF
│ └── MANIFEST.MF
└── sample
├── Main.class
└── Main.java
パッケージ名のディレクトリ sample
の中に Main.java
ファイルと Main.class
ファイルが格納されています。複数のパッケージが含まれる場合には複数のディレクトリが格納されます。
META-INF/MANIFEST.MF
にはメインクラスの情報が書かれています。
Manifest-Version: 1.0
Created-By: 18.0.1.1 (Oracle Corporation)
Main-Class: sample.Main
おわりに
maven使えばええやん、などと思った人もいるかもしれませんが、情報処理演習Iで作成するプログラムはそれほど複雑ではなく、演習問題のたびにいちいちmaven設定するのは正直だるいです。
サクッと書いてサクッとJARファイルを作成して提出、という意味では、それなりに需要があるのではないかと思い、今年度構築した手抜きスクリプトについて記述しました。
しかし、ゲッター・セッターの自動生成やクラスファイルの自動生成、Comparableな実装のテンプレート生成などはEclipseでないと面倒かもしれません。
なお、ご使用は自己責任でお願いします。