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

时间:2015-09-02 19:01:40

标签: python gcc mingw swig distutils

我正在尝试使用distutils模块在Linux for 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界面文件

/* 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 个答案:

答案 0 :(得分:4)

使用distutils编译Python模块时,幕后有很多事情要做。您在问题中的每次尝试都越来越近了,但是您现在遇到的问题是您正在使用带有Windows(交叉)编译器的Linux头文件。 (sys/select.h isn't supported with mingw32,cygwin可能是一个不同的故事)。实际上,缺少配置头文件导致您的交叉编译尝试使用POSIX接口而不是Win32替代品。

我的回答回过头来,开始简单地手工构建模块,在Linux上使用mingw32,然后我们会在我们证明我们已经完成了所有这些工作之后使用distutils。

我还假设您没有Windows构建盒(甚至是VM)可用于在Windows上本地构建扩展,因为这比交叉编译简单得多。如果您正在阅读本文并且可以选择使用Windows框来构建Windows Python扩展,请执行此操作,以节省时间和精力。也就是说,只使用Linux机器就可以构建Windows Python模块。

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

我们希望从Python发行版中获得两件事:

  1. python27.dll(通常放在c:\ windows \ system32或c:\ windows \ syswow64)
  2. 'include'目录(通常位于c:\ python27 \ include)
  3. 在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,你可以生成一个包含dlltool所需信息的.def文件:

    wine bin/pexports.exe -v python27.dll > python27.def
    

    或,(如果你已经将pexports作为本机工具构建),只需:

    ./pexports-0.47/pexports -v python27.dll > python27.def
    

    其中python27.dll是我们之前从.msi文件中提取的内容。

    (这是我的pexports reference

    获得.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 :(得分: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/

希望有帮助!