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.

224 lines
7.0KB

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