Issue #2513: enable 64bit cross compilation on windows.
diff --git a/Lib/distutils/command/bdist.py b/Lib/distutils/command/bdist.py
index d6897d2..ca3da74 100644
--- a/Lib/distutils/command/bdist.py
+++ b/Lib/distutils/command/bdist.py
@@ -97,7 +97,10 @@
     def finalize_options (self):
         # have to finalize 'plat_name' before 'bdist_base'
         if self.plat_name is None:
-            self.plat_name = get_platform()
+            if self.skip_build:
+                self.plat_name = get_platform()
+            else:
+                self.plat_name = self.get_finalized_command('build').plat_name
 
         # 'bdist_base' -- parent of per-built-distribution-format
         # temporary directories (eg. we'll probably have
@@ -121,7 +124,6 @@
 
     # finalize_options()
 
-
     def run (self):
 
         # Figure out which sub-commands we need to run.
diff --git a/Lib/distutils/command/bdist_msi.py b/Lib/distutils/command/bdist_msi.py
index a401452..f94d957 100644
--- a/Lib/distutils/command/bdist_msi.py
+++ b/Lib/distutils/command/bdist_msi.py
@@ -9,11 +9,11 @@
 
 import sys, os
 from distutils.core import Command
-from distutils.util import get_platform
 from distutils.dir_util import remove_tree
 from distutils.sysconfig import get_python_version
 from distutils.version import StrictVersion
 from distutils.errors import DistutilsOptionError
+from distutils.util import get_platform
 from distutils import log
 import msilib
 from msilib import schema, sequence, text
@@ -87,6 +87,9 @@
 
     user_options = [('bdist-dir=', None,
                      "temporary directory for creating the distribution"),
+                    ('plat-name=', 'p',
+                     "platform name to embed in generated filenames "
+                     "(default: %s)" % get_platform()),
                     ('keep-temp', 'k',
                      "keep the pseudo-installation tree around after " +
                      "creating the distribution archive"),
@@ -116,6 +119,7 @@
 
     def initialize_options (self):
         self.bdist_dir = None
+        self.plat_name = None
         self.keep_temp = 0
         self.no_target_compile = 0
         self.no_target_optimize = 0
@@ -139,7 +143,10 @@
         else:
             self.target_version = short_version
 
-        self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
+        self.set_undefined_options('bdist',
+                                   ('dist_dir', 'dist_dir'),
+                                   ('plat_name', 'plat_name'),
+                                   )
 
         if self.pre_install_script:
             raise DistutilsOptionError, "the pre-install-script feature is not yet implemented"
@@ -181,7 +188,7 @@
             if not target_version:
                 assert self.skip_build, "Should have already checked this"
                 target_version = sys.version[0:3]
-            plat_specifier = ".%s-%s" % (get_platform(), target_version)
+            plat_specifier = ".%s-%s" % (self.plat_name, target_version)
             build = self.get_finalized_command('build')
             build.build_lib = os.path.join(build.build_base,
                                            'lib' + plat_specifier)
@@ -633,8 +640,7 @@
 
     def get_installer_filename(self, fullname):
         # Factored out to allow overriding in subclasses
-        plat = get_platform()
-        installer_name = os.path.join(self.dist_dir,
-                                      "%s.%s-py%s.msi" %
-                                       (fullname, plat, self.target_version))
+        base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name,
+                                        self.target_version)
+        installer_name = os.path.join(self.dist_dir, base_name)
         return installer_name
diff --git a/Lib/distutils/command/bdist_wininst.py b/Lib/distutils/command/bdist_wininst.py
index b0691fb..02542af 100644
--- a/Lib/distutils/command/bdist_wininst.py
+++ b/Lib/distutils/command/bdist_wininst.py
@@ -21,6 +21,9 @@
 
     user_options = [('bdist-dir=', None,
                      "temporary directory for creating the distribution"),
+                    ('plat-name=', 'p',
+                     "platform name to embed in generated filenames "
+                     "(default: %s)" % get_platform()),
                     ('keep-temp', 'k',
                      "keep the pseudo-installation tree around after " +
                      "creating the distribution archive"),
@@ -54,6 +57,7 @@
 
     def initialize_options (self):
         self.bdist_dir = None
+        self.plat_name = None
         self.keep_temp = 0
         self.no_target_compile = 0
         self.no_target_optimize = 0
@@ -82,7 +86,10 @@
                       " option must be specified" % (short_version,)
             self.target_version = short_version
 
-        self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
+        self.set_undefined_options('bdist',
+                                   ('dist_dir', 'dist_dir'),
+                                   ('plat_name', 'plat_name'),
+                                  )
 
         if self.install_script:
             for script in self.distribution.scripts:
@@ -110,6 +117,7 @@
         install.root = self.bdist_dir
         install.skip_build = self.skip_build
         install.warn_dir = 0
+        install.plat_name = self.plat_name
 
         install_lib = self.reinitialize_command('install_lib')
         # we do not want to include pyc or pyo files
@@ -127,7 +135,7 @@
             if not target_version:
                 assert self.skip_build, "Should have already checked this"
                 target_version = sys.version[0:3]
-            plat_specifier = ".%s-%s" % (get_platform(), target_version)
+            plat_specifier = ".%s-%s" % (self.plat_name, target_version)
             build = self.get_finalized_command('build')
             build.build_lib = os.path.join(build.build_base,
                                            'lib' + plat_specifier)
@@ -285,11 +293,11 @@
             # if we create an installer for a specific python version,
             # it's better to include this in the name
             installer_name = os.path.join(self.dist_dir,
-                                          "%s.win32-py%s.exe" %
-                                           (fullname, self.target_version))
+                                          "%s.%s-py%s.exe" %
+                                           (fullname, self.plat_name, self.target_version))
         else:
             installer_name = os.path.join(self.dist_dir,
-                                          "%s.win32.exe" % fullname)
+                                          "%s.%s.exe" % (fullname, self.plat_name))
         return installer_name
     # get_installer_filename()
 
@@ -312,9 +320,9 @@
                 bv = get_build_version()
             else:
                 if self.target_version < "2.4":
-                    bv = "6"
+                    bv = 6.0
                 else:
-                    bv = "7.1"
+                    bv = 7.1
         else:
             # for current version - use authoritative check.
             bv = get_build_version()
@@ -323,6 +331,10 @@
         directory = os.path.dirname(__file__)
         # we must use a wininst-x.y.exe built with the same C compiler
         # used for python.  XXX What about mingw, borland, and so on?
-        filename = os.path.join(directory, "wininst-%.1f.exe" % bv)
+        if self.plat_name == 'win32':
+            sfix = ''
+        else:
+            sfix = self.plat_name[3:] # strip 'win' - leaves eg '-amd64'
+        filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix))
         return open(filename, "rb").read()
 # class bdist_wininst
diff --git a/Lib/distutils/command/build.py b/Lib/distutils/command/build.py
index bca031f..7462e93 100644
--- a/Lib/distutils/command/build.py
+++ b/Lib/distutils/command/build.py
@@ -8,6 +8,7 @@
 
 import sys, os
 from distutils.core import Command
+from distutils.errors import DistutilsOptionError
 from distutils.util import get_platform
 
 
@@ -34,6 +35,9 @@
          "build directory for scripts"),
         ('build-temp=', 't',
          "temporary build directory"),
+        ('plat-name=', 'p',
+         "platform name to build for, if supported "
+         "(default: %s)" % get_platform()),
         ('compiler=', 'c',
          "specify the compiler type"),
         ('debug', 'g',
@@ -61,13 +65,25 @@
         self.build_temp = None
         self.build_scripts = None
         self.compiler = None
+        self.plat_name = None
         self.debug = None
         self.force = 0
         self.executable = None
 
     def finalize_options (self):
 
-        plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3])
+        if self.plat_name is None:
+            self.plat_name = get_platform()
+        else:
+            # plat-name only supported for windows (other platforms are
+            # supported via ./configure flags, if at all).  Avoid misleading
+            # other platforms.
+            if os.name != 'nt':
+                raise DistutilsOptionError(
+                            "--plat-name only supported on Windows (try "
+                            "using './configure --help' on your platform)")
+
+        plat_specifier = ".%s-%s" % (self.plat_name, sys.version[0:3])
 
         # Make it so Python 2.x and Python 2.x with --with-pydebug don't
         # share the same build directories. Doing so confuses the build
diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py
index 3042fe0..bf5ad7e 100644
--- a/Lib/distutils/command/build_ext.py
+++ b/Lib/distutils/command/build_ext.py
@@ -15,6 +15,7 @@
 from distutils.sysconfig import customize_compiler, get_python_version
 from distutils.dep_util import newer_group
 from distutils.extension import Extension
+from distutils.util import get_platform
 from distutils import log
 
 if os.name == 'nt':
@@ -60,6 +61,9 @@
          "directory for compiled extension modules"),
         ('build-temp=', 't',
          "directory for temporary files (build by-products)"),
+        ('plat-name=', 'p',
+         "platform name to cross-compile for, if supported "
+         "(default: %s)" % get_platform()),
         ('inplace', 'i',
          "ignore build-lib and put compiled extensions into the source " +
          "directory alongside your pure Python modules"),
@@ -101,6 +105,7 @@
     def initialize_options (self):
         self.extensions = None
         self.build_lib = None
+        self.plat_name = None
         self.build_temp = None
         self.inplace = 0
         self.package = None
@@ -127,7 +132,9 @@
                                    ('build_temp', 'build_temp'),
                                    ('compiler', 'compiler'),
                                    ('debug', 'debug'),
-                                   ('force', 'force'))
+                                   ('force', 'force'),
+                                   ('plat_name', 'plat_name'),
+                                   )
 
         if self.package is None:
             self.package = self.distribution.ext_package
@@ -171,6 +178,9 @@
         # for Release and Debug builds.
         # also Python's library directory must be appended to library_dirs
         if os.name == 'nt':
+            # the 'libs' directory is for binary installs - we assume that
+            # must be the *native* platform.  But we don't really support
+            # cross-compiling via a binary install anyway, so we let it go.
             self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs'))
             if self.debug:
                 self.build_temp = os.path.join(self.build_temp, "Debug")
@@ -181,8 +191,17 @@
             # this allows distutils on windows to work in the source tree
             self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC'))
             if MSVC_VERSION == 9:
-                self.library_dirs.append(os.path.join(sys.exec_prefix,
-                                         'PCbuild'))
+                # Use the .lib files for the correct architecture
+                if self.plat_name == 'win32':
+                    suffix = ''
+                else:
+                    # win-amd64 or win-ia64
+                    suffix = self.plat_name[4:]
+                new_lib = os.path.join(sys.exec_prefix, 'PCbuild')
+                if suffix:
+                    new_lib = os.path.join(new_lib, suffix)
+                self.library_dirs.append(new_lib)
+
             elif MSVC_VERSION == 8:
                 self.library_dirs.append(os.path.join(sys.exec_prefix,
                                          'PC', 'VS8.0', 'win32release'))
@@ -275,6 +294,11 @@
                                      dry_run=self.dry_run,
                                      force=self.force)
         customize_compiler(self.compiler)
+        # If we are cross-compiling, init the compiler now (if we are not
+        # cross-compiling, init would not hurt, but people may rely on
+        # late initialization of compiler even if they shouldn't...)
+        if os.name == 'nt' and self.plat_name != get_platform():
+            self.compiler.initialize(self.plat_name)
 
         # And make sure that any compile/link-related options (which might
         # come from the command-line or from the setup script) are set in
diff --git a/Lib/distutils/command/install.py b/Lib/distutils/command/install.py
index 0d39b91..33a1212 100644
--- a/Lib/distutils/command/install.py
+++ b/Lib/distutils/command/install.py
@@ -16,6 +16,7 @@
 from distutils.errors import DistutilsPlatformError
 from distutils.file_util import write_file
 from distutils.util import convert_path, subst_vars, change_root
+from distutils.util import get_platform
 from distutils.errors import DistutilsOptionError
 
 if sys.version < "2.2":
@@ -503,6 +504,14 @@
         # Obviously have to build before we can install
         if not self.skip_build:
             self.run_command('build')
+            # If we built for any other platform, we can't install.
+            build_plat = self.distribution.get_command_obj('build').plat_name
+            # check warn_dir - it is a clue that the 'install' is happening
+            # internally, and not to sys.path, so we don't check the platform
+            # matches what we are running.
+            if self.warn_dir and build_plat != get_platform():
+                raise DistutilsPlatformError("Can't install when "
+                                             "cross-compiling")
 
         # Run all sub-commands (at least those that need to be run)
         for cmd_name in self.get_sub_commands():
diff --git a/Lib/distutils/command/wininst-9.0-amd64.exe b/Lib/distutils/command/wininst-9.0-amd64.exe
new file mode 100644
index 0000000..c99ede4
--- /dev/null
+++ b/Lib/distutils/command/wininst-9.0-amd64.exe
Binary files differ
diff --git a/Lib/distutils/msvc9compiler.py b/Lib/distutils/msvc9compiler.py
index 828d7fb..8b1cf9a 100644
--- a/Lib/distutils/msvc9compiler.py
+++ b/Lib/distutils/msvc9compiler.py
@@ -22,6 +22,7 @@
 from distutils.ccompiler import (CCompiler, gen_preprocess_options,
     gen_lib_options)
 from distutils import log
+from distutils.util import get_platform
 
 import _winreg
 
@@ -38,13 +39,15 @@
 VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
 WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
 NET_BASE = r"Software\Microsoft\.NETFramework"
-ARCHS = {'DEFAULT' : 'x86',
-    'intel' : 'x86', 'x86' : 'x86',
-    'amd64' : 'x64', 'x64' : 'x64',
-    'itanium' : 'ia64', 'ia64' : 'ia64',
-    }
 
-# The globals VERSION, ARCH, MACROS and VC_ENV are defined later
+# A map keyed by get_platform() return values to values accepted by
+# 'vcvarsall.bat'.  Note a cross-compile may combine these (eg, 'x86_amd64' is
+# the param to cross-compile on x86 targetting amd64.)
+PLAT_TO_VCVARS = {
+    'win32' : 'x86',
+    'win-amd64' : 'amd64',
+    'win-ia64' : 'ia64',
+}
 
 class Reg:
     """Helper class to read values from the registry
@@ -176,23 +179,6 @@
     # else we don't know what version of the compiler this is
     return None
 
-def get_build_architecture():
-    """Return the processor architecture.
-
-    Possible results are "x86" or "amd64".
-    """
-    prefix = " bit ("
-    i = sys.version.find(prefix)
-    if i == -1:
-        return "x86"
-    j = sys.version.find(")", i)
-    sysarch = sys.version[i+len(prefix):j].lower()
-    arch = ARCHS.get(sysarch, None)
-    if arch is None:
-        return ARCHS['DEFAULT']
-    else:
-        return arch
-
 def normalize_and_reduce_paths(paths):
     """Return a list of normalized paths with duplicates removed.
 
@@ -251,6 +237,7 @@
 
     if vcvarsall is None:
         raise IOError("Unable to find vcvarsall.bat")
+    log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)
     popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE)
@@ -281,9 +268,7 @@
 VERSION = get_build_version()
 if VERSION < 8.0:
     raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
-ARCH = get_build_architecture()
 # MACROS = MacroExpander(VERSION)
-VC_ENV = query_vcvarsall(VERSION, ARCH)
 
 class MSVCCompiler(CCompiler) :
     """Concrete class that implements an interface to Microsoft Visual C++,
@@ -318,13 +303,25 @@
     def __init__(self, verbose=0, dry_run=0, force=0):
         CCompiler.__init__ (self, verbose, dry_run, force)
         self.__version = VERSION
-        self.__arch = ARCH
         self.__root = r"Software\Microsoft\VisualStudio"
         # self.__macros = MACROS
         self.__path = []
+        # target platform (.plat_name is consistent with 'bdist')
+        self.plat_name = None
+        self.__arch = None # deprecated name
         self.initialized = False
 
-    def initialize(self):
+    def initialize(self, plat_name=None):
+        # multi-init means we would need to check platform same each time...
+        assert not self.initialized, "don't init multiple times"
+        if plat_name is None:
+            plat_name = get_platform()
+        # sanity check for platforms to prevent obscure errors later.
+        ok_plats = 'win32', 'win-amd64', 'win-ia64'
+        if plat_name not in ok_plats:
+            raise DistutilsPlatformError("--plat-name must be one of %s" %
+                                         (ok_plats,))
+
         if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
             # Assume that the SDK set up everything alright; don't try to be
             # smarter
@@ -334,9 +331,24 @@
             self.rc = "rc.exe"
             self.mc = "mc.exe"
         else:
-            self.__paths = VC_ENV['path'].split(os.pathsep)
-            os.environ['lib'] = VC_ENV['lib']
-            os.environ['include'] = VC_ENV['include']
+            # On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
+            # to cross compile, you use 'x86_amd64'.
+            # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
+            # compile use 'x86' (ie, it runs the x86 compiler directly)
+            # No idea how itanium handles this, if at all.
+            if plat_name == get_platform() or plat_name == 'win32':
+                # native build or cross-compile to win32
+                plat_spec = PLAT_TO_VCVARS[plat_name]
+            else:
+                # cross compile from win32 -> some 64bit
+                plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \
+                            PLAT_TO_VCVARS[plat_name]
+
+            vc_env = query_vcvarsall(VERSION, plat_spec)
+
+            self.__paths = vc_env['path'].split(os.pathsep)
+            os.environ['lib'] = vc_env['lib']
+            os.environ['include'] = vc_env['include']
 
             if len(self.__paths) == 0:
                 raise DistutilsPlatformError("Python was built with %s, "
diff --git a/Lib/distutils/msvccompiler.py b/Lib/distutils/msvccompiler.py
index f3acb53..30a9fc6 100644
--- a/Lib/distutils/msvccompiler.py
+++ b/Lib/distutils/msvccompiler.py
@@ -656,5 +656,5 @@
     log.debug("Importing new compiler from distutils.msvc9compiler")
     OldMSVCCompiler = MSVCCompiler
     from distutils.msvc9compiler import MSVCCompiler
-    from distutils.msvc9compiler import get_build_architecture
+    # get_build_architecture not really relevant now we support cross-compile
     from distutils.msvc9compiler import MacroExpander
diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py
index deb9a0a..69d90cf 100644
--- a/Lib/distutils/util.py
+++ b/Lib/distutils/util.py
@@ -30,7 +30,7 @@
        irix64-6.2
 
     Windows will return one of:
-       win-x86_64 (64bit Windows on x86_64 (AMD64))
+       win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
        win-ia64 (64bit Windows on Itanium)
        win32 (all others - specifically, sys.platform is returned)
 
@@ -45,7 +45,7 @@
         j = string.find(sys.version, ")", i)
         look = sys.version[i+len(prefix):j].lower()
         if look=='amd64':
-            return 'win-x86_64'
+            return 'win-amd64'
         if look=='itanium':
             return 'win-ia64'
         return sys.platform