pyInstaller改变dll和pyd输出位置

时间:2013-10-25 02:10:05

标签: python pyinstaller

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

2 个答案:

答案 0 :(得分:2)

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

当然会想到将代码sys.path.append("relative/path/to/your/subdirectories")放在主脚本之上。但是,这个代码只会在所有内容加载到位后执行。

根据这个blog,解决方案是使用pyinstaller的运行时钩子。 运行时挂钩告诉引导代码在主脚本运行之前运行任何任意代码 - 在导入任何内容之前。

1。制备

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

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

使用spec文件:

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文件夹,其中包含exe文件,清单,library.zip以及一堆.dll.pyd

4。创建自定义文件夹

现在,您可以在步骤1中创建windll文件夹和lib或您添加到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

要不弄乱您的项目,请创建代码文件夹的副本并将此compile.bat放入其中。然后,只需将main_script.py拖放到compile.bat即可。

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

答案 1 :(得分:-4)

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

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

gcc -o loadmyapp loadmyapp.c

./ loadmyapp