动态链接Python扩展(.pyd)到另一个扩展
Python扩展模块其实就是动态库,所以我想把一个Python扩展动态链接到另一个扩展是有可能的。不过在Windows上,Python扩展的文件后缀是.pyd
,而不是.dll
,这让我在运行设置脚本时无法让distutils链接到它们。(我觉得在UNIX系统上就没这个问题,因为Python扩展用的是.so
文件后缀。)
假设我有一个扩展bar.pyd
,它需要链接到foo.pyd
。在我的设置脚本中,我基本上做了以下操作:
from distutils.core import setup, Extension
foo = Extension("foo", sources=["foo.c"])
bar = Extension("bar", libraries=["foo"], sources=["bar.c"])
setup(ext_modules=[foo, bar])
到目前为止,这个方法不太奏效。这真的可行吗?我觉得应该可以,但我在网上找不到相关的信息。我在Windows上使用MinGW,但我希望这个方法也能在不同的MSVC++和其他系统上工作。
编辑:之前,我通过将foo
编译时生成的目标文件(foo.o
)传递给扩展的extra_objects
选项来解决这个问题(这只有在我在bar
中定义了所有foo
符号的原型时才有效):
bar = Extension("bar", sources=["bar.c"], extra_objects=["build/.../foo.o"]
虽然这似乎不是正确的解决方案,但它确实有效。我对动态链接的理解不太深,所以这可能是正确的做法。不过感觉上有点不对劲。
接着,我尝试给gcc传递一些明确的参数,让它编译一个导入库:
foo = Extension("foo", sources=["foo.c"], extra_compile_args=["-Wl,--out-implib,foo.lib,--export-all-symbols"])
然后我把bar
链接到新的导入库上:
bar = Extension("bar", libraries=["foo"], sources=["bar.c"])
这个编译没有报错,但有一些符号出现了问题(具体来说,我在foo
中有几个全局的PyTypeObject
,在bar
中似乎被重新定义了。我需要这两个模块中的PyTypeObject
指向同一个定义)。
编辑 2:所以,我找出了问题所在。在我构建并链接导入库后,函数符号正确导出了,但PyTypeObject
却被重新声明了。假设在foo
中有一个PyTypeObject Foo_Type
。我在foo.h
中声明了它,这个头文件被foo.c
和bar.c
都包含了:
PyTypeObject Foo_Type;
我把这个声明去掉,然后在foo.c
的顶部加上这个:
PyTypeObject __declspec(dllexport) Foo_Type;
在bar.c
的顶部加上这个:
PyTypeObject __declspec(dllimport) Foo_Type;
这样就解决了问题。我可以在foo
和bar
中使用Foo_Type,并且它们都指向同一个Foo_Type的定义。问题是,这种方法在非Windows系统上可能不行。我想如果我把__declspec
去掉,其他系统上应该就能正常工作了。
2 个回答
from distutils.core import Extension as cExtension
from pyd.support import setup, Extension
module1 = Extension("x", sources = ['xclass.c'])
module2 = Extension("y", sources = ['hello.d'], build_deimos=True)
setup(
name = "x",
version = '1.0',
description = "eat a taco",
ext_modules = [
module1,
module2
]
);
如果你在用普通的Python 导入机制,那么就不需要去链接其他的扩展。如果你想调用其他扩展里的函数,可能是因为你拿到了它的头文件,那么在链接之前,你需要先从DLL生成一个导入库。