最近开发的程序到最后需要打包了,于是写一篇文章记录下来。 可以说这篇是描述如何打包 Python 程序的,但是因为 PyQt 的存在,有几个地方需要注意一下。

Overview

网上教程大多说不能使用 OS X 自带的 Python ,但我测试是没有问题的。我打包使用的系统环境是 Mac OS X 10.10 。 用到的软件:

  • pip Python 包管理器
  • PyInstaller Python 打包工具
  • XCode OS X 开发工具

基本上这些就够了。由于我的项目使用了 PyQt ,所以还需要额外安装 Qt 4.8SIPPyQt4

Qt 4.8

与网上教程不一样的地方是,安装 Qt 4.8 并不需要从源码编译,官方提供了安装包,从 Download Qt | Qt Project 下载 dmg 镜像直接安装就可以(点击『 Show downloads 』),直链是 http://download.qt-project.org/official_releases/qt/4.8/4.8.6/qt-opensource-mac-4.8.6-1.dmg

SIP

要安装 SIP ,前往官方网站 Riverbank | Software | SIP | SIP Download 下载适用于 Mac OS X 的源代码,解压后终端进入文件夹, 执行:

1
2
3
python configure.py
make
sudo make install

PyQt4

要安装 PyQt4 ,前往官方网站 Riverbank | Software | PyQt | PyQt4 Download 下载适用于 Mac OS X 的源代码,解压后终端进入文件夹,执行:

1
2
3
python configure.py
make
sudo make install

其中在第一条命令之后会要求同意许可条款。

pip

安装文档: Installation — pip 1.5.6 documentation 。从官方网站下载 get-pip.py 这个文件,然后终端进入所在文件夹,执行:

1
sudo python get-pip.py

PyInstaller

终端执行:

1
sudo pip install pyinstaller

.app 打包

使用 PyInstaller 将项目源码打包成 .app 文件夹。按照 PyInstaller 的官方文档操作就可以。 我的经验是:先对主文件(程序的入口文件)运行一次打包,命令是:

1
pyinstaller --windowed --onefile --clean --noconfirm main.py 

如果编译过程没有错误的话,找到自动生成的 main.spec 文件(文件名取决于你的程序入口文件名),进行修改。 一个典型的配置文件如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
a = Analysis(['main.py'],
             pathex=['.'],
             hiddenimports=[],
             hookspath=None,
             runtime_hooks=None)
pyz = PYZ(a.pure)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas, 
          name='My App Name',
          debug=False,
          strip=None,
          upx=False,
          console=False)
app = BUNDLE(exe,
             a.datas, 
             name='My App Name.app',
             icon='path/to/icon/file.icns')

当然其中可供配置的选项很多,选项之间也有部分相互关联,详细情况可以看 PyInstaller 的官方文档。 有一个特别好用的技巧是来自 StackOverflow 的文件夹收集函数,有兴趣的可以看 Including a directory using Pyinstaller

修改测试好你的编译配置文件之后,使用以下命令就可以编译了:(这里假定你开发的是桌面软件)

1
pyinstaller --clean --noconfirm --windowed --onefile main.spec

.pkg 安装包

经过前面的步骤,就可以打包出一个 .app 文件夹了。在开发机器上可以直接拖入应用程序文件夹里进行,或者直接运行都没问题。但是有时候就是想制作一个安装器,可以一步一步引导用户进行操作,或者要求用户同意一些条款,要怎么做呢?

当然你可以尝试一下 Bitrock 的产品,一个跨平台的安装向导生成器: InstallBuilder 。但是这是商业软件,如果你不想花这笔钱的话,可以打包成一个 .pkg 文件,这样在 OS X 下有统一的安装界面和标准的安装步骤(当然,也可以通过编译时的配置,选择忽略哪些步骤)。

关于打包成 .pkg 文件的最简单描述在这里: Making OS X Installer Packages like a Pro - Xcode4 Developer ID Mountain Lion ready pkg 。 其中主要有四个步骤:

1
2
3
4
pkgbuild --analyze --root my.app mycomponents.plist
pkgbuild --root my.app --ownership preserve --component-plist mycomponents.plist --install-location "/Applications/my.app" my.pkg
productbuild --synthesize --package my.pkg mydist.xml
productbuild --distribution mydist.xml --package-path . myinstaller.pkg
  1. 第一步是使用 pkgbuild 工具分析你的 .app 文件夹,抽取组件信息到一个 XML 文件中;
  2. 第二步是使用 pkgbuild 工具,通过刚才生成的 XML 文件和其他参数,生成 .pkg 文件;
  3. 然后使用 productbuild 工具从 .pkg 文件中抽取发布信息;
  4. 最后生成安装包。

其中两步中生成的配置文件都是可以进行修改的。而在第二步中可以指定安装位置。

要了解更详细的使用方法,请前往 StackOverflow 查看原答案,或者前往苹果开发者中心查看文档。

本段内容于 2015 年 5 月 3 日添加。

有一个更简单的创建安装包的方式,可以从图形界面进行设置,最终打包成一个 .mpkg 安装包,实际测试发现也不错,地址是:WhiteBox - Packages

.dmg 打包

如果使用过一段时间的 OS X 的话,就会发现实际上大部分应用程序的所谓『安装』过程,都是将 .app 文件夹复制到应用程序文件夹中,也就是 /Applications 路径下。而很多应用程序在发布时都是以一个 .dmg 磁盘镜像的方式提供下载,挂载之后会显示一个界面提示用户拖动到『应用程序』文件夹下。

这是怎么做到的呢?

其实挂载 .dmg 之后显示的窗口就是 Finder ,但这个显示效果是可以定制的。简单地说就是在制作这个 dmg 镜像的时候,指定了将来挂载时要如何显示,包括显示方式、图标大小、图标位置、背景图片等等。

可以参考这里来制作这样一个 dmg 镜像: How do I create a nice-looking DMG for Mac OS X using command-line tools? 。或者可以考虑使用 node-appdmg 来制作这样的镜像。

关于这部分我探究得不是特别深入,因为之后要说的代码签名我没有搞懂。而代码如果没有经过签名的话,通过互联网分发的应用程序是默认禁止运行的(你也不可能要求你的每一个用户都为了使用你的程序而关闭系统安全保护),所以过早研究如何分发是没有意义的。

没有涉及的部分:代码签名

这部分我还暂时没有涉及到,也不太清楚具体的操作。 根据苹果的文档( Code Signing Guide: About Code Signing ),代码签名可以保护你的代码以你发布时的状态安装、运行,而不会被有意或无意地修改;同时 OS X 的一些安全特性,例如应用程序沙盒和家长控制,都需要依赖于代码签名。 之后接触过的话我会再更新文章的。

其他平台的应用程序打包

其他平台的参考要视之后的情况而定。我可能会发布一篇 Windows 平台的打包参考,但是 Linux 就不一定了。