読者です 読者をやめる 読者になる 読者になる

A Way of Code

興味の赴くままに書き綴っていきます。

Mac OS X LionでPython 2.7.2をビルドするとエラーになる

pythonbrewからpython 2.7.2を入れようとしたところ、エラーが出てインストールに失敗しました。pythonbrewが悪者みたいな言い方ですが、そんなことはありません。
pythonbrewから普通にpython 2.7.2をインストールすると、ビルドには成功するもののmake testの段階でエラーになります。--no-testオプションを使うとmake testをスキップできるのですが、気になるので調べてみました。調べたついでにパッチを作りました。

(ちなみに、homebrewからpythonをインストールしたときはエラーが起こらなかったなぁ、と思ったら、2012年1月時点のFormulaでは最初からmake testをスキップしてインストールしていました。)

環境

Mac OS X 10.7.2
Xcode 4.2.1 (llvm-gcc-4.2)
readline 6.2.2
sqlite 3.7.9
gdbm 1.10

失敗しているテスト

make test時に実行されるテストスイートのうち、以下のものが失敗していました。

5 tests failed:
    test_anydbm test_ctypes test_distutils test_platform test_whichdb

調べてみる

pythonのpの字も知らない初心者が無謀にも調査してみます。
ひとまずpythonbrewから--no-testオプションでインストールしておきます。

$ pythonbrew install --no-test 2.7.2

test_anydbm

まずはインストール時のログに出ていたtest_anydbmを調べてみます。とりあえずtest_anydbmで検索してみる。

$ cd ~/.pythonbrew
$ find . -name "*test_anydbm*" 
./build/Python-2.7.2/Lib/test/test_anydbm.py
./pythons/Python-2.7.2/lib/python2.7/test/test_anydbm.py
./pythons/Python-2.7.2/lib/python2.7/test/test_anydbm.pyc
./pythons/Python-2.7.2/lib/python2.7/test/test_anydbm.pyo

ほぅほぅ、Lib/testというところにテストスイートが格納されているみたいですね。~/.pythonbrew/pythons/ディレクトリはビルドしたpythonのデプロイ先らしいので~/.pythonbrew/build/Python-2.7.2/Lib/test/test_anydbm.pyを実行してみます。

$ ./pythons/Python-2.7.2/bin/python ./build/Python-2.7.2/Lib/test/test_anydbm.py

簡単に実行できました。以下のテストケースでエラーが出ました。

test_anydbm_creation (__main__.AnyDBMTestCase) ... ok
test_anydbm_keys (__main__.AnyDBMTestCase) ... ERROR
test_anydbm_modification (__main__.AnyDBMTestCase) ... ERROR
test_anydbm_read (__main__.AnyDBMTestCase) ... ERROR

ERRORが出ている箇所は、いずれもanydbm.py:82でした。

ERROR: test_anydbm_keys (__main__.AnyDBMTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_anydbm.py", line 61, in test_anydbm_keys
    f = anydbm.open(_fname, 'r')
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/anydbm.py", line 82, in open
    raise error, "db type could not be determined"
error: db type could not be determined

error: db type could not be determined... DBタイプが判別できない、、、だと?ソースを追っかけてみましょう。
[anydbm.py]

 57 def open(file, flag='r', mode=0666):
 ...
 69     # guess the type of an existing database
 70     from whichdb import whichdb
 71     result=whichdb(file)
 72     if result is None:
 73         # db doesn't exist
 74         if 'c' in flag or 'n' in flag:
 75             # file doesn't exist and the new
 76             # flag was used so use default type
 77             mod = _defaultmod
 78         else:
 79             raise error, "need 'c' or 'n' flag to open new db"
 80     elif result == "":
 81         # db type cannot be determined
 82         raise error, "db type could not be determined"

71行目のwhichdb()の結果が空文字で例外を投げているようです。70行目でimport whichdbってやっているから、whichdb.pyがあるのかな。というかpythonって関数の中でimportできるんかい。
whichdbで検索。

$ find ./ -name "*whichdb*"
./test/test_whichdb.py
./test/test_whichdb.pyc
./test/test_whichdb.pyo
./whichdb.py
./whichdb.pyc
./whichdb.pyo

まさにwhichdb.pyというファイルがありました。

17 def whichdb(filename):
...
93     # Check for GNU dbm
94     if magic == 0x13579ace:
95         return "gdbm"
...
112     # Unknown
113     return ""

gdbmをインストールしているのですが、何故か94行目の判定に引っかからず、113行目に到達して空文字を返しているようですね。バグかな?
if magic == 0x13579ace:でググってみます。
・・・
ググりました、先生!→ http://bugs.python.org/issue13007
どうやら、gdbm 1.9以降で94行目のマジックナンバーが変わってしまったようです。今回はgdbm 1.10を使っているのでパッチを当てないといけないですね。上記ページのリンク先にパッチがあったので、当ててみます。

cd ~/.pythonbrew/build/Python-2.7.2
mkdir patches
cd patches
curl -kLo gdbm_1.9_magic.patch http://hg.python.org/cpython/raw-rev/14cafb8d1480
cd ../
patch -p1 < ./patches/gdbm_1.9_magic.patch 

再度、test_anydbmを実行してみます。

$ cd ~/.pythonbrew/build/Python-2.7.2/Lib/test
$ ../../../../pythons/Python-2.7.2/bin/python test_anydbm.py
test_anydbm_creation (__main__.AnyDBMTestCase) ... ok
test_anydbm_keys (__main__.AnyDBMTestCase) ... ok
test_anydbm_modification (__main__.AnyDBMTestCase) ... ok
test_anydbm_read (__main__.AnyDBMTestCase) ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.019s

OK

今度はテストに成功しました!
よしよし、この調子で次いってみましょ

test_ctypes

例のごとく、test_ctypesを実行。

$ ./pythons/Python-2.7.2/bin/python ./build/Python-2.7.2/Lib/test/test_ctypes.py
...(省略)...
test_byte (ctypes.test.test_cfuncs.CFunctions) ... FAIL
test_byte_plus (ctypes.test.test_cfuncs.CFunctions) ... FAIL
test_short (ctypes.test.test_cfuncs.CFunctions) ... FAIL
test_short_plus (ctypes.test.test_cfuncs.CFunctions) ... FAIL
test_doubleresult (ctypes.test.test_functions.FunctionTestCase) ... FAIL
test_floatresult (ctypes.test.test_functions.FunctionTestCase) ... FAIL
test_intresult (ctypes.test.test_functions.FunctionTestCase) ... FAIL
test_longdoubleresult (ctypes.test.test_functions.FunctionTestCase) ... FAIL
======================================================================
FAIL: test_byte (ctypes.test.test_cfuncs.CFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/ctypes/test/test_cfuncs.py", line 21, in test_byte
    self.assertEqual(self.S(), -126)
AssertionError: 130 != -126

======================================================================
FAIL: test_byte_plus (ctypes.test.test_cfuncs.CFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/ctypes/test/test_cfuncs.py", line 27, in test_byte_plus
    self.assertEqual(self.S(), -126)
AssertionError: 130 != -126

======================================================================
FAIL: test_short (ctypes.test.test_cfuncs.CFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/ctypes/test/test_cfuncs.py", line 45, in test_short
    self.assertEqual(self.S(), -32766)
AssertionError: 32770 != -32766

======================================================================
FAIL: test_short_plus (ctypes.test.test_cfuncs.CFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/ctypes/test/test_cfuncs.py", line 51, in test_short_plus
    self.assertEqual(self.S(), -32766)
AssertionError: 32770 != -32766

======================================================================
FAIL: test_doubleresult (ctypes.test.test_functions.FunctionTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/ctypes/test/test_functions.py", line 143, in test_doubleresult
    self.assertEqual(result, -21)
AssertionError: 65771.0 != -21

======================================================================
FAIL: test_floatresult (ctypes.test.test_functions.FunctionTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/ctypes/test/test_functions.py", line 131, in test_floatresult
    self.assertEqual(result, -21)
AssertionError: 65771.0 != -21

======================================================================
FAIL: test_intresult (ctypes.test.test_functions.FunctionTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/ctypes/test/test_functions.py", line 105, in test_intresult
    self.assertEqual(result, -21)
AssertionError: 65771 != -21

======================================================================
FAIL: test_longdoubleresult (ctypes.test.test_functions.FunctionTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/ctypes/test/test_functions.py", line 155, in test_longdoubleresult
    self.assertEqual(result, -21)
AssertionError: 65771.0 != -21

----------------------------------------------------------------------
Ran 337 tests in 0.458s

FAILED (failures=8, skipped=1)
Traceback (most recent call last):
  File "test_ctypes.py", line 15, in <module>
    test_main()
  File "test_ctypes.py", line 12, in test_main
    run_unittest(unittest.TestSuite(suites))
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/test/test_support.py", line 1089, in run_unittest
    _run_suite(suite)
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/test/test_support.py", line 1072, in _run_suite
    raise TestFailed(err)
test.test_support.TestFailed: multiple errors occurred

( ゚д゚) ...
CFunctions... ctypes... ってpythonとCを連携させる機能ですかね。イヤな予感がしてきました。
とりあえずソースを確認してみます!まずはtest_byteのスタックトレースに出ていたtest_cfuncs.py:21を見ます。
[test_cfuncs.py]

  9 class CFunctions(unittest.TestCase):
 10     _dll = CDLL(_ctypes_test.__file__)
 11 
 12     def S(self):
 13         return c_longlong.in_dll(self._dll, "last_tf_arg_s").value
 14     def U(self):
 15         return c_ulonglong.in_dll(self._dll, "last_tf_arg_u").value
 16 
 17     def test_byte(self):
 18         self._dll.tf_b.restype = c_byte
 19         self._dll.tf_b.argtypes = (c_byte,)
 20         self.assertEqual(self._dll.tf_b(-126), -42)
 21         self.assertEqual(self.S(), -126)

テストエラーのログでは21行目で、self.S()が130になっていてassertに引っかかったようですね。long longのビット値を確認しているのかな。とぉーってもイヤな予感がします☆彡
macに標準で入っているpython 2.7.1で同じテストを実行してみましょう。

$ cd /usr/lib/python2.7/test
$ python test_ctypes.py
...
----------------------------------------------------------------------
Ran 332 tests in 0.458s

OK (skipped=1)

OK牧場!
テストコードに差異は無いか確認してみます。

$ diff /usr/lib/python2.7/ctypes/test/test_cfuncs.py ~/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/ctypes/test/test_cfuncs.py

テストコードの差分は無いですね。ついでにctypesモジュールの差分をとってみます。

$ diff -r ~/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/ctypes  /usr/lib/python2.7/ctypes --exclude='*.pyc' --exclude='*.pyo' --exclude='*test*'

差分が、差分がありません><
イヤな予感しかしない。Appleビルドのpythonがうまく機能していることを考えると、おそらくllvm-gccでビルドしたせいでしょう。

llvm-gccではないgccで、もう一度python 2.7.2をビルドし直してみます。
※macにllvm-gccではないgccを導入する手順はこちら http://toggtc.hatenablog.com/entry/2012/01/28/224006

$ pythonbrew uninstall 2.7.2
$ pythonbrew --no-test --verbose --configure="CC=/usr/bin/gcc-4.2" 2.7.2

再度テストを実行します。

$ cd ~/.pythonbrew/build/Python-2.7.2/Lib/test/
$ ../../../../pythons/Python-2.7.2/bin/python test_ctypes.py
...
----------------------------------------------------------------------
Ran 337 tests in 0.378s

OK (skipped=1) 

gccだと成功しました!
llvm-gcc・・・(´・ω・`)

test_distutils

次はtest_distutilsです。テストスイートを実行したところ、エラーは1個だけでした。

$ ./pythons/Python-2.7.2/bin/python ./build/Python-2.7.2/Lib/test/test_distutils.py
...
i686-apple-darwin11-gcc-4.2.1: /private/var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/tmpAfN6sj/foo.so: No such file or directory

======================================================================
ERROR: test_get_outputs (distutils.tests.test_build_ext.BuildExtTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/distutils/tests/test_build_ext.py", line 291, in test_get_outputs
    cmd.run()
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/distutils/command/build_ext.py", line 340, in run
    self.build_extensions()
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/distutils/command/build_ext.py", line 449, in build_extensions
    self.build_extension(ext)
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/distutils/command/build_ext.py", line 531, in build_extension
    target_lang=language)
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/distutils/ccompiler.py", line 741, in link_shared_object
    extra_preargs, extra_postargs, build_temp, target_lang)
  File "/Users/toggtc/.pythonbrew/pythons/Python-2.7.2/lib/python2.7/distutils/unixccompiler.py", line 258, in link
    raise LinkError, msg
LinkError: command 'gcc' failed with exit status 1

gccの実行でエラーが出ていますね。test_get_outputsというテストケース名とfoo.so: No such file or directoryというメッセージがあるので、どうやらfoo.soを作ろうとしたけど、実際には作られていないか、目的の場所に生成されていなくてエラーになっているようです。
not llvm-gccのgccに変えた後でもエラーが発生しているので、gccの問題では無さそうです。
っていう予想は付くのですが、一応トレースを上から追って行きましょう。
[distutils/command/build_ext.py]

266     def test_get_outputs(self):
267         tmp_dir = self.mkdtemp()
268         c_file = os.path.join(tmp_dir, 'foo.c')
269         self.write_file(c_file, 'void initfoo(void) {};\n')
270         ext = Extension('foo', [c_file])
271         dist = Distribution({'name': 'xx',
272                              'ext_modules': [ext]})
273         cmd = build_ext(dist)
274         self._fixup_command(cmd)
275         cmd.ensure_finalized()
276         self.assertEqual(len(cmd.get_outputs()), 1)
277 
278         if os.name == "nt":
279             cmd.debug = sys.executable.endswith("_d.exe")
280 
281         cmd.build_lib = os.path.join(self.tmp_dir, 'build')
282         cmd.build_temp = os.path.join(self.tmp_dir, 'tempt')
283 
284         # issue #5977 : distutils build_ext.get_outputs
285         # returns wrong result with --inplace
286         other_tmp_dir = os.path.realpath(self.mkdtemp())
287         old_wd = os.getcwd()
288         os.chdir(other_tmp_dir)
289         try:
290             cmd.inplace = 1
291             cmd.run()
292             so_file = cmd.get_outputs()[0]
293         finally:
294             os.chdir(old_wd)
295         self.assertTrue(os.path.exists(so_file))

tmpディレクトリにfoo.cを作って、build_extという関数を実行したところでLinkError例外が出たようですね。build_extはコマンドパターンを実装しているようなので、run関数をみてみましょう。

[distutils/command/build_ext.py]

278     def run(self):
...
304         # Setup the CCompiler object that we'll use to do all the
305         # compiling and linking
306         self.compiler = new_compiler(compiler=self.compiler,
307                                      verbose=self.verbose,
308                                      dry_run=self.dry_run,
309                                      force=self.force)
...
311         # If we are cross-compiling, init the compiler now (if we are not
312         # cross-compiling, init would not hurt, but people may rely on
313         # late initialization of compiler even if they shouldn't...)
314         if os.name == 'nt' and self.plat_name != get_platform():
315             self.compiler.initialize(self.plat_name)
316 
317         # And make sure that any compile/link-related options (which might
318         # come from the command-line or from the setup script) are set in
319         # that CCompiler object -- that way, they automatically apply to
320         # all compiling and linking done here.
321         if self.include_dirs is not None:
322             self.compiler.set_include_dirs(self.include_dirs)
323         if self.define is not None:
324             # 'define' option is a list of (name,value) tuples
325             for (name, value) in self.define:
326                 self.compiler.define_macro(name, value)
327         if self.undef is not None:
328             for macro in self.undef:
329                 self.compiler.undefine_macro(macro)
330         if self.libraries is not None:
331             self.compiler.set_libraries(self.libraries)
332         if self.library_dirs is not None:
333             self.compiler.set_library_dirs(self.library_dirs)
334         if self.rpath is not None:
335             self.compiler.set_runtime_library_dirs(self.rpath)
336         if self.link_objects is not None:
337             self.compiler.set_link_objects(self.link_objects)
...
339         # Now actually compile and link everything.
340         self.build_extensions()

build_extコマンドオブジェクトに予めコンパイル時に必要なパラメータを設定しておく

306行目でコンパイラインスタンスを生成

317行目以降でbuild_extコマンドオブジェクト(self)が持っていたパラメータを、コンパイラオブジェクトに設定し直す
となっているようですね。
続いて、build_extensions()を辿っていくと...
[distutils/command/build_ext.py]

444     def build_extensions(self):
445         # First, sanity-check the 'extensions' list
446         self.check_extensions_list(self.extensions)
447 
448         for ext in self.extensions:
449             self.build_extension(ext)
450 
451     def build_extension(self, ext):
...
493         objects = self.compiler.compile(sources,
494                                          output_dir=self.build_temp,
495                                          macros=macros,
496                                          include_dirs=ext.include_dirs,
497                                          debug=self.debug,
498                                          extra_postargs=extra_args,
499                                          depends=ext.depends)

493-499行目で、さきほど設定したコンパイラオブジェクトからコンパイル処理を実行させて、
[distutils/command/build_ext.py]

522         self.compiler.link_shared_object(
523             objects, ext_path,
524             libraries=self.get_libraries(ext),
525             library_dirs=ext.library_dirs,
526             runtime_library_dirs=ext.runtime_library_dirs,
527             extra_postargs=extra_args,
528             export_symbols=self.get_export_symbols(ext),
529             debug=self.debug,
530             build_temp=self.build_temp,
531             target_lang=language)
532 

522-531行目でリンクを実行しているようです。
スタックトレースでは531行目が出ていたので、493-499行目のコンパイル処理は終わって522-531行目のリンク処理で何らかのエラーが出たようですね。
スタックトレースの情報からself.compiler.link_shared_objectのcompilerオブジェクトはccompiler.pyだと分かるので、ccompilerのlink_shared_objectを見てみましょう。
[ccompiler.py]

 732     def link_shared_object(self, objects, output_filename, output_dir=None,
 733                            libraries=None, library_dirs=None,
 734                            runtime_library_dirs=None, export_symbols=None,
 735                            debug=0, extra_preargs=None, extra_postargs=None,
 736                            build_temp=None, target_lang=None):
 737         self.link(CCompiler.SHARED_OBJECT, objects,
 738                   output_filename, output_dir,
 739                   libraries, library_dirs, runtime_library_dirs,
 740                   export_symbols, debug,
 741                   extra_preargs, extra_postargs, build_temp, target_lang)

self.linkをみると
[ccompiler.py]

 714         raise NotImplementedError

となっていたので、ccompilerは抽象クラスで、スタックトレースに出ていたunixccompilerが具象クラスという関係なのでしょう。
unixccompiler.pyをみてみます。スタックトレースの情報から、LinkError例外が出ているところを探ります。
[unixccompiler.py]

208     def link(self, target_desc, objects,
...
234             try:
235                 if target_desc == CCompiler.EXECUTABLE:
236                     linker = self.linker_exe[:]
237                 else:
238                     linker = self.linker_so[:]
239                 if target_lang == "c++" and self.compiler_cxx:
240                     # skip over environment variable settings if /usr/bin/env
241                     # is used to set up the linker's environment.
242                     # This is needed on OSX. Note: this assumes that the
243                     # normal and C++ compiler have the same environment
244                     # settings.
245                     i = 0
246                     if os.path.basename(linker[0]) == "env":
247                         i = 1
248                         while '=' in linker[i]:
249                             i = i + 1
250 
251                     linker[i] = self.compiler_cxx[i]
252 
253                 if sys.platform == 'darwin':
254                     linker = _darwin_compiler_fixup(linker, ld_args)
255 
256                 self.spawn(linker + ld_args)
257             except DistutilsExecError, msg:
258                 raise LinkError, msg

tryスコープの中で、例外が出そうな箇所は、254行目か256行目ですね。さすがに246のbasename関数で例外を投げるってことはないでしょうから。
254行目にあった_darwin_compiler_fixupの中身を見てみます。
[unixccompiler.py]

 44 def _darwin_compiler_fixup(compiler_so, cc_args):
 53     stripArch = stripSysroot = 0
 54 
 55     compiler_so = list(compiler_so)
 56     kernel_version = os.uname()[2] # 8.4.3
 57     major_version = int(kernel_version.split('.')[0])
 58 
 59     if major_version < 8:
 60         # OSX before 10.4.0, these don't support -arch and -isysroot at
 61         # all.
 62         stripArch = stripSysroot = True
 63     else:
 64         stripArch = '-arch' in cc_args
 65         stripSysroot = '-isysroot' in cc_args
 66 
 67     if stripArch or 'ARCHFLAGS' in os.environ:
 68         while 1:
 69             try:
 70                 index = compiler_so.index('-arch')
 71                 # Strip this argument and the next one:
 72                 del compiler_so[index:index+2]
 73             except ValueError:
 74                 break
 75 
 76     if 'ARCHFLAGS' in os.environ and not stripArch:
 77         # User specified different -arch flags in the environ,
 78         # see also distutils.sysconfig
 79         compiler_so = compiler_so + os.environ['ARCHFLAGS'].split()
...

ざっと見る限り、mac用にパラメータ調整しているだけでリンク処理はしていませんね。
次はspawnを見てみます。unixccompilerにspawnの実装が無かったので、ccompilerを見てみます。
[ccompiler.py]

  14 from distutils.spawn import spawn
 927     def spawn(self, cmd):
 928         spawn(cmd, dry_run=self.dry_run)

おっと、実際にはspawn.pyに定義されていました。
[spawn.py]

 17 def spawn(cmd, search_path=1, verbose=0, dry_run=0):
 33     if os.name == 'posix':
 34         _spawn_posix(cmd, search_path, dry_run=dry_run)
 35     elif os.name == 'nt':
 36         _spawn_nt(cmd, search_path, dry_run=dry_run)
 37     elif os.name == 'os2':
 38         _spawn_os2(cmd, search_path, dry_run=dry_run)
 39     else:
 40         raise DistutilsPlatformError, \

OSごとに実装が異なるようですね。macはPOSIX準拠なので_spawn_posixをみてみます。
[spawn.py]

100 def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0):
101     log.info(' '.join(cmd))
102     if dry_run:
103         return
104     exec_fn = search_path and os.execvp or os.execv
105     pid = os.fork()
106 
107     if pid == 0:  # in the child
108         try:
109             exec_fn(cmd[0], cmd)
110         except OSError, e:
111             sys.stderr.write("unable to execute %s: %s\n" %
112                              (cmd[0], e.strerror))
113             os._exit(1)
114 
115         sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0])
116         os._exit(1)
117     else:   # in the parent
118         # Loop until the child either exits or is terminated by a signal
119         # (ie. keep waiting if it's merely stopped)
120         while 1:
121             try:
122                 pid, status = os.waitpid(pid, 0)
123             except OSError, exc:
124                 import errno
125                 if exc.errno == errno.EINTR:
126                     continue
127                 raise DistutilsExecError, \
128                       "command '%s' failed: %s" % (cmd[0], exc[-1])
129             if os.WIFSIGNALED(status):
130                 raise DistutilsExecError, \
131                       "command '%s' terminated by signal %d" % \
132                       (cmd[0], os.WTERMSIG(status))
133 
134             elif os.WIFEXITED(status):
135                 exit_status = os.WEXITSTATUS(status)
136                 if exit_status == 0:
137                     return   # hey, it succeeded!
138                 else:
139                     raise DistutilsExecError, \
140                           "command '%s' failed with exit status %d" % \
141                           (cmd[0], exit_status)

おおっ!エラーログにあったメッセージを出力している箇所がありました!
140行目ですね。エラーログを抜粋します。

LinkError: command 'gcc' failed with exit status 1

完全に一致。
105行目でforkしたプロセスが109行目でgccコマンドを実行しているようです。
親プロセスは、122行目でwaitpidを呼び出してプロセスの状態変化を見て134行目でリンク処理をした子プロセスが終了したのを確認したけど、終了コードが異常でエラーを投げたんですね。
i686-apple-darwin11-gcc-4.2.1: /private/var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/tmpAfN6sj/foo.so: No such file or directoryっていうメッセージと合致しますね。
さて、処理の流れはわかったので、あとはgccに渡されたパラメータが何だったかを確認すれば原因が掴めそうですね。
パラメータの内容はログに出ていないので、デバッグログを仕込みましょう。
リンク時のパラメータを渡しているunixcompiler.py:256あたりをいじくります。
[unixcompiler.py]

256                 print("[PY_DEBUG] unixcompiler.py) linker=", linker, "ld_args=", ld_args)
257                 self.spawn(linker + ld_args)

print文を追加しました。
実行してみたところ、パラメータ値が取得できました。

('[PY_DEBUG] unixcompiler.py) linker=', ['/usr/bin/gcc-4.2', '-bundle', '-undefined', 'dynamic_lookup'], 'ld_args='['/var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/pythontest_aV8Rs2/tempt/var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/tmpQr8dDM/foo.o', '-L`pwd`', '-L', '-o', '/private/var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/tmp24JHaO/foo.so'])

見づらいので、コマンドに整形します。

$ /usr/bin/gcc-4.2 -bundle -undefined dynamic_lookup /var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/pythontest_aV8Rs2/tempt/var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/tmpQr8dDM/foo.o -L`pwd` -L -o /private/var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/tmp24JHaO/foo.so

お分かり頂けたであろうか?
クローズアップ

-L`pwd` -L -o 

Apple GCCの場合、このパラメータ指定だと-oというディレクトリを探索してしまい、エラーになりますね。No such file or directoryというメッセージはそのせいでしょう。
念のため、mac標準のpython側にも同様のデバッグコードを仕込んで動かしてみます。

llvm-gcc-4.2 -Wl,-F. -bundle -undefined dynamic_lookup -Wl,-F. -arch i386 -arch x86_64 /var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/pythontest_tcsjJO/tempt/var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/tmpsN0zMd/foo.o -o /private/var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/tmpZBrIwM/foo.so

mac標準のpythonでは-Lオプションが出て来ませんね。
ひとまず、渡しているパラメータがわかったので、ソースをもう一度みてみましょう。
gccに渡すパラメータはbuild_extコマンドオブジェクトが保持しているのでしたね。build_extコマンドオブジェクトを生成したところをみてみましょう。
[test_build_ext.py]

266     def test_get_outputs(self):
...
273         cmd = build_ext(dist)
274         self._fixup_command(cmd)
...
291             cmd.run()

273行目でbuild_extコマンドオブジェクトのインスタンスを生成しています。291行目の_fixup_command関数の中でbuild_extコマンドオブジェクトを渡して何かしていますね。あやしぃ
[test_build_ext.py]

 53     def _fixup_command(self, cmd):
 54         # When Python was build with --enable-shared, -L. is not good enough
 55         # to find the libpython<blah>.so.  This is because regrtest runs it
 56         # under a tempdir, not in the top level where the .so lives.  By the
 57         # time we've gotten here, Python's already been chdir'd to the
 58         # tempdir.
 59         #
 60         # To further add to the fun, we can't just add library_dirs to the
 61         # Extension() instance because that doesn't get plumbed through to the
 62         # final compiler command.
 63         if (sysconfig.get_config_var('Py_ENABLE_SHARED') and
 64             not sys.platform.startswith('win')):
 65             runshared = sysconfig.get_config_var('RUNSHARED')
 66             if runshared is None:
 67                 cmd.library_dirs = ['.']
 68             else:
 69                 name, equals, value = runshared.partition('=')
 70                 cmd.library_dirs = value.split(os.pathsep)

何やらコメントが書いてありますね。
意訳すると、"--enable-sharedオプション付きでPythonをビルドした場合、Pythonはtmpディレクトリに移動するから-Lパラメータがうまく機能しないかもしれないよ。" ってことですかね。ふむ、このコメントで警告している内容は、今回の件とは直接関係なさそうですね。

63行目と65行目でconfigの値(Py_ENABLE_SHAREDとRUNSHARED)を取得しているので、実際にpythonを動かしてどんな値が取得されるかみてみましょう。
python 2.7.2の場合:

$ ~/.pythonbrew/pythons/Python-2.7.2/bin/python
Python 2.7.2 (default, Jan 22 2012, 06:39:23) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from distutils import sysconfig
>>> sysconfig.get_config_var('Py_ENABLE_SHARED')
1
>>> sysconfig.get_config_var('RUNSHARED')
'DYLD_LIBRARY_PATH=`pwd`:'

Py_ENABLE_SHARED → 1
RUNSHARED → 'DYLD_LIBRARY_PATH=`pwd`:'

mac標準pythonの場合:

$ python
Python 2.7.1 (r271:86832, Jul 31 2011, 19:30:53) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from distutils import sysconfig
>>> sysconfig.get_config_var('Py_ENABLE_SHARED')
0
>>> sysconfig.get_config_var('RUNSHARED')
'DYLD_FRAMEWORK_PATH=/private/var/tmp/python/python-57~1/2.7/python:'

Py_ENABLE_SHARED → 0
RUNSHARED → 'DYLD_FRAMEWORK_PATH=/private/var/tmp/python/python-57~1/2.7/python:'

どうやらmac標準pythonはPy_ENABLE_SHAREDが0なので、--enable-shared付きでビルドされていないようです(けど、dylibはちゃんと作られています)。ということは、_fixup_command関数の63行目でfalse判定になるので何も処理されないようですね。
pythonbrewからインストールしたpythonは--enable-shared付きでビルドされているので、65行目以降の処理が実行されます。--enable-shared固有の問題でしたね。

さて、さきほど確認した結果だと、RUNSHAREDには'DYLD_LIBRARY_PATH=`pwd`:'が設定されていました。この値とソースを照らしあわせてみましょう。

 65             runshared = sysconfig.get_config_var('RUNSHARED')

まず、ここでrunsharedにDYLD_LIBRARY_PATH=`pwd`: の値が設定されます。

 66             if runshared is None:
 67                 cmd.library_dirs = ['.']
 68             else:
 69                 name, equals, value = runshared.partition('=')
 70                 cmd.library_dirs = value.split(os.pathsep)

ここで、runsharedはNoneではないので、69-70行目で、以下のように文字列分割しているようです。

DYLD_LIBRARY_PATH=`pwd`:
↓
DYLD_LIBRARY_PATH と `pwd`:
↓
`pwd`:
↓
`pwd` と (空白)

つまり、library_dirsには`pwd`と(空白)が設定されているので、gccのオプションで、

-L`pwd` -L (空白) -o /private/var/folders/jy/dhptnvj90b34s0135sb_g6w80000gn/T/tmp24JHaO/foo.so

となってしまい、gccの実行でエラーになったようです。
test_build_ext.pyを修正します。71-75行目を追加しました。
[test_build_ext.py]

 53     def _fixup_command(self, cmd):
 ...
 63         if (sysconfig.get_config_var('Py_ENABLE_SHARED') and
 64             not sys.platform.startswith('win')):
 65             runshared = sysconfig.get_config_var('RUNSHARED')
 66             if runshared is None:
 67                 cmd.library_dirs = ['.']
 68             else:
 69                 name, equals, value = runshared.partition('=')
 70                 cmd.library_dirs = value.split(os.pathsep)
 71                 while(True):
 72                     try:
 73                         cmd.library_dirs.remove('')
 74                     except ValueError:
 75                         break

実行してみます。

$ ~/.pythonbrew/pythons/Python-2.7.2/bin/python test_distutils.py
...
test_get_outputs (distutils.tests.test_build_ext.BuildExtTestCase) ... ok
...
----------------------------------------------------------------------
Ran 154 tests in 4.827s

OK (skipped=6)

おお、成功しました!
もしかするとパッチが出ているかもしれませんが、見つからなかったので作りました。
3.2.2でも同じ事象が起こっていたので、ついでに作りました。
2.7.2版 https://raw.github.com/toggtc/python-patch/master/2.7.2/distutils_test_fixup_command_2.7.2.patch
3.2.2版 https://raw.github.com/toggtc/python-patch/master/3.2.2/distutils_test_fixup_command_3.2.2.patch
公式なパッチがあればご連絡をお願いします。
2012/2/3 追記:
→Ned氏にパッチを作成して頂きました。http://bugs.python.org/issue13901
ただし、↓のパッチは各ブランチの最新に対して当てたもので、現在リリースされている2.7.2には適用できません。2.7.2にパッチを当てたい場合は、↑のgithubにあげたパッチをお使いください。

New changeset 41cabdff2686 by Ned Deily in branch '2.7':
Issue #13901: Prevent test_distutils failures on OS X with --enable-shared.
http://hg.python.org/cpython/rev/41cabdff2686

New changeset 6f6100a752ba by Ned Deily in branch '3.2':
Issue #13901: Prevent test_distutils failures on OS X with --enable-shared.
http://hg.python.org/cpython/rev/6f6100a752ba

New changeset 84be86af9161 by Ned Deily in branch 'default':
Issue #13901: Prevent test_packaging failures on OS X with --enable-shared.
http://hg.python.org/cpython/rev/84be86af9161

ところで、DYLD_LIBRARY_PATH=`pwd`:となっていた理由ですが、DYLD_LIBRARY_PATH環境変数を設定した記憶が無かったので、調べてみたらconfigure.inファイルに設定がありました。

    Darwin*)
    	LDLIBRARY='libpython$(VERSION).dylib'
	BLDLIBRARY='-L. -lpython$(VERSION)'
	RUNSHARED='DYLD_LIBRARY_PATH=`pwd`:${DYLD_LIBRARY_PATH}'

環境変数にDYLD_LIBRARY_PATHを設定していないので、`pwd`:が設定されてしまっていたんですね。こっちも修正するべきか、、

test_platform

あと2つ!次はtest_platformです。
test_platform.pyを実行したところ、以下のエラーが出ました。

$ ./pythons/Python-2.7.2/bin/python ./build/Python-2.7.2/Lib/test/test_platform.py
======================================================================
FAIL: test_mac_ver (__main__.PlatformTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_platform.py", line 195, in test_mac_ver
    self.assertEqual(res[2], 'i386')
AssertionError: 'x86_64' != 'i386'

----------------------------------------------------------------------
Ran 20 tests in 0.325s

FAILED (failures=1, skipped=1)
Traceback (most recent call last):
  File "test_platform.py", line 255, in <module>
    test_main()
  File "test_platform.py", line 251, in test_main
    PlatformTest
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/test/test_support.py", line 1087, in run_unittest
    _run_suite(suite)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/test/test_support.py", line 1070, in _run_suite
    raise TestFailed(err)
test.test_support.TestFailed: Traceback (most recent call last):
  File "test_platform.py", line 194, in test_mac_ver
    self.assertEqual(res[2], 'i386')
AssertionError: 'x86_64' != 'i386'

予想はつきますが、ソースを確認します。test_platform.py:195ですね。

159     def test_mac_ver(self):
160         res = platform.mac_ver()
...
193             if sys.byteorder == 'little':
194                 self.assertEqual(res[2], 'i386')
195             else:
196                 self.assertEqual(res[2], 'PowerPC')

platform.mac_ver()という関数が返す値がi386ではないので、assertに引っかかっています。
mac標準のpython 2.7.1でplatform.mac_ver()を実行すると、x86_64という文字列が取得されます。`uname -m`でも同様にx86_64が返ってきます。

$ python
Python 2.7.1 (r271:86832, Jul 31 2011, 19:30:53) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.mac_ver()
('10.7.2', ('', '', ''), 'x86_64')

この件は、パッチが投稿されているみたいです。
http://mail.python.org/pipermail//python-checkins/2011-July/106590.html

というわけで、194行目を以下に置き換えてテストを再実行。

194                 self.assertIn(res[2], ('i386', 'x86_64'))
----------------------------------------------------------------------
Ran 20 tests in 0.273s

OK (skipped=1)

通りました!

test_whichdb

test_whichdbは、test_anydbmのときにwhichdb.pyにあてたパッチで解決していました。 

まとめ

  • test_anydbm
    • whichdb.pyにパッチを当てる
  • test_ctypes
    • llvm-gccではないgccを使う
  • test_distutils
    • test_build_ext.pyにパッチを当てる
  • test_platform
    • test_platform.pyにパッチを当てる
  • test_whichdb
    • test_anydbmで当てたwhichdb.pyのパッチで解決


スッキリしました。