PyInstaller-Perfect-Build-Method
PyInstaller-Perfect-Build-Method copied to clipboard
分享 pyInstaller 6.3.0 打包成功经验
1. 整体思路(start_tcp_server_19099.py 是主启动文件)
cd E:\gitee-procjet\iot-automatic-pet-feeding\python\wxhelper\py\release
1. 进入项目目录,执行 pyi-makespec .\start_tcp_server_19099.py
- 此处会生成 spec 文件,我们后续将进行修改
- 【注意】不要加 -F,表示所有依赖都将拷贝到 dist 目录下
2. 参考我的 start_tcp_server_19099.spec 文件
1. 设置 exe 程序名称
2. 主动脚本拷贝配置文件目录
3. 指定需要手动添加依赖库的文件
- 解决打包成功,但执行 exe 失败问题,例如:FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\liush\\AppData\\Local\\Temp\\_MEI214962\\borax\\calendars\\FestivalData.csv'
4. 声明项目内所有 python 脚本
3. 执行打包脚本 pyinstaller .\start_tcp_server_19099.spec
- 中途可能要输入 y
4. 启动程序 .\dist\start_tcp_server_19099\start_tcp_server_19099.exe
2. 分享 spec 文件
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.building.api import PYZ, EXE, COLLECT
from PyInstaller.building.build_main import Analysis
import shutil
import os
# 1. 设置 exe 程序名称
exeName = "start_tcp_server_19099"
# 2. 主动脚本拷贝配置文件目录
# (因为 datas 参数会将资源文件都拷贝到 dist/_internal/ 路径下,有些程序的搜索路径会不行,所以此处进行脚本拷贝)
copyDirectory_list = [
("config_file", 'dist\\start_tcp_server_19099\\config_file')
]
# 3. 指定需要手动添加依赖库的文件
# (第三方库含有静态文件,需要在下面目录,编写 hook-xxxx.py 主动添加依赖)
hookspath = "PyInstaller-hookspath\\"
if not os.path.exists(hookspath):
# 判断是否存在文件夹如果不存在则创建为文件夹
os.makedirs(hookspath)
# 导入模块名称
importModuleName_list = [
"borax"
]
# 生成文件,这里是固定模板的,所以直接代码生成
for moduleName in importModuleName_list:
fileName = hookspath + "hook-" + moduleName + ".py"
if os.path.exists(fileName):
print(fileName, " 文件已存在,不重新创建")
continue
# 追加写入内容到文件
with open(fileName, "a") as file:
file.write("from PyInstaller.utils.hooks import collect_data_files\n")
file.write("datas = collect_data_files('" + moduleName + "')\n")
print("已生成 ", fileName, " 文件!")
# ==================新建 a 变量,分析脚本============================
# 4. 声明项目内所有 python 脚本
a = Analysis(
[
'config.py',
'daily_schedule_task.py',
'file_db.py',
'start_tcp_server_19099.py',
'type_1_handler.py',
'type_3_handler.py',
'type_43_handler.py',
'type_49_handler.py',
'custom_http/__init__.py',
'custom_http/http_ali_ai.py',
'custom_http/http_aliCloudDiskSearch_api.py',
'custom_http/http_chatgpt_ai.py',
'custom_http/http_eastmoney_api.py',
'custom_http/http_gaode_weather_api.py',
'custom_http/http_iflytek_ai.py',
'custom_http/http_iflytek_translation.py',
'custom_http/http_iot_api.py',
'custom_http/http_qqMusicDownload_api.py',
'custom_http/http_wxHelper_client.py',
'custom_http/util_borax.py',
'custom_http/util_cnlunar.py',
'custom_http/util_dailyMusicXmlParse.py',
'custom_http/util_efinance.py'
],
pathex=[],
binaries=[],
datas=[
# 写明所有需要拷贝的静态资源文件(来源路径,目标路的地址)
# 注意会全部拷贝到 dist/_internal 路径下所以项目中相对路径可能会有问题
# 所以建议使用上面的 shutil.copytree 进行脚本拷贝
# ('config_file\\*', 'config_file')
],
hiddenimports=[],
# 针对第三方库,内部含有文件的需要手动添加
hookspath=[hookspath],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
)
# ========================为 a 生成 exe =========================
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name=exeName,
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name=exeName,
)
# 放到最后,在生成玩 exe 后再执行
for directory in copyDirectory_list:
src = directory[0]
target = directory[1]
if os.path.exists(target):
print("目标路径已存在,先执行删除 ", target)
shutil.rmtree(target)
# 拷贝目录
shutil.copytree(src, target)
print("成功拷贝路径 ", src, " 到 ", target)
3. 最终效果
4. 可以写个 bat 脚本,用于自动编译
rem 当遇到确认的时候,自动输入 y
echo y |pyinstaller .\start_tcp_server_19099.spec
6.0 进化了很多,主要是所有的打包文件最后都会进入 _internal 文件,有些路径也会改变到 _internal 里。只是我懒得更新文章了。你可以参考下这个我打包脚本:https://github.com/HaujetZhao/CapsWriter-Offline/blob/master/build.spec