mirror of
https://github.com/thead-yocto-mirror/skia
synced 2026-06-21 08:52:36 +02:00
290 lines
11 KiB
Python
Executable File
290 lines
11 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Copyright 2012 The Chromium Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""Makes sure that files include headers from allowed directories.
|
|
|
|
Checks DEPS files in the source tree for rules, and applies those rules to
|
|
"#include" and "import" directives in the .cpp, .java, and .proto source files.
|
|
Any source file including something not permitted by the DEPS files will fail.
|
|
|
|
See README.md for a detailed description of the DEPS format.
|
|
"""
|
|
|
|
import os
|
|
import optparse
|
|
import re
|
|
import sys
|
|
|
|
import proto_checker
|
|
import cpp_checker
|
|
import java_checker
|
|
import results
|
|
|
|
from builddeps import DepsBuilder
|
|
from rules import Rule, Rules
|
|
|
|
|
|
def _IsTestFile(filename):
|
|
"""Does a rudimentary check to try to skip test files; this could be
|
|
improved but is good enough for now.
|
|
"""
|
|
return re.match('(test|mock|dummy)_.*|.*_[a-z]*test\.(cc|mm|java)', filename)
|
|
|
|
|
|
class DepsChecker(DepsBuilder):
|
|
"""Parses include_rules from DEPS files and verifies files in the
|
|
source tree against them.
|
|
"""
|
|
|
|
def __init__(self,
|
|
base_directory=None,
|
|
extra_repos=[],
|
|
verbose=False,
|
|
being_tested=False,
|
|
ignore_temp_rules=False,
|
|
skip_tests=False,
|
|
resolve_dotdot=True):
|
|
"""Creates a new DepsChecker.
|
|
|
|
Args:
|
|
base_directory: OS-compatible path to root of checkout, e.g. C:\chr\src.
|
|
verbose: Set to true for debug output.
|
|
being_tested: Set to true to ignore the DEPS file at tools/checkdeps/DEPS.
|
|
ignore_temp_rules: Ignore rules that start with Rule.TEMP_ALLOW ("!").
|
|
"""
|
|
DepsBuilder.__init__(
|
|
self, base_directory, extra_repos, verbose, being_tested,
|
|
ignore_temp_rules)
|
|
|
|
self._skip_tests = skip_tests
|
|
self._resolve_dotdot = resolve_dotdot
|
|
self.results_formatter = results.NormalResultsFormatter(verbose)
|
|
|
|
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 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|.
|
|
|
|
On completion, self.results_formatter has the results of
|
|
processing, and calling Report() will print a report of results.
|
|
"""
|
|
java = java_checker.JavaChecker(self.base_directory, self.verbose)
|
|
cpp = cpp_checker.CppChecker(
|
|
self.verbose, self._resolve_dotdot, self.base_directory)
|
|
proto = proto_checker.ProtoChecker(
|
|
self.verbose, self._resolve_dotdot, self.base_directory)
|
|
checkers = dict(
|
|
(extension, checker)
|
|
for checker in [java, cpp, proto] for extension in checker.EXTENSIONS)
|
|
|
|
for rules, file_paths in self.GetAllRulesAndFiles(start_dir):
|
|
for full_name in file_paths:
|
|
if self._skip_tests and _IsTestFile(os.path.basename(full_name)):
|
|
continue
|
|
file_extension = os.path.splitext(full_name)[1]
|
|
if not file_extension in checkers:
|
|
continue
|
|
checker = checkers[file_extension]
|
|
file_status = checker.CheckFile(rules, full_name)
|
|
if file_status.HasViolations():
|
|
self.results_formatter.AddError(file_status)
|
|
|
|
def CheckIncludesAndImports(self, added_lines, checker):
|
|
"""Check new import/#include statements added in the change
|
|
being presubmit checked.
|
|
|
|
Args:
|
|
added_lines: ((file_path, (changed_line, changed_line, ...), ...)
|
|
checker: CppChecker/JavaChecker/ProtoChecker checker instance
|
|
|
|
Return:
|
|
A list of tuples, (bad_file_path, rule_type, rule_description)
|
|
where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and
|
|
rule_description is human-readable. Empty if no problems.
|
|
"""
|
|
problems = []
|
|
for file_path, changed_lines in added_lines:
|
|
if not checker.ShouldCheck(file_path):
|
|
continue
|
|
rules_for_file = self.GetDirectoryRules(os.path.dirname(file_path))
|
|
if not rules_for_file:
|
|
continue
|
|
for line in changed_lines:
|
|
is_include, violation = checker.CheckLine(
|
|
rules_for_file, line, file_path, True)
|
|
if not violation:
|
|
continue
|
|
rule_type = violation.violated_rule.allow
|
|
if rule_type == Rule.ALLOW:
|
|
continue
|
|
violation_text = results.NormalResultsFormatter.FormatViolation(
|
|
violation, self.verbose)
|
|
problems.append((file_path, rule_type, violation_text))
|
|
return problems
|
|
|
|
def CheckAddedCppIncludes(self, added_includes):
|
|
"""This is used from PRESUBMIT.py to check new #include statements added in
|
|
the change being presubmit checked.
|
|
|
|
Args:
|
|
added_includes: ((file_path, (include_line, include_line, ...), ...)
|
|
|
|
Return:
|
|
A list of tuples, (bad_file_path, rule_type, rule_description)
|
|
where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and
|
|
rule_description is human-readable. Empty if no problems.
|
|
"""
|
|
return self.CheckIncludesAndImports(
|
|
added_includes, cpp_checker.CppChecker(self.verbose))
|
|
|
|
def CheckAddedJavaImports(self, added_imports, allow_multiple_definitions=None):
|
|
"""This is used from PRESUBMIT.py to check new import statements added in
|
|
the change being presubmit checked.
|
|
|
|
Args:
|
|
added_imports: ((file_path, (import_line, import_line, ...), ...)
|
|
allow_multiple_definitions: [file_name, file_name, ...]. List of java file
|
|
names allowing multipe definition in presubmit check.
|
|
|
|
Return:
|
|
A list of tuples, (bad_file_path, rule_type, rule_description)
|
|
where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and
|
|
rule_description is human-readable. Empty if no problems.
|
|
"""
|
|
return self.CheckIncludesAndImports(
|
|
added_imports,
|
|
java_checker.JavaChecker(self.base_directory, self.verbose,
|
|
added_imports, allow_multiple_definitions))
|
|
|
|
def CheckAddedProtoImports(self, added_imports):
|
|
"""This is used from PRESUBMIT.py to check new #import statements added in
|
|
the change being presubmit checked.
|
|
|
|
Args:
|
|
added_imports : ((file_path, (import_line, import_line, ...), ...)
|
|
|
|
Return:
|
|
A list of tuples, (bad_file_path, rule_type, rule_description)
|
|
where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and
|
|
rule_description is human-readable. Empty if no problems.
|
|
"""
|
|
return self.CheckIncludesAndImports(
|
|
added_imports, proto_checker.ProtoChecker(
|
|
verbose=self.verbose, root_dir=self.base_directory))
|
|
|
|
def PrintUsage():
|
|
print """Usage: python checkdeps.py [--root <root>] [tocheck]
|
|
|
|
--root ROOT Specifies the repository root. This defaults to "../../.."
|
|
relative to the script file. This will be correct given the
|
|
normal location of the script in "<root>/tools/checkdeps".
|
|
|
|
--(others) There are a few lesser-used options; run with --help to show them.
|
|
|
|
tocheck Specifies the directory, relative to root, to check. This defaults
|
|
to "." so it checks everything.
|
|
|
|
Examples:
|
|
python checkdeps.py
|
|
python checkdeps.py --root c:\\source chrome"""
|
|
|
|
|
|
def main():
|
|
option_parser = optparse.OptionParser()
|
|
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(
|
|
'', '--extra-repos',
|
|
action='append', dest='extra_repos', default=[],
|
|
help='Specifies extra repositories relative to root repository.')
|
|
option_parser.add_option(
|
|
'', '--ignore-temp-rules',
|
|
action='store_true', dest='ignore_temp_rules', default=False,
|
|
help='Ignore !-prefixed (temporary) rules.')
|
|
option_parser.add_option(
|
|
'', '--generate-temp-rules',
|
|
action='store_true', dest='generate_temp_rules', default=False,
|
|
help='Print rules to temporarily allow files that fail '
|
|
'dependency checking.')
|
|
option_parser.add_option(
|
|
'', '--count-violations',
|
|
action='store_true', dest='count_violations', default=False,
|
|
help='Count #includes in violation of intended rules.')
|
|
option_parser.add_option(
|
|
'', '--skip-tests',
|
|
action='store_true', dest='skip_tests', default=False,
|
|
help='Skip checking test files (best effort).')
|
|
option_parser.add_option(
|
|
'-v', '--verbose',
|
|
action='store_true', default=False,
|
|
help='Print debug logging')
|
|
option_parser.add_option(
|
|
'', '--json',
|
|
help='Path to JSON output file')
|
|
option_parser.add_option(
|
|
'', '--no-resolve-dotdot',
|
|
action='store_false', dest='resolve_dotdot', default=True,
|
|
help='resolve leading ../ in include directive paths relative '
|
|
'to the file perfoming the inclusion.')
|
|
|
|
options, args = option_parser.parse_args()
|
|
|
|
deps_checker = DepsChecker(options.base_directory,
|
|
extra_repos=options.extra_repos,
|
|
verbose=options.verbose,
|
|
ignore_temp_rules=options.ignore_temp_rules,
|
|
skip_tests=options.skip_tests,
|
|
resolve_dotdot=options.resolve_dotdot)
|
|
base_directory = deps_checker.base_directory # Default if needed, normalized
|
|
|
|
# Figure out which directory we have to check.
|
|
start_dir = base_directory
|
|
if len(args) == 1:
|
|
# Directory specified. Start here. It's supposed to be relative to the
|
|
# base directory.
|
|
start_dir = os.path.abspath(os.path.join(base_directory, args[0]))
|
|
elif len(args) >= 2 or (options.generate_temp_rules and
|
|
options.count_violations):
|
|
# More than one argument, or incompatible flags, we don't handle this.
|
|
PrintUsage()
|
|
return 1
|
|
|
|
if not start_dir.startswith(deps_checker.base_directory):
|
|
print 'Directory to check must be a subdirectory of the base directory,'
|
|
print 'but %s is not a subdirectory of %s' % (start_dir, base_directory)
|
|
return 1
|
|
|
|
print 'Using base directory:', base_directory
|
|
print 'Checking:', start_dir
|
|
|
|
if options.generate_temp_rules:
|
|
deps_checker.results_formatter = results.TemporaryRulesFormatter()
|
|
elif options.count_violations:
|
|
deps_checker.results_formatter = results.CountViolationsFormatter()
|
|
|
|
if options.json:
|
|
deps_checker.results_formatter = results.JSONResultsFormatter(
|
|
options.json, deps_checker.results_formatter)
|
|
|
|
deps_checker.CheckDirectory(start_dir)
|
|
return deps_checker.Report()
|
|
|
|
|
|
if '__main__' == __name__:
|
|
sys.exit(main())
|