Estoy tratando de usar pyInstaller para empaquetar una aplicación wxpython. Busco una variación del modo de “una carpeta” en el que las dlls y pyds no se almacenan en el directorio de nivel superior sino en un subdirectorio (como “dlls” o “libs”).
Este es el archivo de especificaciones actualmente:
# -*- 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" )
Esto permite colocar los dlls (no los pyds) en una carpeta lib, sin embargo, parece hacer esto después de vincularlos, por lo que el progtwig no se inicia porque no puede encontrar los dll esperados.
El problema es que sys.path
no incluye sus subdirectorios. Entonces, cuando el progtwig se ejecuta, no sabe dónde buscar sus archivos .dll o .pyd.
Por supuesto, se le sys.path.append("relative/path/to/your/subdirectories")
poner el código sys.path.append("relative/path/to/your/subdirectories")
sobre su script principal. Pero una vez más, este código solo se ejecuta después de que todo está cargado y en su lugar.
Según este blog , la solución está utilizando el gancho de tiempo de ejecución de pyinstaller. Runtime hook le dice al código de arranque que ejecute cualquiera de su código arbitrario antes de que se ejecute su script principal, antes de importar cualquier cosa.
Cree un hooker.py
que agregará todas sus rutas personalizadas a sys.path
. Ponlo en algún lugar y hazlo solo una vez.
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
con el archivo de especificaciones:
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 )
o con línea de comando:
pyinstaller --runtime-hook="absolute/path/to/hooker.py" the_rest_parameters
Como de costumbre, creará la carpeta dist/your_main_script_name
que contiene el archivo exe
, manifest, library.zip y un montón de .dll
y .pyd
Ahora puede crear una carpeta de windll
y lib
o cualquier cosa que agregue a sys.path
en el paso 1. Luego mueva todos los archivos .pyd
a lib
y todos los archivos .dll
a windll
.
Ejecute su exe
y se estrellará! Así que retrocede estos archivos de abajo a la carpeta principal.
Estos archivos son necesarios para el progtwig de arranque, por lo que no podemos moverlos sin modificar el código del progtwig de arranque.
Ejecute el exe
nuevo y debería funcionar normalmente.
Pronto te aburrirás de repetir todo lo anterior una y otra vez. Así que aquí está lo que he estado haciendo.
Crea compiler.bat
con el contenido similar a:
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
Para no desordenar su proyecto, cree una copia de su carpeta de código y coloque este compile.bat
dentro. Luego, simplemente arrastre y suelte su main_script.py
a compile.bat
.
El comando de pause
mantiene abiertas las ventanas de la consola para que sepa si la comstackción se realizó correctamente o no.
De otra manera, nuevo loadmyapp.c:
#include main(int argc,char *argv[]) { execv("yourapp/app.exe", argv); }
gcc -o loadmyapp loadmyapp.c
./loadmyapp