Add support for finding depot_tools' MSVC toolchain on the bots.

- Pull in a trimmed down version of vs_toolchain.py from Chromium.

- Drop in toolchain_vs2013.hash from Chromium to use Chromium's
  current toolchain.

- Add a very hacky vs_env.py to pull in Visual Studio. This is
  loosely based off a handful of lines of Chromium's
  tools/clang/scripts/update.py. This (and vs_toolchain.py) depends
  on gyp which is now pulled in via DEPS.

BUG=430237

Change-Id: Ic29cbb15e19a99616cfe778d0778b9a71c45338a
Reviewed-on: https://boringssl-review.googlesource.com/3900
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/util/bot/DEPS b/util/bot/DEPS
index 34d9b5e..635dc91 100644
--- a/util/bot/DEPS
+++ b/util/bot/DEPS
@@ -12,6 +12,14 @@
 # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+vars = {
+  'chromium_git': 'https://chromium.googlesource.com',
+}
+
+deps = {
+  'boringssl/util/bot/gyp':
+    Var('chromium_git') + '/external/gyp.git' + '@' + '4a9b712d5cb4a5ba7a9950128a7219569caf7263',
+}
 
 hooks = [
   {
@@ -69,6 +77,14 @@
                 '-s', 'boringssl/util/bot/yasm-win32.exe.sha1',
     ],
   },
+  {
+    'name': 'win_toolchain',
+    'pattern': '.',
+    'action': [ 'python',
+                'boringssl/util/bot/vs_toolchain.py',
+                'update',
+    ],
+  },
   # TODO(davidben): Only extract archives when they've changed. Extracting perl
   # on Windows is a significant part of the cycle time.
   {
diff --git a/util/bot/toolchain_vs2013.hash b/util/bot/toolchain_vs2013.hash
new file mode 100644
index 0000000..4ed8816
--- /dev/null
+++ b/util/bot/toolchain_vs2013.hash
@@ -0,0 +1 @@
+ee7d718ec60c2dc5d255bbe325909c2021a7efef
diff --git a/util/bot/vs_env.py b/util/bot/vs_env.py
new file mode 100644
index 0000000..1847500
--- /dev/null
+++ b/util/bot/vs_env.py
@@ -0,0 +1,37 @@
+# Copyright (c) 2015, Google Inc.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import subprocess
+import sys
+
+import vs_toolchain
+# vs_toolchain adds gyp to sys.path.
+import gyp.MSVSVersion
+
+if len(sys.argv) < 2:
+  print >>sys.stderr, "Usage: vs_env.py TARGET_ARCH CMD..."
+  sys.exit(1)
+
+target_arch = sys.argv[1]
+cmd = sys.argv[2:]
+
+vs_toolchain.SetEnvironmentAndGetRuntimeDllDirs()
+vs_version = gyp.MSVSVersion.SelectVisualStudioVersion()
+
+# Using shell=True is somewhat ugly, but the alternative is to pull in a copy
+# of the Chromium GN build's setup_toolchain.py which runs the setup script,
+# then 'set', and then parses the environment variables out. (GYP internally
+# does the same thing.)
+sys.exit(subprocess.call(vs_version.SetupScript(target_arch) + ["&&"] + cmd,
+                         shell=True))
diff --git a/util/bot/vs_toolchain.py b/util/bot/vs_toolchain.py
new file mode 100644
index 0000000..fd76f39
--- /dev/null
+++ b/util/bot/vs_toolchain.py
@@ -0,0 +1,114 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import os
+import pipes
+import shutil
+import subprocess
+import sys
+
+
+script_dir = os.path.dirname(os.path.realpath(__file__))
+sys.path.insert(0, os.path.join(script_dir, 'gyp', 'pylib'))
+json_data_file = os.path.join(script_dir, 'win_toolchain.json')
+
+
+import gyp
+
+
+def SetEnvironmentAndGetRuntimeDllDirs():
+  """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
+  returns the location of the VS runtime DLLs so they can be copied into
+  the output directory after gyp generation.
+  """
+  vs2013_runtime_dll_dirs = None
+  depot_tools_win_toolchain = \
+      bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
+  if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
+    if not os.path.exists(json_data_file):
+      Update()
+    with open(json_data_file, 'r') as tempf:
+      toolchain_data = json.load(tempf)
+
+    toolchain = toolchain_data['path']
+    version = toolchain_data['version']
+    version_is_pro = version[-1] != 'e'
+    win8sdk = toolchain_data['win8sdk']
+    wdk = toolchain_data['wdk']
+    # TODO(scottmg): The order unfortunately matters in these. They should be
+    # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
+    # below). http://crbug.com/345992
+    vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs']
+
+    os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
+    os.environ['GYP_MSVS_VERSION'] = version
+    # We need to make sure windows_sdk_path is set to the automated
+    # toolchain values in GYP_DEFINES, but don't want to override any
+    # otheroptions.express
+    # values there.
+    gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
+    gyp_defines_dict['windows_sdk_path'] = win8sdk
+    os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
+        for k, v in gyp_defines_dict.iteritems())
+    os.environ['WINDOWSSDKDIR'] = win8sdk
+    os.environ['WDK_DIR'] = wdk
+    # Include the VS runtime in the PATH in case it's not machine-installed.
+    runtime_path = ';'.join(vs2013_runtime_dll_dirs)
+    os.environ['PATH'] = runtime_path + ';' + os.environ['PATH']
+  return vs2013_runtime_dll_dirs
+
+
+def _GetDesiredVsToolchainHashes():
+  """Load a list of SHA1s corresponding to the toolchains that we want installed
+  to build with."""
+  sha1path = os.path.join(script_dir, 'toolchain_vs2013.hash')
+  with open(sha1path, 'rb') as f:
+    return f.read().strip().splitlines()
+
+
+def FindDepotTools():
+  """Returns the path to depot_tools in $PATH."""
+  for path in os.environ['PATH'].split(os.pathsep):
+    if os.path.isfile(os.path.join(path, 'gclient.py')):
+      return path
+  raise Exception("depot_tools not found!")
+
+
+def Update():
+  """Requests an update of the toolchain to the specific hashes we have at
+  this revision. The update outputs a .json of the various configuration
+  information required to pass to gyp which we use in |GetToolchainDir()|.
+  """
+  depot_tools_win_toolchain = \
+      bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
+  if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
+    depot_tools_path = FindDepotTools()
+    json_data_file = os.path.join(script_dir, 'win_toolchain.json')
+    get_toolchain_args = [
+        sys.executable,
+        os.path.join(depot_tools_path,
+                    'win_toolchain',
+                    'get_toolchain_if_necessary.py'),
+        '--output-json', json_data_file,
+      ] + _GetDesiredVsToolchainHashes()
+    subprocess.check_call(get_toolchain_args)
+
+  return 0
+
+
+def main():
+  if not sys.platform.startswith(('win32', 'cygwin')):
+    return 0
+  commands = {
+      'update': Update,
+  }
+  if len(sys.argv) < 2 or sys.argv[1] not in commands:
+    print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands)
+    return 1
+  return commands[sys.argv[1]](*sys.argv[2:])
+
+
+if __name__ == '__main__':
+  sys.exit(main())