cpplint: fixed import. The version on pypi is now up to date and works with Python3.
[waf.git] / waflib / extras / cpplint.py
blob71d037026c9b4ef73e90478fcdb263e90d018afc
1 #! /usr/bin/env python
2 # encoding: utf-8
4 # written by Sylvain Rouquette, 2014
6 '''
8 This is an extra tool, not bundled with the default waf binary.
9 To add the cpplint tool to the waf file:
10 $ ./waf-light --tools=compat15,cpplint
11 or, if you have waf >= 1.6.2
12 $ ./waf update --files=cpplint
14 this tool also requires cpplint for python.
15 If you have PIP, you can install it like this: pip install cpplint
17 But I'd recommend getting the latest version from the SVN,
18 the PIP version is outdated.
19 https://code.google.com/p/google-styleguide/source/browse/trunk/cpplint/cpplint.py
20 Apply this patch if you want to run it with Python 3:
21 https://code.google.com/p/google-styleguide/issues/detail?id=19
24 When using this tool, the wscript will look like:
26 def options(opt):
27 opt.load('compiler_cxx cpplint')
29 def configure(conf):
30 conf.load('compiler_cxx cpplint')
31 # optional, you can also specify them on the command line
32 conf.env.CPPLINT_FILTERS = ','.join((
33 '-whitespace/newline', # c++11 lambda
34 '-readability/braces', # c++11 constructor
35 '-whitespace/braces', # c++11 constructor
36 '-build/storage_class', # c++11 for-range
37 '-whitespace/blank_line', # user pref
38 '-whitespace/labels' # user pref
41 def build(bld):
42 bld(features='cpplint', source='main.cpp', target='app')
43 # add include files, because they aren't usually built
44 bld(features='cpplint', source=bld.path.ant_glob('**/*.hpp'))
45 '''
47 import sys, re
48 import logging
49 import threading
50 from waflib import Task, Build, TaskGen, Logs, Utils
51 try:
52 from cpplint import ProcessFile, _cpplint_state
53 except ImportError:
54 pass
57 critical_errors = 0
58 CPPLINT_FORMAT = '[CPPLINT] %(filename)s:\nline %(linenum)s, severity %(confidence)s, category: %(category)s\n%(message)s\n'
59 RE_EMACS = re.compile('(?P<filename>.*):(?P<linenum>\d+): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]');
60 CPPLINT_RE = {
61 'waf': RE_EMACS,
62 'emacs': RE_EMACS,
63 'vs7': re.compile('(?P<filename>.*)\((?P<linenum>\d+)\): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
64 'eclipse': re.compile('(?P<filename>.*):(?P<linenum>\d+): warning: (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
69 def init_env_from_options(env):
70 from waflib.Options import options
71 for key, value in options.__dict__.items():
72 if not key.startswith('CPPLINT_') or env[key]:
73 continue
74 env[key] = value
75 if env.CPPLINT_OUTPUT != 'waf':
76 _cpplint_state.output_format = env.CPPLINT_OUTPUT
79 def options(opt):
80 opt.add_option('--cpplint-filters', type='string',
81 default='', dest='CPPLINT_FILTERS',
82 help='add filters to cpplint')
83 opt.add_option('--cpplint-level', default=1, type='int', dest='CPPLINT_LEVEL',
84 help='specify the log level (default: 1)')
85 opt.add_option('--cpplint-break', default=5, type='int', dest='CPPLINT_BREAK',
86 help='break the build if error >= level (default: 5)')
87 opt.add_option('--cpplint-skip', action='store_true',
88 default=False, dest='CPPLINT_SKIP',
89 help='skip cpplint during build')
90 opt.add_option('--cpplint-output', type='string',
91 default='waf', dest='CPPLINT_OUTPUT',
92 help='select output format (waf, emacs, vs7)')
95 def configure(conf):
96 conf.start_msg('Checking cpplint')
97 try:
98 import cpplint
99 conf.end_msg('ok')
100 except ImportError:
101 conf.env.CPPLINT_SKIP = True
102 conf.end_msg('not found, skipping it.')
105 class cpplint_formatter(Logs.formatter):
106 def __init__(self, fmt):
107 logging.Formatter.__init__(self, CPPLINT_FORMAT)
108 self.fmt = fmt
110 def format(self, rec):
111 if self.fmt == 'waf':
112 result = CPPLINT_RE[self.fmt].match(rec.msg).groupdict()
113 rec.msg = CPPLINT_FORMAT % result
114 if rec.levelno <= logging.INFO:
115 rec.c1 = Logs.colors.CYAN
116 return super(cpplint_formatter, self).format(rec)
119 class cpplint_handler(Logs.log_handler):
120 def __init__(self, stream=sys.stderr, **kw):
121 super(cpplint_handler, self).__init__(stream, **kw)
122 self.stream = stream
124 def emit(self, rec):
125 rec.stream = self.stream
126 self.emit_override(rec)
127 self.flush()
130 class cpplint_wrapper(object):
131 stream = None
132 tasks_count = 0
133 lock = threading.RLock()
135 def __init__(self, logger, threshold, fmt):
136 self.logger = logger
137 self.threshold = threshold
138 self.error_count = 0
139 self.fmt = fmt
141 def __enter__(self):
142 with cpplint_wrapper.lock:
143 cpplint_wrapper.tasks_count += 1
144 if cpplint_wrapper.tasks_count == 1:
145 sys.stderr.flush()
146 cpplint_wrapper.stream = sys.stderr
147 sys.stderr = self
148 return self
150 def __exit__(self, exc_type, exc_value, traceback):
151 with cpplint_wrapper.lock:
152 cpplint_wrapper.tasks_count -= 1
153 if cpplint_wrapper.tasks_count == 0:
154 sys.stderr = cpplint_wrapper.stream
155 sys.stderr.flush()
157 def isatty(self):
158 return True
160 def write(self, message):
161 global critical_errors
162 result = CPPLINT_RE[self.fmt].match(message)
163 if not result:
164 return
165 level = int(result.groupdict()['confidence'])
166 if level >= self.threshold:
167 critical_errors += 1
168 if level <= 2:
169 self.logger.info(message)
170 elif level <= 4:
171 self.logger.warning(message)
172 else:
173 self.logger.error(message)
176 cpplint_logger = None
177 def get_cpplint_logger(fmt):
178 global cpplint_logger
179 if cpplint_logger:
180 return cpplint_logger
181 cpplint_logger = logging.getLogger('cpplint')
182 hdlr = cpplint_handler()
183 hdlr.setFormatter(cpplint_formatter(fmt))
184 cpplint_logger.addHandler(hdlr)
185 cpplint_logger.setLevel(logging.DEBUG)
186 return cpplint_logger
189 class cpplint(Task.Task):
190 color = 'PINK'
192 def __init__(self, *k, **kw):
193 super(cpplint, self).__init__(*k, **kw)
195 def run(self):
196 global critical_errors
197 _cpplint_state.SetFilters(self.env.CPPLINT_FILTERS)
198 break_level = self.env.CPPLINT_BREAK
199 verbosity = self.env.CPPLINT_LEVEL
200 with cpplint_wrapper(get_cpplint_logger(self.env.CPPLINT_OUTPUT),
201 break_level, self.env.CPPLINT_OUTPUT):
202 ProcessFile(self.inputs[0].abspath(), verbosity)
203 return critical_errors
206 @TaskGen.extension('.h', '.hh', '.hpp', '.hxx')
207 def cpplint_includes(self, node):
208 pass
210 @TaskGen.feature('cpplint')
211 @TaskGen.before_method('process_source')
212 def run_cpplint(self):
213 if not self.env.CPPLINT_INITIALIZED:
214 self.env.CPPLINT_INITIALIZED = True
215 init_env_from_options(self.env)
216 if self.env.CPPLINT_SKIP:
217 return
218 if not self.env.CPPLINT_OUTPUT in CPPLINT_RE:
219 return
220 for src in self.to_list(getattr(self, 'source', [])):
221 if isinstance(src, str):
222 self.create_task('cpplint', self.path.find_or_declare(src))
223 else:
224 self.create_task('cpplint', src)