Gentoo/FreeBSD で portage が動かない件

http://bugs.gentoo.org/show_bug.cgi?id=337465

3週間ほど前の version から portageFreeBSD 上で動かなくなっています。レスポンスの速い portage のメンテナさんと問題解決すべく作業中…。

どうやって問題のポイントをしぼっているかを書いてみます。

git bisect

portage は git でバージョン管理されています。 git には git bisect という強力な機能があります。 簡単に言えば、バグを発生させた commit を二分木検索で見つけだす機能です。 今回はこれを使いました。

$ git bisect start HEAD v2.2_rc60 --
$ git bisect run   ./pym/portage/tests/runTests

まず、 "git bisect start --" で、「問題がでている位置」(テストが通ってないとかSEGVするとか)と、「問題がでていない位置」を指定します。 ここでは git の最先端において FreeBSD 上で動かないので、 に HEAD, 2.2-rc60 あたりでは確実に動いていたので に v2.2_rc60 の tag を指定しています。

すると、 git が勝手にその中間を checkout してきます。ここで手動でテストして OK なら "git bisect good", だめであれば "git bisect bad" としてやっていきます。 good, bad をつけるたびに git が自動的に次にテストするべき位置を checkout します。

しかし、ここでは ./pym/portage/tests/runTests というテストスクリプトがすでに存在しているので、これを使って bisect を自動化できます。 "git bisect run ./pym/portage/tests/runTests" とすると、指定されたプログラムが正常終了(返り値 が 0) の場合 "git bisect good" を、そうでない場合には "git bisect bad" が実行されていきます。(ちなみにビルドがそもそもできない、など commit が不完全な場合には 125 を返すことで、その commit を skip する(good とも bad ともつけない)ことができます。

こうやってしばらく待っておけば

ce6be8caaaba3151f6d7681180c21a6f2a756a40 is the first bad commit
commit ce6be8caaaba3151f6d7681180c21a6f2a756a40
Author: Zac Medico <zmedico gentoo.org>
Date:   Thu Aug 19 22:56:26 2010 -0700

    Make the make.globals path relative to EPREFIX, and add comments possible
    alternative behavior for target systems.

:040000 040000 e2fa6633a30546ff05faba396365b8c0138671bd 5dcd2ca0cdf8525157632f49f57efcb45ea79032 M      pym
bisect run success

結果がでてきます。 この commit を詳しくみていってそのまわりを見ていけばバグの原因も見つけられることでしょう。

commit ce6be8caaaba3151f6d7681180c21a6f2a756a40
Author: Zac Medico <zmedico gentoo.org>
Date:   Thu Aug 19 22:56:26 2010 -0700

    Make the make.globals path relative to EPREFIX, and add comments possible
    alternative behavior for target systems.

diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py
index 46cac3b..2f886fc 100644
--- a/pym/portage/package/ebuild/config.py
+++ b/pym/portage/package/ebuild/config.py
@@ -723,8 +723,30 @@ class config(object):
                        self.configdict["env"] = LazyItemsDict(self.backupenv)
 
                        # make.globals should not be relative to config_root
-                       # because it only contains constants.
-                       for x in (GLOBAL_CONFIG_PATH,):
+                       # because it only contains constants. However, if EPREFIX
+                       # is set then there are two possible scenarios:
+                       # 1) If $ROOT == "/" then make.globals should be
+                       #    relative to EPREFIX.
+                       # 2) If $ROOT != "/" then the correct location of
+                       #    make.globals needs to be specified in the constructor
+                       #    parameters, since it's a property of the host system
+                       #    (and the current config represents the target system).
+                       global_config_path = GLOBAL_CONFIG_PATH
+                       if eprefix:
+                               if target_root == "/":
+                                       # case (1) above
+                                       global_config_path = os.path.join(eprefix,
+                                               GLOBAL_CONFIG_PATH.lstrip(os.sep))
+                               else:
+                                       # case (2) above
+                                       # For now, just assume make.globals is relative
+                                       # to EPREFIX.
+                                       # TODO: Pass in more info to the constructor,
+                                       # so we know the host system configuration.
+                                       global_config_path = os.path.join(eprefix,
+                                               GLOBAL_CONFIG_PATH.lstrip(os.sep))
+
+                       for x in (global_config_path,):
                                self.mygcfg = getconfig(os.path.join(x, "make.globals"),
                                        expand=expand_map)
                                if self.mygcfg:

ちなみに今回の場合、この変更で make.global から読みこまれていた設定が読みこまれなくなっていました。この設定が今まではうまく根本のバグが隠蔽していたようです……。根本のバグの原因はいまだに見つかっていません… orz