cros_setup_toolchains: speed up the package stable detection routine

Evaluate all packages of a single target in one pass using the
mass_best_visible portageq action.

BUG=none
TEST=run it

Change-Id: I35e34cf3d587527f034c7e71aebebedfc23e080b
Reviewed-on: https://gerrit.chromium.org/gerrit/20113
Tested-by: Zdenek Behan <zbehan@chromium.org>
Reviewed-by: David James <davidjames@chromium.org>
Commit-Ready: Zdenek Behan <zbehan@chromium.org>
diff --git a/scripts/cros_setup_toolchains.py b/scripts/cros_setup_toolchains.py
index 4b6a658..1217638 100644
--- a/scripts/cros_setup_toolchains.py
+++ b/scripts/cros_setup_toolchains.py
@@ -217,13 +217,29 @@
 
   returns a string containing the latest version.
   """
-  keyword = GetPortageKeyword(target)
-  extra_env = {'ACCEPT_KEYWORDS' : '-* ' + keyword}
-  atom = GetPortagePackage(target, package)
-  cpv = cros_build_lib.RunCommand(['portageq', 'best_visible', '/', atom],
-                                  print_cmd=False, redirect_stdout=True,
-                                  extra_env=extra_env).output.splitlines()[0]
-  return portage.versions.cpv_getversion(cpv)
+  def mass_portageq_splitline(entry):
+    """Splits the output of mass_best_visible into package:version tuple."""
+    # mass_best_visible returns lines of the format "package:cpv"
+    split_string = entry.split(':', 1)
+    split_string[1] = portage.versions.cpv_getversion(split_string[1])
+    return split_string
+
+  CACHE_ATTR = '_target_stable_map'
+
+  val = getattr(VAR_CACHE, CACHE_ATTR, {})
+  if not target in val:
+    keyword = GetPortageKeyword(target)
+    extra_env = {'ACCEPT_KEYWORDS' : '-* ' + keyword}
+    # Evaluate all packages for a target in one swoop, because it's much faster.
+    pkgs = [GetPortagePackage(target, p) for p in GetTargetPackages(target)]
+    cmd = ['portageq', 'mass_best_visible', '/'] + pkgs
+    cpvs = cros_build_lib.RunCommand(cmd,
+                                     print_cmd=False, redirect_stdout=True,
+                                     extra_env=extra_env).output.splitlines()
+    val[target] = dict(map(mass_portageq_splitline, cpvs))
+    setattr(VAR_CACHE, CACHE_ATTR, val)
+
+  return val[target][GetPortagePackage(target, package)]
 
 
 def VersionListToNumeric(target, package, versions):