5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
28 Test various cases where a target is "built" by multiple builder calls.
37 test = TestSCons.TestSCons(match=TestCmd.match_re)
39 _python_ = TestSCons._python_
42 # A builder with "multi" set can be called multiple times and
43 # the source files are added to the list.
46 test.write('SConstruct', """
47 def build(env, target, source):
48 file = open(str(target[0]), 'wb')
50 file.write(open(str(s), 'rb').read())
52 B = Builder(action=build, multi=1)
53 env = Environment(BUILDERS = { 'B' : B })
54 env.B(target = 'file1.out', source = 'file1a.in')
55 env.B(target = 'file1.out', source = 'file1b.in')
58 test.write('file1a.in', 'file1a.in\n')
59 test.write('file1b.in', 'file1b.in\n')
61 test.run(arguments='file1.out')
63 test.must_match('file1.out', "file1a.in\nfile1b.in\n")
67 # A builder with "multi" not set generates an error on the second call.
70 test.write('SConstruct', """
71 def build(env, target, source):
72 file = open(str(target[0]), 'wb')
74 file.write(open(str(s), 'rb').read())
76 B = Builder(action=build, multi=0)
77 env = Environment(BUILDERS = { 'B' : B })
78 env.B(target = 'file2.out', source = 'file2a.in')
79 env.B(target = 'file2.out', source = 'file2b.in')
82 test.write('file2a.in', 'file2a.in\n')
83 test.write('file2b.in', 'file2b.in\n')
85 test.run(arguments='file2.out',
87 stderr=TestSCons.re_escape("""
88 scons: *** Multiple ways to build the same target were specified for: file2.out (from ['file2a.in'] and from ['file2b.in'])
89 """) + TestSCons.file_expr)
93 # A warning is generated if the calls have different overrides but the
94 # overrides don't appear to affect the build operation.
97 test.write('SConstruct', """
98 def build(env, target, source):
99 file = open(str(target[0]), 'wb')
101 file.write(open(str(s), 'rb').read())
103 B = Builder(action=build, multi=1)
104 env = Environment(BUILDERS = { 'B' : B })
105 env.B(target = 'file3.out', source = 'file3a.in', foo=1)
106 env.B(target = 'file3.out', source = 'file3b.in', foo=2)
109 test.write('file3a.in', 'file3a.in\n')
110 test.write('file3b.in', 'file3b.in\n')
112 test.run(arguments='file3.out',
113 stderr=TestSCons.re_escape("""
114 scons: warning: Two different environments were specified for target file3.out,
115 \tbut they appear to have the same action: build(target, source, env)
116 """) + TestSCons.file_expr)
119 # A warning is generated if the calls have different overrides but the
120 # overrides don't appear to affect the build operation.
123 test.write('build.py',r"""#!/usr/bin/env python
125 def build(num, target, source):
126 file = open(str(target), 'wb')
127 file.write('%s\n'%num)
129 file.write(open(str(s), 'rb').read())
130 build(sys.argv[1],sys.argv[2],sys.argv[3:])
133 test.write('SConstruct', """
135 B = Builder(action='%(_python_)s build.py $foo $TARGET $SOURCES', multi=1)
136 env = Environment(BUILDERS = { 'B' : B })
137 env.B(target = 'file03.out', source = 'file03a.in', foo=1)
138 env.B(target = 'file03.out', source = 'file03b.in', foo=2)
141 test.write('file03a.in', 'file03a.in\n')
142 test.write('file03b.in', 'file03b.in\n')
144 test.run(arguments='file03.out',
146 stderr=TestSCons.re_escape("""
147 scons: *** Two environments with different actions were specified for the same target: file03.out
148 """) + TestSCons.file_expr)
151 # Everything works if the two calls have the same overrides.
154 test.write('build.py',r"""#!/usr/bin/env python
156 def build(num, target, source):
157 file = open(str(target), 'wb')
158 file.write('%s\n'%num)
160 file.write(open(str(s), 'rb').read())
161 build(sys.argv[1],sys.argv[2],sys.argv[3:])
164 test.write('SConstruct', """
166 B = Builder(action='%(_python_)s build.py $foo $TARGET $SOURCES', multi=1)
167 env = Environment(BUILDERS = { 'B' : B })
168 env.B(target = 'file4.out', source = 'file4a.in', foo=3)
169 env.B(target = 'file4.out', source = 'file4b.in', foo=3)
172 test.write('file4a.in', 'file4a.in\n')
173 test.write('file4b.in', 'file4b.in\n')
175 python_expr = string.replace(TestSCons.python, '\\', '\\\\')
176 act = TestSCons.re_escape('"%s" build.py \$foo \$TARGET \$SOURCES' % python_expr)
178 test.run(arguments='file4.out',
180 scons: warning: Two different environments were specified for target file4.out,
181 \tbut they appear to have the same action: %s
182 """ % act) + TestSCons.file_expr)
184 test.must_match('file4.out', "3\nfile4a.in\nfile4b.in\n")
188 # Two different environments can be used for the same target, so long
189 # as the actions have the same signature; a warning is generated.
192 test.write('SConstruct', """
193 def build(env, target, source):
194 file = open(str(target[0]), 'wb')
196 file.write(open(str(s), 'rb').read())
198 B = Builder(action=build, multi=1)
199 env = Environment(BUILDERS = { 'B' : B })
200 env2 = env.Clone(DIFFERENT_VARIABLE = 'true')
201 env.B(target = 'file5.out', source = 'file5a.in')
202 env2.B(target = 'file5.out', source = 'file5b.in')
205 test.write('file5a.in', 'file5a.in\n')
206 test.write('file5b.in', 'file5b.in\n')
208 test.run(arguments='file5.out',
209 stderr=TestSCons.re_escape("""
210 scons: warning: Two different environments were specified for target file5.out,
211 \tbut they appear to have the same action: build(target, source, env)
212 """) + TestSCons.file_expr)
214 test.must_match('file5.out', "file5a.in\nfile5b.in\n")
218 # Environments with actions that have different signatures generate
222 test.write('SConstruct', """
223 def build(env, target, source):
224 file = open(str(target[0]), 'wb')
226 file.write(open(str(s), 'rb').read())
228 B = Builder(action=Action(build, varlist=['XXX']), multi=1)
229 env = Environment(BUILDERS = { 'B' : B }, XXX = 'foo')
230 env2 = env.Clone(XXX = 'var')
231 env.B(target = 'file6.out', source = 'file6a.in')
232 env2.B(target = 'file6.out', source = 'file6b.in')
235 test.write('file6a.in', 'file6a.in\n')
236 test.write('file6b.in', 'file6b.in\n')
238 test.run(arguments='file6.out',
240 stderr=TestSCons.re_escape("""
241 scons: *** Two environments with different actions were specified for the same target: file6.out
242 """) + TestSCons.file_expr)
246 # A builder without "multi" set can still be called multiple times
247 # if the calls are the same.
250 test.write('SConstruct', """
251 def build(env, target, source):
252 file = open(str(target[0]), 'wb')
254 file.write(open(str(s), 'rb').read())
256 B = Builder(action=build, multi=0)
257 env = Environment(BUILDERS = { 'B' : B })
258 env.B(target = 'file7.out', source = 'file7.in')
259 env.B(target = 'file7.out', source = 'file7.in')
262 test.write('file7.in', 'file7.in\n')
264 test.run(arguments='file7.out')
266 test.must_match('file7.out', "file7.in\n")
270 # Trying to call a target with two different "multi" builders
271 # generates an error.
274 test.write('SConstruct', """
275 def build(env, target, source):
276 file = open(str(target[0]), 'wb')
278 file.write(open(str(s), 'rb').read())
280 def build2(env, target, source):
281 build(env, target, source)
283 B = Builder(action=build, multi=1)
284 C = Builder(action=build2, multi=1)
285 env = Environment(BUILDERS = { 'B' : B, 'C' : C })
286 env.B(target = 'file8.out', source = 'file8.in')
287 env.C(target = 'file8.out', source = 'file8.in')
290 test.write('file8a.in', 'file8a.in\n')
291 test.write('file8b.in', 'file8b.in\n')
293 test.run(arguments='file8.out',
295 stderr=TestSCons.re_escape("""
296 scons: *** Two different builders (B and C) were specified for the same target: file8.out
297 """) + TestSCons.file_expr)
301 # A "multi" builder can be called multiple times with the same target list
302 # if everything is identical.
305 test.write('SConstruct', """
306 def build(env, target, source):
308 file = open(str(t), 'wb')
310 file.write(open(str(s), 'rb').read())
312 B = Builder(action=build, multi=1)
313 env = Environment(BUILDERS = { 'B' : B })
314 env.B(target = ['file9a.out', 'file9b.out'], source = 'file9a.in')
315 env.B(target = ['file9a.out', 'file9b.out'], source = 'file9b.in')
318 test.write('file9a.in', 'file9a.in\n')
319 test.write('file9b.in', 'file9b.in\n')
321 test.run(arguments='file9b.out')
323 test.must_match('file9a.out', "file9a.in\nfile9b.in\n")
324 test.must_match('file9b.out', "file9a.in\nfile9b.in\n")
328 # A "multi" builder can NOT be called multiple times with target lists
329 # that have different orders. This is intentional; the order of the
330 # targets matter to the builder because the build command can contain
331 # things like ${TARGET[0]}.
334 test.write('SConstruct', """
335 def build(env, target, source):
337 file = open(str(target[0]), 'wb')
339 file.write(open(str(s), 'rb').read())
341 B = Builder(action=build, multi=1)
342 env = Environment(BUILDERS = { 'B' : B })
343 env.B(target = ['file10a.out', 'file10b.out'], source = 'file10.in')
344 env.B(target = ['file10b.out', 'file10a.out'], source = 'file10.in')
347 test.write('file10.in', 'file10.in\n')
349 test.run(arguments='file10.out',
351 stderr=TestSCons.re_escape("""
352 scons: *** Two different target sets have a target in common: file10b.out
353 """) + TestSCons.file_expr)
357 # A target file can't be in two different target lists.
360 # XXX It would be nice if the following two tests could be made to work
361 # by executing the action once for each unique set of targets. This
362 # would make it simple to deal with PDB files on Windows like so:
364 # env.Object(['foo.obj', 'vc60.pdb'], 'foo.c')
365 # env.Object(['bar.obj', 'vc60.pdb'], 'bar.c')
367 test.write('SConstruct', """
368 def build(env, target, source):
370 file = open(str(target[0]), 'wb')
372 file.write(open(str(s), 'rb').read())
374 B = Builder(action=build, multi=1)
375 env = Environment(BUILDERS = { 'B' : B })
376 env.B(target = ['file11a.out', 'file11b.out'], source = 'file11a.in')
377 env.B(target = ['file11b.out', 'file11c.out'], source = 'file11b.in')
380 test.write('file11a.in', 'file11a.in\n')
381 test.write('file11b.in', 'file11b.in\n')
383 test.run(arguments='file11.out',
385 stderr=TestSCons.re_escape("""
386 scons: *** Two different target sets have a target in common: file11b.out
387 """) + TestSCons.file_expr)
391 # A target file can't be a lone target and in a list.
394 test.write('SConstruct', """
395 def build(env, target, source):
397 file = open(str(target[0]), 'wb')
399 file.write(open(str(s), 'rb').read())
401 B = Builder(action=build, multi=1)
402 env = Environment(BUILDERS = { 'B' : B })
403 env.B(target = ['file12a.out', 'file12b.out'], source = 'file12a.in')
404 env.B(target = 'file12a.out', source = 'file12b.in')
407 test.write('file12a.in', 'file12a.in\n')
408 test.write('file12b.in', 'file12b.in\n')
410 test.run(arguments='file12.out',
412 stderr=TestSCons.re_escape("""
413 scons: *** Cannot build same target `file12a.out' as singular and list
414 """) + TestSCons.file_expr)