2010-02-28

javac が生成するバイトコードの調べ方

javac がどのようなバイトコードを吐いているかを知るにはどうすればいいだろうか?

class test {
    String f(String s, int n) { return s + n; }
}

この test.java のバイトコードを調べてみよう.

$ vi test.java
$ javac test.java
$ javap -c test
Compiled from "test.java"
class test extends java.lang.Object{
test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

java.lang.String f(java.lang.String, int);
  Code:
   0:   new     #2; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   7:   aload_1
   8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:  iload_2
   12:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   15:  invokevirtual   #6; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   18:  areturn

}

まとめ

  • javap -c classname でクラスファイルの「逆アセンブル」ができる.

2010-02-21

upstream パッケージの Debian 化 (1)

最終ゴール

オレオレ apt リポジトリにオレオレ deb パッケージを置いて,特定少人数に対して配布する.

今回のお題

前回「Septième Sens: Debian パッケージを意識した upstream パッケージの作成」で作成した upstream パッケージ my-hello を Debian 化するにあたって,最小限必要な構成はどのようなものだろうか?

作業ディレクトリ

Debian パッケージの生成作業は upstream パッケージの最上位ディレクトリで行う.

$ cd my-hello
$ mkdir debian

debian ディレクトリの中の情報を利用する dpkg-xxx などのコマンドは,作業ディレクトリとして debian ディレクトリではなく upstream パッケージの最上位ディレクトリが設定されていることを前提としている.

dh による debian/rules

今回は現時点でもっともモダンな方法であると考えられる dh コマンドを使って Debian 化してみる.man dh によるともっともシンプルな debian/rules は以下のようになる:

#!/usr/bin/make -f
%:
	dh $@

debian/rules ファイルは実行可能でなければならない.

$ vi debian/rules
$ chmod +x debian/rules

ビルドを試してみる前に,やや天下りになるが,debian/compat というファイルに 7 を書き込んでおく必要がある.man debhelper によると,dh_xxx コマンドの動作モードが debian/compat ファイルの内容によって変わるからである.過去に dh_xxx を利用して作成したパッケージが dh_xxx の仕様変更によってビルドできなくなることを防ぐためにこのような仕様になっている.

$ echo 7 >| debian/compat

それでは実際にビルドを実行してみよう.ビルドのためのコマンドは Chapter 6. Building the package によると以下のようになる.

$ fakeroot debian/rules binary
dh binary
dh: cannot read debian/control: No such file or directory

失敗した.debian/control ファイルが必要だと言われている.

Debian Policy Manual - Control files and their fields を読むと,debian/control ファイルはメールヘッダのようなパラグラフ複数からなり,第1パラグラフで必須なのが Source: と Maintainer:, 第2パラグラフで必須なのが Package: と Architecture: と Description: となっている.

Source: my-hello
Maintainer: Your Name <yourname@example.com>

Package: my-hello
Architecture: all
Description: say hello
 This program says hello.

debian/control を上記の内容で作成し,ビルドしてみる.

$ fakeroot debian/rules binary
...
   dh_installchangelogs
tail: cannot open `debian/changelog' for reading: No such file or directory
...

今度は debian/changelog が必要だと言われている.

debian/changelog ファイルは debchange コマンドで生成できる.

$ debchange --create
my-hello (0.0.1) UNRELEASED; urgency=low

  * Initial release.

 -- Your Name <yourname@example.com>  Sun, 21 Feb 2010 22:35:59 +0900

実行するとエディタが起動するので,PACKAGE と VERSION という記述を my-hello と 0.0.1 に修正する.

$ fakeroot debian/rules binary
...
   dh_auto_install
install -d /home/yourname/my-hello/debian/my-hello/usr/local/bin
install myhello /home/yourname/my-hello/debian/my-hello/usr/local/bin
...
   dh_usrlocal
rmdir: failed to remove `debian/my-hello/usr/local/bin': Directory not empty
...

dh_usrlocal が失敗している,出力を読むと dh_auto_install で make install が実行され,debian/my-hello/usr/local/bin に実行ファイルがインストールされているのが問題なのがわかる.

man dh によると,特定の dh_xxx をオーバーライドするには override_dh_xxx ターゲットを定義すればよい.

#!/usr/bin/make -f
%:
	dh $@
override_dh_auto_install:
	make install DESTDIR=debian/my-hello PREFIX=/usr

debian/rules を上のように修正し,再度ビルドする.

$ debian/rules clean
$ fakeroot debian/rules binary
…
dpkg-deb: building package `my-hello' in `../my-hello_0.0.1_all.deb'.

となり .deb が生成された.

$ sudo dpkg -i ../my-hello_0.0.1_all.deb
$ myhello
hello
$ sudo dpkg -P my-hello
$ myhello
-bash: myhello: No such file or directory

まとめ

  • 最低限必要なファイル
  • my-hello
    |-- Makefile       # DESTDIR と PREFIX
    |-- debian/
    |   |-- changelog  # debchange コマンド
    |   |-- compat     # 7
    |   |-- control    # Source:, Maintainer:, Package:, Architecture:, Description:
    |   `-- rules*     # override_dh_xxx:
    `-- myhello
    
  • ビルドコマンド
  • $ fakeroot debian/rules binary
    
  • リビルド
  • $ debian/rules clean
    $ fakeroot debian/rules binary
    

2010-02-18

Debian パッケージを意識した upstream パッケージの作成

最終ゴール

オレオレ apt リポジトリにオレオレ deb パッケージを置いて,特定少人数に対して配布する.

今回のお題

例えば以下のようなコマンドがあるとする.

#!/bin/sh
echo 'hello'

このコマンドを提供する Debian パッケージを作りたい.この Debian パッケージの upstream にあたるパッケージはどのように作成すればよいだろうか?

パッケージ名

まずパッケージ名を決める必要があるが,パッケージ名に使ってよい文字などの規則はどこにあるだろうか?「debian パッケージ名の規則 - metalglue」に書いた通り,Debian Policy Manual - Control files and their fields (5.6.1 Source) にあり,[a-z0-9][a-z0-9.+-]+ となる.大文字やアンダースコア _ が使えないことに注意.

ここではパッケージ名を my-hello とする.

バージョン

バージョン番号の付け方はどうしたらよいだろうか?「オープンソースソフトウェアの育て方 」を通じて知った「Versioning Numbering Concepts - The Apache Portable Runtime Project」 が参考になる.これによると,バージョン番号は . (ドット) で区切られた3つの数字で,MAJOR.MINOR.PATCH という構成を取る.ドットは小数点ではない.つまり例えば 3.1.9 の次のバージョンは 3.2.0 ではなく 3.1.10 である.

MAJOR と MINOR が同じなら PATCH が違っても機能が違ってはならない.PATCH が異なるバージョン間でアップグレードしてもダウングレードしてもバグでない挙動は同じでなくてはならない.平たく言うとバグフィックスによるバージョンアップである.

MAJOR が同じで MINOR が異なるバージョンにアップグレードしたときに,使えていた機能が使えなくなってはならない.一方,ダウングレードは安全に行えないかもしれない.

MAJOR が異なる場合は,アップグレードもダウングレードも安全に行えない可能性がある.

ここではバージョンを 0.0.1 とする.

ディレクトリ/ファイル構成

以下のようにする.

my-hello/
 |-- Makefile
 `-- myhello

Makefile

普通,作成したコマンドは /usr/local/bin に,ライブラリなら /usr/local/lib にインストールされるように Makefile を記述する.今回の場合はどのような Makefile が都合がいいだろうか?

Debian のパッケージ生成では,あるディレクトリを仮のルートとしてファイルをインストールし,それをアーカイブしてパッケージとする(Chapter?3.?Modifying the source - 3.1 Installation in a subdirectory を参照).よって,任意のディレクトリをルートと考えてインストールできるようになっていることがのぞましい.

また,「Debian Policy Manual」によると,debian ではコマンドは /usr/bin に,ライブラリは /usr/lib にインストールすることになっている.この,/usr を基準にするか /usr/local を基準にするかが選択可能であることが望ましい.

これらを勘案し,また ldapscripts の upstream パッケージも参考にして,DESTDIR と PREFIX という変数でこれらを制御できるようにした.

DESTDIR =
PREFIX = /usr/local

all:

install: myhello
	install -d $(DESTDIR)$(PREFIX)/bin
	install $< $(DESTDIR)$(PREFIX)/bin

.PHONY: all install

以下のようにインストールする.

$ make install DESTDIR=/home/yourname/tmp PREFIX=/usr

疑問点

  • upstream パッケージの作者が Debian 野郎の場合,自分で Debian 用のファイル(パッケージディレクトリ中の debian/ ディレクトリの中身)を用意するだろう.こういう場合,README とか changelog とかはどこにおいておくのがいいんだろうか?
  • よくわからないので,今回は README, changelog のことは考えないことにする.