3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
8 # written by Sylvain Rouquette, 2014
12 This is an extra tool, not bundled with the default waf binary.
13 To add the cpplint tool to the waf file:
14 $ ./waf-light --tools=compat15,cpplint
16 this tool also requires cpplint for python.
17 If you have PIP, you can install it like this: pip install cpplint
19 When using this tool, the wscript will look like:
22 opt.load('compiler_cxx cpplint')
25 conf.load('compiler_cxx cpplint')
26 # optional, you can also specify them on the command line
27 conf.env.CPPLINT_FILTERS = ','.join((
28 '-whitespace/newline', # c++11 lambda
29 '-readability/braces', # c++11 constructor
30 '-whitespace/braces', # c++11 constructor
31 '-build/storage_class', # c++11 for-range
32 '-whitespace/blank_line', # user pref
33 '-whitespace/labels' # user pref
37 bld(features='cpplint', source='main.cpp', target='app')
38 # add include files, because they aren't usually built
39 bld(features='cpplint', source=bld.path.ant_glob('**/*.hpp'))
42 from __future__ import absolute_import
46 from waflib import Task, TaskGen, Logs, Options, Node
48 import cpplint.cpplint as cpplint_tool
51 import cpplint as cpplint_tool
57 CPPLINT_FORMAT = '[CPPLINT] %(filename)s:\nline %(linenum)s, severity %(confidence)s, category: %(category)s\n%(message)s\n'
58 RE_EMACS = re.compile('(?P<filename>.*):(?P<linenum>\d+): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]')
62 'vs7': re.compile('(?P<filename>.*)\((?P<linenum>\d+)\): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
63 'eclipse': re.compile('(?P<filename>.*):(?P<linenum>\d+): warning: (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
67 opt.add_option('--cpplint-filters', type='string',
68 default='', dest='CPPLINT_FILTERS',
69 help='add filters to cpplint')
70 opt.add_option('--cpplint-length', type='int',
71 default=80, dest='CPPLINT_LINE_LENGTH',
72 help='specify the line length (default: 80)')
73 opt.add_option('--cpplint-level', default=1, type='int', dest='CPPLINT_LEVEL',
74 help='specify the log level (default: 1)')
75 opt.add_option('--cpplint-break', default=5, type='int', dest='CPPLINT_BREAK',
76 help='break the build if error >= level (default: 5)')
77 opt.add_option('--cpplint-root', type='string',
78 default=None, dest='CPPLINT_ROOT',
79 help='root directory used to derive header guard')
80 opt.add_option('--cpplint-skip', action='store_true',
81 default=False, dest='CPPLINT_SKIP',
82 help='skip cpplint during build')
83 opt.add_option('--cpplint-output', type='string',
84 default='waf', dest='CPPLINT_OUTPUT',
85 help='select output format (waf, emacs, vs7)')
89 conf.start_msg('Checking cpplint')
91 cpplint_tool._cpplint_state
94 conf.env.CPPLINT_SKIP = True
95 conf.end_msg('not found, skipping it.')
98 class cpplint_formatter(Logs.formatter, object):
99 def __init__(self, fmt):
100 logging.Formatter.__init__(self, CPPLINT_FORMAT)
103 def format(self, rec):
104 if self.fmt == 'waf':
105 result = CPPLINT_RE[self.fmt].match(rec.msg).groupdict()
106 rec.msg = CPPLINT_FORMAT % result
107 if rec.levelno <= logging.INFO:
108 rec.c1 = Logs.colors.CYAN
109 return super(cpplint_formatter, self).format(rec)
112 class cpplint_handler(Logs.log_handler, object):
113 def __init__(self, stream=sys.stderr, **kw):
114 super(cpplint_handler, self).__init__(stream, **kw)
118 rec.stream = self.stream
119 self.emit_override(rec)
123 class cpplint_wrapper(object):
126 lock = threading.RLock()
128 def __init__(self, logger, threshold, fmt):
130 self.threshold = threshold
135 with cpplint_wrapper.lock:
136 cpplint_wrapper.tasks_count += 1
137 if cpplint_wrapper.tasks_count == 1:
139 cpplint_wrapper.stream = sys.stderr
143 def __exit__(self, exc_type, exc_value, traceback):
144 with cpplint_wrapper.lock:
145 cpplint_wrapper.tasks_count -= 1
146 if cpplint_wrapper.tasks_count == 0:
147 sys.stderr = cpplint_wrapper.stream
153 def write(self, message):
154 global critical_errors
155 result = CPPLINT_RE[self.fmt].match(message)
158 level = int(result.groupdict()['confidence'])
159 if level >= self.threshold:
162 self.logger.info(message)
164 self.logger.warning(message)
166 self.logger.error(message)
169 cpplint_logger = None
170 def get_cpplint_logger(fmt):
171 global cpplint_logger
173 return cpplint_logger
174 cpplint_logger = logging.getLogger('cpplint')
175 hdlr = cpplint_handler()
176 hdlr.setFormatter(cpplint_formatter(fmt))
177 cpplint_logger.addHandler(hdlr)
178 cpplint_logger.setLevel(logging.DEBUG)
179 return cpplint_logger
182 class cpplint(Task.Task):
185 def __init__(self, *k, **kw):
186 super(cpplint, self).__init__(*k, **kw)
189 global critical_errors
190 with cpplint_wrapper(get_cpplint_logger(self.env.CPPLINT_OUTPUT), self.env.CPPLINT_BREAK, self.env.CPPLINT_OUTPUT):
191 if self.env.CPPLINT_OUTPUT != 'waf':
192 cpplint_tool._SetOutputFormat(self.env.CPPLINT_OUTPUT)
193 cpplint_tool._SetFilters(self.env.CPPLINT_FILTERS)
194 cpplint_tool._line_length = self.env.CPPLINT_LINE_LENGTH
195 cpplint_tool._root = self.env.CPPLINT_ROOT
196 cpplint_tool.ProcessFile(self.inputs[0].abspath(), self.env.CPPLINT_LEVEL)
197 return critical_errors
199 @TaskGen.extension('.h', '.hh', '.hpp', '.hxx')
200 def cpplint_includes(self, node):
203 @TaskGen.feature('cpplint')
204 @TaskGen.before_method('process_source')
205 def post_cpplint(self):
206 if not self.env.CPPLINT_INITIALIZED:
207 for key, value in Options.options.__dict__.items():
208 if not key.startswith('CPPLINT_') or self.env[key]:
210 self.env[key] = value
211 self.env.CPPLINT_INITIALIZED = True
213 if self.env.CPPLINT_SKIP:
216 if not self.env.CPPLINT_OUTPUT in CPPLINT_RE:
219 for src in self.to_list(getattr(self, 'source', [])):
220 if isinstance(src, Node.Node):
223 node = self.path.find_or_declare(src)
225 self.bld.fatal('Could not find %r' % src)
226 self.create_task('cpplint', node)