From c2d1e1b3e586dfdd0864e377a16947e12be75109 Mon Sep 17 00:00:00 2001 From: Syl Date: Sat, 8 Feb 2014 20:57:46 +0100 Subject: [PATCH] added cpplint tool --- playground/cpplint/main.cpp | 8 ++ playground/cpplint/my_include.hpp | 9 ++ playground/cpplint/wscript | 12 ++ waflib/extras/cpplint.py | 190 ++++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+) create mode 100644 playground/cpplint/main.cpp create mode 100644 playground/cpplint/my_include.hpp create mode 100644 playground/cpplint/wscript create mode 100644 waflib/extras/cpplint.py diff --git a/playground/cpplint/main.cpp b/playground/cpplint/main.cpp new file mode 100644 index 00000000..3f9152a6 --- /dev/null +++ b/playground/cpplint/main.cpp @@ -0,0 +1,8 @@ +#include "my_include.hpp" + +#include + +int main() +{ + std::string test; +} diff --git a/playground/cpplint/my_include.hpp b/playground/cpplint/my_include.hpp new file mode 100644 index 00000000..79af8710 --- /dev/null +++ b/playground/cpplint/my_include.hpp @@ -0,0 +1,9 @@ +#ifndef GUARD +#define GUARD + +class my_class { +public: + my_class() {} +}; + +#endif diff --git a/playground/cpplint/wscript b/playground/cpplint/wscript new file mode 100644 index 00000000..90ff7b04 --- /dev/null +++ b/playground/cpplint/wscript @@ -0,0 +1,12 @@ +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx cpplint') + +def configure(conf): + conf.load('compiler_cxx cpplint') + +def build(bld): + bld(features='cpplint', source='my_include.hpp') + bld.program(features='cpplint', source='main.cpp', target='app') diff --git a/waflib/extras/cpplint.py b/waflib/extras/cpplint.py new file mode 100644 index 00000000..3cbe4c5c --- /dev/null +++ b/waflib/extras/cpplint.py @@ -0,0 +1,190 @@ +#! /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 + or, if you have waf >= 1.6.2 +$ ./waf update --files=cpplint + +this tool also requires cpplint for python. +If you have PIP, install it like this: pip install cpplint + + +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 +from waflib import Task, Build, TaskGen, Logs, Utils +try: + from cpplint.cpplint import ProcessFile, _cpplint_state +except ImportError: + pass + + +CPPLINT_FORMAT = '[CPPLINT] %(filename)s:\nline %(linenum)s, severity %(confidence)s, category: %(category)s\n%(message)s\n' +CPPLINT_RE = re.compile('(?P.*):(?P\d+): (?P.*) \[(?P.*)\] \[(?P\d+)\]') + + +def options(opt): + opt.add_option('--cpplint-filters', type='string', + default='', dest='CPPLINT_FILTERS', + help='add filters to cpplint') + 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') + + +def configure(conf): + conf.start_msg('Checking cpplint') + try: + import cpplint + except ImportError: + conf.fatal('cpplint not found. try "pip install cpplint".') + conf.end_msg('ok') + if not conf.env.CPPLINT_FILTERS: + conf.env.CPPLINT_FILTERS = conf.options.CPPLINT_FILTERS + if not conf.env.CPPLINT_LEVEL: + conf.env.CPPLINT_LEVEL = conf.options.CPPLINT_LEVEL + if not conf.env.CPPLINT_BREAK: + conf.env.CPPLINT_BREAK = conf.options.CPPLINT_BREAK + if not conf.env.CPPLINT_SKIP: + conf.env.CPPLINT_SKIP = conf.options.CPPLINT_SKIP + + +class cpplint_formatter(Logs.formatter): + def __init__(self): + logging.Formatter.__init__(self, CPPLINT_FORMAT) + + def format(self, rec): + result = CPPLINT_RE.match(rec.msg).groupdict() + rec.msg = CPPLINT_FORMAT % result + 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): + self.logger = logger + self.threshold = threshold + self.error_count = 0 + + 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 write(self, message): + result = CPPLINT_RE.match(message) + if not result: + return + level = int(result.groupdict()['confidence']) + if level >= self.threshold: + self.error_count += 1 + 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(): + global cpplint_logger + if cpplint_logger: + return cpplint_logger + cpplint_logger = logging.getLogger('cpplint') + hdlr = cpplint_handler() + hdlr.setFormatter(cpplint_formatter()) + 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): + _cpplint_state.SetFilters(self.env.CPPLINT_FILTERS) + break_level = self.env.CPPLINT_BREAK + verbosity = self.env.CPPLINT_LEVEL + result = 0 + with cpplint_wrapper(get_cpplint_logger(), break_level) as wrapper: + ProcessFile(self.inputs[0].abspath(), verbosity) + result = wrapper.error_count + return result + + +@TaskGen.extension('.h', '.hh', '.hpp', '.hxx') +def cpplint_includes(self, node): + pass + +@TaskGen.feature('cpplint') +@TaskGen.before_method('process_source') +def run_cpplint(self): + if self.env.CPPLINT_SKIP: + return + for src in self.to_list(getattr(self, 'source', [])): + if isinstance(src, str): + self.create_task('cpplint', self.path.find_or_declare(src)) + else: + self.create_task('cpplint', src)