summaryrefslogtreecommitdiffstats
path: root/src/util/bot
diff options
context:
space:
mode:
authorAdam Langley <agl@google.com>2015-05-11 17:20:37 -0700
committerKenny Root <kroot@google.com>2015-05-12 23:06:14 +0000
commite9ada863a7b3e81f5d2b1e3bdd2305da902a87f5 (patch)
tree6e43e34595ecf887c26c32b86d8ab097fe8cac64 /src/util/bot
parentb3106a0cc1493bbe0505c0ec0ce3da4ca90a29ae (diff)
downloadexternal_boringssl-e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5.zip
external_boringssl-e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5.tar.gz
external_boringssl-e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5.tar.bz2
external/boringssl: bump revision.
This change bumps the BoringSSL revision to the current tip-of-tree. Change-Id: I91d5bf467e16e8d86cb19a4de873985f524e5faa
Diffstat (limited to 'src/util/bot')
-rw-r--r--src/util/bot/DEPS134
-rw-r--r--src/util/bot/README3
-rw-r--r--src/util/bot/cmake-linux64.tar.gz.sha11
-rw-r--r--src/util/bot/cmake-mac.tar.gz.sha11
-rw-r--r--src/util/bot/cmake-win32.zip.sha11
-rw-r--r--src/util/bot/extract.py139
-rwxr-xr-xsrc/util/bot/go/bootstrap.py297
-rwxr-xr-xsrc/util/bot/go/env.py49
-rw-r--r--src/util/bot/perl-win32.zip.sha11
-rw-r--r--src/util/bot/toolchain_vs2013.hash1
-rw-r--r--src/util/bot/update_clang.py71
-rw-r--r--src/util/bot/vs_env.py37
-rw-r--r--src/util/bot/vs_toolchain.py114
-rw-r--r--src/util/bot/yasm-win32.exe.sha11
14 files changed, 850 insertions, 0 deletions
diff --git a/src/util/bot/DEPS b/src/util/bot/DEPS
new file mode 100644
index 0000000..738fbd3
--- /dev/null
+++ b/src/util/bot/DEPS
@@ -0,0 +1,134 @@
+# 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.
+
+vars = {
+ 'chromium_git': 'https://chromium.googlesource.com',
+}
+
+deps = {
+ 'boringssl/util/bot/gyp':
+ Var('chromium_git') + '/external/gyp.git' + '@' + '4a9b712d5cb4a5ba7a9950128a7219569caf7263',
+}
+
+hooks = [
+ {
+ 'name': 'cmake_linux64',
+ 'pattern': '.',
+ 'action': [ 'download_from_google_storage',
+ '--no_resume',
+ '--platform=linux*',
+ '--no_auth',
+ '--bucket', 'chromium-tools',
+ '-s', 'boringssl/util/bot/cmake-linux64.tar.gz.sha1',
+ ],
+ },
+ {
+ 'name': 'cmake_mac',
+ 'pattern': '.',
+ 'action': [ 'download_from_google_storage',
+ '--no_resume',
+ '--platform=darwin',
+ '--no_auth',
+ '--bucket', 'chromium-tools',
+ '-s', 'boringssl/util/bot/cmake-mac.tar.gz.sha1',
+ ],
+ },
+ {
+ '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',
+ ],
+ },
+ {
+ 'name': 'win_toolchain',
+ 'pattern': '.',
+ 'action': [ 'python',
+ 'boringssl/util/bot/vs_toolchain.py',
+ 'update',
+ ],
+ },
+ {
+ 'name': 'clang',
+ 'pattern': '.',
+ 'action': [ 'python',
+ 'boringssl/util/bot/update_clang.py',
+ ],
+ },
+ # 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': '.',
+ 'action': [ 'python',
+ 'boringssl/util/bot/extract.py',
+ 'boringssl/util/bot/cmake-linux64.tar.gz',
+ 'boringssl/util/bot/cmake-linux64/',
+ ],
+ },
+ {
+ 'name': 'cmake_mac_extract',
+ 'pattern': '.',
+ 'action': [ 'python',
+ 'boringssl/util/bot/extract.py',
+ 'boringssl/util/bot/cmake-mac.tar.gz',
+ '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/src/util/bot/README b/src/util/bot/README
new file mode 100644
index 0000000..b7a4332
--- /dev/null
+++ b/src/util/bot/README
@@ -0,0 +1,3 @@
+This directory contains tools for setting up a hermetic toolchain on the
+continuous integration bots. It is in the repository for convenience and can be
+ignored in development.
diff --git a/src/util/bot/cmake-linux64.tar.gz.sha1 b/src/util/bot/cmake-linux64.tar.gz.sha1
new file mode 100644
index 0000000..6a8aa1c
--- /dev/null
+++ b/src/util/bot/cmake-linux64.tar.gz.sha1
@@ -0,0 +1 @@
+32cd1d5fe84ae569dbb36f5767650d62efb8be38 \ No newline at end of file
diff --git a/src/util/bot/cmake-mac.tar.gz.sha1 b/src/util/bot/cmake-mac.tar.gz.sha1
new file mode 100644
index 0000000..cb7251b
--- /dev/null
+++ b/src/util/bot/cmake-mac.tar.gz.sha1
@@ -0,0 +1 @@
+310df6945ae7f8c9da559d22f5794ee8e578a663 \ No newline at end of file
diff --git a/src/util/bot/cmake-win32.zip.sha1 b/src/util/bot/cmake-win32.zip.sha1
new file mode 100644
index 0000000..9196b58
--- /dev/null
+++ b/src/util/bot/cmake-win32.zip.sha1
@@ -0,0 +1 @@
+e9493171de0edd8879755aa7229a701010a19561 \ No newline at end of file
diff --git a/src/util/bot/extract.py b/src/util/bot/extract.py
new file mode 100644
index 0000000..77603c0
--- /dev/null
+++ b/src/util/bot/extract.py
@@ -0,0 +1,139 @@
+# 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.
+
+"""Extracts archives."""
+
+
+import optparse
+import os
+import os.path
+import tarfile
+import shutil
+import sys
+import zipfile
+
+
+def CheckedJoin(output, path):
+ """
+ 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.
+ """
+ path = os.path.normpath(path)
+ if os.path.isabs(path) or path.startswith('.'):
+ raise ValueError(path)
+ return os.path.join(output, path)
+
+
+def IterateZip(path):
+ """
+ IterateZip opens the zip file at path and returns a generator of
+ (filename, mode, fileobj) tuples for each file in it.
+ """
+ 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))
+
+
+def IterateTar(path):
+ """
+ IterateTar opens the tar.gz file at path and returns a generator of
+ (filename, mode, fileobj) tuples for each file in it.
+ """
+ with tarfile.open(path, 'r:gz') as tar_file:
+ for info in tar_file:
+ if info.isdir():
+ continue
+ if not info.isfile():
+ raise ValueError('Unknown entry type "%s"' % (info.name, ))
+ yield (info.name, info.mode, tar_file.extractfile(info))
+
+
+def main(args):
+ 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
+
+ if not os.path.exists(archive):
+ # Skip archives that weren't downloaded.
+ return 0
+
+ if archive.endswith('.zip'):
+ entries = IterateZip(archive)
+ elif archive.endswith('.tar.gz'):
+ entries = IterateTar(archive)
+ else:
+ raise ValueError(archive)
+
+ try:
+ if os.path.exists(output):
+ print "Removing %s" % (output, )
+ shutil.rmtree(output)
+
+ print "Extracting %s to %s" % (archive, output)
+ prefix = None
+ num_extracted = 0
+ for path, mode, inp in entries:
+ # Even on Windows, zip files must always use forward slashes.
+ if '\\' in path or path.startswith('/'):
+ raise ValueError(path)
+
+ if not options.no_prefix:
+ new_prefix, rest = path.split('/', 1)
+
+ # 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, '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[1:]))
diff --git a/src/util/bot/go/bootstrap.py b/src/util/bot/go/bootstrap.py
new file mode 100755
index 0000000..166ef3b
--- /dev/null
+++ b/src/util/bot/go/bootstrap.py
@@ -0,0 +1,297 @@
+#!/usr/bin/env python
+# 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.
+
+# Modified from go/bootstrap.py in Chromium infrastructure's repository to patch
+# out everything but the core toolchain.
+#
+# https://chromium.googlesource.com/infra/infra/
+
+"""Prepares a local hermetic Go installation.
+
+- Downloads and unpacks the Go toolset in ../golang.
+"""
+
+import contextlib
+import logging
+import os
+import platform
+import shutil
+import stat
+import subprocess
+import sys
+import tarfile
+import tempfile
+import urllib
+import zipfile
+
+# TODO(vadimsh): Migrate to new golang.org/x/ paths once Golang moves to
+# git completely.
+
+LOGGER = logging.getLogger(__name__)
+
+
+# /path/to/util/bot
+ROOT = os.path.dirname(os.path.abspath(__file__))
+
+# Where to install Go toolset to. GOROOT would be <TOOLSET_ROOT>/go.
+TOOLSET_ROOT = os.path.join(os.path.dirname(ROOT), 'golang')
+
+# Default workspace with infra go code.
+WORKSPACE = os.path.join(ROOT, 'go')
+
+# Platform depended suffix for executable files.
+EXE_SFX = '.exe' if sys.platform == 'win32' else ''
+
+# Pinned version of Go toolset to download.
+TOOLSET_VERSION = 'go1.4'
+
+# Platform dependent portion of a download URL. See http://golang.org/dl/.
+TOOLSET_VARIANTS = {
+ ('darwin', 'x86-32'): 'darwin-386-osx10.8.tar.gz',
+ ('darwin', 'x86-64'): 'darwin-amd64-osx10.8.tar.gz',
+ ('linux2', 'x86-32'): 'linux-386.tar.gz',
+ ('linux2', 'x86-64'): 'linux-amd64.tar.gz',
+ ('win32', 'x86-32'): 'windows-386.zip',
+ ('win32', 'x86-64'): 'windows-amd64.zip',
+}
+
+# Download URL root.
+DOWNLOAD_URL_PREFIX = 'https://storage.googleapis.com/golang'
+
+
+class Failure(Exception):
+ """Bootstrap failed."""
+
+
+def get_toolset_url():
+ """URL of a platform specific Go toolset archive."""
+ # TODO(vadimsh): Support toolset for cross-compilation.
+ arch = {
+ 'amd64': 'x86-64',
+ 'x86_64': 'x86-64',
+ 'i386': 'x86-32',
+ 'x86': 'x86-32',
+ }.get(platform.machine().lower())
+ variant = TOOLSET_VARIANTS.get((sys.platform, arch))
+ if not variant:
+ # TODO(vadimsh): Compile go lang from source.
+ raise Failure('Unrecognized platform')
+ return '%s/%s.%s' % (DOWNLOAD_URL_PREFIX, TOOLSET_VERSION, variant)
+
+
+def read_file(path):
+ """Returns contents of a given file or None if not readable."""
+ assert isinstance(path, (list, tuple))
+ try:
+ with open(os.path.join(*path), 'r') as f:
+ return f.read()
+ except IOError:
+ return None
+
+
+def write_file(path, data):
+ """Writes |data| to a file."""
+ assert isinstance(path, (list, tuple))
+ with open(os.path.join(*path), 'w') as f:
+ f.write(data)
+
+
+def remove_directory(path):
+ """Recursively removes a directory."""
+ assert isinstance(path, (list, tuple))
+ p = os.path.join(*path)
+ if not os.path.exists(p):
+ return
+ LOGGER.info('Removing %s', p)
+ # Crutch to remove read-only file (.git/* in particular) on Windows.
+ def onerror(func, path, _exc_info):
+ if not os.access(path, os.W_OK):
+ os.chmod(path, stat.S_IWUSR)
+ func(path)
+ else:
+ raise
+ shutil.rmtree(p, onerror=onerror if sys.platform == 'win32' else None)
+
+
+def install_toolset(toolset_root, url):
+ """Downloads and installs Go toolset.
+
+ GOROOT would be <toolset_root>/go/.
+ """
+ if not os.path.exists(toolset_root):
+ os.makedirs(toolset_root)
+ pkg_path = os.path.join(toolset_root, url[url.rfind('/')+1:])
+
+ LOGGER.info('Downloading %s...', url)
+ download_file(url, pkg_path)
+
+ LOGGER.info('Extracting...')
+ if pkg_path.endswith('.zip'):
+ with zipfile.ZipFile(pkg_path, 'r') as f:
+ f.extractall(toolset_root)
+ elif pkg_path.endswith('.tar.gz'):
+ with tarfile.open(pkg_path, 'r:gz') as f:
+ f.extractall(toolset_root)
+ else:
+ raise Failure('Unrecognized archive format')
+
+ LOGGER.info('Validating...')
+ if not check_hello_world(toolset_root):
+ raise Failure('Something is not right, test program doesn\'t work')
+
+
+def download_file(url, path):
+ """Fetches |url| to |path|."""
+ last_progress = [0]
+ def report(a, b, c):
+ progress = int(a * b * 100.0 / c)
+ if progress != last_progress[0]:
+ print >> sys.stderr, 'Downloading... %d%%' % progress
+ last_progress[0] = progress
+ # TODO(vadimsh): Use something less crippled, something that validates SSL.
+ urllib.urlretrieve(url, path, reporthook=report)
+
+
+@contextlib.contextmanager
+def temp_dir(path):
+ """Creates a temporary directory, then deletes it."""
+ tmp = tempfile.mkdtemp(dir=path)
+ try:
+ yield tmp
+ finally:
+ remove_directory([tmp])
+
+
+def check_hello_world(toolset_root):
+ """Compiles and runs 'hello world' program to verify that toolset works."""
+ with temp_dir(toolset_root) as tmp:
+ path = os.path.join(tmp, 'hello.go')
+ write_file([path], r"""
+ package main
+ func main() { println("hello, world\n") }
+ """)
+ out = subprocess.check_output(
+ [get_go_exe(toolset_root), 'run', path],
+ env=get_go_environ(toolset_root, tmp),
+ stderr=subprocess.STDOUT)
+ if out.strip() != 'hello, world':
+ LOGGER.error('Failed to run sample program:\n%s', out)
+ return False
+ return True
+
+
+def ensure_toolset_installed(toolset_root):
+ """Installs or updates Go toolset if necessary.
+
+ Returns True if new toolset was installed.
+ """
+ installed = read_file([toolset_root, 'INSTALLED_TOOLSET'])
+ available = get_toolset_url()
+ if installed == available:
+ LOGGER.debug('Go toolset is up-to-date: %s', TOOLSET_VERSION)
+ return False
+
+ LOGGER.info('Installing Go toolset.')
+ LOGGER.info(' Old toolset is %s', installed)
+ LOGGER.info(' New toolset is %s', available)
+ remove_directory([toolset_root])
+ install_toolset(toolset_root, available)
+ LOGGER.info('Go toolset installed: %s', TOOLSET_VERSION)
+ write_file([toolset_root, 'INSTALLED_TOOLSET'], available)
+ return True
+
+
+def get_go_environ(
+ toolset_root,
+ workspace=None):
+ """Returns a copy of os.environ with added GO* environment variables.
+
+ Overrides GOROOT, GOPATH and GOBIN. Keeps everything else. Idempotent.
+
+ Args:
+ toolset_root: GOROOT would be <toolset_root>/go.
+ workspace: main workspace directory or None if compiling in GOROOT.
+ """
+ env = os.environ.copy()
+ env['GOROOT'] = os.path.join(toolset_root, 'go')
+ if workspace:
+ env['GOBIN'] = os.path.join(workspace, 'bin')
+ else:
+ env.pop('GOBIN', None)
+
+ all_go_paths = []
+ if workspace:
+ all_go_paths.append(workspace)
+ env['GOPATH'] = os.pathsep.join(all_go_paths)
+
+ # New PATH entries.
+ paths_to_add = [
+ os.path.join(env['GOROOT'], 'bin'),
+ env.get('GOBIN'),
+ ]
+
+ # Make sure not to add duplicates entries to PATH over and over again when
+ # get_go_environ is invoked multiple times.
+ path = env['PATH'].split(os.pathsep)
+ paths_to_add = [p for p in paths_to_add if p and p not in path]
+ env['PATH'] = os.pathsep.join(paths_to_add + path)
+
+ return env
+
+
+def get_go_exe(toolset_root):
+ """Returns path to go executable."""
+ return os.path.join(toolset_root, 'go', 'bin', 'go' + EXE_SFX)
+
+
+def bootstrap(logging_level):
+ """Installs all dependencies in default locations.
+
+ Supposed to be called at the beginning of some script (it modifies logger).
+
+ Args:
+ logging_level: logging level of bootstrap process.
+ """
+ logging.basicConfig()
+ LOGGER.setLevel(logging_level)
+ ensure_toolset_installed(TOOLSET_ROOT)
+
+
+def prepare_go_environ():
+ """Returns dict with environment variables to set to use Go toolset.
+
+ Installs or updates the toolset if necessary.
+ """
+ bootstrap(logging.INFO)
+ return get_go_environ(TOOLSET_ROOT, WORKSPACE)
+
+
+def find_executable(name, workspaces):
+ """Returns full path to an executable in some bin/ (in GOROOT or GOBIN)."""
+ basename = name
+ if EXE_SFX and basename.endswith(EXE_SFX):
+ basename = basename[:-len(EXE_SFX)]
+ roots = [os.path.join(TOOLSET_ROOT, 'go', 'bin')]
+ for path in workspaces:
+ roots.extend([
+ os.path.join(path, 'bin'),
+ ])
+ for root in roots:
+ full_path = os.path.join(root, basename + EXE_SFX)
+ if os.path.exists(full_path):
+ return full_path
+ return name
+
+
+def main(args):
+ if args:
+ print >> sys.stderr, sys.modules[__name__].__doc__,
+ return 2
+ bootstrap(logging.DEBUG)
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/src/util/bot/go/env.py b/src/util/bot/go/env.py
new file mode 100755
index 0000000..820968c
--- /dev/null
+++ b/src/util/bot/go/env.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# 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.
+
+# Modified from go/env.py in Chromium infrastructure's repository to patch out
+# everything but the core toolchain.
+#
+# https://chromium.googlesource.com/infra/infra/
+
+"""Can be used to point environment variable to hermetic Go toolset.
+
+Usage (on linux and mac):
+$ eval `./env.py`
+$ go version
+
+Or it can be used to wrap a command:
+
+$ ./env.py go version
+"""
+
+assert __name__ == '__main__'
+
+import imp
+import os
+import subprocess
+import sys
+
+# Do not want to mess with sys.path, load the module directly.
+bootstrap = imp.load_source(
+ 'bootstrap', os.path.join(os.path.dirname(__file__), 'bootstrap.py'))
+
+old = os.environ.copy()
+new = bootstrap.prepare_go_environ()
+
+if len(sys.argv) == 1:
+ for key, value in sorted(new.iteritems()):
+ if old.get(key) != value:
+ print 'export %s="%s"' % (key, value)
+else:
+ exe = sys.argv[1]
+ if exe == 'python':
+ exe = sys.executable
+ else:
+ # Help Windows to find the executable in new PATH, do it only when
+ # executable is referenced by name (and not by path).
+ if os.sep not in exe:
+ exe = bootstrap.find_executable(exe, [bootstrap.WORKSPACE])
+ sys.exit(subprocess.call([exe] + sys.argv[2:], env=new))
diff --git a/src/util/bot/perl-win32.zip.sha1 b/src/util/bot/perl-win32.zip.sha1
new file mode 100644
index 0000000..a5559d8
--- /dev/null
+++ b/src/util/bot/perl-win32.zip.sha1
@@ -0,0 +1 @@
+ab6e7aee6a915c4d820b86f5227094763b649fce \ No newline at end of file
diff --git a/src/util/bot/toolchain_vs2013.hash b/src/util/bot/toolchain_vs2013.hash
new file mode 100644
index 0000000..4ed8816
--- /dev/null
+++ b/src/util/bot/toolchain_vs2013.hash
@@ -0,0 +1 @@
+ee7d718ec60c2dc5d255bbe325909c2021a7efef
diff --git a/src/util/bot/update_clang.py b/src/util/bot/update_clang.py
new file mode 100644
index 0000000..0836d11
--- /dev/null
+++ b/src/util/bot/update_clang.py
@@ -0,0 +1,71 @@
+# 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 os.path
+import shutil
+import sys
+import tarfile
+import tempfile
+import urllib
+
+# CLANG_REVISION and CLANG_SUB_REVISION determine the build of clang
+# to use. These should be synced with tools/clang/scripts/update.sh in
+# Chromium.
+CLANG_REVISION = "233105"
+CLANG_SUB_REVISION = "1"
+
+PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
+LLVM_BUILD_DIR = os.path.join(os.path.dirname(__file__), "llvm-build")
+STAMP_FILE = os.path.join(LLVM_BUILD_DIR, "cr_build_revision")
+
+CDS_URL = "https://commondatastorage.googleapis.com/chromium-browser-clang"
+
+def DownloadFile(url, path):
+ """DownloadFile fetches |url| to |path|."""
+ last_progress = [0]
+ def report(a, b, c):
+ progress = int(a * b * 100.0 / c)
+ if progress != last_progress[0]:
+ print >> sys.stderr, "Downloading... %d%%" % progress
+ last_progress[0] = progress
+ urllib.urlretrieve(url, path, reporthook=report)
+
+def main(args):
+ # For now, only download clang on Linux.
+ if not sys.platform.startswith("linux"):
+ return 0
+
+ if os.path.exists(STAMP_FILE):
+ with open(STAMP_FILE) as f:
+ if f.read().strip() == PACKAGE_VERSION:
+ print >> sys.stderr, "Clang already at %s" % (PACKAGE_VERSION,)
+ return 0
+
+ if os.path.exists(LLVM_BUILD_DIR):
+ shutil.rmtree(LLVM_BUILD_DIR)
+
+ print >> sys.stderr, "Downloading Clang %s" % (PACKAGE_VERSION,)
+ cds_full_url = "%s/Linux_x64/clang-%s.tgz" % (CDS_URL, PACKAGE_VERSION)
+ with tempfile.NamedTemporaryFile() as temp:
+ DownloadFile(cds_full_url, temp.name)
+ with tarfile.open(temp.name, "r:gz") as tar_file:
+ tar_file.extractall(LLVM_BUILD_DIR)
+
+ with open(STAMP_FILE, "wb") as stamp_file:
+ stamp_file.write(PACKAGE_VERSION)
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv[1:]))
diff --git a/src/util/bot/vs_env.py b/src/util/bot/vs_env.py
new file mode 100644
index 0000000..1847500
--- /dev/null
+++ b/src/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/src/util/bot/vs_toolchain.py b/src/util/bot/vs_toolchain.py
new file mode 100644
index 0000000..fd76f39
--- /dev/null
+++ b/src/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())
diff --git a/src/util/bot/yasm-win32.exe.sha1 b/src/util/bot/yasm-win32.exe.sha1
new file mode 100644
index 0000000..5b8c9aa
--- /dev/null
+++ b/src/util/bot/yasm-win32.exe.sha1
@@ -0,0 +1 @@
+4c4d1951181a610923523cb10d83d9ae9952fbf3 \ No newline at end of file