Add a libcxx_revision presubmit check.

Both DEPS and buildtools/deps_revisions.gni have libcxx_revision, and
they have to stay in sync. Add a presubmit to make sure this is the
case.

The check needs to be a separate tool that lives outside of
PRESUBMIT.py, because the ReadFile() API available inside of
PRESUBMIT.py cannot read files outside of the PDFium git repo. Since
buildtools is pulled in via DEPS, buildtools/deps_revisions.gni is
actually in a different git repo.

Change-Id: I5eb2aff800bf89da2d17045be61bbbf61e9cab4e
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/97773
Reviewed-by: Nigi <nigi@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 606f48d..8f2c59a 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -367,6 +367,28 @@
   return results
 
 
+def _CheckLibcxxRevision(input_api, output_api):
+  """Makes sure that libcxx_revision is set correctly."""
+  if 'DEPS' not in [f.LocalPath() for f in input_api.AffectedFiles()]:
+    return []
+
+  script_path = input_api.os_path.join('testing', 'tools', 'libcxx_check.py')
+  buildtools_deps_path = input_api.os_path.join('buildtools',
+                                                'deps_revisions.gni')
+
+  try:
+    errors = input_api.subprocess.check_output(
+        [script_path, 'DEPS', buildtools_deps_path])
+  except input_api.subprocess.CalledProcessError as error:
+    msg = 'libcxx_check.py failed:'
+    long_text = error.output.decode('utf-8', 'ignore')
+    return [output_api.PresubmitError(msg, long_text=long_text)]
+
+  if errors:
+    return [output_api.PresubmitError(errors)]
+  return []
+
+
 def _CheckTestDuplicates(input_api, output_api):
   """Checks that pixel and javascript tests don't contain duplicates.
   We use .in and .pdf files, having both can cause race conditions on the bots,
@@ -474,6 +496,7 @@
       input_api.canned_checks.CheckChangeLintsClean(
           input_api, output_api, lint_filters=LINT_FILTERS))
   results.extend(_CheckIncludeOrder(input_api, output_api))
+  results.extend(_CheckLibcxxRevision(input_api, output_api))
   results.extend(_CheckTestDuplicates(input_api, output_api))
   results.extend(_CheckPNGFormat(input_api, output_api))
   results.extend(_CheckUselessForwardDeclarations(input_api, output_api))
diff --git a/testing/tools/libcxx_check.py b/testing/tools/libcxx_check.py
new file mode 100755
index 0000000..ad6abea
--- /dev/null
+++ b/testing/tools/libcxx_check.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Verifies libcxx_revision entries are in sync.
+
+DEPS and buildtools/deps_revisions.gni both have libcxx_revision entries.
+Check that they are in sync.
+"""
+
+import re
+import sys
+
+
+def _ExtractRevisionFromFile(path, regex):
+  """Gets the revision by reading path and searching the lines using regex."""
+  data = open(path, 'rb').read().splitlines()
+  revision = None
+  for line in data:
+    match = regex.match(line)
+    if not match:
+      continue
+    if revision:
+      return None
+    revision = match.group(1)
+  return revision
+
+
+def _GetDepsLibcxxRevision(deps_path):
+  """Gets the libcxx_revision from DEPS."""
+  regex = re.compile(b"^  'libcxx_revision': '(.*)',$")
+  return _ExtractRevisionFromFile(deps_path, regex)
+
+
+def _GetBuildtoolsLibcxxRevision(buildtools_deps_path):
+  """Gets the libcxx_revision from buildtools/deps_revisions.gni."""
+  regex = re.compile(b'^  libcxx_revision = "(.*)"$')
+  return _ExtractRevisionFromFile(buildtools_deps_path, regex)
+
+
+def main():
+  if len(sys.argv) != 3:
+    print('Wrong number of arguments')
+    return 0
+
+  deps_path = sys.argv[1]
+  deps_revision = _GetDepsLibcxxRevision(deps_path)
+  if not deps_revision:
+    print('Cannot parse', deps_path)
+    return 0
+
+  buildtools_deps_path = sys.argv[2]
+  buildtools_revision = _GetBuildtoolsLibcxxRevision(buildtools_deps_path)
+  if not buildtools_revision:
+    print('Cannot parse', buildtools_deps_path)
+    return 0
+
+  if deps_revision != buildtools_revision:
+    print('libcxx_revision mismatch between %s and %s: %s vs. %s' %
+          (deps_path, buildtools_deps_path, deps_revision, buildtools_revision))
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())