waf/waflib/extras/cpplint.py

220 lines
7.2 KiB
Python
Raw Normal View History

2014-02-08 20:57:46 +01:00
#! /usr/bin/env python
# encoding: utf-8
#
# written by Sylvain Rouquette, 2014
'''
This is an extra tool, not bundled with the default waf binary.
To add the cpplint tool to the waf file:
$ ./waf-light --tools=compat15,cpplint
this tool also requires cpplint for python.
If you have PIP, you can install it like this: pip install cpplint
2014-02-08 20:57:46 +01:00
When using this tool, the wscript will look like:
def options(opt):
opt.load('compiler_cxx cpplint')
def configure(conf):
conf.load('compiler_cxx cpplint')
# optional, you can also specify them on the command line
conf.env.CPPLINT_FILTERS = ','.join((
'-whitespace/newline', # c++11 lambda
'-readability/braces', # c++11 constructor
'-whitespace/braces', # c++11 constructor
'-build/storage_class', # c++11 for-range
'-whitespace/blank_line', # user pref
'-whitespace/labels' # user pref
))
def build(bld):
bld(features='cpplint', source='main.cpp', target='app')
# add include files, because they aren't usually built
bld(features='cpplint', source=bld.path.ant_glob('**/*.hpp'))
'''
import sys, re
import logging
import threading
2016-02-13 00:21:37 +01:00
from waflib import Task, TaskGen, Logs, Options, Node
2014-02-08 20:57:46 +01:00
try:
2016-02-26 22:57:59 +01:00
import cpplint.cpplint as cpplint_tool
2014-02-08 20:57:46 +01:00
except ImportError:
2015-05-06 22:33:31 +02:00
try:
2016-02-26 22:57:59 +01:00
import cpplint as cpplint_tool
2015-05-06 22:33:31 +02:00
except ImportError:
pass
2014-02-08 20:57:46 +01:00
critical_errors = 0
CPPLINT_FORMAT = '[CPPLINT] %(filename)s:\nline %(linenum)s, severity %(confidence)s, category: %(category)s\n%(message)s\n'
RE_EMACS = re.compile('(?P<filename>.*):(?P<linenum>\d+): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]')
CPPLINT_RE = {
'waf': RE_EMACS,
'emacs': RE_EMACS,
'vs7': re.compile('(?P<filename>.*)\((?P<linenum>\d+)\): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
'eclipse': re.compile('(?P<filename>.*):(?P<linenum>\d+): warning: (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
}
2014-02-08 20:57:46 +01:00
def options(opt):
opt.add_option('--cpplint-filters', type='string',
default='', dest='CPPLINT_FILTERS',
help='add filters to cpplint')
2016-02-26 22:57:59 +01:00
opt.add_option('--cpplint-length', type='int',
default=80, dest='CPPLINT_LINE_LENGTH',
help='specify the line length (default: 80)')
2014-02-08 20:57:46 +01:00
opt.add_option('--cpplint-level', default=1, type='int', dest='CPPLINT_LEVEL',
help='specify the log level (default: 1)')
opt.add_option('--cpplint-break', default=5, type='int', dest='CPPLINT_BREAK',
help='break the build if error >= level (default: 5)')
opt.add_option('--cpplint-skip', action='store_true',
default=False, dest='CPPLINT_SKIP',
help='skip cpplint during build')
opt.add_option('--cpplint-output', type='string',
default='waf', dest='CPPLINT_OUTPUT',
help='select output format (waf, emacs, vs7)')
2014-02-08 20:57:46 +01:00
def configure(conf):
conf.start_msg('Checking cpplint')
try:
2016-02-26 22:57:59 +01:00
cpplint_tool._cpplint_state
conf.end_msg('ok')
2015-09-01 19:40:26 +02:00
except NameError:
conf.env.CPPLINT_SKIP = True
conf.end_msg('not found, skipping it.')
2014-02-08 20:57:46 +01:00
class cpplint_formatter(Logs.formatter):
def __init__(self, fmt):
2014-02-08 20:57:46 +01:00
logging.Formatter.__init__(self, CPPLINT_FORMAT)
self.fmt = fmt
2014-02-08 20:57:46 +01:00
def format(self, rec):
if self.fmt == 'waf':
result = CPPLINT_RE[self.fmt].match(rec.msg).groupdict()
rec.msg = CPPLINT_FORMAT % result
if rec.levelno <= logging.INFO:
rec.c1 = Logs.colors.CYAN
2014-02-08 20:57:46 +01:00
return super(cpplint_formatter, self).format(rec)
class cpplint_handler(Logs.log_handler):
def __init__(self, stream=sys.stderr, **kw):
super(cpplint_handler, self).__init__(stream, **kw)
self.stream = stream
def emit(self, rec):
rec.stream = self.stream
self.emit_override(rec)
self.flush()
class cpplint_wrapper(object):
stream = None
tasks_count = 0
lock = threading.RLock()
def __init__(self, logger, threshold, fmt):
2014-02-08 20:57:46 +01:00
self.logger = logger
self.threshold = threshold
self.error_count = 0
self.fmt = fmt
2014-02-08 20:57:46 +01:00
def __enter__(self):
with cpplint_wrapper.lock:
cpplint_wrapper.tasks_count += 1
if cpplint_wrapper.tasks_count == 1:
sys.stderr.flush()
cpplint_wrapper.stream = sys.stderr
sys.stderr = self
return self
def __exit__(self, exc_type, exc_value, traceback):
with cpplint_wrapper.lock:
cpplint_wrapper.tasks_count -= 1
if cpplint_wrapper.tasks_count == 0:
sys.stderr = cpplint_wrapper.stream
sys.stderr.flush()
def isatty(self):
return True
2014-02-08 20:57:46 +01:00
def write(self, message):
global critical_errors
result = CPPLINT_RE[self.fmt].match(message)
2014-02-08 20:57:46 +01:00
if not result:
return
level = int(result.groupdict()['confidence'])
if level >= self.threshold:
critical_errors += 1
2014-02-08 20:57:46 +01:00
if level <= 2:
self.logger.info(message)
elif level <= 4:
self.logger.warning(message)
else:
self.logger.error(message)
cpplint_logger = None
def get_cpplint_logger(fmt):
2014-02-08 20:57:46 +01:00
global cpplint_logger
if cpplint_logger:
return cpplint_logger
cpplint_logger = logging.getLogger('cpplint')
hdlr = cpplint_handler()
hdlr.setFormatter(cpplint_formatter(fmt))
2014-02-08 20:57:46 +01:00
cpplint_logger.addHandler(hdlr)
cpplint_logger.setLevel(logging.DEBUG)
return cpplint_logger
class cpplint(Task.Task):
color = 'PINK'
def __init__(self, *k, **kw):
super(cpplint, self).__init__(*k, **kw)
def run(self):
global critical_errors
2016-02-13 00:21:37 +01:00
with cpplint_wrapper(get_cpplint_logger(self.env.CPPLINT_OUTPUT), self.env.CPPLINT_BREAK, self.env.CPPLINT_OUTPUT):
if self.env.CPPLINT_OUTPUT != 'waf':
2016-02-26 22:57:59 +01:00
cpplint_tool._cpplint_state.output_format = self.env.CPPLINT_OUTPUT
cpplint_tool._cpplint_state.SetFilters(self.env.CPPLINT_FILTERS)
cpplint_tool._line_length = self.env.CPPLINT_LINE_LENGTH
cpplint_tool.ProcessFile(self.inputs[0].abspath(), self.env.CPPLINT_LEVEL)
return critical_errors
2014-02-08 20:57:46 +01:00
@TaskGen.extension('.h', '.hh', '.hpp', '.hxx')
def cpplint_includes(self, node):
pass
@TaskGen.feature('cpplint')
@TaskGen.before_method('process_source')
2016-02-13 00:21:37 +01:00
def post_cpplint(self):
2014-02-08 20:57:46 +01:00
if self.env.CPPLINT_SKIP:
return
2016-02-13 00:21:37 +01:00
if not self.env.CPPLINT_INITIALIZED:
for key, value in Options.options.__dict__.items():
if not key.startswith('CPPLINT_') or self.env[key]:
continue
self.env[key] = value
self.env.CPPLINT_INITIALIZED = True
if not self.env.CPPLINT_OUTPUT in CPPLINT_RE:
return
2016-02-13 00:21:37 +01:00
2014-02-08 20:57:46 +01:00
for src in self.to_list(getattr(self, 'source', [])):
2016-02-13 00:21:37 +01:00
if isinstance(src, Node.Node):
node = src
2014-02-08 20:57:46 +01:00
else:
2016-02-13 00:21:37 +01:00
node = self.path.find_or_declare(src)
if not node:
self.bld.fatal('Could not find %r' % src)
self.create_task('cpplint', node)