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 のことは考えないことにする.

2010-02-07

debian/rules の書き方の情報源

debian/rules の書き方の情報源はあちこちにあるが,どれが時代遅れでどれが最新かよくわからないのでネットを逍遙して検討してみた.

  • 原典としては「Debian Policy Manual 4.9 Main building script: debian/rules」がある.「Debian New Maintainers’ Guide」を読むと理解しやすい.
  • Debian Developer’s Reference」によると,debhelper を使うようだ.以下のような雰囲気:
  • binary-arch: install
        dh_installdocs -a NEWS
        dh_installchangelogs -a ChangeLog
        dh_strip -a
        dh_compress -a
        dh_fixperms -a
        dh_installdeb -a
        dh_shlibdeps -a
        dh_gencontrol -a
        dh_md5sums -a
        dh_builddeb -a
    
  • Debianパッケージ作成の手引き」によると,dh_* を使うと同じようなことを何度も書かなければならなくなるということで,これを解決するのが CDBS だということだ.以下のように変数や :: ターゲットでカスタマイズする雰囲気:
  • #!/usr/bin/make -f
    
    include /usr/share/cdbs/1/rules/debhelper.mk
    
    package = hello-cdbs
    
    install/hello-cdbs::
    	$(MAKE) prefix=$(CURDIR)/debian/$(package)/usr install
    
    DEB_INSTALL_DOCS_ALL := NEWS
    DEB_INSTALL_CHANGELOGS_ALL := ChangeLog
    
  • Debian JP Project - 最近の話題」によると,debhelper v7 が提供する dh コマンドを使うと多くのパッケージで debuan/rules が 3 行で書けるようになると言うことだ.
  • #!/usr/bin/make -f
    %:
        dh $@
    
  • man debhelper, man dh 以外に公式のドキュメントはないようだが,作者のブログにいくつか記事がある.

Debian リポジトリを作成する方法の情報源

HowToSetupADebianRepository - Debian Wiki が最新情報.

Gauche で trace

Debian マシンで Gauche で trace するには以下のようにする.

まず SLIB (portable scheme library) が必要である.

$ sudo aptitude install slib

プログラム中で以下のようにする.

(define (fact n)
  (if (= n 0) 1
              (* n (fact (- n 1)))))

(use slib)
(require 'trace)
(trace fact)

(fact 5)

2010-02-06

GitHub の設定を手短に

  • GitHub へのアカウント開設はすでに済んでいるとする.
  • ssh の鍵は既に生成済みとする.
  • Account Settings にて公開鍵を登録する.
  • GitHub 側のユーザ名は常に git なので,手元のローカルユーザ名からのアクセスを可能にするために ~/.ssh/config を編集する.
  • Host github.com
        User git
    
  • 設定を確認する.
  • $ ssh git@github.com
    Hi YOURNAME! You've successfully authenticated, but GitHub does not provide shell access
    

    と出力されれば OK.

  • Dashboard の Create a Repository で新しいリポジトリを作成する.ここでは例として hellos というリポジトリを作成したとする.
    • プロジェクト名への入力に .git を付加する必要は特にない.付加するとそれがそのままリポジトリ名になり,付加しないと .git が付加されたものがリポジトリ名になる.
    • 新たにファイルを作成していく場合
    • $ git clone git@github.com:YOURNAME/hellos.git
      $ cd hello
      $ vi README
      $ git add README
      $ git commit
      $ git push origin HEAD
      
    • 既に手元に git レポジトリが存在する場合.
    • $ cd proj/hellos
      $ git remote add origin git@github.com:YOURNAME/hellos.git
      $ git checkout master
      $ git push origin HEAD
      
  • ユーザ名とメールアドレスを設定(必要?)
  • $ git config user.name YOURNAME
    $ git config user.email YOURNAME@EXAMPLE.COM
    

2010-02-02

Mac OS X の Java 環境

Mac OS X 上で Java で開発する必要がでてきたので,Snow Leopard での Java について調べ始めた.順不同で箇条書きする.

  • Java 6 が搭載されている.今日現在で 1.6.0_17 である.
  • 64 bit 版と 32 bit 版があり,64 bit 版がデフォルト.これはアプリケーション>ユーティリティ>Java Preferences というアプリケーションで変更できる.
  • javac のエラーメッセージが文字化けする!
    • エラーメッセージを英語にすればよいが,LANG ではなく LC_ALL で制御する.
    • $ LC_ALL= javac ...
      
    • エラーメッセージの文字コードを変更できる変なコマンドラインオプションがある.
    • $ javac -J'-Dfile.encoding=EUC-JP' ...
      
    • これらは /etc のどこかにあるファイルでデフォルトとして設定できるといいのだが,Java にはそのような思想はないのだろう(anywhere だから?).alias で逃げるしかないか.

情報源

apt でインストールしたものの履歴

apt-get でインストールしたものの履歴,それもコマンドラインから陽に指定したパッケージの履歴が欲しいとずっと思っていた.自分がどういったパッケージを真に必要としたかがわかり,将来の環境構築の参考になるからである.

apt-get でなく,aptitude を用いれば可能である./var/log/aptitude* に aptitude 関連の履歴がとられており,その中から [INSTALL] という表現を grep すればよい.

ただし,デフォルトでは aptitude 関連のログは 6 ヶ月以前のものは消されてしまうので,全ての履歴を調べたいなら debian をインストールしたときに /etc/logrotate.d/aptitude を変更しておかなければならない.

2010-02-01

iTerm 0.10 の不具合

iTerm を 0.10 にしてから時々表示が欠けるようになった.

SourceForge.net: iTerm.app: Detail: 2888004 - Occasional ’missing’ lines によると,Preferences の Display Refereshing Rate を遅くすると顕在化しなくなるということだ.

このレートを調整するには以下のコマンドを用いればよい.

$ ruby -e 'loop do ; puts "foo" ; sleep(rand*0.01) ; end'