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.
26 This tests the -j command line option, and the num_jobs
27 SConscript settable option.
30 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
37 python = TestSCons.python
42 # if threads are not supported, then
43 # there is nothing to test
48 test = TestSCons.TestSCons()
50 test.write('build.py', r"""
53 file = open(sys.argv[1], 'wb')
54 file.write(str(time.time()) + '\n')
56 file.write(str(time.time()))
62 test.write(['foo','foo.in'], r"""
66 test.write('SConstruct', """
67 MyBuild = Builder(action = r'%s build.py $TARGETS')
68 env = Environment(BUILDERS = { 'MyBuild' : MyBuild })
69 env.MyBuild(target = 'f1', source = 'f1.in')
70 env.MyBuild(target = 'f2', source = 'f2.in')
72 def copyn(env, target, source):
77 shutil.copy(str(source[0]), str(t))
79 t = env.Command(target=['foo/foo1.out', 'foo/foo2.out'],
85 def RunTest(args, extra):
86 """extra is used to make scons rebuild the output file"""
87 test.write('f1.in', 'f1.in'+extra)
88 test.write('f2.in', 'f2.in'+extra)
90 test.run(arguments = args)
93 start1,finish1 = map(float, string.split(str, "\n"))
96 start2,finish2 = map(float, string.split(str, "\n"))
98 return start2, finish1
100 # Test 2 parallel jobs.
101 # fail if the second file was not started
102 # before the first one was finished.
103 start2, finish1 = RunTest('-j 2 f1 f2', "first")
104 test.fail_test(not (start2 < finish1))
106 # re-run the test with the same input, fail if we don't
107 # get back the same times, which would indicate that
108 # SCons rebuilt the files even though nothing changed
109 s2, f1 = RunTest('-j 2 f1 f2', "first")
110 test.fail_test(start2 != s2)
111 test.fail_test(finish1 != f1)
113 # Test a single serial job.
114 # fail if the second file was started
115 # before the first one was finished
116 start2, finish1 = RunTest('f1 f2', "second")
117 test.fail_test(start2 < finish1)
119 # Make sure that a parallel build using a list builder
121 test.run(arguments='-j 2 out')
124 # Test that we fall back and warn properly if there's no threading.py
125 # module (simulated), which is the case if this version of Python wasn't
126 # built with threading support.
128 test.subdir('pythonlib')
130 test.write(['pythonlib', 'threading.py'], """\
134 save_pythonpath = os.environ.get('PYTHONPATH', '')
135 os.environ['PYTHONPATH'] = test.workpath('pythonlib')
137 #start2, finish1 = RunTest('-j 2 f1, f2', "fifth")
139 test.write('f1.in', 'f1.in pythonlib\n')
140 test.write('f2.in', 'f2.in pythonlib\n')
142 test.run(arguments = "-j 2 f1 f2", stderr=None)
145 """scons: warning: parallel builds are unsupported by this version of Python;
146 \tignoring -j or num_jobs option.
148 test.fail_test(string.find(test.stderr(), warn) == -1)
150 str = test.read("f1")
151 start1,finish1 = map(float, string.split(str, "\n"))
153 str = test.read("f2")
154 start2,finish2 = map(float, string.split(str, "\n"))
156 test.fail_test(start2 < finish1)
158 os.environ['PYTHONPATH'] = save_pythonpath
161 # Test SetJobs() with no -j:
162 test.write('SConstruct', """
163 MyBuild = Builder(action = r'%s build.py $TARGETS')
164 env = Environment(BUILDERS = { 'MyBuild' : MyBuild })
165 env.MyBuild(target = 'f1', source = 'f1.in')
166 env.MyBuild(target = 'f2', source = 'f2.in')
168 def copyn(env, target, source):
173 shutil.copy(str(source[0]), str(t))
175 t = env.Command(target=['foo/foo1.out', 'foo/foo2.out'], source='foo/foo.in', action=copyn)
176 env.Install('out', t)
178 assert GetOption('num_jobs') == 1
179 SetOption('num_jobs', 2)
180 assert GetOption('num_jobs') == 2
183 # This should be a parallel build because the SConscript sets jobs to 2.
184 # fail if the second file was not started
185 # before the first one was finished
186 start2, finish1 = RunTest('f1 f2', "third")
187 test.fail_test(not (start2 < finish1))
189 # Test SetJobs() with -j:
190 test.write('SConstruct', """
191 MyBuild = Builder(action = r'%s build.py $TARGETS')
192 env = Environment(BUILDERS = { 'MyBuild' : MyBuild })
193 env.MyBuild(target = 'f1', source = 'f1.in')
194 env.MyBuild(target = 'f2', source = 'f2.in')
196 def copyn(env, target, source):
201 shutil.copy(str(source[0]), str(t))
203 t = env.Command(target=['foo/foo1.out', 'foo/foo2.out'], source='foo/foo.in', action=copyn)
204 env.Install('out', t)
206 assert GetOption('num_jobs') == 1
207 SetOption('num_jobs', 2)
208 assert GetOption('num_jobs') == 1
211 # This should be a serial build since -j 1 overrides the call to SetJobs().
212 # fail if the second file was started
213 # before the first one was finished
214 start2, finish1 = RunTest('-j 1 f1 f2', "fourth")
215 test.fail_test(start2 < finish1)
218 # Test that a failed build with -j works properly.
220 test.write('mycopy.py', r"""\
224 open(sys.argv[1], 'wb').write(open(sys.argv[2], 'rb').read())
227 test.write('myfail.py', r"""\
232 test.write('SConstruct', """
233 MyCopy = Builder(action = r'%s mycopy.py $TARGET $SOURCE')
234 Fail = Builder(action = r'%s myfail.py $TARGETS $SOURCE')
235 env = Environment(BUILDERS = { 'MyCopy' : MyCopy, 'Fail' : Fail })
236 env.Fail(target = 'f3', source = 'f3.in')
237 env.MyCopy(target = 'f4', source = 'f4.in')
238 env.MyCopy(target = 'f5', source = 'f5.in')
239 env.MyCopy(target = 'f6', source = 'f6.in')
240 """ % (python, python))
242 test.write('f3.in', "f3.in\n")
243 test.write('f4.in', "f4.in\n")
244 test.write('f5.in', "f5.in\n")
245 test.write('f6.in', "f6.in\n")
247 test.run(arguments = '-j 2 .',
249 stderr = "scons: *** [f3] Error 1\n")
251 test.fail_test(os.path.exists(test.workpath('f3')))
252 test.fail_test(test.read(test.workpath('f4')) != 'f4.in\n')
253 test.fail_test(os.path.exists(test.workpath('f5')))
254 test.fail_test(os.path.exists(test.workpath('f6')))