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

你离开我真会死。 提交于 2019-12-18 13:36:49

问题


I am trying to cross-compile a simple SWIG Python extension on Linux for Windows (mingw32), using the distutils module.

The ultimate goal is to compile a Python wrapper for some library and being able to use it on Windows. Obviously I started with the most basic example and unfortunately it fails.

Here are the files I am using:

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"])])

In order to compile using the native (Linux) gcc compiler, I am invoking:

python setup.py build

Everything works like a charm! Unfortunately when trying to specify the Windows target:

python setup.py build --compiler=mingw32

I get the error saying that gcc can't recognize -mdll switch:

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

Fair enough, it makes perfect sense, since toolchain is not valid. I made sure that mingw32 is installed on my machine. By calling dpkg -L mingw32 I know that compiler is located in /usr/bin/i586-mingw32msvc-gcc.

My next step was to override the CC environmental variable with the actual path to my compiler. When I try to compile it again, then I am getting the following error with missing sys/select.h header file:

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

Does anyone have an idea how to manage that task?


回答1:


There's quite a bit going on behind the scenes when you compile Python modules using distutils. You're getting closer with each try in your question, however the problem you've now encountered is that you're using Linux header files with a Windows (cross) compiler. (sys/select.h isn't supported with mingw32, cygwin might be a different story though). In reality it's the lack of the configuration header file that's causing your cross compile to try and use the POSIX interfaces instead of Win32 alternatives.

My answer rewinds a few steps and starts out simply building the module by hand, using mingw32 on Linux and then we'll look at using distutils once we've proven that we have all that's required.

I'm also assuming that you don't have a Windows build box (or even a VM) available to simply build your extension on Windows natively since that's far simpler than cross compiling. If you're reading this and have the option to use a Windows box to build your Windows Python extensions, do that instead and save time and effort. That said it is possible to build Windows Python modules using only a Linux box.

Starting with mingw32 already installed and working on your Linux box (e.g. using the Debian/Ubuntu packages) the first step is to get the Windows header files (or configuration to be more specific). I'm assuming you're targeting the build that most people get when they type "python windows" into a search engine so I downloaded the Windows MSI installers from python.org and extracted them from there.

There are two things we want to get from the Python distribution:

  1. python27.dll (Usually gets placed in c:\windows\system32 or c:\windows\syswow64)
  2. The 'include' directory (Usually gets placed in c:\python27\include)

Under Linux there are a few different ways you can extract this. You could use Wine to install the MSI file. I used both cabextract and 7z with success in my testing though, for example with cabextract:

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

(Note: if you use 7z you'll find the files you really want inside a second, inner archive named 'python').

At this point you could also extract the file 'libpython27.a' which usually lives inside c:\python27\libs\ however this file isn't sufficient or even useful for linking using mingw32.

Given the header files we've now got enough to compile our extension, although as noted above to get mingw32 to link against python27.dll we need to do a bit more work first. We're going to need a tool called pexports to list all the exported symbols in the Python DLL and let dlltool generate a stub library for mingw32 to link against. I downloaded pexports directly and then extracted it with:

tar xvf ~/Downloads/pexports-0.47-mingw32-bin.tar.xz

Once that's extracted we get a single Windows executable. I used Wine in my example here to run it directly; alternatively, you could extract the source, and build it as a tool to run natively on the Linux host:

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

or you could have duplicated the functionality of the tool using the Python module pefile (which runs fine cross platform) to extract the exports that we care about if you were looking to avoid using Wine as well.

Anyway with pexports you can generate a .def file that contains the information we need for dlltool:

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

or, (if you've built pexports as a native tool), simply:

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

where python27.dll is what we extracted from the .msi file earlier.

(This was my pexports reference)

Once you've got the .def file you can use the mingw32 dlltool to generate a .a file that we'll use later to link our Python module against:

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

Now we've reached a point where we can think about running SWIG itself to generate the code for us to compile. I simplified your example interface even further to be just:

%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;
}
%}

And then ran SWIG on my Linux box as:

swig -Wall -python test.i

This generated test_wrap.c which I compiled with:

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

And there we have a Windows Python module built using just Linux.

To check it really runs I copied test.py and _test.pyd to a Windows box and then did:

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

Now all that remains is to make sure distutils can find the right include files and libraries to link against by manipulating its paths.




回答2:


For 64 bits, I couldnt make it work with pexports, so I used gendef to generate the python27.def file.

gendef is a tool which generate def files from DLLs. A def file is a list of symbols exported by a DLL. The primary use of this tool is to allow creation of a import library of DLLs created by non-GCC compilers. It can handle both x86 (win32) and amd64 (win64) executables.

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

Hope It helps!



来源:https://stackoverflow.com/questions/32361119/error-when-trying-to-cross-compile-swig-python-extension-for-mingw32-using-distu

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!