使用 gmsh import 从 Python 代码创建可执行文件

问题描述

我正在尝试使用 pyinstaller 将我的 Python 包打包成一个可执行文件。脚本名称叫“run-jointbuilder.py”,包有很多依赖(比如numpy),但重要的是gmsh

当使用 pyinstaller 编译我的代码时,它似乎是成功的,但是当我尝试运行可执行文件时,我收到以下错误

import gmsh # PyInstaller PYZ\
import ctypes.util # PyInstaller PYZ\
import 'ctypes.util' # <pyimod03_importers.FrozenImporter object at 0x000001BD783FC910>\
Traceback (most recent call last):\
  File "PyInstaller\loader\pyiboot01_bootstrap.py",line 144,in __init__
  File "ctypes\__init__.py",line 381,in __init__\
FileNotFoundError: Could not find module 'C:\Users\willber\Anaconda3\Scripts\gmsh' (or one of its dependencies). Try using the full path with constructor Syntax.

然后我收到此错误

__main__.PyInstallerImportError: Failed to load dynlib/dll

'C:\\Users\\willber\\Anaconda3\\Scripts\\gmsh'. Most probably this dynlib/dll was not found when the application was frozen.

[18612] Failed to execute script run-jointbuilder

有没有人尝试编译一些导入 gmsh 包的 Python 代码?我真的很感谢一个示例 .spec 文件,如果是这样的话,可以与 pyinstaller 一起使用!

解决方法

gmsh python 包包装了一堆已编译的库,其中包含您从 python 调用的方法的实现。当您将 gmsh.py 导入脚本时,gmsh 在后台加载这些库,让您可以通过 python 方法访问它们的功能。因此,必须将这些库嵌入到 pyinstaller 输出中,以便您的代码能够像直接通过 Python 解释器运行时那样运行。

pyinstaller 很难始终如一地找到这些库,因为它们的访问方式与普通的 Python 导入不同,它们是使用 cytpes 包加载的。有一些关于 pyinstaller 如何做到这一点的描述in the docs。由于您在运行编译的 python 脚本时看到 dynlib/dll 加载错误,这表明 pyinstaller 在编译期间没有找到 gmsh 库,因此它在可执行文件中丢失。

如果您查看 gmsh.py source,您可以看到 gmsh.py 为 Windows 操作系统加载了一个名为 gmsh-4.9.dll 的 .dll 库。您可以使用 pyinstaller .spec 文件的 binaries 输入将编译器指向 gmsh-4.9.dll

这是一个示例 .spec 文件,它在编译时动态定位 gmsh-4.9.dll,以便它为您的活动环境选择正确的 .dll。您可以通过过滤 gmsh 目录中的所有 *.dll 使其更通用,但为了清晰起见,我已进行硬编码:

# -*- mode: python ; coding: utf-8 -*-
from pathlib import Path
import gmsh

# get the location of the gmsh dll which sits next to the gmsh.py file
libname = 'gmsh-4.9.dll'
libpath = Path(gmsh.__file__).parent / libname
print('Adding {} to binaries'.format(libpath))

block_cipher = None

a = Analysis(['gmsh-test.py'],pathex=['C:\\Users\\user\\dev\\gmsh'],# tell pyinstaller to add the binary to the compiled path
         binaries=[(str(libpath),'.')],datas=[],hiddenimports=[],hookspath=[],runtime_hooks=[],excludes=[],win_no_prefer_redirects=False,win_private_assemblies=False,cipher=block_cipher,noarchive=False)

pyz = PYZ(a.pure,a.zipped_data,cipher=block_cipher)

exe = EXE(pyz,a.scripts,a.binaries,a.zipfiles,a.datas,name='gmsh-test',debug=False,bootloader_ignore_signals=False,strip=False,upx=True,console=True,runtime_tmpdir=None,)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...