git cl try: support multiple bots from different masters without specifying master.

It might be good to make this change after the refactoring CL http://crrev.com/2442153002.

BUG=640740

Review-Url: https://codereview.chromium.org/2439293002
diff --git a/git_cl.py b/git_cl.py
index 27dd8b4..6b0d304 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -363,6 +363,7 @@
 
     # Fall back to deprecated method: get try slaves from PRESUBMIT.py
     # files.
+    # TODO(qyearsley): Remove this.
     options.bot = presubmit_support.DoGetTrySlaves(
         change=change,
         changed_files=change.LocalPaths(),
@@ -378,10 +379,22 @@
   if options.bucket:
     return {options.bucket: {b: [] for b in options.bot}}
 
+  if not options.master:
+    bucket_map, error_message = _get_bucket_map_for_builders(options.bot)
+    if error_message:
+      option_parser.error(
+          'Tryserver master cannot be found because: %s\n'
+          'Please manually specify the tryserver master, e.g. '
+          '"-m tryserver.chromium.linux".' % error_message)
+    return bucket_map
+
   builders_and_tests = {}
 
   # TODO(machenbach): The old style command-line options don't support
   # multiple try masters yet.
+  # TODO(qyearsley): If options.bot is always a list of strings, then
+  # "new_style" never applies, and so we should remove support for Specifying
+  # test filters completely.
   old_style = filter(lambda x: isinstance(x, basestring), options.bot)
   new_style = filter(lambda x: isinstance(x, tuple), options.bot)
 
@@ -396,58 +409,37 @@
   for bot, tests in new_style:
     builders_and_tests.setdefault(bot, []).extend(tests)
 
-  if not options.master:
-    # TODO(qyearsley): crbug.com/640740
-    options.master, error_message = _get_builder_master(options.bot)
-    if error_message:
-      option_parser.error(
-          'Tryserver master cannot be found because: %s\n'
-          'Please manually specify the tryserver master, e.g. '
-          '"-m tryserver.chromium.linux".' % error_message)
-
-  # Return a master map with one master to be backwards compatible. The
-  # master name defaults to an empty string, which will cause the master
-  # not to be set on rietveld (deprecated).
-  bucket = ''
-  if options.master:
-    # Add the "master." prefix to the master name to obtain the bucket name.
-    bucket = _prefix_master(options.master)
+  # Add the "master." prefix to the master name to obtain the bucket name.
+  bucket = _prefix_master(options.master)
   return {bucket: builders_and_tests}
 
 
-def _get_builder_master(bot_list):
-  """Fetches a master for the given list of builders.
-
-  Returns a pair (master, error_message), where either master or
-  error_message is None.
-  """
+def _get_bucket_map_for_builders(builders):
+  """Returns a map of buckets to builders for the given builders."""
   map_url = 'https://builders-map.appspot.com/'
   try:
-    master_map = json.load(urllib2.urlopen(map_url))
+    builders_map = json.load(urllib2.urlopen(map_url))
   except urllib2.URLError as e:
     return None, ('Failed to fetch builder-to-master map from %s. Error: %s.' %
                   (map_url, e))
   except ValueError as e:
     return None, ('Invalid json string from %s. Error: %s.' % (map_url, e))
-  if not master_map:
+  if not builders_map:
     return None, 'Failed to build master map.'
 
-  result_master = ''
-  for bot in bot_list:
-    builder = bot.split(':', 1)[0]
-    master_list = master_map.get(builder, [])
-    if not master_list:
+  bucket_map = {}
+  for builder in builders:
+    builder = builder.split(':', 1)[0]
+    masters = builders_map.get(builder, [])
+    if not masters:
       return None, ('No matching master for builder %s.' % builder)
-    elif len(master_list) > 1:
+    if len(masters) > 1:
       return None, ('The builder name %s exists in multiple masters %s.' %
-                    (builder, master_list))
-    else:
-      cur_master = master_list[0]
-      if not result_master:
-        result_master = cur_master
-      elif result_master != cur_master:
-        return None, 'The builders do not belong to the same master.'
-  return result_master, None
+                    (builder, masters))
+    bucket = _prefix_master(masters[0])
+    bucket_map.setdefault(bucket, {})[builder] = []
+
+  return bucket_map, None
 
 
 def _trigger_try_jobs(auth_config, changelist, buckets, options,