前一阵子答应帮了个忙,开发一套仪器软件。写了一天之后写好了,交付源码,但是对方几乎没有懂计算机的人,所以要求我把所有东西再打包成一个 windows 上的可执行文件。这几天刚好回来,稍微有一点空闲时间,随手打了一下包。不过,因为很多年没有用windows,实在是很不熟悉,打包windows软件的时候遇到了很多奇怪的小坑,稍微总结一些,方便自己以后参考。
PyInstaller
PyInstaller在多数情况下都还算表现良好,不过很难正确处理比较复杂的依赖。最好全部用–hidden-import手动指定。官方手册里甚至有如果炸了怎么办的章节,所以debug应该不成问题。
对于类似 Scrapy, Bokeh 这种自己提供命令行工具启动的,直接写 spec 文件会不太方便。因此,可以自己写一个 init.py 文件来 invoke 特定的组件,作为入口。
PyInstaller自带的 –add-data 参数每次只接受一个文件,如果有一个文件夹的内容要加入,可以 patch 一下 .spec 文件,这是从stackoverflow上抄来的一段代码:
##### include mydir in distribution #######
def extra_datas(mydir):
def rec_glob(p, files):
import os
import glob
for d in glob.glob(p):
if os.path.isfile(d):
files.append(d)
rec_glob("%s/*" % d, files)
files = []
rec_glob("%s/*" % mydir, files)
extra_datas = []
for f in files:
extra_datas.append((f, f, 'DATA'))
return extra_datas
###########################################
# append the 'data' dir
a.datas += extra_datas('data')
在使用PyInstaller的时候,要注意所有package都需要正确处理 frozen scripts 的情况。比如说如果我们想使用 jinja2 模板,为了获得正确的 Jinja2 环境,应该先做判断 sys.frozen 是否为 True,如果是,应该用 jinja2.Enviroment(jinja2.FileSystemLoader())。如果不作处理而使用 PackageLoader,会导致打包之后无法加载模板
虚拟机和windows
因为为了贪图方便,我打包程序用的是之前弄的那个测试病毒用的沙盒 windows,这是个很蛋疼的决定。。因为所有文件交换都要经过过渡仓。不过好处是各种环境都很干净。
Windows总是会试图不停地下载补丁,这对于关机就清空所有东西的沙盒当然是没卵意义的,而且还关不掉。。搞得很耗电,不过幸好沙盒的网络有审计,最后只能把微软的域名都屏蔽了。
Windows的编码系统非常杂乱,目前还没有发现有效的办法来处理不同语言版本的编码问题。当年玩Galgame的时候好像会用一款叫做 AppLocale 还是什么的远古的软件来模拟locale,后来会用locale emulator之类的软件,都是由于windows混乱的编码造成不同语言版本的windows程序不兼容。英文版的windows似乎是混用 ANSI 和 utf-8,中文版的更加复杂,目前还没有搞清楚。似乎在一些地方用了 gb2312 或者 gb18030,一些地方用了 ANSI,一些地方用了 utf-8。另外,如果在windows安装完之后再更换语言,会导致编码问题更加混乱,结果是完全不可预测的。因此,最好写一个自动化脚本在每个 target windows 语言平台上分别打包该平台的应用。
nodejs
npm 官方源在国内的直连速度非常感人,就几十 MB 的东西总是下到一半卡住。。我又很久没用windows,一时也懒得去设置windows上的fq,所以先试着换成了国内的源。首先试了一下淘宝的镜像
npm config set registry https://registry.npm.taobao.org
没想到国内的源也一样慢。。到50KB/s,就卡住不会往上走了。。看了下淘宝的镜像给的文档,原来是要打开gzip。为了方便,就直接安装cnpm来用就好了。
npm install -g cnpm
windows 没有自带的包管理软件,似乎有人做了一个巧克力还是什么的,不过很松散,上面也没有几个软件。如果使用 yarn,一种办法是下载官网那个 msi 包来安装,另一种办法是用 cyarn
npm install -g cyarn
NSIS
NSIS是一个非常不错的老牌打包软件,全程没有踩坑,点赞。
其它犯傻的问题
- unix下的软链接在windows下是没用的。。不知道怎么才能同步
- cmd.exe> ls
- powershell太高级了,比cmd好用10000倍,但是蓝底看瞎我…这个地方好像讲了怎么换主题,但是太懒了,将就一下就过去了 https://www.petri.com/change-powershell-console-font-and-background-colors
python 软件在 windows 下真正稳定的打包方法
在踩了一大堆 pyinstaller, py2exe 什么什么号称可以打包 python 脚本的软件的坑之后,完全受不了了,遂决定手动打包。
首先 pyinstaller 前面说了,它处理依赖根本就不行,需要手动设置很多东西,可以说比手动打包还要麻烦
py2exe 这个东西就更厉害了,不知道它对简单的 python 脚本打包效果怎么样,不过可以确定的是如果依赖了像是 bokeh, scrapy, django 这种库的话,简直没有一丝希望
所以最后的办法其实很简单,我们直接去 python.org 的下载页面,下载 embedded distribution 版本的 python。。。
当然,这个版本的 python 是没有标准库以外的东西在里面的,所以我们需要手动把依赖加进去,首先在解压之后的目录里创建一个 Lib 文件夹,然后修改 pythonXX._pth (XX是版本号,比如下载的是python3.7, XX=37)文件,这个文件里的东西其实就是这个 embedded 版本 python 的搜索路径,把刚才创建的 Lib 文件夹加入进去。
接下来我们还是和往常一样安装和这个 embedded 版本 python 的版本号完全一样的 python,并且安装依赖。依赖都装完之后,把 C:\\Users\username\AppData\PythonXX\Lib\site-packages 里的所有库都复制到刚才创建的 Lib 文件夹中,再从 C:\\Program Files\PythonXX\Lib\site-packages中把全局安装的库复制过去,这样就得到了一个完全 portable 的 python。
最后,我们再打包这一整个东西即可。这个办法虽然略微麻烦,不过还是很容易自动化,而且理论上来说应该会非常稳定,虽然性能堪忧但是对于桌面端应用来说问题应该不大。