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/bash', 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
)