pyInstaller改变dll和pyd输出位置

dil*_*ert 13 python pyinstaller

我正在尝试使用pyInstaller打包一个wxpython应用程序.我正在寻找"单文件夹"模式的变体,其中dll和pyds不存储在顶级目录中,而是存储在子目录中(如"dlls"或"libs").

这是当前的spec文件:

# -*- 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"
)
Run Code Online (Sandbox Code Playgroud)

这样做可以将dll(而不是pyds)放入lib文件夹中,但是它似乎在链接后执行此操作,因此程序无法启动,因为它无法找到预期的dll.

dra*_*fly 6

问题是不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
Run Code Online (Sandbox Code Playgroud)

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
)
Run Code Online (Sandbox Code Playgroud)

或使用命令行:

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文件夹和lib/或在步骤 1 中添加的任何内容。sys.path然后将所有.pyd文件移至lib以及将所有.dll文件移至windll

运行你的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
Run Code Online (Sandbox Code Playgroud)

为了不搞乱您的项目,请创建代码文件夹的副本并将其放入compile.bat其中。然后,只需将其拖放main_script.pycompile.bat.

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


小智 -4

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

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

gcc -o loadmyapp loadmyapp.c

./loadmyapp