Index: cpplint.py |
diff --git a/cpplint.py b/cpplint.py |
index 92384dc37181f6e982d0e6f74afe93d393c5de19..65e2b4259217663e6c50ad2d4e700aec80938419 100755 |
--- a/cpplint.py |
+++ b/cpplint.py |
@@ -133,6 +133,33 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] |
Examples: |
--extensions=hpp,cpp |
+ |
+ cpplint.py supports per-directory configurations specified in CPPLINT.cfg |
+ files. CPPLINT.cfg file can contain a number of key=value pairs. |
+ Currently the following options are supported: |
+ |
+ filter=+filter1,-filter2,... |
+ exclude_files=regex |
+ |
+ The "filter" option is similar in function to --filter flag. It specifies |
+ message filters in addition to the |_DEFAULT_FILTERS| and those specified |
+ through --filter command-line flag. |
+ |
+ "exclude_files" allows to specify a regular expression to be matched against |
+ a file name. If the expression matches, the file is skipped and not run |
+ through liner. |
+ |
+ CPPLINT.cfg has an effect on files in the same directory and all |
+ sub-directories, unless overridden by a nested configuration file. |
+ |
+ Example file: |
+ filter=-build/include_order,+build/include_alpha |
+ exclude_files=.*\.cc |
+ |
+ The above example disables build/include_order warning and enables |
+ build/include_alpha as well as excludes all .cc from being |
+ processed by linter, in the current directory (where the .cfg |
+ file is located) and all sub-directories. |
""" |
# We categorize each error message we print. Here are the categories. |
@@ -682,6 +709,8 @@ class _CppLintState(object): |
self.error_count = 0 # global count of reported errors |
# filters to apply when emitting error messages |
self.filters = _DEFAULT_FILTERS[:] |
+ # backup of filter list. Used to restore the state after each file. |
+ 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 |
@@ -720,6 +749,10 @@ class _CppLintState(object): |
""" |
# Default filters always have less priority than the flag ones. |
self.filters = _DEFAULT_FILTERS[:] |
+ self.AddFilters(filters) |
+ |
+ def AddFilters(self, filters): |
+ """ Adds more filters to the existing list of error-message filters. """ |
for filt in filters.split(','): |
clean_filt = filt.strip() |
if clean_filt: |
@@ -729,6 +762,14 @@ class _CppLintState(object): |
raise ValueError('Every filter in --filters must start with + or -' |
' (%s does not)' % filt) |
+ def BackupFilters(self): |
+ """ Saves the current filter list to backup storage.""" |
+ self._filters_backup = self.filters[:] |
+ |
+ def RestoreFilters(self): |
+ """ Restores filters previously backed up.""" |
+ self.filters = self._filters_backup[:] |
+ |
def ResetErrorCounts(self): |
"""Sets the module's error statistic back to zero.""" |
self.error_count = 0 |
@@ -796,6 +837,25 @@ def _SetFilters(filters): |
""" |
_cpplint_state.SetFilters(filters) |
+def _AddFilters(filters): |
+ """Adds more filter overrides. |
+ |
+ Unlike _SetFilters, this function does not reset the current list of filters |
+ available. |
+ |
+ Args: |
+ filters: A string of comma-separated filters (eg "whitespace/indent"). |
+ Each filter should start with + or -; else we die. |
+ """ |
+ _cpplint_state.AddFilters(filters) |
+ |
+def _BackupFilters(): |
+ """ Saves the current filter list to backup storage.""" |
+ _cpplint_state.BackupFilters() |
+ |
+def _RestoreFilters(): |
+ """ Restores filters previously backed up.""" |
+ _cpplint_state.RestoreFilters() |
class _FunctionState(object): |
"""Tracks current function name and the number of lines in its body.""" |
@@ -5328,7 +5388,7 @@ def ProcessLine(filename, file_extension, clean_lines, line, |
CheckDefaultLambdaCaptures(filename, clean_lines, line, error) |
for check_fn in extra_check_functions: |
check_fn(filename, clean_lines, line, error) |
- |
+ |
def FlagCxx11Features(filename, clean_lines, linenum, error): |
"""Flag those c++11 features that we only allow in certain places. |
@@ -5424,6 +5484,83 @@ def ProcessFileData(filename, file_extension, lines, error, |
CheckForNewlineAtEOF(filename, lines, error) |
+def GetConfigOverrideFiles(filename): |
+ """ Looks for CPPLINT.cfg files in the directory containing |filename| and |
sosa
2014/07/23 20:25:42
docstring issue
Alex Vakulenko (Google)
2014/07/23 20:44:32
Done.
|
+ its parents and returns a list of configuration files found. |
+ |
+ Args: |
+ filename: The name of the file being processed by the linter. |
+ """ |
+ |
+ cfg_override_files = [] |
+ abs_path = os.path.abspath(os.path.dirname(filename)) |
+ |
+ while True: |
+ file = os.path.join(abs_path, "CPPLINT.cfg") |
+ if os.path.isfile(file): |
+ cfg_override_files.append(file) |
+ parent = os.path.dirname(abs_path) |
+ if parent == abs_path: |
+ break |
+ abs_path = parent |
+ |
+ return reversed(cfg_override_files) |
+ |
+def ProcessConfigFile(cfg_file, filename): |
+ """ Loads the configuration file and processes the config overrides. |
+ |
+ Args: |
+ cfg_file: config file to process |
+ filename: The name of the file being processed by the linter. |
+ |
+ Returns: |
+ false if the current |filename| should not be processed further. |
+ """ |
+ |
+ try: |
+ with open(cfg_file) as file_handle: |
+ for line in file_handle: |
+ line, _, _ = line.partition('#') # Remove comments. |
+ if line.strip(): |
+ name, _, val = line.partition('=') |
+ name = name.strip() |
+ val = val.strip() |
+ if name == 'filter': |
+ _AddFilters(val) |
+ elif name == 'exclude_files': |
+ pattern = re.compile(val) |
+ if pattern.match(filename): |
+ sys.stderr.write('Ignoring %s; file excluded by (%s)\n' % |
+ (filename, cfg_file)) |
+ return False |
+ else: |
+ sys.stderr.write('Invalid configuration option (%s) in file %s\n' % |
+ (name, cfg_file)) |
+ |
+ except IOError: |
+ sys.stderr.write( |
+ "Skipping config file '%s': Can't open for reading\n" % cfg_file) |
+ _RestoreFilters() |
+ return True |
+ |
+ return True |
+ |
+ |
+def ProcessConfigOverrides(cfg_override_files, filename): |
+ """ Loads the configuration files and processes the config overrides. |
+ |
+ Args: |
+ cfg_override_files: list of cfg files to process |
+ filename: The name of the file being processed by the linter. |
+ |
+ Returns: |
+ false if the current |filename| should not be processed further. |
+ """ |
+ for cfg_file in cfg_override_files: |
+ if not ProcessConfigFile(cfg_file, filename): |
+ return False |
+ |
+ return True |
def ProcessFile(filename, vlevel, extra_check_functions=[]): |
"""Does google-lint on a single file. |
@@ -5440,6 +5577,12 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): |
""" |
_SetVerboseLevel(vlevel) |
+ _BackupFilters() |
+ |
+ cfg_override_files = GetConfigOverrideFiles(filename) |
+ if not ProcessConfigOverrides(cfg_override_files, filename): |
+ _RestoreFilters() |
+ return |
lf_lines = [] |
crlf_lines = [] |
@@ -5471,6 +5614,7 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): |
except IOError: |
sys.stderr.write( |
"Skipping input '%s': Can't open for reading\n" % filename) |
+ _RestoreFilters() |
return |
# Note, if no dot is found, this will give the entire filename as the ext. |
@@ -5504,6 +5648,7 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): |
'Unexpected \\r (^M) found; better to use only \\n') |
sys.stderr.write('Done processing %s\n' % filename) |
+ _RestoreFilters() |
def PrintUsage(message): |