Implement git cl lint.

Review URL: https://codereview.chromium.org/134223010

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@256289 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/git_cl.py b/git_cl.py
index 82359c0..12f2c87 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -51,6 +51,10 @@
 GIT_INSTRUCTIONS_URL = 'http://code.google.com/p/chromium/wiki/UsingGit'
 CHANGE_ID = 'Change-Id:'
 
+# Valid extensions for files we want to lint.
+DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
+DEFAULT_LINT_IGNORE_REGEX = r"$^"
+
 # Shortcut since it quickly becomes redundant.
 Fore = colorama.Fore
 
@@ -423,6 +427,14 @@
       self.git_editor = self._GetConfig('core.editor', error_ok=True)
     return self.git_editor or None
 
+  def GetLintRegex(self):
+    return (self._GetRietveldConfig('cpplint-regex', error_ok=True) or
+            DEFAULT_LINT_REGEX)
+
+  def GetLintIgnoreRegex(self):
+    return (self._GetRietveldConfig('cpplint-ignore-regex', error_ok=True) or
+            DEFAULT_LINT_IGNORE_REGEX)
+
   def _GetRietveldConfig(self, param, **kwargs):
     return self._GetConfig('rietveld.' + param, **kwargs)
 
@@ -1044,6 +1056,8 @@
   SetProperty('tree-status-url', 'STATUS', unset_error_ok=True)
   SetProperty('viewvc-url', 'VIEW_VC', unset_error_ok=True)
   SetProperty('bug-prefix', 'BUG_PREFIX', unset_error_ok=True)
+  SetProperty('cpplint-regex', 'LINT_REGEX', unset_error_ok=True)
+  SetProperty('cpplint-ignore-regex', 'LINT_IGNORE_REGEX', unset_error_ok=True)
 
   if 'GERRIT_HOST' in keyvals:
     RunGit(['config', 'gerrit.host', keyvals['GERRIT_HOST']])
@@ -1366,6 +1380,51 @@
   return RunGit(['log', '--pretty=format:%s\n\n%b'] + log_args)
 
 
+def CMDlint(parser, args):
+  """Runs cpplint on the current changelist."""
+  _, args = parser.parse_args(args)
+
+  # Access to a protected member _XX of a client class
+  # pylint: disable=W0212
+  try:
+    import cpplint
+    import cpplint_chromium
+  except ImportError:
+    print "Your depot_tools is missing cpplint.py and/or cpplint_chromium.py."
+    return 1
+
+  # Change the current working directory before calling lint so that it
+  # shows the correct base.
+  previous_cwd = os.getcwd()
+  os.chdir(settings.GetRoot())
+  try:
+    cl = Changelist()
+    change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None)
+    files = [f.LocalPath() for f in change.AffectedFiles()]
+
+    # Process cpplints arguments if any.
+    filenames = cpplint.ParseArguments(args + files)
+
+    white_regex = re.compile(settings.GetLintRegex())
+    black_regex = re.compile(settings.GetLintIgnoreRegex())
+    extra_check_functions = [cpplint_chromium.CheckPointerDeclarationWhitespace]
+    for filename in filenames:
+      if white_regex.match(filename):
+        if black_regex.match(filename):
+          print "Ignoring file %s" % filename
+        else:
+          cpplint.ProcessFile(filename, cpplint._cpplint_state.verbose_level,
+                              extra_check_functions)
+      else:
+        print "Skipping file %s" % filename
+  finally:
+    os.chdir(previous_cwd)
+  print "Total errors found: %d\n" % cpplint._cpplint_state.error_count
+  if cpplint._cpplint_state.error_count != 0:
+    return 1
+  return 0
+
+
 def CMDpresubmit(parser, args):
   """Runs presubmit tests on the current changelist."""
   parser.add_option('-u', '--upload', action='store_true',
diff --git a/tests/git_cl_test.py b/tests/git_cl_test.py
index 1be5290..e5caec5 100755
--- a/tests/git_cl_test.py
+++ b/tests/git_cl_test.py
@@ -674,6 +674,10 @@
            'rietveld.viewvc-url'],), ''),
         ((['git', 'config', '--unset-all',
            'rietveld.bug-prefix'],), ''),
+        ((['git', 'config', '--unset-all',
+           'rietveld.cpplint-regex'],), ''),
+        ((['git', 'config', '--unset-all',
+           'rietveld.cpplint-ignore-regex'],), ''),
         ((['git', 'config', 'gerrit.host',
            'gerrit.chromium.org'],), ''),
         # DownloadHooks(False)