├── .gitignore ├── README.md ├── build.sh └── files ├── Python-2.7.3-xcompile.patch ├── Setup └── config.site /.gitignore: -------------------------------------------------------------------------------- 1 | build.log 2 | Python-*/ 3 | Python-*.tar.xz 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | python-arm-xcompile 2 | =================== 3 | 4 | This is a build script and patches for cross-compiling Python to target the ARM architecture. 5 | 6 | You must have a cross-compile toolchain already set up. [This guide](http://akanto.wordpress.com/2012/10/02/cross-compiling-kernel-for-raspberry-pi-on-fedora-17-part-2/) is an excellent resource for setting up crosstool-ng. 7 | 8 | 1. Edit `build.sh` and change the variables at the top to match your environment. 9 | 2. Run `build.sh`. This will download Python and build it for you. 10 | 11 | Assuming the build succeeds, a list of modules will be printed out. Some modules 12 | will not build statically since they need to be dynamically linked to glibc (TODO). 13 | 14 | Python 2.7.4 unsupported 15 | ------------------------ 16 | 17 | Note that Python 2.7.4 introduced breaking changes to _sre.MAXREPEATS that will 18 | fail to cross-compile statically. You must use 2.7.3 instead (for now) -- 19 | `build.sh` will download and extract this for you. 20 | 21 | Credits 22 | ------- 23 | 24 | * The `files/Python-2.7.3-xcompile.patch` file is modified from the patch given by 25 | Lothsahn on the [Cross Compiling Python for Embedded Linux](http://randomsplat.com/id5-cross-compiling-python-for-embedded-linux.html) post. 26 | * http://stackoverflow.com/a/1155092 for statically compiling Python interpreter. 27 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # change these to match your environment 4 | TARGET_HOST="arm-unknown-linux-gnueabi" 5 | CROSS_TOOLS_PATH=~/arm-toolchain/tools/$TARGET_HOST/bin 6 | BUILD_HOST="x86_64-linux-gnu" 7 | 8 | # you shouldn't need to change these 9 | PYTHON="Python-2.7.3" 10 | CONFIGURE_ARGS="--disable-ipv6" 11 | BUILD_LOG="build.log" 12 | 13 | # build log goes here 14 | rm -f $BUILD_LOG 15 | touch $BUILD_LOG 16 | echo "Build output will be in $BUILD_LOG" 17 | 18 | # download dist if it doesn't already exist 19 | if [ ! -f $PYTHON.tar.xz ] ; then 20 | echo "Downloading $PYTHON .." 21 | wget http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tar.xz >> $BUILD_LOG 22 | fi 23 | 24 | rm -rf $PYTHON 25 | tar -xf $PYTHON.tar.xz 26 | cp -p files/config.site $PYTHON 27 | cd $PYTHON 28 | BUILD_LOG="../$BUILD_LOG" 29 | unset CROSS_COMPILE 30 | 31 | # ensure static glibc is installed 32 | rpm -qa | grep -q glibc-static 33 | if [ $? -eq 1 ] ; then 34 | echo "Installing glibc-static (with sudo yum install) .." 35 | sudo yum install -y glibc-static >> $BUILD_LOG 36 | fi 37 | 38 | set -e 39 | 40 | # first we need to build the host executables (python and Parser/pgen) 41 | echo "Stage 1: compiling host executables .." 42 | ./configure $CONFIGURE_ARGS CONFIG_SITE="config.site" >> $BUILD_LOG 43 | make python Parser/pgen >> $BUILD_LOG 44 | mv python hostpython 45 | mv Parser/pgen Parser/hostpgen 46 | make distclean 47 | 48 | # set up environment for cross compile - we really shouldn't blindly add to PATH 49 | export PATH="$PATH:$CROSS_TOOLS_PATH" 50 | export CROSS_COMPILE=arm-unknown-linux-gnueabi- 51 | 52 | echo "Stage 1.5: patching Python for cross-compile .." 53 | patch -p0 < ../files/Python-2.7.3-xcompile.patch 54 | 55 | # cross compile 56 | echo "Stage 2: cross-compiling for $TARGET_HOST .." 57 | ./configure $CONFIGURE_ARGS --build=$BUILD_HOST --host=$TARGET_HOST \ 58 | LDFLAGS="-static -static-libgcc" CPPFLAGS="-static" CONFIG_SITE="config.site" >> $BUILD_LOG 59 | sed -i '1r ../files/Setup' Modules/Setup 60 | make HOSTPYTHON=./hostpython HOSTPGEN=./Parser/hostpgen CROSS_COMPILE_TARGET=yes BUILDARCH=$BUILD_HOST HOSTARCH=$TARGET_HOST >> $BUILD_LOG 61 | 62 | sed -n -e '/Python build finished/,$p' $BUILD_LOG | grep -v 'install' 63 | file python 64 | -------------------------------------------------------------------------------- /files/Python-2.7.3-xcompile.patch: -------------------------------------------------------------------------------- 1 | diff -ur Python-2.7.3.orig/configure configure 2 | --- Python-2.7.3.orig/configure 2012-04-09 19:07:36.000000000 -0400 3 | +++ configure 2012-10-23 14:10:45.305220393 -0400 4 | @@ -13697,7 +13697,7 @@ 5 | $as_echo_n "(cached) " >&6 6 | else 7 | if test "$cross_compiling" = yes; then : 8 | - ac_cv_have_long_long_format=no 9 | + ac_cv_have_long_long_format="cross -- assuming yes" 10 | else 11 | cat confdefs.h - <<_ACEOF >conftest.$ac_ext 12 | /* end confdefs.h. */ 13 | @@ -13749,7 +13749,7 @@ 14 | $as_echo "$ac_cv_have_long_long_format" >&6; } 15 | fi 16 | 17 | -if test "$ac_cv_have_long_long_format" = yes 18 | +if test "$ac_cv_have_long_long_format" != no 19 | then 20 | 21 | $as_echo "#define PY_FORMAT_LONG_LONG \"ll\"" >>confdefs.h 22 | diff -ur Python-2.7.3.orig/Makefile.pre.in Makefile.pre.in 23 | --- Python-2.7.3.orig/Makefile.pre.in 2012-04-09 19:07:33.000000000 -0400 24 | +++ Makefile.pre.in 2012-10-23 14:10:45.305220393 -0400 25 | @@ -182,6 +182,7 @@ 26 | 27 | PYTHON= python$(EXE) 28 | BUILDPYTHON= python$(BUILDEXE) 29 | +HOSTPYTHON= ./$(BUILDPYTHON) 30 | 31 | # The task to run while instrument when building the profile-opt target 32 | PROFILE_TASK= $(srcdir)/Tools/pybench/pybench.py -n 2 --with-gc --with-syscheck 33 | @@ -215,6 +216,8 @@ 34 | # Parser 35 | PGEN= Parser/pgen$(EXE) 36 | 37 | +HOSTPGEN= $(PGEN) 38 | + 39 | POBJS= \ 40 | Parser/acceler.o \ 41 | Parser/grammar1.o \ 42 | @@ -408,8 +411,8 @@ 43 | # Build the shared modules 44 | sharedmods: $(BUILDPYTHON) 45 | @case $$MAKEFLAGS in \ 46 | - *s*) $(RUNSHARED) CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' ./$(BUILDPYTHON) -E $(srcdir)/setup.py -q build;; \ 47 | - *) $(RUNSHARED) CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' ./$(BUILDPYTHON) -E $(srcdir)/setup.py build;; \ 48 | + *s*) $(RUNSHARED) CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' PYTHONXCPREFIX='$(DESTDIR)$(prefix)' $(HOSTPYTHON) -E $(srcdir)/setup.py -q build;; \ 49 | + *) $(RUNSHARED) CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' PYTHONXCPREFIX='$(DESTDIR)$(prefix)' $(HOSTPYTHON) -E $(srcdir)/setup.py build;; \ 50 | esac 51 | 52 | # Build static library 53 | @@ -543,7 +546,7 @@ 54 | $(GRAMMAR_H) $(GRAMMAR_C): Parser/pgen.stamp 55 | Parser/pgen.stamp: $(PGEN) $(GRAMMAR_INPUT) 56 | -@$(INSTALL) -d Include 57 | - $(PGEN) $(GRAMMAR_INPUT) $(GRAMMAR_H) $(GRAMMAR_C) 58 | + -$(HOSTPGEN) $(GRAMMAR_INPUT) $(GRAMMAR_H) $(GRAMMAR_C) 59 | -touch Parser/pgen.stamp 60 | 61 | $(PGEN): $(PGENOBJS) 62 | @@ -938,26 +941,26 @@ 63 | $(INSTALL_DATA) $(srcdir)/Modules/xxmodule.c \ 64 | $(DESTDIR)$(LIBDEST)/distutils/tests ; \ 65 | fi 66 | - PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ 67 | - ./$(BUILDPYTHON) -Wi -tt $(DESTDIR)$(LIBDEST)/compileall.py \ 68 | + -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ 69 | + ./$(HOSTPYTHON) -Wi -tt $(DESTDIR)$(LIBDEST)/compileall.py \ 70 | -d $(LIBDEST) -f \ 71 | -x 'bad_coding|badsyntax|site-packages|lib2to3/tests/data' \ 72 | $(DESTDIR)$(LIBDEST) 73 | - PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ 74 | - ./$(BUILDPYTHON) -Wi -tt -O $(DESTDIR)$(LIBDEST)/compileall.py \ 75 | + -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ 76 | + ./$(HOSTPYTHON) -Wi -tt -O $(DESTDIR)$(LIBDEST)/compileall.py \ 77 | -d $(LIBDEST) -f \ 78 | -x 'bad_coding|badsyntax|site-packages|lib2to3/tests/data' \ 79 | $(DESTDIR)$(LIBDEST) 80 | -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ 81 | - ./$(BUILDPYTHON) -Wi -t $(DESTDIR)$(LIBDEST)/compileall.py \ 82 | + ./$(HOSTPYTHON) -Wi -t $(DESTDIR)$(LIBDEST)/compileall.py \ 83 | -d $(LIBDEST)/site-packages -f \ 84 | -x badsyntax $(DESTDIR)$(LIBDEST)/site-packages 85 | -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ 86 | - ./$(BUILDPYTHON) -Wi -t -O $(DESTDIR)$(LIBDEST)/compileall.py \ 87 | + ./$(HOSTPYTHON) -Wi -t -O $(DESTDIR)$(LIBDEST)/compileall.py \ 88 | -d $(LIBDEST)/site-packages -f \ 89 | -x badsyntax $(DESTDIR)$(LIBDEST)/site-packages 90 | -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ 91 | - ./$(BUILDPYTHON) -Wi -t -c "import lib2to3.pygram, lib2to3.patcomp;lib2to3.patcomp.PatternCompiler()" 92 | + ./$(HOSTPYTHON) -Wi -t -c "import lib2to3.pygram, lib2to3.patcomp;lib2to3.patcomp.PatternCompiler()" 93 | 94 | # Create the PLATDIR source directory, if one wasn't distributed.. 95 | $(srcdir)/Lib/$(PLATDIR): 96 | @@ -1062,7 +1065,9 @@ 97 | # Install the dynamically loadable modules 98 | # This goes into $(exec_prefix) 99 | sharedinstall: sharedmods 100 | - $(RUNSHARED) ./$(BUILDPYTHON) -E $(srcdir)/setup.py install \ 101 | + CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' \ 102 | + $(RUNSHARED) $(HOSTPYTHON) -E $(srcdir)/setup.py install \ 103 | + --skip-build \ 104 | --prefix=$(prefix) \ 105 | --install-scripts=$(BINDIR) \ 106 | --install-platlib=$(DESTSHARED) \ 107 | diff -ur Python-2.7.3.orig/setup.py setup.py 108 | --- Python-2.7.3.orig/setup.py 2012-04-09 19:07:36.000000000 -0400 109 | +++ setup.py 2012-10-23 14:10:56.085220237 -0400 110 | @@ -145,6 +145,7 @@ 111 | def __init__(self, dist): 112 | build_ext.__init__(self, dist) 113 | self.failed = [] 114 | + self.cross_compile = os.environ.get('CROSS_COMPILE_TARGET') == 'yes' 115 | 116 | def build_extensions(self): 117 | 118 | @@ -278,6 +279,14 @@ 119 | (ext.name, sys.exc_info()[1])) 120 | self.failed.append(ext.name) 121 | return 122 | + 123 | + # Import check will not work when cross-compiling. 124 | + if os.environ.has_key('PYTHONXCPREFIX'): 125 | + self.announce( 126 | + 'WARNING: skipping import check for cross-compiled: "%s"' % 127 | + ext.name) 128 | + return 129 | + 130 | # Workaround for Mac OS X: The Carbon-based modules cannot be 131 | # reliably imported into a command-line Python 132 | if 'Carbon' in ext.extra_link_args: 133 | @@ -369,9 +378,10 @@ 134 | 135 | def detect_modules(self): 136 | # Ensure that /usr/local is always used 137 | - add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib') 138 | - add_dir_to_list(self.compiler.include_dirs, '/usr/local/include') 139 | - self.add_multiarch_paths() 140 | + if not self.cross_compile: 141 | + add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib') 142 | + add_dir_to_list(self.compiler.include_dirs, '/usr/local/include') 143 | + self.add_multiarch_paths() 144 | 145 | # Add paths specified in the environment variables LDFLAGS and 146 | # CPPFLAGS for header and library files. 147 | @@ -408,7 +418,8 @@ 148 | add_dir_to_list(dir_list, directory) 149 | 150 | if os.path.normpath(sys.prefix) != '/usr' \ 151 | - and not sysconfig.get_config_var('PYTHONFRAMEWORK'): 152 | + and not sysconfig.get_config_var('PYTHONFRAMEWORK') \ 153 | + and not self.cross_compile: 154 | # OSX note: Don't add LIBDIR and INCLUDEDIR to building a framework 155 | # (PYTHONFRAMEWORK is set) to avoid # linking problems when 156 | # building a framework with different architectures than 157 | @@ -426,11 +437,23 @@ 158 | # lib_dirs and inc_dirs are used to search for files; 159 | # if a file is found in one of those directories, it can 160 | # be assumed that no additional -I,-L directives are needed. 161 | - lib_dirs = self.compiler.library_dirs + [ 162 | - '/lib64', '/usr/lib64', 163 | - '/lib', '/usr/lib', 164 | - ] 165 | - inc_dirs = self.compiler.include_dirs + ['/usr/include'] 166 | + lib_dirs = self.compiler.library_dirs 167 | + inc_dirs = self.compiler.include_dirs 168 | + if not self.cross_compile: 169 | + lib_dirs += [ 170 | + '/lib64', '/usr/lib64', 171 | + '/lib', '/usr/lib', 172 | + ] 173 | + inc_dirs += ['/usr/include'] 174 | + else: 175 | + # The common install prefix of 3rd party libraries used during 176 | + # cross compilation 177 | + mydir = os.environ.get('PYTHON_XCOMPILE_DEPENDENCIES_PREFIX') 178 | + if mydir: 179 | + inc_dirs += [mydir + '/include' ] 180 | + inc_dirs += [mydir + '/lib/libffi-3.0.10/include'] 181 | + lib_dirs += [mydir + '/lib' ] 182 | + 183 | exts = [] 184 | missing = [] 185 | 186 | @@ -1004,13 +1027,24 @@ 187 | # We hunt for #define SQLITE_VERSION "n.n.n" 188 | # We need to find >= sqlite version 3.0.8 189 | sqlite_incdir = sqlite_libdir = None 190 | - sqlite_inc_paths = [ '/usr/include', 191 | - '/usr/include/sqlite', 192 | - '/usr/include/sqlite3', 193 | - '/usr/local/include', 194 | - '/usr/local/include/sqlite', 195 | - '/usr/local/include/sqlite3', 196 | - ] 197 | + 198 | + if not self.cross_compile: 199 | + sqlite_inc_paths = [ '/usr/include', 200 | + '/usr/include/sqlite', 201 | + '/usr/include/sqlite3', 202 | + '/usr/local/include', 203 | + '/usr/local/include/sqlite', 204 | + '/usr/local/include/sqlite3', 205 | + ] 206 | + else: 207 | + # The common install prefix of 3rd party headers used during 208 | + # cross compilation 209 | + mydir = os.environ.get('PYTHON_XCOMPILE_DEPENDENCIES_PREFIX') 210 | + if mydir: 211 | + sqlite_inc_paths = [mydir + '/include' ] 212 | + else: 213 | + sqlite_inc_paths = [] 214 | + 215 | MIN_SQLITE_VERSION_NUMBER = (3, 0, 8) 216 | MIN_SQLITE_VERSION = ".".join([str(x) 217 | for x in MIN_SQLITE_VERSION_NUMBER]) 218 | @@ -1050,12 +1084,22 @@ 219 | print "sqlite: %s had no SQLITE_VERSION"%(f,) 220 | 221 | if sqlite_incdir: 222 | - sqlite_dirs_to_check = [ 223 | - os.path.join(sqlite_incdir, '..', 'lib64'), 224 | - os.path.join(sqlite_incdir, '..', 'lib'), 225 | - os.path.join(sqlite_incdir, '..', '..', 'lib64'), 226 | - os.path.join(sqlite_incdir, '..', '..', 'lib'), 227 | - ] 228 | + if not self.cross_compile: 229 | + sqlite_dirs_to_check = [ 230 | + os.path.join(sqlite_incdir, '..', 'lib64'), 231 | + os.path.join(sqlite_incdir, '..', 'lib'), 232 | + os.path.join(sqlite_incdir, '..', '..', 'lib64'), 233 | + os.path.join(sqlite_incdir, '..', '..', 'lib'), 234 | + ] 235 | + else: 236 | + # The common install prefix of 3rd party headers used during 237 | + # cross compilation 238 | + mydir = os.environ.get('PYTHON_XCOMPILE_DEPENDENCIES_PREFIX') 239 | + if mydir: 240 | + sqlite_dirs_to_check = [mydir + '/lib' ] 241 | + else: 242 | + sqlite_dirs_to_check = [] 243 | + 244 | sqlite_libfile = self.compiler.find_library_file( 245 | sqlite_dirs_to_check + lib_dirs, 'sqlite3') 246 | if sqlite_libfile: 247 | @@ -1864,8 +1908,15 @@ 248 | 249 | # Pass empty CFLAGS because we'll just append the resulting 250 | # CFLAGS to Python's; -g or -O2 is to be avoided. 251 | - cmd = "cd %s && env CFLAGS='' '%s/configure' %s" \ 252 | - % (ffi_builddir, ffi_srcdir, " ".join(config_args)) 253 | + if self.cross_compile: 254 | + cmd = "cd %s && env CFLAGS='' %s/configure --host=%s --build=%s %s" \ 255 | + % (ffi_builddir, ffi_srcdir, 256 | + os.environ.get('HOSTARCH'), 257 | + os.environ.get('BUILDARCH'), 258 | + " ".join(config_args)) 259 | + else: 260 | + cmd = "cd %s && env CFLAGS='' '%s/configure' %s" \ 261 | + % (ffi_builddir, ffi_srcdir, " ".join(config_args)) 262 | 263 | res = os.system(cmd) 264 | if res or not os.path.exists(ffi_configfile): 265 | -------------------------------------------------------------------------------- /files/Setup: -------------------------------------------------------------------------------- 1 | *static* 2 | -------------------------------------------------------------------------------- /files/config.site: -------------------------------------------------------------------------------- 1 | ac_cv_file__dev_ptmx=no 2 | ac_cv_file__dev_ptc=no 3 | --------------------------------------------------------------------------------