bpo-35920: Windows 10 ARM32 platform support (GH-11774)

This commit is contained in:
Paul Monson 2019-04-25 11:36:45 -07:00 committed by Steve Dower
parent 8c3ecc6bac
commit 62dfd7d6fe
17 changed files with 134 additions and 15 deletions

View File

@ -216,6 +216,21 @@ Windows Platform
later (support for this was added in Python 2.6). It obviously later (support for this was added in Python 2.6). It obviously
only runs on Win32 compatible platforms. 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 Mac OS Platform
--------------- ---------------

View File

@ -89,13 +89,24 @@ def _find_vc2017():
return None, None return None, None
PLAT_SPEC_TO_RUNTIME = {
'x86' : 'x86',
'x86_amd64' : 'x64',
'x86_arm' : 'arm',
}
def _find_vcvarsall(plat_spec): def _find_vcvarsall(plat_spec):
_, best_dir = _find_vc2017() _, best_dir = _find_vc2017()
vcruntime = None 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: if best_dir:
vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**", vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**",
"Microsoft.VC141.CRT", "vcruntime140.dll") vcruntime_plat, "Microsoft.VC141.CRT", "vcruntime140.dll")
try: try:
import glob import glob
vcruntime = glob.glob(vcredist, recursive=True)[-1] vcruntime = glob.glob(vcredist, recursive=True)[-1]
@ -178,6 +189,7 @@ def _find_exe(exe, paths=None):
PLAT_TO_VCVARS = { PLAT_TO_VCVARS = {
'win32' : 'x86', 'win32' : 'x86',
'win-amd64' : 'x86_amd64', 'win-amd64' : 'x86_amd64',
'win-arm32' : 'x86_arm',
} }
# A set containing the DLLs that are guaranteed to be available for # A set containing the DLLs that are guaranteed to be available for

View 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)) "command %r failed with exit status %d" % (cmd, rc))
if sys.platform == 'darwin': if sys.platform == 'darwin':
from distutils import sysconfig
_cfg_target = None _cfg_target = None
_cfg_target_split = 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': if sys.platform == 'darwin':
global _cfg_target, _cfg_target_split global _cfg_target, _cfg_target_split
if _cfg_target is None: if _cfg_target is None:
from distutils import sysconfig
_cfg_target = sysconfig.get_config_var( _cfg_target = sysconfig.get_config_var(
'MACOSX_DEPLOYMENT_TARGET') or '' 'MACOSX_DEPLOYMENT_TARGET') or ''
if _cfg_target: if _cfg_target:

View File

@ -15,6 +15,7 @@ import re
import sys import sys
from .errors import DistutilsPlatformError 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. # These are needed in a couple of spots, so just compute them once.
PREFIX = os.path.normpath(sys.prefix) PREFIX = os.path.normpath(sys.prefix)

View File

@ -15,7 +15,7 @@ from distutils.spawn import spawn
from distutils import log from distutils import log
from distutils.errors import DistutilsByteCompileError 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 """Return a string that identifies the current platform. This is used mainly to
distinguish platform-specific build directories and platform-specific built distinguish platform-specific build directories and platform-specific built
distributions. Typically includes the OS name and version and the distributions. Typically includes the OS name and version and the
@ -38,6 +38,8 @@ def get_platform ():
if os.name == 'nt': if os.name == 'nt':
if 'amd64' in sys.version.lower(): if 'amd64' in sys.version.lower():
return 'win-amd64' return 'win-amd64'
if '(arm)' in sys.version.lower():
return 'win-arm32'
return sys.platform return sys.platform
# Set for cross builds explicitly # Set for cross builds explicitly
@ -90,8 +92,16 @@ def get_platform ():
return "%s-%s-%s" % (osname, release, machine) 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): def convert_path (pathname):
"""Return 'pathname' as a name that will work on the native filesystem, """Return 'pathname' as a name that will work on the native filesystem,

View File

@ -334,6 +334,27 @@ _WIN32_SERVER_RELEASES = {
(6, None): "post2012ServerR2", (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=''): def win32_ver(release='', version='', csd='', ptype=''):
try: try:
from sys import getwindowsversion from sys import getwindowsversion

View File

@ -626,6 +626,8 @@ def get_platform():
if os.name == 'nt': if os.name == 'nt':
if 'amd64' in sys.version.lower(): if 'amd64' in sys.version.lower():
return 'win-amd64' return 'win-amd64'
if '(arm)' in sys.version.lower():
return 'win-arm32'
return sys.platform return sys.platform
if os.name != "posix" or not hasattr(os, 'uname'): if os.name != "posix" or not hasattr(os, 'uname'):

View File

@ -27,6 +27,26 @@ def coding_checker(self, coder):
self.assertEqual(coder(input), (expect, len(input))) self.assertEqual(coder(input), (expect, len(input)))
return check 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): class Queue(object):
""" """
@ -3078,9 +3098,19 @@ class CodePageTest(unittest.TestCase):
def test_code_page_decode_flags(self): def test_code_page_decode_flags(self):
# Issue #36312: For some code pages (e.g. UTF-7) flags for # Issue #36312: For some code pages (e.g. UTF-7) flags for
# MultiByteToWideChar() must be set to 0. # MultiByteToWideChar() must be set to 0.
if support.verbose:
sys.stdout.write('\n')
for cp in (50220, 50221, 50222, 50225, 50227, 50229, for cp in (50220, 50221, 50222, 50225, 50227, 50229,
*range(57002, 57011+1), 65000): *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'), self.assertEqual(codecs.code_page_decode(42, b'abc'),
('\uf061\uf062\uf063', 3)) ('\uf061\uf062\uf063', 3))

View File

@ -6,6 +6,7 @@ import sys
import unittest import unittest
from test import support from test import support
from platform import win32_edition
# Tell it we don't know about external files: # Tell it we don't know about external files:
mimetypes.knownfiles = [] mimetypes.knownfiles = []
@ -116,6 +117,8 @@ class Win32MimeTypesTestCase(unittest.TestCase):
mimetypes.types_map.clear() mimetypes.types_map.clear()
mimetypes.types_map.update(self.original_types_map) 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): def test_registry_parsing(self):
# the original, minimum contents of the MIME database in the # the original, minimum contents of the MIME database in the
# Windows registry is undocumented AFAIK. # Windows registry is undocumented AFAIK.

View File

@ -28,6 +28,7 @@ import unittest
import uuid import uuid
import warnings import warnings
from test import support from test import support
from platform import win32_is_iot
try: try:
import resource import resource
@ -2439,7 +2440,7 @@ class DeviceEncodingTests(unittest.TestCase):
# Return None when an fd doesn't actually exist. # Return None when an fd doesn't actually exist.
self.assertIsNone(os.device_encoding(123456)) 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'))), (hasattr(locale, 'nl_langinfo') and hasattr(locale, 'CODESET'))),
'test requires a tty and either Windows or nl_langinfo(CODESET)') 'test requires a tty and either Windows or nl_langinfo(CODESET)')
def test_device_encoding(self): def test_device_encoding(self):

View File

@ -10,6 +10,7 @@
import unittest import unittest
from test import support from test import support
import os import os
import platform
import sys import sys
from os import path from os import path
@ -20,6 +21,7 @@ class TestCase(unittest.TestCase):
def test_nonexisting(self): def test_nonexisting(self):
self.assertRaises(OSError, startfile, "nonexisting.vbs") 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): def test_empty(self):
# We need to make sure the child process starts in a directory # 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 # we're not about to delete. If we're running under -j, that

View File

@ -1,5 +1,6 @@
"""Do a minimal test of all the modules that aren't otherwise tested.""" """Do a minimal test of all the modules that aren't otherwise tested."""
import importlib import importlib
import platform
import sys import sys
from test import support from test import support
import unittest import unittest
@ -25,7 +26,7 @@ class TestUntestedModules(unittest.TestCase):
import distutils.unixccompiler import distutils.unixccompiler
import distutils.command.bdist_dumb 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_msi
import distutils.command.bdist import distutils.command.bdist
import distutils.command.bdist_rpm import distutils.command.bdist_rpm

View File

@ -5,7 +5,7 @@ import os, sys, errno
import unittest import unittest
from test import support from test import support
import threading 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 # Do this first so test will be skipped if module doesn't exist
support.import_module('winreg', required_on=['win']) support.import_module('winreg', required_on=['win'])
@ -399,6 +399,7 @@ class Win64WinregTests(BaseWinregTests):
DeleteKeyEx(key=HKEY_CURRENT_USER, sub_key=test_key_name, DeleteKeyEx(key=HKEY_CURRENT_USER, sub_key=test_key_name,
access=KEY_ALL_ACCESS, reserved=0) access=KEY_ALL_ACCESS, reserved=0)
@unittest.skipIf(win32_edition() in ('WindowsCoreHeadless', 'IoTEdgeOS'), "APIs not available on WindowsCoreHeadless")
def test_reflection_functions(self): def test_reflection_functions(self):
# Test that we can call the query, enable, and disable functions # Test that we can call the query, enable, and disable functions
# on a key which isn't on the reflection list with no consequences. # on a key which isn't on the reflection list with no consequences.

View File

@ -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.

View File

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations"> <ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32"> <ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration> <Configuration>Debug</Configuration>
<Platform>Win32</Platform> <Platform>Win32</Platform>
@ -9,6 +13,10 @@
<Configuration>Debug</Configuration> <Configuration>Debug</Configuration>
<Platform>x64</Platform> <Platform>x64</Platform>
</ProjectConfiguration> </ProjectConfiguration>
<ProjectConfiguration Include="PGInstrument|ARM">
<Configuration>PGInstrument</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGInstrument|Win32"> <ProjectConfiguration Include="PGInstrument|Win32">
<Configuration>PGInstrument</Configuration> <Configuration>PGInstrument</Configuration>
<Platform>Win32</Platform> <Platform>Win32</Platform>
@ -17,6 +25,10 @@
<Configuration>PGInstrument</Configuration> <Configuration>PGInstrument</Configuration>
<Platform>x64</Platform> <Platform>x64</Platform>
</ProjectConfiguration> </ProjectConfiguration>
<ProjectConfiguration Include="PGUpdate|ARM">
<Configuration>PGUpdate</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGUpdate|Win32"> <ProjectConfiguration Include="PGUpdate|Win32">
<Configuration>PGUpdate</Configuration> <Configuration>PGUpdate</Configuration>
<Platform>Win32</Platform> <Platform>Win32</Platform>
@ -25,6 +37,10 @@
<Configuration>PGUpdate</Configuration> <Configuration>PGUpdate</Configuration>
<Platform>x64</Platform> <Platform>x64</Platform>
</ProjectConfiguration> </ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32"> <ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>Win32</Platform> <Platform>Win32</Platform>

View File

@ -41,7 +41,7 @@ echo.
echo.Available arguments: echo.Available arguments:
echo. -c Release ^| Debug ^| PGInstrument ^| PGUpdate echo. -c Release ^| Debug ^| PGInstrument ^| PGUpdate
echo. Set the configuration (default: Release) echo. Set the configuration (default: Release)
echo. -p x64 ^| Win32 echo. -p x64 ^| Win32 ^| ARM
echo. Set the platform (default: Win32) echo. Set the platform (default: Win32)
echo. -t Build ^| Rebuild ^| Clean ^| CleanAll echo. -t Build ^| Rebuild ^| Clean ^| CleanAll
echo. Set the target manually echo. Set the target manually

View File

@ -597,16 +597,16 @@ Global
{D06B6426-4762-44CC-8BAD-D79052507F2F}.Release|Win32.Build.0 = Release|Win32 {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.ActiveCfg = Release|x64
{D06B6426-4762-44CC-8BAD-D79052507F2F}.Release|x64.Build.0 = 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|Win32.ActiveCfg = Debug|Win32
{EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.Debug|x64.ActiveCfg = Release|x64 {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|Win32.ActiveCfg = Release|Win32
{EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.PGInstrument|x64.ActiveCfg = Release|x64 {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|Win32.ActiveCfg = Release|Win32
{EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.PGUpdate|x64.ActiveCfg = Release|x64 {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|Win32.ActiveCfg = Release|Win32
{EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.Release|x64.ActiveCfg = Release|x64 {EB1C19C1-1F18-421E-9735-CAEE69DC6A3C}.Release|x64.ActiveCfg = Release|x64
{447F05A8-F581-4CAC-A466-5AC7936E207E}.Debug|ARM.ActiveCfg = Debug|ARM {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.ActiveCfg = PGUpdate|x64
{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.PGUpdate|x64.Build.0 = 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.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.ActiveCfg = Release|Win32
{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|Win32.Build.0 = Release|Win32 {0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|Win32.Build.0 = Release|Win32
{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|x64.ActiveCfg = Release|x64 {0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|x64.ActiveCfg = Release|x64