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

Side by Side Diff: tools/roll_swiftshader.py

Issue 2769543003: Add DEPS roll script for SwiftShader. (Closed)
Patch Set: Made script executable on Linux. Created 3 years, 9 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2015 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 import argparse
7 import collections
8 import logging
9 import os
10 import re
11 import subprocess
12 import sys
13 import time
14
15 extra_cq_trybots = [
16 {
17 "mastername": "master.tryserver.chromium.win",
18 "buildernames": ["win_optional_gpu_tests_rel"]
19 },
20 {
21 "mastername": "master.tryserver.chromium.mac",
22 "buildernames": ["mac_optional_gpu_tests_rel"]
23 },
24 {
25 "mastername": "master.tryserver.chromium.linux",
26 "buildernames": ["linux_optional_gpu_tests_rel"]
27 },
28 {
29 "mastername": "master.tryserver.chromium.android",
30 "buildernames": ["android_optional_gpu_tests_rel"]
31 }
32 ]
33 extra_fyi_trybots = [
34 {
35 "mastername": "master.tryserver.chromium.win",
36 "buildernames": ["win_clang_dbg"]
37 }
38 ]
39
40 SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
41 SRC_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, os.pardir))
42 sys.path.insert(0, os.path.join(SRC_DIR, 'build'))
43 import find_depot_tools
44 find_depot_tools.add_depot_tools_to_path()
45 import roll_dep_svn
46 from gclient import GClientKeywords
47 from third_party import upload
48
49 # Avoid depot_tools/third_party/upload.py print verbose messages.
50 upload.verbosity = 0 # Errors only.
51
52 CHROMIUM_GIT_URL = 'https://chromium.googlesource.com/chromium/src.git'
53 CL_ISSUE_RE = re.compile('^Issue number: ([0-9]+) \((.*)\)$')
54 RIETVELD_URL_RE = re.compile('^https?://(.*)/(.*)')
55 ROLL_BRANCH_NAME = 'special_swiftshader_roll_branch'
56 TRYJOB_STATUS_SLEEP_SECONDS = 30
57
58 # Use a shell for subcommands on Windows to get a PATH search.
59 IS_WIN = sys.platform.startswith('win')
60 SWIFTSHADER_PATH = os.path.join('third_party', 'swiftshader')
61
62 CommitInfo = collections.namedtuple('CommitInfo', ['git_commit',
63 'git_repo_url'])
64 CLInfo = collections.namedtuple('CLInfo', ['issue', 'url', 'rietveld_server'])
65
66 def _PosixPath(path):
67 """Convert a possibly-Windows path to a posix-style path."""
68 (_, path) = os.path.splitdrive(path)
69 return path.replace(os.sep, '/')
70
71 def _ParseGitCommitHash(description):
72 for line in description.splitlines():
73 if line.startswith('commit '):
74 return line.split()[1]
75 logging.error('Failed to parse git commit id from:\n%s\n', description)
76 sys.exit(-1)
77 return None
78
79
80 def _ParseDepsFile(filename):
81 with open(filename, 'rb') as f:
82 deps_content = f.read()
83 return _ParseDepsDict(deps_content)
84
85
86 def _ParseDepsDict(deps_content):
87 local_scope = {}
88 var = GClientKeywords.VarImpl({}, local_scope)
89 global_scope = {
90 'From': GClientKeywords.FromImpl,
91 'Var': var.Lookup,
92 'deps_os': {},
93 }
94 exec(deps_content, global_scope, local_scope)
95 return local_scope
96
97
98 def _GenerateCLDescriptionCommand(swiftshader_current, swiftshader_new, bugs,
99 tbr):
100 def GetChangeString(current_hash, new_hash):
101 return '%s..%s' % (current_hash[0:7], new_hash[0:7]);
102
103 def GetChangeLogURL(git_repo_url, change_string):
104 return '%s/+log/%s' % (git_repo_url, change_string)
105
106 def GetBugString(bugs):
107 bug_str = 'BUG='
108 for bug in bugs:
109 bug_str += bug + ','
110 return bug_str.rstrip(',')
111
112 if swiftshader_current.git_commit != swiftshader_new.git_commit:
113 change_str = GetChangeString(swiftshader_current.git_commit,
114 swiftshader_new.git_commit)
115 changelog_url = GetChangeLogURL(swiftshader_current.git_repo_url,
116 change_str)
117
118 def GetExtraCQTrybotString():
119 s = ''
120 for t in extra_cq_trybots:
121 if s:
122 s += ';'
123 s += t['mastername'] + ':' + ','.join(t['buildernames'])
124 return s
125
126 def GetTBRString(tbr):
127 if not tbr:
128 return ''
129 return 'TBR=' + tbr
130
131 extra_trybot_args = []
132 if extra_cq_trybots:
133 extra_trybot_string = GetExtraCQTrybotString()
134 extra_trybot_args = ['-m', 'CQ_INCLUDE_TRYBOTS=' + extra_trybot_string]
135
136 return [
137 '-m', 'Roll SwiftShader ' + change_str,
138 '-m', '%s' % changelog_url,
139 '-m', GetBugString(bugs),
140 '-m', GetTBRString(tbr),
141 '-m', 'TEST=bots',
142 ] + extra_trybot_args
143
144
145 class AutoRoller(object):
146 def __init__(self, chromium_src):
147 self._chromium_src = chromium_src
148
149 def _RunCommand(self, command, working_dir=None, ignore_exit_code=False,
150 extra_env=None):
151 """Runs a command and returns the stdout from that command.
152
153 If the command fails (exit code != 0), the function will exit the process.
154 """
155 working_dir = working_dir or self._chromium_src
156 logging.debug('cmd: %s cwd: %s', ' '.join(command), working_dir)
157 env = os.environ.copy()
158 if extra_env:
159 logging.debug('extra env: %s', extra_env)
160 env.update(extra_env)
161 p = subprocess.Popen(command, stdout=subprocess.PIPE,
162 stderr=subprocess.PIPE, shell=IS_WIN, env=env,
163 cwd=working_dir, universal_newlines=True)
164 output = p.stdout.read()
165 p.wait()
166 p.stdout.close()
167 p.stderr.close()
168
169 if not ignore_exit_code and p.returncode != 0:
170 logging.error('Command failed: %s\n%s', str(command), output)
171 sys.exit(p.returncode)
172 return output
173
174 def _GetCommitInfo(self, path_below_src, git_hash=None, git_repo_url=None):
175 working_dir = os.path.join(self._chromium_src, path_below_src)
176 self._RunCommand(['git', 'fetch', 'origin'], working_dir=working_dir)
177 revision_range = git_hash or 'origin'
178 ret = self._RunCommand(
179 ['git', '--no-pager', 'log', revision_range,
180 '--no-abbrev-commit', '--pretty=full', '-1'],
181 working_dir=working_dir)
182 return CommitInfo(_ParseGitCommitHash(ret), git_repo_url)
183
184 def _GetDepsCommitInfo(self, deps_dict, path_below_src):
185 entry = deps_dict['deps'][_PosixPath('src/%s' % path_below_src)]
186 at_index = entry.find('@')
187 git_repo_url = entry[:at_index]
188 git_hash = entry[at_index + 1:]
189 return self._GetCommitInfo(path_below_src, git_hash, git_repo_url)
190
191 def _GetCLInfo(self):
192 cl_output = self._RunCommand(['git', 'cl', 'issue'])
193 m = CL_ISSUE_RE.match(cl_output.strip())
194 if not m:
195 logging.error('Cannot find any CL info. Output was:\n%s', cl_output)
196 sys.exit(-1)
197 issue_number = int(m.group(1))
198 url = m.group(2)
199
200 # Parse the Rietveld host from the URL.
201 m = RIETVELD_URL_RE.match(url)
202 if not m:
203 logging.error('Cannot parse Rietveld host from URL: %s', url)
204 sys.exit(-1)
205 rietveld_server = m.group(1)
206 return CLInfo(issue_number, url, rietveld_server)
207
208 def _GetCurrentBranchName(self):
209 return self._RunCommand(
210 ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).splitlines()[0]
211
212 def _IsTreeClean(self):
213 lines = self._RunCommand(
214 ['git', 'status', '--porcelain', '-uno']).splitlines()
215 if len(lines) == 0:
216 return True
217
218 logging.debug('Dirty/unversioned files:\n%s', '\n'.join(lines))
219 return False
220
221 def _GetBugList(self, path_below_src, swiftshader_current, swiftshader_new):
222 working_dir = os.path.join(self._chromium_src, path_below_src)
223 lines = self._RunCommand(
224 ['git','log',
225 '%s..%s' % (swiftshader_current.git_commit,
226 swiftshader_new.git_commit)],
227 working_dir=working_dir).split('\n')
228 ignored_projects = set(['swiftshader'])
229 bugs = set()
230 for line in lines:
231 line = line.strip()
232 bug_prefix = 'BUG='
233 if line.startswith(bug_prefix):
234 bugs_strings = line[len(bug_prefix):].split(',')
235 for bug_string in bugs_strings:
236 ignore_bug = False
237 for ignored_project in ignored_projects:
238 if bug_string.startswith(ignored_project + ':'):
239 ignore_bug = True
240 break
241 if not ignore_bug:
242 bugs.add(bug_string)
243 return bugs
244
245 def _UpdateReadmeFile(self, readme_path, new_revision):
246 readme = open(os.path.join(self._chromium_src, readme_path), 'r+')
247 txt = readme.read()
248 m = re.sub(re.compile('.*^Revision\: ([0-9]*).*', re.MULTILINE),
249 ('Revision: %s' % new_revision), txt)
250 readme.seek(0)
251 readme.write(m)
252 readme.truncate()
253
254 def _TriggerExtraTrybots(self, trybots):
255 for trybot in trybots:
256 for builder in trybot['buildernames']:
257 self._RunCommand([
258 'git', 'cl', 'try',
259 '-m', trybot['mastername'],
260 '-b', builder])
261
262 def PrepareRoll(self, ignore_checks, tbr, should_commit):
263 # TODO(kjellander): use os.path.normcase, os.path.join etc for all paths for
264 # cross platform compatibility.
265
266 if not ignore_checks:
267 if self._GetCurrentBranchName() != 'master':
268 logging.error('Please checkout the master branch.')
269 return -1
270 if not self._IsTreeClean():
271 logging.error('Please make sure you don\'t have any modified files.')
272 return -1
273
274 # Always clean up any previous roll.
275 self.Abort()
276
277 logging.debug('Pulling latest changes')
278 if not ignore_checks:
279 self._RunCommand(['git', 'pull'])
280
281 self._RunCommand(['git', 'checkout', '-b', ROLL_BRANCH_NAME])
282
283 # Modify Chromium's DEPS file.
284
285 # Parse current hashes.
286 deps_filename = os.path.join(self._chromium_src, 'DEPS')
287 deps = _ParseDepsFile(deps_filename)
288 swiftshader_current = self._GetDepsCommitInfo(deps, SWIFTSHADER_PATH)
289
290 # Find ToT revisions.
291 swiftshader_latest = self._GetCommitInfo(SWIFTSHADER_PATH)
292
293 if IS_WIN:
294 # Make sure the roll script doesn't use windows line endings
295 self._RunCommand(['git', 'config', 'core.autocrlf', 'true'])
296
297 self._UpdateDep(deps_filename, SWIFTSHADER_PATH, swiftshader_latest)
298
299 if self._IsTreeClean():
300 logging.debug('Tree is clean - no changes detected.')
301 self._DeleteRollBranch()
302 else:
303 bugs = self._GetBugList(SWIFTSHADER_PATH, swiftshader_current,
304 swiftshader_latest)
305 description = _GenerateCLDescriptionCommand(
306 swiftshader_current, swiftshader_latest, bugs, tbr)
307 logging.debug('Committing changes locally.')
308 self._RunCommand(['git', 'add', '--update', '.'])
309 self._RunCommand(['git', 'commit'] + description)
310 logging.debug('Uploading changes...')
311 self._RunCommand(['git', 'cl', 'upload'],
312 extra_env={'EDITOR': 'true'})
313
314 # Kick off tryjobs.
315 base_try_cmd = ['git', 'cl', 'try']
316 self._RunCommand(base_try_cmd)
317
318 if extra_cq_trybots:
319 # Run additional tryjobs.
320 # TODO(kbr): this should not be necessary -- the
321 # CQ_INCLUDE_TRYBOTS directive above should handle it.
322 # http://crbug.com/585237
323 self._TriggerExtraTrybots(extra_cq_trybots)
324
325 if extra_fyi_trybots:
326 self._TriggerExtraTrybots(extra_fyi_trybots)
327
328 # Mark the CL to be committed if requested
329 if should_commit:
330 self._RunCommand(['git', 'cl', 'set-commit'])
331
332 cl_info = self._GetCLInfo()
333 print 'Issue: %d URL: %s' % (cl_info.issue, cl_info.url)
334
335 # Checkout master again.
336 self._RunCommand(['git', 'checkout', 'master'])
337 print 'Roll branch left as ' + ROLL_BRANCH_NAME
338 return 0
339
340 def _UpdateDep(self, deps_filename, dep_relative_to_src, commit_info):
341 dep_name = _PosixPath(os.path.join('src', dep_relative_to_src))
342
343 # roll_dep_svn.py relies on cwd being the Chromium checkout, so let's
344 # temporarily change the working directory and then change back.
345 cwd = os.getcwd()
346 os.chdir(os.path.dirname(deps_filename))
347 roll_dep_svn.update_deps(deps_filename, dep_relative_to_src, dep_name,
348 commit_info.git_commit, '')
349 os.chdir(cwd)
350
351 def _DeleteRollBranch(self):
352 self._RunCommand(['git', 'checkout', 'master'])
353 self._RunCommand(['git', 'branch', '-D', ROLL_BRANCH_NAME])
354 logging.debug('Deleted the local roll branch (%s)', ROLL_BRANCH_NAME)
355
356
357 def _GetBranches(self):
358 """Returns a tuple of active,branches.
359
360 The 'active' is the name of the currently active branch and 'branches' is a
361 list of all branches.
362 """
363 lines = self._RunCommand(['git', 'branch']).split('\n')
364 branches = []
365 active = ''
366 for l in lines:
367 if '*' in l:
368 # The assumption is that the first char will always be the '*'.
369 active = l[1:].strip()
370 branches.append(active)
371 else:
372 b = l.strip()
373 if b:
374 branches.append(b)
375 return (active, branches)
376
377 def Abort(self):
378 active_branch, branches = self._GetBranches()
379 if active_branch == ROLL_BRANCH_NAME:
380 active_branch = 'master'
381 if ROLL_BRANCH_NAME in branches:
382 print 'Aborting pending roll.'
383 self._RunCommand(['git', 'checkout', ROLL_BRANCH_NAME])
384 # Ignore an error here in case an issue wasn't created for some reason.
385 self._RunCommand(['git', 'cl', 'set_close'], ignore_exit_code=True)
386 self._RunCommand(['git', 'checkout', active_branch])
387 self._RunCommand(['git', 'branch', '-D', ROLL_BRANCH_NAME])
388 return 0
389
390
391 def main():
392 parser = argparse.ArgumentParser(
393 description='Auto-generates a CL containing a SwiftShader roll.')
394 parser.add_argument('--abort',
395 help=('Aborts a previously prepared roll. '
396 'Closes any associated issues and deletes the roll branches'),
397 action='store_true')
398 parser.add_argument('--ignore-checks', action='store_true', default=False,
399 help=('Skips checks for being on the master branch, dirty workspaces and '
400 'the updating of the checkout. Will still delete and create local '
401 'Git branches.'))
402 parser.add_argument('--tbr', help='Add a TBR to the commit message.')
403 parser.add_argument('--commit', action='store_true', default=False,
404 help='Submit the roll to the CQ after uploading.')
405 parser.add_argument('-v', '--verbose', action='store_true', default=False,
406 help='Be extra verbose in printing of log messages.')
407 args = parser.parse_args()
408
409 if args.verbose:
410 logging.basicConfig(level=logging.DEBUG)
411 else:
412 logging.basicConfig(level=logging.ERROR)
413
414 autoroller = AutoRoller(SRC_DIR)
415 if args.abort:
416 return autoroller.Abort()
417 else:
418 return autoroller.PrepareRoll(args.ignore_checks, args.tbr, args.commit)
419
420 if __name__ == '__main__':
421 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698