| Index: bootstrap/bootstrap.py
|
| diff --git a/bootstrap/bootstrap.py b/bootstrap/bootstrap.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..41f20cc4ec0cfc5f141d73a0e60455aa85099975
|
| --- /dev/null
|
| +++ b/bootstrap/bootstrap.py
|
| @@ -0,0 +1,177 @@
|
| +#!/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.
|
| +
|
| +import argparse
|
| +import contextlib
|
| +import logging
|
| +import os
|
| +import shutil
|
| +import subprocess
|
| +import sys
|
| +import tempfile
|
| +
|
| +from util import STORAGE_URL, OBJECT_URL, read_deps, print_deps, platform_tag
|
| +
|
| +LOGGER = logging.getLogger(__name__)
|
| +
|
| +# /path/to/infra
|
| +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| +
|
| +
|
| +class NoWheelException(Exception):
|
| + def __init__(self, name, version, build, source_sha):
|
| + super(NoWheelException, self).__init__(
|
| + 'No matching wheel found for (%s==%s (build %s_%s))' %
|
| + (name, version, build, source_sha))
|
| +
|
| +
|
| +def ls(prefix):
|
| + from pip._vendor import requests
|
| + data = requests.get(STORAGE_URL, params=dict(
|
| + prefix=prefix,
|
| + fields='items(name,md5Hash)'
|
| + )).json()
|
| + return data.get('items', ())
|
| +
|
| +
|
| +def sha_for(deps_entry):
|
| + if 'rev' in deps_entry:
|
| + return deps_entry['rev']
|
| + else:
|
| + return deps_entry['gs'].split('.')[0]
|
| +
|
| +
|
| +def get_links(deps):
|
| + import pip.wheel
|
| +
|
| + plat_tag = platform_tag()
|
| +
|
| + links = []
|
| +
|
| + for name, dep in deps.iteritems():
|
| + version, source_sha = dep['version'] , sha_for(dep)
|
| + prefix = 'wheels/{}-{}-{}_{}'.format(name, version, dep['build'],
|
| + source_sha)
|
| + generic_link = None
|
| + binary_link = None
|
| +
|
| + for entry in ls(prefix):
|
| + fname = entry['name'].split('/')[-1]
|
| + md5hash = entry['md5Hash'].decode('base64').encode('hex')
|
| + wheel_info = pip.wheel.Wheel.wheel_file_re.match(fname)
|
| + if not wheel_info:
|
| + LOGGER.warn('Skipping invalid wheel: %r', fname)
|
| + continue
|
| +
|
| + if pip.wheel.Wheel(fname).supported():
|
| + link = OBJECT_URL.format(entry['name'], md5hash)
|
| + if plat_tag and plat_tag in fname:
|
| + if binary_link:
|
| + LOGGER.error(
|
| + 'Found more than one binary matching wheel for %r: %r',
|
| + prefix, dep)
|
| + continue
|
| + binary_link = link
|
| + else:
|
| + if generic_link:
|
| + LOGGER.error(
|
| + 'Found more than one generic matching wheel for %r: %r',
|
| + prefix, dep)
|
| + continue
|
| + generic_link = link
|
| +
|
| + if not binary_link and not generic_link:
|
| + raise NoWheelException(name, version, dep['build'], source_sha)
|
| +
|
| + links.append(binary_link or generic_link)
|
| +
|
| + return links
|
| +
|
| +
|
| +@contextlib.contextmanager
|
| +def html_index(links):
|
| + tf = tempfile.mktemp('.html')
|
| + try:
|
| + with open(tf, 'w') as f:
|
| + print >> f, '<html><body>'
|
| + for link in links:
|
| + print >> f, '<a href="%s">wat</a>' % link
|
| + print >> f, '</body></html>'
|
| + yield tf
|
| + finally:
|
| + os.unlink(tf)
|
| +
|
| +
|
| +def install(deps):
|
| + py = os.path.join(sys.prefix, 'bin', 'python')
|
| + pip = os.path.join(sys.prefix, 'bin', 'pip')
|
| +
|
| + links = get_links(deps)
|
| + with html_index(links) as ipath:
|
| + requirements = []
|
| + # TODO(iannucci): Do this as a requirements.txt
|
| + for name, deps_entry in deps.iteritems():
|
| + if not deps_entry.get('implicit'):
|
| + requirements.append('%s==%s' % (name, deps_entry['version']))
|
| + subprocess.check_call(
|
| + [py, pip, 'install', '--no-index', '--download-cache',
|
| + os.path.join(ROOT, '.wheelcache'), '-f', ipath] + requirements)
|
| +
|
| +
|
| +def activate_env(env, deps):
|
| + if hasattr(sys, 'real_prefix'):
|
| + LOGGER.error('Already activated environment!')
|
| + return
|
| +
|
| + print 'Activating environment: %r' % env
|
| + if isinstance(deps, basestring):
|
| + deps = read_deps(deps)
|
| + assert deps is not None
|
| +
|
| + manifest_path = os.path.join(env, 'manifest.pyl')
|
| + cur_deps = read_deps(manifest_path)
|
| + if cur_deps != deps:
|
| + print ' Removing old environment: %r' % cur_deps
|
| + shutil.rmtree(env, ignore_errors=True)
|
| + cur_deps = None
|
| +
|
| + if cur_deps is None:
|
| + print ' Building new environment'
|
| + # Add in bundled virtualenv lib
|
| + sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'virtualenv'))
|
| + import virtualenv # pylint: disable=F0401
|
| + virtualenv.create_environment(
|
| + env, search_dirs=virtualenv.file_search_dirs())
|
| +
|
| + print ' Activating environment'
|
| + activate_this = os.path.join(env, 'bin', 'activate_this.py')
|
| + execfile(activate_this, dict(__file__=activate_this))
|
| +
|
| + if cur_deps is None:
|
| + print ' Installing deps'
|
| + print_deps(deps, indent=2, with_implicit=False)
|
| + install(deps)
|
| + with open(manifest_path, 'wb') as f:
|
| + f.write(repr(deps) + '\n')
|
| +
|
| + print 'Done creating environment'
|
| +
|
| +
|
| +def main(args):
|
| + parser = argparse.ArgumentParser()
|
| + parser.add_argument('--deps_file',
|
| + help='Path to python deps file (default: %(default)s)')
|
| + parser.add_argument('env_path', nargs='?',
|
| + help='Path to place environment (default: %(default)s)',
|
| + default='ENV')
|
| + opts = parser.parse_args(args)
|
| +
|
| + activate_env(opts.env_path, opts.deps_file or {})
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + logging.basicConfig()
|
| + LOGGER.setLevel(logging.DEBUG)
|
| + sys.exit(main(sys.argv[1:]))
|
|
|