gclient flatten: implement --pin-all-deps

Bug: 570091
Change-Id: Ibf7c6a73cab8bb777a7e4a8d958f085238c76450
Reviewed-on: https://chromium-review.googlesource.com/562138
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Commit-Queue: Paweł Hajdan Jr. <phajdan.jr@chromium.org>
diff --git a/gclient.py b/gclient.py
index 657c5af..2ded56b 100755
--- a/gclient.py
+++ b/gclient.py
@@ -1720,11 +1720,13 @@
 class Flattener(object):
   """Flattens a gclient solution."""
 
-  def __init__(self, client):
+  def __init__(self, client, pin_all_deps=False):
     """Constructor.
 
     Arguments:
       client (GClient): client to flatten
+      pin_all_deps (bool): whether to pin all deps, even if they're not pinned
+          in DEPS
     """
     self._client = client
 
@@ -1738,18 +1740,35 @@
     self._pre_deps_hooks = []
     self._vars = {}
 
-    self._flatten()
+    self._flatten(pin_all_deps=pin_all_deps)
 
   @property
   def deps_string(self):
     assert self._deps_string is not None
     return self._deps_string
 
-  def _flatten(self):
-    """Runs the flattener. Saves resulting DEPS string."""
+  def _flatten(self, pin_all_deps=False):
+    """Runs the flattener. Saves resulting DEPS string.
+
+    Arguments:
+      pin_all_deps (bool): whether to pin all deps, even if they're not pinned
+          in DEPS
+    """
     for solution in self._client.dependencies:
       self._flatten_solution(solution)
 
+    if pin_all_deps:
+      for dep in self._deps.itervalues():
+        if dep.parsed_url is None:
+          continue
+        url, revision = gclient_utils.SplitUrlRevision(dep.parsed_url)
+        if revision and gclient_utils.IsGitSha(revision):
+          continue
+        scm = gclient_scm.CreateSCM(
+            dep.parsed_url, self._client.root_dir, dep.name, dep.outbuf)
+        dep._parsed_url = dep._url = '%s@%s' % (
+            url, scm.revinfo(self._client._options, [], None))
+
     self._deps_string = '\n'.join(
         _GNSettingsToLines(
             self._client.dependencies[0]._gn_args_file,
@@ -1825,6 +1844,10 @@
 def CMDflatten(parser, args):
   """Flattens the solutions into a single DEPS file."""
   parser.add_option('--output-deps', help='Path to the output DEPS file')
+  parser.add_option(
+      '--pin-all-deps', action='store_true',
+      help=('Pin all deps, even if not pinned in DEPS. CAVEAT: only does so '
+            'for checked out deps, NOT deps_os.'))
   options, args = parser.parse_args(args)
 
   options.nohooks = True
@@ -1836,7 +1859,7 @@
   if code != 0:
     return code
 
-  flattener = Flattener(client)
+  flattener = Flattener(client, pin_all_deps=options.pin_all_deps)
 
   if options.output_deps:
     with open(options.output_deps, 'w') as f: