54776ffdbe0653938bec95a76039113a74fca481
[scons.git] / src / engine / SCons / BuilderTests.py
1 #
2 # Copyright (c) 2001 Steven Knight
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 # Define a null function for use as a builder action.
27 # Where this is defined in the file seems to affect its
28 # byte-code contents, so try to minimize changes by
29 # defining it here, before we even import anything.
30 def Func():
31     pass
32
33 import sys
34 import unittest
35
36 import TestCmd
37 import SCons.Builder
38
39 # Initial setup of the common environment for all tests,
40 # a temporary working directory containing a
41 # script for writing arguments to an output file.
42 #
43 # We don't do this as a setUp() method because it's
44 # unnecessary to create a separate directory and script
45 # for each test, they can just use the one.
46 test = TestCmd.TestCmd(workdir = '')
47
48 test.write('act.py', """import os, string, sys
49 f = open(sys.argv[1], 'w')
50 f.write("act.py: '" + string.join(sys.argv[2:], "' '") + "'\\n")
51 try:
52     if sys.argv[3]:
53         f.write("act.py: '" + os.environ[sys.argv[3]] + "'\\n")
54 except:
55     pass
56 f.close()
57 sys.exit(0)
58 """)
59
60 act_py = test.workpath('act.py')
61 outfile = test.workpath('outfile')
62
63 show_string = None
64 instanced = None
65
66 class Environment:
67             def subst(self, s):
68                 return s
69 env = Environment()
70
71 class BuilderTestCase(unittest.TestCase):
72
73     def test__call__(self):
74         """Test calling a builder to establish source dependencies
75         """
76         class Node:
77             def __init__(self, name):
78                 self.name = name
79                 self.sources = []
80                 self.builder = None
81             def __str__(self):
82                 return self.name
83             def builder_set(self, builder):
84                 self.builder = builder
85             def env_set(self, env, safe=0):
86                 self.env = env
87             def add_source(self, source):
88                 self.sources.extend(source)
89         builder = SCons.Builder.Builder(action = "foo")
90         n1 = Node("n1");
91         n2 = Node("n2");
92         builder(env, target = n1, source = n2)
93         assert n1.env == env
94         assert n1.builder == builder
95         assert n1.sources == [n2]
96         assert n2.env == env
97
98     def test_action(self):
99         """Test Builder creation
100
101         Verify that we can retrieve the supplied action attribute.
102         """
103         builder = SCons.Builder.Builder(action = "foo")
104         assert builder.action.command == "foo"
105
106     def test_cmp(self):
107         """Test simple comparisons of Builder objects
108         """
109         b1 = SCons.Builder.Builder(src_suffix = '.o')
110         b2 = SCons.Builder.Builder(src_suffix = '.o')
111         assert b1 == b2
112         b3 = SCons.Builder.Builder(src_suffix = '.x')
113         assert b1 != b3
114         assert b2 != b3
115
116     def test_execute(self):
117         """Test execution of simple Builder objects
118         
119         One Builder is a string that executes an external command,
120         one is an internal Python function, one is a list
121         containing one of each.
122         """
123
124         python = sys.executable
125
126         cmd1 = r'%s %s %s xyzzy' % (python, act_py, outfile)
127
128         builder = SCons.Builder.Builder(action = cmd1)
129         r = builder.execute()
130         assert r == 0
131         c = test.read(outfile, 'r')
132         assert c == "act.py: 'xyzzy'\n", c
133
134         cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile)
135
136         builder = SCons.Builder.Builder(action = cmd2)
137         r = builder.execute(target = 'foo')
138         assert r == 0
139         c = test.read(outfile, 'r')
140         assert c == "act.py: 'foo'\n", c
141
142         cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile)
143
144         builder = SCons.Builder.Builder(action = cmd3)
145         r = builder.execute(target = ['aaa', 'bbb'])
146         assert r == 0
147         c = test.read(outfile, 'r')
148         assert c == "act.py: 'aaa' 'bbb'\n", c
149
150         cmd4 = r'%s %s %s $SOURCES' % (python, act_py, outfile)
151
152         builder = SCons.Builder.Builder(action = cmd4)
153         r = builder.execute(source = ['one', 'two'])
154         assert r == 0
155         c = test.read(outfile, 'r')
156         assert c == "act.py: 'one' 'two'\n", c
157
158         cmd4 = r'%s %s %s ${SOURCES[:2]}' % (python, act_py, outfile)
159
160         builder = SCons.Builder.Builder(action = cmd4)
161         r = builder.execute(source = ['three', 'four', 'five'])
162         assert r == 0
163         c = test.read(outfile, 'r')
164         assert c == "act.py: 'three' 'four'\n", c
165
166         cmd5 = r'%s %s %s $TARGET XYZZY' % (python, act_py, outfile)
167
168         builder = SCons.Builder.Builder(action = cmd5)
169         r = builder.execute(target = 'out5', env = {'ENV' : {'XYZZY' : 'xyzzy'}})
170         assert r == 0
171         c = test.read(outfile, 'r')
172         assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy'\n", c
173
174         cmd7 = '%s %s %s one\n\n%s %s %s two' % (python, act_py, outfile,
175                                                  python, act_py, outfile)
176         expect7 = '%s %s %s one\n%s %s %s two\n' % (python, act_py, outfile,
177                                                     python, act_py, outfile)
178
179         builder = SCons.Builder.Builder(action = cmd7)
180
181         global show_string
182         show_string = ""
183         def my_show(string):
184             global show_string
185             show_string = show_string + string + "\n"
186         builder.action.show = my_show
187
188         r = builder.execute()
189         assert r == 0
190         assert show_string == expect7, show_string
191
192         def function1(**kw):
193             open(kw['out'], 'w').write("function1\n")
194             return 1
195
196         builder = SCons.Builder.Builder(action = function1)
197         r = builder.execute(out = outfile)
198         assert r == 1
199         c = test.read(outfile, 'r')
200         assert c == "function1\n", c
201
202         class class1a:
203             def __init__(self, **kw):
204                 open(kw['out'], 'w').write("class1a\n")
205
206         builder = SCons.Builder.Builder(action = class1a)
207         r = builder.execute(out = outfile)
208         assert r.__class__ == class1a
209         c = test.read(outfile, 'r')
210         assert c == "class1a\n", c
211
212         class class1b:
213             def __call__(self, **kw):
214                 open(kw['out'], 'w').write("class1b\n")
215                 return 2
216
217         builder = SCons.Builder.Builder(action = class1b())
218         r = builder.execute(out = outfile)
219         assert r == 2
220         c = test.read(outfile, 'r')
221         assert c == "class1b\n", c
222
223         cmd2 = r'%s %s %s syzygy' % (python, act_py, outfile)
224
225         def function2(**kw):
226             open(kw['out'], 'a').write("function2\n")
227             return 0
228
229         class class2a:
230             def __call__(self, **kw):
231                 open(kw['out'], 'a').write("class2a\n")
232                 return 0
233
234         class class2b:
235             def __init__(self, **kw):
236                 open(kw['out'], 'a').write("class2b\n")
237
238         builder = SCons.Builder.Builder(action = [cmd2, function2, class2a(), class2b])
239         r = builder.execute(out = outfile)
240         assert r.__class__ == class2b
241         c = test.read(outfile, 'r')
242         assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
243
244     def test_get_contents(self):
245         """Test returning the signature contents of a Builder
246         """
247
248         b1 = SCons.Builder.Builder(action = "foo")
249         contents = b1.get_contents()
250         assert contents == "foo", contents
251
252         b2 = SCons.Builder.Builder(action = Func)
253         contents = b2.get_contents()
254         assert contents == "\177\036\000\177\037\000d\000\000S", repr(contents)
255
256         b3 = SCons.Builder.Builder(action = ["foo", Func, "bar"])
257         contents = b3.get_contents()
258         assert contents == "foo\177\036\000\177\037\000d\000\000Sbar", repr(contents)
259
260     def test_name(self):
261         """Test Builder creation with a specified name
262         """
263         builder = SCons.Builder.Builder(name = 'foo')
264         assert builder.name == 'foo'
265
266     def test_node_factory(self):
267         """Test a Builder that creates nodes of a specified class
268         """
269         class Foo:
270             pass
271         def FooFactory(target):
272             global Foo
273             return Foo(target)
274         builder = SCons.Builder.Builder(node_factory = FooFactory)
275         assert builder.node_factory is FooFactory
276
277     def test_prefix(self):
278         """Test Builder creation with a specified target prefix
279
280         Make sure that there is no '.' separator appended.
281         """
282         builder = SCons.Builder.Builder(prefix = 'lib.')
283         assert builder.prefix == 'lib.'
284         builder = SCons.Builder.Builder(prefix = 'lib')
285         assert builder.prefix == 'lib'
286         tgt = builder(env, target = 'tgt1', source = 'src1')
287         assert tgt.path == 'libtgt1', \
288                 "Target has unexpected name: %s" % tgt.path
289
290     def test_src_suffix(self):
291         """Test Builder creation with a specified source file suffix
292         
293         Make sure that the '.' separator is appended to the
294         beginning if it isn't already present.
295         """
296         builder = SCons.Builder.Builder(src_suffix = '.c')
297         assert builder.src_suffix == '.c'
298         builder = SCons.Builder.Builder(src_suffix = 'c')
299         assert builder.src_suffix == '.c'
300         tgt = builder(env, target = 'tgt2', source = 'src2')
301         assert tgt.sources[0].path == 'src2.c', \
302                 "Source has unexpected name: %s" % tgt.sources[0].path
303
304     def test_suffix(self):
305         """Test Builder creation with a specified target suffix
306
307         Make sure that the '.' separator is appended to the
308         beginning if it isn't already present.
309         """
310         builder = SCons.Builder.Builder(suffix = '.o')
311         assert builder.suffix == '.o'
312         builder = SCons.Builder.Builder(suffix = 'o')
313         assert builder.suffix == '.o'
314         tgt = builder(env, target = 'tgt3', source = 'src3')
315         assert tgt.path == 'tgt3.o', \
316                 "Target has unexpected name: %s" % tgt[0].path
317
318     def test_MultiStepBuilder(self):
319         """Testing MultiStepBuilder class."""
320         builder1 = SCons.Builder.Builder(action='foo',
321                                         src_suffix='.bar',
322                                         suffix='.foo')
323         builder2 = SCons.Builder.MultiStepBuilder(action='foo',
324                                                   src_builder = builder1)
325         tgt = builder2(env, target='baz', source='test.bar test2.foo test3.txt')
326         flag = 0
327         for snode in tgt.sources:
328             if snode.path == 'test.foo':
329                 flag = 1
330                 assert snode.sources[0].path == 'test.bar'
331         assert flag
332
333     def test_CompositeBuilder(self):
334         """Testing CompositeBuilder class."""
335         builder = SCons.Builder.Builder(action={ '.foo' : 'foo',
336                                                  '.bar' : 'bar' })
337         
338         assert isinstance(builder, SCons.Builder.CompositeBuilder)
339         tgt = builder(env, target='test1', source='test1.foo')
340         assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
341         assert tgt.builder.action.command == 'foo'
342         tgt = builder(env, target='test2', source='test2.bar')
343         assert tgt.builder.action.command == 'bar'
344         flag = 0
345         try:
346             tgt = builder(env, target='test2', source='test2.bar test1.foo')
347         except SCons.Errors.UserError:
348             flag = 1
349         assert flag, "UserError should be thrown when we build targets with files of different suffixes."
350
351     def test_build_scanner(self):
352         """Testing ability to set a target scanner through a builder."""
353         global instanced
354         class TestScanner:
355             def instance(self, env):
356                 global instanced
357                 instanced = 1
358                 return self
359         scn = TestScanner()
360         builder=SCons.Builder.Builder(scanner=scn)
361         tgt = builder(env, target='foo', source='bar')
362         assert tgt.scanner == scn, tgt.scanner
363         assert instanced
364
365         instanced = None
366         builder1 = SCons.Builder.Builder(action='foo',
367                                          src_suffix='.bar',
368                                          suffix='.foo')
369         builder2 = SCons.Builder.Builder(action='foo',
370                                          src_builder = builder1,
371                                          scanner = scn)
372         tgt = builder2(env, target='baz', source='test.bar test2.foo test3.txt')
373         assert tgt.scanner == scn, tgt.scanner
374         assert instanced
375
376 if __name__ == "__main__":
377     suite = unittest.makeSuite(BuilderTestCase, 'test_')
378     if not unittest.TextTestRunner().run(suite).wasSuccessful():
379         sys.exit(1)