eba979b53289f151c9fd761a580a172f2a9059ca
[bbaumbach/samba-autobuild/.git] / third_party / waf / waflib / extras / cpplint.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #! /usr/bin/env python
6 # encoding: utf-8
7 #
8 # written by Sylvain Rouquette, 2014
9
10 '''
11
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
15
16 this tool also requires cpplint for python.
17 If you have PIP, you can install it like this: pip install cpplint
18
19 When using this tool, the wscript will look like:
20
21     def options(opt):
22         opt.load('compiler_cxx cpplint')
23
24     def configure(conf):
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
34             ))
35
36     def build(bld):
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'))
40 '''
41
42 from __future__ import absolute_import
43 import sys, re
44 import logging
45 import threading
46 from waflib import Task, TaskGen, Logs, Options, Node
47 try:
48     import cpplint.cpplint as cpplint_tool
49 except ImportError:
50     try:
51         import cpplint as cpplint_tool
52     except ImportError:
53         pass
54
55
56 critical_errors = 0
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+)\]')
59 CPPLINT_RE = {
60     'waf': RE_EMACS,
61     'emacs': RE_EMACS,
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+)\]'),
64 }
65
66 def options(opt):
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)')
86
87
88 def configure(conf):
89     conf.start_msg('Checking cpplint')
90     try:
91         cpplint_tool._cpplint_state
92         conf.end_msg('ok')
93     except NameError:
94         conf.env.CPPLINT_SKIP = True
95         conf.end_msg('not found, skipping it.')
96
97
98 class cpplint_formatter(Logs.formatter, object):
99     def __init__(self, fmt):
100         logging.Formatter.__init__(self, CPPLINT_FORMAT)
101         self.fmt = fmt
102
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)
110
111
112 class cpplint_handler(Logs.log_handler, object):
113     def __init__(self, stream=sys.stderr, **kw):
114         super(cpplint_handler, self).__init__(stream, **kw)
115         self.stream = stream
116
117     def emit(self, rec):
118         rec.stream = self.stream
119         self.emit_override(rec)
120         self.flush()
121
122
123 class cpplint_wrapper(object):
124     stream = None
125     tasks_count = 0
126     lock = threading.RLock()
127
128     def __init__(self, logger, threshold, fmt):
129         self.logger = logger
130         self.threshold = threshold
131         self.error_count = 0
132         self.fmt = fmt
133
134     def __enter__(self):
135         with cpplint_wrapper.lock:
136             cpplint_wrapper.tasks_count += 1
137             if cpplint_wrapper.tasks_count == 1:
138                 sys.stderr.flush()
139                 cpplint_wrapper.stream = sys.stderr
140                 sys.stderr = self
141             return self
142
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
148                 sys.stderr.flush()
149
150     def isatty(self):
151         return True
152
153     def write(self, message):
154         global critical_errors
155         result = CPPLINT_RE[self.fmt].match(message)
156         if not result:
157             return
158         level = int(result.groupdict()['confidence'])
159         if level >= self.threshold:
160             critical_errors += 1
161         if level <= 2:
162             self.logger.info(message)
163         elif level <= 4:
164             self.logger.warning(message)
165         else:
166             self.logger.error(message)
167
168
169 cpplint_logger = None
170 def get_cpplint_logger(fmt):
171     global cpplint_logger
172     if 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
180
181
182 class cpplint(Task.Task):
183     color = 'PINK'
184
185     def __init__(self, *k, **kw):
186         super(cpplint, self).__init__(*k, **kw)
187
188     def run(self):
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
198
199 @TaskGen.extension('.h', '.hh', '.hpp', '.hxx')
200 def cpplint_includes(self, node):
201     pass
202
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]:
209                 continue
210             self.env[key] = value
211         self.env.CPPLINT_INITIALIZED = True
212
213     if self.env.CPPLINT_SKIP:
214         return
215
216     if not self.env.CPPLINT_OUTPUT in CPPLINT_RE:
217         return
218
219     for src in self.to_list(getattr(self, 'source', [])):
220         if isinstance(src, Node.Node):
221             node = src
222         else:
223             node = self.path.find_or_declare(src)
224         if not node:
225             self.bld.fatal('Could not find %r' % src)
226         self.create_task('cpplint', node)