Exemple #1
0
    def AutomagicalSettings(self):
        """Determines settings based on supported code review and checkout tools.
    """
        # Try to find gclient or repo root first.
        if not self.options.no_search:
            self.toplevel_root = gclient_utils.FindGclientRoot(
                self.checkout_root)
            if self.toplevel_root:
                logging.info('Found .gclient at %s' % self.toplevel_root)
            else:
                self.toplevel_root = gclient_utils.FindFileUpwards(
                    os.path.join('..', '.repo'), self.checkout_root)
                if self.toplevel_root:
                    logging.info('Found .repo dir at %s' %
                                 os.path.dirname(self.toplevel_root))

            # Parse TRYSERVER_* settings from codereview.settings before falling back
            # on setting self.options.root manually further down. Otherwise
            # TRYSERVER_ROOT would never be used in codereview.settings.
            self._GclStyleSettings()

            if self.toplevel_root and not self.options.root:
                assert os.path.abspath(
                    self.toplevel_root) == self.toplevel_root
                self.options.root = gclient_utils.PathDifference(
                    self.toplevel_root, self.checkout_root)
        else:
            self._GclStyleSettings()
 def _GclientStyleSettings(self):
     """Find the root, assuming a gclient-style checkout."""
     if not self.options.no_gclient and not self.options.root:
         root = self.checkout_root
         self.gclient_root = gclient_utils.FindGclientRoot(root)
         if self.gclient_root:
             logging.info('Found .gclient at %s' % self.gclient_root)
             self.options.root = gclient_utils.PathDifference(
                 self.gclient_root, root)
Exemple #3
0
    def AutomagicalSettings(self):
        """Determines settings based on supported code review and checkout tools.
    """
        # Try to find gclient or repo root first.
        if not self.options.no_search:
            self.toplevel_root = gclient_utils.FindGclientRoot(
                self.checkout_root)
            if self.toplevel_root:
                logging.info('Found .gclient at %s' % self.toplevel_root)
            else:
                self.toplevel_root = gclient_utils.FindFileUpwards(
                    os.path.join('..', '.repo'), self.checkout_root)
                if self.toplevel_root:
                    logging.info('Found .repo dir at %s' %
                                 os.path.dirname(self.toplevel_root))

            if self.toplevel_root and not self.options.root:
                assert os.path.abspath(
                    self.toplevel_root) == self.toplevel_root
                self.options.root = gclient_utils.PathDifference(
                    self.toplevel_root, self.checkout_root)

        self._GclStyleSettings()
Exemple #4
0
def TryChange(argv, change, swallow_exception, prog=None, extra_epilog=None):
    """
  Args:
    argv: Arguments and options.
    change: Change instance corresponding to the CL.
    swallow_exception: Whether we raise or swallow exceptions.
  """
    parser = gen_parser(prog)
    epilog = EPILOG % {'prog': prog}
    if extra_epilog:
        epilog += extra_epilog
    parser.epilog = epilog

    options, args = parser.parse_args(argv)

    # If they've asked for help, give it to them
    if len(args) == 1 and args[0] == 'help':
        parser.print_help()
        return 0

    # If they've said something confusing, don't spawn a try job until you
    # understand what they want.
    if args:
        parser.error('Extra argument(s) "%s" not understood' % ' '.join(args))

    if options.dry_run:
        options.verbose += 1

    LOG_FORMAT = '%(levelname)s %(filename)s(%(lineno)d): %(message)s'
    if not swallow_exception:
        if options.verbose == 0:
            logging.basicConfig(level=logging.WARNING, format=LOG_FORMAT)
        elif options.verbose == 1:
            logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
        elif options.verbose > 1:
            logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)

    logging.debug(argv)

    if (options.patchlevel is not None
            and (options.patchlevel < 0 or options.patchlevel > 10)):
        parser.error(
            'Have you tried --port instead? You probably confused -p and -P.')

    # Strip off any @ in the user, otherwise svn gets confused.
    options.user = options.user.split('@', 1)[0]

    if options.rietveld_url:
        # Try to extract the review number if possible and fix the protocol.
        if not '://' in options.rietveld_url:
            options.rietveld_url = 'http://' + options.rietveld_url
        match = re.match(r'^(.*)/(\d+)/?$', options.rietveld_url)
        if match:
            if options.issue or options.patchset:
                parser.error(
                    'Cannot use both --issue and use a review number url')
            options.issue = int(match.group(2))
            options.rietveld_url = match.group(1)

    try:
        changed_files = None
        # Always include os.getcwd() in the checkout settings.
        path = os.getcwd()

        file_list = []
        if options.files:
            file_list = options.files
        elif change:
            file_list = [f.LocalPath() for f in change.AffectedFiles()]

        if options.upstream_branch:
            path += '@' + options.upstream_branch
            # Clear file list so that the correct list will be retrieved from the
            # upstream branch.
            file_list = []

        current_vcs = GuessVCS(options, path, file_list)
        current_vcs.AutomagicalSettings()
        options = current_vcs.options
        vcs_is_git = type(current_vcs) is GIT

        # So far, git_repo doesn't work with SVN
        if options.git_repo and not vcs_is_git:
            parser.error(
                '--git_repo option is supported only for GIT repositories')

        # If revision==auto, resolve it
        if options.revision and options.revision.lower() == 'auto':
            if not vcs_is_git:
                parser.error(
                    '--revision=auto is supported only for GIT repositories')
            options.revision = scm.GIT.Capture(
                ['rev-parse', current_vcs.diff_against], cwd=path)

        checkouts = [current_vcs]
        for item in options.sub_rep:
            # Pass file_list=None because we don't know the sub repo's file list.
            checkout = GuessVCS(options,
                                os.path.join(current_vcs.checkout_root, item),
                                None)
            if checkout.checkout_root in [c.checkout_root for c in checkouts]:
                parser.error('Specified the root %s two times.' %
                             checkout.checkout_root)
            checkouts.append(checkout)

        can_http = options.port and options.host
        can_svn = options.svn_repo
        can_git = options.git_repo
        can_gerrit = options.gerrit_url
        can_something = can_http or can_svn or can_git or can_gerrit
        # If there was no transport selected yet, now we must have enough data to
        # select one.
        if not options.send_patch and not can_something:
            parser.error('Please specify an access method.')

        # Convert options.diff into the content of the diff.
        if options.url:
            if options.files:
                parser.error(
                    'You cannot specify files and --url at the same time.')
            options.diff = urllib2.urlopen(options.url).read()
        elif options.diff:
            if options.files:
                parser.error(
                    'You cannot specify files and --diff at the same time.')
            options.diff = gclient_utils.FileRead(options.diff, 'rb')
        elif options.issue and options.patchset is None:
            # Retrieve the patch from rietveld when the diff is not specified.
            # When patchset is specified, it's because it's done by gcl/git-try.
            api_url = '%s/api/%d' % (options.rietveld_url, options.issue)
            logging.debug(api_url)
            contents = json.loads(urllib2.urlopen(api_url).read())
            options.patchset = contents['patchsets'][-1]
            diff_url = (
                '%s/download/issue%d_%d.diff' %
                (options.rietveld_url, options.issue, options.patchset))
            diff = GetMungedDiff('', urllib2.urlopen(diff_url).readlines())
            options.diff = ''.join(diff[0])
            changed_files = diff[1]
        else:
            # Use this as the base.
            root = checkouts[0].checkout_root
            diffs = []
            for checkout in checkouts:
                raw_diff = checkout.GenerateDiff()
                if not raw_diff:
                    continue
                diff = raw_diff.splitlines(True)
                path_diff = gclient_utils.PathDifference(
                    root, checkout.checkout_root)
                # Munge it.
                diffs.extend(GetMungedDiff(path_diff, diff)[0])
            if not diffs:
                logging.error('Empty or non-existant diff, exiting.')
                return 1
            options.diff = ''.join(diffs)

        if not options.name:
            if options.issue:
                options.name = 'Issue %s' % options.issue
            else:
                options.name = 'Unnamed'
                print('Note: use --name NAME to change the try job name.')

        if not options.email:
            parser.error(
                'Using an anonymous checkout. Please use --email or set '
                'the TRYBOT_RESULTS_EMAIL_ADDRESS environment variable.')
        print('Results will be emailed to: ' + options.email)

        if options.bot:
            bot_spec = _ApplyTestFilter(
                options.testfilter,
                _ParseBotList(options.bot, options.testfilter))
        else:
            bot_spec = _GenTSBotSpec(checkouts, change, changed_files, options)

        if options.testfilter:
            bot_spec = _ApplyTestFilter(options.testfilter, bot_spec)

        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' %
                bot_spec)
            return 1

        if options.print_bots:
            print 'Bots which would be used:'
            for bot in bot_spec:
                if bot[1]:
                    print '  %s:%s' % (bot[0], ','.join(bot[1]))
                else:
                    print '  %s' % (bot[0])
            return 0

        # Determine sending protocol
        if options.send_patch:
            # If forced.
            senders = [options.send_patch]
        else:
            # Try sending patch using avaialble protocols
            all_senders = [
                (_SendChangeHTTP, can_http),
                (_SendChangeSVN, can_svn),
                (_SendChangeGerrit, can_gerrit),
                (_SendChangeGit, can_git),
            ]
            senders = [sender for sender, can in all_senders if can]

        # Send the patch.
        for sender in senders:
            try:
                sender(bot_spec, options)
                return 0
            except NoTryServerAccess:
                is_last = sender == senders[-1]
                if is_last:
                    raise
        assert False, "Unreachable code"
    except Error, e:
        if swallow_exception:
            return 1
        print >> sys.stderr, e
        return 1
Exemple #5
0
def TryChange(argv, change, swallow_exception, prog=None, extra_epilog=None):
    """
  Args:
    argv: Arguments and options.
    change: Change instance corresponding to the CL.
    swallow_exception: Whether we raise or swallow exceptions.
  """
    parser = gen_parser(prog)
    epilog = EPILOG % {'prog': prog}
    if extra_epilog:
        epilog += extra_epilog
    parser.epilog = epilog
    # Remove epilog formatting
    parser.format_epilog = lambda x: parser.epilog

    options, args = parser.parse_args(argv)

    # If they've asked for help, give it to them
    if len(args) == 1 and args[0] == 'help':
        parser.print_help()
        return 0

    # If they've said something confusing, don't spawn a try job until you
    # understand what they want.
    if args:
        parser.error('Extra argument(s) "%s" not understood' % ' '.join(args))

    if options.dry_run:
        options.verbose += 1

    LOG_FORMAT = '%(levelname)s %(filename)s(%(lineno)d): %(message)s'
    if not swallow_exception:
        if options.verbose == 0:
            logging.basicConfig(level=logging.WARNING, format=LOG_FORMAT)
        elif options.verbose == 1:
            logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
        elif options.verbose > 1:
            logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)

    logging.debug(argv)

    if (options.patchlevel is not None
            and (options.patchlevel < 0 or options.patchlevel > 10)):
        parser.error(
            'Have you tried --port instead? You probably confused -p and -P.')

    # Strip off any @ in the user, otherwise svn gets confused.
    options.user = options.user.split('@', 1)[0]

    if options.rietveld_url:
        # Try to extract the review number if possible and fix the protocol.
        if not '://' in options.rietveld_url:
            options.rietveld_url = 'http://' + options.rietveld_url
        match = re.match(r'^(.*)/(\d+)/?$', options.rietveld_url)
        if match:
            if options.issue or options.patchset:
                parser.error(
                    'Cannot use both --issue and use a review number url')
            options.issue = int(match.group(2))
            options.rietveld_url = match.group(1)

    try:
        changed_files = None
        # Always include os.getcwd() in the checkout settings.
        checkouts = []
        path = os.getcwd()

        file_list = []
        if options.files:
            file_list = options.files
        elif change:
            file_list = [f.LocalPath() for f in change.AffectedFiles()]

        if options.upstream_branch:
            path += '@' + options.upstream_branch
            # Clear file list so that the correct list will be retrieved from the
            # upstream branch.
            file_list = []
        checkouts.append(GuessVCS(options, path, file_list))
        checkouts[0].AutomagicalSettings()
        for item in options.sub_rep:
            # Pass file_list=None because we don't know the sub repo's file list.
            checkout = GuessVCS(options,
                                os.path.join(checkouts[0].checkout_root, item),
                                None)
            if checkout.checkout_root in [c.checkout_root for c in checkouts]:
                parser.error('Specified the root %s two times.' %
                             checkout.checkout_root)
            checkouts.append(checkout)

        can_http = options.port and options.host
        can_svn = options.svn_repo
        # If there was no transport selected yet, now we must have enough data to
        # select one.
        if not options.send_patch and not (can_http or can_svn):
            parser.error('Please specify an access method.')

        # Convert options.diff into the content of the diff.
        if options.url:
            if options.files:
                parser.error(
                    'You cannot specify files and --url at the same time.')
            options.diff = urllib.urlopen(options.url).read()
        elif options.diff:
            if options.files:
                parser.error(
                    'You cannot specify files and --diff at the same time.')
            options.diff = gclient_utils.FileRead(options.diff, 'rb')
        elif options.issue and options.patchset is None:
            # Retrieve the patch from rietveld when the diff is not specified.
            # When patchset is specified, it's because it's done by gcl/git-try.
            api_url = '%s/api/%d' % (options.rietveld_url, options.issue)
            logging.debug(api_url)
            contents = json.loads(urllib.urlopen(api_url).read())
            options.patchset = contents['patchsets'][-1]
            diff_url = (
                '%s/download/issue%d_%d.diff' %
                (options.rietveld_url, options.issue, options.patchset))
            diff = GetMungedDiff('', urllib.urlopen(diff_url).readlines())
            options.diff = ''.join(diff[0])
            changed_files = diff[1]
        else:
            # Use this as the base.
            root = checkouts[0].checkout_root
            diffs = []
            for checkout in checkouts:
                raw_diff = checkout.GenerateDiff()
                if not raw_diff:
                    logging.error('Empty or non-existant diff, exiting.')
                    return 1
                diff = raw_diff.splitlines(True)
                path_diff = gclient_utils.PathDifference(
                    root, checkout.checkout_root)
                # Munge it.
                diffs.extend(GetMungedDiff(path_diff, diff)[0])
            options.diff = ''.join(diffs)

        if not options.name:
            if options.issue:
                options.name = 'Issue %s' % options.issue
            else:
                options.name = 'Unnamed'
                print('Note: use --name NAME to change the try job name.')

        if not options.email:
            parser.error(
                'Using an anonymous checkout. Please use --email or set '
                '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 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 any('triggered' in b.split(':', 1)[0] for b in options.bot):
            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)
            return 1

        if options.print_bots:
            print 'Bots which would be used:'
            for bot in options.bot:
                print '  %s' % bot
            return 0

        # Send the patch.
        if options.send_patch:
            # If forced.
            options.send_patch(options)
            PrintSuccess(options)
            return 0
        try:
            if can_http:
                _SendChangeHTTP(options)
                PrintSuccess(options)
                return 0
        except NoTryServerAccess:
            if not can_svn:
                raise
        _SendChangeSVN(options)
        PrintSuccess(options)
        return 0
    except (InvalidScript, NoTryServerAccess), e:
        if swallow_exception:
            return 1
        print >> sys.stderr, e
        return 1
def TryChange(argv,
              file_list,
              swallow_exception,
              prog=None,
              extra_epilog=None):
    """
  Args:
    argv: Arguments and options.
    file_list: Default value to pass to --file.
    swallow_exception: Whether we raise or swallow exceptions.
  """
    # Parse argv
    parser = optparse.OptionParser(usage=USAGE, version=__version__, prog=prog)
    epilog = EPILOG % {'prog': prog}
    if extra_epilog:
        epilog += extra_epilog
    parser.epilog = epilog
    # Remove epilog formatting
    parser.format_epilog = lambda x: parser.epilog
    parser.add_option("-v",
                      "--verbose",
                      action="count",
                      default=0,
                      help="Prints debugging infos")
    group = optparse.OptionGroup(parser, "Result and status")
    group.add_option("-u",
                     "--user",
                     default=getpass.getuser(),
                     help="Owner user name [default: %default]")
    group.add_option(
        "-e",
        "--email",
        default=os.environ.get('TRYBOT_RESULTS_EMAIL_ADDRESS',
                               os.environ.get('EMAIL_ADDRESS')),
        help="Email address where to send the results. Use either "
        "the TRYBOT_RESULTS_EMAIL_ADDRESS environment "
        "variable or EMAIL_ADDRESS to set the email address "
        "the try bots report results to [default: %default]")
    group.add_option("-n", "--name", help="Descriptive name of the try job")
    group.add_option("--issue",
                     type='int',
                     help="Update rietveld issue try job status")
    group.add_option("--patchset",
                     type='int',
                     help="Update rietveld issue try job status. This is "
                     "optional if --issue is used, In that case, the "
                     "latest patchset will be used.")
    group.add_option("--dry_run",
                     action='store_true',
                     help="Don't send the try job. This implies --verbose, so "
                     "it will print the diff.")
    parser.add_option_group(group)

    group = optparse.OptionGroup(parser, "Try job options")
    group.add_option("-b",
                     "--bot",
                     action="append",
                     help="Only use specifics build slaves, ex: "
                     "'--bot win,layout_mac'; see the try "
                     "server waterfall for the slave's name")
    group.add_option("-r",
                     "--revision",
                     help="Revision to use for the try job; default: the "
                     "revision will be determined by the try server; see "
                     "its waterfall for more info")
    group.add_option("-c",
                     "--clobber",
                     action="store_true",
                     help="Force a clobber before building; e.g. don't do an "
                     "incremental build")
    # TODO(maruel): help="Select a specific configuration, usually 'debug' or "
    #                    "'release'"
    group.add_option("--target", help=optparse.SUPPRESS_HELP)

    group.add_option(
        "--project",
        help="Override which project to use. Projects are defined "
        "server-side to define what default bot set to use")

    group.add_option(
        "-t",
        "--testfilter",
        action="append",
        help="Add a gtest_filter to a test. Use multiple times to "
        "specify filters for different tests. (i.e. "
        "--testfilter base_unittests:ThreadTest.* "
        "--testfilter ui_tests) If you specify any testfilters "
        "the test results will not be reported in rietveld and "
        "only tests with filters will run.")

    parser.add_option_group(group)

    group = optparse.OptionGroup(parser, "Patch to run")
    group.add_option("-f",
                     "--file",
                     default=file_list,
                     dest="files",
                     metavar="FILE",
                     action="append",
                     help="Use many times to list the files to include in the "
                     "try, relative to the repository root")
    group.add_option("--diff", help="File containing the diff to try")
    group.add_option("--url",
                     help="Url where to grab a patch, e.g. "
                     "http://example.com/x.diff")
    group.add_option("-R",
                     "--rietveld_url",
                     default="codereview.appspot.com",
                     metavar="URL",
                     help="Has 2 usages, both refer to the rietveld instance: "
                     "Specify which code review patch to use as the try job "
                     "or rietveld instance to update the try job results "
                     "Default:%default")
    group.add_option("--root",
                     help="Root to use for the patch; base subdirectory for "
                     "patch created in a subdirectory")
    group.add_option("-p",
                     "--patchlevel",
                     type='int',
                     metavar="LEVEL",
                     help="Used as -pN parameter to patch")
    group.add_option("-s",
                     "--sub_rep",
                     action="append",
                     default=[],
                     help="Subcheckout to use in addition. This is mainly "
                     "useful for gclient-style checkouts. In git, checkout "
                     "the branch with changes first. Use @rev or "
                     "@branch to specify the "
                     "revision/branch to diff against. If no @branch is "
                     "given the diff will be against the upstream branch. "
                     "If @branch then the diff is branch..HEAD. "
                     "All edits must be checked in.")
    group.add_option("--no_gclient",
                     action="store_true",
                     help="Disable automatic search for gclient checkout.")
    group.add_option(
        "-E",
        "--exclude",
        action="append",
        default=['ChangeLog'],
        metavar='REGEXP',
        help="Regexp patterns to exclude files. Default: %default")
    group.add_option("--upstream_branch",
                     action="store",
                     help="Specify the upstream branch to diff against in the "
                     "main checkout")
    parser.add_option_group(group)

    group = optparse.OptionGroup(parser, "Access the try server by HTTP")
    group.add_option("--use_http",
                     action="store_const",
                     const=_SendChangeHTTP,
                     dest="send_patch",
                     help="Use HTTP to talk to the try server [default]")
    group.add_option("-H", "--host", help="Host address")
    group.add_option("-P", "--port", help="HTTP port")
    group.add_option("--proxy", help="HTTP proxy")
    parser.add_option_group(group)

    group = optparse.OptionGroup(parser, "Access the try server with SVN")
    group.add_option("--use_svn",
                     action="store_const",
                     const=_SendChangeSVN,
                     dest="send_patch",
                     help="Use SVN to talk to the try server")
    group.add_option(
        "-S",
        "--svn_repo",
        metavar="SVN_URL",
        help="SVN url to use to write the changes in; --use_svn is "
        "implied when using --svn_repo")
    parser.add_option_group(group)

    options, args = parser.parse_args(argv)

    # If they've asked for help, give it to them
    if len(args) == 1 and args[0] == 'help':
        parser.print_help()
        return 0

    # If they've said something confusing, don't spawn a try job until you
    # understand what they want.
    if args:
        parser.error('Extra argument(s) "%s" not understood' % ' '.join(args))

    if options.dry_run:
        options.verbose += 1

    LOG_FORMAT = '%(levelname)s %(filename)s(%(lineno)d): %(message)s'
    if not swallow_exception:
        if options.verbose == 0:
            logging.basicConfig(level=logging.WARNING, format=LOG_FORMAT)
        elif options.verbose == 1:
            logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
        elif options.verbose > 1:
            logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)

    logging.debug(argv)

    # Strip off any @ in the user, otherwise svn gets confused.
    options.user = options.user.split('@', 1)[0]

    if options.rietveld_url:
        # Try to extract the review number if possible and fix the protocol.
        if not '://' in options.rietveld_url:
            options.rietveld_url = 'http://' + options.rietveld_url
        match = re.match(r'^(.*)/(\d+)/?$', options.rietveld_url)
        if match:
            if options.issue or options.patchset:
                parser.error(
                    'Cannot use both --issue and use a review number url')
            options.issue = int(match.group(2))
            options.rietveld_url = match.group(1)

    try:
        # Always include os.getcwd() in the checkout settings.
        checkouts = []
        path = os.getcwd()
        if options.upstream_branch:
            path += '@' + options.upstream_branch
        checkouts.append(GuessVCS(options, path))
        checkouts[0].AutomagicalSettings()
        for item in options.sub_rep:
            checkout = GuessVCS(options,
                                os.path.join(checkouts[0].checkout_root, item))
            if checkout.checkout_root in [c.checkout_root for c in checkouts]:
                parser.error('Specified the root %s two times.' %
                             checkout.checkout_root)
            checkouts.append(checkout)

        can_http = options.port and options.host
        can_svn = options.svn_repo
        # If there was no transport selected yet, now we must have enough data to
        # select one.
        if not options.send_patch and not (can_http or can_svn):
            parser.error('Please specify an access method.')

        # Convert options.diff into the content of the diff.
        if options.url:
            if options.files:
                parser.error(
                    'You cannot specify files and --url at the same time.')
            options.diff = urllib.urlopen(options.url).read()
        elif options.diff:
            if options.files:
                parser.error(
                    'You cannot specify files and --diff at the same time.')
            options.diff = gclient_utils.FileRead(options.diff, 'rb')
        elif options.issue and options.patchset is None:
            # Retrieve the patch from rietveld when the diff is not specified.
            # When patchset is specified, it's because it's done by gcl/git-try.
            if json is None:
                parser.error(
                    'json or simplejson library is missing, please install.')
            api_url = '%s/api/%d' % (options.rietveld_url, options.issue)
            logging.debug(api_url)
            contents = json.loads(urllib.urlopen(api_url).read())
            options.patchset = contents['patchsets'][-1]
            diff_url = (
                '%s/download/issue%d_%d.diff' %
                (options.rietveld_url, options.issue, options.patchset))
            diff = GetMungedDiff('', urllib.urlopen(diff_url).readlines())
            options.diff = ''.join(diff)
        else:
            # Use this as the base.
            root = checkouts[0].checkout_root
            diffs = []
            for checkout in checkouts:
                diff = checkout.GenerateDiff().splitlines(True)
                path_diff = gclient_utils.PathDifference(
                    root, checkout.checkout_root)
                # Munge it.
                diffs.extend(GetMungedDiff(path_diff, diff))
            options.diff = ''.join(diffs)

        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')
                options.bot = presubmit_support.DoGetTrySlaves(
                    checkouts[0].GetFileNames(), checkouts[0].checkout_root,
                    root_presubmit, options.project, False, sys.stdout)
            except ImportError:
                pass
            # 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.name is None:
            if options.issue:
                options.name = 'Issue %s' % options.issue
            else:
                options.name = 'Unnamed'
                print('Note: use --name NAME to change the try job name.')
        if not options.email:
            parser.error(
                'Using an anonymous checkout. Please use --email or set '
                'the TRYBOT_RESULTS_EMAIL_ADDRESS environment variable.')
        else:
            print('Results will be emailed to: ' + options.email)

        # Prevent rietveld updates if we aren't running all the tests.
        if options.testfilter is not None:
            options.issue = None
            options.patchset = None

        # Send the patch.
        if options.send_patch:
            # If forced.
            options.send_patch(options)
            PrintSuccess(options)
            return 0
        try:
            if can_http:
                _SendChangeHTTP(options)
                PrintSuccess(options)
                return 0
        except NoTryServerAccess:
            if not can_svn:
                raise
        _SendChangeSVN(options)
        PrintSuccess(options)
        return 0
    except (InvalidScript, NoTryServerAccess), e:
        if swallow_exception:
            return 1
        print >> sys.stderr, e
        return 1