Blame third_party/waf/waflib/extras/cpplint.py

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)