brew update して brew outdated したら python のバージョンが 2.7.1 から 2.7.2 に上がったっぽかったので、何も考えずに brew upgrade した後 brew cleanup したら pip で入れたものとか virtualenv で作った環境が動かなくなって困った。pip で入れ直したり、virtualenv を作り直したりするのもアレなので適当に直したけど、どうするのが正しいのか、よくわからない。

以下、homebrew を入れたディレクトリを ~/homebrew としている(デフォルトは /usr/local )。

pip

こんな感じで動かない。

$ pip freeze
zsh: /Users/nulll/homebrew/bin/pip: bad interpreter: /Users/nulll/homebrew/Cellar/python/2.7.1/bin/python: no such file or directory
$ hg version
zsh: /Users/nulll/homebrew/bin/hg: bad interpreter: /Users/nulll/homebrew/Cellar/python/2.7.1/bin/python: no such file or directory
なんで python のバージョンが上がると pip で入れたものが動かなくなるのか

pip で入れたものは ~/homebrew/share/python 以下にある。

$ ls -lF ~/homebrew/share/python
total 88
-rwxr-xr-x  1 nulll  staff   361B  6 15 00:28 easy_install*
-rwxr-xr-x  1 nulll  staff   369B  6 15 00:29 easy_install-2.7*
-rwxr-xr-x  1 nulll  staff   1.1K  6 15 00:29 hg*
-rwxr-xr-x  1 nulll  staff   319B  6 15 00:28 pip*
-rwxr-xr-x  1 nulll  staff   327B  6 15 00:28 pip-2.7*
-rwxr-xr-x  1 nulll  staff   354B  6 15 00:29 virtualenv*
-rwxr-xr-x  1 nulll  staff    19K  4 27 01:22 virtualenvwrapper.sh*

適当に中身を見ると……

$ cat ~/homebrew/share/python/pip
#!/Users/nulll/homebrew/Cellar/python/2.7.1/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'pip==1.0.1','console_scripts','pip'
__requires__ = 'pip==1.0.1'
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.exit(
        load_entry_point('pip==1.0.1', 'console_scripts', 'pip')()
    )

shebang が pip で入れたときに使った python を指しているが、brew cleanup で消してしまったので、動かなくなったらしい。

どうすれば直るか

shebang を直す。

$ cat ~/homebrew/share/python/pip
#!/Users/nulll/homebrew/Cellar/python/2.7.2/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'pip==1.0.1','console_scripts','pip'
__requires__ = 'pip==1.0.1'
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.exit(
        load_entry_point('pip==1.0.1', 'console_scripts', 'pip')()
    )

pip 以外も、とりあえず手で直した。自動化したい。

動いた。

$ pip freeze
distribute==0.6.19
mercurial==1.8.4
virtualenv==1.6.1
virtualenvwrapper==2.7.1
wsgiref==0.1.2
$ hg version
Mercurial Distributed SCM (version 1.8.4)
(see http://mercurial.selenic.com for more information)

Copyright (C) 2005-2011 Matt Mackall and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

virtualenv

こんな感じで動かない。

$ workon work
(work)$ pip freeze
Traceback (most recent call last):
  File "/Users/nulll/.virtualenvs/work/bin/pip", line 5, in <module>
    from pkg_resources import load_entry_point
  File "/Users/nulll/.virtualenvs/work/lib/python2.7/site-packages/pkg_resources.py", line 17, in <module>
    from urlparse import urlparse, urlunparse
ImportError: No module named urlparse
(work)$ python
Python 2.7.2 (default, Jun 15 2011, 00:21:27) 
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/nulll/.virtualenvs/work/lib/python2.7/site-packages/numpy/__init__.py", line 137, in <module>
    import add_newdocs
  File "/Users/nulll/.virtualenvs/work/lib/python2.7/site-packages/numpy/add_newdocs.py", line 9, in <module>
    from numpy.lib import add_newdoc
  File "/Users/nulll/.virtualenvs/work/lib/python2.7/site-packages/numpy/lib/__init__.py", line 4, in <module>
    from type_check import *
  File "/Users/nulll/.virtualenvs/work/lib/python2.7/site-packages/numpy/lib/type_check.py", line 8, in <module>
    import numpy.core.numeric as _nx
  File "/Users/nulll/.virtualenvs/work/lib/python2.7/site-packages/numpy/core/__init__.py", line 40, in <module>
    from numpy.testing import Tester
  File "/Users/nulll/.virtualenvs/work/lib/python2.7/site-packages/numpy/testing/__init__.py", line 8, in <module>
    from unittest import TestCase
ImportError: No module named unittest
なんで python のバージョンが上がると virtualenv が動かなくなるのか

virutualenvwrapper を使っているので、virtualenv は $WORKON_HOME 以下にある。$WORKON_HOME は ~/.virtualenvs に設定している。

$ ls -lF ~/.virtualenvs
total 120
drwxr-xr-x  5 nulll  staff   170B  6 15 00:33 _/
-rwxrwxr-x  1 nulll  staff   105B  3 30 23:04 get_env_details*
-rw-r--r--  1 nulll  staff   4.3K  6 15 00:42 hook.log
-rw-r--r--  1 nulll  staff   9.9K  6 15 00:28 hook.log.1
-rwxrwxr-x  1 nulll  staff    91B  3 30 23:04 initialize*
-rwxrwxr-x  1 nulll  staff    68B  3 30 23:04 postactivate*
-rwxrwxr-x  1 nulll  staff    70B  3 30 23:04 postdeactivate*
-rwxrwxr-x  1 nulll  staff    68B  3 30 23:04 postmkvirtualenv*
-rwxrwxr-x  1 nulll  staff    62B  3 30 23:04 postrmvirtualenv*
-rwxrwxr-x  1 nulll  staff    69B  3 30 23:04 preactivate*
-rwxrwxr-x  1 nulll  staff    71B  3 30 23:04 predeactivate*
-rwxrwxr-x  1 nulll  staff    93B  3 30 23:04 premkvirtualenv*
-rwxrwxr-x  1 nulll  staff    63B  3 30 23:04 prermvirtualenv*
drwxr-xr-x  6 nulll  staff   204B  6 10 22:43 work/

上記の場合、_ と work という virtualenv があって、中身は以下のようになっている。

$ ls -lF ~/.virtualenvs/work 
total 0
drwxr-xr-x  14 nulll  staff  476  6  2 21:18 bin/
drwxr-xr-x   3 nulll  staff  102  3 30 23:09 include/
drwxr-xr-x   3 nulll  staff  102  3 30 23:09 lib/
drwxr-xr-x   3 nulll  staff  102  3 30 23:10 man/

ディレクトリの中身は以下のようになっている。

$ ls -lF ~/.virtualenvs/work/bin
total 112
-rw-r--r--  1 nulll  staff  2133  3 30 23:09 activate
-rw-r--r--  1 nulll  staff  1053  3 30 23:09 activate.csh
-rw-r--r--  1 nulll  staff  2872  3 30 23:09 activate.fish
-rw-r--r--  1 nulll  staff  1005  3 30 23:09 activate_this.py
-rwxr-xr-x  1 nulll  staff   350  6  2 21:13 easy_install*
-rwxr-xr-x  1 nulll  staff   358  6  2 21:13 easy_install-2.7*
-rwxr-xr-x  1 nulll  staff   682  6  2 21:18 f2py*
-rwxr-xr-x  1 nulll  staff   323  3 30 23:10 nosetests*
-rwxr-xr-x  1 nulll  staff   331  3 30 23:10 nosetests-2.7*
-rwxr-xr-x  1 nulll  staff   308  5  3 22:43 pip*
-rwxr-xr-x  1 nulll  staff   316  5  3 22:43 pip-2.7*
-rwxr-xr-x  1 nulll  staff  9072  3 30 23:09 python*

bin の中身は特に問題ない。たぶん。

$ ls -lF ~/.virtualenvs/work/include/
total 8
lrwxr-xr-x  1 nulll  staff  60  3 30 23:09 python2.7@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/include/python2.7

include の中身は python2.7 だけで、virtualenv を作成するときに使った python の include/python2.7 へのシンボリックリンクになっている。

$ ls -lF ~/.virtualenvs/work/lib/python2.7
total 880
lrwxr-xr-x   1 nulll  staff     68  3 30 23:09 UserDict.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/UserDict.py
-rw-r--r--   1 nulll  staff   9945  4 19 01:36 UserDict.pyc
lrwxr-xr-x   1 nulll  staff     67  3 30 23:09 _abcoll.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/_abcoll.py
-rw-r--r--   1 nulll  staff  24459  4 19 01:36 _abcoll.pyc
lrwxr-xr-x   1 nulll  staff     71  3 30 23:09 _weakrefset.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/_weakrefset.py
-rw-r--r--   1 nulll  staff  11470  4 19 01:36 _weakrefset.pyc
lrwxr-xr-x   1 nulll  staff     63  3 30 23:09 abc.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/abc.py
-rw-r--r--   1 nulll  staff   6418  4 19 01:36 abc.pyc
lrwxr-xr-x   1 nulll  staff     66  3 30 23:09 codecs.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/codecs.py
-rw-r--r--   1 nulll  staff  38952  4 19 01:36 codecs.pyc
lrwxr-xr-x   1 nulll  staff     63  3 30 23:09 config@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/config
lrwxr-xr-x   1 nulll  staff     68  3 30 23:09 copy_reg.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/copy_reg.py
-rw-r--r--   1 nulll  staff   5388  4 19 01:36 copy_reg.pyc
drwxr-xr-x   5 nulll  staff    170  3 30 23:09 distutils/
lrwxr-xr-x   1 nulll  staff     66  3 30 23:09 encodings@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/encodings
lrwxr-xr-x   1 nulll  staff     67  3 30 23:09 fnmatch.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/fnmatch.py
-rw-r--r--   1 nulll  staff   3684  4 19 01:36 fnmatch.pyc
lrwxr-xr-x   1 nulll  staff     71  3 30 23:09 genericpath.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/genericpath.py
-rw-r--r--   1 nulll  staff   3513  4 19 01:36 genericpath.pyc
lrwxr-xr-x   1 nulll  staff     68  3 30 23:09 lib-dynload@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/lib-dynload
lrwxr-xr-x   1 nulll  staff     69  3 30 23:09 linecache.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/linecache.py
-rw-r--r--   1 nulll  staff   3361  4 19 01:36 linecache.pyc
lrwxr-xr-x   1 nulll  staff     66  3 30 23:09 locale.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/locale.py
-rw-r--r--   1 nulll  staff  50058  4 19 01:36 locale.pyc
-rw-r--r--   1 nulll  staff      0  3 30 23:09 no-global-site-packages.txt
lrwxr-xr-x   1 nulll  staff     66  3 30 23:09 ntpath.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/ntpath.py
-rw-r--r--   1 nulll  staff     42  3 30 23:09 orig-prefix.txt
lrwxr-xr-x   1 nulll  staff     62  3 30 23:09 os.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/os.py
-rw-r--r--   1 nulll  staff  27493  4 19 01:36 os.pyc
lrwxr-xr-x   1 nulll  staff     69  3 30 23:09 posixpath.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/posixpath.py
-rw-r--r--   1 nulll  staff  11769  4 19 01:36 posixpath.pyc
lrwxr-xr-x   1 nulll  staff     62  3 30 23:09 re.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/re.py
-rw-r--r--   1 nulll  staff  13657  4 19 01:36 re.pyc
drwxr-xr-x  29 nulll  staff    986  6 10 22:43 site-packages/
-rw-r--r--   1 nulll  staff  26050  3 30 23:09 site.py
-rw-r--r--   1 nulll  staff  24204  3 30 23:09 site.pyc
lrwxr-xr-x   1 nulll  staff     63  3 30 23:09 sre.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/sre.py
lrwxr-xr-x   1 nulll  staff     71  3 30 23:09 sre_compile.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/sre_compile.py
-rw-r--r--   1 nulll  staff  11377  4 19 01:36 sre_compile.pyc
lrwxr-xr-x   1 nulll  staff     73  3 30 23:09 sre_constants.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/sre_constants.py
-rw-r--r--   1 nulll  staff   6213  4 19 01:36 sre_constants.pyc
lrwxr-xr-x   1 nulll  staff     69  3 30 23:09 sre_parse.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/sre_parse.py
-rw-r--r--   1 nulll  staff  20000  4 19 01:36 sre_parse.pyc
lrwxr-xr-x   1 nulll  staff     64  3 30 23:09 stat.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/stat.py
-rw-r--r--   1 nulll  staff   2953  4 19 01:36 stat.pyc
lrwxr-xr-x   1 nulll  staff     65  3 30 23:09 types.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/types.py
-rw-r--r--   1 nulll  staff   2656  4 19 01:36 types.pyc
lrwxr-xr-x   1 nulll  staff     68  3 30 23:09 warnings.py@ -> /Users/nulll/homebrew/Cellar/python/2.7.1/lib/python2.7/warnings.py
-rw-r--r--   1 nulll  staff  13691  4 19 01:36 warnings.pyc

lib の中身は python2.7 だけで、virtualenv を作成するときに使った python の lib/python2.7/* の一部へのシンボリックリンクになっている。site-packages は virtualenv の pip で入れたものが入っている。

以上より、各 virtualenv は virtualenv を作成するときに使った python の include とか lib にシンボリックリンクしているが、brew cleanup でシンボリック先の lib とか include を消してしまったので、動かなくなったらしい。

どうすれば直るか

各 virtualenv の include と lib のシンボリックリンクをし直す。

まず、シンボリックリンクを消す。

$ find ~/.virtualenv -type l | xargs rm

virtualenv を作り直して、シンボリックリンクし直す。

$ mkvirtualenv work --no-site-packages --distribute
Overwriting work/lib/python2.7/site.py with new content
Overwriting work/lib/python2.7/orig-prefix.txt with new content
New python executable in work/bin/python
Overwriting work/lib/python2.7/distutils/__init__.py with new content
Installing distribute........done.
Installing pip...............done.

シンボリックリンクを消しておかないと、以下のようなエラーが出る。

$ mkvirtualenv work --no-site-packages --distribute
Traceback (most recent call last):
  File "/Users/nulll/homebrew/bin/virtualenv", line 9, in <module>
    load_entry_point('virtualenv==1.6.1', 'console_scripts', 'virtualenv')()
  File "/Users/nulll/homebrew/Cellar/python/2.7.2/lib/python2.7/site-packages/virtualenv.py", line 795, in main
    never_download=options.never_download)
  File "/Users/nulll/homebrew/Cellar/python/2.7.2/lib/python2.7/site-packages/virtualenv.py", line 886, in create_environment
    site_packages=site_packages, clear=clear))
  File "/Users/nulll/homebrew/Cellar/python/2.7.2/lib/python2.7/site-packages/virtualenv.py", line 1018, in install_python
    copyfile(join(stdlib_dir, fn), join(lib_dir, fn))
  File "/Users/nulll/homebrew/Cellar/python/2.7.2/lib/python2.7/site-packages/virtualenv.py", line 414, in copyfile
    copyfileordir(src, dest)
  File "/Users/nulll/homebrew/Cellar/python/2.7.2/lib/python2.7/site-packages/virtualenv.py", line 389, in copyfileordir
    shutil.copytree(src, dest, True)
  File "/Users/nulll/homebrew/Cellar/python/2.7.2/lib/python2.7/shutil.py", line 174, in copytree
    os.makedirs(dst)
  File "/Users/nulll/homebrew/Cellar/python/2.7.2/lib/python2.7/os.py", line 157, in makedirs
    mkdir(name, mode)
OSError: [Errno 17] File exists: 'work/lib/python2.7/config'

一応 distribute を upgrade する。

$ workon work
(work)$ pip install --upgrade distribute
...

動いた。

(work)$ pip freeze
PyYAML==3.10
distribute==0.6.19
nltk==2.0.1rc1
nose==1.0.0
numpy==1.6.0
openopt==0.33
scipy==0.9.0
setproctitle==1.1.2
wsgiref==0.1.2
(work)$ python
Python 2.7.2 (default, Jun 15 2011, 00:21:27)
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> numpy.test()
Running unit tests for numpy
NumPy version 1.6.0
NumPy is installed in /Users/nulll/.virtualenvs/work/lib/python2.7/site-packages/numpy
Python version 2.7.2 (default, Jun 15 2011, 00:21:27) [GCC 4.2.1 (Apple Inc. build 5664)]
nose version 1.0.0
...
Ran 3529 tests in 22.735s

OK (KNOWNFAIL=3, SKIP=1)
<nose.result.TextTestResult run=3529 errors=0 failures=0>
>>> 

まとめ

どうするのが正しいのか、よくわからない。

新しいマシンに Homebrew をクリーンインストールした後、最初の brew update が失敗して困ったけど、git reset で解決できた、という話。

最初の brew update が失敗するまで

2011年 5月10日 火曜日 01時24分05秒 JST 現在、以下で再現した。

Homebrew をインストールする。

$ mkdir foo
$ cd foo
$ curl -LsSf https://github.com/mxcl/homebrew/tarball/master | /usr/bin/tar xvz --strip 1
x .gitignore
x Library/
...
x share/man/man1/brew.1

git を入れる(path が通っている場所に git があるなら、ここは飛ばして mkdir Cellar するだけで良い)。

$ ./bin/brew install git
==> Downloading http://kernel.org/pub/software/scm/git/git-1.7.5.1.tar.bz2
...

brew update すると失敗する。

$ ./bin/brew update

Initialized empty Git repository in /Users/nulll/foo/.git/
remote: Counting objects: 36669, done.
remote: Compressing objects: 100% (15870/15870), done.
remote: Total 36669 (delta 21633), reused 33578 (delta 20229)
Receiving objects: 100% (36669/36669), 4.98 MiB | 1.02 MiB/s, done.
Resolving deltas: 100% (21633/21633), done.
From http://github.com/mxcl/homebrew
 * branch            master     -> FETCH_HEAD
error: Untracked working tree file '.gitignore' would be overwritten by merge.

Error: Failed while executing git pull http://github.com/mxcl/homebrew.git master

解決法

適当に検索したら https://github.com/mxcl/homebrew/issues/5128 にたどりついた。色々と書いてあるが、具体的な解決法としては https://github.com/mxcl/homebrew/issues/5128#issuecomment-1111364

$ cd `brew --prefix`
$ git remote add origin https://github.com/mxcl/homebrew.git
$ git fetch origin
$ git reset --hard origin/master

とするか、あるいは

$ cd `brew --prefix`
$ git fetch https://github.com/mxcl/homebrew.git
$ git reset --hard FETCH_HEAD

とする、と書いてある。

が、最初の brew update で失敗した場合は Homebrew を入れたディレクトリ(上記例だと foo)に移動した後

$ git reset

として brew update すると

$ ./bin/brew update
From http://github.com/mxcl/homebrew
 * branch            master     -> FETCH_HEAD
Already up-to-date.

というように、上手くいったっぽい感じになった。

なんで git reset するだけで良かったのか

Homebrew を入れたディレクトリを $HOMEBREW として、$HOMEBRREW/Library/Homebrew/cmd/update.rb を見ると、最初の brew update のときは

$ cd `brew --prefix`
$ git init
$ git pull http://github.com/mxcl/homebrew.git master

が実行されることになる。pull は fetch + merge であるということを思い出しつつ、もう一度 brew update が失敗したときの出力を見ると

$ ./bin/brew update

Initialized empty Git repository in /Users/nulll/foo/.git/
remote: Counting objects: 36669, done.
remote: Compressing objects: 100% (15870/15870), done.
remote: Total 36669 (delta 21633), reused 33578 (delta 20229)
Receiving objects: 100% (36669/36669), 4.98 MiB | 1.02 MiB/s, done.
Resolving deltas: 100% (21633/21633), done.
From http://github.com/mxcl/homebrew
 * branch            master     -> FETCH_HEAD
error: Untracked working tree file '.gitignore' would be overwritten by merge.

Error: Failed while executing git pull http://github.com/mxcl/homebrew.git master

init して、リモートリポジトリの master ブランチ(HEAD として FETCH_HEAD を指している)を fetch したけど merge が失敗した、書いてある(ように見える)。ここで git reset すると、commit が指定されていないのでデフォルトの HEAD が選択されたことになり、さらにデフォルト操作の --mixed により、index だけが HEAD の内容に書き換えられる(戻される?)ことになる。git init した直後で working tree は何も変更されていないので、結果として FETCH_HEAD と同じ状態になり、上手くいったっぽい感じになった、……という解釈でいいんだろうか?

まとめ

ぎっと、ちょうむずかしい。

brew update したら以下のように distribute と pip がなくなって、python が更新された。

適当に検索したら https://github.com/mxcl/homebrew/wiki/Homebrew-and-Python というドキュメントが見つかった。いままで easy_install とか pip でパッケージを入れると、$HOMEBREW を homebrew を入れたディレクトリとして、$HOMEBREW/Cellar/python/2.7.1/lib/python2.7/site-packages とか $HOMEBREW/Cellar/python/2.7.1/bin に入っていたけど、python のバージョンが変更される(2.7.1 から 2.7.2 に上がるとか)たびに入れなおしたり、シンボリックリンクしなおしたりする必要があるので、これからは $HOMEBREW/Cellar/python/{SOME_VERSION}/lib/python2.7/site-packages は $HOMEBREW/lib/python2.7/site-packages へのシンボリックリンクにして、$HOMEBREW/Cellar/python/{SOME_VERSION}/bin に入るものは $HOMEBREW/share/python に入れる、らしい。で、最後に「distribute と pip の Formula はなくなったけど、brewpython を入れると easy_install は入るから easy_install pip すれば pip も使えるョ!」みたいなことが書いてある。

というわけで、python の環境を作りなおした。

前の環境を消す

brew で入れたものを uninstall して、cleanup でリンク切れ?したシンボリックリンクを消したり空のディレクトリを消したりしておく。

$ brew uninstall pip distribute python
$ brew cleanup

一応 virtualenv も作りなおすことにしたので、消しておく。

$ rm -rf ~/.virtualenvs/*

基本環境を作る

brewpython を install する。

$ brew install python
==> Downloading http://www.python.org/ftp/python/2.7.1/Python-2.7.1.tar.bz2
######################################################################## 100.0%
==> ./configure --prefix=/Users/nulll/homebrew/Cellar/python/2.7.1 --enable-shared
==> make
==> make install
==> Downloading http://pypi.python.org/packages/source/d/distribute/distribute-0.6.15.tar.gz
######################################################################## 100.0%
==> /Users/nulll/homebrew/Cellar/python/2.7.1/bin/python setup.py install
==> Caveats
A "distutils.cfg" has been written, specifing the install-scripts folder as:
  /Users/nulll/homebrew/share/python

If you install Python packages via "python setup.py install", easy_install, pip,
any provided scripts will go into the install-scripts folder above, so you may
want to add it to your PATH.

Distribute has been installed, so easy_install is available.
To update distribute itself outside of Homebrew:
$ easy_install pip
$ pip install --upgrade distribute

See: https://github.com/mxcl/homebrew/wiki/Homebrew-and-Python
==> Summary
/Users/nulll/homebrew/Cellar/python/2.7.1: 4768 files, 82M, built in 3.3 minutes

distribute も一緒に入ったけど、このままだと使えないので $HOMEBREW/bin からシンボリックリンクする。以下、$HOMEBREW を ~/homebrew とする。

$ cd ~/homebrew/bin
$ ln -s ../share/python/easy_install ./

easy_install で pip を入れて、シンボリックリンクする。

$ easy_install pip
$ cd ~/homebrew/bin
$ ln -s ../share/python/pip ./

pip で mercurial を入れて、シンボリックリンクする。

$ pip install mercurial
$ cd ~/homebrew/bin
$ ln -s ../share/python/hg ./

pip で virtualenvwrapper を入れる。virtualenv も入るので、シンボリックリンクする(virtualenvwrapper が中で使う)。

$ pip install virtualenvwrapper
$ cd ~/homebrew/bin
$ ln -s ../share/python/virtualenv ./

.zshrc を修正する。

...
export HOMEBREW=$HOME/homebrew
...
export WORKON_HOME=$HOME/.virtualenvs
if [ -f $HOMEBREW/share/python/virtualenvwrapper.sh ]; then
  source $HOMEBREW/share/python/virtualenvwrapper.sh
fi
...

virtualenv を作って、numpy と scipy を入れる

下準備

numpy と scipy を使うための virtualenv を作る。

$ mkvirtualenv work --no-site-packages --distribute

virtualenv 切り替え。以下、$ の左側の括弧で、現在の virtualenv を表わす。

$ workon work
(work)$

後で numpy と scipy のテストを走らせるために pip で nose を入れる。

(work)$ pip install nose
numpy を入れる

brew で gfortran を入れる。

(work)$ brew install gfortran

pip で numpy を入れる。

(work)$ pip install numpy

どんな感じで build されたか show_config で見つつ、まともに動くか test する。

(work)$ python
Python 2.7.1 (r271:86832, Mar 30 2011, 22:36:37) 
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> numpy.show_config()
lapack_opt_info:
    extra_link_args = ['-Wl,-framework', '-Wl,Accelerate']
    extra_compile_args = ['-faltivec']
    define_macros = [('NO_ATLAS_INFO', 3)]
blas_opt_info:
    extra_link_args = ['-Wl,-framework', '-Wl,Accelerate']
    extra_compile_args = ['-faltivec', '-I/System/Library/Frameworks/vecLib.framework/Headers']
    define_macros = [('NO_ATLAS_INFO', 3)]
>>> numpy.test()
Running unit tests for numpy
NumPy version 1.5.1
NumPy is installed in /Users/nulll/.virtualenvs/work/lib/python2.7/site-packages/numpy
Python version 2.7.1 (r271:86832, Mar 30 2011, 22:36:37) [GCC 4.2.1 (Apple Inc. build 5664)]
nose version 1.0.0
....
----------------------------------------------------------------------
Ran 3006 tests in 14.955s

OK (KNOWNFAIL=4, SKIP=1)
<nose.result.TextTestResult run=3006 errors=0 failures=0>

MacPorts だと ATLAS を入れたような記憶があるけど、外部ライブラリとして Apple 謹製の Accelerate, vecLib という BLAS っぽい?ものが使われているみたいなので、まあこれでいいんじゃないでしょうか。test も(KNOWNFAIL, SKIP があるけど)OK みたいだし。

scipy

参考: http://www.scipy.org/Installing_SciPy/Linux の Building everything from source with gfortran on Ubuntu (Nov 2010) 以下

pip で入れると外部ライブラリを見つけてくれないので、ソースを落としてきて site.cfg を書いて build して install する。外部ライブラリとして使うのは amd, umfpack だけにする。昔は fftw も使えたみたいだけど、最近はライセンスの関係で使え(使わ?)なくなったらしい。代わりに pyfftw というものが使えるらしいが、fft はわりとどうでもいいので、やめた。

amd, umfpack は SuiteSparse の一部なので、brew で suite-sparse を入れる。依存関係で、metis と tbb も入る。

(work)$ brew install suite-sparse

scipy のソースを落としてくる。

(work)$ curl -LsSf http://downloads.sourceforge.net/project/scipy/scipy/0.9.0/scipy-0.9.0.tar.gz | tar xvz
(work)$ cd scipy-0.9.0

以下のような site.cfg を書く。

[DEFAULT]
library_dirs = /Users/nulll/homebrew/lib
include_dirs = /Users/nulll/homebrew/include

[amd]
amd_libs = amd

[umfpack]
umfpack_libs = umfpack

以下のような構成になる。setup.py と同じディレクトリに site.cfg があれば、見てくれるらしい。

(work)$ ls
INSTALL.txt     MANIFEST.in     THANKS.txt      scipy           setupscons.py
LATEST.txt      PKG-INFO        TOCHANGE.txt    setup.py        site.cfg
LICENSE.txt     README.txt      doc             setupegg.py     tools

setup.py で build して、install する。

(work)$ python setup.py build
...
(work)$ python setup.py install
...

show_config と test の結果は以下のようになった。ちなみに、build したディレクトリで python インタプリタを起動して import scipy すると怒られる(怒られた)ので、適当なディレクトリに移動してからやった。

(work)$ python
Python 2.7.1 (r271:86832, Mar 30 2011, 22:36:37) 
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import scipy
>>> scipy.show_config()
amd_info:
    libraries = ['amd']
    library_dirs = ['/Users/nulll/homebrew/lib']
    define_macros = [('SCIPY_AMD_H', None)]
    swig_opts = ['-I/Users/nulll/homebrew/include']
    include_dirs = ['/Users/nulll/homebrew/include']
umfpack_info:
    libraries = ['umfpack', 'amd']
    library_dirs = ['/Users/nulll/homebrew/lib']
    define_macros = [('SCIPY_UMFPACK_H', None), ('SCIPY_AMD_H', None)]
    swig_opts = ['-I/Users/nulll/homebrew/include', '-I/Users/nulll/homebrew/include']
    include_dirs = ['/Users/nulll/homebrew/include']
lapack_opt_info:
    extra_link_args = ['-Wl,-framework', '-Wl,Accelerate']
    extra_compile_args = ['-faltivec']
    define_macros = [('NO_ATLAS_INFO', 3)]
blas_opt_info:
    extra_link_args = ['-Wl,-framework', '-Wl,Accelerate']
    extra_compile_args = ['-faltivec', '-I/System/Library/Frameworks/vecLib.framework/Headers']
    define_macros = [('NO_ATLAS_INFO', 3)]
>>> scipy.test()
Running unit tests for scipy
NumPy version 1.5.1
NumPy is installed in /Users/nulll/.virtualenvs/work/lib/python2.7/site-packages/numpy
SciPy version 0.9.0
SciPy is installed in /Users/nulll/.virtualenvs/work/lib/python2.7/site-packages/scipy
Python version 2.7.1 (r271:86832, Mar 30 2011, 22:36:37) [GCC 4.2.1 (Apple Inc. build 5664)]
nose version 1.0.0
......
----------------------------------------------------------------------
Ran 4722 tests in 85.832s

OK (KNOWNFAIL=18, SKIP=42)
<nose.result.TextTestResult run=4722 errors=0 failures=0>

amd, umfpack, lapack, blas で外部ライブラリが使われていることがわかる。test も KNOWNFAIL, SKIP があるものの、OK らしい。

まとめ

work という名前の virtualenv を作ったけど、働きたくない。

諸事情*1により MacPorts から Homebrew に移行した。以下の三つの作業を行なった。

  1. MacPorts 関係のバイナリを使うのをやめる
  2. MacPorts を消す
  3. Homebrew を入れる

1. MacPorts 関係のバイナリを使うのをやめる

MacPorts で入れた zsh を使うのをやめる

chsh する。

$ chsh
Changing shell for nulll.
Password for nulll: 

$EDITOR で以下が開くので、Shell を /bin/zsh 等に変更する。

# Changing user information for nulll.
# Use "passwd" to change the password.
##
# Open Directory: /Local/Default
##
Shell: /bin/zsh
Full Name: nulll
Office Location:
Office Phone:
Home Phone:

/etc/shells から /opt/local/bin/zsh を削除しておく。

$PATH から /opt/local/bin を外す

.zshrc を修正する。

2. MacPorts を消す

MacPorts の公式ドキュメントに消しかた http://guide.macports.org/chunked/installing.macports.uninstalling.html が書いてあったので、だいたいこの通りやった。関係するディレクトリを消した後、

$ mdfind macports

で、引っかかったものも消した。ログを見ると

$ sudo rm -rf /private/var/db/receipts/org.macports.MacPorts.*

とか、やったらしい。

3. Homebrew を入れる

好きな場所に Homebrew を入れる

とりあえず Xcode を入れるのは当然として、Homebrew の公式ドキュメント https://github.com/mxcl/homebrew/wiki/installation を見ると

$ ruby -e "$(curl -fsSLk https://gist.github.com/raw/323731/install_homebrew.rb)"

を実行する、と書いてあるけど、これだと問答無用で /usr/local に入れられる(た)ので、やめる。代わりに Homebrew を入れたいディレクトリを適当に作って移動した後、github から tarball を取得して展開する。個人的に、$HOME 以下に入れたかったので、以下のようにした。

$ mkdir ~/homebrew
$ cd ~/homebrew
$ curl -LsSf https://github.com/mxcl/homebrew/tarball/master | /usr/bin/tar xvz --strip 1

その後 .zshrc を修正するなどして、展開したディレクトリの bin(上記例だと $HOME/homebrew/bin) を $PATH に加える。以降、パスを通した brew コマンドを使って install したアプリケーションやライブラリは、パスを通した brew コマンドの親ディレクトリ以下の bin とか lib に入る(上記例だと $HOME/homebrew/bin とか $HOME/homebrew/lib)。

Homebrew を使えるようにする

とりあえず update しようとすると、

$ brew update
Please `brew install git' first.

まずは git を install しろ、と言われるので、git を入れる。

$ brew install git
==> Downloading http://kernel.org/pub/software/scm/git/git-1.7.4.1.tar.bz2
######################################################################## 100.0%
==> make prefix=/Users/nulll/homebrew/Cellar/git/1.7.4.1 install
...

git を入れた後は brew update で Homebrew を更新する。

$ brew update

Initialized empty Git repository in /Users/nulll/homebrew/.git/
remote: Counting objects: 33487, done.
remote: Compressing objects: 100% (15498/15498), done.
remote: Total 33487 (delta 19204), reused 30271 (delta 17435)
Receiving objects: 100% (33487/33487), 4.55 MiB | 1.15 MiB/s, done.
Resolving deltas: 100% (19204/19204), done.
From http://github.com/mxcl/homebrew
 * branch            master     -> FETCH_HEAD
Updated Homebrew from TAIL to 406c3050.
No formulae were updated.
No external commands were updated.

Library 以下と、brew コマンド本体と man が git で管理されているらしい。

$ cd ~/homebrew
$ git ls-files
.gitignore
Library/Aliases/0mq
...
Library/Contributions/brew_bash_completion.sh
...
Library/Formula/a2ps.rb
...
Library/Homebrew/LICENSE
...
README.md
bin/brew
share/man/man1/brew-man.1
share/man/man1/brew.1
zshbrew コマンドの補完

homebrew を入れたディレクトリの、Library/Contributions に brew_zsh_completion.zsh という、zsh 用の completion ファイルがあったので、http://d.hatena.ne.jp/hagure_beans/20101211/1292081391 を参考にして使えるようにした。ls するとわかるけど、bash とか fish 用のものもある。

$ ls ~/homebrew/Library/Contributions
brew_bash_completion.sh         brew_zsh_completion.zsh         manpages
brew_fish_completion.fish       examples

シンボリックリンクして、

$ ln -s ~/homebrew/Library/Contributions/brew_zsh_completion.zsh ~/.zsh/completion/_homebrew

以下のように .zshrc を修正する。

...

fpath=(~/.zsh/completions ${fpath})
if [ -d ~/.zsh/cache ]; then
    zstyle ':completion:*' use-cache yes
    zstyle ':completion:*' cache-path ~/.zsh/cache
fi
autoload -U compinit
compinit

...

適当に shell を開き直したり、.zshrc を再読み込みした後、brew 以降の補完が効くようになる。

$ source ~/.zshrc
$ brew [TAB]
cat       -- display formula file for a formula
cleanup   -- uninstall unused and old versions of packages
create    -- create a new formula
deps      -- list dependencies and dependants of a formula
doctor    -- audits your installation for common issues
edit      -- edit a formula
home      -- visit the homepage of a formula or the brew project
info      -- information about a formula
install   -- install a formula
link      -- link a formula
list      -- list files in a formula or not-installed formulae
log       -- git commit log for a formula
missing   -- check all installed formuale for missing dependencies.
outdated  -- list formulas for which a newer version is available
prune     -- remove dead links
remove    -- remove a formula
search    -- search for a formula (/regex/ or string)
server    -- start a local web app that lets you browse formulae (requires Sinatra)
unlink    -- unlink a formula
update    -- freshen up linksuses      -- show formulas which depend on a formula
$ brew u[TAB]
unlink    -- unlink a formula
update    -- freshen up links
uses      -- show formulas which depend on a formula

まとめ

とりあえず MacPorts を捨てて、Homebrew を使えるようになった、が、 Formula が git で管理されているということは、自分で勝手に新しい Formula を追加したり、自分で勝手に既存の Fromula を書き換えたりすると brew update 時に conflict する可能性があるということなので、ここらへんをうまくやりたいところである。

*1:作業用マシンを自分以外の人間と微妙に共有しているという状況で MacPorts を使っていたが、MacPorts だと /opt/local という共有エリアに色々入るので、勝手に $ sudo port -u upgrade outdated とかするとバージョンの関係で自分以外の人の何かが動かなくなったりしそうでこわい、かと言って自分が使ってるものだけ選んで upgrade とかめんどい、ので、個人エリアに色々入れて管理するようにしたい

MacBookSSD を載せたものの、SSD にしたことで容量が減ったし、読んだり書いたりしまくると性能が落ちそうなので、色々と設定をした。もはや読み書きによる性能低下は気にする必要はないっぽいけど、気休めということで。

方針としては以下のような感じ。

  • 無駄なものは消す
  • たくさん読み書きされるものは SSD 上に置かない
    • 容量が小さい + 消えてもいいもの: メモリ上に置く
    • 容量が大きいもの: 外付け HDD に置く

具体的には、以下の設定をした。

  • swap(dynamic_pager)を無効にする
  • hibernatemode 変更
  • 各種キャッシュを ramdisk に移動
  • iTunes 関連のディレクトリを外付け HDD に移動

swap(dynamic_pager)を無効にする、hibernatemode 変更

swap(dynamic_pager)を無効にする

メモリが 8GB に増えたし、余計な swapfile を勝手に作られるのもアレなので、無効にした。Snow Leopard では以下のようにして dynamic_pager を起動しないようにした後、システムを再起動すればいいようだ。

$ sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.dynamic_pager.plist

再起動した後 swapfile を消す。

$ sudo rm /var/vm/swapfile*

sysctl で、swap が無効になっていることを確認した。

$ sysctl vm.swapusage
vm.swapusage: total = 0.00M  used = 0.00M  free = 0.00M
hibernatemode 変更

デフォルトだと sleep 時にメモリの内容をディスクに書き込む(メモリと同サイズのファイル /var/vm/sleepimage を作成する)ので、ディスクに書き込まないようにする。

$ sudo pmset -a hibernatemode 0

hibernatemode の引数の数字の詳細は man に書いてあった。

$ man pmset
...

SAFE SLEEP ARGUMENTS
...
     0000 0001 (bit 0) enables hibernation; causes OS X to write memory state to hibernation image
     at sleep time. On wake (without bit 1 set) OS X will resume from the hibernation image. Bit 0
     set (without bit 1 set) causes OS X to write memory state and immediately hibernate at sleep
     time.

     0000 0010 (bit 1), in conjunction with bit 0, causes OS X to maintain system state in memory
     and leave system power on until battery level drops below a near empty threshold (This enables
     quicker wakeup from memory while battery power is available). Upon nearly emptying the bat-
     tery, OS X shuts off all system power and hibernates; on wake the system will resume from
     hibernation image, not from memory.
...
     hibernatemode = 0 (binary 0000) by default on supported desktops. The system will not back
     memory up to persistent storage. The system must wake from the contents of memory; the system
     will lose context on power loss. This is, historically, plain old sleep.

     hibernatemode = 3 (binary 0011) by default on supported portables. The system will store a
     copy of memory to persistent storage (the disk), and will power memory during sleep. The sys-
     tem will wake from memory, unless a power loss forces it to restore from disk image.
...

MacBook における hibernatemode のデフォルトは 3(bit 0 と bit 1 に1が立っている)で、以下の二つの合わせ技、という状態のようだ。

  • bit 0 に1が立っている == hibernation 有効
    • sleep 時にメモリの内容をディスクに書き込んで hibernate image(/var/vm/sleepimage) 作成、即 hibernation する
    • sleep から復帰するときは hibernate image から内容を読み込む
  • bit 1 に1が立っている == sleep 時、ある閾値以上バッテリ残量が残っている間はメモリの内容をディスクに書き込まないが、バッテリ残量が閾値以下になるとメモリの内容をディスクに書き込む

各種キャッシュを ramdisk に移動

ramdisk を使うためには ramdisk を作成した後、ファイルシステムを構築してマウントする。ファイルシステムを構築してマウントする方法には、いくつか選択肢があるようだ(他にもあるかも)。

ramdisk を作成する

hdid で ramdisk を作成することができる。ramdisk の容量はブロック数(1ブロック == 512 byte)で指定する。1GB の ramdisk を作りたかったので 1024 * 1024 * 1024 / 512 = 2097152 にした。

$ sudo hdid -nomount ram://2097152
/dev/disk3
newfs_hfs でファイルシステム構築、mount でマウント

newfs_hfs でファイルシステムを構築する。-v で名前を付けることができる。

$ sudo newfs_hfs -v tmppp /dev/disk3
Initialized /dev/rdisk3 as a 1024 MB HFS Plus volume

mount でマウントする。ディスクデバイスの後の引数で、マウントポイントを指定することができる。

$ mkdir /tmp/tmppp
$ sudo mount -t hfs /dev/disk3 /tmp/tmppp
newfs_hfs でファイルシステム構築、diskutil mount でマウント

ファイルシステムを構築する。

$ sudo newfs_hfs -v tmppp /dev/disk3
Initialized /dev/rdisk3 as a 1024 MB HFS Plus volume

diskutil mount でマウントする。マウントポイントは /Volumes/ファイルシステム構築時に指定した名前 になる。

$ sudo diskutil mount /dev/disk3
diskutil eraseDisk でファイルシステム構築 + マウント

diskutil eraseDisk は、ファイルシステムのフォーマット、マウントポイント、ディスクデバイス、を指定すると、ディスクデバイス上にファイルシステムを構築して、マウントポイントにマウントしてくれる。

$ sudo diskutil eraseDisk HFS+ tmppp /dev/disk3

上記のように実行すると、/dev/disk3 に HFS+ のファイルシステムを構築し、 /Volumes/tmppp にマウントされる。

何がちがうの?

mount でマウントすると、Finder から取り出せないようだ(強制的に…… もできない)。

このせいか、再起動やシャットダウンをすると、1分くらいかかる(タイムアウトするまで待たされている?)。コマンドラインを使えば umount した後、hdiutil detach することで、取り出す(というか、削除?)ことができる。

$ sudo umount /dev/disk3
$ sudo hdiutil detach /dev/disk3     
"disk3" unmounted.
"disk3" ejected.

umount の前に hdiutil detach はできないらしい。

$ sudo hdiutil detach /dev/disk3
"disk3" unmounted.
hdiutil: couldn't eject "disk3" - リソースが使用中です

他の違いを確認するべく、5 MB の ramdisk、/dev/disk3, /dev/disk4, /dev/diks5 を作って以下のようにした。

diskutil list と mount で確認する。

$ diskutil list
/dev/disk3
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                                                   *5.2 MB     disk3
/dev/disk4
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                            tmppp                  *5.2 MB     disk4
/dev/disk5
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *5.2 MB     disk5
   1:                  Apple_HFS tmppp                   5.2 MB     disk5s1

newfs_hfs でファイルシステムを構築するとパーティションが作られないが、diskutil eraseDisk でファイルシステムを構築するとパーティションが作られる。

$ mount
...
/dev/disk3 on /tmp/foobar (hfs, local)
/dev/disk4 on /Volumes/tmppp (hfs, local, nodev, nosuid, noowners)
/dev/disk5s1 on /Volumes/tmppp 1 (hfs, local, nodev, nosuid, noowners)

diskutil でマウントすると、nodev, nosuid, noowners というオプションが付く。といった感じか。これによって、何が変わるんだ……?

各種キャッシュを ramdisk に移動

とりあえず以下を ramdisk 上に置くことにした。

  • /private/tmp
  • /private/var/folders/Hs/HsEDZDoMF1uNrEutSd+kCE+++TI/-Tmp-
    • 汎用的な一時ファイルが置かれる(Preview.app で画像を開くとき、とか)
    • /private/var/folders 以下、Hs/HsEDZDoMF1uNrEutSd+kCE+++TI の部分はマシン毎に異なる
  • /private/var/folders/Hs/HsEDZDoMF1uNrEutSd+kCE+++TI/TemporaryItems
    • ブラウザで flash 動画を見るとき、一時的に動画ファイルが置かれる
  • /opt/local/var/macports/build
    • macports における、ビルド時の working ディレクト
    • 大きめのものをビルドすると割とすぐに溢れるので、あまりよくないかもしれない
      • 例: gcc をビルドしたら溢れた → 外付け HDD に移動 → 一時的に 3 GB を超えた……
  • /Users/nulll/Library/Application\ Support/Reeder/Caches
    • Reeder がキャッシュを保存する
  • /Users/nulll/Library/Caches/Adobe
    • flash が各種設定を保存する
  • /Users/nulll/Library/Caches/Chromium
    • Chromium がキャッシュを保存する
  • /Users/nulll/Library/Caches/Firefox
    • Firefox がキャッシュを保存する
  • /Users/nulll/Library/Caches/TemporaryItems
    • flash が一時的な情報を置く
  • /Users/nulll/Library/Caches/com.apple.Safari
    • Safari がキャッシュを保存する
  • /Users/nulll/Library/Caches/com.reederapp.mac.Reeder
    • Reeder がキャッシュを保存する
  • /Users/nulll/Library/Preferences/Macromedia
    • flash が各種設定を保存する

ramdisk 上に適当なディレクトリを作成して、上記ディレクトリを削除した後、シンボリックリンクする。

$ sudo rm -rf /opt/local/var/macports/build
$ mkdir /Volumes/tmp/mp_build
$ sudo ln -s /Volumes/tmp/mp_build /opt/local/var/macports/build
rc.local で起動時に実行する

ramdisk 作成、ファイルシステム構築、マウント、上述した各種ディレクトリからシンボリックリンクディレクトリの所有者変更、を起動時に行なうべく、/etc/rc.local として、以下のようなスクリプトを書いた。ファイルシステム構築 + マウントは、newfs_hfs + diskutil mount を使った。

#!/bin/sh

launchctl start com.apple.hdiejectd
sleep 3

rd=`hdid -nomount ram://2097152`
newfs_hfs -v tmp $rd
diskutil mount $rd

vol=/Volumes/tmp
while read src dst
do
  rm -rf $src
  mkdir -p $vol/$dst
  ln -s $vol/$dst $src
done <<SRC_DSTS
/private/tmp priv_tmp
/private/var/folders/Hs/HsEDZDoMF1uNrEutSd+kCE+++TI/-Tmp- var_tmp
/private/var/folders/Hs/HsEDZDoMF1uNrEutSd+kCE+++TI/TemporaryItems var_tmpitems
/opt/local/var/macports/build mp_build
/Users/nulll/Library/Application\ Support/Reeder/Caches cache_reeder_
/Users/nulll/Library/Caches/Adobe cache_adobe
/Users/nulll/Library/Caches/Chromium cache_chromium
/Users/nulll/Library/Caches/Firefox cache_firefox
/Users/nulll/Library/Caches/TemporaryItems cache_tmpitems
/Users/nulll/Library/Caches/com.apple.Safari cache_safari
/Users/nulll/Library/Caches/com.reederapp.mac.Reeder cache_reeder
/Users/nulll/Library/Preferences/Macromedia prefs_flash
SRC_DSTS

mkdir $vol/Downloads
chown -R nulll:wheel $vol

mount -u -o noatime /

launchctl start com.apple.hdiejectd; sleep 3 は、起動した後だと普通に ramdisk を作成することができるのに、rc.local に書くと ramdisk が作成されなかったので、http://moimoitei.blogspot.com/2010/11/use-ramdisk-on-macosx.html を参考にして入れた。

ついでに Dwonloads というディレクトリを作成して、Firefox におけるファイルのダウンロード先として使っている。

mount -u -o noatime / は、/ の最終アクセス時刻を更新しないようにすることで、SSD の書き込み回数が減ることを期待して入れた。無いよりはマシかもね、程度で、かなり気休め感が高い。

シングルユーザーモード

当初、rc.local 内のヒアドキュメントあたりで間違っていたらしく、起動はするが壁紙が表示されるだけで何もできない、という状況になった。ほんの一週間ちょっと前、HDD が壊れたときのあの悪夢再び……! みたいな感じで、泣きそうになりながらシングルユーザーモードで起動して、rc.local を編集した。

  • シングルユーザーモードで起動する
    • 電源を押して、ジャーンと鳴った後、command + s を押し続ける
  • シングルユーザーモードで起動すると、/ が readonly でマウントされているので、書き込めるようにする
# mount -uw /
  • 適当に編集したり、実行されないように移動したり消したりする

iTunes 関連のディレクトリを外付け HDD に移動

iTunes 関連のディレクトリ、以下の二つを外付け HDD に移動した。

  • /Users/nulll/Library/Application\ Support/MobileSync/Backup
    • iPhoneiPad のバックアップが作成される
  • /Users/nulll/Music/iTunes
    • iTunes のライブラリとか、iPhone のアプリケーションとかが置かれる

適当に移動した後、シンボリックリンクする

$ mv ~/Music/iTunes /Volumes/data/
$ rm -rf ~/Music/iTunes
$ ln -s /Volumes/data/iTunes ~/Music/iTunes
$ mv ~/Library/Application\ Support/MobileSync/Backup /Volumes/data/
$ rm -rf ~/Library/Application\ Support/MobileSync/Backup
$ ln -s /Volumes/data/iTunes/Backup ~/Library/Application\ Support/MobileSync/Backup

まとめ

今後もキャッシュっぽいディレクトリを発見し次第、ramdisk 上に置いていきたいところであるが、全体的に貧乏臭い感じの使い方であることは否めない。

MacBook Air 11インチ欲しい! ……けど買うお金がないので、物理的な重さには目をつぶるとして(そこが一番重要な気もするが)、いま使っている MacBook(late 2008, alminium)に SSD とメモリを増設して性能だけでも MacBook Air ばりにすることでガマンした。

SSD

Amazon とかだと 30000円くらいする中身が東芝な IO-Data の SSD 128GB が、ioPLAZA http://www.ioplaza.jp/ のワケありプレミアムアウトレットで 19800円だったので、これにした。ワケありと言っても箱がないだけ(たぶん)。産地直送、という感じで、金沢から北國新聞に包まれて届いた。

メモリ

SanMax 4GB*2 にした。Ark http://www.ark-pc.co.jp/ で 8800円だった。

交換

写真とか撮りつつ詳しく説明しようと思ったけど、簡単すぎて何も説明することがなかったのでやめる。特に HDD はネジを一本外すだけで取り外すことができる。http://ascii.jp/elem/000/000/186/186030/ とか http://ascii.jp/elem/000/000/187/187408/ を見れば誰でもできると思う。

というわけで無事に認識した。当初、いま使っているこの MacBook(late 2008, alminium)では8GBのメモリを認識しなかったらしいけど、EFI アップデートをすると認識するようになるらしい。"この Mac について → 詳しい情報 → ハードウェア" と開いていくと、"ブート ROM のバージョン" は MB51.007D.003 だった。Apple の情報 http://support.apple.com/kb/HT1237?viewlocale=ja_JP&locale=ja_JP と見比べると、最新バージョンになっているらしい。昔は EFI アップデートというやつを単体でやったような気がしたが、今回は OS をクリーンインストールした後すぐ、ソフトウェアアップデートをしただけで上記状態になっていた。あるいはブート "ROM" というだけあって、前にアップデートした状態が生きているのか?

SSD ベンチマーク

MacBook AirSSD ベンチ結果と比べるべく、やってみた。Gizmodo に 64GB のベンチ結果 http://www.gizmodo.jp/2010/10/ssd_macbook_air.html が、Engadget に 128GB のベンチ結果 http://japanese.engadget.com/2010/10/22/macbook-air-11-ssd/ がある。ベンチマークは同様に Xbench http://www.xbench.com/ を使った。

MacBook Air の 128GB(engadget の方)と比べると、シーケンシャルは勝っているけど、ランダムは少し負けている、という感じか。

メモリテスト

Mac でも memtest86+ http://www.memtest.org/ が動くらしいけど、その前に軽くチェックしてみよう、ということで Rember http://www.kelleycomputing.net/rember/ というアプリケーションを使ってみた。"軽く" のつもりが二時間くらいかかったけど、エラーはなかった。その後、寝ている間に memtest したのだが、6時間で三周も終わっていなかった。なんかもうちょっと周したいな、ということで放置して出かけて、帰ってきてみたら

六周しか終わってなかった。時間かかりすぎだろ……。まあこれくらいでいいだろう、ということで、強制終了した。

まとめ

とりあえず次の、新しい MacBook が出るまではがんばれそう。

真実

といった感じで本来は MacBook Air をガマンするための交換だったのだが、注文した SSD が届いて「週末にでも交換するか」などと思っていたら、週末を迎えることなく HDD が突然死したので、割と本気で泣きそうになりながら必死で交換したのだった。壊れた HDD は外付けケースに入れてもマウントできなかったよ!

iPhone に入っている各種アプリは iTunesiPhone を自動で同期しないように設定して、iTunes Store でアカウントを認証した後、iPhone を繋ぎ、右クリックして表われる "購入した項目を転送"

を選ぶことで iTunes に転送できた。その後、同期したら(おまえの iPhone のアプリを全部消すからな! ほんとに消すからな! いいんだな! と、おどされる)元通り? になったっぽい。本当か。


しかし、それ以外の、失なわれた、ものは、けっこう、それなりに、多い。SSD のおかげで Spotlight が超速くなったけど、検索するべきファイルは失なわれてしまった……。

Togetter - まとめ「条件付き確率の問題」 の話をやっと理解できた気がしたので書いておく。

数えあげ

条件「子供が二人いて、少なくとも一人は男で火曜日に生まれた」を満たす組み合わせを考える。子供がa、bの二人いたとして、条件を満たすものは……

aが男で火曜日に生まれた、bが男で月曜日に生まれた
aが男で火曜日に生まれた、bが男で火曜日に生まれた
aが男で火曜日に生まれた、bが男で水曜日に生まれた
aが男で火曜日に生まれた、bが男で木曜日に生まれた
aが男で火曜日に生まれた、bが男で金曜日に生まれた
aが男で火曜日に生まれた、bが男で土曜日に生まれた
aが男で火曜日に生まれた、bが男で日曜日に生まれた

aが男で月曜日に生まれた、bが男で火曜日に生まれた
aが男で火曜日に生まれた、bが男で火曜日に生まれた ← これは既出なので無視
aが男で水曜日に生まれた、bが男で火曜日に生まれた
aが男で木曜日に生まれた、bが男で火曜日に生まれた
aが男で金曜日に生まれた、bが男で火曜日に生まれた
aが男で土曜日に生まれた、bが男で火曜日に生まれた
aが男で日曜日に生まれた、bが男で火曜日に生まれた

aが男で火曜日に生まれた、bが女で月曜日に生まれた
aが男で火曜日に生まれた、bが女で火曜日に生まれた
aが男で火曜日に生まれた、bが女で水曜日に生まれた
aが男で火曜日に生まれた、bが女で木曜日に生まれた
aが男で火曜日に生まれた、bが女で金曜日に生まれた
aが男で火曜日に生まれた、bが女で土曜日に生まれた
aが男で火曜日に生まれた、bが女で日曜日に生まれた

aが女で月曜日に生まれた、bが男で火曜日に生まれた
aが女で火曜日に生まれた、bが男で火曜日に生まれた
aが女で水曜日に生まれた、bが男で火曜日に生まれた
aが女で木曜日に生まれた、bが男で火曜日に生まれた
aが女で金曜日に生まれた、bが男で火曜日に生まれた
aが女で土曜日に生まれた、bが男で火曜日に生まれた
aが女で日曜日に生まれた、bが男で火曜日に生まれた

以上、27通りになる。「aが男で火曜日に生まれた、bが男で火曜日に生まれた」を二回数えないように注意する。上記27通りのうち、「二人とも男」であるものは13通りある。よって、「子供が二人いて、少なくとも一人は男で火曜日に生まれたとき、子供が二人とも男」である確率は 13/27 になる。

条件付き確率

「子供が二人いて、少なくとも一人は男で火曜日に生まれたとき、子供が二人とも男」である確率、を条件付き確率で表わすと

P(子供が二人とも男 | 子供が二人いて、少なくとも一人は男で火曜日に生まれた)

になる。条件付き確率の公式から以下が成り立つ。

P(子供が二人とも男 | 子供が二人いて、少なくとも一人は男で火曜日に生まれた)
= P(子供が二人いて、少なくとも一人は男で火曜日に生まれたとき、子供が二人とも男) / P(子供が二人いて、少なくとも一人は男で火曜日に生まれた)

右辺の式の確率をそれぞれ考える。まず P(子供が二人いて、少なくとも一人は男で火曜日に生まれたとき、子供が二人とも男) を考える。中身をわかりやすく書き直すと P(子供が二人とも男で、少なくとも一人は火曜日に生まれた) となる。この確率は以下のようになる。

P(子供が二人とも男で、少なくとも一人は火曜日に生まれた) 
= P(aが男で火曜日に生まれた、bが男で月曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で水曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で木曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で金曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で土曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で日曜日に生まれた)
+ P(aが男で月曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で水曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で木曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で金曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で土曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で日曜日に生まれた、bが男で火曜日に生まれた)
= 13 * 1/2 * 1/7 * 1/2 * 1/7
= 13/196

まとめると以下のように表わせる。

P(子供が二人とも男で、少なくとも一人は火曜日に生まれた) 
= P(子供が二人とも男) * (1 - P(二人とも火曜日に生まれていない)
= 1/2 * 1/2 * (1 - 6/7 * 6/7)
= 13/196

P(子供が二人いて、少なくとも一人は男で火曜日に生まれた) は以下のようになる。

P(子供が二人いて、少なくとも一人は男で火曜日に生まれた)
= P(aが男で火曜日に生まれた、bが男で月曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で水曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で木曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で金曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で土曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが男で日曜日に生まれた)
+ P(aが男で月曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で水曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で木曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で金曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で土曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で日曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが女で月曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが女で火曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが女で水曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが女で木曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが女で金曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが女で土曜日に生まれた)
+ P(aが男で火曜日に生まれた、bが女で日曜日に生まれた)
+ P(aが女で月曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが女で火曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが女で水曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが女で木曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが女で金曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが女で土曜日に生まれた、bが男で火曜日に生まれた)
+ P(aが女で日曜日に生まれた、bが男で火曜日に生まれた)
= 27 * 1/2 * 1/7 * 1/2 * 1/7
= 27/196

まとめると以下のように表わせる。

P(子供が二人いて、少なくとも一人は男で火曜日に生まれた)
= P(子供が二人いて、二人とも男、少なくとも一人は火曜日に生まれた)
+ P(子供が二人いて、一人は男で火曜日に生まれた、一人は女)

= P(子供が二人いて、二人とも男) * (1 - P(二人とも火曜日に生まれていない))
+ P(子供が二人いて、一人は男で火曜日に生まれた、一人は女)

= 1/2 * 1/2 * (1 - 6/7 * 6/7) + 2 * 1/2 * 1/2 * 1/7
= 13/196 + 14/196
= 27/196

P(子供が二人いて、一人は男で火曜日に生まれた、一人は女) は「aが男で火曜日に生まれた、bが女」と「aが女、bが男で火曜日に生まれた」の二通りがあるので2をかけるのを忘れないようにする。

以上より、以下を得る。

P(子供が二人とも男 | 子供が二人いて、少なくとも一人は男で火曜日に生まれた)
= P(子供が二人いて、少なくとも一人は男で火曜日に生まれたとき、子供が二人とも男) / P(子供が二人いて、少なくとも一人は男で火曜日に生まれた)
= 13/196 / 27/196
= 13/27