pyInstaller cambiando la ubicación de salida de dll y pyd

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.

1. Preparación

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 

2. Dile a pyinstaller que incluya el hooker.py

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

3. Ejecutar pyinstaller

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

4. Crea carpetas personalizadas

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.

  • pythonXX.dll, donde XX es su versión de python
  • VCRUNTIME140.dll
  • pywintypesXX.dll, donde XX es su versión de python y si está incluida

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