赞
踩
搜了很多pyinstaller的资料,知识点都比较散。
另外像“怎样使用虚拟环境的pyinstaller打包程序”、“pyinstaller指定多个参数的写法”、“带具体信息的参数是怎么写的”这些问题,也没有明确的说法,只能是依葫芦画瓢。
于是花了点功夫,把pyinstaller的官方文档过了一下,并参照官方文档进行了释义、筛选,再结合平时使用pyinstaller的零散知识,最终整理成本篇文章。
本文围绕“使用cmd窗口进行程序打包”展开,另外也详细介绍了出现问题时的常用解决方法。关于pyinstaller的复杂用法(spec、hook),仅简单提及。
安装:cmd窗口,pip install pyinstaller
升级:cmd窗口,pip install --upgrade pyinstaller
cmd窗口的pip install xxx,是安装在默认环境(系统环境)中,若想给虚拟环境安装python包,可参考笔者另一篇文章:windows下python虚拟环境的创建,以及怎么在VS Code中使用虚拟环境
注意:
1、虚拟环境,需要单独安装pyinstaller,不然后续打包时调用的是系统环境的pyinstaller,会把系统环境中所有的包、模块都打包进去(如何使用虚拟环境的pyinstaller打包程序,见“二 3”)。
2、最好把pillow也安装到虚拟环境,图标处理时会用到,如果你的图标文件没问题,那没有pillow也可以。
cd到“要打包的脚本”所在的位置,然后执行①或②:
① pyinstaller [options] script [script…]
- pyinstaller在虚拟环境下需要重命名,使用新名字替代即可(详见“二 3”)
- [options] 为可选参数,后文会进行详细介绍
- script 为脚本名,即需要打包的.py文件
- [script…] 是多脚本打包
② pyinstaller specfile
打包的时候要配置很多参数时,用spec配置会更方便。spec文件告诉pyinstaller如何处理脚本。根据spec文件打包时,在命令行中给出的大部分参数无效,会被忽略并替换为spec文件中的参数。
可以使用“pyi-makespec name.py”,创建一个spec文件。另外使用方法①,也会自动生成spec文件。
对于许多使用pyinstaller的情况,您不需要检查或修改spec文件。通常,仅需将所有所需的信息(例如隐藏导入)作为pyinstaller命令的选项提供,并让其运行即可。在四种情况下,修改spec文件会很有用:
- 当您想要通过应用程序捆绑数据文件时
- 当您想要包括运行时库(.dll或.sofiles),pyinstaller没有从任何其他来源获取的库时
- 当您想要向可执行文件添加Python运行时选项时
- 当您想要创建具有合并常见模块的多程序捆绑包时
pyinstaller会创建一些文件、文件夹:
参数和具体信息之间可以用空格相连,也可以用等号相连,像下面四种写法都是合法的:
常用参数:
-D -F -n -c -w -i -p --log-level -d
注:有的参数需要给出具体信息,这部分会单独加粗标蓝;
脚本名称(.py文件名)
要处理的脚本文件的名称,或一个.spec文件。如果指定了.spec文件,则大多数选项都不需要,不会起作用。
-h,--help
显示此帮助消息并退出。
-v,--version
显示程序版本信息并退出。
--dispath DIR
放置捆绑应用程序的位置(默认值:./dist)。
--workpath WORKPATH
放置所有临时工作文件(.log、.pyz 等)的位置(默认值:./build)。
-y,--noconfirm
替换输出目录(默认值:spec文件路径/dist/spec文件名)而无需确认。
--upx-dir UPX_DIR
UPX实用程序的路径(默认值:搜索执行路径)。
-a,--ascii
不包括Unicode编码支持(默认情况下,如果可用,则包括Unicode编码支持)。
--clean
在构建前清除 PyInstaller 缓存和临时文件。
--log-level LEVEL
生成时控制台消息的详细程度。LEVEL 可以是 TRACE、DEBUG、INFO、WARN、DEPRECATION、ERROR、FATAL(默认值:INFO)。也可以通过 PYI_LOG_LEVEL 环境变量设置并覆盖。
-D,--onedir
创建一个包含可执行文件的单文件夹(默认值)。
-F,--onefile
创建一个可执行文件。
--specpath DIR
生成的 spec 文件存放的文件夹(默认值:脚本所在目录)。
-n NAME,--name NAME
指定打包应用的名称和 spec 文件名称(默认值:第一个脚本的名称)。
--add-data <SRC;DEST or SRC:DEST>
添加非二进制文件或文件夹到可执行文件中。路径分隔符是平台特定的,使用 os.pathsep(在 Windows 上是‘;’,在大多数 Unix 系统上是‘:’)。该选项可以多次使用。
--add-binary <SRC;DEST or SRC:DEST>
添加二进制文件到可执行文件中。有关更多详细信息,请参见--add-data 选项。该选项可以多次使用。
-p DIR,--paths DIR
一个用于搜索导入路径的路径(类似于使用 PYTHONPATH)。可以使用多个路径,用':'分隔,或者多次使用此选项。相当于在 spec 文件中提供一个 pathex 参数。
--hidden-import MODULENAME,--hiddenimport MODULENAME
命名脚本中未显示的导入模块。该选项可以多次使用。
--collect-submodules MODULENAME
收集指定包或模块的所有子模块。该选项可以多次使用。
--collect-data MODULENAME,--collect-datas MODULENAME
收集指定包或模块的所有数据文件。该选项可以多次使用。
--collect-binaries MODULENAME
收集指定包或模块的所有二进制文件。该选项可以多次使用。
--collect-all MODULENAME
收集指定包或模块的所有子模块、数据文件和二进制文件。该选项可以多次使用。
--copy-metadata PACKAGENAME
复制指定包的元数据。该选项可以多次使用。
--recursive-copy-metadata PACKAGENAME
复制指定包和其所有依赖项的元数据。该选项可以多次使用。
--additional-hooks-dir HOOKS_PATH
用于搜索钩子的附加路径。该选项可以多次使用。
--runtime-hook RUNTIME_HOOKS
自定义运行时钩子文件路径。运行时钩子是与可执行文件捆绑的代码,并在任何其他代码或模块之前执行,以设置运行时环境的特殊特性。该选项可以多次使用。
--exclude-module EXCLUDES
忽略的可选模块或包(非路径名而是 Python 名称)。该选项可以多次使用。
--splash IMAGE_FILE
(实验性)向应用程序添加一个带有图像 IMAGE_FILE 的启动画面。在解压缩时,启动画面可以显示进度更新。
-d {all,imports,bootloader,noarchive},--debug{all,imports,bootloader,noarchive}
提供帮助调试冻结应用程序。可多次提供此参数以选择以下多个选项。
--python-option PYTHON_OPTION
指定要在运行时传递给 Python 解释器的命令行选项。当前支持“v”(等效于“--debug imports”)、“u”和“W <warning control>”。
-s,--strip
Apply a symbol-table strip to the executable and shared libs(不建议在 Windows 上使用)。
--noupx
禁止使用 UPX,即使它可用(在 Windows 和 *nix 之间的工作方式不同)。
--upx-exclude FILE
当使用 upx 时,防止对二进制文件进行压缩。如果在压缩期间 upx 损坏某些二进制文件,则通常使用此选项。FILE 是不带路径的二进制文件名。该选项可以多次使用。
-c,--console,--nowindowed
打开控制台窗口,禁止普通窗口。(默认值)
-w,--windowed,--noconsole
打开普通窗口,禁止控制台窗口。
-i <FILE.ico, FILE.icns , Image ,“NONE”>,
--icon < FILE.ico, FILE.icns , Image ,“NONE”>
--disable-windowed-traceback
禁用窗口(noconsole)模式下无处理异常的 traceback 转储(仅适用于 Windows 和 macOS),并显示一条消息,说明禁用了此功能。
--version-file FILE
将版本资源从FILE加入exe文件中。
-m <FILE或XML>,--manifest <FILE或XML>
把manifest或XML加入到exe文件中。
--no-embed-manifest
生成外部的.exe.manifest文件,而不是将manifest嵌入exe文件中。仅适用于onedir模式; 在onefile模式中,无论这个选项如何,manifest都会被嵌入。
-r RESOURCE,--resource RESOURCE
向Windows可执行文件添加或更新资源。
RESOURCE有一到四个项:FILE[,TYPE[,NAME[,LANGUAGE]]]。
--uac-admin
使用此选项创建一个请求在应用程序启动时提升权限的清单。
--uac-uiaccess
使用此选项可允许提升后的应用程序使用远程桌面。
① 把虚拟环境scripts目录下的pyinstaller.exe改个名字(不然和系统环境的pyinstaller重名,重名则优先使用系统环境的pyinstaller)
② 激活虚拟环境
③ cd到“要打包的.py文件”所在的位置,使用重命名后的pyinstaller进行打包即可
当Analysis步骤运行时,它会产生错误和警告消息。如果“--log-level”选项允许,这些信息将显示在命令行之后。Analysis还将消息放在work-path=目录下名为build/name/warn-name.txt的警告文件中。
当Analysis检测到一个导入并且它命名的模块找不到时,它会创建一条消息。当在包(__init__.py模块)中声明类或函数时,也可能产生消息,并且导入指定了package.name。在这种情况下,分析无法判断name是否应该引用子模块或包。
“未找到模块”消息不被归类为错误,因为通常有很多这样的消息。例如,许多标准模块有条件地为不同的平台导入模块,这些平台可能存在,也可能不存在。
所有" module not found "消息都被写入build/name/ warning -name.txt文件。它们不会显示在标准输出中,因为它们太多了。检查警告文件;通常会有几十个模块找不到,但他们的缺席没有影响。
当你运行打包的应用程序时,它会以ImportError终止,这时就该检查警告文件了。
每次运行时,PyInstaller都会在构建文件夹中写入一个关于依赖关系的交叉引用文件:work-path=目录中的build/name/xref-name.html是一个HTML文件,列出了导入图的全部内容,显示哪些模块被哪些模块导入。你可以在任何浏览器中打开它。找到一个模块名,然后继续单击“imported by”链接,直到找到导致包含该模块的顶级导入。
如果你在pyinstaller命令中指定--log-level=DEBUG, pyinstaller会另外生成一个GraphViz输入文件,表示依赖关系图。文件是build/name/graph-name。在工作路径=目录中的点。你可以使用任意GraphViz命令来处理它,例如,dot,生成导入依赖项的图形化显示。
这些文件非常大,因为即使是最简单的“hello world”,Python程序最终也包括大量的标准模块。由于这个原因,图形文件不是很有用。
PyInstaller有时会通过引发Python异常而终止。在大多数情况下,从异常消息中可以清楚地看出原因,例如“您的系统不受支持”或“Pyinstaller至少需要Python 3.7”。其他的则清楚地指出了应该报告的错误。
然而,其中一个错误可能令人费解:IOError("Python库未找到!")PyInstaller需要捆绑Python库,它是Python解释器的主要部分,链接为动态加载库。该文件的名称和位置因使用的平台而异。一些Python安装默认不包括动态Python库(可能存在静态链接的库,但不能使用)。您可能需要安装某种类型的开发包。或者,库可能存在,但不在PyInstaller正在搜索的文件夹中。
在不同的操作系统中,PyInstaller查找python库的位置是不同的,但在大多数系统中都会检查/lib和/usr/lib,如果不能将python库放在那里,请尝试在环境变量LD_LIBRARY_PATH (GNU/Linux)或DYLD_LIBRARY_PATH (macOS)中设置正确的路径。
--debug=all选项(及其_choices_)提供了大量诊断信息。这在开发复杂包或应用程序看起来不起作用时很有用,或者仅了解运行时如何工作。
通常,调试进度消息会发送到标准输出。如果在打包Windows应用程序时使用了--windowed选项,则会将它们发送到任何附加的调试器。如果您没有使用调试器(或没有调试器),则可以使用DebugView免费(beer)工具来显示此类消息。它必须在运行打包的应用程序之前启动。
对于--windowed,在macOS应用程序中不会被显示。
考虑为您的生产版本打包而不使用--debug。调试消息需要系统调用并对性能产生影响。
你可以使用--debug=imports构建应用程序,这将向嵌入式Python解释器传递-v(详细导入)标志。这可能非常有用。即使是表面上工作良好的应用程序,也可能会提供信息,以确保它们从装捆包中获得所有导入,而不会泄漏到本地安装的Python中。
Python冗长和警告消息始终进入标准输出,并且在使用--windowed选项时不可见。请记住,不要将其用于您的生产版本。
如果您使用了--windowed选项,则打包应用程序可能无法启动,并显示类似“Failedtoexecute script my_gui”的错误消息。在这种情况下,您需要获取更多详细的输出以了解情况。
这将为您提供阻止应用程序初始化的相关错误,然后您可以继续进行其他调试步骤。
如果您使用--onefile并且它无法运行,您的程序将出现错误,例如:
- ./hello: errorwhile loading shared libraries: libz.so.1:
- failed to map segmentfrom sharedobject: Operationnotpermitted
这可能是由/tmp目录的错误权限引起的(例如,文件系统使用noexec标志进行挂载)。
解决此问题的简单方法是,在环境变量TMPDIR中设置一个在没有noexec标志挂载的文件系统中的目录的路径,例如:export TMPDIR=/var/tmp/
如果分析识别到需要模块却找不到该模块,则通常是因为脚本正在操作sys.path。在这种情况下,最简单的方法是使用--paths选项列出脚本可能搜索导入的所有其他位置:
- --paths=/path/to/thisdir \
-
- --paths=/path/to/otherdir
这些路径将在spec文件中以pathex参数的形式被记录下来。它们将被添加到当前分析期间的sys.path中。
如果分析认为已找到所有导入项,但应用程序却以导入错误失败,则问题是隐藏导入项;也就是说,在分析阶段不可见的导入项。
当代码使用__import__(),importlib.import_module()或可能使用exec()或eval()时,可能会出现隐藏导入项。当扩展模块使用Python/C API执行导入时,也可能会出现此情况。发生这种情况时,分析不能检测到任何东西。没有警告,只有运行时的ImportError。
要找到这些隐藏的导入项,请使用--debug=imports标志构建应用程序并运行它。
知道需要哪些模块之后,您可以使用--hidden-import命令选项,或编辑spec文件,或使用钩子文件(请参见_理解PyInstaller Hooks_以下),将所需的模块添加到包中。
Python允许脚本通过__path__机制扩展用于导入的搜索路径。通常,导入模块的__path__只有一个条目,即找到__init__.py的目录。但是__init__.py可以自由地扩展它的__path__来包含其他目录。例如:win32com.shell。Shell模块实际解析为win32com/win32comext/ Shell / Shell .pyd。这是因为win32com/__init__.py附加了…/win32comext到它的__path__。
因为导入模块的__init__.py在分析过程中并没有实际执行,所以PyInstaller看不到它对__path__所做的更改。我们使用与隐藏导入相同的钩子机制修复了这个问题,并添加了一些额外的逻辑。
注意,以这种方式钩住的__path__操作只适用于Analysis。在运行时,所有的导入都被拦截并从bundle中得到满足。win32com。Shell的解析方式与win32com相同。还有win32com。__path__对../win32comext一无所知。
偶尔,这还不够。
更奇怪的情况可以通过运行时挂钩来容纳。这些是操作环境的小脚本,使您的脚本运行之前提供附加的顶级代码。
有两种提供运行时挂钩的方法。您可以使用选项--runtime-hook= path-to-script_来命名它们。
其次,一些运行时挂钩是提供的。在分析结束时,由分析阶段产生的模块列表中的名称在PyInstaller安装文件夹中查找loader/rthooks.dat。这个文本文件是一个Python字典的字符串表示形式。键是模块名称,值是挂接脚本路径名的列表。如果有匹配,这些脚本将被包含在捆绑应用程序中,并在启动您的主脚本之前调用。
使用选项命名的挂钩按给定顺序执行,并在任何安装的运行时挂钩之前执行。如果你指定 --runtime-hook=file1.py --runtime-hook=file2.py,那么运行时的执行顺序将是:
以这种方式调用的挂钩虽然需要小心其导入内容,但几乎可以做任何事情。编写运行时钩子的一个原因是覆盖一些模块的某些函数或变量。 Django运行时钩子的一个很好的例子(请参阅PyInstaller文件夹中的loader/rthooks/pyi_rth_django.py)。 Django动态地导入一些模块,并寻找一些.py文件。但是,在单个文件捆绑中,.py文件不可用。我们需要以返回值列表的方式重写函数django.core.management.find_commands。运行时挂钩如下所示:
- import django.core.management
-
- def_find_commands(_):
-
- return """cleanup shell runfcgi runserver""".split()
-
- django.core.management.find_commands = _find_commands
如果您有某种原因认为在PyInstaller中发现了一个错误,则可以尝试下载最新的开发版本。这个版本可能具有尚未在PyPI中的修复或功能。您可以从PyInstaller下载页面下载最新的稳定版本和最新的开发版本。
你也可以直接使用pip安装PyInstaller的最新版本:
pip install https://github.com/pyinstaller/pyinstaller/archive/develop.zip
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。