Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / tools / 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 and .java source files.
10 Any source file including something not permitted by the DEPS files will fail.
11
12 See builddeps.py 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 cpp_checker
21 import java_checker
22 import results
23
24 from builddeps import DepsBuilder
25 from rules import Rule, Rules
26
27
28 def _IsTestFile(filename):
29   """Does a rudimentary check to try to skip test files; this could be
30   improved but is good enough for now.
31   """
32   return re.match('(test|mock|dummy)_.*|.*_[a-z]*test\.(cc|mm|java)', filename)
33
34
35 class DepsChecker(DepsBuilder):
36   """Parses include_rules from DEPS files and verifies files in the
37   source tree against them.
38   """
39
40   def __init__(self,
41                base_directory=None,
42                verbose=False,
43                being_tested=False,
44                ignore_temp_rules=False,
45                skip_tests=False):
46     """Creates a new DepsChecker.
47
48     Args:
49       base_directory: OS-compatible path to root of checkout, e.g. C:\chr\src.
50       verbose: Set to true for debug output.
51       being_tested: Set to true to ignore the DEPS file at tools/checkdeps/DEPS.
52       ignore_temp_rules: Ignore rules that start with Rule.TEMP_ALLOW ("!").
53     """
54     DepsBuilder.__init__(
55         self, base_directory, verbose, being_tested, ignore_temp_rules)
56
57     self._skip_tests = skip_tests
58     self.results_formatter = results.NormalResultsFormatter(verbose)
59
60   def Report(self):
61     """Prints a report of results, and returns an exit code for the process."""
62     if self.results_formatter.GetResults():
63       self.results_formatter.PrintResults()
64       return 1
65     print '\nSUCCESS\n'
66     return 0
67
68   def CheckDirectory(self, start_dir):
69     """Checks all relevant source files in the specified directory and
70     its subdirectories for compliance with DEPS rules throughout the
71     tree (starting at |self.base_directory|).  |start_dir| must be a
72     subdirectory of |self.base_directory|.
73
74     On completion, self.results_formatter has the results of
75     processing, and calling Report() will print a report of results.
76     """
77     java = java_checker.JavaChecker(self.base_directory, self.verbose)
78     cpp = cpp_checker.CppChecker(self.verbose)
79     checkers = dict(
80         (extension, checker)
81         for checker in [java, cpp] for extension in checker.EXTENSIONS)
82     self._CheckDirectoryImpl(checkers, start_dir)
83
84   def _CheckDirectoryImpl(self, checkers, dir_name):
85     rules = self.GetDirectoryRules(dir_name)
86     if rules is None:
87       return
88
89     # Collect a list of all files and directories to check.
90     files_to_check = []
91     dirs_to_check = []
92     contents = sorted(os.listdir(dir_name))
93     for cur in contents:
94       full_name = os.path.join(dir_name, cur)
95       if os.path.isdir(full_name):
96         dirs_to_check.append(full_name)
97       elif os.path.splitext(full_name)[1] in checkers:
98         if not self._skip_tests or not _IsTestFile(cur):
99           files_to_check.append(full_name)
100
101     # First check all files in this directory.
102     for cur in files_to_check:
103       checker = checkers[os.path.splitext(cur)[1]]
104       file_status = checker.CheckFile(rules, cur)
105       if file_status.HasViolations():
106         self.results_formatter.AddError(file_status)
107
108     # Next recurse into the subdirectories.
109     for cur in dirs_to_check:
110       self._CheckDirectoryImpl(checkers, cur)
111
112   def CheckAddedCppIncludes(self, added_includes):
113     """This is used from PRESUBMIT.py to check new #include statements added in
114     the change being presubmit checked.
115
116     Args:
117       added_includes: ((file_path, (include_line, include_line, ...), ...)
118
119     Return:
120       A list of tuples, (bad_file_path, rule_type, rule_description)
121       where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and
122       rule_description is human-readable. Empty if no problems.
123     """
124     cpp = cpp_checker.CppChecker(self.verbose)
125     problems = []
126     for file_path, include_lines in added_includes:
127       if not cpp.IsCppFile(file_path):
128         continue
129       rules_for_file = self.GetDirectoryRules(os.path.dirname(file_path))
130       if not rules_for_file:
131         continue
132       for line in include_lines:
133         is_include, violation = cpp.CheckLine(
134             rules_for_file, line, file_path, True)
135         if not violation:
136           continue
137         rule_type = violation.violated_rule.allow
138         if rule_type == Rule.ALLOW:
139           continue
140         violation_text = results.NormalResultsFormatter.FormatViolation(
141             violation, self.verbose)
142         problems.append((file_path, rule_type, violation_text))
143     return problems
144
145
146 def PrintUsage():
147   print """Usage: python checkdeps.py [--root <root>] [tocheck]
148
149   --root ROOT Specifies the repository root. This defaults to "../../.."
150               relative to the script file. This will be correct given the
151               normal location of the script in "<root>/tools/checkdeps".
152
153   --(others)  There are a few lesser-used options; run with --help to show them.
154
155   tocheck  Specifies the directory, relative to root, to check. This defaults
156            to "." so it checks everything.
157
158 Examples:
159   python checkdeps.py
160   python checkdeps.py --root c:\\source chrome"""
161
162
163 def main():
164   option_parser = optparse.OptionParser()
165   option_parser.add_option(
166       '', '--root',
167       default='', dest='base_directory',
168       help='Specifies the repository root. This defaults '
169            'to "../../.." relative to the script file, which '
170            'will normally be the repository root.')
171   option_parser.add_option(
172       '', '--ignore-temp-rules',
173       action='store_true', dest='ignore_temp_rules', default=False,
174       help='Ignore !-prefixed (temporary) rules.')
175   option_parser.add_option(
176       '', '--generate-temp-rules',
177       action='store_true', dest='generate_temp_rules', default=False,
178       help='Print rules to temporarily allow files that fail '
179            'dependency checking.')
180   option_parser.add_option(
181       '', '--count-violations',
182       action='store_true', dest='count_violations', default=False,
183       help='Count #includes in violation of intended rules.')
184   option_parser.add_option(
185       '', '--skip-tests',
186       action='store_true', dest='skip_tests', default=False,
187       help='Skip checking test files (best effort).')
188   option_parser.add_option(
189       '-v', '--verbose',
190       action='store_true', default=False,
191       help='Print debug logging')
192   option_parser.add_option(
193       '', '--json',
194       help='Path to JSON output file')
195   options, args = option_parser.parse_args()
196
197   deps_checker = DepsChecker(options.base_directory,
198                              verbose=options.verbose,
199                              ignore_temp_rules=options.ignore_temp_rules,
200                              skip_tests=options.skip_tests)
201   base_directory = deps_checker.base_directory  # Default if needed, normalized
202
203   # Figure out which directory we have to check.
204   start_dir = base_directory
205   if len(args) == 1:
206     # Directory specified. Start here. It's supposed to be relative to the
207     # base directory.
208     start_dir = os.path.abspath(os.path.join(base_directory, args[0]))
209   elif len(args) >= 2 or (options.generate_temp_rules and
210                           options.count_violations):
211     # More than one argument, or incompatible flags, we don't handle this.
212     PrintUsage()
213     return 1
214
215   if not start_dir.startswith(deps_checker.base_directory):
216     print 'Directory to check must be a subdirectory of the base directory,'
217     print 'but %s is not a subdirectory of %s' % (start_dir, base_directory)
218     return 1
219
220   print 'Using base directory:', base_directory
221   print 'Checking:', start_dir
222
223   if options.generate_temp_rules:
224     deps_checker.results_formatter = results.TemporaryRulesFormatter()
225   elif options.count_violations:
226     deps_checker.results_formatter = results.CountViolationsFormatter()
227
228   if options.json:
229     deps_checker.results_formatter = results.JSONResultsFormatter(
230         options.json, deps_checker.results_formatter)
231
232   deps_checker.CheckDirectory(start_dir)
233   return deps_checker.Report()
234
235
236 if '__main__' == __name__:
237   sys.exit(main())