Drop in prebuilt versions of Windows files.

These are upstream's prebuilt binaries of:
e9493171de0edd8879755aa7229a701010a19561  cmake-3.1.3-win32-x86.zip
ab6e7aee6a915c4d820b86f5227094763b649fce  strawberry-perl-5.20.2.1-32bit-portable.zip
4c4d1951181a610923523cb10d83d9ae9952fbf3  yasm-1.2.0-win32.exe

This is intentionally using yasm 1.2.0 rather than the latest 1.3.0 to match
Chromium's current bundled version. Chromium has additional patches, but they
all seem to be either in 1.2.0 or not relevant for us.

Also update extract.py a little to account for these.

BUG=430237

Change-Id: Iad6687e493900b25390d99882c7ceea62fff8b9b
Reviewed-on: https://boringssl-review.googlesource.com/3710
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/util/bot/DEPS b/util/bot/DEPS
index cc61854..34d9b5e 100644
--- a/util/bot/DEPS
+++ b/util/bot/DEPS
@@ -36,9 +36,41 @@
                 '-s', 'boringssl/util/bot/cmake-mac.tar.gz.sha1',
     ],
   },
-  # TODO(davidben): The cycle time on these bots is on the order of seconds, so
-  # these scripts do not currently make any attempt to avoid the extraction step
-  # when it's not needed.
+  {
+    'name': 'cmake_win32',
+    'pattern': '.',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=win32',
+                '--no_auth',
+                '--bucket', 'chromium-tools',
+                '-s', 'boringssl/util/bot/cmake-win32.zip.sha1',
+    ],
+  },
+  {
+    'name': 'perl_win32',
+    'pattern': '.',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=win32',
+                '--no_auth',
+                '--bucket', 'chromium-tools',
+                '-s', 'boringssl/util/bot/perl-win32.zip.sha1',
+    ],
+  },
+  {
+    'name': 'yasm_win32',
+    'pattern': '.',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=win32',
+                '--no_auth',
+                '--bucket', 'chromium-tools',
+                '-s', 'boringssl/util/bot/yasm-win32.exe.sha1',
+    ],
+  },
+  # TODO(davidben): Only extract archives when they've changed. Extracting perl
+  # on Windows is a significant part of the cycle time.
   {
     'name': 'cmake_linux64_extract',
     'pattern': '.',
@@ -57,4 +89,23 @@
                 'boringssl/util/bot/cmake-mac/',
     ],
   },
+  {
+    'name': 'cmake_win32_extract',
+    'pattern': '.',
+    'action': [ 'python',
+                'boringssl/util/bot/extract.py',
+                'boringssl/util/bot/cmake-win32.zip',
+                'boringssl/util/bot/cmake-win32/',
+    ],
+  },
+  {
+    'name': 'perl_win32_extract',
+    'pattern': '.',
+    'action': [ 'python',
+                'boringssl/util/bot/extract.py',
+                '--no-prefix',
+                'boringssl/util/bot/perl-win32.zip',
+                'boringssl/util/bot/perl-win32/',
+    ],
+  },
 ]
diff --git a/util/bot/cmake-win32.zip.sha1 b/util/bot/cmake-win32.zip.sha1
new file mode 100644
index 0000000..9196b58
--- /dev/null
+++ b/util/bot/cmake-win32.zip.sha1
@@ -0,0 +1 @@
+e9493171de0edd8879755aa7229a701010a19561
\ No newline at end of file
diff --git a/util/bot/extract.py b/util/bot/extract.py
index 9e36410..77603c0 100644
--- a/util/bot/extract.py
+++ b/util/bot/extract.py
@@ -15,6 +15,7 @@
 """Extracts archives."""
 
 
+import optparse
 import os
 import os.path
 import tarfile
@@ -23,21 +24,16 @@
 import zipfile
 
 
-def FixPath(output, path):
+def CheckedJoin(output, path):
   """
-  FixPath removes the first directory from path and returns the it and the
-  concatenation of output and the remainder. It does sanity checks to ensure
-  the resulting path is under output, but shouldn't be used on untrusted input.
+  CheckedJoin returns os.path.join(output, path). It does sanity checks to
+  ensure the resulting path is under output, but shouldn't be used on untrusted
+  input.
   """
-  # Even on Windows, zip files must always use forward slashes.
-  if '\\' in path or path.startswith('/'):
+  path = os.path.normpath(path)
+  if os.path.isabs(path) or path.startswith('.'):
     raise ValueError(path)
-
-  first, rest = path.split('/', 1)
-  rest = os.path.normpath(rest)
-  if os.path.isabs(rest) or rest.startswith('.'):
-    raise ValueError(rest)
-  return first, os.path.join(output, rest)
+  return os.path.join(output, path)
 
 
 def IterateZip(path):
@@ -47,6 +43,8 @@
   """
   with zipfile.ZipFile(path, 'r') as zip_file:
     for info in zip_file.infolist():
+      if info.filename.endswith('/'):
+        continue
       yield (info.filename, None, zip_file.open(info))
 
 
@@ -65,11 +63,16 @@
 
 
 def main(args):
-  if len(args) != 3:
-    print >> sys.stderr, 'Usage: %s ARCHIVE OUTPUT' % (args[0],)
+  parser = optparse.OptionParser(usage='Usage: %prog ARCHIVE OUTPUT')
+  parser.add_option('--no-prefix', dest='no_prefix', action='store_true',
+                    help='Do not remove a prefix from paths in the archive.')
+  options, args = parser.parse_args(args)
+
+  if len(args) != 2:
+    parser.print_help()
     return 1
 
-  _, archive, output = args
+  archive, output = args
 
   if not os.path.exists(archive):
     # Skip archives that weren't downloaded.
@@ -89,32 +92,48 @@
 
     print "Extracting %s to %s" % (archive, output)
     prefix = None
+    num_extracted = 0
     for path, mode, inp in entries:
-      # Pivot the path onto the output directory.
-      new_prefix, fixed_path = FixPath(output, path)
+      # Even on Windows, zip files must always use forward slashes.
+      if '\\' in path or path.startswith('/'):
+        raise ValueError(path)
 
-      # Ensure the archive is consistent.
-      if prefix is None:
-        prefix = new_prefix
-      if prefix != new_prefix:
-        raise ValueError((prefix, new_prefix))
+      if not options.no_prefix:
+        new_prefix, rest = path.split('/', 1)
 
-      # Extract the file.
+        # Ensure the archive is consistent.
+        if prefix is None:
+          prefix = new_prefix
+        if prefix != new_prefix:
+          raise ValueError((prefix, new_prefix))
+      else:
+        rest = path
+
+      # Extract the file into the output directory.
+      fixed_path = CheckedJoin(output, rest)
       if not os.path.isdir(os.path.dirname(fixed_path)):
         os.makedirs(os.path.dirname(fixed_path))
-      with open(fixed_path, 'w') as out:
-        out.write(inp.read())
+      with open(fixed_path, 'wb') as out:
+        shutil.copyfileobj(inp, out)
 
       # Fix up permissions if needbe.
       # TODO(davidben): To be extra tidy, this should only track the execute bit
       # as in git.
       if mode is not None:
         os.chmod(fixed_path, mode)
+
+      # Print every 100 files, so bots do not time out on large archives.
+      num_extracted += 1
+      if num_extracted % 100 == 0:
+        print "Extracted %d files..." % (num_extracted,)
   finally:
     entries.close()
 
+  if num_extracted % 100 == 0:
+    print "Done. Extracted %d files." % (num_extracted,)
+
   return 0
 
 
 if __name__ == '__main__':
-  sys.exit(main(sys.argv))
+  sys.exit(main(sys.argv[1:]))
diff --git a/util/bot/perl-win32.zip.sha1 b/util/bot/perl-win32.zip.sha1
new file mode 100644
index 0000000..a5559d8
--- /dev/null
+++ b/util/bot/perl-win32.zip.sha1
@@ -0,0 +1 @@
+ab6e7aee6a915c4d820b86f5227094763b649fce
\ No newline at end of file
diff --git a/util/bot/yasm-win32.exe.sha1 b/util/bot/yasm-win32.exe.sha1
new file mode 100644
index 0000000..5b8c9aa
--- /dev/null
+++ b/util/bot/yasm-win32.exe.sha1
@@ -0,0 +1 @@
+4c4d1951181a610923523cb10d83d9ae9952fbf3
\ No newline at end of file