我正在努力将Python模块移植到Windows。我有一个玩具示例如下。
文件夹结构为:
foo/
libfoo/
foo.c
setup.py
setup.py
from setuptools import setup, Extension
sources = ['libfoo/foo.c']
foo = Extension('libfoo',
sources = sources,
define_macros = None,
include_dirs = ['./libfoo'],
libraries = None,
library_dirs = None,
)
setup(name = 'foo',
ext_modules = [foo],
install_requires = ['setuptools'],
)
libfoo / foo.c (完整性)
#include <stdio.h>
void foo() {
printf("Hello World!");
}
当我尝试安装软件包时,遇到错误。
C:\Users\user\foo>python setup.py install
running install
running bdist_egg
running egg_info
creating foo.egg-info
writing requirements to foo.egg-info\requires.txt
writing foo.egg-info\PKG-INFO
writing top-level names to foo.egg-info\top_level.txt
writing dependency_links to foo.egg-info\dependency_links.txt
writing manifest file 'foo.egg-info\SOURCES.txt'
reading manifest file 'foo.egg-info\SOURCES.txt'
writing manifest file 'foo.egg-info\SOURCES.txt'
installing library code to build\bdist.win32\egg
running install_lib
running build_ext
building 'libfoo' extension
creating build
creating build\temp.win32-2.7
creating build\temp.win32-2.7\Release
creating build\temp.win32-2.7\Release\libfoo
c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\BIN\cl.exe /c /nologo /Ox
/MD /W3 /GS- /DNDEBUG -I./libfoo -IC:\Python27\include -IC:\Python27\PC /Tclibfo
o/foo.c /Fobuild\temp.win32-2.7\Release\libfoo/foo.obj
foo.c
creating build\lib.win32-2.7
c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\BIN\link.exe /DLL /nologo
/INCREMENTAL:NO /LIBPATH:C:\Python27\libs /LIBPATH:C:\Python27\PCbuild /EXPORT:i
nitlibfoo build\temp.win32-2.7\Release\libfoo/foo.obj /OUT:build\lib.win32-2.7\l
ibfoo.pyd /IMPLIB:build\temp.win32-2.7\Release\libfoo\libfoo.lib /MANIFESTFILE:b
uild\temp.win32-2.7\Release\libfoo\libfoo.pyd.manifest
LINK : error LNK2001: unresolved external symbol initlibfoo
build\temp.win32-2.7\Release\libfoo\libfoo.lib : fatal error LNK1120: 1 unresolv
ed externals
error: command 'c:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\BIN\\l
ink.exe' failed with exit status 1120
似乎distutils包(在本例中为setuputils)将始终从共享扩展中导出一个符号,即“init + extension_name”[Link]。
它在Windows链接器“导出”选项[Link]中指定,但找不到符号。
帮助?
编辑:C代码不使用Python C API,即“#include”。这是因为该项目的目标是获取现有的C库并通过Python扩展包含在Python包装器中。该软件包适用于Unix / Linux。
答案 0 :(得分:6)
经过大量的游戏,我能够理解这个问题。
要在Python 2中解决此问题,请在源代码中定义initlibfoo()
方法。
#include <Python.h>
PyMODINIT_FUNC initlibfoo(void) {
// do stuff...
}
要在Python 3中解决此问题,请在源代码中定义PyInit_libfoo()
方法。
#include <Python.h>
PyMODINIT_FUNC PyInit_libfoo(void) {
// do stuff...
}
现在, foo.c 将如下所示:
#include <stdio.h>
#include <Python.h>
void foo() {
printf("Hello World!");
}
PyMODINIT_FUNC initlibfoo(void) // Python 2.7
//PyMODINIT_FUNC PyInit_libfoo(void) // Python 3.5
{
// do stuff...
}
在为Windows编译Python扩展时,Extension类将告诉linker to export a function其他程序要调用。这可以在终端输出中看到:
c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\BIN\link.exe /DLL /nologo
/INCREMENTAL:NO /LIBPATH:C:\Python27\libs /LIBPATH:C:\Python27\PCbuild /EXPORT:i
nitlibfoo build\temp.win32-2.7\Release\libfoo/foo.obj /OUT:build\lib.win32-2.7\l
ibfoo.pyd /IMPLIB:build\temp.win32-2.7\Release\libfoo\libfoo.lib /MANIFESTFILE:b
uild\temp.win32-2.7\Release\libfoo\libfoo.pyd.manifest
(强调/EXPORT:initlibfoo
)
现在,这是C / C ++ Extensions的必需功能,因此可以将它们作为模块导入。换句话说,当你import libfoo
Python将在.pyd上调用函数initlibfoo
作为模块初始化的一部分。这里遇到的错误是由于链接器被告知要导出函数initlibfoo
,但它无法在C对象文件中找到符号,因为它从未在源代码中定义过!
当然,人们应该能够拒绝出口符号的选项吗?显然不是。回到Extension类文档,有一个参数export_symbols
。我尝试将None
传递给它,并且链接器选项仍在使用中。似乎没有办法绕过这个链接器选项。
不推荐:如果由于某种原因,需要让Python.h
困扰你,那么就有办法解决这个问题。你仍然需要定义上面的“init”方法,但你可以这样做:
void initlibfoo() {} //Python 2.7
void PyInit_libfoo() {} //Python 3.5
但是现在,您无法通过import libfoo
使用该库。您可以使用 ctypes 模块并使用ctypes.PyDLL('/path/to/pyd')
加载它。从本质上讲,您使用Python为您构建DLL,在这种情况下,它会创建一个特殊的DLL,称为.pyd文件。然后要使用它,您必须通过 ctypes 模块加载它。
答案 1 :(得分:0)
另一个可能的解决方案:
from distutils.command.build_ext import build_ext as _du_build_ext
from unittest.mock import Mock
mockobj = _du_build_ext
mockobj.get_export_symbols = Mock(return_value=None)
答案 2 :(得分:0)
我在这里设置了一个最小工作的python包,带有ctypes扩展名:
https://github.com/himbeles/ctypes-example
可以在Windows,Mac,Linux上运行,并且不需要在C / C ++中包括Python.h
build_ext.get_export_symbols()
。.so
。