From e8ffd7ce60e5c0e98ba37745749fed0222982668 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Fri, 10 Nov 2017 11:52:02 -0800 Subject: [PATCH] cpplint: add --quiet flag to suppress all output when there's no errors Example: cpplint.py --quiet Will now return with an exit code of 0 and return empty output if there were no errors. This makes it particularly useful to be driven by a build system such as makefiles or gradle. In particular, these messages are now suppressed: Ignoring , excluded by CPPLINT.cfg, ... Done processing Total errors found: 0 If there were any errors, the above messages are printed nevertheless. There is no behavior change if --quiet is not passed in. --- cpplint/cpplint.py | 39 +++++++++++++++++-- cpplint/cpplint_unittest.py | 78 +++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/cpplint/cpplint.py b/cpplint/cpplint.py index a981512..53dbe81 100755 --- a/cpplint/cpplint.py +++ b/cpplint/cpplint.py @@ -57,6 +57,7 @@ _USAGE = """ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] [--counting=total|toplevel|detailed] [--root=subdir] [--linelength=digits] [--headers=x,y,...] + [--quiet] [file] ... The style guidelines this tries to follow are those in @@ -83,6 +84,9 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] verbose=# Specify a number 0-5 to restrict errors to certain verbosity levels. + quiet + Don't print anything if no errors are found. + filter=-x,+y,... Specify a comma-separated list of category-filters to apply: only error messages whose category names pass the filters will be printed. @@ -861,6 +865,7 @@ class _CppLintState(object): self._filters_backup = self.filters[:] self.counting = 'total' # In what way are we counting errors? self.errors_by_category = {} # string to int dict storing error counts + self.quiet = False # Suppress non-error messagess? # output format: # "emacs" - format that emacs can parse (default) @@ -871,6 +876,12 @@ class _CppLintState(object): """Sets the output format for errors.""" self.output_format = output_format + def SetQuiet(self, quiet): + """Sets the module's quiet settings, and returns the previous setting.""" + last_quiet = self.quiet + self.quiet = quiet + return last_quiet + def SetVerboseLevel(self, level): """Sets the module's verbosity, and returns the previous setting.""" last_verbose_level = self.verbose_level @@ -952,6 +963,14 @@ def _SetOutputFormat(output_format): """Sets the module's output format.""" _cpplint_state.SetOutputFormat(output_format) +def _Quiet(): + """Return's the module's quiet setting.""" + return _cpplint_state.quiet + +def _SetQuiet(quiet): + """Set the module's quiet status, and return previous setting.""" + return _cpplint_state.SetQuiet(quiet) + def _VerboseLevel(): """Returns the module's verbosity setting.""" @@ -5955,6 +5974,9 @@ def ProcessConfigOverrides(filename): if base_name: pattern = re.compile(val) if pattern.match(base_name): + if _cpplint_state.quiet: + # Suppress "Ignoring file" warning when using --quiet. + return False sys.stderr.write('Ignoring "%s": file excluded by "%s". ' 'File path component "%s" matches ' 'pattern "%s"\n' % @@ -6006,6 +6028,7 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): _SetVerboseLevel(vlevel) _BackupFilters() + old_errors = _cpplint_state.error_count if not ProcessConfigOverrides(filename): _RestoreFilters() @@ -6074,7 +6097,10 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): Error(filename, linenum, 'whitespace/newline', 1, 'Unexpected \\r (^M) found; better to use only \\n') - sys.stdout.write('Done processing %s\n' % filename) + # Suppress printing anything if --quiet was passed unless the error + # count has increased after processing this file. + if not _cpplint_state.quiet or old_errors != _cpplint_state.error_count: + sys.stdout.write('Done processing %s\n' % filename) _RestoreFilters() @@ -6118,13 +6144,15 @@ def ParseArguments(args): 'root=', 'linelength=', 'extensions=', - 'headers=']) + 'headers=', + 'quiet']) except getopt.GetoptError: PrintUsage('Invalid arguments.') verbosity = _VerboseLevel() output_format = _OutputFormat() filters = '' + quiet = _Quiet() counting_style = '' for (opt, val) in opts: @@ -6134,6 +6162,8 @@ def ParseArguments(args): if val not in ('emacs', 'vs7', 'eclipse'): PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') output_format = val + elif opt == '--quiet': + quiet = True elif opt == '--verbose': verbosity = int(val) elif opt == '--filter': @@ -6166,6 +6196,7 @@ def ParseArguments(args): PrintUsage('No files were specified.') _SetOutputFormat(output_format) + _SetQuiet(quiet) _SetVerboseLevel(verbosity) _SetFilters(filters) _SetCountingStyle(counting_style) @@ -6186,7 +6217,9 @@ def main(): _cpplint_state.ResetErrorCounts() for filename in filenames: ProcessFile(filename, _cpplint_state.verbose_level) - _cpplint_state.PrintErrorCounts() + # If --quiet is passed, suppress printing error count unless there are errors. + if not _cpplint_state.quiet or _cpplint_state.error_count > 0: + _cpplint_state.PrintErrorCounts() sys.exit(_cpplint_state.error_count > 0) diff --git a/cpplint/cpplint_unittest.py b/cpplint/cpplint_unittest.py index a1cd481..a07ffd8 100755 --- a/cpplint/cpplint_unittest.py +++ b/cpplint/cpplint_unittest.py @@ -37,6 +37,7 @@ import codecs import os import random import re +import subprocess import sys import unittest @@ -5686,6 +5687,83 @@ class NestingStateTest(unittest.TestCase): self.assertEquals(len(self.nesting_state.stack), 0) +class QuietTest(unittest.TestCase): + + def setUp(self): + self.this_dir_path = os.path.dirname(os.path.abspath(__file__)) + self.python_executable = sys.executable or 'python' + self.cpplint_test_h = os.path.join(self.this_dir_path, + 'cpplint_test_header.h') + + def _runCppLint(self, *args): + cpplint_abspath = os.path.join(self.this_dir_path, 'cpplint.py') + + cmd_line = [self.python_executable, cpplint_abspath] + \ + list(args) + \ + [ self.cpplint_test_h ] + + return_code = 0 + try: + output = subprocess.check_output(cmd_line, + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as err: + return_code = err.returncode + output = err.output + + return (return_code, output) + + def testNonQuietWithErrors(self): + # This will fail: the test header is missing a copyright and header guard. + (return_code, output) = self._runCppLint() + self.assertEquals(1, return_code) + # Always-on behavior: Print error messages as they come up. + self.assertIn("[legal/copyright]", output) + self.assertIn("[build/header_guard]", output) + # If --quiet was unspecified: Print 'Done processing' and 'Total errors..' + self.assertIn("Done processing", output) + self.assertIn("Total errors found:", output) + + def testQuietWithErrors(self): + # When there are errors, behavior is identical to not passing --quiet. + (return_code, output) = self._runCppLint('--quiet') + self.assertEquals(1, return_code) + self.assertIn("[legal/copyright]", output) + self.assertIn("[build/header_guard]", output) + # Even though --quiet was used, print these since there were errors. + self.assertIn("Done processing", output) + self.assertIn("Total errors found:", output) + + def testNonQuietWithoutErrors(self): + # This will succeed. We filtered out all the known errors for that file. + (return_code, output) = self._runCppLint('--filter=' + + '-legal/copyright,' + + '-build/header_guard') + self.assertEquals(0, return_code, output) + # No cpplint errors are printed since there were no errors. + self.assertNotIn("[legal/copyright]", output) + self.assertNotIn("[build/header_guard]", output) + # Print 'Done processing' and 'Total errors found' since + # --quiet was not specified. + self.assertIn("Done processing", output) + self.assertIn("Total errors found:", output) + + def testQuietWithoutErrors(self): + # This will succeed. We filtered out all the known errors for that file. + (return_code, output) = self._runCppLint('--quiet', + '--filter=' + + '-legal/copyright,' + + '-build/header_guard') + self.assertEquals(0, return_code, output) + # No cpplint errors are printed since there were no errors. + self.assertNotIn("[legal/copyright]", output) + self.assertNotIn("[build/header_guard]", output) + # --quiet was specified and there were no errors: + # skip the printing of 'Done processing' and 'Total errors..' + self.assertNotIn("Done processing", output) + self.assertNotIn("Total errors found:", output) + # Output with no errors must be completely blank! + self.assertEquals("", output) + # pylint: disable-msg=C6409 def setUp(): """Runs before all tests are executed.