| Index: tools/checkdeps/checkdeps.py
|
| diff --git a/tools/checkdeps/checkdeps.py b/tools/checkdeps/checkdeps.py
|
| index 0b2b7ac6a642bb15c49253836f6d8674747fe3fb..a7cff8bc633bc18ddc2f3c751cd7f17fd61c6797 100755
|
| --- a/tools/checkdeps/checkdeps.py
|
| +++ b/tools/checkdeps/checkdeps.py
|
| @@ -72,16 +72,17 @@ import copy
|
|
|
| import cpp_checker
|
| import java_checker
|
| +from results import NormalResultsFormatter, TemporaryRulesFormatter
|
| from rules import Rule, Rules
|
|
|
|
|
| # Variable name used in the DEPS file to add or subtract include files from
|
| # the module-level deps.
|
| -INCLUDE_RULES_VAR_NAME = "include_rules"
|
| +INCLUDE_RULES_VAR_NAME = 'include_rules'
|
|
|
| # Optionally present in the DEPS file to list subdirectories which should not
|
| # be checked. This allows us to skip third party code, for example.
|
| -SKIP_SUBDIRS_VAR_NAME = "skip_child_includes"
|
| +SKIP_SUBDIRS_VAR_NAME = 'skip_child_includes'
|
|
|
|
|
| def NormalizePath(path):
|
| @@ -109,11 +110,25 @@ class DepsChecker(object):
|
| os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..'))
|
|
|
| self.verbose = verbose
|
| + self.results_formatter = NormalResultsFormatter(verbose)
|
| +
|
| + self._under_test = being_tested
|
|
|
| self.git_source_directories = set()
|
| self._AddGitSourceDirectories()
|
|
|
| - self._under_test = being_tested
|
| + # Map of normalized directory paths to rules to use for those
|
| + # directories, or None for directories that should be skipped.
|
| + self.directory_rules = {}
|
| + self._ApplyDirectoryRulesAndSkipSubdirs(Rules(), self.base_directory)
|
| +
|
| + def Report(self):
|
| + """Prints a report of results, and returns an exit code for the process."""
|
| + if self.results_formatter.GetResults():
|
| + self.results_formatter.PrintResults()
|
| + return 1
|
| + print '\nSUCCESS\n'
|
| + return 0
|
|
|
| def _ApplyRules(self, existing_rules, includes, cur_dir):
|
| """Applies the given include rules, returning the new rules.
|
| @@ -136,17 +151,17 @@ class DepsChecker(object):
|
|
|
| source = relative_dir
|
| if len(source) == 0:
|
| - source = "top level" # Make the help string a little more meaningful.
|
| - rules.AddRule("+" + relative_dir, "Default rule for " + source)
|
| + source = 'top level' # Make the help string a little more meaningful.
|
| + rules.AddRule('+' + relative_dir, 'Default rule for ' + source)
|
| else:
|
| - raise Exception("Internal error: base directory is not at the beginning" +
|
| - " for\n %s and base dir\n %s" %
|
| + raise Exception('Internal error: base directory is not at the beginning' +
|
| + ' for\n %s and base dir\n %s' %
|
| (cur_dir, self.base_directory))
|
|
|
| # Last, apply the additional explicit rules.
|
| for (_, rule_str) in enumerate(includes):
|
| if not relative_dir:
|
| - rule_description = "the top level include_rules"
|
| + rule_description = 'the top level include_rules'
|
| else:
|
| rule_description = relative_dir + "'s include_rules"
|
| rules.AddRule(rule_str, rule_description)
|
| @@ -182,7 +197,7 @@ class DepsChecker(object):
|
|
|
| # Check the DEPS file in this directory.
|
| if self.verbose:
|
| - print "Applying rules from", dir_name
|
| + print 'Applying rules from', dir_name
|
| def FromImpl(_unused, _unused2):
|
| pass # NOP function so "From" doesn't fail.
|
|
|
| @@ -195,17 +210,17 @@ class DepsChecker(object):
|
|
|
| def Lookup(self, var_name):
|
| """Implements the Var syntax."""
|
| - if var_name in self._local_scope.get("vars", {}):
|
| - return self._local_scope["vars"][var_name]
|
| - raise Exception("Var is not defined: %s" % var_name)
|
| + if var_name in self._local_scope.get('vars', {}):
|
| + return self._local_scope['vars'][var_name]
|
| + raise Exception('Var is not defined: %s' % var_name)
|
|
|
| local_scope = {}
|
| global_scope = {
|
| - "File": FileImpl,
|
| - "From": FromImpl,
|
| - "Var": _VarImpl(local_scope).Lookup,
|
| + 'File': FileImpl,
|
| + 'From': FromImpl,
|
| + 'Var': _VarImpl(local_scope).Lookup,
|
| }
|
| - deps_file = os.path.join(dir_name, "DEPS")
|
| + deps_file = os.path.join(dir_name, 'DEPS')
|
|
|
| # The second conditional here is to disregard the
|
| # tools/checkdeps/DEPS file while running tests. This DEPS file
|
| @@ -219,7 +234,7 @@ class DepsChecker(object):
|
| not self._under_test or not os.path.split(dir_name)[1] == 'checkdeps'):
|
| execfile(deps_file, global_scope, local_scope)
|
| elif self.verbose:
|
| - print " No deps file found in", dir_name
|
| + print ' No deps file found in', dir_name
|
|
|
| # Even if a DEPS file does not exist we still invoke ApplyRules
|
| # to apply the implicit "allow" rule for the current directory
|
| @@ -229,38 +244,81 @@ class DepsChecker(object):
|
| return (self._ApplyRules(existing_rules, include_rules, norm_dir_name),
|
| skip_subdirs)
|
|
|
| + def _ApplyDirectoryRulesAndSkipSubdirs(self, parent_rules, dir_path):
|
| + """Given |parent_rules| and a subdirectory |dir_path| from the
|
| + directory that owns the |parent_rules|, add |dir_path|'s rules to
|
| + |self.directory_rules|, and add None entries for any of its
|
| + subdirectories that should be skipped.
|
| + """
|
| + directory_rules, excluded_subdirs = self._ApplyDirectoryRules(parent_rules,
|
| + dir_path)
|
| + self.directory_rules[NormalizePath(dir_path)] = directory_rules
|
| + for subdir in excluded_subdirs:
|
| + self.directory_rules[NormalizePath(
|
| + os.path.normpath(os.path.join(dir_path, subdir)))] = None
|
| +
|
| + def GetDirectoryRules(self, dir_path):
|
| + """Returns a Rules object to use for the given directory, or None
|
| + if the given directory should be skipped. This takes care of
|
| + first building rules for parent directories (up to
|
| + self.base_directory) if needed.
|
| +
|
| + Args:
|
| + dir_path: A real (non-normalized) path to the directory you want
|
| + rules for.
|
| + """
|
| + norm_dir_path = NormalizePath(dir_path)
|
| +
|
| + if not norm_dir_path.startswith(
|
| + NormalizePath(os.path.normpath(self.base_directory))):
|
| + dir_path = os.path.join(self.base_directory, dir_path)
|
| + norm_dir_path = NormalizePath(dir_path)
|
| +
|
| + parent_dir = os.path.dirname(dir_path)
|
| + parent_rules = None
|
| + if not norm_dir_path in self.directory_rules:
|
| + parent_rules = self.GetDirectoryRules(parent_dir)
|
| +
|
| + # We need to check for an entry for our dir_path again, in case we
|
| + # are at a path e.g. A/B/C where A/B/DEPS specifies the C
|
| + # subdirectory to be skipped; in this case, the invocation to
|
| + # GetDirectoryRules(parent_dir) has already filled in an entry for
|
| + # A/B/C.
|
| + if not norm_dir_path in self.directory_rules:
|
| + if not parent_rules:
|
| + # If the parent directory should be skipped, then the current
|
| + # directory should also be skipped.
|
| + self.directory_rules[norm_dir_path] = None
|
| + else:
|
| + self._ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path)
|
| + return self.directory_rules[norm_dir_path]
|
| +
|
| def CheckDirectory(self, start_dir):
|
| """Checks all relevant source files in the specified directory and
|
| its subdirectories for compliance with DEPS rules throughout the
|
| tree (starting at |self.base_directory|). |start_dir| must be a
|
| subdirectory of |self.base_directory|.
|
|
|
| - Returns an empty array on success. On failure, the array contains
|
| - strings that can be printed as human-readable error messages.
|
| + On completion, self.results_formatter has the results of
|
| + processing, and calling Report() will print a report of results.
|
| """
|
| - # TODO(joi): Make this work for start_dir != base_dir (I have a
|
| - # subsequent change in flight to do this).
|
| - base_rules = Rules()
|
| java = java_checker.JavaChecker(self.base_directory, self.verbose)
|
| cpp = cpp_checker.CppChecker(self.verbose)
|
| checkers = dict(
|
| (extension, checker)
|
| for checker in [java, cpp] for extension in checker.EXTENSIONS)
|
| - return self._CheckDirectoryImpl(base_rules, checkers, start_dir)
|
| + self._CheckDirectoryImpl(checkers, start_dir)
|
|
|
| - def _CheckDirectoryImpl(self, parent_rules, checkers, dir_name):
|
| - (rules, skip_subdirs) = self._ApplyDirectoryRules(parent_rules, dir_name)
|
| + def _CheckDirectoryImpl(self, checkers, dir_name):
|
| + rules = self.GetDirectoryRules(dir_name)
|
| if rules == None:
|
| - return []
|
| + return
|
|
|
| # Collect a list of all files and directories to check.
|
| files_to_check = []
|
| dirs_to_check = []
|
| - results = []
|
| contents = os.listdir(dir_name)
|
| for cur in contents:
|
| - if cur in skip_subdirs:
|
| - continue # Don't check children that DEPS has asked us to skip.
|
| full_name = os.path.join(dir_name, cur)
|
| if os.path.isdir(full_name):
|
| dirs_to_check.append(full_name)
|
| @@ -271,13 +329,12 @@ class DepsChecker(object):
|
| for cur in files_to_check:
|
| checker = checkers[os.path.splitext(cur)[1]]
|
| file_status = checker.CheckFile(rules, cur)
|
| - if file_status:
|
| - results.append("ERROR in " + cur + "\n" + file_status)
|
| + if file_status.HasViolations():
|
| + self.results_formatter.AddError(file_status)
|
|
|
| # Next recurse into the subdirectories.
|
| for cur in dirs_to_check:
|
| - results.extend(self._CheckDirectoryImpl(rules, checkers, cur))
|
| - return results
|
| + self._CheckDirectoryImpl(checkers, cur)
|
|
|
| def CheckAddedCppIncludes(self, added_includes):
|
| """This is used from PRESUBMIT.py to check new #include statements added in
|
| @@ -291,74 +348,32 @@ class DepsChecker(object):
|
| where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and
|
| rule_description is human-readable. Empty if no problems.
|
| """
|
| - # Map of normalized directory paths to rules to use for those
|
| - # directories, or None for directories that should be skipped.
|
| - directory_rules = {}
|
| -
|
| - def ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path):
|
| - rules_tuple = self._ApplyDirectoryRules(parent_rules, dir_path)
|
| - directory_rules[NormalizePath(dir_path)] = rules_tuple[0]
|
| - for subdir in rules_tuple[1]:
|
| - # We skip this one case for running tests.
|
| - directory_rules[NormalizePath(
|
| - os.path.normpath(os.path.join(dir_path, subdir)))] = None
|
| -
|
| - ApplyDirectoryRulesAndSkipSubdirs(Rules(), self.base_directory)
|
| -
|
| - def GetDirectoryRules(dir_path):
|
| - """Returns a Rules object to use for the given directory, or None
|
| - if the given directory should be skipped.
|
| - """
|
| - norm_dir_path = NormalizePath(dir_path)
|
| -
|
| - if not dir_path.startswith(
|
| - NormalizePath(os.path.normpath(self.base_directory))):
|
| - dir_path = os.path.join(self.base_directory, dir_path)
|
| - norm_dir_path = NormalizePath(dir_path)
|
| -
|
| - parent_dir = os.path.dirname(dir_path)
|
| - parent_rules = None
|
| - if not norm_dir_path in directory_rules:
|
| - parent_rules = GetDirectoryRules(parent_dir)
|
| -
|
| - # We need to check for an entry for our dir_path again, in case we
|
| - # are at a path e.g. A/B/C where A/B/DEPS specifies the C
|
| - # subdirectory to be skipped; in this case, the invocation to
|
| - # GetDirectoryRules(parent_dir) has already filled in an entry for
|
| - # A/B/C.
|
| - if not norm_dir_path in directory_rules:
|
| - if not parent_rules:
|
| - # If the parent directory should be skipped, then the current
|
| - # directory should also be skipped.
|
| - directory_rules[norm_dir_path] = None
|
| - else:
|
| - ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path)
|
| - return directory_rules[norm_dir_path]
|
| -
|
| cpp = cpp_checker.CppChecker(self.verbose)
|
| -
|
| problems = []
|
| for file_path, include_lines in added_includes:
|
| # TODO(joi): Make this cover Java as well.
|
| if not cpp.IsCppFile(file_path):
|
| pass
|
| - rules_for_file = GetDirectoryRules(os.path.dirname(file_path))
|
| + rules_for_file = self.GetDirectoryRules(os.path.dirname(file_path))
|
| if rules_for_file:
|
| for line in include_lines:
|
| - is_include, line_status, rule_type = cpp.CheckLine(
|
| - rules_for_file, line, True)
|
| - if rule_type != Rule.ALLOW:
|
| - problems.append((file_path, rule_type, line_status))
|
| + is_include, violation = cpp.CheckLine(rules_for_file, line, True)
|
| + if violation:
|
| + rule_type = violation.violated_rule.allow
|
| + if rule_type != Rule.ALLOW:
|
| + violation_text = NormalResultsFormatter.FormatViolation(
|
| + violation, self.verbose)
|
| + problems.append((file_path, rule_type, violation_text))
|
| return problems
|
|
|
| def _AddGitSourceDirectories(self):
|
| """Adds any directories containing sources managed by git to
|
| self.git_source_directories.
|
| """
|
| - if not os.path.exists(os.path.join(self.base_directory, ".git")):
|
| + if not os.path.exists(os.path.join(self.base_directory, '.git')):
|
| return
|
|
|
| - popen_out = os.popen("cd %s && git ls-files --full-name ." %
|
| + popen_out = os.popen('cd %s && git ls-files --full-name .' %
|
| subprocess.list2cmdline([self.base_directory]))
|
| for line in popen_out.readlines():
|
| dir_name = os.path.join(self.base_directory, os.path.dirname(line))
|
| @@ -378,8 +393,7 @@ def PrintUsage():
|
| of the script in "<root>/tools/checkdeps".
|
|
|
| tocheck Specifies the directory, relative to root, to check. This defaults
|
| - to "." so it checks everything. Only one level deep is currently
|
| - supported, so you can say "chrome" but not "chrome/browser".
|
| + to "." so it checks everything.
|
|
|
| Examples:
|
| python checkdeps.py
|
| @@ -388,15 +402,18 @@ Examples:
|
|
|
| def main():
|
| option_parser = optparse.OptionParser()
|
| - option_parser.add_option("", "--root", default="", dest="base_directory",
|
| + option_parser.add_option('', '--root', default='', dest='base_directory',
|
| help='Specifies the repository root. This defaults '
|
| 'to "../../.." relative to the script file, which '
|
| 'will normally be the repository root.')
|
| - option_parser.add_option("-v", "--verbose", action="store_true",
|
| - default=False, help="Print debug logging")
|
| + option_parser.add_option('', '--temprules', action='store_true',
|
| + default=False, help='Print rules to temporarily '
|
| + 'allow files that fail dependency checking.')
|
| + option_parser.add_option('-v', '--verbose', action='store_true',
|
| + default=False, help='Print debug logging')
|
| options, args = option_parser.parse_args()
|
|
|
| - deps_checker = DepsChecker(options.base_directory, options.verbose)
|
| + deps_checker = DepsChecker(options.base_directory, verbose=options.verbose)
|
|
|
| # Figure out which directory we have to check.
|
| start_dir = deps_checker.base_directory
|
| @@ -410,17 +427,13 @@ def main():
|
| PrintUsage()
|
| return 1
|
|
|
| - print "Using base directory:", deps_checker.base_directory
|
| - print "Checking:", start_dir
|
| + print 'Using base directory:', deps_checker.base_directory
|
| + print 'Checking:', start_dir
|
|
|
| - results = deps_checker.CheckDirectory(start_dir)
|
| - if results:
|
| - for result in results:
|
| - print result
|
| - print "\nFAILED\n"
|
| - return 1
|
| - print "\nSUCCESS\n"
|
| - return 0
|
| + if options.temprules:
|
| + deps_checker.results_formatter = TemporaryRulesFormatter()
|
| + deps_checker.CheckDirectory(start_dir)
|
| + return deps_checker.Report()
|
|
|
|
|
| if '__main__' == __name__:
|
|
|