gclient: add gitmodules command to update/add submodules based on DEPS.

Bug: 1421776
Change-Id: Id1ac48c4b65c17027fa24d0ba350a1a7f2eec64d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/4665787
Commit-Queue: Joanna Wang <jojwang@chromium.org>
Reviewed-by: Josip Sokcevic <sokcevic@chromium.org>
diff --git a/gclient.py b/gclient.py
index ddc36ac..8b5606c 100755
--- a/gclient.py
+++ b/gclient.py
@@ -115,6 +115,7 @@
 import subcommand
 import subprocess2
 import setup_color
+import git_cl
 
 from third_party import six
 
@@ -2654,6 +2655,67 @@
         self._flatten_dep(d)
 
 
+@metrics.collector.collect_metrics('gclient gitmodules')
+def CMDgitmodules(parser, args):
+  """Adds or updates Git Submodules based on the contents of the DEPS file.
+
+  This command should be run in the root director of the repo.
+  It will create or update the .gitmodules file and include
+  `gclient-condition` values. Commits in gitlinks will also be updated.
+  """
+  parser.add_option('--output-gitmodules',
+                    help='name of the .gitmodules file to write to',
+                    default='.gitmodules')
+  parser.add_option(
+      '--deps-file',
+      help=
+      'name of the deps file to parse for git dependency paths and commits.',
+      default='DEPS')
+  parser.add_option(
+      '--skip-dep',
+      action="append",
+      help='skip adding gitmodules for the git dependency at the given path',
+      default=[])
+  options, args = parser.parse_args(args)
+
+  deps_dir = os.path.dirname(os.path.abspath(options.deps_file))
+  gclient_path = gclient_paths.FindGclientRoot(deps_dir)
+  if not gclient_path:
+    logging.error(
+        '.gclient not found\n'
+        'Make sure you are running this script from a gclient workspace.')
+    sys.exit(1)
+
+  deps_content = gclient_utils.FileRead(options.deps_file)
+  ls = gclient_eval.Parse(deps_content, options.deps_file, None, None)
+
+  prefix_length = 0
+  if not 'use_relative_paths' in ls or ls['use_relative_paths'] != True:
+    delta_path = os.path.relpath(deps_dir, os.path.abspath(gclient_path))
+    if delta_path:
+      prefix_length = len(delta_path.replace(os.path.sep, '/')) + 1
+
+  with open(options.output_gitmodules, 'w') as f:
+    for path, dep in ls.get('deps').items():
+      if path in options.skip_dep:
+        continue
+      if dep.get('dep_type') == 'cipd':
+        continue
+      try:
+        url, commit = dep['url'].split('@', maxsplit=1)
+      except ValueError:
+        logging.error('error on %s; %s, not adding it', path, dep["url"])
+        continue
+      if prefix_length:
+        path = path[prefix_length:]
+
+      git_cl.RunGit(
+          ['update-index', '--add', '--cacheinfo', '160000', commit, path])
+      f.write(f'[submodule "{path}"]\n\tpath = {path}\n\turl = {url}\n')
+      if 'condition' in dep:
+        f.write(f'\tgclient-condition = {dep["condition"]}\n')
+
+
 @metrics.collector.collect_metrics('gclient flatten')
 def CMDflatten(parser, args):
   """Flattens the solutions into a single DEPS file."""