diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py index bce840274dc..9833bf95c05 100644 --- a/Lib/distutils/util.py +++ b/Lib/distutils/util.py @@ -53,6 +53,10 @@ def get_platform (): return 'win-ia64' return sys.platform + # Set for cross builds explicitly + if "_PYTHON_HOST_PLATFORM" in os.environ: + return os.environ["_PYTHON_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 --git a/Lib/sysconfig.py b/Lib/sysconfig.py index 2f2fac287b0..eccd3041a4e 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -138,6 +138,10 @@ def _safe_realpath(path): 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 @@ def get_platform(): # Mac OS is M68k or PPC, etc. return sys.platform + # Set for cross builds explicitly + if "_PYTHON_HOST_PLATFORM" in os.environ: + return os.environ["_PYTHON_HOST_PLATFORM"] + # Try to distinguish various flavours of Unix osname, host, release, version, machine = os.uname() diff --git a/Makefile.pre.in b/Makefile.pre.in index 29a42df7783..9c92b512395 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -194,6 +194,10 @@ LIBOBJS= @LIBOBJS@ PYTHON= python$(EXE) BUILDPYTHON= python$(BUILDEXE) +PYTHON_FOR_BUILD=@PYTHON_FOR_BUILD@ +_PYTHON_HOST_PLATFORM=@_PYTHON_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 @@ -446,6 +450,7 @@ build_all_generate_profile: $(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: @@ -462,18 +467,17 @@ $(BUILDPYTHON): Modules/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) $(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 @@ -1073,25 +1077,25 @@ libinstall: build_all $(srcdir)/Lib/$(PLATDIR) $(srcdir)/Modules/xxmodule.c $(DESTDIR)$(LIBDEST)/distutils/tests ; \ fi -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ - ./$(BUILDPYTHON) -Wi $(DESTDIR)$(LIBDEST)/compileall.py \ + $(PYTHON_FOR_BUILD) -Wi $(DESTDIR)$(LIBDEST)/compileall.py \ -d $(LIBDEST) -f \ -x 'bad_coding|badsyntax|site-packages|lib2to3/tests/data' \ $(DESTDIR)$(LIBDEST) -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ - ./$(BUILDPYTHON) -Wi -O $(DESTDIR)$(LIBDEST)/compileall.py \ + $(PYTHON_FOR_BUILD) -Wi -O $(DESTDIR)$(LIBDEST)/compileall.py \ -d $(LIBDEST) -f \ -x 'bad_coding|badsyntax|site-packages|lib2to3/tests/data' \ $(DESTDIR)$(LIBDEST) -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ - ./$(BUILDPYTHON) -Wi $(DESTDIR)$(LIBDEST)/compileall.py \ + $(PYTHON_FOR_BUILD) -Wi $(DESTDIR)$(LIBDEST)/compileall.py \ -d $(LIBDEST)/site-packages -f \ -x badsyntax $(DESTDIR)$(LIBDEST)/site-packages -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ - ./$(BUILDPYTHON) -Wi -O $(DESTDIR)$(LIBDEST)/compileall.py \ + $(PYTHON_FOR_BUILD) -Wi -O $(DESTDIR)$(LIBDEST)/compileall.py \ -d $(LIBDEST)/site-packages -f \ -x badsyntax $(DESTDIR)$(LIBDEST)/site-packages -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ - ./$(BUILDPYTHON) -Wi -c "import lib2to3.pygram, lib2to3.patcomp;lib2to3.patcomp.PatternCompiler()" + $(PYTHON_FOR_BUILD) -Wi -c "import lib2to3.pygram, lib2to3.patcomp;lib2to3.patcomp.PatternCompiler()" # Create the PLATDIR source directory, if one wasn't distributed.. $(srcdir)/Lib/$(PLATDIR): @@ -1185,7 +1189,7 @@ libainstall: all python-config # 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) \ @@ -1257,7 +1261,7 @@ frameworkinstallextras: # This installs a few of the useful scripts in Tools/scripts scriptsinstall: SRCDIR=$(srcdir) $(RUNSHARED) \ - ./$(BUILDPYTHON) $(srcdir)/Tools/scripts/setup.py install \ + $(PYTHON_FOR_BUILD) $(srcdir)/Tools/scripts/setup.py install \ --prefix=$(prefix) \ --install-scripts=$(BINDIR) \ --root=$(DESTDIR)/ diff --git a/configure b/configure index de4096125d6..816b8e89279 100755 --- a/configure +++ b/configure @@ -682,6 +682,7 @@ CC EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET SGI_ABI +_PYTHON_HOST_PLATFORM MACHDEP FRAMEWORKINSTALLAPPSPREFIX FRAMEWORKUNIXTOOLSPREFIX @@ -700,6 +701,7 @@ UNIVERSALSDK CONFIG_ARGS SOVERSION VERSION +PYTHON_FOR_BUILD host_os host_vendor host_cpu @@ -2880,6 +2882,29 @@ case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac +if test "$cross_compiling" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for python interpreter for cross build" >&5 +$as_echo_n "checking for python interpreter for cross build... " >&6; } + 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 + as_fn_error $? "python$PACKAGE_VERSION interpreter not found" "$LINENO" 5 + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $interp" >&5 +$as_echo "$interp" >&6; } + PYTHON_FOR_BUILD="_PROJECT_BASE=$srcdir"' _PYTHON_HOST_PLATFORM=$(_PYTHON_HOST_PLATFORM) PYTHONPATH=$(srcdir)/Lib:$(srcdir)/Lib/plat-$(MACHDEP) '$interp + fi +else + PYTHON_FOR_BUILD='./$(BUILDPYTHON) -E' +fi + + if test "$prefix" != "/"; then prefix=`echo "$prefix" | sed -e 's/\/$//g'` @@ -3218,6 +3243,29 @@ then esac fi + +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" + as_fn_error $? "cross build not supported for $host" "$LINENO" 5 + esac + _PYTHON_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 # features as extensions. For these systems, we skip the definition of @@ -5540,6 +5588,10 @@ else # shared is disabled esac fi +if test "$cross_compiling" = yes; then + RUNSHARED= +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LDLIBRARY" >&5 $as_echo "$LDLIBRARY" >&6; } diff --git a/configure.ac b/configure.ac index c0ed0241744..91a588a58cc 100644 --- a/configure.ac +++ b/configure.ac @@ -35,6 +35,27 @@ AC_CONFIG_HEADER(pyconfig.h) 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"' _PYTHON_HOST_PLATFORM=$(_PYTHON_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 @@ then '') MACHDEP="unknown";; esac fi + +AC_SUBST(_PYTHON_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 + _PYTHON_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 @@ -913,6 +957,10 @@ else # shared is disabled esac fi +if test "$cross_compiling" = yes; then + RUNSHARED= +fi + AC_MSG_RESULT($LDLIBRARY) AC_PROG_RANLIB diff --git a/setup.py b/setup.py index 998fd3b2db9..30a7a8fee6b 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,12 @@ from distutils.command.build_scripts import build_scripts from distutils.spawn import find_executable +cross_compiling = "_PYTHON_HOST_PLATFORM" in os.environ + def get_platform(): + # cross build + if "_PYTHON_HOST_PLATFORM" in os.environ: + return os.environ["_PYTHON_HOST_PLATFORM"] # Get value of sys.platform if sys.platform.startswith('osf1'): return 'osf1' @@ -23,7 +28,7 @@ def get_platform(): 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 @@ def build_extension(self, ext): # 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 @@ def add_multiarch_paths(self): # 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 @@ def add_multiarch_paths(self): 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 @@ def detect_modules(self): # 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 = [] @@ -596,8 +649,6 @@ def detect_modules(self): os.makedirs(self.build_temp) # Determine if readline is already linked against curses or tinfo. if do_readline: - # FIXME: needs patch from issue #14330 - cross_compiling = False if cross_compiling: ret = os.system("%s -d %s | grep '(NEEDED)' > %s" \ % (sysconfig.get_config_var('READELF'), @@ -839,6 +890,9 @@ def gen_db_minor_ver_nums(major): db_inc_paths.append('/pkg/db-3.%d/include' % x) db_inc_paths.append('/opt/db-3.%d/include' % x) + if cross_compiling: + db_inc_paths = [] + # Add some common subdirectories for Sleepycat DB to the list, # based on the standard include directories. This way DB3/4 gets # picked up when it is installed in a non-standard prefix and @@ -975,7 +1029,9 @@ class db_found(Exception): pass '/usr/local/include', '/usr/local/include/sqlite', '/usr/local/include/sqlite3', - ] + ] + if cross_compiling: + sqlite_inc_paths = [] MIN_SQLITE_VERSION_NUMBER = (3, 0, 8) MIN_SQLITE_VERSION = ".".join([str(x) for x in MIN_SQLITE_VERSION_NUMBER]) @@ -1710,7 +1766,8 @@ def configure_ctypes(self, ext): 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.