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.

225 lines
7.2KB

  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. or, if you have waf >= 1.6.2
  10. $ ./waf update --files=cpplint
  11. this tool also requires cpplint for python.
  12. If you have PIP, you can install it like this: pip install cpplint
  13. But I'd recommend getting the latest version from the SVN,
  14. the PIP version is outdated.
  15. https://code.google.com/p/google-styleguide/source/browse/trunk/cpplint/cpplint.py
  16. Apply this patch if you want to run it with Python 3:
  17. https://code.google.com/p/google-styleguide/issues/detail?id=19
  18. When using this tool, the wscript will look like:
  19. def options(opt):
  20. opt.load('compiler_cxx cpplint')
  21. def configure(conf):
  22. conf.load('compiler_cxx cpplint')
  23. # optional, you can also specify them on the command line
  24. conf.env.CPPLINT_FILTERS = ','.join((
  25. '-whitespace/newline', # c++11 lambda
  26. '-readability/braces', # c++11 constructor
  27. '-whitespace/braces', # c++11 constructor
  28. '-build/storage_class', # c++11 for-range
  29. '-whitespace/blank_line', # user pref
  30. '-whitespace/labels' # user pref
  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. import sys, re
  38. import logging
  39. import threading
  40. from waflib import Task, Build, TaskGen, Logs, Utils
  41. try:
  42. from cpplint.cpplint import ProcessFile, _cpplint_state
  43. except ImportError:
  44. pass
  45. critical_errors = 0
  46. CPPLINT_FORMAT = '[CPPLINT] %(filename)s:\nline %(linenum)s, severity %(confidence)s, category: %(category)s\n%(message)s\n'
  47. RE_EMACS = re.compile('(?P<filename>.*):(?P<linenum>\d+): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]');
  48. CPPLINT_RE = {
  49. 'waf': RE_EMACS,
  50. 'emacs': RE_EMACS,
  51. 'vs7': re.compile('(?P<filename>.*)\((?P<linenum>\d+)\): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
  52. 'eclipse': re.compile('(?P<filename>.*):(?P<linenum>\d+): warning: (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
  53. }
  54. def init_env_from_options(env):
  55. from waflib.Options import options
  56. for key, value in options.__dict__.items():
  57. if not key.startswith('CPPLINT_') or env[key]:
  58. continue
  59. env[key] = value
  60. if env.CPPLINT_OUTPUT != 'waf':
  61. _cpplint_state.output_format = env.CPPLINT_OUTPUT
  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-level', default=1, type='int', dest='CPPLINT_LEVEL',
  67. help='specify the log level (default: 1)')
  68. opt.add_option('--cpplint-break', default=5, type='int', dest='CPPLINT_BREAK',
  69. help='break the build if error >= level (default: 5)')
  70. opt.add_option('--cpplint-skip', action='store_true',
  71. default=False, dest='CPPLINT_SKIP',
  72. help='skip cpplint during build')
  73. opt.add_option('--cpplint-output', type='string',
  74. default='waf', dest='CPPLINT_OUTPUT',
  75. help='select output format (waf, emacs, vs7)')
  76. def configure(conf):
  77. conf.start_msg('Checking cpplint')
  78. try:
  79. import cpplint
  80. conf.end_msg('ok')
  81. except ImportError:
  82. conf.env.CPPLINT_SKIP = True
  83. conf.end_msg('not found, skipping it.')
  84. class cpplint_formatter(Logs.formatter):
  85. def __init__(self, fmt):
  86. logging.Formatter.__init__(self, CPPLINT_FORMAT)
  87. self.fmt = fmt
  88. def format(self, rec):
  89. if self.fmt == 'waf':
  90. result = CPPLINT_RE[self.fmt].match(rec.msg).groupdict()
  91. rec.msg = CPPLINT_FORMAT % result
  92. if rec.levelno <= logging.INFO:
  93. rec.c1 = Logs.colors.CYAN
  94. return super(cpplint_formatter, self).format(rec)
  95. class cpplint_handler(Logs.log_handler):
  96. def __init__(self, stream=sys.stderr, **kw):
  97. super(cpplint_handler, self).__init__(stream, **kw)
  98. self.stream = stream
  99. def emit(self, rec):
  100. rec.stream = self.stream
  101. self.emit_override(rec)
  102. self.flush()
  103. class cpplint_wrapper(object):
  104. stream = None
  105. tasks_count = 0
  106. lock = threading.RLock()
  107. def __init__(self, logger, threshold, fmt):
  108. self.logger = logger
  109. self.threshold = threshold
  110. self.error_count = 0
  111. self.fmt = fmt
  112. def __enter__(self):
  113. with cpplint_wrapper.lock:
  114. cpplint_wrapper.tasks_count += 1
  115. if cpplint_wrapper.tasks_count == 1:
  116. sys.stderr.flush()
  117. cpplint_wrapper.stream = sys.stderr
  118. sys.stderr = self
  119. return self
  120. def __exit__(self, exc_type, exc_value, traceback):
  121. with cpplint_wrapper.lock:
  122. cpplint_wrapper.tasks_count -= 1
  123. if cpplint_wrapper.tasks_count == 0:
  124. sys.stderr = cpplint_wrapper.stream
  125. sys.stderr.flush()
  126. def isatty(self):
  127. return True
  128. def write(self, message):
  129. global critical_errors
  130. result = CPPLINT_RE[self.fmt].match(message)
  131. if not result:
  132. return
  133. level = int(result.groupdict()['confidence'])
  134. if level >= self.threshold:
  135. critical_errors += 1
  136. if level <= 2:
  137. self.logger.info(message)
  138. elif level <= 4:
  139. self.logger.warning(message)
  140. else:
  141. self.logger.error(message)
  142. cpplint_logger = None
  143. def get_cpplint_logger(fmt):
  144. global cpplint_logger
  145. if cpplint_logger:
  146. return cpplint_logger
  147. cpplint_logger = logging.getLogger('cpplint')
  148. hdlr = cpplint_handler()
  149. hdlr.setFormatter(cpplint_formatter(fmt))
  150. cpplint_logger.addHandler(hdlr)
  151. cpplint_logger.setLevel(logging.DEBUG)
  152. return cpplint_logger
  153. class cpplint(Task.Task):
  154. color = 'PINK'
  155. def __init__(self, *k, **kw):
  156. super(cpplint, self).__init__(*k, **kw)
  157. def run(self):
  158. global critical_errors
  159. _cpplint_state.SetFilters(self.env.CPPLINT_FILTERS)
  160. break_level = self.env.CPPLINT_BREAK
  161. verbosity = self.env.CPPLINT_LEVEL
  162. with cpplint_wrapper(get_cpplint_logger(self.env.CPPLINT_OUTPUT),
  163. break_level, self.env.CPPLINT_OUTPUT):
  164. ProcessFile(self.inputs[0].abspath(), verbosity)
  165. return critical_errors
  166. @TaskGen.extension('.h', '.hh', '.hpp', '.hxx')
  167. def cpplint_includes(self, node):
  168. pass
  169. @TaskGen.feature('cpplint')
  170. @TaskGen.before_method('process_source')
  171. def run_cpplint(self):
  172. if not self.env.CPPLINT_INITIALIZED:
  173. self.env.CPPLINT_INITIALIZED = True
  174. init_env_from_options(self.env)
  175. if self.env.CPPLINT_SKIP:
  176. return
  177. if not self.env.CPPLINT_OUTPUT in CPPLINT_RE:
  178. return
  179. for src in self.to_list(getattr(self, 'source', [])):
  180. if isinstance(src, str):
  181. self.create_task('cpplint', self.path.find_or_declare(src))
  182. else:
  183. self.create_task('cpplint', src)