diff --git a/Doc/library/platform.rst b/Doc/library/platform.rst index 60c6089ad3c..e07f9d613a0 100644 --- a/Doc/library/platform.rst +++ b/Doc/library/platform.rst @@ -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 --------------- diff --git a/Lib/distutils/_msvccompiler.py b/Lib/distutils/_msvccompiler.py index 58b20a21024..c7ac3f049eb 100644 --- a/Lib/distutils/_msvccompiler.py +++ b/Lib/distutils/_msvccompiler.py @@ -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 diff --git a/Lib/distutils/spawn.py b/Lib/distutils/spawn.py index d3a12c28339..ceb94945dc8 100644 --- a/Lib/distutils/spawn.py +++ b/Lib/distutils/spawn.py @@ -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: diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index 570a612d1b1..b51629eb94f 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -15,6 +15,7 @@ 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) diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py index 15cd2ad9a9a..50550e18934 100644 --- a/Lib/distutils/util.py +++ b/Lib/distutils/util.py @@ -15,7 +15,7 @@ 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, diff --git a/Lib/platform.py b/Lib/platform.py index 21defd1095d..9f7bd95980a 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -334,6 +334,27 @@ def _syscmd_ver(system='', release='', version='', (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 diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py index cc8c7962b1b..8446c8deb24 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -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'): diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 05843c54bd5..027a84e275e 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -27,6 +27,26 @@ def check(input, expect): 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 @@ def test_multibyte_encoding(self): 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)) diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index 554d3d5cead..c4b2fe2047a 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -6,6 +6,7 @@ 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 @@ def tearDown(self): 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. diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index bbadb81069b..a2021b1eba0 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -28,6 +28,7 @@ import uuid import warnings from test import support +from platform import win32_is_iot try: import resource @@ -2439,7 +2440,7 @@ def test_bad_fd(self): # 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): diff --git a/Lib/test/test_startfile.py b/Lib/test/test_startfile.py index f59252e97ad..1a26a8025e6 100644 --- a/Lib/test/test_startfile.py +++ b/Lib/test/test_startfile.py @@ -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 diff --git a/Lib/test/test_sundry.py b/Lib/test/test_sundry.py index 6e36a6123da..2accad1aeeb 100644 --- a/Lib/test/test_sundry.py +++ b/Lib/test/test_sundry.py @@ -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 @@ def test_untested_modules_can_be_imported(self): 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 diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index 11d054e16cd..dc2b46e4252 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -5,7 +5,7 @@ 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 @@ def test_named_arguments(self): 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 index 00000000000..455e82450eb --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2019-04-22-16-59-20.bpo-35920.VSfGOI.rst @@ -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. diff --git a/PC/bdist_wininst/bdist_wininst.vcxproj b/PC/bdist_wininst/bdist_wininst.vcxproj index 70bfb9c9337..d2f1bb75e30 100644 --- a/PC/bdist_wininst/bdist_wininst.vcxproj +++ b/PC/bdist_wininst/bdist_wininst.vcxproj @@ -1,6 +1,10 @@  + + Debug + ARM + Debug Win32 @@ -9,6 +13,10 @@ Debug x64 + + PGInstrument + ARM + PGInstrument Win32 @@ -17,6 +25,10 @@ PGInstrument x64 + + PGUpdate + ARM + PGUpdate Win32 @@ -25,6 +37,10 @@ PGUpdate x64 + + Release + ARM + Release Win32 diff --git a/PCbuild/build.bat b/PCbuild/build.bat index 759aa5221b4..cd0c07abbf3 100644 --- a/PCbuild/build.bat +++ b/PCbuild/build.bat @@ -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 diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln index 951dc932a8e..66be9ac7a4a 100644 --- a/PCbuild/pcbuild.sln +++ b/PCbuild/pcbuild.sln @@ -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