joj*_*jek 4 python swig gcc distutils mingw
我正在尝试使用 distutils 模块在 Linux for Windows (mingw32) 上交叉编译一个简单的 SWIG Python 扩展。
\n\n最终目标是为某些库编译 Python 包装器并能够在 Windows 上使用它。显然,我从最基本的示例开始,不幸的是它失败了。
\n\n这是我正在使用的文件:
\n\n示例.c
\n\n/* File : example.c */\n\n/* A global variable */\ndouble Foo = 3.0;\n\n/* Compute the greatest common divisor of positive integers */\nint gcd(int x, int y) {\n int g;\n g = y;\n while (x > 0) {\n g = x;\n x = y % x;\n y = g;\n }\n return g;\n}\nRun Code Online (Sandbox Code Playgroud)\n\nexample.i - SWIG 接口文件
\n\n/* File : example.i */\n%module example\n\n%inline %{\nextern int gcd(int x, int y);\nextern double Foo;\n%}\nRun Code Online (Sandbox Code Playgroud)\n\n安装程序.py
\n\n# setup.py\nimport distutils\nfrom distutils.core import setup, Extension\n\nsetup(name = "SWIG example",\n version = "1.0",\n ext_modules = [Extension("_example", ["example.i","example.c"])])\nRun Code Online (Sandbox Code Playgroud)\n\n为了使用本机 (Linux) gcc 编译器进行编译,我调用:
\n\npython setup.py build\nRun Code Online (Sandbox Code Playgroud)\n\n一切都像魅力一样!不幸的是,当尝试指定 Windows 目标时:
\n\npython setup.py build --compiler=mingw32\nRun Code Online (Sandbox Code Playgroud)\n\n我收到错误消息说 gcc 无法识别 -mdll 开关:
\n\nrunning build\nrunning build_ext\nbuilding \'_example\' extension\nswigging example.i to example_wrap.c\nswig -python -o example_wrap.c example.i\ncreating build\ncreating build/temp.linux-x86_64-2.7\ngcc -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\ngcc: error: unrecognized command line option \xe2\x80\x98-mdll\xe2\x80\x99\nerror: command \'gcc\' failed with exit status 1\nRun Code Online (Sandbox Code Playgroud)\n\n公平地说,这是完全有道理的,因为工具链无效。我确保它mingw32安装在我的机器上。通过调用dpkg -L mingw32我知道编译器位于/usr/bin/i586-mingw32msvc-gcc.
我的下一步是使用编译器的实际路径覆盖 CC 环境变量。当我尝试再次编译它时,出现以下错误,缺少sys/select.h头文件:
running build\nrunning build_ext\nbuilding \'_example\' extension\nswigging example.i to example_wrap.c\nswig -python -o example_wrap.c example.i\ncreating build\ncreating build/temp.linux-x86_64-2.7\n/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\nexample_wrap.c:1: warning: -fPIC ignored for target (all code is position independent)\nIn file included from /home/jojek/anaconda/include/python2.7/Python.h:58,\n from example_wrap.c:125:\n/home/jojek/anaconda/include/python2.7/pyport.h:351:24: error: sys/select.h: No such file or directory\nerror: command \'/usr/bin/i586-mingw32msvc-gcc\' failed with exit status 1\nRun Code Online (Sandbox Code Playgroud)\n\n有谁知道如何管理该任务?
\n当您使用 distutils 编译 Python 模块时,幕后会发生很多事情。您的问题中的每次尝试都越来越接近,但是您现在遇到的问题是您正在将 Linux 头文件与 Windows(交叉)编译器一起使用。(mingw32 不支持 sys/select.h,但 cygwin 可能是一个不同的故事)。实际上,正是缺少配置头文件导致交叉编译尝试使用 POSIX 接口而不是 Win32 替代方案。
我的回答回顾了几个步骤,首先在 Linux 上使用 mingw32 手动构建模块,然后在证明我们拥有所需的所有内容后,我们将考虑使用 distutils。
我还假设您没有 Windows 构建框(甚至虚拟机)来简单地在 Windows 上本地构建扩展,因为这比交叉编译简单得多。如果您正在阅读本文并且可以选择使用 Windows 盒子来构建 Windows Python 扩展,请改为这样做,这样可以节省时间和精力。也就是说,仅使用 Linux 机器就可以构建 Windows Python 模块。
从已安装的 mingw32 开始并在 Linux 机器上运行(例如使用 Debian/Ubuntu 软件包),第一步是获取 Windows 头文件(或更具体的配置)。我假设您的目标是大多数人在搜索引擎中输入“python windows”时获得的版本,因此我从python.org下载了 Windows MSI 安装程序并从那里提取它们。
我们希望从 Python 发行版中获得两件事:
在 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'
Run Code Online (Sandbox Code Playgroud)
(注意:如果您使用 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
Run Code Online (Sandbox Code Playgroud)
提取后,我们将获得一个 Windows 可执行文件。我在这里的例子中使用了 Wine 来直接运行它;或者,您可以提取源代码,并将其构建为在 Linux 主机上本地运行的工具:
tar xvf ~/Downloads/pexports-0.47-mingw32-src.tar.xz
(cd pexports-0.47 && ./configure && make)
Run Code Online (Sandbox Code Playgroud)
或者您可以使用 Python 模块pefile复制该工具的功能来提取我们关心的导出。
无论如何,使用 pexports,您可以生成一个 .def 文件,其中包含 dlltool 所需的信息:
wine bin/pexports.exe -v python27.dll > python27.def
Run Code Online (Sandbox Code Playgroud)
或者,(如果您已将 pexports 构建为本机工具),只需:
./pexports-0.47/pexports -v python27.dll > python27.def
Run Code Online (Sandbox Code Playgroud)
其中 python27.dll 是我们之前从 .msi 文件中提取的内容。
(这是我的pexports 参考)
获得 .def 文件后,您可以使用 mingw32 dlltool 生成一个 .a 文件,稍后我们将使用该文件来链接我们的 Python 模块:
i586-mingw32msvc-dlltool -A --dllname python27.dll --def python27.def --output-lib libpython27.a
Run Code Online (Sandbox Code Playgroud)
现在我们已经到了可以考虑运行 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;
}
%}
Run Code Online (Sandbox Code Playgroud)
然后在我的 Linux 机器上运行 SWIG:
swig -Wall -python test.i
Run Code Online (Sandbox Code Playgroud)
这生成了我编译的 test_wrap.c:
i586-mingw32msvc-gcc test_wrap.c -I../include -Wall -Wextra -shared -o _test.pyd ./libpython27.a
Run Code Online (Sandbox Code Playgroud)
我们有一个仅使用 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
>>>
Run Code Online (Sandbox Code Playgroud)
现在剩下的就是确保 distutils 可以通过操作其路径找到正确的包含文件和库来链接。