fixup! Upload upstream chromium 76.0.3809.146
[platform/framework/web/chromium-efl.git] / buildtools / checkdeps / checkdeps.py
1 #!/usr/bin/env python
2 # Copyright 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Makes sure that files include headers from allowed directories.
7
8 Checks DEPS files in the source tree for rules, and applies those rules to
9 "#include" and "import" directives in the .cpp, .java, and .proto source files.
10 Any source file including something not permitted by the DEPS files will fail.
11
12 See README.md for a detailed description of the DEPS format.
13 """
14
15 import os
16 import optparse
17 import re
18 import sys
19
20 import proto_checker
21 import cpp_checker
22 import java_checker
23 import results
24
25 from builddeps import DepsBuilder
26 from rules import Rule, Rules
27
28
29 def _IsTestFile(filename):
30   """Does a rudimentary check to try to skip test files; this could be
31   improved but is good enough for now.
32   """
33   return re.match('(test|mock|dummy)_.*|.*_[a-z]*test\.(cc|mm|java)', filename)
34
35
36 class DepsChecker(DepsBuilder):
37   """Parses include_rules from DEPS files and verifies files in the
38   source tree against them.
39   """
40
41   def __init__(self,
42                base_directory=None,
43                extra_repos=[],
44                verbose=False,
45                being_tested=False,
46                ignore_temp_rules=False,
47                skip_tests=False,
48                resolve_dotdot=True):
49     """Creates a new DepsChecker.
50
51     Args:
52       base_directory: OS-compatible path to root of checkout, e.g. C:\chr\src.
53       verbose: Set to true for debug output.
54       being_tested: Set to true to ignore the DEPS file at tools/checkdeps/DEPS.
55       ignore_temp_rules: Ignore rules that start with Rule.TEMP_ALLOW ("!").
56     """
57     DepsBuilder.__init__(
58         self, base_directory, extra_repos, verbose, being_tested,
59         ignore_temp_rules)
60
61     self._skip_tests = skip_tests
62     self._resolve_dotdot = resolve_dotdot
63     self.results_formatter = results.NormalResultsFormatter(verbose)
64
65   def Report(self):
66     """Prints a report of results, and returns an exit code for the process."""
67     if self.results_formatter.GetResults():
68       self.results_formatter.PrintResults()
69       return 1
70     print '\nSUCCESS\n'
71     return 0
72
73   def CheckDirectory(self, start_dir):
74     """Checks all relevant source files in the specified directory and
75     its subdirectories for compliance with DEPS rules throughout the
76     tree (starting at |self.base_directory|).  |start_dir| must be a
77     subdirectory of |self.base_directory|.
78
79     On completion, self.results_formatter has the results of
80     processing, and calling Report() will print a report of results.
81     """
82     java = java_checker.JavaChecker(self.base_directory, self.verbose)
83     cpp = cpp_checker.CppChecker(
84         self.verbose, self._resolve_dotdot, self.base_directory)
85     proto = proto_checker.ProtoChecker(
86         self.verbose, self._resolve_dotdot, self.base_directory)
87     checkers = dict(
88         (extension, checker)
89         for checker in [java, cpp, proto] for extension in checker.EXTENSIONS)
90
91     for rules, file_paths in self.GetAllRulesAndFiles(start_dir):
92       for full_name in file_paths:
93         if self._skip_tests and _IsTestFile(os.path.basename(full_name)):
94           continue
95         file_extension = os.path.splitext(full_name)[1]
96         if not file_extension in checkers:
97           continue
98         checker = checkers[file_extension]
99         file_status = checker.CheckFile(rules, full_name)
100         if file_status.HasViolations():
101           self.results_formatter.AddError(file_status)
102
103   def CheckIncludesAndImports(self, added_lines, checker):
104     """Check new import/#include statements added in the change
105     being presubmit checked.
106
107     Args:
108       added_lines: ((file_path, (changed_line, changed_line, ...), ...)
109       checker: CppChecker/JavaChecker/ProtoChecker checker instance
110
111     Return:
112       A list of tuples, (bad_file_path, rule_type, rule_description)
113       where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and
114       rule_description is human-readable. Empty if no problems.
115     """
116     problems = []
117     for file_path, changed_lines in added_lines:
118       if not checker.ShouldCheck(file_path):
119         continue
120       rules_for_file = self.GetDirectoryRules(os.path.dirname(file_path))
121       if not rules_for_file:
122         continue
123       for line in changed_lines:
124         is_include, violation = checker.CheckLine(
125             rules_for_file, line, file_path, True)
126         if not violation:
127           continue
128         rule_type = violation.violated_rule.allow
129         if rule_type == Rule.ALLOW:
130           continue
131         violation_text = results.NormalResultsFormatter.FormatViolation(
132             violation, self.verbose)
133         problems.append((file_path, rule_type, violation_text))
134     return problems
135
136   def CheckAddedCppIncludes(self, added_includes):
137     """This is used from PRESUBMIT.py to check new #include statements added in
138     the change being presubmit checked.
139
140     Args:
141       added_includes: ((file_path, (include_line, include_line, ...), ...)
142
143     Return:
144       A list of tuples, (bad_file_path, rule_type, rule_description)
145       where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and
146       rule_description is human-readable. Empty if no problems.
147     """
148     return self.CheckIncludesAndImports(
149         added_includes, cpp_checker.CppChecker(self.verbose))
150
151   def CheckAddedJavaImports(self, added_imports, allow_multiple_definitions=None):
152     """This is used from PRESUBMIT.py to check new import statements added in
153     the change being presubmit checked.
154
155     Args:
156       added_imports: ((file_path, (import_line, import_line, ...), ...)
157       allow_multiple_definitions: [file_name, file_name, ...]. List of java file
158                                   names allowing multipe definition in presubmit check.
159
160     Return:
161       A list of tuples, (bad_file_path, rule_type, rule_description)
162       where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and
163       rule_description is human-readable. Empty if no problems.
164     """
165     return self.CheckIncludesAndImports(
166         added_imports,
167         java_checker.JavaChecker(self.base_directory, self.verbose,
168                                  added_imports, allow_multiple_definitions))
169
170   def CheckAddedProtoImports(self, added_imports):
171     """This is used from PRESUBMIT.py to check new #import statements added in
172     the change being presubmit checked.
173
174     Args:
175       added_imports : ((file_path, (import_line, import_line, ...), ...)
176
177     Return:
178       A list of tuples, (bad_file_path, rule_type, rule_description)
179       where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and
180       rule_description is human-readable. Empty if no problems.
181     """
182     return self.CheckIncludesAndImports(
183         added_imports, proto_checker.ProtoChecker(
184             verbose=self.verbose, root_dir=self.base_directory))
185
186 def PrintUsage():
187   print """Usage: python checkdeps.py [--root <root>] [tocheck]
188
189   --root ROOT Specifies the repository root. This defaults to "../../.."
190               relative to the script file. This will be correct given the
191               normal location of the script in "<root>/tools/checkdeps".
192
193   --(others)  There are a few lesser-used options; run with --help to show them.
194
195   tocheck  Specifies the directory, relative to root, to check. This defaults
196            to "." so it checks everything.
197
198 Examples:
199   python checkdeps.py
200   python checkdeps.py --root c:\\source chrome"""
201
202
203 def main():
204   option_parser = optparse.OptionParser()
205   option_parser.add_option(
206       '', '--root',
207       default='', dest='base_directory',
208       help='Specifies the repository root. This defaults '
209            'to "../../.." relative to the script file, which '
210            'will normally be the repository root.')
211   option_parser.add_option(
212       '', '--extra-repos',
213       action='append', dest='extra_repos', default=[],
214       help='Specifies extra repositories relative to root repository.')
215   option_parser.add_option(
216       '', '--ignore-temp-rules',
217       action='store_true', dest='ignore_temp_rules', default=False,
218       help='Ignore !-prefixed (temporary) rules.')
219   option_parser.add_option(
220       '', '--generate-temp-rules',
221       action='store_true', dest='generate_temp_rules', default=False,
222       help='Print rules to temporarily allow files that fail '
223            'dependency checking.')
224   option_parser.add_option(
225       '', '--count-violations',
226       action='store_true', dest='count_violations', default=False,
227       help='Count #includes in violation of intended rules.')
228   option_parser.add_option(
229       '', '--skip-tests',
230       action='store_true', dest='skip_tests', default=False,
231       help='Skip checking test files (best effort).')
232   option_parser.add_option(
233       '-v', '--verbose',
234       action='store_true', default=False,
235       help='Print debug logging')
236   option_parser.add_option(
237       '', '--json',
238       help='Path to JSON output file')
239   option_parser.add_option(
240       '', '--no-resolve-dotdot',
241       action='store_false', dest='resolve_dotdot', default=True,
242       help='resolve leading ../ in include directive paths relative '
243            'to the file perfoming the inclusion.')
244
245   options, args = option_parser.parse_args()
246
247   deps_checker = DepsChecker(options.base_directory,
248                              extra_repos=options.extra_repos,
249                              verbose=options.verbose,
250                              ignore_temp_rules=options.ignore_temp_rules,
251                              skip_tests=options.skip_tests,
252                              resolve_dotdot=options.resolve_dotdot)
253   base_directory = deps_checker.base_directory  # Default if needed, normalized
254
255   # Figure out which directory we have to check.
256   start_dir = base_directory
257   if len(args) == 1:
258     # Directory specified. Start here. It's supposed to be relative to the
259     # base directory.
260     start_dir = os.path.abspath(os.path.join(base_directory, args[0]))
261   elif len(args) >= 2 or (options.generate_temp_rules and
262                           options.count_violations):
263     # More than one argument, or incompatible flags, we don't handle this.
264     PrintUsage()
265     return 1
266
267   if not start_dir.startswith(deps_checker.base_directory):
268     print 'Directory to check must be a subdirectory of the base directory,'
269     print 'but %s is not a subdirectory of %s' % (start_dir, base_directory)
270     return 1
271
272   print 'Using base directory:', base_directory
273   print 'Checking:', start_dir
274
275   if options.generate_temp_rules:
276     deps_checker.results_formatter = results.TemporaryRulesFormatter()
277   elif options.count_violations:
278     deps_checker.results_formatter = results.CountViolationsFormatter()
279
280   if options.json:
281     deps_checker.results_formatter = results.JSONResultsFormatter(
282         options.json, deps_checker.results_formatter)
283
284   deps_checker.CheckDirectory(start_dir)
285   return deps_checker.Report()
286
287
288 if '__main__' == __name__:
289   sys.exit(main())