【问题标题】:Error when trying to cross-compile SWIG Python extension for mingw32 using distutils尝试使用 distutils 为 mingw32 交叉编译 SWIG Python 扩展时出错
【发布时间】:2015-09-02 19:01:40
【问题描述】:

我正在尝试使用 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

有人知道如何管理该任务吗?

【问题讨论】:

    标签: python gcc mingw swig distutils


    【解决方案1】:

    当您使用 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 模块。

    从已经安装并在您的 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 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 可以通过操作路径找到正确的包含文件和库以进行链接。

    【讨论】:

    • FWIW,你的 pexports 参考已经过时了。来自 MinGW.org 的当前版本是 pexports-0.46(我在两​​年半前发布)。这个版本可以构建为 Linux 托管的交叉工具,(我更新它的主要动机),虽然,在我的 64 位 Linux 主机上,它偶尔会出现段错误,(我只是在尝试从Win98的MSVCRT.DLL),由于错误的数据转换为64位指针; (我仍在尝试追踪此错误)。
    • @KeithMarshall 哦,这很好。当我坐下来写这篇文章时,我已经很久没有接触过 mingw32 了。我找不到为 Debian 打包的 pexports,这是我改变的试金石。如果您想解决这个问题,请随时编辑我的答案 - 如果这会推动您,我很乐意将其设为 CW。
    • 好的,我已经编辑了 pexports 引用——现在是 0.47,因为我发现(并修复了)错误的 64 位指针错误。我没有足够的 SO 声誉点数无法直接通过编辑,因此正在等待同行评审。
    • 出于好奇,为什么msi 文件中捆绑的libpython27.a 不适用于mingw32?它在某种程度上是畸形的吗?或者,它是否适用于不同的架构?
    • 好吧,所有 MinGW 库都只是 ar 档案;这个预构建的 libpython27.a 是一个(格式错误的)导入库,(我们通常希望 Visual Studio 创建为 .lib 文件)。 MinGW 和 Visual Studio 的对象格式相同 (COFF),在这种情况下,所有导入存根都是有效的 COFF 对象。格式错误在于头/尾对象对(指定 DLL 关联);这些应该也应该是 COFF 目标文件,但在这种情况下它们不是......它们是没有具体可识别格式的原始数据(从 GNU binutils 的角度来看)。
    【解决方案2】:

    对于 64 位,我无法使其与 pexports 一起使用,因此我使用 gendef 生成了 python27.def 文件。

    gendef 是一个从 DLL 生成 def 文件的工具。 def 文件是 DLL 导出的符号列表。该工具的主要用途是允许创建由非 GCC 编译器创建的 DLL 导入库。它可以处理 x86 (win32) 和 amd64 (win64) 可执行文件。

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

    希望对你有帮助!

    【讨论】:

      猜你喜欢
      • 2011-03-13
      • 2014-12-29
      • 1970-01-01
      • 2012-01-26
      • 1970-01-01
      • 1970-01-01
      • 2016-11-13
      • 2012-08-18
      • 1970-01-01
      相关资源
      最近更新 更多