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.

221 lines
6.7KB

  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. # Thomas Nagy, 2011 (ita)
  4. """
  5. errcheck: highlight common mistakes
  6. There is a performance hit, so this tool is only loaded when running "waf -v"
  7. """
  8. typos = {
  9. 'feature':'features',
  10. 'sources':'source',
  11. 'targets':'target',
  12. 'include':'includes',
  13. 'export_include':'export_includes',
  14. 'define':'defines',
  15. 'importpath':'includes',
  16. 'installpath':'install_path',
  17. 'iscopy':'is_copy',
  18. }
  19. meths_typos = ['__call__', 'program', 'shlib', 'stlib', 'objects']
  20. from waflib import Logs, Build, Node, Task, TaskGen, ConfigSet, Errors, Utils
  21. import waflib.Tools.ccroot
  22. def check_same_targets(self):
  23. mp = Utils.defaultdict(list)
  24. uids = {}
  25. def check_task(tsk):
  26. if not isinstance(tsk, Task.Task):
  27. return
  28. for node in tsk.outputs:
  29. mp[node].append(tsk)
  30. try:
  31. uids[tsk.uid()].append(tsk)
  32. except KeyError:
  33. uids[tsk.uid()] = [tsk]
  34. for g in self.groups:
  35. for tg in g:
  36. try:
  37. for tsk in tg.tasks:
  38. check_task(tsk)
  39. except AttributeError:
  40. # raised if not a task generator, which should be uncommon
  41. check_task(tg)
  42. dupe = False
  43. for (k, v) in mp.items():
  44. if len(v) > 1:
  45. dupe = True
  46. msg = '* Node %r is created more than once%s. The task generators are:' % (k, Logs.verbose == 1 and " (full message on 'waf -v -v')" or "")
  47. Logs.error(msg)
  48. for x in v:
  49. if Logs.verbose > 1:
  50. Logs.error(' %d. %r' % (1 + v.index(x), x.generator))
  51. else:
  52. Logs.error(' %d. %r in %r' % (1 + v.index(x), x.generator.name, getattr(x.generator, 'path', None)))
  53. if not dupe:
  54. for (k, v) in uids.items():
  55. if len(v) > 1:
  56. Logs.error('* Several tasks use the same identifier. Please check the information on\n http://docs.waf.googlecode.com/git/apidocs_16/Task.html#waflib.Task.Task.uid')
  57. for tsk in v:
  58. Logs.error(' - object %r (%r) defined in %r' % (tsk.__class__.__name__, tsk, tsk.generator))
  59. def check_invalid_constraints(self):
  60. feat = set([])
  61. for x in list(TaskGen.feats.values()):
  62. feat.union(set(x))
  63. for (x, y) in TaskGen.task_gen.prec.items():
  64. feat.add(x)
  65. feat.union(set(y))
  66. ext = set([])
  67. for x in TaskGen.task_gen.mappings.values():
  68. ext.add(x.__name__)
  69. invalid = ext & feat
  70. if invalid:
  71. Logs.error('The methods %r have invalid annotations: @extension <-> @feature/@before_method/@after_method' % list(invalid))
  72. # the build scripts have been read, so we can check for invalid after/before attributes on task classes
  73. for cls in list(Task.classes.values()):
  74. for x in ('before', 'after'):
  75. for y in Utils.to_list(getattr(cls, x, [])):
  76. if not Task.classes.get(y, None):
  77. Logs.error('Erroneous order constraint %r=%r on task class %r' % (x, y, cls.__name__))
  78. if getattr(cls, 'rule', None):
  79. Logs.error('Erroneous attribute "rule" on task class %r (rename to "run_str")' % cls.__name__)
  80. def replace(m):
  81. """
  82. We could add properties, but they would not work in some cases:
  83. bld.program(...) requires 'source' in the attributes
  84. """
  85. oldcall = getattr(Build.BuildContext, m)
  86. def call(self, *k, **kw):
  87. ret = oldcall(self, *k, **kw)
  88. for x in typos:
  89. if x in kw:
  90. if x == 'iscopy' and 'subst' in getattr(self, 'features', ''):
  91. continue
  92. err = True
  93. Logs.error('Fix the typo %r -> %r on %r' % (x, typos[x], ret))
  94. return ret
  95. setattr(Build.BuildContext, m, call)
  96. def enhance_lib():
  97. """
  98. modify existing classes and methods
  99. """
  100. for m in meths_typos:
  101. replace(m)
  102. # catch '..' in ant_glob patterns
  103. def ant_glob(self, *k, **kw):
  104. if k:
  105. lst=Utils.to_list(k[0])
  106. for pat in lst:
  107. if '..' in pat.split('/'):
  108. Logs.error("In ant_glob pattern %r: '..' means 'two dots', not 'parent directory'" % k[0])
  109. if kw.get('remove', True):
  110. try:
  111. if self.is_child_of(self.ctx.bldnode) and not kw.get('quiet', False):
  112. Logs.error('Using ant_glob on the build folder (%r) is dangerous (quiet=True to disable this warning)' % self)
  113. except AttributeError:
  114. pass
  115. return self.old_ant_glob(*k, **kw)
  116. Node.Node.old_ant_glob = Node.Node.ant_glob
  117. Node.Node.ant_glob = ant_glob
  118. # catch conflicting ext_in/ext_out/before/after declarations
  119. old = Task.is_before
  120. def is_before(t1, t2):
  121. ret = old(t1, t2)
  122. if ret and old(t2, t1):
  123. Logs.error('Contradictory order constraints in classes %r %r' % (t1, t2))
  124. return ret
  125. Task.is_before = is_before
  126. # check for bld(feature='cshlib') where no 'c' is given - this can be either a mistake or on purpose
  127. # so we only issue a warning
  128. def check_err_features(self):
  129. lst = self.to_list(self.features)
  130. if 'shlib' in lst:
  131. Logs.error('feature shlib -> cshlib, dshlib or cxxshlib')
  132. for x in ('c', 'cxx', 'd', 'fc'):
  133. if not x in lst and lst and lst[0] in [x+y for y in ('program', 'shlib', 'stlib')]:
  134. Logs.error('%r features is probably missing %r' % (self, x))
  135. TaskGen.feature('*')(check_err_features)
  136. # check for erroneous order constraints
  137. def check_err_order(self):
  138. if not hasattr(self, 'rule') and not 'subst' in Utils.to_list(self.features):
  139. for x in ('before', 'after', 'ext_in', 'ext_out'):
  140. if hasattr(self, x):
  141. Logs.warn('Erroneous order constraint %r on non-rule based task generator %r' % (x, self))
  142. else:
  143. for x in ('before', 'after'):
  144. for y in self.to_list(getattr(self, x, [])):
  145. if not Task.classes.get(y, None):
  146. Logs.error('Erroneous order constraint %s=%r on %r (no such class)' % (x, y, self))
  147. TaskGen.feature('*')(check_err_order)
  148. # check for @extension used with @feature/@before_method/@after_method
  149. def check_compile(self):
  150. check_invalid_constraints(self)
  151. try:
  152. ret = self.orig_compile()
  153. finally:
  154. check_same_targets(self)
  155. return ret
  156. Build.BuildContext.orig_compile = Build.BuildContext.compile
  157. Build.BuildContext.compile = check_compile
  158. # check for invalid build groups #914
  159. def use_rec(self, name, **kw):
  160. try:
  161. y = self.bld.get_tgen_by_name(name)
  162. except Errors.WafError:
  163. pass
  164. else:
  165. idx = self.bld.get_group_idx(self)
  166. odx = self.bld.get_group_idx(y)
  167. if odx > idx:
  168. msg = "Invalid 'use' across build groups:"
  169. if Logs.verbose > 1:
  170. msg += '\n target %r\n uses:\n %r' % (self, y)
  171. else:
  172. msg += " %r uses %r (try 'waf -v -v' for the full error)" % (self.name, name)
  173. raise Errors.WafError(msg)
  174. self.orig_use_rec(name, **kw)
  175. TaskGen.task_gen.orig_use_rec = TaskGen.task_gen.use_rec
  176. TaskGen.task_gen.use_rec = use_rec
  177. # check for env.append
  178. def getattri(self, name, default=None):
  179. if name == 'append' or name == 'add':
  180. raise Errors.WafError('env.append and env.add do not exist: use env.append_value/env.append_unique')
  181. elif name == 'prepend':
  182. raise Errors.WafError('env.prepend does not exist: use env.prepend_value')
  183. if name in self.__slots__:
  184. return object.__getattr__(self, name, default)
  185. else:
  186. return self[name]
  187. ConfigSet.ConfigSet.__getattr__ = getattri
  188. def options(opt):
  189. """
  190. Add a few methods
  191. """
  192. enhance_lib()
  193. def configure(conf):
  194. pass