homebrew — Mac OS X 下新的软件包管理工具

虽然 Mac OS X 自带了不少 Unix 下的开源软件,比如 vim, ruby, python, perl 等等,也自带了许多常用的库,包括 iconv, zlib 等等,但我们仍然有时会需要自己动手安装一些这样的软件或者库,要自动化这样的安装,现有最常见的选择是 [MacPorts][macports] 和 [Fink][fink],其中 MacPorts 是基于源代码的包管理,并不在自己的库里储存软件的实际内容,只有一个定义如何编译代码的 [Portfile][portfile] 和一些专门针对这个平台的 patch;而 Fink 则是 [Apt][apt] 包管理系统在 Mac OS X 下的一个克隆,采用二进制分发,用户直接从 Fink 的仓库中下载安装软件。

[macports]: http://www.macports.org
[fink]: http://www.finkproject.org
[portfile]: http://guide.macports.org/chunked/development.html
[apt]: http://en.wikipedia.org/wiki/Advanced_Packaging_Tool

这两种方式各自有各自的优点和缺陷:

MacPorts 基于源代码的管理优点是非常灵活,更新很快 (很多时候更新只需要修改一下 Portfile 里的版本号和压缩包校验码就可以),用户要订制安装也可以简单的通过修改 Portfile 实现,而且很多开源软件的安装配置会有多种模式 (典型的大都通过 `configure` 步骤配置),在 MacPorts 中可以方便地通过 `variants` 参数指定,而不必像二进制分发那样,在远程服务器上编译的时候就定死了。而 MacPorts 的问题是,它希望自己安装的每套软件,所有的依赖都在它自己这个系统内 (一般就是你的 `/opt/local`) 解决,就算 Mac OS X 系统原生自带了满足依赖的库,它也坚决不用,这样就给你的系统增加了许多冗余,也客观增加了管理上的难度,典型的情况是:你的系统里装了两套 Python,该怎么管理外部安装的 Python 模块?比如通过 `easy_install` 或 `setup.py` 安装的,往往很难记住到底装到哪里了。

而 Fink 虽然不会这么自作主张地添加依赖,最大的问题是更新不够及时,这也是缺乏维护人手导致的。二进制安装的缺点在上面也提到了:不便定制。

所以 [Homebrew](http://github.com/mxcl/homebrew) 的出现,也许不是很及时,但在现在仍然是很必要的,它有这么一些优点:

1. 尽可能的利用你的系统里自带的库,包括 zlib, OpenSSL, Python 等等,只要 Mac OS X 自带了,它就不会另装一份。

2. 定制简单,通过用 Ruby 写的 Homebrew formula 来定制,甚至可以灵活的跟踪直接来自版本管理库的最新软件

3. 用 Git 管理和同步自身

4. 直接装在 `/usr/local` 下,这样可以少定义很多各种 `PATH` 环境变量

其中第一点尤为重要。好的,下面简单介绍一下 Homebrew 的安装,以及它是如何工作的。

#### 安装

首先,Homebrew 的原则是“No sudo”,也就是说,既然 Mac OS X (client 版本) 绝大部分情况下都是归你这个有管理员权限的用户,为什么在自己的 `/usr/local` 下安装程序还需要 `sudo` 呢?所以,首先:

sudo chown -R `whoami` /usr/local

然后可以正式开始安装,我推荐的安装方式是先用 [git-osx-installer](http://code.google.com/p/git-osx-installer/) 装上 git,然后用 git 安装:

cd /usr/local
git init
git remote add origin git://github.com/mxcl/homebrew.git
git pull origin master

这么做的实际作用是把你的 `/usr/local` 目录变成了一个本地 git 仓库,只不过这个仓库只跟踪跟 Homebrew 相关的更新,并不影响任何其他软件的安装。

这样安装会在 `/usr/local` 下创建 `Library` 这个目录,然后在 `/usr/local/bin` 中加入 `brew` 这个 ruby 脚本。

#### 使用

安装完毕,下面就可以试试了:

brew search

这个命令用来搜索所有可以通过 homebrew 安装的软件,不带任何参数的时候就是列出所有的。可以看到数量已经不少了。

下面就是选择安装,比如我想安装 unrar:

$ brew search rar
gnu-scientific-library unrar

$ brew install unrar
Warning: It appears you have Macports or Fink installed
Although, unlikely, this can break builds or cause obscure runtime issues.
If you experience problems try uninstalling these tools.
/usr/local/Library/Formula/unrar.rb:3: warning: already initialized constant ALL_CPP
==> Downloading http://www.rarlab.com/rar/unrarsrc-3.9.4.tar.gz
######################################################################## 100.0%
==> g++ -O4 -march=core2 -mmmx -msse3 -w -pipe -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE all.cpp -o unrar
/usr/local/Cellar/unrar/3.9.4: 3 files, 320K, built in 13 seconds

可以看到,unrar 被安装到了 `/usr/local/Cellar/unrar/3.9.4` 这个目录下,但这样我们访问起来显然很不方便,所以 Homebrew 会在 `/usr/local/bin` 下面创建到 `unrar` 程序的符号链接,如果安装的是库之类的,也会对应在 `/usr/local/lib` 这样的目录下创建符号链接。所以这是一套类似 [GoboLinux](http://www.gobolinux.org/) 的软件管理方式。

安装后就可以用 `list` 命令列出:

$ brew list
pkg-config unrar

#### 更新

如果用了一段时间,需要更新同步上游的 Formula,可以简单地:

$ brew update
From git://github.com/mxcl/homebrew
* branch master -> FETCH_HEAD
Updated Homebrew from 60600885 to 60600885.
No formulae were updated.

Homebrew 会通过 git 完成同步。

总结起来,Homebrew 是一套很有新意的软件包管理工具,虽然它的更新及时程度还有待考验,但至少在目前还是比较令我满意的解决方案。

Mac 下使用常见主机

手头在玩的几个主机和掌机包括 Wii, PS2, Xbox 360, PSP 和 NDSL,大部分网上能找到的信息都是 PC 上的使用方法,Mac 下要用的话,偶尔有点地方需要注意的,这里顺便记录一下。

Wii 现在大家一般都软解后用 USB Loader,这个一般无非是把下载下来的游戏镜像文件复制到移动硬盘里的 WBFS 文件系统中,在 Mac 下,[WBFS for MacOS X](http://wakoopa.com/software/wbfs-for-macos-x) 是最方便的工具。偶尔需要提取 Wii 游戏光盘镜像时,[wiiscrubber-ng](http://github.com/jjgod/wiiscrubber-ng/tree/master) 是个不太完全的移植,我略作了一点修改。

PS2 无非是刻盘,这个用系统自带的 Disk Utility 就可以胜任。

Xbox 360 的刻盘稍微无法一点,因为是双层 DVD,有时需要设置分层点,[iBurn360](http://idog.appleguru.org/iBurn360/) 或者 [OSx360](http://osx360.tla.su/osx360/) 都可以设置分层点进行刻录,其中 OSx360 在刻录新的 Wave 3 游戏时,需要去掉“Require Stealth Verification”选项。

PSP 偶尔需要转换 CSO 和 ISO 格式,[Prometeus](http://www.wattks.com/macosx/sony/sony.html) 是一个很好的工具。

NDSL 没什么可说的,使用了烧录卡后就是往 TF 卡里复制游戏而已。

在 Mac 下解决 Wii Sports Resort 不能启动的经历

1. 收到从淘宝购买的 [Wii MotionPlus](http://en.wikipedia.org/wiki/Wii_MotionPlus)
2. 用 [WBFS for MacOS X](http://wakoopa.com/software/wbfs-for-macos-x) 把 [Wii Sports Resort](http://en.wikipedia.org/wiki/Wii_Sports_Resort) 美版 ISO 复制到移动硬盘
3. 打开 Wii,用 [USB Loader GX](http://usbloadergx.koureio.net/) 启动 Wii Sports Resort,蓝屏,Error #002 错误
4. 启用 USB Loader GX 的“防 002 错误”功能,再次启动 Wii Sports Resort,黑屏重启
5. 发现需要从 Wii Sports Resort 的光盘镜像里提取一个文件放到 SD 卡根目录,但网上没人提供美版的对应文件 (只有日版和欧版的)
6. 发现用来提取文件的 [WiiScrubber](http://gbatemp.net/index.php?download=4838) 只有 Win32 版本
7. 找到 [WiiScrubber-ng](http://www.dcemu.co.uk/vbulletin/showthread.php?t=138935),一个 Unix 移植
8. 下载编译 WiiScrubber-ng 的源代码,发现缺少 key.bin 文件无法执行
9. 下载 MakeKeyBin 的源代码,提取出跨平台部分单独编译,生成 key.bin
10. 运行 wiiscrubber-ng,发现提取文件部分并没有移植
11. [少量修改](http://github.com/jjgod/wiiscrubber-ng/tree/master) wiiscrubber-ng, 加入提取文件功能,获得所需的 `player.dol` 文件
12. 复制获得的文件到 SD 卡中,启用 USB Loader GX 的“Alternate DOL”功能,成功进入 Wii Sports Resort, 看完 MotionPlus 的使用演示
13. 退出游戏,关闭“Alternate DOL”功能,再次启动 Wii Sports Resort,正式开始游戏

dump 混合光盘镜像中的 ISO 9660 分区

Leopard / Snow Leopard 光盘镜像是混合 (hybrid) 分区格式,也就是一个磁盘镜像中,既有 HFS+ 分区,也有 ISO 9660 分区 (Boot Camp 驱动就存放在这个分区上),在 Mac OS X 上只会挂载前者,在 Windows 下只会挂载后者。虽然可以在 Terminal 下手工挂载后者,但也不是很方便,这里记录一个脚本,方便直接把 ISO 9660 分区 dump 为 ISO 文件:

$ cat dump.sh
#!/usr/bin/env bash
DISK=`hdiutil attach -noverify $1 | awk ‘/Apple_partition_scheme/ { print $1 }’`
dd if=$DISK of=$2 bs=512 count=`hdiutil pmap $DISK | awk ‘/WINDOWSSUPPORT/ { print $6 }’`
hdiutil detach $DISK

使用方法:

$ ./dump.sh snowleopard_10a354_userdvd.dmg bc.iso

Packaging for Installation on Mac OS X

在 Mac OS X 下为程序打包该用什么格式?这是个很有趣的问题,首先,显然绝大部分的应用程序都应该:

* 使用手动安装,也就是一个独立的 `.app`,里面通过 `@executable_path/../Frameworks/` 的方式[链接所有依赖的第三方库](http://www.cocoadevcentral.com/articles/000042.php),也就是说,不往系统相关目录里乱放东西。这是 Apple 官方的 Software Delivery Guide 中[推荐的安装方式](http://developer.apple.com/documentation/DeveloperTools/Conceptual/SoftwareDistribution/Introduction/Introduction.html)。
* 支持用 Sparkle 这样的自动更新框架,实际上符合上一点的也就自然可以用 Sparkle 完全自动的完成更新。
* 用 `.zip` 格式分发。为什么不用 `.dmg`?因为 `.dmg` 不容易直接解压,挂载速度比 `.zip` 慢得多,增加拖动的手续,所以我支持这篇 [zip vs dmg](http://0xced.blogspot.com/2006/11/zip-vs-dmg.html) 的观点。

SunPinyin 安装界面

但是如果遇到特殊的情况呢?比如你写的是一个**非得**往系统目录 (`/Library/…`) 安装内容的软件呢?比如我最近参与改进的 [SunPinyin 输入法](http://blogs.sun.com/yongsun/entry/sunpinyin_for_mac_leopard_updated6),就必须打包为 Installer 支持的 `.pkg` 格式安装了,否则很难确保软件被正确的安装。

关于这个打包方式,最重要的一点心得是:千万不要用 Apple 自己提供的 [PackageMaker](http://developer.apple.com/DOCUMENTATION/DeveloperTools/Conceptual/PackageMakerUserGuide/index.html),这恐怕是 Apple 开发的最恐怖的一个开发辅助工具了,因为:

* 它必须用图形界面修改内容
* 它保存的 `.pmdoc` 格式非常混乱,而且居然喜欢把*绝对路径*存入文件中
* 不支持 [Installer Plugins](http://developer.apple.com/samplecode/InstallerPluginSample/listing1.html) 扩展

那替代的工具是什么呢?是一个非常棒的第三方工具: [Iceberg](http://s.sudre.free.fr/Software/Iceberg.html),它能:

* 打包出 Mac OS X 10.2+ 以上都支持的 `.pkg` bundle 格式
* 界面比 PackageMaker 友好得多,而且有非常详尽的文档,也很方便调试
* 支持 Installer Plugin

涉及打包、Sparkle 的 appcast 自动生成,Iceberg 的使用等问题,都可以参考 SunPinyin [目前使用的代码](http://src.opensolaris.org/source/xref/nv-g11n/inputmethod/sunpinyin/ime/wrapper/macos/packaging/)。