From 98202df75c8a3988e7c4d1fa1ef18c2ac5c37b6f Mon Sep 17 00:00:00 2001 From: "brucedawson@chromium.org" Date: Fri, 15 Jan 2016 20:52:57 +0000 Subject: [PATCH] Package/Install the Windows 10 Universal C Runtime for VS 2015 The VS 2015 tools require the Windows 10 Universal C Runtime, either installed in C:\Windows or in every directory containing tools, or dynamically linked executables created with VS 2015. Installing to C:\Windows seems less error prone. This is only applicable for Google developers and build machines that don't have VS 2015 installed. This updates the packaging script so that it packages the three installers, and no longer packages the installed files (which vary between operating systems anyway). The installer is updated to check for the existence of one of the Universal C Runtime files. If it isn't found then it detects the version of Windows in order to select and run the correct installer. I manually confirmed that, for instance, the installers for Windows 7 and Windows 2008 R2, were identical despite coming from different download URLs. If the installation fails because gclient runhooks is run non-elevated then the developer will have to do a one-time manual install of the update. A message will be printed indicating this. BUG=440500 Review URL: https://codereview.chromium.org/1588673004 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@298286 0039d316-1c4b-4281-b951-d872f2087c98 --- win_toolchain/get_toolchain_if_necessary.py | 87 ++++++++++++++++++++- win_toolchain/package_from_installed.py | 42 +++++----- 2 files changed, 101 insertions(+), 28 deletions(-) diff --git a/win_toolchain/get_toolchain_if_necessary.py b/win_toolchain/get_toolchain_if_necessary.py index 27fa18120..b3cc347fd 100755 --- a/win_toolchain/get_toolchain_if_necessary.py +++ b/win_toolchain/get_toolchain_if_necessary.py @@ -26,10 +26,12 @@ future when a hypothetical VS2015 is released, the 2013 script will be maintained, and a new 2015 script would be added. """ +import _winreg import hashlib import json import optparse import os +import platform import shutil import subprocess import sys @@ -217,6 +219,75 @@ def DoTreeMirror(target_dir, tree_sha1): RmDir(temp_dir) +def GetInstallerName(): + """Return the name of the Windows 10 Universal C Runtime installer for the + current platform, or None if installer is not needed or not applicable. + The registry has to be used instead of sys.getwindowsversion() because + Python 2.7 is only manifested as being compatible up to Windows 8, so the + version APIs helpfully return a maximum of 6.2 (Windows 8). + """ + key_name = r'Software\Microsoft\Windows NT\CurrentVersion' + key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, key_name) + value, keytype = _winreg.QueryValueEx(key, "CurrentVersion") + key.Close() + if keytype != _winreg.REG_SZ: + raise Exception("Unexpected type in registry") + if value == '6.1': + # Windows 7 and Windows Server 2008 R2 + return 'Windows6.1-KB2999226-x64.msu' + elif value == '6.2': + # Windows 8 and Windows Server 2012 + return 'Windows8-RT-KB2999226-x64.msu' + elif value == '6.3': + # Windows 8.1, Windows Server 2012 R2, and Windows 10. + # The Windows 8.1 installer doesn't work on Windows 10, but it will never + # be used because the UCRT is always installed on Windows 10. + return 'Windows8.1-KB2999226-x64.msu' + else: + # Some future OS. + return None + + +def InstallUniversalCRTIfNeeded(abs_target_dir): + installer_name = GetInstallerName() + if not installer_name: + return + + bitness = platform.architecture()[0] + # When running 64-bit python the x64 DLLs will be in System32 + x64_path = 'System32' if bitness == '64bit' else 'Sysnative' + x64_path = os.path.join(r'C:\Windows', x64_path) + sample_crt_file = os.path.join(x64_path, 'ucrtbase.dll') + + if os.path.exists(sample_crt_file): + # Nothing to do. + return + + print ('%s does not exist - installing Windows 10 Universal C Runtime' % + sample_crt_file) + + installer = os.path.join(abs_target_dir, "installers", installer_name) + command = r'wusa.exe /quiet "%s"' % installer + print 'Running %s' % command + + try: + subprocess.check_call(command) + # Trap OSError instead of WindowsError so pylint will succeed on Linux. + except OSError as e: + if e.winerror == 740: # The requested operation requires elevation + print + print '-'*80 + print + print 'Elevation required. You must manually install this update:' + print ' %s' % installer + print + print '-'*80 + print + raise Exception('Elevation required. You must manually install %s' % + installer) + raise e + + def main(): parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) parser.add_option('--output-json', metavar='FILE', @@ -281,10 +352,15 @@ def main(): sys.stdout.flush() DelayBeforeRemoving(target_dir) if sys.platform == 'win32': - # This stays resident and will make the rmdir below fail. - with open(os.devnull, 'wb') as nul: - subprocess.call(['taskkill', '/f', '/im', 'mspdbsrv.exe'], - stdin=nul, stdout=nul, stderr=nul) + # These stay resident and will make the rmdir below fail. + kill_list = [ + 'mspdbsrv.exe', + 'vctip.exe', # Compiler and tools experience improvement data uploader. + ] + for process_name in kill_list: + with open(os.devnull, 'wb') as nul: + subprocess.call(['taskkill', '/f', '/im', process_name], + stdin=nul, stdout=nul, stderr=nul) if os.path.isdir(target_dir): RmDir(target_dir) @@ -331,6 +407,9 @@ def main(): shutil.copyfile(os.path.join(target_dir, '..', 'data.json'), options.output_json) + if os.environ.get('GYP_MSVS_VERSION') == '2015': + InstallUniversalCRTIfNeeded(abs_target_dir) + return 0 diff --git a/win_toolchain/package_from_installed.py b/win_toolchain/package_from_installed.py index bda571a86..e0a3ff262 100644 --- a/win_toolchain/package_from_installed.py +++ b/win_toolchain/package_from_installed.py @@ -133,32 +133,26 @@ def BuildFileList(): result.append((combined, to)) if VS_VERSION == '2015': + # The Windows 10 Universal C Runtime installers are needed when packaging + # VS 2015. They can be download from here: + # https://support.microsoft.com/en-us/kb/2999226 + # and they must be downloaded to the current user's downloads directory. + # The versions needed are those for 64-bit Windows 7, Windows 8, and + # Windows 8.1. The 64-bit Server 2008 R2, Server 2012, and Server 2012 R2 + # versions are identical (same name and contents). + universal_runtime_installers = [ + 'Windows6.1-KB2999226-x64.msu', + 'Windows8-RT-KB2999226-x64.msu', + 'Windows8.1-KB2999226-x64.msu', + ] + + for installer in universal_runtime_installers: + result.append((os.path.join(os.environ['userprofile'], 'downloads', + installer), + os.path.join('installers', installer))) system_crt_files = [ - 'api-ms-win-core-file-l1-2-0.dll', - 'api-ms-win-core-file-l2-1-0.dll', - 'api-ms-win-core-localization-l1-2-0.dll', - 'api-ms-win-core-processthreads-l1-1-1.dll', - 'api-ms-win-core-synch-l1-2-0.dll', - 'api-ms-win-core-timezone-l1-1-0.dll', - 'api-ms-win-core-xstate-l2-1-0.dll', - 'api-ms-win-crt-conio-l1-1-0.dll', - 'api-ms-win-crt-convert-l1-1-0.dll', - 'api-ms-win-crt-environment-l1-1-0.dll', - 'api-ms-win-crt-filesystem-l1-1-0.dll', - 'api-ms-win-crt-heap-l1-1-0.dll', - 'api-ms-win-crt-locale-l1-1-0.dll', - 'api-ms-win-crt-math-l1-1-0.dll', - 'api-ms-win-crt-multibyte-l1-1-0.dll', - 'api-ms-win-crt-private-l1-1-0.dll', - 'api-ms-win-crt-process-l1-1-0.dll', - 'api-ms-win-crt-runtime-l1-1-0.dll', - 'api-ms-win-crt-stdio-l1-1-0.dll', - 'api-ms-win-crt-string-l1-1-0.dll', - 'api-ms-win-crt-time-l1-1-0.dll', - 'api-ms-win-crt-utility-l1-1-0.dll', - 'api-ms-win-eventing-provider-l1-1-0.dll', - 'ucrtbase.dll', + # Needed to let debug binaries run. 'ucrtbased.dll', ] bitness = platform.architecture()[0]