2011年12月8日木曜日

ソースの入手と再ビルド & デバッグ ~ Mac OS X Homebrew 編

お疲れ様です、サイオス 那賀です。

はじめて UNIX OS を買いました、Mac OS X です。そこで、「ソースの入手と再ビルド ~ CentOS / Scientific Linux 編」、「ソースの入手と再ビルド ~ Ubuntu 編」に続いて、Mac OS X 上での UNIX (POSIX?) 系パッケージの管理について簡単にお話ししようと思います。

Mac OS X 上での後入れの UNIX 系パッケージの管理には、今回 Homebrew を利用することにしました。Homebrew は、BSD 系のシステムで多く用いられているパッケージ管理システムである ports 系の一派であり、FreeBSD Ports, Gentoo Portage, MacPorts (旧 DarwinPorts) などの仲間です。

RPM (Red Hat) 系や deb (Debian) 系のパッケージ管理では一般的に、ディストリビューション開発者の側でビルドされたバイナリパッケージを入手してインストールする方式を採るのに対して、ports 系のパッケージは、ネットワークからソースコードを取得してきて、自分のホスト上で自前でビルドをするという特徴を持ちます (バイナリ配布もできるようにはなっていますけどね)。ターゲット・ホスト上には、インストール可能なパッケージのソースの所在をあらわす URL とビルド手順が書かれたファイルが格納されており、それらに従って、ホスト上で都度コンパイルをしながら、インストールを行います。もちろん、パッケージ管理ツールとしては当然のこととして、パッケージ単位でのインストール、アンインストール、ファイルのリスト管理、依存関係の解決などの機能を備えています。

ports 系のメリットとしては、OS や CPU フィーチャごとに最高の最適化がかけられる、やっていることが分かりやすい、などが挙げられる反面、環境ごとにバイナリが異なるのでサポートがしづらい、ネットワーク環境が必須、ビルドにマシンパワーと長い時間を要する、依存される度合いの大きいライブラリの API が更新されると大変、使っているだけで変態扱いされる、等のデメリットもあります。「貴重な一台をマルチユースで大事に使う」という旧来型のコンピューティング・シーンにおいては快適ですが、昨今の、安価なサーバを次から次に使い捨て、クラウドでガンガンとスケールするような使い方とは、相性が悪いのではないかなぁと思います。

さて、私の Mac の使い方は、当然前者の「貴重な一台をマルチユースで大事に使う」スタイルですので、ジャンジャン Homebrew を活用しましょう。従来 Mac OS X 環境で広く用いられてきた MacPorts が、すでに OS X の標準アプリケーションとして /bin/, /sbin/, /usr/bin/ 等にインストールされているソフトウェアも、重複して MacPorts の管理下でインストールしなければならず、無用に CPU パワーと初期導入時間を要し、別バージョンの多重管理に起因する問題を起こしたりしていたのに対して、Homebrew では、重複しないものだけを、/usr/local/ の下で完結して管理するところに、非常に好感が持てます。

では、ソースの入手とビルド手順を解説して行きましょう…かと思ったのですが、正直なところ「Formula Cookbook - GitHub」を読んで /usr/local/Library/Formula/ 以下の formula ファイルを眺めれば、Homebrew がやっていることについては誰にでも分かると思いますので、そちらに譲ります。

では。

…と、それだけでは何なので、フルにデバッグ情報を積んで、ソースレベル・デバッグが可能な状態でのインストール方法でも紹介しようと思います。

/usr/local/Library/Homebrew/formula.rb:Formula#mktemp() におけるビルドディレクトリの処理を見ると、インストール終了後は "ensure" で、何が何でもビルドツリーを消しに行きますので、ソースファイルとオブジェクトファイルを消さずに残すことができません。private だし。

private
  def mktemp
    tmp_prefix = ENV['HOMEBREW_TEMP'] || '/tmp'
    tmp=Pathname.new `/usr/bin/mktemp -d #{tmp_prefix}/homebrew-#{name}-#{version}-XXXX`.strip
    raise "Couldn't create build sandbox" if not tmp.directory? or $? != 0
    begin
      wd=Dir.pwd
      Dir.chdir tmp
      yield
    ensure
      Dir.chdir wd
      tmp.rmtree
    end
  end

仕方が無いので、Cellar ディレクトリ内で直接ビルドをするよう、Formula ファイルを以下のように修正します。(RPM の debuginfo が、ビルドした場所と違うディレクトリにソースを格納できるのは、debugedit コマンドで、ELF 内の DWARF 情報を直接書き換えという大技を使えるからです。Mac OS X の Mach-O バイナリでは、そもそも DWARF 情報を *.o が持っていて、実行ファイルは、それらへのリンクしか持っていないんですね。それらがすべてフルパスで格納されているので、どうやってソースを移動して良いやらさっぱり分からないので、やめておきます)

$ brew edit wget
$ (cd /usr/local/; git diff Library/Formula/wget.rb)
diff --git a/Library/Formula/wget.rb b/Library/Formula/wget.rb
index 66240e7..26c3328 100644
--- a/Library/Formula/wget.rb
+++ b/Library/Formula/wget.rb
@@ -13,6 +13,8 @@ class Wget < Formula
     [["--enable-iri", "Enable iri support."]]
   end

+  skip_clean :all if ARGV.debug? # strip を抑制します
+
   def install
     args = ["--disable-debug",
             "--prefix=#{prefix}",
@@ -20,6 +22,14 @@ class Wget < Formula

     args << "--disable-iri" unless ARGV.include? "--enable-iri"

+    if ARGV.debug?
+      ENV.Og # 最適化を切ります → "-O0"
+      d = Dir.getwd
+      Dir.chdir ".."
+      mv d, prefix + 'src' # ソースをインストール対象とします
+      Dir.chdir prefix + 'src' # その中でビルドします
+    end
+
     system "./configure", *args
     system "make install"
   end

上記の修正の後、"--debug" オプションをつけてインストールすれば、最適化が無効になり、ソースレベルのデバッグが有効になります。

$ brew install --debug --force wget
$ gdb wget
GNU gdb 6.3.50-20050815 (Apple version gdb-1708) (Thu Nov XX 21:59:02 UTC 2011)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin"...
Executing: ~/etc/gdbinit.
Done: ~/etc/gdbinit.
Reading symbols for shared libraries ..... done

(gdb) b main
Breakpoint 1 at 0x10002bef2: file main.c, line 915.
(gdb) run
Starting program: /usr/local/bin/wget
Reading symbols for shared libraries ++++......................... done

Breakpoint 1, main (argc=1, argv=0x7fff5fbff998) at main.c:915
915       bool append_to_log = false;
(gdb) l
910     main (int argc, char **argv)
911     {
912       char **url, **t;
913       int i, ret, longindex;
914       int nurl;
915       bool append_to_log = false;
916
917       total_downloaded_bytes = 0;
918
919       program_name = argv[0];
(gdb)

Formula ファイルは Git で管理されていますので、修正を加えても、コンフリクトしない限りは問題なくアップデートができます。

$ brew update

ところで、Homebrew の用いる用語のアナロジー―homebrew (自家醸造), formula (醸造法), cellar (酒蔵), keg (樽)―等は、とても秀逸だと思います。ではまた。

0 件のコメント:

コメントを投稿