|
rpm-build |
95f51c |
#! /usr/bin/env python
|
|
rpm-build |
95f51c |
# encoding: utf-8
|
|
rpm-build |
95f51c |
#
|
|
rpm-build |
95f51c |
# written by Sylvain Rouquette, 2014
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
'''
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
This is an extra tool, not bundled with the default waf binary.
|
|
rpm-build |
95f51c |
To add the cpplint tool to the waf file:
|
|
rpm-build |
95f51c |
$ ./waf-light --tools=compat15,cpplint
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
this tool also requires cpplint for python.
|
|
rpm-build |
95f51c |
If you have PIP, you can install it like this: pip install cpplint
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
When using this tool, the wscript will look like:
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def options(opt):
|
|
rpm-build |
95f51c |
opt.load('compiler_cxx cpplint')
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def configure(conf):
|
|
rpm-build |
95f51c |
conf.load('compiler_cxx cpplint')
|
|
rpm-build |
95f51c |
# optional, you can also specify them on the command line
|
|
rpm-build |
95f51c |
conf.env.CPPLINT_FILTERS = ','.join((
|
|
rpm-build |
95f51c |
'-whitespace/newline', # c++11 lambda
|
|
rpm-build |
95f51c |
'-readability/braces', # c++11 constructor
|
|
rpm-build |
95f51c |
'-whitespace/braces', # c++11 constructor
|
|
rpm-build |
95f51c |
'-build/storage_class', # c++11 for-range
|
|
rpm-build |
95f51c |
'-whitespace/blank_line', # user pref
|
|
rpm-build |
95f51c |
'-whitespace/labels' # user pref
|
|
rpm-build |
95f51c |
))
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def build(bld):
|
|
rpm-build |
95f51c |
bld(features='cpplint', source='main.cpp', target='app')
|
|
rpm-build |
95f51c |
# add include files, because they aren't usually built
|
|
rpm-build |
95f51c |
bld(features='cpplint', source=bld.path.ant_glob('**/*.hpp'))
|
|
rpm-build |
95f51c |
'''
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
from __future__ import absolute_import
|
|
rpm-build |
95f51c |
import sys, re
|
|
rpm-build |
95f51c |
import logging
|
|
rpm-build |
95f51c |
from waflib import Errors, Task, TaskGen, Logs, Options, Node, Utils
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
critical_errors = 0
|
|
rpm-build |
95f51c |
CPPLINT_FORMAT = '[CPPLINT] %(filename)s:\nline %(linenum)s, severity %(confidence)s, category: %(category)s\n%(message)s\n'
|
|
rpm-build |
95f51c |
RE_EMACS = re.compile(r'(?P<filename>.*):(?P<linenum>\d+): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]')
|
|
rpm-build |
95f51c |
CPPLINT_RE = {
|
|
rpm-build |
95f51c |
'waf': RE_EMACS,
|
|
rpm-build |
95f51c |
'emacs': RE_EMACS,
|
|
rpm-build |
95f51c |
'vs7': re.compile(r'(?P<filename>.*)\((?P<linenum>\d+)\): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
|
|
rpm-build |
95f51c |
'eclipse': re.compile(r'(?P<filename>.*):(?P<linenum>\d+): warning: (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
|
|
rpm-build |
95f51c |
}
|
|
rpm-build |
95f51c |
CPPLINT_STR = ('${CPPLINT} '
|
|
rpm-build |
95f51c |
'--verbose=${CPPLINT_LEVEL} '
|
|
rpm-build |
95f51c |
'--output=${CPPLINT_OUTPUT} '
|
|
rpm-build |
95f51c |
'--filter=${CPPLINT_FILTERS} '
|
|
rpm-build |
95f51c |
'--root=${CPPLINT_ROOT} '
|
|
rpm-build |
95f51c |
'--linelength=${CPPLINT_LINE_LENGTH} ')
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def options(opt):
|
|
rpm-build |
95f51c |
opt.add_option('--cpplint-filters', type='string',
|
|
rpm-build |
95f51c |
default='', dest='CPPLINT_FILTERS',
|
|
rpm-build |
95f51c |
help='add filters to cpplint')
|
|
rpm-build |
95f51c |
opt.add_option('--cpplint-length', type='int',
|
|
rpm-build |
95f51c |
default=80, dest='CPPLINT_LINE_LENGTH',
|
|
rpm-build |
95f51c |
help='specify the line length (default: 80)')
|
|
rpm-build |
95f51c |
opt.add_option('--cpplint-level', default=1, type='int', dest='CPPLINT_LEVEL',
|
|
rpm-build |
95f51c |
help='specify the log level (default: 1)')
|
|
rpm-build |
95f51c |
opt.add_option('--cpplint-break', default=5, type='int', dest='CPPLINT_BREAK',
|
|
rpm-build |
95f51c |
help='break the build if error >= level (default: 5)')
|
|
rpm-build |
95f51c |
opt.add_option('--cpplint-root', type='string',
|
|
rpm-build |
95f51c |
default='', dest='CPPLINT_ROOT',
|
|
rpm-build |
95f51c |
help='root directory used to derive header guard')
|
|
rpm-build |
95f51c |
opt.add_option('--cpplint-skip', action='store_true',
|
|
rpm-build |
95f51c |
default=False, dest='CPPLINT_SKIP',
|
|
rpm-build |
95f51c |
help='skip cpplint during build')
|
|
rpm-build |
95f51c |
opt.add_option('--cpplint-output', type='string',
|
|
rpm-build |
95f51c |
default='waf', dest='CPPLINT_OUTPUT',
|
|
rpm-build |
95f51c |
help='select output format (waf, emacs, vs7, eclipse)')
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def configure(conf):
|
|
rpm-build |
95f51c |
try:
|
|
rpm-build |
95f51c |
conf.find_program('cpplint', var='CPPLINT')
|
|
rpm-build |
95f51c |
except Errors.ConfigurationError:
|
|
rpm-build |
95f51c |
conf.env.CPPLINT_SKIP = True
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
class cpplint_formatter(Logs.formatter, object):
|
|
rpm-build |
95f51c |
def __init__(self, fmt):
|
|
rpm-build |
95f51c |
logging.Formatter.__init__(self, CPPLINT_FORMAT)
|
|
rpm-build |
95f51c |
self.fmt = fmt
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def format(self, rec):
|
|
rpm-build |
95f51c |
if self.fmt == 'waf':
|
|
rpm-build |
95f51c |
result = CPPLINT_RE[self.fmt].match(rec.msg).groupdict()
|
|
rpm-build |
95f51c |
rec.msg = CPPLINT_FORMAT % result
|
|
rpm-build |
95f51c |
if rec.levelno <= logging.INFO:
|
|
rpm-build |
95f51c |
rec.c1 = Logs.colors.CYAN
|
|
rpm-build |
95f51c |
return super(cpplint_formatter, self).format(rec)
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
class cpplint_handler(Logs.log_handler, object):
|
|
rpm-build |
95f51c |
def __init__(self, stream=sys.stderr, **kw):
|
|
rpm-build |
95f51c |
super(cpplint_handler, self).__init__(stream, **kw)
|
|
rpm-build |
95f51c |
self.stream = stream
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def emit(self, rec):
|
|
rpm-build |
95f51c |
rec.stream = self.stream
|
|
rpm-build |
95f51c |
self.emit_override(rec)
|
|
rpm-build |
95f51c |
self.flush()
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
class cpplint_wrapper(object):
|
|
rpm-build |
95f51c |
def __init__(self, logger, threshold, fmt):
|
|
rpm-build |
95f51c |
self.logger = logger
|
|
rpm-build |
95f51c |
self.threshold = threshold
|
|
rpm-build |
95f51c |
self.fmt = fmt
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def __enter__(self):
|
|
rpm-build |
95f51c |
return self
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def __exit__(self, exc_type, exc_value, traceback):
|
|
rpm-build |
95f51c |
if isinstance(exc_value, Utils.subprocess.CalledProcessError):
|
|
rpm-build |
95f51c |
messages = [m for m in exc_value.output.splitlines()
|
|
rpm-build |
95f51c |
if 'Done processing' not in m
|
|
rpm-build |
95f51c |
and 'Total errors found' not in m]
|
|
rpm-build |
95f51c |
for message in messages:
|
|
rpm-build |
95f51c |
self.write(message)
|
|
rpm-build |
95f51c |
return True
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def write(self, message):
|
|
rpm-build |
95f51c |
global critical_errors
|
|
rpm-build |
95f51c |
result = CPPLINT_RE[self.fmt].match(message)
|
|
rpm-build |
95f51c |
if not result:
|
|
rpm-build |
95f51c |
return
|
|
rpm-build |
95f51c |
level = int(result.groupdict()['confidence'])
|
|
rpm-build |
95f51c |
if level >= self.threshold:
|
|
rpm-build |
95f51c |
critical_errors += 1
|
|
rpm-build |
95f51c |
if level <= 2:
|
|
rpm-build |
95f51c |
self.logger.info(message)
|
|
rpm-build |
95f51c |
elif level <= 4:
|
|
rpm-build |
95f51c |
self.logger.warning(message)
|
|
rpm-build |
95f51c |
else:
|
|
rpm-build |
95f51c |
self.logger.error(message)
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
cpplint_logger = None
|
|
rpm-build |
95f51c |
def get_cpplint_logger(fmt):
|
|
rpm-build |
95f51c |
global cpplint_logger
|
|
rpm-build |
95f51c |
if cpplint_logger:
|
|
rpm-build |
95f51c |
return cpplint_logger
|
|
rpm-build |
95f51c |
cpplint_logger = logging.getLogger('cpplint')
|
|
rpm-build |
95f51c |
hdlr = cpplint_handler()
|
|
rpm-build |
95f51c |
hdlr.setFormatter(cpplint_formatter(fmt))
|
|
rpm-build |
95f51c |
cpplint_logger.addHandler(hdlr)
|
|
rpm-build |
95f51c |
cpplint_logger.setLevel(logging.DEBUG)
|
|
rpm-build |
95f51c |
return cpplint_logger
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
class cpplint(Task.Task):
|
|
rpm-build |
95f51c |
color = 'PINK'
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def __init__(self, *k, **kw):
|
|
rpm-build |
95f51c |
super(cpplint, self).__init__(*k, **kw)
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
def run(self):
|
|
rpm-build |
95f51c |
global critical_errors
|
|
rpm-build |
95f51c |
with cpplint_wrapper(get_cpplint_logger(self.env.CPPLINT_OUTPUT), self.env.CPPLINT_BREAK, self.env.CPPLINT_OUTPUT):
|
|
rpm-build |
95f51c |
params = {key: str(self.env[key]) for key in self.env if 'CPPLINT_' in key}
|
|
rpm-build |
95f51c |
if params['CPPLINT_OUTPUT'] is 'waf':
|
|
rpm-build |
95f51c |
params['CPPLINT_OUTPUT'] = 'emacs'
|
|
rpm-build |
95f51c |
params['CPPLINT'] = self.env.get_flat('CPPLINT')
|
|
rpm-build |
95f51c |
cmd = Utils.subst_vars(CPPLINT_STR, params)
|
|
rpm-build |
95f51c |
env = self.env.env or None
|
|
rpm-build |
95f51c |
Utils.subprocess.check_output(cmd + self.inputs[0].abspath(),
|
|
rpm-build |
95f51c |
stderr=Utils.subprocess.STDOUT,
|
|
rpm-build |
95f51c |
env=env, shell=True)
|
|
rpm-build |
95f51c |
return critical_errors
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
@TaskGen.extension('.h', '.hh', '.hpp', '.hxx')
|
|
rpm-build |
95f51c |
def cpplint_includes(self, node):
|
|
rpm-build |
95f51c |
pass
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
@TaskGen.feature('cpplint')
|
|
rpm-build |
95f51c |
@TaskGen.before_method('process_source')
|
|
rpm-build |
95f51c |
def post_cpplint(self):
|
|
rpm-build |
95f51c |
if not self.env.CPPLINT_INITIALIZED:
|
|
rpm-build |
95f51c |
for key, value in Options.options.__dict__.items():
|
|
rpm-build |
95f51c |
if not key.startswith('CPPLINT_') or self.env[key]:
|
|
rpm-build |
95f51c |
continue
|
|
rpm-build |
95f51c |
self.env[key] = value
|
|
rpm-build |
95f51c |
self.env.CPPLINT_INITIALIZED = True
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
if self.env.CPPLINT_SKIP:
|
|
rpm-build |
95f51c |
return
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
if not self.env.CPPLINT_OUTPUT in CPPLINT_RE:
|
|
rpm-build |
95f51c |
return
|
|
rpm-build |
95f51c |
|
|
rpm-build |
95f51c |
for src in self.to_list(getattr(self, 'source', [])):
|
|
rpm-build |
95f51c |
if isinstance(src, Node.Node):
|
|
rpm-build |
95f51c |
node = src
|
|
rpm-build |
95f51c |
else:
|
|
rpm-build |
95f51c |
node = self.path.find_or_declare(src)
|
|
rpm-build |
95f51c |
if not node:
|
|
rpm-build |
95f51c |
self.bld.fatal('Could not find %r' % src)
|
|
rpm-build |
95f51c |
self.create_task('cpplint', node)
|