1 import os, signal, subprocess, sys
8 def executeCommand(command, cwd=None, env=None):
9 p = subprocess.Popen(command, cwd=cwd,
10 stdin=subprocess.PIPE,
11 stdout=subprocess.PIPE,
12 stderr=subprocess.PIPE,
14 out,err = p.communicate()
17 # Detect Ctrl-C in subprocess.
18 if exitCode == -signal.SIGINT:
19 raise KeyboardInterrupt
21 return out, err, exitCode
23 def executeShCmd(cmd, cfg, cwd, results):
24 if isinstance(cmd, ShUtil.Seq):
26 res = executeShCmd(cmd.lhs, cfg, cwd, results)
27 return executeShCmd(cmd.rhs, cfg, cwd, results)
30 raise NotImplementedError,"unsupported test command: '&'"
33 res = executeShCmd(cmd.lhs, cfg, cwd, results)
35 res = executeShCmd(cmd.rhs, cfg, cwd, results)
38 res = executeShCmd(cmd.lhs, cfg, cwd, results)
43 res = executeShCmd(cmd.rhs, cfg, cwd, results)
46 raise ValueError,'Unknown shell command: %r' % cmd.op
48 assert isinstance(cmd, ShUtil.Pipeline)
50 input = subprocess.PIPE
51 for j in cmd.commands:
52 redirects = [(0,), (1,), (2,)]
55 redirects[2] = [r[1], 'w', None]
56 elif r[0] == ('>&',2) and r[1] in '012':
57 redirects[2] = redirects[int(r[1])]
58 elif r[0] == ('>&',) or r[0] == ('&>',):
59 redirects[1] = redirects[2] = [r[1], 'w', None]
61 redirects[1] = [r[1], 'w', None]
63 redirects[0] = [r[1], 'r', None]
65 raise NotImplementedError,"Unsupported redirect: %r" % (r,)
68 for index,r in enumerate(redirects):
73 raise NotImplementedError,"Unsupported redirect for stdin"
75 result = subprocess.PIPE
77 result = subprocess.STDOUT
80 raise NotImplementedError,"Unsupported redirect on stdout"
81 result = subprocess.PIPE
84 r[2] = open(r[0], r[1])
86 final_redirects.append(result)
88 stdin, stdout, stderr = final_redirects
90 # If stderr wants to come from stdout, but stdout isn't a pipe, then put
91 # stderr on a pipe and treat it as stdout.
92 if (stderr == subprocess.STDOUT and stdout != subprocess.PIPE):
93 stderr = subprocess.PIPE
96 stderrIsStdout = False
97 procs.append(subprocess.Popen(j.args, cwd=cwd,
101 env = cfg.environment,
104 # Immediately close stdin for any process taking stdin from us.
105 if stdin == subprocess.PIPE:
106 procs[-1].stdin.close()
107 procs[-1].stdin = None
109 # Update the current stdin source.
110 if stdout == subprocess.PIPE:
111 input = procs[-1].stdout
113 input = procs[-1].stderr
115 input = subprocess.PIPE
117 # FIXME: There is a potential for deadlock here, when we have a pipe and
118 # some process other than the last one ends up blocked on stderr.
119 procData = [None] * len(procs)
120 procData[-1] = procs[-1].communicate()
121 for i in range(len(procs) - 1):
122 if procs[i].stdout is not None:
123 out = procs[i].stdout.read()
126 if procs[i].stderr is not None:
127 err = procs[i].stderr.read()
130 procData[i] = (out,err)
133 for i,(out,err) in enumerate(procData):
134 res = procs[i].wait()
135 # Detect Ctrl-C in subprocess.
136 if res == -signal.SIGINT:
137 raise KeyboardInterrupt
139 results.append((cmd.commands[i], out, err, res))
141 # Python treats the exit code as a signed char.
143 exitCode = min(exitCode, res)
145 exitCode = max(exitCode, res)
150 exitCode = not exitCode
154 def executeScriptInternal(test, litConfig, tmpBase, commands, cwd):
155 ln = ' &&\n'.join(commands)
157 cmd = ShUtil.ShParser(ln, litConfig.isWindows).parse()
159 return (Test.FAIL, "shell parser error on: %r" % ln)
162 exitCode = executeShCmd(cmd, test.config, cwd, results)
165 for i,(cmd, cmd_out,cmd_err,res) in enumerate(results):
166 out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
167 out += 'Command %d Result: %r\n' % (i, res)
168 out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
169 out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
171 return out, err, exitCode
173 def executeTclScriptInternal(test, litConfig, tmpBase, commands, cwd):
177 # Given the unfortunate way LLVM's test are written, the line gets
178 # backslash substitution done twice.
179 ln = TclUtil.TclLexer(ln).lex_unquoted(process_all = True)
182 tokens = list(TclUtil.TclLexer(ln).lex())
184 return (Test.FAIL, "Tcl lexer error on: %r" % ln)
186 # Validate there are no control tokens.
188 if not isinstance(t, str):
190 "Invalid test line: %r containing %r" % (ln, t))
193 cmds.append(TclUtil.TclExecCommand(tokens).parse_pipeline())
195 return (Test.FAIL, "Tcl 'exec' parse error on: %r" % ln)
199 cmd = ShUtil.Seq(cmd, '&&', c)
201 if litConfig.useTclAsSh:
202 script = tmpBase + '.script'
206 print >>f, 'set -o pipefail'
207 cmd.toShell(f, pipefail = True)
211 print >>sys.stdout, cmd
212 print >>sys.stdout, open(script).read()
216 command = ['/bin/sh', script]
217 out,err,exitCode = executeCommand(command, cwd=cwd,
218 env=test.config.environment)
220 # Tcl commands fail on standard error output.
223 out = 'Command has output on stderr!\n\n' + out
225 return out,err,exitCode
228 exitCode = executeShCmd(cmd, test.config, cwd, results)
232 # Tcl commands fail on standard error output.
233 if [True for _,_,err,res in results if err]:
235 out += 'Command has output on stderr!\n\n'
237 for i,(cmd, cmd_out, cmd_err, res) in enumerate(results):
238 out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
239 out += 'Command %d Result: %r\n' % (i, res)
240 out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
241 out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
243 return out, err, exitCode
245 def executeScript(test, litConfig, tmpBase, commands, cwd):
246 script = tmpBase + '.script'
247 if litConfig.isWindows:
252 if litConfig.isWindows:
253 f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands))
255 f.write(' &&\n'.join(commands))
259 if litConfig.isWindows:
260 command = ['cmd','/c', script]
262 command = ['/bin/sh', script]
263 if litConfig.useValgrind:
264 # FIXME: Running valgrind on sh is overkill. We probably could just
265 # run on clang with no real loss.
266 valgrindArgs = ['valgrind', '-q',
267 '--tool=memcheck', '--trace-children=yes',
268 '--error-exitcode=123']
269 valgrindArgs.extend(litConfig.valgrindArgs)
271 command = valgrindArgs + command
273 return executeCommand(command, cwd=cwd, env=test.config.environment)
275 def parseIntegratedTestScript(test, xfailHasColon, requireAndAnd):
276 """parseIntegratedTestScript - Scan an LLVM/Clang style integrated test
277 script and extract the lines to 'RUN' as well as 'XFAIL' and 'XTARGET'
278 information. The RUN lines also will have variable substitution performed.
281 # Get the temporary location, this is always relative to the test suite
282 # root, not test source root.
284 # FIXME: This should not be here?
285 sourcepath = test.getSourcePath()
286 execpath = test.getExecPath()
287 execdir,execbase = os.path.split(execpath)
288 tmpBase = os.path.join(execdir, 'Output', execbase)
290 # We use #_MARKER_# to hide %% while we do the other substitutions.
291 substitutions = [('%%', '#_MARKER_#')]
292 substitutions.extend(test.config.substitutions)
293 substitutions.extend([('%s', sourcepath),
294 ('%S', os.path.dirname(sourcepath)),
295 ('%p', os.path.dirname(sourcepath)),
296 ('%t', tmpBase + '.tmp'),
297 ('#_MARKER_#', '%')])
299 # Collect the test lines from the script.
303 for ln in open(sourcepath):
305 # Isolate the command to run.
306 index = ln.index('RUN:')
309 # Trim trailing whitespace.
312 # Collapse lines with trailing '\\'.
313 if script and script[-1][-1] == '\\':
314 script[-1] = script[-1][:-1] + ln
317 elif xfailHasColon and 'XFAIL:' in ln:
318 items = ln[ln.index('XFAIL:') + 6:].split(',')
319 xfails.extend([s.strip() for s in items])
320 elif not xfailHasColon and 'XFAIL' in ln:
321 items = ln[ln.index('XFAIL') + 5:].split(',')
322 xfails.extend([s.strip() for s in items])
323 elif 'XTARGET:' in ln:
324 items = ln[ln.index('XTARGET:') + 8:].split(',')
325 xtargets.extend([s.strip() for s in items])
327 # Check for END. lines.
328 if ln[ln.index('END.'):].strip() == 'END.':
331 # Apply substitutions to the script.
333 # Apply substitutions
334 for a,b in substitutions:
337 # Strip the trailing newline and any extra whitespace.
339 script = map(processLine, script)
341 # Verify the script contains a run line.
343 return (Test.UNRESOLVED, "Test has no run line!")
345 if script[-1][-1] == '\\':
346 return (Test.UNRESOLVED, "Test has unterminated run lines (with '\\')")
348 # Validate interior lines for '&&', a lovely historical artifact.
350 for i in range(len(script) - 1):
353 if not ln.endswith('&&'):
355 ("MISSING \'&&\': %s\n" +
356 "FOLLOWED BY : %s\n") % (ln, script[i + 1]))
361 return script,xfails,xtargets,tmpBase,execdir
363 def formatTestOutput(status, out, err, exitCode, script):
364 output = StringIO.StringIO()
365 print >>output, "Script:"
367 print >>output, '\n'.join(script)
369 print >>output, "Exit Code: %r" % exitCode
370 print >>output, "Command Output (stdout):"
374 print >>output, "Command Output (stderr):"
378 return (status, output.getvalue())
380 def executeTclTest(test, litConfig):
381 if test.config.unsupported:
382 return (Test.UNSUPPORTED, 'Test is unsupported')
384 res = parseIntegratedTestScript(test, True, False)
388 script, xfails, xtargets, tmpBase, execdir = res
390 if litConfig.noExecute:
391 return (Test.PASS, '')
393 # Create the output directory if it does not already exist.
394 Util.mkdir_p(os.path.dirname(tmpBase))
396 res = executeTclScriptInternal(test, litConfig, tmpBase, script, execdir)
402 if item == '*' or item in test.suite.config.target_triple:
406 # If this is XFAIL, see if it is expected to pass on this target.
408 for item in xtargets:
409 if item == '*' or item in test.suite.config.target_triple:
413 out,err,exitCode = res
416 status = (Test.XPASS, Test.XFAIL)[ok]
419 status = (Test.FAIL, Test.PASS)[ok]
424 return formatTestOutput(status, out, err, exitCode, script)
426 def executeShTest(test, litConfig, useExternalSh, requireAndAnd):
427 if test.config.unsupported:
428 return (Test.UNSUPPORTED, 'Test is unsupported')
430 res = parseIntegratedTestScript(test, False, requireAndAnd)
434 script, xfails, xtargets, tmpBase, execdir = res
436 if litConfig.noExecute:
437 return (Test.PASS, '')
439 # Create the output directory if it does not already exist.
440 Util.mkdir_p(os.path.dirname(tmpBase))
443 res = executeScript(test, litConfig, tmpBase, script, execdir)
445 res = executeScriptInternal(test, litConfig, tmpBase, script, execdir)
449 out,err,exitCode = res
452 status = (Test.XPASS, Test.XFAIL)[ok]
455 status = (Test.FAIL, Test.PASS)[ok]
460 return formatTestOutput(status, out, err, exitCode, script)