third_party/waf: upgrade to waf 2.0.8
[bbaumbach/samba-autobuild/.git] / third_party / waf / waflib / extras / cpplint.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 #
4 # written by Sylvain Rouquette, 2014
5
6 '''
7
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
12 this tool also requires cpplint for python.
13 If you have PIP, you can install it like this: pip install cpplint
14
15 When using this tool, the wscript will look like:
16
17     def options(opt):
18         opt.load('compiler_cxx cpplint')
19
20     def configure(conf):
21         conf.load('compiler_cxx cpplint')
22         # optional, you can also specify them on the command line
23         conf.env.CPPLINT_FILTERS = ','.join((
24             '-whitespace/newline',      # c++11 lambda
25             '-readability/braces',      # c++11 constructor
26             '-whitespace/braces',       # c++11 constructor
27             '-build/storage_class',     # c++11 for-range
28             '-whitespace/blank_line',   # user pref
29             '-whitespace/labels'        # user pref
30             ))
31
32     def build(bld):
33         bld(features='cpplint', source='main.cpp', target='app')
34         # add include files, because they aren't usually built
35         bld(features='cpplint', source=bld.path.ant_glob('**/*.hpp'))
36 '''
37
38 from __future__ import absolute_import
39 import sys, re
40 import logging
41 import threading
42 from waflib import Task, TaskGen, Logs, Options, Node
43 try:
44     import cpplint.cpplint as cpplint_tool
45 except ImportError:
46     try:
47         import cpplint as cpplint_tool
48     except ImportError:
49         pass
50
51
52 critical_errors = 0
53 CPPLINT_FORMAT = '[CPPLINT] %(filename)s:\nline %(linenum)s, severity %(confidence)s, category: %(category)s\n%(message)s\n'
54 RE_EMACS = re.compile('(?P<filename>.*):(?P<linenum>\d+):  (?P<message>.*)  \[(?P<category>.*)\] \[(?P<confidence>\d+)\]')
55 CPPLINT_RE = {
56     'waf': RE_EMACS,
57     'emacs': RE_EMACS,
58     'vs7': re.compile('(?P<filename>.*)\((?P<linenum>\d+)\):  (?P<message>.*)  \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
59     'eclipse': re.compile('(?P<filename>.*):(?P<linenum>\d+): warning: (?P<message>.*)  \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
60 }
61
62 def options(opt):
63     opt.add_option('--cpplint-filters', type='string',
64                    default='', dest='CPPLINT_FILTERS',
65                    help='add filters to cpplint')
66     opt.add_option('--cpplint-length', type='int',
67                    default=80, dest='CPPLINT_LINE_LENGTH',
68                    help='specify the line length (default: 80)')
69     opt.add_option('--cpplint-level', default=1, type='int', dest='CPPLINT_LEVEL',
70                    help='specify the log level (default: 1)')
71     opt.add_option('--cpplint-break', default=5, type='int', dest='CPPLINT_BREAK',
72                    help='break the build if error >= level (default: 5)')
73     opt.add_option('--cpplint-root', type='string',
74                    default=None, dest='CPPLINT_ROOT',
75                    help='root directory used to derive header guard')
76     opt.add_option('--cpplint-skip', action='store_true',
77                    default=False, dest='CPPLINT_SKIP',
78                    help='skip cpplint during build')
79     opt.add_option('--cpplint-output', type='string',
80                    default='waf', dest='CPPLINT_OUTPUT',
81                    help='select output format (waf, emacs, vs7)')
82
83
84 def configure(conf):
85     conf.start_msg('Checking cpplint')
86     try:
87         cpplint_tool._cpplint_state
88         conf.end_msg('ok')
89     except NameError:
90         conf.env.CPPLINT_SKIP = True
91         conf.end_msg('not found, skipping it.')
92
93
94 class cpplint_formatter(Logs.formatter, object):
95     def __init__(self, fmt):
96         logging.Formatter.__init__(self, CPPLINT_FORMAT)
97         self.fmt = fmt
98
99     def format(self, rec):
100         if self.fmt == 'waf':
101             result = CPPLINT_RE[self.fmt].match(rec.msg).groupdict()
102             rec.msg = CPPLINT_FORMAT % result
103         if rec.levelno <= logging.INFO:
104             rec.c1 = Logs.colors.CYAN
105         return super(cpplint_formatter, self).format(rec)
106
107
108 class cpplint_handler(Logs.log_handler, object):
109     def __init__(self, stream=sys.stderr, **kw):
110         super(cpplint_handler, self).__init__(stream, **kw)
111         self.stream = stream
112
113     def emit(self, rec):
114         rec.stream = self.stream
115         self.emit_override(rec)
116         self.flush()
117
118
119 class cpplint_wrapper(object):
120     stream = None
121     tasks_count = 0
122     lock = threading.RLock()
123
124     def __init__(self, logger, threshold, fmt):
125         self.logger = logger
126         self.threshold = threshold
127         self.error_count = 0
128         self.fmt = fmt
129
130     def __enter__(self):
131         with cpplint_wrapper.lock:
132             cpplint_wrapper.tasks_count += 1
133             if cpplint_wrapper.tasks_count == 1:
134                 sys.stderr.flush()
135                 cpplint_wrapper.stream = sys.stderr
136                 sys.stderr = self
137             return self
138
139     def __exit__(self, exc_type, exc_value, traceback):
140         with cpplint_wrapper.lock:
141             cpplint_wrapper.tasks_count -= 1
142             if cpplint_wrapper.tasks_count == 0:
143                 sys.stderr = cpplint_wrapper.stream
144                 sys.stderr.flush()
145
146     def isatty(self):
147         return True
148
149     def write(self, message):
150         global critical_errors
151         result = CPPLINT_RE[self.fmt].match(message)
152         if not result:
153             return
154         level = int(result.groupdict()['confidence'])
155         if level >= self.threshold:
156             critical_errors += 1
157         if level <= 2:
158             self.logger.info(message)
159         elif level <= 4:
160             self.logger.warning(message)
161         else:
162             self.logger.error(message)
163
164
165 cpplint_logger = None
166 def get_cpplint_logger(fmt):
167     global cpplint_logger
168     if cpplint_logger:
169         return cpplint_logger
170     cpplint_logger = logging.getLogger('cpplint')
171     hdlr = cpplint_handler()
172     hdlr.setFormatter(cpplint_formatter(fmt))
173     cpplint_logger.addHandler(hdlr)
174     cpplint_logger.setLevel(logging.DEBUG)
175     return cpplint_logger
176
177
178 class cpplint(Task.Task):
179     color = 'PINK'
180
181     def __init__(self, *k, **kw):
182         super(cpplint, self).__init__(*k, **kw)
183
184     def run(self):
185         global critical_errors
186         with cpplint_wrapper(get_cpplint_logger(self.env.CPPLINT_OUTPUT), self.env.CPPLINT_BREAK, self.env.CPPLINT_OUTPUT):
187             if self.env.CPPLINT_OUTPUT != 'waf':
188                 cpplint_tool._SetOutputFormat(self.env.CPPLINT_OUTPUT)
189             cpplint_tool._SetFilters(self.env.CPPLINT_FILTERS)
190             cpplint_tool._line_length = self.env.CPPLINT_LINE_LENGTH
191             cpplint_tool._root = self.env.CPPLINT_ROOT
192             cpplint_tool.ProcessFile(self.inputs[0].abspath(), self.env.CPPLINT_LEVEL)
193         return critical_errors
194
195 @TaskGen.extension('.h', '.hh', '.hpp', '.hxx')
196 def cpplint_includes(self, node):
197     pass
198
199 @TaskGen.feature('cpplint')
200 @TaskGen.before_method('process_source')
201 def post_cpplint(self):
202     if not self.env.CPPLINT_INITIALIZED:
203         for key, value in Options.options.__dict__.items():
204             if not key.startswith('CPPLINT_') or self.env[key]:
205                 continue
206             self.env[key] = value
207         self.env.CPPLINT_INITIALIZED = True
208
209     if self.env.CPPLINT_SKIP:
210         return
211
212     if not self.env.CPPLINT_OUTPUT in CPPLINT_RE:
213         return
214
215     for src in self.to_list(getattr(self, 'source', [])):
216         if isinstance(src, Node.Node):
217             node = src
218         else:
219             node = self.path.find_or_declare(src)
220         if not node:
221             self.bld.fatal('Could not find %r' % src)
222         self.create_task('cpplint', node)