3 # Thomas Nagy, 2011 (ita)
6 Common mistakes highlighting.
8 There is a performance impact, so this tool is only loaded when running ``waf -v``
16 'export_include':'export_includes',
18 'importpath':'includes',
19 'installpath':'install_path',
24 meths_typos
= ['__call__', 'program', 'shlib', 'stlib', 'objects']
27 from waflib
import Logs
, Build
, Node
, Task
, TaskGen
, ConfigSet
, Errors
, Utils
28 from waflib
.Tools
import ccroot
30 def check_same_targets(self
):
31 mp
= Utils
.defaultdict(list)
35 if not isinstance(tsk
, Task
.Task
):
37 if hasattr(tsk
, 'no_errcheck_out'):
40 for node
in tsk
.outputs
:
43 uids
[tsk
.uid()].append(tsk
)
45 uids
[tsk
.uid()] = [tsk
]
52 except AttributeError:
53 # raised if not a task generator, which should be uncommon
57 for (k
, v
) in mp
.items():
60 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 "")
64 Logs
.error(' %d. %r', 1 + v
.index(x
), x
.generator
)
66 Logs
.error(' %d. %r in %r', 1 + v
.index(x
), x
.generator
.name
, getattr(x
.generator
, 'path', None))
67 Logs
.error('If you think that this is an error, set no_errcheck_out on the task instance')
70 for (k
, v
) in uids
.items():
72 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')
73 tg_details
= tsk
.generator
.name
75 tg_details
= tsk
.generator
77 Logs
.error(' - object %r (%r) defined in %r', tsk
.__class
__.__name
__, tsk
, tg_details
)
79 def check_invalid_constraints(self
):
81 for x
in list(TaskGen
.feats
.values()):
83 for (x
, y
) in TaskGen
.task_gen
.prec
.items():
87 for x
in TaskGen
.task_gen
.mappings
.values():
91 Logs
.error('The methods %r have invalid annotations: @extension <-> @feature/@before_method/@after_method', list(invalid
))
93 # the build scripts have been read, so we can check for invalid after/before attributes on task classes
94 for cls
in list(Task
.classes
.values()):
95 if sys
.hexversion
> 0x3000000 and issubclass(cls
, Task
.Task
) and isinstance(cls
.hcode
, str):
96 raise Errors
.WafError('Class %r has hcode value %r of type <str>, expecting <bytes> (use Utils.h_cmd() ?)' % (cls
, cls
.hcode
))
98 for x
in ('before', 'after'):
99 for y
in Utils
.to_list(getattr(cls
, x
, [])):
100 if not Task
.classes
.get(y
):
101 Logs
.error('Erroneous order constraint %r=%r on task class %r', x
, y
, cls
.__name
__)
102 if getattr(cls
, 'rule', None):
103 Logs
.error('Erroneous attribute "rule" on task class %r (rename to "run_str")', cls
.__name
__)
107 Replaces existing BuildContext methods to verify parameter names,
108 for example ``bld(source=)`` has no ending *s*
110 oldcall
= getattr(Build
.BuildContext
, m
)
111 def call(self
, *k
, **kw
):
112 ret
= oldcall(self
, *k
, **kw
)
115 if x
== 'iscopy' and 'subst' in getattr(self
, 'features', ''):
117 Logs
.error('Fix the typo %r -> %r on %r', x
, typos
[x
], ret
)
119 setattr(Build
.BuildContext
, m
, call
)
123 Modifies existing classes and methods to enable error verification
125 for m
in meths_typos
:
128 # catch '..' in ant_glob patterns
129 def ant_glob(self
, *k
, **kw
):
131 lst
= Utils
.to_list(k
[0])
135 Logs
.error("In ant_glob pattern %r: '..' means 'two dots', not 'parent directory'", k
[0])
137 Logs
.error("In ant_glob pattern %r: '.' means 'one dot', not 'current directory'", k
[0])
138 return self
.old_ant_glob(*k
, **kw
)
139 Node
.Node
.old_ant_glob
= Node
.Node
.ant_glob
140 Node
.Node
.ant_glob
= ant_glob
142 # catch ant_glob on build folders
143 def ant_iter(self
, accept
=None, maxdepth
=25, pats
=[], dir=False, src
=True, remove
=True, quiet
=False):
146 if self
.is_child_of(self
.ctx
.bldnode
) and not quiet
:
148 Logs
.error('Calling ant_glob on build folders (%r) is dangerous: add quiet=True / remove=False', self
)
149 except AttributeError:
151 return self
.old_ant_iter(accept
, maxdepth
, pats
, dir, src
, remove
, quiet
)
152 Node
.Node
.old_ant_iter
= Node
.Node
.ant_iter
153 Node
.Node
.ant_iter
= ant_iter
155 # catch conflicting ext_in/ext_out/before/after declarations
157 def is_before(t1
, t2
):
159 if ret
and old(t2
, t1
):
160 Logs
.error('Contradictory order constraints in classes %r %r', t1
, t2
)
162 Task
.is_before
= is_before
164 # check for bld(feature='cshlib') where no 'c' is given - this can be either a mistake or on purpose
165 # so we only issue a warning
166 def check_err_features(self
):
167 lst
= self
.to_list(self
.features
)
169 Logs
.error('feature shlib -> cshlib, dshlib or cxxshlib')
170 for x
in ('c', 'cxx', 'd', 'fc'):
171 if not x
in lst
and lst
and lst
[0] in [x
+y
for y
in ('program', 'shlib', 'stlib')]:
172 Logs
.error('%r features is probably missing %r', self
, x
)
173 TaskGen
.feature('*')(check_err_features
)
175 # check for erroneous order constraints
176 def check_err_order(self
):
177 if not hasattr(self
, 'rule') and not 'subst' in Utils
.to_list(self
.features
):
178 for x
in ('before', 'after', 'ext_in', 'ext_out'):
180 Logs
.warn('Erroneous order constraint %r on non-rule based task generator %r', x
, self
)
182 for x
in ('before', 'after'):
183 for y
in self
.to_list(getattr(self
, x
, [])):
184 if not Task
.classes
.get(y
):
185 Logs
.error('Erroneous order constraint %s=%r on %r (no such class)', x
, y
, self
)
186 TaskGen
.feature('*')(check_err_order
)
188 # check for @extension used with @feature/@before_method/@after_method
189 def check_compile(self
):
190 check_invalid_constraints(self
)
192 ret
= self
.orig_compile()
194 check_same_targets(self
)
196 Build
.BuildContext
.orig_compile
= Build
.BuildContext
.compile
197 Build
.BuildContext
.compile = check_compile
199 # check for invalid build groups #914
200 def use_rec(self
, name
, **kw
):
202 y
= self
.bld
.get_tgen_by_name(name
)
203 except Errors
.WafError
:
206 idx
= self
.bld
.get_group_idx(self
)
207 odx
= self
.bld
.get_group_idx(y
)
209 msg
= "Invalid 'use' across build groups:"
211 msg
+= '\n target %r\n uses:\n %r' % (self
, y
)
213 msg
+= " %r uses %r (try 'waf -v -v' for the full error)" % (self
.name
, name
)
214 raise Errors
.WafError(msg
)
215 self
.orig_use_rec(name
, **kw
)
216 TaskGen
.task_gen
.orig_use_rec
= TaskGen
.task_gen
.use_rec
217 TaskGen
.task_gen
.use_rec
= use_rec
219 # check for env.append
220 def _getattr(self
, name
, default
=None):
221 if name
== 'append' or name
== 'add':
222 raise Errors
.WafError('env.append and env.add do not exist: use env.append_value/env.append_unique')
223 elif name
== 'prepend':
224 raise Errors
.WafError('env.prepend does not exist: use env.prepend_value')
225 if name
in self
.__slots
__:
226 return super(ConfigSet
.ConfigSet
, self
).__getattr
__(name
, default
)
229 ConfigSet
.ConfigSet
.__getattr
__ = _getattr
234 Error verification can be enabled by default (not just on ``waf -v``) by adding to the user script options