Allow building Chrome with local VC++ 2017 install

This updates the toolchain scripts enough so that it is possible to
build Chrome with VC++ 2017 if DEPOT_TOOLS_WIN_TOOLCHAIN=0 and
GYP_MSVS_VERSION=2017, followed by gclient runhooks.

There are still some local patches needed to make it work, and
treat_warnings_as_errors = false and is_component_build = false in
args.gn may be needed initially, but my local builds are working.

The toolchain code should be updated eventually to use COM to locate
the VC++ install location, instead of the hard-coded path which is being
used as a temporary measure in this change.

R=scottmg@chromium.org
BUG=683729

Review-Url: https://codereview.chromium.org/2653473002
Cr-Commit-Position: refs/heads/master@{#445329}


CrOS-Libchrome-Original-Commit: adddab40528d1fb6dd94c0e4d2bc794ffccf194a
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 3edf1ff..aa56a79 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -135,20 +135,31 @@
   year_to_version = {
       '2013': '12.0',
       '2015': '14.0',
+      '2017': '15.0',
   }
   if version_as_year not in year_to_version:
     raise Exception(('Visual Studio version %s (from GYP_MSVS_VERSION)'
                      ' not supported. Supported versions are: %s') % (
                        version_as_year, ', '.join(year_to_version.keys())))
   version = year_to_version[version_as_year]
-  keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
-          r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version]
-  for key in keys:
-    path = _RegistryGetValue(key, 'InstallDir')
-    if not path:
-      continue
-    path = os.path.normpath(os.path.join(path, '..', '..'))
-    return path
+  if version_as_year == '2017':
+    # The VC++ 2017 install location needs to be located using COM instead of
+    # the registry. For details see:
+    # https://blogs.msdn.microsoft.com/heaths/2016/09/15/changes-to-visual-studio-15-setup/
+    # For now we use a hardcoded default with an environment variable override.
+    path = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional'
+    path = os.environ.get('vs2017_install', path)
+    if os.path.exists(path):
+      return path
+  else:
+    keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
+            r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version]
+    for key in keys:
+      path = _RegistryGetValue(key, 'InstallDir')
+      if not path:
+        continue
+      path = os.path.normpath(os.path.join(path, '..', '..'))
+      return path
 
   raise Exception(('Visual Studio Version %s (from GYP_MSVS_VERSION)'
                    ' not found.') % (version_as_year))
@@ -162,6 +173,8 @@
     return '120'
   elif vs_version == '2015':
     return '140'
+  elif vs_version == '2017':
+    return '150'
   else:
     raise ValueError('Unexpected GYP_MSVS_VERSION')
 
@@ -196,7 +209,7 @@
     _CopyRuntimeImpl(target, source)
 
 
-def _CopyRuntime2015(target_dir, source_dir, dll_pattern, suffix):
+def _CopyUCRTRuntime(target_dir, source_dir, dll_pattern, suffix):
   """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
   exist, but the target directory does exist."""
   for file_part in ('msvcp', 'vccorlib', 'vcruntime'):
@@ -223,10 +236,11 @@
 
 def _CopyRuntime(target_dir, source_dir, target_cpu, debug):
   """Copy the VS runtime DLLs, only if the target doesn't exist, but the target
-  directory does exist. Handles VS 2013 and VS 2015."""
+  directory does exist. Handles VS 2013, VS 2015, and VS 2017."""
   suffix = "d.dll" if debug else ".dll"
-  if GetVisualStudioVersion() == '2015':
-    _CopyRuntime2015(target_dir, source_dir, '%s140' + suffix, suffix)
+  if GetVisualStudioVersion() == '2015' or GetVisualStudioVersion() == '2017':
+    # VS 2017 RC uses the same CRT DLLs as VS 2015.
+    _CopyUCRTRuntime(target_dir, source_dir, '%s140' + suffix, suffix)
   else:
     _CopyRuntime2013(target_dir, source_dir, 'msvc%s120' + suffix)