14

我正在尝试使用 pyInstaller 来打包 wxpython 应用程序。我正在寻找“单文件夹”模式的变体,其中 dll 和 pyd 不存储在顶级目录中,而是存储在子目录中(如“dlls”或“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。

4

2 回答 2

4

问题是sys.path不包括您的子目录。因此,当程序运行时,它不知道在哪里寻找您的 .dll 或 .pyd 文件。

当然sys.path.append("relative/path/to/your/subdirectories")会想到将代码放在主脚本之上。但是再一次,此代码仅在所有内容都加载并就位后才执行。

根据这个博客,解决方案是使用 pyinstaller 的运行时钩子。运行时钩子告诉引导代码在你的主脚本运行之前运行你的任意代码——在导入任何东西之前。

1. 准备

创建一个hooker.py将所有自定义路径添加到sys.path. 把它放在某个地方,只做 1 次。

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

3.运行pyinstaller

通常,它会创建dist/your_main_script_name一个文件夹,其中包含文件、清单、library.zip和exe一堆.dll.pyd

4.创建自定义文件夹

现在您可以创建一个windll文件夹和/或您在步骤 1lib中添加的任何内容。然后将所有文件移动到.sys.path.pydlib.dllwindll

运行你的exe,它会崩溃!因此,将以下这些文件移回父文件夹。

  • pythonXX.dll ,其中 XX 是您的 python 版本
  • VCRUNTIME140.dll
  • pywintypesXX.dll,其中 XX 是您的 python 版本,如果包含它

引导程序需要这些文件,因此我们无法在不修改引导程序代码的情况下移动它们。

exe再次运行它应该可以正常工作。


很快你就会厌倦一遍又一遍地重复上述所有内容。这就是我一直在做的事情。

创建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.pycompile.bat.

pause命令使控制台窗口保持打开状态,以便您知道编译是否成功。

于 2018-04-09T13:32:24.130 回答
-4

另一种方式,新的 loadmyapp.c:

#include<stdlib.h>
main(int argc,char *argv[]) {
    execv("yourapp/app.exe", argv);
}

gcc -o loadmyapp loadmyapp.c

./loadmyapp

于 2014-08-31T07:27:00.953 回答