4 # written by Sylvain Rouquette, 2014
8 This is an extra tool, not bundled with the default waf binary.
9 To add the cpplint tool to the waf file:
10 $ ./waf-light --tools=compat15,cpplint
11 or, if you have waf >= 1.6.2
12 $ ./waf update --files=cpplint
14 this tool also requires cpplint for python.
15 If you have PIP, you can install it like this: pip install cpplint
17 But I'd recommend getting the latest version from the SVN,
18 the PIP version is outdated.
19 https://code.google.com/p/google-styleguide/source/browse/trunk/cpplint/cpplint.py
20 Apply this patch if you want to run it with Python 3:
21 https://code.google.com/p/google-styleguide/issues/detail?id=19
24 When using this tool, the wscript will look like:
27 opt.load('compiler_cxx cpplint')
30 conf.load('compiler_cxx cpplint')
31 # optional, you can also specify them on the command line
32 conf.env.CPPLINT_FILTERS = ','.join((
33 '-whitespace/newline', # c++11 lambda
34 '-readability/braces', # c++11 constructor
35 '-whitespace/braces', # c++11 constructor
36 '-build/storage_class', # c++11 for-range
37 '-whitespace/blank_line', # user pref
38 '-whitespace/labels' # user pref
42 bld(features='cpplint', source='main.cpp', target='app')
43 # add include files, because they aren't usually built
44 bld(features='cpplint', source=bld.path.ant_glob('**/*.hpp'))
50 from waflib
import Task
, Build
, TaskGen
, Logs
, Utils
52 from cpplint
import ProcessFile
, _cpplint_state
58 CPPLINT_FORMAT
= '[CPPLINT] %(filename)s:\nline %(linenum)s, severity %(confidence)s, category: %(category)s\n%(message)s\n'
59 RE_EMACS
= re
.compile('(?P<filename>.*):(?P<linenum>\d+): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]');
63 'vs7': re
.compile('(?P<filename>.*)\((?P<linenum>\d+)\): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
64 'eclipse': re
.compile('(?P<filename>.*):(?P<linenum>\d+): warning: (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
69 def init_env_from_options(env
):
70 from waflib
.Options
import options
71 for key
, value
in options
.__dict
__.items():
72 if not key
.startswith('CPPLINT_') or env
[key
]:
75 if env
.CPPLINT_OUTPUT
!= 'waf':
76 _cpplint_state
.output_format
= env
.CPPLINT_OUTPUT
80 opt
.add_option('--cpplint-filters', type='string',
81 default
='', dest
='CPPLINT_FILTERS',
82 help='add filters to cpplint')
83 opt
.add_option('--cpplint-level', default
=1, type='int', dest
='CPPLINT_LEVEL',
84 help='specify the log level (default: 1)')
85 opt
.add_option('--cpplint-break', default
=5, type='int', dest
='CPPLINT_BREAK',
86 help='break the build if error >= level (default: 5)')
87 opt
.add_option('--cpplint-skip', action
='store_true',
88 default
=False, dest
='CPPLINT_SKIP',
89 help='skip cpplint during build')
90 opt
.add_option('--cpplint-output', type='string',
91 default
='waf', dest
='CPPLINT_OUTPUT',
92 help='select output format (waf, emacs, vs7)')
96 conf
.start_msg('Checking cpplint')
101 conf
.env
.CPPLINT_SKIP
= True
102 conf
.end_msg('not found, skipping it.')
105 class cpplint_formatter(Logs
.formatter
):
106 def __init__(self
, fmt
):
107 logging
.Formatter
.__init
__(self
, CPPLINT_FORMAT
)
110 def format(self
, rec
):
111 if self
.fmt
== 'waf':
112 result
= CPPLINT_RE
[self
.fmt
].match(rec
.msg
).groupdict()
113 rec
.msg
= CPPLINT_FORMAT
% result
114 if rec
.levelno
<= logging
.INFO
:
115 rec
.c1
= Logs
.colors
.CYAN
116 return super(cpplint_formatter
, self
).format(rec
)
119 class cpplint_handler(Logs
.log_handler
):
120 def __init__(self
, stream
=sys
.stderr
, **kw
):
121 super(cpplint_handler
, self
).__init
__(stream
, **kw
)
125 rec
.stream
= self
.stream
126 self
.emit_override(rec
)
130 class cpplint_wrapper(object):
133 lock
= threading
.RLock()
135 def __init__(self
, logger
, threshold
, fmt
):
137 self
.threshold
= threshold
142 with cpplint_wrapper
.lock
:
143 cpplint_wrapper
.tasks_count
+= 1
144 if cpplint_wrapper
.tasks_count
== 1:
146 cpplint_wrapper
.stream
= sys
.stderr
150 def __exit__(self
, exc_type
, exc_value
, traceback
):
151 with cpplint_wrapper
.lock
:
152 cpplint_wrapper
.tasks_count
-= 1
153 if cpplint_wrapper
.tasks_count
== 0:
154 sys
.stderr
= cpplint_wrapper
.stream
160 def write(self
, message
):
161 global critical_errors
162 result
= CPPLINT_RE
[self
.fmt
].match(message
)
165 level
= int(result
.groupdict()['confidence'])
166 if level
>= self
.threshold
:
169 self
.logger
.info(message
)
171 self
.logger
.warning(message
)
173 self
.logger
.error(message
)
176 cpplint_logger
= None
177 def get_cpplint_logger(fmt
):
178 global cpplint_logger
180 return cpplint_logger
181 cpplint_logger
= logging
.getLogger('cpplint')
182 hdlr
= cpplint_handler()
183 hdlr
.setFormatter(cpplint_formatter(fmt
))
184 cpplint_logger
.addHandler(hdlr
)
185 cpplint_logger
.setLevel(logging
.DEBUG
)
186 return cpplint_logger
189 class cpplint(Task
.Task
):
192 def __init__(self
, *k
, **kw
):
193 super(cpplint
, self
).__init
__(*k
, **kw
)
196 global critical_errors
197 _cpplint_state
.SetFilters(self
.env
.CPPLINT_FILTERS
)
198 break_level
= self
.env
.CPPLINT_BREAK
199 verbosity
= self
.env
.CPPLINT_LEVEL
200 with
cpplint_wrapper(get_cpplint_logger(self
.env
.CPPLINT_OUTPUT
),
201 break_level
, self
.env
.CPPLINT_OUTPUT
):
202 ProcessFile(self
.inputs
[0].abspath(), verbosity
)
203 return critical_errors
206 @TaskGen.extension('.h', '.hh', '.hpp', '.hxx')
207 def cpplint_includes(self
, node
):
210 @TaskGen.feature('cpplint')
211 @TaskGen.before_method('process_source')
212 def run_cpplint(self
):
213 if not self
.env
.CPPLINT_INITIALIZED
:
214 self
.env
.CPPLINT_INITIALIZED
= True
215 init_env_from_options(self
.env
)
216 if self
.env
.CPPLINT_SKIP
:
218 if not self
.env
.CPPLINT_OUTPUT
in CPPLINT_RE
:
220 for src
in self
.to_list(getattr(self
, 'source', [])):
221 if isinstance(src
, str):
222 self
.create_task('cpplint', self
.path
.find_or_declare(src
))
224 self
.create_task('cpplint', src
)