pyinstaller是一个python代码打包工具,打包后,可以避免代码暴露,也可以避免函数库的依赖。
0.推荐的最佳实践:
01. 将build,dist目录另行放置:
这样可以防止代码备份时备份进无关的文件
#将dist目录设置到指定位置
pyinstaller --distpath=../../../build/cfg_daemon/dist
#将build目录设置到指定位置
pyinstaller --workpath=../../../build/cfg_daemon/build
02. 添加额外的模块目录(配置,脚本,数据)
#cfg:cfg 前一个cfg是打包时当前目录./cfg 后一个是打包后位于dist目录./_internel/cfg目录
--add-data cfg:cfg
--add-data ext_ep:ext_ep
注意,最终的./cfg如果与其他模块公用,可以通过设置ln -s的符号连接来实现。
03.去除特定目录和文件
#去除整个目录
--exclude=trash
#去除单个文件,注意它们的语法不同。
--exclude-module north_bridge/named_pipe_client.py
#最终的实际打包的文件和配置在:*.spec文件中可以看到:
#下面是.spec中实际包含的文件,数据配置文件:
a = Analysis(
['daemon_mqtt_message.py', 'sub/appConfig.py', 'sub/mqtt_message_check.py', 'sub/mqtt_message_reply.py', 'sub/mqtt_message_save.py', 'sub/mysql_con
fig_retrieve.py', 'sub/update_local_json_config_from_mqtt_message.py', 'common/gpLog.py'],
pathex=[],
binaries=[],
datas=[('cfg', 'cfg'), ('ext_ep', 'ext_ep'), ('sub/dumb', 'sub/dumb')],
hiddenimports=['mysql_config_retrieve'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
#
04. 对于代码中包含自测模块和处理
pyinstaller 会扫描.py源码中所有的:
if __name__ == "__main__":
test_gp_calc_envelope()
所以,如果你的源码中,单元测试代码和源码是混在一起的,这个时候就需要避免把这类文件打包进去。
#Step1:pyinstaller打包前不希望打包的模块测试文件改名:
mv math_tools/gp_calc_envelope_test.py math_tools/gp_calc_envelope_test.py.hide
mv math_tools/gp_xjtu_tools_test.py math_tools/gp_xjtu_tools_test.py.hide
mv vibration_calc_test.py vibration_calc_test.py.hide
mv sourth_bridge/gp_xjtu_data_test.py sourth_bridge/gp_xjtu_data_test.py.hide
#Step2:执行pyinstaller
yes | pyinstaller vibration_calc.py math_tools/*.py error_detect/*.py north_bridge/*.py --add-data cfg:cfg --argv-emulation --distpath ../../../../build/py/dist --workpath ../../../../build/py/build --exclude-module vibration_calc_test.py --exclude-module math_tools/gp_calc_envelope_test.py --exclude-module math_tools/gp_xjtu_tools_test.py --exclude=trash --exclude-module north_bridge/named_pipe_client.py --exclude-module north_bridge/named_pipe_src.py --exclude-module sourth_bridge/gp_xjtu_data_test.py
#Step3:不希望打包的文件名字改回
mv math_tools/gp_calc_envelope_test.py.hide math_tools/gp_calc_envelope_test.py
mv math_tools/gp_xjtu_tools_test.py.hide math_tools/gp_xjtu_tools_test.py
mv vibration_calc_test.py.hide vibration_calc_test.py
mv sourth_bridge/gp_xjtu_data_test.py sourth_bridge/gp_xjtu_data_test.py.hide
#Step4:最终二进制发行包的打包:
cd ../../../../build/py/dist/vibration_calc
tar czvf ../../vibration_calc.tar.gz *
05. 只适应调用.py或pyinstaller打包后的文件的自适应处理:
注意execlp的前两个参数都是可执行文件,要重复两遍。
void InvokePythonModule(const char *param)
{
static int isInvokePython = -1;
struct stat buffer;
const char* python_filename = "./py/vibration_calc.py";
const char* pyinstaller_filename = "./py/vibration_calc";
if (stat(python_filename, &buffer) == 0) {
//printf("文件存在\n");
isInvokePython = 1;
}
else {
isInvokePython = 0;
}
switch (isInvokePython)
{
case 0:
execlp(pyinstaller_filename, pyinstaller_filename, param, NULL);
break;
case 1:
execlp("python3", "python3", python_filename, param, NULL);
break;
default:
printf("py_calc module is missing"); fflush(stdout);
calcOverlapped->ret = GPCALC_RET_DONE;
calcOverlapped->errorCode = GPCALC_CALC_MODULE_MISSING;
exit(0);
//break;
}
}
1.案例:python已经启用了多个实例,导致打包时失败
yes | rm -rf dist
yes | pyinstaller daemon_mqtt_message.py sub/*.py common/*.py --add-data cfg:cfg --add-data ext_ep:ext_ep --hidden-import mysql_config_retrieve
tar czvf daemon_mqtt_message.tar.gz -C ./dist/daemon_mqtt_message --strip-components=1 .
cd temp
tar zxvf ../daemon_mqtt_message.tar.gz222 INFO: PyInstaller: 4.10
222 INFO: Python: 3.6.8
223 INFO: Platform: Linux-3.10.0-1160.el7.x86_64-x86_64-with-centos-7.9.2009-Core
293 INFO: wrote /home/hxjd/src/src_shake/cfg_daemon/daemon_mqtt_message.spec
296 INFO: UPX is not available.
298 INFO: Extending PYTHONPATH with paths
['/home/hxjd/src/src_shake/cfg_daemon',
'/home/hxjd/src/src_shake/cfg_daemon/sub',
'/home/hxjd/src/src_shake/cfg_daemon/sub',
'/home/hxjd/src/src_shake/cfg_daemon/sub',
'/home/hxjd/src/src_shake/cfg_daemon/sub',
'/home/hxjd/src/src_shake/cfg_daemon/sub',
'/home/hxjd/src/src_shake/cfg_daemon/sub',
'/home/hxjd/src/src_shake/cfg_daemon/common']
1312 INFO: checking Analysis
1315 INFO: Appending 'datas' from .spec
1316 INFO: checking PYZ
1317 INFO: checking PKG
1317 INFO: Bootloader /usr/local/lib/python3.6/site-packages/PyInstaller/bootloader/Linux-64bit-intel/run
1318 INFO: checking EXE
1318 INFO: checking COLLECT
1318 INFO: Building COLLECT COLLECT-00.toc
1342 INFO: Building COLLECT COLLECT-00.toc completed successfully.
./
./daemon_mqtt_message
./libpython3.6m.so.1.0
./lib-dynload/
./lib-dynload/_struct.cpython-36m-x86_64-linux-gnu.so
./lib-dynload/_opcode.cpython-36m-x86_64-linux-gnu.so[root@localhost temp]# ./daemon_mqtt_message
Traceback (most recent call last):
File "daemon_mqtt_message.py", line 11, in <module>
from sub import mqtt_message_check
File "PyInstaller/loader/pyimod03_importers.py", line 495, in exec_module
File "mqtt_message_check.py", line 16, in <module>
File "PyInstaller/loader/pyimod03_importers.py", line 495, in exec_module
File "mysql_config_retrieve.py", line 11, in <module>
ModuleNotFoundError: No module named 'mysql'
[40081] Failed to execute script 'daemon_mqtt_message' due to unhandled exception!
1.1 正确的应对:
1.1.1 为当前应用程序创建python虚拟执行环境
python3.11 -m venv gp_python_env
1.1.2 激活这个执行环境
source gp_python_env/bin/activate
(gp_python_env) [root@localhost cfg_daemon]#
1.1.3 之后安装组件和打包都在这个虚拟环境里进行
gp_poython_env/bin/pip install....