Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / chromite / cros / commands / cros_lint.py
1 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Run lint checks on the specified files."""
6
7 import os
8 import sys
9
10 from chromite.cbuildbot import constants
11 from chromite.lib import cros_build_lib
12 from chromite.lib import git
13
14 from chromite import cros
15
16
17 PYTHON_EXTENSIONS = frozenset(['.py'])
18
19 # Note these are defined to keep in line with cpplint.py. Technically, we could
20 # include additional ones, but cpplint.py would just filter them out.
21 CPP_EXTENSIONS = frozenset(['.cc', '.cpp', '.h'])
22
23
24 def _GetProjectPath(path):
25   """Find the absolute path of the git checkout that contains |path|."""
26   if git.FindRepoCheckoutRoot(path):
27     manifest = git.ManifestCheckout.Cached(path)
28     return manifest.FindCheckoutFromPath(path).GetPath(absolute=True)
29   else:
30     # Maybe they're running on a file outside of a checkout.
31     # e.g. cros lint ~/foo.py /tmp/test.py
32     return os.path.dirname(path)
33
34
35 def _GetPylintGroups(paths):
36   """Return a dictionary mapping pylintrc files to lists of paths."""
37   groups = {}
38   for path in paths:
39     if path.endswith('.py'):
40       path = os.path.realpath(path)
41       project_path = _GetProjectPath(path)
42       parent = os.path.dirname(path)
43       while project_path and parent.startswith(project_path):
44         pylintrc = os.path.join(parent, 'pylintrc')
45         if os.path.isfile(pylintrc):
46           break
47         parent = os.path.dirname(parent)
48       if project_path is None or not os.path.isfile(pylintrc):
49         pylintrc = os.path.join(constants.SOURCE_ROOT, 'chromite', 'pylintrc')
50       groups.setdefault(pylintrc, []).append(path)
51   return groups
52
53
54 def _GetPythonPath(paths):
55   """Return the set of Python library paths to use."""
56   return sys.path + [
57     # Add the Portage installation inside the chroot to the Python path.
58     # This ensures that scripts that need to import portage can do so.
59     os.path.join(constants.SOURCE_ROOT, 'chroot', 'usr', 'lib', 'portage',
60                  'pym'),
61
62     # Scripts outside of chromite expect the scripts in src/scripts/lib to
63     # be importable.
64     os.path.join(constants.CROSUTILS_DIR, 'lib'),
65
66     # Allow platform projects to be imported by name (e.g. crostestutils).
67     os.path.join(constants.SOURCE_ROOT, 'src', 'platform'),
68
69     # Ideally we'd modify meta_path in pylint to handle our virtual chromite
70     # module, but that's not possible currently.  We'll have to deal with
71     # that at some point if we want `cros lint` to work when the dir is not
72     # named 'chromite'.
73     constants.SOURCE_ROOT,
74
75     # Also allow scripts to import from their current directory.
76   ] + list(set(os.path.dirname(x) for x in paths))
77
78
79 def _CpplintFiles(files, debug):
80   """Returns true if cpplint ran successfully on all files."""
81   cmd = ['cpplint.py'] + files
82   res = cros_build_lib.RunCommand(cmd,
83                                   error_code_ok=True,
84                                   print_cmd=debug)
85   return res.returncode != 0
86
87
88 def _PylintFiles(files, debug):
89   """Returns true if pylint ran successfully on all files."""
90   errors = False
91   for pylintrc, paths in sorted(_GetPylintGroups(files).items()):
92     paths = sorted(list(set([os.path.realpath(x) for x in paths])))
93     cmd = ['pylint', '--rcfile=%s' % pylintrc] + paths
94     extra_env = {'PYTHONPATH': ':'.join(_GetPythonPath(paths))}
95     res = cros_build_lib.RunCommand(cmd, extra_env=extra_env,
96                                     error_code_ok=True,
97                                     print_cmd=debug)
98     if res.returncode != 0:
99       errors = True
100
101   return errors
102
103
104 def _BreakoutFilesByLinter(files):
105   """Maps a linter method to the list of files to lint."""
106   map_to_return = {}
107   for f in files:
108     extension = os.path.splitext(f)[1]
109     if extension in PYTHON_EXTENSIONS:
110       pylint_list = map_to_return.setdefault(_PylintFiles, [])
111       pylint_list.append(f)
112     elif extension in CPP_EXTENSIONS:
113       cpplint_list = map_to_return.setdefault(_CpplintFiles, [])
114       cpplint_list.append(f)
115
116   return map_to_return
117
118
119 @cros.CommandDecorator('lint')
120 class LintCommand(cros.CrosCommand):
121   """Run lint checks on the specified files."""
122
123   EPILOG = """
124 Right now, only supports cpplint and pylint. We may also in the future
125 run other checks (e.g. pyflakes, etc.)
126 """
127
128   @classmethod
129   def AddParser(cls, parser):
130     super(LintCommand, cls).AddParser(parser)
131     parser.add_argument('files', help='Files to lint', nargs='*')
132
133   def Run(self):
134     files = self.options.files
135     if not files:
136       # Running with no arguments is allowed to make the repo upload hook
137       # simple, but print a warning so that if someone runs this manually
138       # they are aware that nothing was linted.
139       cros_build_lib.Warning('No files provided to lint.  Doing nothing.')
140
141     errors = False
142     linter_map = _BreakoutFilesByLinter(files)
143     for linter, files in linter_map.iteritems():
144       errors = linter(files, self.options.debug)
145
146     if errors:
147       sys.exit(1)