diff -r af15910d2ffb Lib/distutils/util.py --- a/Lib/distutils/util.py Sat Jun 30 14:42:46 2012 +0200 +++ b/Lib/distutils/util.py Sat Jun 30 15:24:08 2012 +0200 @@ -53,6 +53,10 @@ return 'win-ia64' return sys.platform + # Set for cross builds explicitly + if "_HOST_PLATFORM" in os.environ: + return os.environ["_HOST_PLATFORM"] + if os.name != "posix" or not hasattr(os, 'uname'): # XXX what about the architecture? NT is Intel or Alpha, # Mac OS is M68k or PPC, etc. diff -r af15910d2ffb Lib/sysconfig.py --- a/Lib/sysconfig.py Sat Jun 30 14:42:46 2012 +0200 +++ b/Lib/sysconfig.py Sat Jun 30 15:24:08 2012 +0200 @@ -138,6 +138,10 @@ if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower(): _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) +# set for cross builds +if "_PROJECT_BASE" in os.environ: + _PROJECT_BASE = _safe_realpath(os.environ["_PROJECT_BASE"]) + def _is_python_source_dir(d): for fn in ("Setup.dist", "Setup.local"): if os.path.isfile(os.path.join(d, "Modules", fn)): @@ -673,6 +677,10 @@ # Mac OS is M68k or PPC, etc. return sys.platform + # Set for cross builds explicitly + if "_HOST_PLATFORM" in os.environ: + return os.environ["_HOST_PLATFORM"] + # Try to distinguish various flavours of Unix osname, host, release, version, machine = os.uname() diff -r af15910d2ffb Makefile.pre.in --- a/Makefile.pre.in Sat Jun 30 14:42:46 2012 +0200 +++ b/Makefile.pre.in Sat Jun 30 15:24:08 2012 +0200 @@ -193,6 +193,10 @@ PYTHON= python$(EXE) BUILDPYTHON= python$(BUILDEXE) +PYTHON_FOR_BUILD=@PYTHON_FOR_BUILD@ +_HOST_PLATFORM=@_HOST_PLATFORM@ +HOST_GNU_TYPE= @host@ + # The task to run while instrument when building the profile-opt target PROFILE_TASK= $(srcdir)/Tools/pybench/pybench.py -n 2 --with-gc --with-syscheck #PROFILE_TASK= $(srcdir)/Lib/test/regrtest.py @@ -445,6 +449,7 @@ $(MAKE) all CFLAGS="$(CFLAGS) -fprofile-generate" LIBS="$(LIBS) -lgcov" run_profile_task: + : # FIXME: can't run for a cross build $(RUNSHARED) ./$(BUILDPYTHON) $(PROFILE_TASK) build_all_use_profile: @@ -461,18 +466,17 @@ $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ Modules/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) platform: $(BUILDPYTHON) $(SYSCONFIGDATA) - $(RUNSHARED) ./$(BUILDPYTHON) -E -c 'import sys ; from sysconfig import get_platform ; print(get_platform()+"-"+sys.version[0:3])' >platform + $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print(get_platform()+"-"+sys.version[0:3])' >platform # Generate the sysconfig build-time data $(SYSCONFIGDATA): $(BUILDPYTHON) - $(RUNSHARED) ./$(BUILDPYTHON) -SE -m sysconfig --generate-posix-vars + $(RUNSHARED) $(PYTHON_FOR_BUILD) -S -m sysconfig --generate-posix-vars # Build the shared modules sharedmods: $(BUILDPYTHON) $(SYSCONFIGDATA) - @case $$MAKEFLAGS in \ - *s*) $(RUNSHARED) CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' ./$(BUILDPYTHON) -E $(srcdir)/setup.py -q build;; \ - *) $(RUNSHARED) CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' ./$(BUILDPYTHON) -E $(srcdir)/setup.py build;; \ - esac + case $$MAKEFLAGS in *s*) quiet=-q; esac; \ + $(RUNSHARED) CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' \ + $(PYTHON_FOR_BUILD) $(srcdir)/setup.py $$quiet build # Build static library # avoid long command lines, same as LIBRARY_OBJS @@ -1184,7 +1188,7 @@ # Install the dynamically loadable modules # This goes into $(exec_prefix) sharedinstall: sharedmods - $(RUNSHARED) ./$(BUILDPYTHON) -E $(srcdir)/setup.py install \ + $(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/setup.py install \ --prefix=$(prefix) \ --install-scripts=$(BINDIR) \ --install-platlib=$(DESTSHARED) \ diff -r af15910d2ffb configure.ac --- a/configure.ac Sat Jun 30 14:42:46 2012 +0200 +++ b/configure.ac Sat Jun 30 15:24:08 2012 +0200 @@ -35,6 +35,27 @@ AC_CANONICAL_HOST +if test "$cross_compiling" = yes; then + AC_MSG_CHECKING([for python interpreter for cross build]) + if test -z "$PYTHON_FOR_BUILD"; then + for interp in python$PACKAGE_VERSION python3 python; do + which $interp >/dev/null 2>&1 || continue + if $interp -c 'import sys;sys.exit(not sys.version_info@<:@:2@:>@ >= (3,3))'; then + break + fi + interp= + done + if test x$interp = x; then + AC_MSG_ERROR([python$PACKAGE_VERSION interpreter not found]) + fi + AC_MSG_RESULT($interp) + PYTHON_FOR_BUILD="_PROJECT_BASE=$srcdir"' _HOST_PLATFORM=$(_HOST_PLATFORM) PYTHONPATH=$(srcdir)/Lib:$(srcdir)/Lib/plat-$(MACHDEP) '$interp + fi +else + PYTHON_FOR_BUILD='./$(BUILDPYTHON) -E' +fi +AC_SUBST(PYTHON_FOR_BUILD) + dnl Ensure that if prefix is specified, it does not end in a slash. If dnl it does, we get path names containing '//' which is both ugly and dnl can cause trouble. @@ -352,6 +373,29 @@ '') MACHDEP="unknown";; esac fi + +AC_SUBST(_HOST_PLATFORM) +if test "$cross_compiling" = yes; then + case "$host" in + *-*-linux*) + case "$host_cpu" in + arm*) + _host_cpu=arm + ;; + *) + _host_cpu=$host_cpu + esac + ;; + *-*-cygwin*) + _host_cpu= + ;; + *) + # for now, limit cross builds to known configurations + MACHDEP="unknown" + AC_MSG_ERROR([cross build not supported for $host]) + esac + _HOST_PLATFORM="$MACHDEP${_host_cpu:+-$_host_cpu}" +fi # Some systems cannot stand _XOPEN_SOURCE being defined at all; they # disable features if it is defined, without any means to access these @@ -912,6 +956,10 @@ esac fi +if test "$cross_compiling" = yes; then + RUNSHARED= +fi + AC_MSG_RESULT($LDLIBRARY) AC_PROG_RANLIB diff -r af15910d2ffb setup.py --- a/setup.py Sat Jun 30 14:42:46 2012 +0200 +++ b/setup.py Sat Jun 30 15:24:08 2012 +0200 @@ -15,7 +15,12 @@ from distutils.command.build_scripts import build_scripts from distutils.spawn import find_executable +cross_compiling = "_HOST_PLATFORM" in os.environ + def get_platform(): + # cross build + if "_HOST_PLATFORM" in os.environ: + return os.environ["_HOST_PLATFORM"] # Get value of sys.platform if sys.platform.startswith('osf1'): return 'osf1' @@ -23,7 +28,7 @@ host_platform = get_platform() # Were we compiled --with-pydebug or with #define Py_DEBUG? -COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount') +COMPILED_WITH_PYDEBUG = ('--with-pydebug' in sysconfig.get_config_var("CONFIG_ARGS")) # This global variable is used to hold the list of modules to be disabled. disabled_module_list = [] @@ -334,6 +339,10 @@ # cached. Clear that cache before trying to import. sys.path_importer_cache.clear() + # Don't try to load extensions for cross builds + if cross_compiling: + return + try: imp.load_dynamic(ext.name, ext_filename) except ImportError as why: @@ -370,12 +379,15 @@ # https://wiki.ubuntu.com/MultiarchSpec if not find_executable('dpkg-architecture'): return + opt = '' + if cross_compiling: + opt = '-t' + sysconfig.get_config_var('HOST_GNU_TYPE') tmpfile = os.path.join(self.build_temp, 'multiarch') if not os.path.exists(self.build_temp): os.makedirs(self.build_temp) ret = os.system( - 'dpkg-architecture -qDEB_HOST_MULTIARCH > %s 2> /dev/null' % - tmpfile) + 'dpkg-architecture %s -qDEB_HOST_MULTIARCH > %s 2> /dev/null' % + (opt, tmpfile)) try: if ret >> 8 == 0: with open(tmpfile) as fp: @@ -387,12 +399,46 @@ finally: os.unlink(tmpfile) + def add_gcc_paths(self): + gcc = sysconfig.get_config_var('CC') + tmpfile = os.path.join(self.build_temp, 'gccpaths') + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + ret = os.system('%s -E -v - %s 1>/dev/null' % (gcc, tmpfile)) + is_gcc = False + in_incdirs = False + inc_dirs = [] + lib_dirs = [] + try: + if ret >> 8 == 0: + with open(tmpfile) as fp: + for line in fp.readlines(): + if line.startswith("gcc version"): + is_gcc = True + elif line.startswith("#include <...>"): + in_incdirs = True + elif line.startswith("End of search list"): + in_incdirs = False + elif is_gcc and line.startswith("LIBRARY_PATH"): + for d in line.strip().split("=")[1].split(":"): + d = os.path.normpath(d) + if '/gcc/' not in d: + add_dir_to_list(self.compiler.library_dirs, + d) + elif is_gcc and in_incdirs and '/gcc/' not in line: + add_dir_to_list(self.compiler.include_dirs, + line.strip()) + finally: + os.unlink(tmpfile) + def detect_modules(self): # Ensure that /usr/local is always used, but the local build # directories (i.e. '.' and 'Include') must be first. See issue # 10520. - add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib') - add_dir_to_list(self.compiler.include_dirs, '/usr/local/include') + if not cross_compiling: + add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib') + add_dir_to_list(self.compiler.include_dirs, '/usr/local/include') + self.add_gcc_paths() self.add_multiarch_paths() # Add paths specified in the environment variables LDFLAGS and @@ -443,11 +489,18 @@ # lib_dirs and inc_dirs are used to search for files; # if a file is found in one of those directories, it can # be assumed that no additional -I,-L directives are needed. - lib_dirs = self.compiler.library_dirs + [ - '/lib64', '/usr/lib64', - '/lib', '/usr/lib', - ] - inc_dirs = self.compiler.include_dirs + ['/usr/include'] + inc_dirs = self.compiler.include_dirs[:] + lib_dirs = self.compiler.library_dirs[:] + if not cross_compiling: + for d in ( + '/usr/include', + ): + add_dir_to_list(inc_dirs, d) + for d in ( + '/lib64', '/usr/lib64', + '/lib', '/usr/lib', + ): + add_dir_to_list(lib_dirs, d) exts = [] missing = [] @@ -1701,7 +1754,8 @@ ffi_configfile): from distutils.dir_util import mkpath mkpath(ffi_builddir) - config_args = [] + config_args = [arg for arg in sysconfig.get_config_var("CONFIG_ARGS").split() + if (('--host=' in arg) or ('--build=' in arg))] # Pass empty CFLAGS because we'll just append the resulting # CFLAGS to Python's; -g or -O2 is to be avoided.