Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Side by Side Diff: bootstrap/bootstrap.py

Issue 2061263003: add basic bootstrap for coverage (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/recipes-py@master
Patch Set: Add recipes.py flag. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2016 The LUCI Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0
4 # that can be found in the LICENSE file.
5
6 import argparse
7 import contextlib
8 import glob
9 import logging
10 import os
11 import re
12 import shutil
13 import subprocess
14 import sys
15 import tempfile
16
17 from util import STORAGE_URL, OBJECT_URL, LOCAL_STORAGE_PATH, LOCAL_OBJECT_URL
18 from util import read_deps, merge_deps, print_deps, platform_tag
19
20 LOGGER = logging.getLogger(__name__)
21
22 # /path/to/infra
Paweł Hajdan Jr. 2016/06/15 10:56:19 nit: Update this comment (it's recipes-py repo, no
martiniss 2016/06/15 20:36:53 Done.
23 ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
24
25 PYTHON_BAT_WIN = '@%~dp0\\..\\Scripts\\python.exe %*'
26
27
28 class NoWheelException(Exception):
29 def __init__(self, name, version, build, source_sha):
30 super(NoWheelException, self).__init__(
31 'No matching wheel found for (%s==%s (build %s_%s))' %
32 (name, version, build, source_sha))
33
34
35 def check_pydistutils():
36 if os.path.exists(os.path.expanduser('~/.pydistutils.cfg')):
37 print >> sys.stderr, '\n'.join([
38 '',
39 '',
40 '=========== ERROR ===========',
41 'You have a ~/.pydistutils.cfg file, which interferes with the ',
42 'infra virtualenv environment. Please move it to the side and bootstrap ',
Paweł Hajdan Jr. 2016/06/15 10:56:19 nit: Also consider replacing infra here and below.
martiniss 2016/06/15 20:36:53 Done.
43 'again. Once infra has bootstrapped, you may move it back.',
44 '',
45 'Upstream bug: https://github.com/pypa/virtualenv/issues/88/',
46 ''
47 ])
48 sys.exit(1)
49
50
51 def ls(prefix):
52 from pip._vendor import requests # pylint: disable=E0611
53 data = requests.get(STORAGE_URL, params=dict(
54 prefix=prefix,
55 fields='items(name,md5Hash)'
56 )).json()
57 entries = data.get('items', [])
58 for entry in entries:
59 entry['md5Hash'] = entry['md5Hash'].decode('base64').encode('hex')
60 entry['local'] = False
61 # Also look in the local cache
62 entries.extend([
63 {'name': fname, 'md5Hash': None, 'local': True}
64 for fname in glob.glob(os.path.join(LOCAL_STORAGE_PATH,
65 prefix.split('/')[-1] + '*'))])
66 return entries
67
68
69 def sha_for(deps_entry):
70 if 'rev' in deps_entry:
71 return deps_entry['rev']
72 else:
73 return deps_entry['gs'].split('.')[0]
74
75
76 def get_links(deps):
77 import pip.wheel # pylint: disable=E0611
78 plat_tag = platform_tag()
79
80 links = []
81
82 for name, dep in deps.iteritems():
83 version, source_sha = dep['version'] , sha_for(dep)
84 prefix = 'wheels/{}-{}-{}_{}'.format(name, version, dep['build'],
85 source_sha)
86 generic_link = None
87 binary_link = None
88 local_link = None
89
90 for entry in ls(prefix):
91 fname = entry['name'].split('/')[-1]
92 md5hash = entry['md5Hash']
93 wheel_info = pip.wheel.Wheel.wheel_file_re.match(fname)
94 if not wheel_info:
95 LOGGER.warn('Skipping invalid wheel: %r', fname)
96 continue
97
98 if pip.wheel.Wheel(fname).supported():
99 if entry['local']:
100 link = LOCAL_OBJECT_URL.format(entry['name'])
101 local_link = link
102 continue
103 else:
104 link = OBJECT_URL.format(entry['name'], md5hash)
105 if fname.endswith('none-any.whl'):
106 if generic_link:
107 LOGGER.error(
108 'Found more than one generic matching wheel for %r: %r',
109 prefix, dep)
110 continue
111 generic_link = link
112 elif plat_tag in fname:
113 if binary_link:
114 LOGGER.error(
115 'Found more than one binary matching wheel for %r: %r',
116 prefix, dep)
117 continue
118 binary_link = link
119
120 if not binary_link and not generic_link and not local_link:
121 raise NoWheelException(name, version, dep['build'], source_sha)
122
123 links.append(local_link or binary_link or generic_link)
124
125 return links
126
127
128 @contextlib.contextmanager
129 def html_index(links):
130 tf = tempfile.mktemp('.html')
131 try:
132 with open(tf, 'w') as f:
133 print >> f, '<html><body>'
134 for link in links:
135 print >> f, '<a href="%s">wat</a>' % link
136 print >> f, '</body></html>'
137 yield tf
138 finally:
139 os.unlink(tf)
140
141
142 def install(deps):
143 bin_dir = 'Scripts' if sys.platform.startswith('win') else 'bin'
144 pip = os.path.join(sys.prefix, bin_dir, 'pip')
145
146 links = get_links(deps)
147 with html_index(links) as ipath:
148 requirements = []
149 # TODO(iannucci): Do this as a requirements.txt
150 for name, deps_entry in deps.iteritems():
151 if not deps_entry.get('implicit'):
152 requirements.append('%s==%s' % (name, deps_entry['version']))
153 subprocess.check_call(
154 [pip, 'install', '--no-index', '--download-cache',
155 os.path.join(ROOT, '.wheelcache'), '-f', ipath] + requirements)
156
157
158 def activate_env(env, deps, quiet=False):
159 if hasattr(sys, 'real_prefix'):
160 LOGGER.error('Already activated environment!')
161 return
162
163 if not quiet:
164 print 'Activating environment: %r' % env
165 assert isinstance(deps, dict)
166
167 manifest_path = os.path.join(env, 'manifest.pyl')
168 cur_deps = read_deps(manifest_path)
169 if cur_deps != deps:
170 if not quiet:
171 print ' Removing old environment: %r' % cur_deps
172 shutil.rmtree(env, ignore_errors=True)
173 cur_deps = None
174
175 if cur_deps is None:
176 check_pydistutils()
177
178 if not quiet:
179 print ' Building new environment'
180 # Add in bundled virtualenv lib
181 sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'virtualenv'))
182 import virtualenv # pylint: disable=F0401
183 virtualenv.create_environment(
184 env, search_dirs=virtualenv.file_search_dirs())
185
186 # Hack: On windows orig-prefix.txt contains the hardcoded path
187 # "E:\b\depot_tools\python276_bin", but some systems have depot_tools
188 # installed on C:\ instead, so fiddle site.py to try loading it from there
189 # as well.
190 if sys.platform.startswith('win'):
191 site_py_path = os.path.join(env, 'Lib\\site.py')
192 with open(site_py_path) as fh:
193 site_py = fh.read()
194
195 m = re.search(r'( +)sys\.real_prefix = .*', site_py)
196 replacement = ('%(indent)sif (sys.real_prefix.startswith("E:\\\\") and\n'
197 '%(indent)s not os.path.exists(sys.real_prefix)):\n'
198 '%(indent)s cand = "C:\\\\setup" + sys.real_prefix[4:]\n'
199 '%(indent)s if os.path.exists(cand):\n'
200 '%(indent)s sys.real_prefix = cand\n'
201 '%(indent)s else:\n'
202 '%(indent)s sys.real_prefix = "C" + sys.real_prefix'
203 '[1:]\n'
204 % {'indent': m.group(1)})
205
206 site_py = site_py[:m.end(0)] + '\n' + replacement + site_py[m.end(0):]
207 with open(site_py_path, 'w') as fh:
208 fh.write(site_py)
209
210 if not quiet:
211 print ' Activating environment'
212 # Ensure hermeticity during activation.
213 os.environ.pop('PYTHONPATH', None)
214 bin_dir = 'Scripts' if sys.platform.startswith('win') else 'bin'
215 activate_this = os.path.join(env, bin_dir, 'activate_this.py')
216 execfile(activate_this, dict(__file__=activate_this))
217
218 if cur_deps is None:
219 if not quiet:
220 print ' Installing deps'
221 print_deps(deps, indent=2, with_implicit=False)
222 install(deps)
223 virtualenv.make_environment_relocatable(env)
224 with open(manifest_path, 'wb') as f:
225 f.write(repr(deps) + '\n')
226
227 # Create bin\python.bat on Windows to unify path where Python is found.
228 if sys.platform.startswith('win'):
229 bin_path = os.path.join(env, 'bin')
230 if not os.path.isdir(bin_path):
231 os.makedirs(bin_path)
232 python_bat_path = os.path.join(bin_path, 'python.bat')
233 if not os.path.isfile(python_bat_path):
234 with open(python_bat_path, 'w') as python_bat_file:
235 python_bat_file.write(PYTHON_BAT_WIN)
236
237 if not quiet:
238 print 'Done creating environment'
239
240
241 def main(args):
242 parser = argparse.ArgumentParser()
243 parser.add_argument('--deps-file', '--deps_file', action='append',
244 help='Path to deps.pyl file (may be used multiple times)')
245 parser.add_argument('-q', '--quiet', action='store_true', default=False,
246 help='Supress all output')
247 parser.add_argument('env_path',
248 help='Path to place environment (default: %(default)s)',
249 default='ENV')
250 opts = parser.parse_args(args)
251
252 deps = merge_deps(opts.deps_file)
253 activate_env(opts.env_path, deps, opts.quiet)
254
255
256 if __name__ == '__main__':
257 logging.basicConfig()
258 LOGGER.setLevel(logging.DEBUG)
259 sys.exit(main(sys.argv[1:]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698