问题描述
我正在尝试使用 pyInstaller 来打包 wxpython 应用程序.我正在寻找一种单文件夹"模式的变体,其中 dll 和 pyd 不存储在顶级目录中,而是存储在子目录中(如dlls"或libs").
I'm trying to use pyInstaller to package a wxpython application. I looking for a variation of the "one-folder" mode whereby the dlls and pyds are not stored in the top-level directory but in a subdirectory instead (like "dlls" or "libs").
这是当前的规范文件:
# -*- mode: python -*-
import os
a = Analysis\
(
["..\\job_scraper\\load_gui.py"],
pathex = ["C:\\Users\\Administrator\\Documents\\Projects\\python\\PyInstaller\\load_gui"],
hiddenimports = [],
hookspath = None,
runtime_hooks = None
)
a_binaries = []
for (name, path, data_type) in a.binaries:
(non_ext, ext) = os.path.splitext(name)
if(ext in [".pyd", ".dll"]):
a_binaries.append((os.path.join("libs", name), path, data_type))
else:
a_binaries.append((name, path, data_type))
a.binaries = TOC(a_binaries)
pyz = PYZ(a.pure)
exe = EXE\
(
pyz,
a.scripts,
exclude_binaries = True,
name = "load_gui.exe",
debug = False,
strip = None,
upx = True,
console = False
)
coll = COLLECT\
(
exe,
a.binaries,
a.zipfiles,
a.datas,
[("control.csv", "..\\job_scraper\\control.csv", "DATA")],
strip = None,
upx = True,
name = "load_gui"
)
这样做是为了将 dll(而不是 pyds)放入 lib 文件夹,但是它似乎是在链接后执行此操作,因此程序无法启动,因为它找不到预期的 dll.
This does to put the dlls (not the pyds) into a lib folder, however it seems to do this after linking and so the program fails to launch because it can't find the expected dlls.
推荐答案
问题在于 sys.path
不包含您的子目录.所以当程序运行时,它不知道去哪里寻找你的 .dll 或 .pyd 文件.
The problem is that the sys.path
doesn't include your subdirectories. So when the program runs, it doesn't know where to look for your .dll or .pyd files.
当然会想到将代码 sys.path.append("relative/path/to/your/subdirectories")
放在主脚本之上.但同样,此代码仅在所有内容加载并就位后才执行.
Of course putting the code sys.path.append("relative/path/to/your/subdirectories")
on top of your main script would come to mind. But one again, this code is only executed after everything is loaded and in place.
根据这个博客,解决方法正在使用 pyinstaller 的运行时钩子.运行时钩子告诉引导代码在主脚本运行之前运行任意代码 - 在导入任何内容之前.
According to this blog, the solution is using the runtime hook of pyinstaller.Runtime hook tells the bootstrapping code to run any of your arbitrary code before your main script runs - before importing any stuff.
创建一个 hooker.py
,它将把你所有的自定义路径添加到 sys.path
.把它放在某个地方,只做 1 次.
Create a hooker.py
which will add all your custom paths to sys.path
. Put it somewhere and do it 1 time only.
import sys
import os
sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), "lib")) # for pyd
sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), "windll")) # for dll
2.告诉 pyinstaller 包含 hooker.py
带有规范文件:
a = Analysis\
(
["..\\job_scraper\\load_gui.py"],
pathex = ["C:\\Users\\Administrator\\Documents\\Projects\\python\\PyInstaller\\load_gui"],
hiddenimports = [],
hookspath = None,
runtime_hooks = "absolute/path/to/hooker.py" # <----- add it here
)
或使用命令行:
pyinstaller --runtime-hook="absolute/path/to/hooker.py" the_rest_parameters
通常,它会创建dist/your_main_script_name
文件夹,其中包含exe
文件、manifest、library.zip和一堆.dll
code> 和 .pyd
As usually, it will create dist/your_main_script_name
folder which contains the exe
file, manifest, library.zip, and a bunch of .dll
and .pyd
现在您可以创建一个 windll
文件夹和 lib
或您在步骤 1 中添加到 sys.path
的任何内容.然后移动所有 .pyd
文件到 lib
,所有 .dll
文件到 windll
.
Now you can create a windll
folder and lib
or anything that you add to sys.path
at step 1. Then move all .pyd
files to lib
and all .dll
files to windll
.
运行你的exe
,它会崩溃!所以将这些下面的文件移回父文件夹.
Run your exe
and it will crash! So move back these below files to parent folder.
- pythonXX.dll ,其中 XX 是你的 python 版本
- VCRUNTIME140.dll
- pywintypesXX.dll,其中 XX 是您的 python 版本,如果包含它
引导程序需要这些文件,因此我们无法在不修改引导程序代码的情况下移动它们.
These files are needed for the bootstrap so we cannot move them without modifying the bootstrap code.
再次运行exe
,它应该可以正常工作了.
Run the exe
again and it should work normally.
很快你就会厌倦一遍又一遍地重复以上所有内容.所以这就是我一直在做的事情.
Soon you will get bored of repeating all of the above over and over again.So here is what I have been doing.
创建compiler.bat
,内容类似于:
pyinstaller --runtime-hook="absolute/path/to/hooker.py" --onedir --icon path/to/icon ^
--exclude-module=UnNeeded_module_A ^
--exclude-module=UnNeeded_module_B ^
%1
@echo off
for %%F in (%1) do set pyi_output=%%~nxF
set pyi_output=%pyi_output:~0,-3%
mkdir dist\%pyi_output%\windll
mkdir dist\%pyi_output%\lib
move dist\%pyi_output%\*.dll dist\%pyi_output%\windll
move dist\%pyi_output%\*.pyd dist\%pyi_output%\lib
move dist\%pyi_output%\windll\python36.dll dist\%pyi_output%
move dist\%pyi_output%\windll\VCRUNTIME140.dll dist\%pyi_output%
if exist dist\%pyi_output%\windll\pywintypes36.dll (
move dist\%pyi_output%\windll\pywintypes36.dll dist\%pyi_output%
)
pause
为了不弄乱你的项目,创建一个你的代码文件夹的副本并将这个 compile.bat
放在里面.然后,只需将您的 main_script.py
拖放到 compile.bat
.
To not mess up your project, create a copy of your code folder and place this compile.bat
inside. Then, just drag and drop your main_script.py
to compile.bat
.
pause
命令使控制台窗口保持打开状态,以便您知道编译是否成功.
The pause
command keeps the console windows open so that you know if the compilation is successful or not.
这篇关于pyInstaller 更改 dll 和 pyd 输出位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!