在 Cython 中使用 "extern __declspec(dllimport)"

Use "extern __declspec(dllimport)" in Cython

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

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

例如,Embree 2.17.7 x64 header 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 file rtcore.pxd. This seems consistent with the Cython docs 中使用它们的函数签名被遗漏了,

Leave out any platform-specific extensions to C declarations such as __declspec()

但是,即使我通过更改行

将 pyembree setup.py 文件指向我下载的 embree DLL
ext.libraries = ["embree"]

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

我仍然遇到 3 个 linking 错误:

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

我从 and the Microsoft docs 得知 __imp_ 相关的 linker 错误是由于未找到 DLL。但是在rtcore_geometry.h中可以看到是这样定义的:

rtcore_geometry.pxd中定义为:

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

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

EDIT: 还需要注意的是,我已经添加了

# distutils: language=c++

我所有的 .pyx.pxd 文件。 也审核了,但是没有解决我的问题

更新: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文件夹中的top-level文件夹):

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

一旦我手动将 DLL 复制并粘贴到我的 .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 复制并粘贴这些 DLL?我可以在我的 setup.py 文件中添加一些东西吗?

编辑: 根据@ead 的评论,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"]},
)