Chromium Code Reviews| Index: trychange.py |
| diff --git a/trychange.py b/trychange.py |
| index d0cdab69f8db857545ac9a034bd0174fca2fa540..aeee7e4635ea7ed31ebab6b9a341a0fc9a60b36f 100755 |
| --- a/trychange.py |
| +++ b/trychange.py |
| @@ -11,6 +11,7 @@ to the server by HTTP. |
| import datetime |
| import errno |
| import getpass |
| +import itertools |
| import json |
| import logging |
| import optparse |
| @@ -314,7 +315,89 @@ class GIT(SCM): |
| branch=self.diff_against) |
| -def _ParseSendChangeOptions(options): |
| +def _ParseBotList(botlist, testfilter): |
| + """Parses bot configurations from the command line.""" |
| + bots = [] |
| + if testfilter: |
| + for bot in itertools.chain.from_iterable(botspec.split(',') |
| + for botspec in botlist): |
| + tests = set() |
| + if ':' in bot: |
| + if bot.endswith(':compile'): |
| + tests |= set(['compile']) |
| + else: |
| + raise ValueError( |
| + 'Can\'t use both --testfilter and --bot builder:test formats ' |
| + 'at the same time') |
| + |
| + bots.append((bot, tests)) |
| + else: |
| + for botspec in botlist: |
| + botname = botspec.split(':')[0] |
| + tests = set() |
| + if ':' in botspec: |
| + tests |= set(filter(None, botspec.split(':')[1].split(','))) |
| + bots.append((botname, tests)) |
| + return bots |
| + |
| + |
| +def _ApplyTestFilter(testfilter, bot_spec): |
| + """Applies testfilter from CLI. |
| + |
| + Specifying a testfilter strips off any builder-specified tests (except for |
| + compile). |
| + """ |
| + if testfilter: |
| + return [(botname, set(testfilter) | (tests & set(['compile']))) |
| + for botname, tests in bot_spec] |
| + else: |
| + return bot_spec |
| + |
| + |
| +def _GenTSBotSpec(checkouts, change, changed_files, options): |
| + bot_spec = [] |
| + # Get try slaves from PRESUBMIT.py files if not specified. |
| + # Even if the diff comes from options.url, use the local checkout for bot |
| + # selection. |
| + try: |
| + import presubmit_support |
| + root_presubmit = checkouts[0].ReadRootFile('PRESUBMIT.py') |
| + if not change: |
| + if not changed_files: |
| + changed_files = checkouts[0].file_tuples |
| + change = presubmit_support.Change(options.name, |
| + '', |
| + checkouts[0].checkout_root, |
| + changed_files, |
| + options.issue, |
| + options.patchset, |
| + options.email) |
| + trybots = presubmit_support.DoGetTrySlaves( |
| + change, |
| + checkouts[0].GetFileNames(), |
| + checkouts[0].checkout_root, |
| + root_presubmit, |
| + options.project, |
| + options.verbose, |
| + sys.stdout) |
| + if trybots: |
| + if isinstance(trybots[0], basestring): |
| + # PRESUBMIT.py sent us an old-style string list of bots. |
| + # _ParseBotList's testfilter is set to None otherwise it will complain. |
| + bot_spec = _ApplyTestFilter(options.testfilter, |
| + _ParseBotList(trybots, None)) |
| + else: |
| + # PRESUBMIT.py sent us a new-style (bot, set()) specification. |
| + bot_spec = _ApplyTestFilter(options.testfilter, trybots) |
| + |
| + except ImportError: |
| + pass |
| + |
| + return bot_spec |
| + |
| + |
|
M-A Ruel
2013/11/01 13:13:57
lines
ghost stip (do not use)
2013/11/04 22:01:52
Done.
|
| + |
| +def _ParseSendChangeOptions(bot_spec, options): |
| """Parse common options passed to _SendChangeHTTP and _SendChangeSVN.""" |
| values = [ |
| ('user', options.user), |
| @@ -339,23 +422,13 @@ def _ParseSendChangeOptions(options): |
| if options.project: |
| values.append(('project', options.project)) |
| - filters = ','.join(options.testfilter) |
| - if filters: |
| - for botlist in options.bot: |
| - for bot in botlist.split(','): |
| - if ':' in bot: |
| - raise ValueError( |
| - 'Can\'t use both --testfilter and --bot builder:test formats ' |
| - 'at the same time') |
| - else: |
| - values.append(('bot', '%s:%s' % (bot, filters))) |
| - else: |
| - for bot in options.bot: |
| - values.append(('bot', bot)) |
| + for bot, tests in bot_spec: |
| + values.append(('bot', ('%s:%s' % (bot, ','.join(tests))))) |
| + |
| return values |
| -def _SendChangeHTTP(options): |
| +def _SendChangeHTTP(bot_spec, options): |
| """Send a change to the try server using the HTTP protocol.""" |
| if not options.host: |
| raise NoTryServerAccess('Please use the --host option to specify the try ' |
| @@ -364,7 +437,7 @@ def _SendChangeHTTP(options): |
| raise NoTryServerAccess('Please use the --port option to specify the try ' |
| 'server port to connect to.') |
| - values = _ParseSendChangeOptions(options) |
| + values = _ParseSendChangeOptions(bot_spec, options) |
| values.append(('patch', options.diff)) |
| url = 'http://%s:%s/send_try_patch' % (options.host, options.port) |
| @@ -389,7 +462,7 @@ def _SendChangeHTTP(options): |
| logging.info('Done') |
| except IOError, e: |
| logging.info(str(e)) |
| - if options.bot and len(e.args) > 2 and e.args[2] == 'got a bad status line': |
| + if bot_spec and len(e.args) > 2 and e.args[2] == 'got a bad status line': |
| raise NoTryServerAccess('%s is unaccessible. Bad --bot argument?' % url) |
| else: |
| raise NoTryServerAccess('%s is unaccessible. Reason: %s' % (url, |
| @@ -403,14 +476,14 @@ def _SendChangeHTTP(options): |
| raise NoTryServerAccess('%s is unaccessible. Got:\n%s' % (url, response)) |
| -def _SendChangeSVN(options): |
| +def _SendChangeSVN(bot_spec, options): |
| """Send a change to the try server by committing a diff file on a subversion |
| server.""" |
| if not options.svn_repo: |
| raise NoTryServerAccess('Please use the --svn_repo option to specify the' |
| ' try server svn repository to connect to.') |
| - values = _ParseSendChangeOptions(options) |
| + values = _ParseSendChangeOptions(bot_spec, options) |
| description = ''.join("%s=%s\n" % (k, v) for k, v in values) |
| logging.info('Sending by SVN') |
| logging.info(description) |
| @@ -460,11 +533,12 @@ def _SendChangeSVN(options): |
| shutil.rmtree(temp_dir, True) |
| -def PrintSuccess(options): |
| +def PrintSuccess(bot_spec, options): |
| if not options.dry_run: |
| text = 'Patch \'%s\' sent to try server' % options.name |
| - if options.bot: |
| - text += ': %s' % ', '.join(options.bot) |
| + if bot_spec: |
| + text += ': %s' % ', '.join( |
| + '%s:%s' % (b[0], ','.join(b[1])) for b in bot_spec) |
| print(text) |
| @@ -811,75 +885,47 @@ def TryChange(argv, |
| 'the TRYBOT_RESULTS_EMAIL_ADDRESS environment variable.') |
| print('Results will be emailed to: ' + options.email) |
| - if not options.bot: |
| - # Get try slaves from PRESUBMIT.py files if not specified. |
| - # Even if the diff comes from options.url, use the local checkout for bot |
| - # selection. |
| - try: |
| - import presubmit_support |
| - root_presubmit = checkouts[0].ReadRootFile('PRESUBMIT.py') |
| - if not change: |
| - if not changed_files: |
| - changed_files = checkouts[0].file_tuples |
| - change = presubmit_support.Change(options.name, |
| - '', |
| - checkouts[0].checkout_root, |
| - changed_files, |
| - options.issue, |
| - options.patchset, |
| - options.email) |
| - options.bot = presubmit_support.DoGetTrySlaves( |
| - change, |
| - checkouts[0].GetFileNames(), |
| - checkouts[0].checkout_root, |
| - root_presubmit, |
| - options.project, |
| - options.verbose, |
| - sys.stdout) |
| - except ImportError: |
| - pass |
| - if options.testfilter: |
| - bots = set() |
| - for bot in options.bot: |
| - assert ',' not in bot |
| - if bot.endswith(':compile'): |
| - # Skip over compile-only builders for now. |
| - continue |
| - bots.add(bot.split(':', 1)[0]) |
| - options.bot = list(bots) |
| + if options.bot: |
| + bot_spec = _ApplyTestFilter( |
| + options.testfilter, _ParseBotList(options.bot, options.testfilter)) |
| + else: |
| + bot_spec = _GenTSBotSpec(checkouts, change, changed_files, options) |
| - # If no bot is specified, either the default pool will be selected or the |
| - # try server will refuse the job. Either case we don't need to interfere. |
| + if options.testfilter: |
| + bot_spec = _ApplyTestFilter(options.testfilter, bot_spec) |
| - if any('triggered' in b.split(':', 1)[0] for b in options.bot): |
| + if any('triggered' in b[0] for b in bot_spec): |
| print >> sys.stderr, ( |
| 'ERROR You are trying to send a job to a triggered bot. This type of' |
| ' bot requires an\ninitial job from a parent (usually a builder). ' |
| - 'Instead send your job to the parent.\nBot list: %s' % options.bot) |
| + 'Instead send your job to the parent.\nBot list: %s' % bot_spec) |
| return 1 |
| if options.print_bots: |
| print 'Bots which would be used:' |
| - for bot in options.bot: |
| - print ' %s' % bot |
| + for bot in bot_spec: |
| + if bot[1]: |
| + print ' %s:%s' % (bot[0], ','.join(bot[1])) |
| + else: |
| + print ' %s' % (bot[0]) |
| return 0 |
| # Send the patch. |
| if options.send_patch: |
| # If forced. |
| - options.send_patch(options) |
| - PrintSuccess(options) |
| + options.send_patch(bot_spec, options) |
| + PrintSuccess(bot_spec, options) |
| return 0 |
| try: |
| if can_http: |
| - _SendChangeHTTP(options) |
| - PrintSuccess(options) |
| + _SendChangeHTTP(bot_spec, options) |
| + PrintSuccess(bot_spec, options) |
| return 0 |
| except NoTryServerAccess: |
| if not can_svn: |
| raise |
| - _SendChangeSVN(options) |
| - PrintSuccess(options) |
| + _SendChangeSVN(bot_spec, options) |
| + PrintSuccess(bot_spec, options) |
| return 0 |
| except (InvalidScript, NoTryServerAccess), e: |
| if swallow_exception: |