尝试使用distutils将SWIG Python扩展交叉编译到mingw32时出错

4

我正在尝试使用distutils模块在Linux上为Windows(mingw32)交叉编译一个简单的SWIG Python扩展。最终目标是编译一个Python库的包装器,并能够在Windows上使用它。很明显,我从最基本的例子开始,但不幸的是失败了。

以下是我使用的文件:

example.c

/* File : example.c */

/* A global variable */
double Foo = 3.0;

/* Compute the greatest common divisor of positive integers */
int gcd(int x, int y) {
  int g;
  g = y;
  while (x > 0) {
    g = x;
    x = y % x;
    y = g;
  }
  return g;
}

example.i - SWIG interface file

/* File : example.i */
%module example

%inline %{
extern int    gcd(int x, int y);
extern double Foo;
%}

setup.py

# setup.py
import distutils
from distutils.core import setup, Extension

setup(name = "SWIG example",
      version = "1.0",
      ext_modules = [Extension("_example", ["example.i","example.c"])])

为了使用本地(Linux)gcc编译器进行编译,我正在调用以下命令:
python setup.py build

一切都运行得很好!但是当尝试指定Windows目标时出现了问题:

python setup.py build --compiler=mingw32

我遇到了一个错误,提示gcc无法识别-mdll开关:

running build
running build_ext
building '_example' extension
swigging example.i to example_wrap.c
swig -python -o example_wrap.c example.i
creating build
creating build/temp.linux-x86_64-2.7
gcc -mdll -O -Wall -I/home/jojek/anaconda/include/python2.7 -c example_wrap.c -o build/temp.linux-x86_64-2.7/example_wrap.o
gcc: error: unrecognized command line option ‘-mdll’
error: command 'gcc' failed with exit status 1

非常好,这很有道理,因为工具链无效。我确保在我的机器上安装了 mingw32。通过调用 dpkg -L mingw32,我知道编译器位于 /usr/bin/i586-mingw32msvc-gcc

我的下一步是使用实际路径覆盖 CC 环境变量。当我尝试重新编译时,我遇到了缺少 sys/select.h 头文件的以下错误:

running build
running build_ext
building '_example' extension
swigging example.i to example_wrap.c
swig -python -o example_wrap.c example.i
creating build
creating build/temp.linux-x86_64-2.7
/usr/bin/i586-mingw32msvc-gcc -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/jojek/anaconda/include/python2.7 -c example_wrap.c -o build/temp.linux-x86_64-2.7/example_wrap.o
example_wrap.c:1: warning: -fPIC ignored for target (all code is position independent)
In file included from /home/jojek/anaconda/include/python2.7/Python.h:58,
                 from example_wrap.c:125:
/home/jojek/anaconda/include/python2.7/pyport.h:351:24: error: sys/select.h: No such file or directory
error: command '/usr/bin/i586-mingw32msvc-gcc' failed with exit status 1

有没有人有关于如何管理这个任务的想法?

2个回答

5
使用distutils编译Python模块时,背后发生了很多事情。您在提问中每次都接近答案,但现在遇到的问题是,您正在使用Linux头文件与Windows(交叉)编译器。实际上,缺少配置头文件导致您的交叉编译尝试使用POSIX接口而不是Win32替代方案。(sys/select.h在mingw32上不受支持,cygwin可能会有所不同)。我的答案从简单地手动构建模块开始,使用Linux上的mingw32,然后在我们证明已具备所有要求之后再考虑使用distutils。我还假设您没有可用于在Windows本机上构建扩展的Windows构建框(甚至虚拟机),因为这比交叉编译简单得多。如果您正在阅读此内容并且有使用Windows框来构建Windows Python扩展的选项,请选择该选项以节省时间和精力。话虽如此,仅使用Linux框即可构建Windows Python模块是可能的。

假设您已经在Linux上安装并使用mingw32(例如使用Debian/Ubuntu软件包),第一步是获取Windows头文件(或更具体地说是配置)。我假设您的目标是大多数人在搜索引擎中键入“python windows”时得到的构建,因此我从python.org下载了Windows MSI安装程序并从中提取了它们。

我们想从Python发行版中获取两个东西:

  1. python27.dll(通常放置在c:\windows\system32或c:\windows\syswow64中)
  2. 'include'目录(通常放置在c:\python27\include中)

在Linux下,有几种不同的方法可以提取它。您可以使用Wine安装MSI文件。我在测试中成功地使用了cabextract和7z,例如使用cabextract:

cabextract /tmp/python-2.7.10.msi -F '*.h'
cabextract /tmp/python-2.7.10.msi -F 'python27.dll'

(注:如果您使用7z,您将在第二个名为“python”的内部存档中找到您真正想要的文件)。
此时,您还可以提取文件“libpython27.a”,该文件通常位于c:\ python27 \ libs \中,但是这个文件对于使用mingw32链接甚至没有用处。
鉴于我们现在拥有的头文件,我们已经足够编译我们的扩展了,尽管如上所述,为了让mingw32针对python27.dll进行链接,我们需要先做一些额外的工作。我们需要一个名为pexports的工具,以列出Python DLL中的所有导出符号,并让dlltool生成一个存根库,供mingw32链接。我直接下载了pexports,然后进行了提取:
tar xvf ~/Downloads/pexports-0.47-mingw32-bin.tar.xz

提取后我们得到一个Windows可执行文件。我在这里的示例中使用了Wine直接运行它;或者,您可以提取源代码,并将其构建为在Linux主机上本地运行的工具:

tar xvf ~/Downloads/pexports-0.47-mingw32-src.tar.xz
(cd pexports-0.47 && ./configure && make)

或者你可以使用Python模块pefile(跨平台运行良好)复制该工具的功能,以提取我们关心的导出项,如果你想避免使用Wine的话。无论如何,使用pexports,你可以生成一个包含我们需要的信息的.def文件,用于dlltool。
wine bin/pexports.exe -v python27.dll > python27.def

或者,(如果您已经将pexports构建为本机工具),只需:
./pexports-0.47/pexports -v python27.dll > python27.def

这里的python27.dll是我们之前从.msi文件中提取出来的。

(这是我参考pexports得到的)

一旦你获得了.def文件,就可以使用mingw32 dlltool生成一个.a文件,稍后我们将使用它来链接Python模块:

i586-mingw32msvc-dlltool -A --dllname python27.dll --def python27.def --output-lib libpython27.a

现在,我们已经到了可以考虑运行SWIG本身来为我们生成需要编译的代码的地步。我进一步简化了你的示例接口,只剩下如下内容:
%module test

%inline %{
int gcd(int x, int y) {
  int g;
  g = y;
  while (x > 0) {
    g = x;
    x = y % x;
    y = g;
  }
  return g;
}
%}

然后在我的Linux机器上运行SWIG:

swig -Wall -python test.i

这生成了test_wrap.c文件,我使用以下命令进行编译:

i586-mingw32msvc-gcc test_wrap.c -I../include -Wall -Wextra -shared -o _test.pyd ./libpython27.a

我们使用Linux构建了一个Windows Python模块。

为了检查它是否真的可以运行,我将test.py和_test.pyd复制到Windows计算机上,然后执行以下操作:

Python 2.7.10 (default, May 23 2015, 09:40:32) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> test.gcd(1024, 512)
512
>>>

现在需要做的就是通过调整路径,确保distutils能够找到正确的包含文件和库进行链接。

1
就此而言,你提到的 pexports 版本已经过时了。当前版本来自 MinGW.org,是 pexports-0.46(我在两年半前发布的)。这个版本可以作为 Linux 主机交叉编译工具进行构建(这是我更新它的主要动机),尽管在我的 64 位 Linux 主机上,它偶尔会出现段错误(我只在尝试从 Win98 的 MSVCRT.DLL 中转储导出函数时注意到这一点),由于将数据转换为 64 位指针时出现了错误;(我仍在努力追踪这个 bug)。 - Keith Marshall
1
@KeithMarshall哦,那很棒。当我坐下来写这篇文章时,我已经很久没有碰mingw32了。我找不到Debian的pexports软件包,这是我变更的试金石。如果您想修复这个问题,请随意编辑我的答案 - 如果这可以促使您修改它,我很乐意将其改为CW。 - Flexo
1
好的,我已经编辑了pexports引用——现在是0.47版本,因为我发现并修复了64位指针错误。我的SO声望点数不够高,无法直接通过编辑,所以正在等待同行审查。 - Keith Marshall
1
出于好奇,为什么msi文件中捆绑的libpython27.a不适用于mingw32?它是以某种方式格式错误吗?还是它是为不同的架构而设计的? - Keith Marshall
2
所有的MinGW库都只是ar档案;这个预构建的libpython27.a是一个(格式不正确的)导入库,(我们通常希望Visual Studio创建为.lib文件)。 对于MinGW和Visual Studio,对象格式相同(COFF),在这种情况下,所有导入存根都是有效的COFF对象。 毛病在于一对头/尾对象(指定DLL关联); 这些_也应该是COFF对象文件,但在这种情况下它们不是...它们是没有特定可识别格式的原始数据(从GNU binutils的角度来看)。 - Keith Marshall
显示剩余7条评论

1
对于64位,我无法使用pexports使其工作,所以我使用gendef生成了python27.def文件。 gendef是一个从DLL生成def文件的工具。Def文件是由DLL导出的符号列表。该工具的主要用途是允许创建由非GCC编译器创建的DLL的导入库。它可以处理x86(win32)和amd64(win64)可执行文件。

https://sourceforge.net/p/mingw-w64/wiki2/gendef/

希望它有所帮助!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接