jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

210 lines
7.3KB

  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. #
  4. # written by Sylvain Rouquette, 2014
  5. '''
  6. This is an extra tool, not bundled with the default waf binary.
  7. To add the cpplint tool to the waf file:
  8. $ ./waf-light --tools=compat15,cpplint
  9. this tool also requires cpplint for python.
  10. If you have PIP, you can install it like this: pip install cpplint
  11. When using this tool, the wscript will look like:
  12. def options(opt):
  13. opt.load('compiler_cxx cpplint')
  14. def configure(conf):
  15. conf.load('compiler_cxx cpplint')
  16. # optional, you can also specify them on the command line
  17. conf.env.CPPLINT_FILTERS = ','.join((
  18. '-whitespace/newline', # c++11 lambda
  19. '-readability/braces', # c++11 constructor
  20. '-whitespace/braces', # c++11 constructor
  21. '-build/storage_class', # c++11 for-range
  22. '-whitespace/blank_line', # user pref
  23. '-whitespace/labels' # user pref
  24. ))
  25. def build(bld):
  26. bld(features='cpplint', source='main.cpp', target='app')
  27. # add include files, because they aren't usually built
  28. bld(features='cpplint', source=bld.path.ant_glob('**/*.hpp'))
  29. '''
  30. from __future__ import absolute_import
  31. import sys, re
  32. import logging
  33. from waflib import Errors, Task, TaskGen, Logs, Options, Node, Utils
  34. critical_errors = 0
  35. CPPLINT_FORMAT = '[CPPLINT] %(filename)s:\nline %(linenum)s, severity %(confidence)s, category: %(category)s\n%(message)s\n'
  36. RE_EMACS = re.compile('(?P<filename>.*):(?P<linenum>\d+): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]')
  37. CPPLINT_RE = {
  38. 'waf': RE_EMACS,
  39. 'emacs': RE_EMACS,
  40. 'vs7': re.compile('(?P<filename>.*)\((?P<linenum>\d+)\): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
  41. 'eclipse': re.compile('(?P<filename>.*):(?P<linenum>\d+): warning: (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
  42. }
  43. CPPLINT_STR = ('${CPPLINT} '
  44. '--verbose=${CPPLINT_LEVEL} '
  45. '--output=${CPPLINT_OUTPUT} '
  46. '--filter=${CPPLINT_FILTERS} '
  47. '--root=${CPPLINT_ROOT} '
  48. '--linelength=${CPPLINT_LINE_LENGTH} ')
  49. def options(opt):
  50. opt.add_option('--cpplint-filters', type='string',
  51. default='', dest='CPPLINT_FILTERS',
  52. help='add filters to cpplint')
  53. opt.add_option('--cpplint-length', type='int',
  54. default=80, dest='CPPLINT_LINE_LENGTH',
  55. help='specify the line length (default: 80)')
  56. opt.add_option('--cpplint-level', default=1, type='int', dest='CPPLINT_LEVEL',
  57. help='specify the log level (default: 1)')
  58. opt.add_option('--cpplint-break', default=5, type='int', dest='CPPLINT_BREAK',
  59. help='break the build if error >= level (default: 5)')
  60. opt.add_option('--cpplint-root', type='string',
  61. default='', dest='CPPLINT_ROOT',
  62. help='root directory used to derive header guard')
  63. opt.add_option('--cpplint-skip', action='store_true',
  64. default=False, dest='CPPLINT_SKIP',
  65. help='skip cpplint during build')
  66. opt.add_option('--cpplint-output', type='string',
  67. default='waf', dest='CPPLINT_OUTPUT',
  68. help='select output format (waf, emacs, vs7, eclipse)')
  69. def configure(conf):
  70. try:
  71. conf.find_program('cpplint', var='CPPLINT')
  72. except Errors.ConfigurationError:
  73. conf.env.CPPLINT_SKIP = True
  74. class cpplint_formatter(Logs.formatter, object):
  75. def __init__(self, fmt):
  76. logging.Formatter.__init__(self, CPPLINT_FORMAT)
  77. self.fmt = fmt
  78. def format(self, rec):
  79. if self.fmt == 'waf':
  80. result = CPPLINT_RE[self.fmt].match(rec.msg).groupdict()
  81. rec.msg = CPPLINT_FORMAT % result
  82. if rec.levelno <= logging.INFO:
  83. rec.c1 = Logs.colors.CYAN
  84. return super(cpplint_formatter, self).format(rec)
  85. class cpplint_handler(Logs.log_handler, object):
  86. def __init__(self, stream=sys.stderr, **kw):
  87. super(cpplint_handler, self).__init__(stream, **kw)
  88. self.stream = stream
  89. def emit(self, rec):
  90. rec.stream = self.stream
  91. self.emit_override(rec)
  92. self.flush()
  93. class cpplint_wrapper(object):
  94. def __init__(self, logger, threshold, fmt):
  95. self.logger = logger
  96. self.threshold = threshold
  97. self.fmt = fmt
  98. def __enter__(self):
  99. return self
  100. def __exit__(self, exc_type, exc_value, traceback):
  101. if isinstance(exc_value, Utils.subprocess.CalledProcessError):
  102. messages = [m for m in exc_value.output.splitlines()
  103. if 'Done processing' not in m
  104. and 'Total errors found' not in m]
  105. for message in messages:
  106. self.write(message)
  107. return True
  108. def write(self, message):
  109. global critical_errors
  110. result = CPPLINT_RE[self.fmt].match(message)
  111. if not result:
  112. return
  113. level = int(result.groupdict()['confidence'])
  114. if level >= self.threshold:
  115. critical_errors += 1
  116. if level <= 2:
  117. self.logger.info(message)
  118. elif level <= 4:
  119. self.logger.warning(message)
  120. else:
  121. self.logger.error(message)
  122. cpplint_logger = None
  123. def get_cpplint_logger(fmt):
  124. global cpplint_logger
  125. if cpplint_logger:
  126. return cpplint_logger
  127. cpplint_logger = logging.getLogger('cpplint')
  128. hdlr = cpplint_handler()
  129. hdlr.setFormatter(cpplint_formatter(fmt))
  130. cpplint_logger.addHandler(hdlr)
  131. cpplint_logger.setLevel(logging.DEBUG)
  132. return cpplint_logger
  133. class cpplint(Task.Task):
  134. color = 'PINK'
  135. def __init__(self, *k, **kw):
  136. super(cpplint, self).__init__(*k, **kw)
  137. def run(self):
  138. global critical_errors
  139. with cpplint_wrapper(get_cpplint_logger(self.env.CPPLINT_OUTPUT), self.env.CPPLINT_BREAK, self.env.CPPLINT_OUTPUT):
  140. params = {key: str(self.env[key]) for key in self.env if 'CPPLINT_' in key}
  141. if params['CPPLINT_OUTPUT'] is 'waf':
  142. params['CPPLINT_OUTPUT'] = 'emacs'
  143. params['CPPLINT'] = self.env.get_flat('CPPLINT')
  144. cmd = Utils.subst_vars(CPPLINT_STR, params)
  145. env = self.env.env or None
  146. Utils.subprocess.check_output(cmd + self.inputs[0].abspath(),
  147. stderr=Utils.subprocess.STDOUT,
  148. env=env, shell=True)
  149. return critical_errors
  150. @TaskGen.extension('.h', '.hh', '.hpp', '.hxx')
  151. def cpplint_includes(self, node):
  152. pass
  153. @TaskGen.feature('cpplint')
  154. @TaskGen.before_method('process_source')
  155. def post_cpplint(self):
  156. if not self.env.CPPLINT_INITIALIZED:
  157. for key, value in Options.options.__dict__.items():
  158. if not key.startswith('CPPLINT_') or self.env[key]:
  159. continue
  160. self.env[key] = value
  161. self.env.CPPLINT_INITIALIZED = True
  162. if self.env.CPPLINT_SKIP:
  163. return
  164. if not self.env.CPPLINT_OUTPUT in CPPLINT_RE:
  165. return
  166. for src in self.to_list(getattr(self, 'source', [])):
  167. if isinstance(src, Node.Node):
  168. node = src
  169. else:
  170. node = self.path.find_or_declare(src)
  171. if not node:
  172. self.bld.fatal('Could not find %r' % src)
  173. self.create_task('cpplint', node)