python - 是否有*任何*解决方案来打包使用 cppyy 的 python 应用程序?

标签 python pyinstaller packaging cppyy

在创建我的 python 桌面应用程序的跨平台运行时时,我不是新手。我为我的本科生创建了各种工具,主要使用 pyinstaller、cxfreeze,有时是 fbs,有时是公文包。任何定期执行此操作的人都知道,在使用任意 Python 模块集合时,针对 Linux、Windows 和 macos 需要进行许多怪癖和调整,但到目前为止,我已经设法解决了所有问题。
我有一个 python GUI 应用程序,它使用一个庞大且不断变化的 c++ 库,所以我不能只用 python 重写它。我已经成功地编写了使用 c++ 库的 python 代码,它使用了一个名为 cppyy 的令人惊叹的(并且可能是神奇的)库,它允许您从 python 运行 c++ 代码而不需要任何努力。一切都在 Linux、mac 和 windows 上运行良好,但我无法将其打包到运行时中,并且我已经尝试了上述所有系统。它们都在生成运行时没有问题(即没有错误),但是当您运行它们时它们会失败。本质上,它们都给出了某种关于无法找到 cppyy-backend 的错误(例如,使用 pyinstaller 的 pyinstaller 和 fbs 在您运行二进制文件时会给出此消息):

/home/nogard/Desktop/cppyytest/target/MyApp/cppyy_backend/loader.py:113: UserWarning: No precompiled header available ([Errno 2] No such file or directory: '/home/nogard/Desktop/cppyytest/target/MyApp/cppyy_backend'); this may impact performance.
Traceback (most recent call last):
  File "main.py", line 5, in <module>
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "/home/nogard/Desktop/cppyytest/venv/lib/python3.6/site-packages/PyInstaller/loader/pyimod03_importers.py", line 628, in exec_module
    exec(bytecode, module.__dict__)
  File "cppyy/__init__.py", line 74, in <module>
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "/home/nogard/Desktop/cppyytest/venv/lib/python3.6/site-packages/PyInstaller/loader/pyimod03_importers.py", line 628, in exec_module
    exec(bytecode, module.__dict__)
  File "cppyy/_cpython_cppyy.py", line 20, in <module>
  File "cppyy_backend/loader.py", line 74, in load_cpp_backend
RuntimeError: could not load cppyy_backend library
[11195] Failed to execute script main
我真的很难过。通常,您使用 pip 安装 cppyy,它会安装 cppyy-backend 和其他包。我什至使用 cppyy docs 方法来编译每个依赖项以及 cppyy,但结果是一样的。
我将使用任何有效的构建系统......有人成功了吗?我知道我可以使用 docker,但是我之前尝试过这个,我的许多学生都对 docker 感到害怕,要求他们更改他们的 bios 设置以支持虚拟化所以我想使用一个普通的打包系统来生成某种可运行的二进制文件。
如果您知道如何让 pyinstaller、cxfreeze、fbs 或公文包与 cppyy 一起工作(例如,如果您知道如何处理上述错误),请告诉我。但是,如果你有一个与其他系统打包的 cppyy 应用程序,请告诉我,我会使用那个。
如果您正在寻找一些要运行的代码,我一直在使用这个最少的代码测试打包方法:
import cppyy

print('hello world from python\n')

cppyy.cppexec('''
#include <string>
using namespace std;
string mystring("hello world from c++");
std::cout << mystring << std::endl;
''')

最佳答案

编辑:找出 pyinstaller 钩子(Hook);一旦发布,这一切都应该是全自动的
需要注意的是,我对打包运行时没有任何经验,所以我可能遗漏了一些明显的东西,但我刚刚尝试过 pyinstaller ,并且以下似乎有效。
首先,将上面的脚本保存为 example.py ,然后创建一个规范文件:

$ pyi-makespec example.py
然后,从 cppyy_backend 添加头文件和库如datas (跳过默认添加的 python 文件)。最简单的似乎是从后端获取所有目录,因此更改生成的example.spec通过在顶部添加:
def backend_files():
    import cppyy_backend, glob, os

    all_files = glob.glob(os.path.join(
        os.path.dirname(cppyy_backend.__file__), '*'))

    def datafile(path):
        return path, os.path.join('cppyy_backend', os.path.basename(path))

    return [datafile(filename) for filename in all_files if os.path.isdir(filename)]
并替换空的 datasAnalysis对象:
             datas=backend_files(),
如果您还需要来自 CPyCppyy 的 API header ,然后可以找到这些,例如像这样:
def api_files():
    import cppyy, os

    paths = str(cppyy.gbl.gInterpreter.GetIncludePath()).split('-I')
    for p in paths:
        if not p: continue
       
        apipath = os.path.join(p.strip()[1:-1], 'CPyCppyy')
        if os.path.exists(apipath):
            return [(apipath, os.path.join('include', 'CPyCppyy'))]

    return []
并添加到分析对象:
             datas=backend_files()+api_files(),
但是请注意,Python.h然后还需要存在于将要部署包的系统上。如果需要,Python.h可以通过模块sysconfig找到及其通过 cppyy.add_include_path 提供的路径在 bootstrap.py下面讨论的文件。
接下来,考虑预编译的头文件(文件 cppyy_backend/etc/allDict.cxx.pch):它包含 LLVM 中间表示形式的 C++ 标准头文件。如果添加,它会预先要求部署包的系统编译器。但是,如果有系统编译器,那么理想情况下,PCH 应该在部署后首次使用时重新创建。
然而,loader.py cppyy_backend 中的脚本使用 sys.executable它被卡住破坏(意思是,它是顶级脚本,而不是 python ,导致无限递归)。即使 PCH 可用,它的时间戳也会与 include 的时间戳进行比较。目录,如果旧则重建。由于 PCH 和包含目录都根据复制顺序而不是构建顺序获取新的时间戳,因此这是不可靠的,并且可能导致虚假的重建。因此,要么禁用 PCH,要么禁用时间戳检查。
为此,请选择这两个选项之一并将其写入名为 bootstrap.py 的文件中。 ,通过取消注释所需的行为:
### option 1: disable the PCH altogether

# import os
# os.environ['CLING_STANDARD_PCH'] = 'none'

### option 2: force the loader to declare the PCH up-to-date

# import cppyy_backend.loader
#
# def _is_uptodate(*args):
#    return True
#
# cppyy_backend.loader._is_uptodate = _is_uptodate
然后将 Bootstrap 作为 Hook 添加到 Analysis 中的规范文件中目的:
             runtime_hooks=['bootstrap.py'],
如上所述,bootstrap.py也是根据需要添加更多包含路径的好地方,例如对于 Python.h .
最后,照常运行:
$ pyinstaller example.spec

关于python - 是否有*任何*解决方案来打包使用 cppyy 的 python 应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64406727/

相关文章:

python - 高效的嵌套正则表达式

python - PyInstaller 中的 Pandas、Numpy

python - PyPandoc 与 PyInstaller 结合使用

c++ - 如何在其他 linux 平台上部署带有所有 dll 文件的 Qt 应用程序?

python - 有没有办法从 python 控制台中查看模块的源代码?

python - VSCode 折叠文档字符串 Python MacOS

python - 使用错误的参数类型调用 PySide2.QtWidgets.QxxxxLayout.addWidget

java.lang.Exception 与滚动您自己的异常

Python 打包 : subdirectories not installed

python - 在使用 Formalchemy 验证期间更改其他字段值?