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

Error when trying to cross-compile SWIG Python extension for mingw32 using distutils

我正在尝试使用 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 接口文件

/* 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

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

当您使用 distutils 编译 Python 模块时,幕后发生了很多事情。您在问题中的每一次尝试都越来越接近,但是您现在遇到的问题是您正在使用 Linux 头文件和 Windows (交叉)编译器。 (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)

在 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' 的第二个内部存档中找到您真正想要的文件)。

此时您还可以提取通常位于 c:\python27\libs\ 中的文件 'libpython27.a',但是该文件对于使用 mingw32 linking 来说不够用甚至没有用.

鉴于头文件,我们现在已经有足够的时间来编译我们的扩展,尽管如上所述要使 mingw32 达到 link 而不是 python27.dll 我们需要先做更多的工作。我们将需要一个名为 pexports 的工具来列出 Python DLL 中所有导出的符号,并让 dlltool 为 mingw32 生成一个存根库以 link 反对。我直接下载了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 文件,稍后我们将使用该文件来 link 我们的 Python 模块:

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

现在我们已经到了可以考虑 运行ning 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 -Wall -python test.i

这生成了 test_wrap.c,我用它编译:

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

我们有一个 Windows Python 模块只使用 Linux.

构建

为了真正检查 运行s,我将 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 可以通过操纵其路径找到正确的包含文件和库以 link 反对。

对于 64 位,我不能让它与 pexports 一起工作,所以我使用 gendef 来生成 python27.def 文件。

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

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

希望对您有所帮助!