1 需要安装 PyInstaller
C:\Users\30818> pip install pyinstaller
2 将模块py文件打包成pyd 防止反编译暴露源代码 pyd是一个模块插件 可以通过 Import 来引入pyd文件 直接编译会提示二进制读取错误
2-1 安装cython
pip install cython
2-2 在当前目录创建一个 setup.py 的文件 文件名可以随意 写入如下代码
#!/usr/bin/python3.7
# -*- coding: utf-8 -*- from distutils.core import setup
from Cython.Build import cythonizesetup(#name名无实际作用可以随意写name='api_sign', #cythonize()是将Python代码转换成C代码的API 下方替换成需要转换的Py文件ext_modules=cythonize("online_update.py"))
2-3 cmd中运行如下代码进行操作
#需要先切换到文件所所在位置路径
D:\>cd SDM_FILE\web_model\tkinter_library\helloworld
D:\SDM_FILE\web_model\tkinter_library\helloworld> python setup.py build_ext --inplace
2-4 运行时可能会提示错误 unable to find vcvarsall.bat
我是 python 3.7.8 安装 vs2015 或者 2017 都可以 这边安装的是vs2017 安装时一定要勾选 使用C+桌面开发
同时也有可能遇到 setuptools 工具问题
pip uninstall setuptools 然后再 pip install setuptools -upgrade
2-5 运行完成后 再对应目录下 如helloworld/online_update.cp37-win_amd64.pyd 即可查看到 pyd文件
删除build、disk文件夹 将pyd文件移出来重新进行打包即可 py源文件不用删除 因为当同时存在mylib.pyd和mylib.py时,引入优先级是pyd>py,所以不用移除py文件,默认引入时就是pyd
3-1 此时已经可以直接编译了
# -F 生成结果是一个exe文件,所有的第三方依赖、资源和代码均被打包进该exe内
# -w 禁止Windows的命令行窗口。不然双击exe时会打开一个黑乎乎的dos窗口;
# -i 生成的exe文件会带有这个图标,有识别度也更好看
#直接编译
D:\helloworld\online_update> pyinstaller -i animo03.ico -Fw main.py
执行失败错误一 Failed to execute script main
# 再次运行下方代码 运行完成后 会在dist 目录 生成一个目录
# cd进入该目录,用cmd 命令行窗口 执行对应的.exe 就能看到错误了
# 确认错误后根据错误排查pyinstaller -D main.py#如提示icon图标未找到 将图表放入相同文件中即可
3-2 也可以进一步生成spec后再进行编译 进入文件夹目录 将主文件main.py 添加一个图标文件 并生成spec 配置文件
#制作spec文件
C:\Users\30818\Desktop\imgdownload> pyi-makespec --icon animo03_128x128.ico -Fw --hidden-import online_update.pyd main.py#输入对应参数 生成后对应spec文件即已经拥有了对应属性
pyi-makespec 对应参数一览表 | ||
参数名 | 描述 | 说明 |
可选参数 | ||
-h, --help | 显示帮助消息并退出 | 无 |
--log-level LEVEL | 生成时控制台消息中的详细信息量。level 等级可以是TRACE、DEBUG、INFO、WARN、ERROR、CRITICAL之一(默认值:INFO) | 控制编译时pyi打印的信息 也就是默认情况下,不打印TRACE和DEBUG信息 |
-v | 显示版本号 | |
–distpath | 生成文件放在哪里 | 默认:当前目录的dist文件夹内 |
–workpath | 生成过程中的中间文件放在哪里 | 默认:当前目录的build文件夹内 |
-y | 如果dist文件夹内已经存在生成文件,则不询问用户,直接覆盖 | 默认:询问是否覆盖 |
–upx-dir UPX_DIR | 指定upx工具的目录 | 默认:execution path |
-a | 不包含unicode支持 | 默认:尽可能支持unicode |
–clean | 在本次编译开始时,清空上一次编译生成的各种文件 | 默认:不清除 |
生成什么 | ||
-D, --onedir | 结果是一个目录,各种第三方依赖、资源和exe同时存储在该目录 在dist 中可以直接运行生成的exe 调试错误 | |
-F, --onefile | 创建一个文件绑定的可执行文件。 结果是一个exe文件,所有的第三方依赖、资源和代码均被打包进该exe内 | |
--specpath DIR | 指定.spec文件的存储路径 (默认:当前目录) | |
-n NAME, --name NAME | 生成的.exe文件和.spec的文件名 (默认:第一个脚本的基础名 如 main.py 那么名称为 main.spec main.exe) | |
捆绑的内容 搜索的位置 | ||
–add-data | 打包额外资源 | 用法:pyinstaller main.py --add-data=src;dest。windows以;分割,linux以:分割 |
–add-binary | 打包额外的代码 | 用法:同–add-data。与–add-data不同的是,用binary添加的文件,pyi会分析它引用的文件并把它们一同添加进来 |
-p DIR, --paths DIR | 指定额外的import路径,类似于使用PYTHONPATH | 参见PYTHONPATH |
--hidden-import MODULENAME --hiddenimport MODULENAME | 打包额外py库 | pyi在分析过程中,有些import没有正确分析出来,运行时会报import error,这时可以使用该参数 |
--additional-hooks-dir HOOKSPATH | 指定用户的hook目录 | hook用法参见其他,系统hook在PyInstaller\hooks目录下 |
--runtime-hook RUNTIME_HOOKS | 指定用户runtime-hook | 如果设置了此参数,则runtime-hook会在运行main.py之前被运行 |
--exclude-module EXCLUDES | 需要排除的module | pyi会分析出很多相互关联的库,但是某些库对用户来说是没用的,可以用这个参数排除这些库,有助于减少生成文件的大小 |
–key | pyi会存储字节码,指定加密字节码的key | 16位的字符串 |
如何生成 | ||
-d {all,imports,bootloader,noarchive} --debug {all,imports,bootloader,noarchive} | 执行生成的main.exe时,会输出pyi的一些log,有助于查错 | 默认:不输出pyi的log |
-s, --strip | 优化符号表 | 原文明确表示不建议在windows上使用 |
–noupx | 强制不使用upx | 默认:尽可能使用 |
--upx-exclude FILE | 使用时防止压缩二进制文件upx。这个通常在upx在压缩期间损坏某些二进制文件时使用。FILE是没有路径的二进制文件的文件名。此选项可以多次使用 | |
Windows和Mac特有的选项 | ||
-c, --console, --nowindowed | 显示命令行窗口 | 与-w相反,默认含有此参数 |
-w, --windowed, --noconsole | 不显示命令行窗口 | 编写GUI程序时使用此参数有用。 |
-i <FILE.ico or FILE.exe,ID or FILE.icns> --icon <FILE.ico or FILE.exe,ID or FILE.icns> | 为main.exe指定图标 | pyinstaller -i beauty.ico main.py |
Windows特有的选项 | ||
--version-file FILE | 添加版本信息文件 | pyinstaller --version-file ver.txt |
-m <FILE or XML>, --manifest <FILE or XML> | 添加manifest文件 或者 XML 文件到exe | |
-r RESOURCE, --resource RESOURCE | 向Windows可执行文件添加或更新资源。资源是一到四个项目,文件[,类型[,名称[,语言]]]。文件可以是数据文件或exe/dll。对于数据文件,至少必须指定类型和名称。语言默认为0,也可以指定为通配符*以更新给定类型和名称的所有资源。对于exe/dll文件,如果省略类型、名称和语言或将其指定为通配符*,则文件中的所有资源都将添加/更新到最终可执行文件中。此选项可以多次使用。 | |
--uac-admin | 使用此选项将创建一个清单,该清单将在应用程序重新启动时请求提升 | |
--uac-uiaccess | 使用此选项允许提升的应用程序与远程桌面一起工作 | |
很少使用的特殊选项 | ||
--runtime-tmpdir PATH | 在“onefile”模式下从何处提取库和支持文件。如果给定此选项,引导加载程序将忽略运行时操作系统定义的任何临时文件夹位置。“u MEIxxxxxx”文件夹将在此处创建。只有当你知道你在做什么时,请使用这个选项 | 指定运行时的临时目录, 默认:使用系统临时目录 |
--bootloader-ignore-signals | 告诉引导加载程序忽略信号,而不是将它们转发给子进程。在这样的情况下非常有用,例如,一个管理程序进程同时向引导加载程序和子进程发送信号(例如,通过进程组),以避免向子进程发送两次信号 |
4 修改spec配置文件
# -*- mode: python ; coding: utf-8 -*-block_cipher = None#分析文件
#多个文件或者其他文件在这里添加
a = Analysis(['main.py','其他.py'],#生成路径pathex=['C:\\Users\\30818\\Desktop\\imgdownload'],# 非python模块的二进制文件# DLL,动态库,共享对象文件等,PyInstaller将在其中搜索其他 二进制依赖项。# 空的list,把要添加的资源文件以tuple的形式传入,tuple的第一个元素是资源文件的路 # 径,第二个元素是打包后存放资源的文件夹。比如:# ('./bin/pdftotext.exe', 'bin')## 就是把 './bin/pdftotext.exe' 打包后放到bin目录下面。## 如果运行找不到程序 注意# 注意以下情况# 最终的exe文件有可能放在任何目录执行,其当前目录下不会有bin目录下面的资源文件,而 # 是被解压到了临时目录下面,所以程序报错找不到相关文件binaries=[],# 需要应用的文件 如资源图片文件等# 如 需要将readme文件添加到 顶层 datas=[ ('src/README.txt', '.') ],# 需要将资源文件 添加到 资源文件夹 datas=[('Resources','Resources')],# .mp3文件夹中的所有文件/mygame/sfx将被复制到sfx捆绑的应用程序中命名的文件夹中# datas= [ ('/mygame/sfx/*.mp3', 'sfx' ) ],datas=[],# 隐藏导入的文件 文件变为PYD后可能会找不到引入模块# 所以需要在这里进行引入操作hiddenimports=['test'],hookspath=[],runtime_hooks=[],excludes=[],win_no_prefer_redirects=False,win_private_assemblies=False,cipher=block_cipher,noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,cipher=block_cipher)
exe = EXE(pyz,a.scripts,a.binaries,a.zipfiles,a.datas,[],name='main',debug=False,bootloader_ignore_signals=False,strip=False,upx=True,upx_exclude=[],runtime_tmpdir=None,#console=False 关闭程序运行时的命令窗口console=True , icon='图片下载助手.ico')
5 编译生成
--clean 清除pyinstaller的缓存并移除之前建立的临时文件
C:\Users\30818\Desktop\imgdownload> pyinstaller --clean main.spec
6 pyinstall 参数设置一览 可参考 这里
7 pyinstall sepc 参数设置一览表
8 常见错误
打包成功后出现"failed to execute pyi_rth_pkgres” 的问题 |
卸载后尝试重新安装
pip uninstall pyinstaller |
将python程序打包成单文件(使用 -F 参数)后,尝试运行外部文件却提示找不到的问题 |
当你将python程序打包成单文件(使用 -F 参数)后,运行程序,它实际上是先将exe内的资源文件解压到临时文件夹,然后再运行的,所以会导致这种问题
解决方法
在spec 文件中 找到data=[] 添加元素,变成了:
在sepc文件中声明打包后需要注意 打包出来的exe在运行时,它的工作路径和它解压到的路径,是不一样的 通过如下代码查看
诸如open('xxx.txt')这些操作文件的函数,一般首先都是在工作路径查找你所指定的文件的。 所以,当我们直接这样执行已打包的外部文件时,程序会报找不到文件!所以请使用它的解压路径。 下面提供一个函数,可以很方便的获取到解压路径
所以当我们在调用已打包的外部文件时,应该先使用os.chdir()将工作路径改为解压路径: 再进行操作,就不会报文件找不到了
不过要注意的是,如果你要写出文件到程序所在的目录(非解压目录),那么你得把工作目录改回来,否则文件会被写出到解压路径(临时文件夹)。 稍微封装一下就好了:
|
当你使用cython将py文件编译成pyd文件后使用pyinstaller打包,提示找不到模块的问题 |
有一个文件main.py,引入了位于同级目录下的test.py模块
现在我将test.py 编译成pyd文件,生成了: 这个pyd文件名除了我们原本的文件名test,还会带上编译环境的名称,这个环境后缀名我们可以不用管 ,因为python引入模块还是很智能的(会自动引入.pyd文件,因为它的优先级高于.py文件)。 这么智能,但是为什么我用pyinstaller打包时就提示找不到文件?
1、直接添加 在打包时添加
2、使用spec文件 同样的,运行一次pyinstaller打包命令后会生成spec文件,打开它 |