Skip to content

Commit 8ff26ac

Browse files
longnguyen2004mstorsjo
authored andcommitted
Add a script for cross building python
1 parent 177cd0d commit 8ff26ac

File tree

3 files changed

+329
-0
lines changed

3 files changed

+329
-0
lines changed

build-python.sh

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#!/bin/sh
2+
#
3+
# Copyright (c) 2018 Martin Storsjo
4+
#
5+
# Permission to use, copy, modify, and/or distribute this software for any
6+
# purpose with or without fee is hereby granted, provided that the above
7+
# copyright notice and this permission notice appear in all copies.
8+
#
9+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16+
17+
set -e
18+
19+
: ${LIBFFI_VERSION:=v3.4.2}
20+
: ${PYTHON_MAJOR:=3}
21+
: ${PYTHON_MINOR:=9}
22+
: ${PYTHON_PATCH:=6}
23+
: ${PYTHON_VERSION:=v${PYTHON_MAJOR}.${PYTHON_MINOR}.${PYTHON_PATCH}}
24+
: ${PYTHON_VERSION_MINGW:=61c5a4a14932539b31878de0b60bc98bcba791b9}
25+
26+
unset HOST
27+
28+
BUILDDIR=build
29+
30+
while [ $# -gt 0 ]; do
31+
case "$1" in
32+
--host=*)
33+
HOST="${1#*=}"
34+
BUILDDIR=$BUILDDIR-$HOST
35+
;;
36+
*)
37+
PREFIX="$1"
38+
;;
39+
esac
40+
shift
41+
done
42+
43+
if [ -z "$CHECKOUT_ONLY" ]; then
44+
if [ -z "$PREFIX" ]; then
45+
echo $0 --host=triple dest
46+
exit 1
47+
fi
48+
49+
mkdir -p "$PREFIX"
50+
PREFIX="$(cd "$PREFIX" && pwd)"
51+
fi
52+
53+
MAKE=make
54+
if [ -n "$(which gmake)" ]; then
55+
MAKE=gmake
56+
fi
57+
58+
: ${CORES:=$(nproc 2>/dev/null)}
59+
: ${CORES:=$(sysctl -n hw.ncpu 2>/dev/null)}
60+
: ${CORES:=4}
61+
62+
if [ ! -d libffi ]; then
63+
git clone https://github.com/libffi/libffi.git
64+
CHECKOUT_LIBFFI=1
65+
fi
66+
67+
if [ -n "$SYNC" ] || [ -n "$CHECKOUT_LIBFFI" ]; then
68+
cd libffi
69+
[ -z "$SYNC" ] || git fetch
70+
git checkout $LIBFFI_VERSION
71+
autoreconf -vfi
72+
cd ..
73+
fi
74+
75+
if [ -z "$HOST" ]; then
76+
# Use a separate checkout for python for the native build;
77+
# mingw builds use a separate fork, maintained by msys2
78+
# which doesn't build on regular Unix
79+
if [ ! -d cpython-native ]; then
80+
git clone https://github.com/python/cpython.git cpython-native
81+
CHECKOUT_PYTHON_NATIVE=1
82+
fi
83+
84+
if [ -n "$SYNC" ] || [ -n "$CHECKOUT_PYTHON_NATIVE" ]; then
85+
cd cpython-native
86+
[ -z "$SYNC" ] || git fetch
87+
git checkout $PYTHON_VERSION
88+
cd ..
89+
fi
90+
91+
[ -z "$CHECKOUT_ONLY" ] || exit 0
92+
93+
cd libffi
94+
[ -z "$CLEAN" ] || rm -rf $BUILDDIR
95+
mkdir -p $BUILDDIR
96+
cd $BUILDDIR
97+
../configure --prefix="$PREFIX" --disable-symvers --disable-docs
98+
$MAKE -j$CORES
99+
$MAKE install
100+
cd ../..
101+
102+
cd cpython-native
103+
[ -z "$CLEAN" ] || rm -rf $BUILDDIR
104+
mkdir -p $BUILDDIR
105+
cd $BUILDDIR
106+
../configure --prefix="$PREFIX" \
107+
CFLAGS="-I$PREFIX/include" CXXFLAGS="-I$PREFIX/include" LDFLAGS="-L$PREFIX/lib -Wl,-s" \
108+
--without-ensurepip
109+
$MAKE -j$CORES
110+
$MAKE install
111+
exit 0
112+
fi
113+
114+
# Fetching
115+
if [ ! -d cpython-mingw ]; then
116+
git clone https://github.com/msys2-contrib/cpython-mingw.git
117+
CHECKOUT_PYTHON=1
118+
fi
119+
120+
if [ -n "$SYNC" ] || [ -n "$CHECKOUT_PYTHON" ]; then
121+
cd cpython-mingw
122+
[ -z "$SYNC" ] || git fetch
123+
git checkout $PYTHON_VERSION_MINGW
124+
cat ../patches/python/*.patch | patch -Nup1
125+
autoreconf -vfi
126+
cd ..
127+
fi
128+
129+
[ -z "$CHECKOUT_ONLY" ] || exit 0
130+
131+
cd libffi
132+
[ -z "$CLEAN" ] || rm -rf $BUILDDIR
133+
mkdir -p $BUILDDIR
134+
cd $BUILDDIR
135+
../configure --prefix="$PREFIX" --host=$HOST --disable-symvers --disable-docs
136+
$MAKE -j$CORES
137+
$MAKE install
138+
cd ../..
139+
140+
cd cpython-mingw
141+
[ -z "$CLEAN" ] || rm -rf $BUILDDIR
142+
mkdir -p $BUILDDIR
143+
cd $BUILDDIR
144+
BUILD=$(../config.guess) # Python configure requires build triplet for cross compilation
145+
146+
# Avoid gcc workarounds in distutils
147+
export CC=$HOST-clang
148+
export CXX=$HOST-clang++
149+
150+
../configure --prefix="$PREFIX" --build=$BUILD --host=$HOST \
151+
CFLAGS="-I$PREFIX/include" CXXFLAGS="-I$PREFIX/include" LDFLAGS="-L$PREFIX/lib -Wl,-s" \
152+
--enable-shared \
153+
--with-system-ffi \
154+
--without-ensurepip \
155+
--without-c-locale-coercion
156+
157+
$MAKE -j$CORES
158+
$MAKE install
159+
rm -rf $PREFIX/lib/python*/test
160+
find $PREFIX/lib/python* -name __pycache__ | xargs rm -rf
161+
cd ../..
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
diff --git a/Lib/distutils/cygwinccompiler.py b/Lib/distutils/cygwinccompiler.py
2+
index 66c12dd..2f76fb2 100644
3+
--- a/Lib/distutils/cygwinccompiler.py
4+
+++ b/Lib/distutils/cygwinccompiler.py
5+
@@ -44,6 +44,8 @@ cygwin in no-cygwin mode).
6+
# (ld supports -shared)
7+
# * mingw gcc 3.2/ld 2.13 works
8+
# (ld supports -shared)
9+
+# * llvm-mingw with Clang 11 works
10+
+# (lld supports -shared)
11+
12+
import os
13+
import sys
14+
@@ -109,41 +111,46 @@ class CygwinCCompiler(UnixCCompiler):
15+
"Compiling may fail because of undefined preprocessor macros."
16+
% details)
17+
18+
- self.gcc_version, self.ld_version, self.dllwrap_version = \
19+
- get_versions()
20+
- self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" %
21+
- (self.gcc_version,
22+
- self.ld_version,
23+
- self.dllwrap_version) )
24+
-
25+
- # ld_version >= "2.10.90" and < "2.13" should also be able to use
26+
- # gcc -mdll instead of dllwrap
27+
- # Older dllwraps had own version numbers, newer ones use the
28+
- # same as the rest of binutils ( also ld )
29+
- # dllwrap 2.10.90 is buggy
30+
- if self.ld_version >= "2.10.90":
31+
- self.linker_dll = "gcc"
32+
- else:
33+
- self.linker_dll = "dllwrap"
34+
+ self.cc = os.environ.get('CC', 'gcc')
35+
+ self.cxx = os.environ.get('CXX', 'g++')
36+
+
37+
+ if ('gcc' in self.cc): # Start gcc workaround
38+
+ self.gcc_version, self.ld_version, self.dllwrap_version = \
39+
+ get_gcc_versions()
40+
+ self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" %
41+
+ (self.gcc_version,
42+
+ self.ld_version,
43+
+ self.dllwrap_version) )
44+
+
45+
+ # ld_version >= "2.10.90" and < "2.13" should also be able to use
46+
+ # gcc -mdll instead of dllwrap
47+
+ # Older dllwraps had own version numbers, newer ones use the
48+
+ # same as the rest of binutils ( also ld )
49+
+ # dllwrap 2.10.90 is buggy
50+
+ if self.ld_version >= "2.10.90":
51+
+ self.linker_dll = self.cc
52+
+ else:
53+
+ self.linker_dll = "dllwrap"
54+
55+
- # ld_version >= "2.13" support -shared so use it instead of
56+
- # -mdll -static
57+
- if self.ld_version >= "2.13":
58+
+ # ld_version >= "2.13" support -shared so use it instead of
59+
+ # -mdll -static
60+
+ if self.ld_version >= "2.13":
61+
+ shared_option = "-shared"
62+
+ else:
63+
+ shared_option = "-mdll -static"
64+
+ else: # Assume linker is up to date
65+
+ self.linker_dll = self.cc
66+
shared_option = "-shared"
67+
- else:
68+
- shared_option = "-mdll -static"
69+
70+
- # Hard-code GCC because that's what this is all about.
71+
- # XXX optimization, warnings etc. should be customizable.
72+
- self.set_executables(compiler='gcc -mcygwin -O -Wall',
73+
- compiler_so='gcc -mcygwin -mdll -O -Wall',
74+
- compiler_cxx='g++ -mcygwin -O -Wall',
75+
- linker_exe='gcc -mcygwin',
76+
+ self.set_executables(compiler='%s -mcygwin -O -Wall' % self.cc,
77+
+ compiler_so='%s -mcygwin -mdll -O -Wall' % self.cc,
78+
+ compiler_cxx='%s -mcygwin -O -Wall' % self.cxx,
79+
+ linker_exe='%s -mcygwin' % self.cc,
80+
linker_so=('%s -mcygwin %s' %
81+
(self.linker_dll, shared_option)))
82+
83+
# cygwin and mingw32 need different sets of libraries
84+
- if self.gcc_version == "2.91.57":
85+
+ if ('gcc' in self.cc and self.gcc_version == "2.91.57"):
86+
# cygwin shouldn't need msvcrt, but without the dlls will crash
87+
# (gcc version 2.91.57) -- perhaps something about initialization
88+
self.dll_libraries=["msvcrt"]
89+
@@ -281,26 +288,26 @@ class Mingw32CCompiler(CygwinCCompiler):
90+
91+
# ld_version >= "2.13" support -shared so use it instead of
92+
# -mdll -static
93+
- if self.ld_version >= "2.13":
94+
- shared_option = "-shared"
95+
- else:
96+
+ if ('gcc' in self.cc and self.ld_version < "2.13"):
97+
shared_option = "-mdll -static"
98+
+ else:
99+
+ shared_option = "-shared"
100+
101+
# A real mingw32 doesn't need to specify a different entry point,
102+
# but cygwin 2.91.57 in no-cygwin-mode needs it.
103+
- if self.gcc_version <= "2.91.57":
104+
+ if ('gcc' in self.cc and self.gcc_version <= "2.91.57"):
105+
entry_point = '--entry _DllMain@12'
106+
else:
107+
entry_point = ''
108+
109+
- if is_cygwingcc():
110+
+ if is_cygwincc(self.cc):
111+
raise CCompilerError(
112+
'Cygwin gcc cannot be used with --compiler=mingw32')
113+
114+
- self.set_executables(compiler='gcc -O2 -Wall',
115+
- compiler_so='gcc -mdll -O2 -Wall',
116+
- compiler_cxx='g++ -O2 -Wall',
117+
- linker_exe='gcc',
118+
+ self.set_executables(compiler='%s -O2 -Wall' % self.cc,
119+
+ compiler_so='%s -mdll -O2 -Wall' % self.cc,
120+
+ compiler_cxx='%s -O2 -Wall' % self.cxx,
121+
+ linker_exe='%s' % self.cc,
122+
linker_so='%s %s %s'
123+
% (self.linker_dll, shared_option,
124+
entry_point))
125+
@@ -351,6 +358,10 @@ def check_config_h():
126+
if "GCC" in sys.version:
127+
return CONFIG_H_OK, "sys.version mentions 'GCC'"
128+
129+
+ # Clang would also work
130+
+ if "Clang" in sys.version:
131+
+ return CONFIG_H_OK, "sys.version mentions 'Clang'"
132+
+
133+
# let's see if __GNUC__ is mentioned in python.h
134+
fn = sysconfig.get_config_h_filename()
135+
try:
136+
@@ -389,7 +400,7 @@ def _find_exe_version(cmd):
137+
# so we need to decode our bytes
138+
return LooseVersion(result.group(1).decode())
139+
140+
-def get_versions():
141+
+def get_gcc_versions():
142+
""" Try to find out the versions of gcc, ld and dllwrap.
143+
144+
If not possible it returns None for it.
145+
@@ -397,7 +408,7 @@ def get_versions():
146+
commands = [gcc+' -dumpversion', ld+' -v', 'dllwrap --version']
147+
return tuple([_find_exe_version(cmd) for cmd in commands])
148+
149+
-def is_cygwingcc():
150+
- '''Try to determine if the gcc that would be used is from cygwin.'''
151+
- out_string = check_output(['gcc', '-dumpmachine'])
152+
+def is_cygwincc(cc):
153+
+ '''Try to determine if the compiler that would be used is from cygwin.'''
154+
+ out_string = check_output([cc, '-dumpmachine'])
155+
return out_string.strip().endswith(b'cygwin')
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py
2+
index dbcd9d1..274b1d8 100644
3+
--- a/Lib/distutils/command/build_ext.py
4+
+++ b/Lib/distutils/command/build_ext.py
5+
@@ -219,7 +219,7 @@ class build_ext(Command):
6+
# For extensions under Cygwin, Python's library directory must be
7+
# appended to library_dirs
8+
if sys.platform[:6] == 'cygwin' or self.plat_name.startswith(('mingw')):
9+
- if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")):
10+
+ if not sysconfig.python_build:
11+
# building third party extensions
12+
self.library_dirs.append(os.path.join(sys.prefix, "lib",
13+
"python" + get_python_version(),

0 commit comments

Comments
 (0)