]> granicus.if.org Git - python/commitdiff
bpo-35920: Windows 10 ARM32 platform support (GH-11774)
authorPaul Monson <paulmon@users.noreply.github.com>
Thu, 25 Apr 2019 18:36:45 +0000 (11:36 -0700)
committerSteve Dower <steve.dower@python.org>
Thu, 25 Apr 2019 18:36:45 +0000 (18:36 +0000)
17 files changed:
Doc/library/platform.rst
Lib/distutils/_msvccompiler.py
Lib/distutils/spawn.py
Lib/distutils/sysconfig.py
Lib/distutils/util.py
Lib/platform.py
Lib/sysconfig.py
Lib/test/test_codecs.py
Lib/test/test_mimetypes.py
Lib/test/test_os.py
Lib/test/test_startfile.py
Lib/test/test_sundry.py
Lib/test/test_winreg.py
Misc/NEWS.d/next/Windows/2019-04-22-16-59-20.bpo-35920.VSfGOI.rst [new file with mode: 0644]
PC/bdist_wininst/bdist_wininst.vcxproj
PCbuild/build.bat
PCbuild/pcbuild.sln

index 60c6089ad3ccb5820b0e714f7e15ccfb055f5e6c..e07f9d613a0d219bebfe6694f84d9e58e8afe238 100644 (file)
@@ -216,6 +216,21 @@ Windows Platform
       later (support for this was added in Python 2.6). It obviously
       only runs on Win32 compatible platforms.
 
+.. function:: win32_edition()
+
+   Returns a string representing the current Windows edition.  Possible
+   values include but are not limited to ``'Enterprise'``, ``'IoTUAP'``,
+   ``'ServerStandard'``, and ``'nanoserver'``.
+
+   .. versionadded:: 3.8
+
+.. function:: win32_is_iot()
+
+   Returns True if the windows edition returned by win32_edition is recognized
+   as an IoT edition.
+
+   .. versionadded:: 3.8
+
 
 Mac OS Platform
 ---------------
index 58b20a2102473394a64b627de3339da2e6cc5fe4..c7ac3f049ebf227e26c3932381c31c955dd28d28 100644 (file)
@@ -89,13 +89,24 @@ def _find_vc2017():
 
     return None, None
 
+PLAT_SPEC_TO_RUNTIME = {
+    'x86' : 'x86',
+    'x86_amd64' : 'x64',
+    'x86_arm' : 'arm',
+}
+
 def _find_vcvarsall(plat_spec):
     _, best_dir = _find_vc2017()
     vcruntime = None
-    vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86'
+
+    if plat_spec in PLAT_SPEC_TO_RUNTIME:
+        vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec]
+    else:
+        vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86'
+
     if best_dir:
         vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**",
-            "Microsoft.VC141.CRT", "vcruntime140.dll")
+            vcruntime_plat, "Microsoft.VC141.CRT", "vcruntime140.dll")
         try:
             import glob
             vcruntime = glob.glob(vcredist, recursive=True)[-1]
@@ -178,6 +189,7 @@ def _find_exe(exe, paths=None):
 PLAT_TO_VCVARS = {
     'win32' : 'x86',
     'win-amd64' : 'x86_amd64',
+    'win-arm32' : 'x86_arm',
 }
 
 # A set containing the DLLs that are guaranteed to be available for
index d3a12c283397827933325c9a0b72e37c4e9dbe3b..ceb94945dc8bed2a6e28321208cd8b09b7acf2f0 100644 (file)
@@ -81,7 +81,6 @@ def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0):
                   "command %r failed with exit status %d" % (cmd, rc))
 
 if sys.platform == 'darwin':
-    from distutils import sysconfig
     _cfg_target = None
     _cfg_target_split = None
 
@@ -95,6 +94,7 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0):
     if sys.platform == 'darwin':
         global _cfg_target, _cfg_target_split
         if _cfg_target is None:
+            from distutils import sysconfig
             _cfg_target = sysconfig.get_config_var(
                                   'MACOSX_DEPLOYMENT_TARGET') or ''
             if _cfg_target:
index 570a612d1b1029ba5390a8bd42492dc5c09284b0..b51629eb94f825d8d0f857b8aa5b431814199d72 100644 (file)
@@ -15,6 +15,7 @@ import re
 import sys
 
 from .errors import DistutilsPlatformError
+from .util import get_platform, get_host_platform
 
 # These are needed in a couple of spots, so just compute them once.
 PREFIX = os.path.normpath(sys.prefix)
index 15cd2ad9a9afb8413c4badfefe0192470acd0733..50550e1893418cd5e17c5411f06b4a8f325038e2 100644 (file)
@@ -15,7 +15,7 @@ from distutils.spawn import spawn
 from distutils import log
 from distutils.errors import DistutilsByteCompileError
 
-def get_platform ():
+def get_host_platform():
     """Return a string that identifies the current platform.  This is used mainly to
     distinguish platform-specific build directories and platform-specific built
     distributions.  Typically includes the OS name and version and the
@@ -38,6 +38,8 @@ def get_platform ():
     if os.name == 'nt':
         if 'amd64' in sys.version.lower():
             return 'win-amd64'
+        if '(arm)' in sys.version.lower():
+            return 'win-arm32'
         return sys.platform
 
     # Set for cross builds explicitly
@@ -90,8 +92,16 @@ def get_platform ():
 
     return "%s-%s-%s" % (osname, release, machine)
 
-# get_platform ()
-
+def get_platform():
+    if os.name == 'nt':
+        TARGET_TO_PLAT = {
+            'x86' : 'win32',
+            'x64' : 'win-amd64',
+            'arm' : 'win-arm32',
+        }
+        return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform()
+    else:
+        return get_host_platform()
 
 def convert_path (pathname):
     """Return 'pathname' as a name that will work on the native filesystem,
index 21defd1095d247b4f1b7e1a56db38e2784873f11..9f7bd95980a15cc8668edfd23b6be8ad4729f9db 100755 (executable)
@@ -334,6 +334,27 @@ _WIN32_SERVER_RELEASES = {
     (6, None): "post2012ServerR2",
 }
 
+def win32_is_iot():
+    return win32_edition() in ('IoTUAP', 'NanoServer', 'WindowsCoreHeadless', 'IoTEdgeOS')
+
+def win32_edition():
+    try:
+        try:
+            import winreg
+        except ImportError:
+            import _winreg as winreg
+    except ImportError:
+        pass
+    else:
+        try:
+            cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
+            with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
+                return winreg.QueryValueEx(key, 'EditionId')[0]
+        except OSError:
+            pass
+
+    return None
+
 def win32_ver(release='', version='', csd='', ptype=''):
     try:
         from sys import getwindowsversion
index cc8c7962b1bca2edfd83db2064c16e9883566c40..8446c8deb2427e3b106f254869c7e99733e93435 100644 (file)
@@ -626,6 +626,8 @@ def get_platform():
     if os.name == 'nt':
         if 'amd64' in sys.version.lower():
             return 'win-amd64'
+        if '(arm)' in sys.version.lower():
+            return 'win-arm32'
         return sys.platform
 
     if os.name != "posix" or not hasattr(os, 'uname'):
index 05843c54bd5f9a3ee220b5b1791273e074d18b1c..027a84e275e369c765ebad7c09c6e5a0ce0d2d1d 100644 (file)
@@ -27,6 +27,26 @@ def coding_checker(self, coder):
         self.assertEqual(coder(input), (expect, len(input)))
     return check
 
+# On small versions of Windows like Windows IoT or Windows Nano Server not all codepages are present
+def is_code_page_present(cp):
+    from ctypes import POINTER, WINFUNCTYPE, windll, WinError, Structure, WinDLL
+    from ctypes.wintypes import BOOL, UINT, BYTE, WCHAR, UINT, DWORD
+
+    MAX_LEADBYTES = 12  # 5 ranges, 2 bytes ea., 0 term.
+    MAX_DEFAULTCHAR = 2 # single or double byte
+    MAX_PATH = 260
+    class CPINFOEXW(ctypes.Structure):
+        _fields_ = [("MaxCharSize", UINT),
+                    ("DefaultChar", BYTE*MAX_DEFAULTCHAR),
+                    ("LeadByte", BYTE*MAX_LEADBYTES),
+                    ("UnicodeDefaultChar", WCHAR),
+                    ("CodePage", UINT),
+                    ("CodePageName", WCHAR*MAX_PATH)]
+
+    prototype = WINFUNCTYPE(BOOL, UINT, DWORD, POINTER(CPINFOEXW))
+    GetCPInfoEx = prototype(("GetCPInfoExW", WinDLL("kernel32")))
+    info = CPINFOEXW()
+    return GetCPInfoEx(cp, 0, info)
 
 class Queue(object):
     """
@@ -3078,9 +3098,19 @@ class CodePageTest(unittest.TestCase):
     def test_code_page_decode_flags(self):
         # Issue #36312: For some code pages (e.g. UTF-7) flags for
         # MultiByteToWideChar() must be set to 0.
+        if support.verbose:
+            sys.stdout.write('\n')
         for cp in (50220, 50221, 50222, 50225, 50227, 50229,
                    *range(57002, 57011+1), 65000):
-            self.assertEqual(codecs.code_page_decode(cp, b'abc'), ('abc', 3))
+            # On small versions of Windows like Windows IoT
+            # not all codepages are present.
+            # A missing codepage causes an OSError exception
+            # so check for the codepage before decoding
+            if is_code_page_present(cp):
+                self.assertEqual(codecs.code_page_decode(cp, b'abc'), ('abc', 3), f'cp{cp}')
+            else:
+                if support.verbose:
+                    print(f"  skipping cp={cp}")
         self.assertEqual(codecs.code_page_decode(42, b'abc'),
                          ('\uf061\uf062\uf063', 3))
 
index 554d3d5cead5db4a8d3e5f3d340067c00fa5c72c..c4b2fe2047a71d6f5f9ba969f336cd77daf72dd2 100644 (file)
@@ -6,6 +6,7 @@ import sys
 import unittest
 
 from test import support
+from platform import win32_edition
 
 # Tell it we don't know about external files:
 mimetypes.knownfiles = []
@@ -116,6 +117,8 @@ class Win32MimeTypesTestCase(unittest.TestCase):
         mimetypes.types_map.clear()
         mimetypes.types_map.update(self.original_types_map)
 
+    @unittest.skipIf(win32_edition() in ('NanoServer', 'WindowsCoreHeadless', 'IoTEdgeOS'),
+                                         "MIME types registry keys unavailable")
     def test_registry_parsing(self):
         # the original, minimum contents of the MIME database in the
         # Windows registry is undocumented AFAIK.
index bbadb81069b9da5644760ad7b3c184021bb4d751..a2021b1eba068f196c39bcb5a9c26dd2f1283e05 100644 (file)
@@ -28,6 +28,7 @@ import unittest
 import uuid
 import warnings
 from test import support
+from platform import win32_is_iot
 
 try:
     import resource
@@ -2439,7 +2440,7 @@ class DeviceEncodingTests(unittest.TestCase):
         # Return None when an fd doesn't actually exist.
         self.assertIsNone(os.device_encoding(123456))
 
-    @unittest.skipUnless(os.isatty(0) and (sys.platform.startswith('win') or
+    @unittest.skipUnless(os.isatty(0) and not win32_is_iot() and (sys.platform.startswith('win') or
             (hasattr(locale, 'nl_langinfo') and hasattr(locale, 'CODESET'))),
             'test requires a tty and either Windows or nl_langinfo(CODESET)')
     def test_device_encoding(self):
index f59252e97ad051e4df3814abae9585e34ba94573..1a26a8025e6243e6b43fbb7a08132b1c63a81da3 100644 (file)
@@ -10,6 +10,7 @@
 import unittest
 from test import support
 import os
+import platform
 import sys
 from os import path
 
@@ -20,6 +21,7 @@ class TestCase(unittest.TestCase):
     def test_nonexisting(self):
         self.assertRaises(OSError, startfile, "nonexisting.vbs")
 
+    @unittest.skipIf(platform.win32_is_iot(), "starting files is not supported on Windows IoT Core or nanoserver")
     def test_empty(self):
         # We need to make sure the child process starts in a directory
         # we're not about to delete. If we're running under -j, that
index 6e36a6123daa0e85efa18ac5260298b77eef9f49..2accad1aeebd4f9734835c02c7ee1d3faaf0bf1e 100644 (file)
@@ -1,5 +1,6 @@
 """Do a minimal test of all the modules that aren't otherwise tested."""
 import importlib
+import platform
 import sys
 from test import support
 import unittest
@@ -25,7 +26,7 @@ class TestUntestedModules(unittest.TestCase):
             import distutils.unixccompiler
 
             import distutils.command.bdist_dumb
-            if sys.platform.startswith('win'):
+            if sys.platform.startswith('win') and not platform.win32_is_iot():
                 import distutils.command.bdist_msi
             import distutils.command.bdist
             import distutils.command.bdist_rpm
index 11d054e16cdbfaa9a7dbacdcfaa8d80e9f9095ee..dc2b46e42521f3577c4fb3647c0c2be886e31378 100644 (file)
@@ -5,7 +5,7 @@ import os, sys, errno
 import unittest
 from test import support
 import threading
-from platform import machine
+from platform import machine, win32_edition
 
 # Do this first so test will be skipped if module doesn't exist
 support.import_module('winreg', required_on=['win'])
@@ -399,6 +399,7 @@ class Win64WinregTests(BaseWinregTests):
         DeleteKeyEx(key=HKEY_CURRENT_USER, sub_key=test_key_name,
                     access=KEY_ALL_ACCESS, reserved=0)
 
+    @unittest.skipIf(win32_edition() in ('WindowsCoreHeadless', 'IoTEdgeOS'), "APIs not available on WindowsCoreHeadless")
     def test_reflection_functions(self):
         # Test that we can call the query, enable, and disable functions
         # on a key which isn't on the reflection list with no consequences.
diff --git a/Misc/NEWS.d/next/Windows/2019-04-22-16-59-20.bpo-35920.VSfGOI.rst b/Misc/NEWS.d/next/Windows/2019-04-22-16-59-20.bpo-35920.VSfGOI.rst
new file mode 100644 (file)
index 0000000..455e824
--- /dev/null
@@ -0,0 +1,3 @@
+Added platform.win32_edition() and platform.win32_is_iot(). Added support
+for cross-compiling packages for Windows ARM32. Skip tests that are not
+expected to work on Windows IoT Core ARM32.
index 70bfb9c933794316f3767177a34f9b2981f63712..d2f1bb75e30d87221e66cb06a31572a0e161fde1 100644 (file)
@@ -1,6 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|ARM">
+      <Configuration>Debug</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
       <Platform>Win32</Platform>
@@ -9,6 +13,10 @@
       <Configuration>Debug</Configuration>
       <Platform>x64</Platform>
     </ProjectConfiguration>
+    <ProjectConfiguration Include="PGInstrument|ARM">
+      <Configuration>PGInstrument</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
     <ProjectConfiguration Include="PGInstrument|Win32">
       <Configuration>PGInstrument</Configuration>
       <Platform>Win32</Platform>
       <Configuration>PGInstrument</Configuration>
       <Platform>x64</Platform>
     </ProjectConfiguration>
+    <ProjectConfiguration Include="PGUpdate|ARM">
+      <Configuration>PGUpdate</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
     <ProjectConfiguration Include="PGUpdate|Win32">
       <Configuration>PGUpdate</Configuration>
       <Platform>Win32</Platform>
       <Configuration>PGUpdate</Configuration>
       <Platform>x64</Platform>
     </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|ARM">
+      <Configuration>Release</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
     <ProjectConfiguration Include="Release|Win32">
       <Configuration>Release</Configuration>
       <Platform>Win32</Platform>
index 759aa5221b42662407b261f54afa4e4ed99c4bed..cd0c07abbf352b56b09a2019ed11dd99183a8c6e 100644 (file)
@@ -41,7 +41,7 @@ echo.
 echo.Available arguments:
 echo.  -c Release ^| Debug ^| PGInstrument ^| PGUpdate
 echo.     Set the configuration (default: Release)
-echo.  -p x64 ^| Win32
+echo.  -p x64 ^| Win32 ^| ARM
 echo.     Set the platform (default: Win32)
 echo.  -t Build ^| Rebuild ^| Clean ^| CleanAll
 echo.     Set the target manually
index 951dc932a8e57095e65e5f35fdd33428eb5e45c2..66be9ac7a4ad94059dbf7f005de9dea124450286 100644 (file)
@@ -597,16 +597,16 @@ Global
                {D06B6426-4762-44CC-8BAD-D79052507F2F}.Release|Win32.Build.0 = Release|Win32
                {D06B6426-4762-44CC-8BAD-D79052507F2F}.Release|x64.ActiveCfg = Release|x64
                {D06B6426-4762-44CC-8BAD-D79052507F2F}.Release|x64.Build.0 = Release|x64
-               {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.Debug|ARM.ActiveCfg = Debug|Win32
+               {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.Debug|ARM.ActiveCfg = Debug|ARM
                {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.Debug|Win32.ActiveCfg = Debug|Win32
                {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.Debug|x64.ActiveCfg = Release|x64
-               {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.PGInstrument|ARM.ActiveCfg = PGInstrument|Win32
+               {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.PGInstrument|ARM.ActiveCfg = PGInstrument|ARM
                {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.PGInstrument|Win32.ActiveCfg = Release|Win32
                {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.PGInstrument|x64.ActiveCfg = Release|x64
-               {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.PGUpdate|ARM.ActiveCfg = PGUpdate|Win32
+               {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.PGUpdate|ARM.ActiveCfg = PGUpdate|ARM
                {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.PGUpdate|Win32.ActiveCfg = Release|Win32
                {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.PGUpdate|x64.ActiveCfg = Release|x64
-               {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.Release|ARM.ActiveCfg = Release|Win32
+               {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.Release|ARM.ActiveCfg = Release|ARM
                {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.Release|Win32.ActiveCfg = Release|Win32
                {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.Release|x64.ActiveCfg = Release|x64
                {447F05A8-F581-4CAC-A466-5AC7936E207E}.Debug|ARM.ActiveCfg = Debug|ARM
@@ -896,6 +896,7 @@ Global
                {0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.PGUpdate|x64.ActiveCfg = PGUpdate|x64
                {0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.PGUpdate|x64.Build.0 = PGUpdate|x64
                {0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|ARM.ActiveCfg = Release|ARM
+               {0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|ARM.Build.0 = Release|ARM
                {0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|Win32.ActiveCfg = Release|Win32
                {0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|Win32.Build.0 = Release|Win32
                {0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|x64.ActiveCfg = Release|x64