【问题标题】:Use "extern __declspec(dllimport)" in Cython在 Cython 中使用“extern __declspec(dllimport)”
【发布时间】:2021-07-30 17:22:13
【问题描述】:

我可以在 Cython 中使用 extern __declspec(dllimport) 吗?我正在尝试在 Windows 中包装 embree,但不确定是否可以在 Cython 中动态链接。

我阅读了this SO post,它非常适合直接更改 C/C++ 和头文件,但我不确定如何在 .pxd 文件中实现这一点。

例如,Embree 2.17.7 x64 标头 rtcore.hRTCORE_API 定义为

#ifndef RTCORE_API
#if defined(_WIN32) && !defined(EMBREE_STATIC_LIB)
#    define RTCORE_API extern "C" __declspec(dllimport)
#else
#    define RTCORE_API extern "C"
#endif
#endif

但是,这些在pyembree pxd 文件rtcore.pxd 中使用它们的函数签名中被忽略了。这似乎与Cython docs 一致,该状态为

省略对 C 声明的任何特定于平台的扩展,例如 __declspec()

但是,即使我通过更改行将 pyembree setup.py 文件指向我下载的 embree DLL

ext.libraries = ["embree"]

ext.libraries = [""C:/Program Files/Intel/Embree v2.17.7 x64/bin/embree""]

我仍然收到 3 个链接错误:

mesh_construction.obj : error LNK2001: unresolved external symbol __imp_rtcMapBuffer
mesh_construction.obj : error LNK2001: unresolved external symbol __imp_rtcNewTriangleMesh
mesh_construction.obj : error LNK2001: unresolved external symbol __imp_rtcUnmapBuffer
build\lib.win-amd64-3.8\pyembree\mesh_construction.cp38-win_amd64.pyd : fatal error LNK1120: 3 unresolved externals

我从this SO postMicrosoft docs 得知与__imp_ 相关的链接器错误是由于找不到DLL。但是,您可以在rtcore_geometry.h 中看到它已定义:

rtcore_geometry.pxd 中定义:

唯一的区别是.pxd 文件的签名中不包含RTCORE_API

有谁知道我该如何解决这个问题,以便 pyembree 构建?

编辑:还要注意,我已经加了

# distutils: language=c++

我所有的.pyx.pxd 文件。 This SO post 也被审核过,但并没有解决我的问题。

更新:embree.lib文件添加到我的本地pyembree/embree2文件夹并将setup.py更新为

ext.libraries = ["pyembree/embree2/*"]

允许代码编译通过

py setup.py build_ext -i

但是,包不加载:

>>> import pyembree
>>> from pyembree import rtcore_scene
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: DLL load failed while importing rtcore_scene: The specified module could not be found.

我需要在setup.py 中定义“子包”吗?这是我目前的setup.py

from setuptools import find_packages, setup

import numpy as np
from Cython.Build import cythonize
from Cython.Distutils import build_ext

include_path = [np.get_include()]

ext_modules = cythonize(
    'pyembree/*.pyx',
    language_level=3,
    include_path=include_path)

for ext in ext_modules:
    ext.include_dirs = include_path
    ext.libraries = [
        "pyembree/embree2/*",
    ]

setup(
    name="pyembree",
    version='0.1.6',
    cmdclass = {"build_ext": build_ext},
    ext_modules=ext_modules,
    zip_safe=False,
    packages=find_packages(),
    include_package_data = True
)

目录结构如下(pyembree是我项目的.venv\lib\site-packages文件夹中的顶级文件夹):

pyembree
│   .authors.yml
│   .gitignore
│   .mailmap
│   AUTHORS
│   CHANGELOG.rst
│   LICENSE
│   MANIFEST.in
│   pyproject.toml
│   README.rst
│   setup.py
│   
├───build
│   └───temp.win-amd64-3.8
│       └───Release
│           └───pyembree
│                   mesh_construction.cp38-win_amd64.exp
│                   mesh_construction.cp38-win_amd64.lib
│                   mesh_construction.obj
│                   rtcore.cp38-win_amd64.exp
│                   rtcore.cp38-win_amd64.lib
│                   rtcore.obj
│                   rtcore_scene.cp38-win_amd64.exp
│                   rtcore_scene.cp38-win_amd64.lib
│                   rtcore_scene.obj
│                   triangles.cp38-win_amd64.exp
│                   triangles.cp38-win_amd64.lib
│                   triangles.obj
│
├───pyembree
│   │   mesh_construction.cp38-win_amd64.pyd
│   │   mesh_construction.cpp
│   │   mesh_construction.h
│   │   mesh_construction.pyx
│   │   rtcore.cp38-win_amd64.pyd
│   │   rtcore.cpp
│   │   rtcore.pxd
│   │   rtcore.pyx
│   │   rtcore_geometry.pxd
│   │   rtcore_geometry_user.pxd
│   │   rtcore_ray.pxd
│   │   rtcore_scene.cp38-win_amd64.pyd
│   │   rtcore_scene.cpp
│   │   rtcore_scene.pxd
│   │   rtcore_scene.pyx
│   │   triangles.cp38-win_amd64.pyd
│   │   triangles.cpp
│   │   triangles.pyx
│   │   __init__.pxd
│   │   __init__.py
│   │
│   ├───embree2
│   │       embree.lib
│   │       rtcore.h
│   │       rtcore.isph
│   │       rtcore_builder.h
│   │       rtcore_geometry.h
│   │       rtcore_geometry.isph
│   │       rtcore_geometry_user.h
│   │       rtcore_geometry_user.isph
│   │       rtcore_ray.h
│   │       rtcore_ray.isph
│   │       rtcore_scene.h
│   │       rtcore_scene.isph
│   │       rtcore_version.h
│   │       tbb.lib
│   │       tbbmalloc.lib
│   │
│   └───__pycache__
│           __init__.cpython-38.pyc
│
└───tests
        test_intersection.py

【问题讨论】:

  • .pxd 文件的内容是什么?你通常做cdef extern from "header.h":。这将标头包含在 Cython 生成的 C 文件中。因此__declspec 不相关,因为 Cython 不需要知道它,并且它在包含的标头中正确定义。
  • @Davidw 感谢您的帮助! pxd 文件位于此处:github.com/scopatz/pyembree/tree/master/pyembree。让我知道是否有更好的方法可以将其转移给您。
  • 我不知道答案(抱歉),但我很确定 Cython 中的 __declspec 不是你的问题
  • @DavidW 我实际上有一个更新。我会把它添加到问题中,如果你可以看看

标签: dll cython dllimport


【解决方案1】:

一旦我将DLLs 手动复制并粘贴到我的.venv\Lib\site-packages 文件夹中生成的.egg 文件夹中,代码就会正常运行:

pyembree-0.1.6-py3.8-win-amd64.egg
├───EGG-INFO
│       dependency_links.txt
│       native_libs.txt
│       not-zip-safe
│       PKG-INFO
│       SOURCES.txt
│       top_level.txt
│       
└───pyembree
    │   embree.dll
    │   freeglut.dll
    │   mesh_construction.cp38-win_amd64.pyd
    │   mesh_construction.cpp
    │   mesh_construction.py
    │   rtcore.cp38-win_amd64.pyd
    │   rtcore.cpp
    │   rtcore.py
    │   rtcore_scene.cp38-win_amd64.pyd
    │   rtcore_scene.cpp
    │   rtcore_scene.py
    │   tbb.dll
    │   tbbmalloc.dll
    │   triangles.cp38-win_amd64.pyd
    │   triangles.cpp
    │   triangles.py
    │   __init__.py
    │
    └───__pycache__
            mesh_construction.cpython-38.pyc
            rtcore.cpython-38.pyc
            rtcore_scene.cpython-38.pyc
            triangles.cpython-38.pyc
            __init__.cpython-38.pyc

但是,我怎样才能告诉python 复制并粘贴这些DLLs?我可以在我的setup.py 文件中添加一些内容吗?

编辑:根据@ead 的cmets,setup.py 可以更新为以下内容,以自动将 DLL 复制到正确的文件夹(感谢@ead!):

import os
from setuptools import find_packages, setup

import numpy as np
from Cython.Build import cythonize
from Cython.Distutils import build_ext


include_path = [
    np.get_include(),
]

ext_modules = cythonize("pyembree/*.pyx", language_level=3, include_path=include_path)

for ext in ext_modules:
    ext.include_dirs = include_path
    ext.libraries = [
        "pyembree/embree2/lib/embree",
        "pyembree/embree2/lib/tbb",
        "pyembree/embree2/lib/tbbmalloc",
    ]

setup(
    name="pyembree",
    version="0.1.6",
    cmdclass={"build_ext": build_ext},
    ext_modules=ext_modules,
    zip_safe=False,
    packages=find_packages(),
    include_package_data=True,
    package_data={"pyembree": ["*.cpp", "*.dll"]},
)

【讨论】:

猜你喜欢
  • 2011-01-18
  • 1970-01-01
  • 1970-01-01
  • 2011-03-18
  • 2013-03-26
  • 1970-01-01
  • 2013-07-29
  • 2012-07-14
  • 2020-04-30
相关资源
最近更新 更多