背景
同事A需要在一臺只能訪問局域網(wǎng)的mac mini上運行一個python腳本,腳本依賴了一些模塊,有numpy這種三方模塊,也有自己開發(fā)的本地模塊,而mac mini上除了python,什么都沒有安裝。同事B接到求助后三下五除二生成了一個可執(zhí)行文件,堪稱滿分答案。但對于具體細(xì)節(jié),同事B表示很復(fù)雜說不清,于是就有了姑娘的周日趣事。
工具
淺百度一下,迅速定位到工具為PyInstaller, 查到官網(wǎng)上有以下描述,確認(rèn)就是你沒錯!
PyInstaller bundles a Python application and all its dependencies into a single package. The user can run the packaged app without installing a Python interpreter or any modules.
從首頁的描述來看,使用簡直不能再簡單:
安裝工具:
pip install -U pyinstaller
使用工具:
pyinstaller your_program.py
真的這么簡單嗎?
如果同我一樣順手用了import numpy做demo的話,就不是了!
我的腳本tryPyinstaller.py:
import numpy as np
print("6 / 3 = " + str(np.divide(6,3)))
使用工具:
pyinstaller --onefile tryPyinstaller.py
運行生成的應(yīng)用:
(base) leixiaoyues-MacBook-Pro:~ leixiaoyue$ /Users/leixiaoyue/Code/YuePythonScript/tryPyinstaller/dist/tryPyinstaller ; exit;
INTEL MKL ERROR: dlopen(/private/var/folders/fq/qx5csyvd54x6p60p38qvqfpm0000gn/T/_MEI8YGfHc/libmkl_intel_thread.1.dylib, 0x0009): Library not loaded: '@rpath/libiomp5.dylib'
Referenced from: '/private/var/folders/fq/qx5csyvd54x6p60p38qvqfpm0000gn/T/_MEI8YGfHc/libmkl_intel_thread.1.dylib'
Reason: tried: '/usr/local/lib/libiomp5.dylib' (no such file), '/usr/lib/libiomp5.dylib' (no such file).
Intel MKL FATAL ERROR: Cannot load libmkl_intel_thread.1.dylib.
logout
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.
為什么會缺庫???!這件事情還不得而知,選擇暫時放過自己,先來解決問題現(xiàn)象!這就引出了PyInstaller一個非常重要的機(jī)制——鉤子!
鉤子??萬歲
淺百度一下,又在官網(wǎng)中看到有“Understanding PyInstaller Hooks”一節(jié),大概感到有救。鉤子是編程中用來實現(xiàn)可拓展性的一個常用技巧。鉤子已在!缺什么鉤什么!
接下來看看PyInstsaller里具體怎么鉤,抄 StackOverflow上的作業(yè)。
- 新建鉤子腳本hook-numpy.py,把要鉤的庫列在里面
from PyInstaller import log as logging
from PyInstaller import compat
from os import listdir
mkldir = compat.base_prefix + "/lib"
logger = logging.getLogger(name)
logger.info("MKL installed as part of numpy, importing that!")
binaries = [(mkldir + "/" + mkl, '.') for mkl in listdir(mkldir) if mkl.startswith('libmkl_')]
- 通過參數(shù)additional-hooks-dir告訴程序新增鉤子的路徑,參數(shù)clean是為了清除之前運行的緩存,多個香爐多個鬼,清一清沒啥不好
pyinstaller --additional-hooks-dir=. --clean --onefile tryPyinstaller.py
雙擊應(yīng)用,成功運行
Last login: Sun Apr 9 15:02:31 on ttys008
The default interactive shell is now zsh.
To update your account to use zsh, please runchsh -s /bin/zsh.
For more details, please visit https://support.apple.com/kb/HT208050.
/Users/leixiaoyue/Code/YuePythonScript/tryPyinstaller/dist/tryPyinstaller/tryPyinstaller ; exit;
(base) leixiaoyues-MacBook-Pro:~ leixiaoyue$ /Users/leixiaoyue/Code/YuePythonScript/tryPyinstaller/dist/tryPyinstaller/tryPyinstaller ; exit;
6 / 3 = 2.0
logout
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.
[Process completed]
運行是真的慢……
工程師的工具箱里又多了什么
首先,是把所有依賴統(tǒng)統(tǒng)打包成應(yīng)用的思路。這在幫完全外行的朋友寫軟件時超級有用,這樣簡單直接的思路以前還真沒想到過。
再就是又一次領(lǐng)略了??提供可拓展性的魅力。
解決舊問題帶來新問題
為什么打包出來的程序運行那么那么慢……
為什么打包numpy會缺libmkl_xxx一系列Math Kernal Library?
Hook 真的是個好的命名嗎?韓語??(hooking),日語フック(hook),直接用音譯,大陸譯作“鉤子”,不能在亞洲圈獲得統(tǒng)一的翻譯,往往是命名不夠準(zhǔn)確的一個表現(xiàn)……