Remove the CellSPU port.
[oota-llvm.git] / utils / lit / lit / TestRunner.py
1 import os, signal, subprocess, sys
2 import StringIO
3
4 import ShUtil
5 import Test
6 import Util
7
8 import platform
9 import tempfile
10
11 import re
12
13 class InternalShellError(Exception):
14     def __init__(self, command, message):
15         self.command = command
16         self.message = message
17
18 kIsWindows = platform.system() == 'Windows'
19
20 # Don't use close_fds on Windows.
21 kUseCloseFDs = not kIsWindows
22
23 # Use temporary files to replace /dev/null on Windows.
24 kAvoidDevNull = kIsWindows
25
26 def executeCommand(command, cwd=None, env=None):
27     # Close extra file handles on UNIX (on Windows this cannot be done while
28     # also redirecting input).
29     close_fds = not kIsWindows
30
31     p = subprocess.Popen(command, cwd=cwd,
32                          stdin=subprocess.PIPE,
33                          stdout=subprocess.PIPE,
34                          stderr=subprocess.PIPE,
35                          env=env, close_fds=close_fds)
36     out,err = p.communicate()
37     exitCode = p.wait()
38
39     # Detect Ctrl-C in subprocess.
40     if exitCode == -signal.SIGINT:
41         raise KeyboardInterrupt
42
43     return out, err, exitCode
44
45 def executeShCmd(cmd, cfg, cwd, results):
46     if isinstance(cmd, ShUtil.Seq):
47         if cmd.op == ';':
48             res = executeShCmd(cmd.lhs, cfg, cwd, results)
49             return executeShCmd(cmd.rhs, cfg, cwd, results)
50
51         if cmd.op == '&':
52             raise NotImplementedError,"unsupported test command: '&'"
53
54         if cmd.op == '||':
55             res = executeShCmd(cmd.lhs, cfg, cwd, results)
56             if res != 0:
57                 res = executeShCmd(cmd.rhs, cfg, cwd, results)
58             return res
59         if cmd.op == '&&':
60             res = executeShCmd(cmd.lhs, cfg, cwd, results)
61             if res is None:
62                 return res
63
64             if res == 0:
65                 res = executeShCmd(cmd.rhs, cfg, cwd, results)
66             return res
67
68         raise ValueError,'Unknown shell command: %r' % cmd.op
69
70     assert isinstance(cmd, ShUtil.Pipeline)
71     procs = []
72     input = subprocess.PIPE
73     stderrTempFiles = []
74     opened_files = []
75     named_temp_files = []
76     # To avoid deadlock, we use a single stderr stream for piped
77     # output. This is null until we have seen some output using
78     # stderr.
79     for i,j in enumerate(cmd.commands):
80         # Apply the redirections, we use (N,) as a sentinal to indicate stdin,
81         # stdout, stderr for N equal to 0, 1, or 2 respectively. Redirects to or
82         # from a file are represented with a list [file, mode, file-object]
83         # where file-object is initially None.
84         redirects = [(0,), (1,), (2,)]
85         for r in j.redirects:
86             if r[0] == ('>',2):
87                 redirects[2] = [r[1], 'w', None]
88             elif r[0] == ('>>',2):
89                 redirects[2] = [r[1], 'a', None]
90             elif r[0] == ('>&',2) and r[1] in '012':
91                 redirects[2] = redirects[int(r[1])]
92             elif r[0] == ('>&',) or r[0] == ('&>',):
93                 redirects[1] = redirects[2] = [r[1], 'w', None]
94             elif r[0] == ('>',):
95                 redirects[1] = [r[1], 'w', None]
96             elif r[0] == ('>>',):
97                 redirects[1] = [r[1], 'a', None]
98             elif r[0] == ('<',):
99                 redirects[0] = [r[1], 'r', None]
100             else:
101                 raise NotImplementedError,"Unsupported redirect: %r" % (r,)
102
103         # Map from the final redirections to something subprocess can handle.
104         final_redirects = []
105         for index,r in enumerate(redirects):
106             if r == (0,):
107                 result = input
108             elif r == (1,):
109                 if index == 0:
110                     raise NotImplementedError,"Unsupported redirect for stdin"
111                 elif index == 1:
112                     result = subprocess.PIPE
113                 else:
114                     result = subprocess.STDOUT
115             elif r == (2,):
116                 if index != 2:
117                     raise NotImplementedError,"Unsupported redirect on stdout"
118                 result = subprocess.PIPE
119             else:
120                 if r[2] is None:
121                     if kAvoidDevNull and r[0] == '/dev/null':
122                         r[2] = tempfile.TemporaryFile(mode=r[1])
123                     else:
124                         r[2] = open(r[0], r[1])
125                     # Workaround a Win32 and/or subprocess bug when appending.
126                     #
127                     # FIXME: Actually, this is probably an instance of PR6753.
128                     if r[1] == 'a':
129                         r[2].seek(0, 2)
130                     opened_files.append(r[2])
131                 result = r[2]
132             final_redirects.append(result)
133
134         stdin, stdout, stderr = final_redirects
135
136         # If stderr wants to come from stdout, but stdout isn't a pipe, then put
137         # stderr on a pipe and treat it as stdout.
138         if (stderr == subprocess.STDOUT and stdout != subprocess.PIPE):
139             stderr = subprocess.PIPE
140             stderrIsStdout = True
141         else:
142             stderrIsStdout = False
143
144             # Don't allow stderr on a PIPE except for the last
145             # process, this could deadlock.
146             #
147             # FIXME: This is slow, but so is deadlock.
148             if stderr == subprocess.PIPE and j != cmd.commands[-1]:
149                 stderr = tempfile.TemporaryFile(mode='w+b')
150                 stderrTempFiles.append((i, stderr))
151
152         # Resolve the executable path ourselves.
153         args = list(j.args)
154         args[0] = Util.which(args[0], cfg.environment['PATH'])
155         if not args[0]:
156             raise InternalShellError(j, '%r: command not found' % j.args[0])
157
158         # Replace uses of /dev/null with temporary files.
159         if kAvoidDevNull:
160             for i,arg in enumerate(args):
161                 if arg == "/dev/null":
162                     f = tempfile.NamedTemporaryFile(delete=False)
163                     f.close()
164                     named_temp_files.append(f.name)
165                     args[i] = f.name
166
167         procs.append(subprocess.Popen(args, cwd=cwd,
168                                       stdin = stdin,
169                                       stdout = stdout,
170                                       stderr = stderr,
171                                       env = cfg.environment,
172                                       close_fds = kUseCloseFDs))
173
174         # Immediately close stdin for any process taking stdin from us.
175         if stdin == subprocess.PIPE:
176             procs[-1].stdin.close()
177             procs[-1].stdin = None
178
179         # Update the current stdin source.
180         if stdout == subprocess.PIPE:
181             input = procs[-1].stdout
182         elif stderrIsStdout:
183             input = procs[-1].stderr
184         else:
185             input = subprocess.PIPE
186
187     # Explicitly close any redirected files. We need to do this now because we
188     # need to release any handles we may have on the temporary files (important
189     # on Win32, for example). Since we have already spawned the subprocess, our
190     # handles have already been transferred so we do not need them anymore.
191     for f in opened_files:
192         f.close()
193
194     # FIXME: There is probably still deadlock potential here. Yawn.
195     procData = [None] * len(procs)
196     procData[-1] = procs[-1].communicate()
197
198     for i in range(len(procs) - 1):
199         if procs[i].stdout is not None:
200             out = procs[i].stdout.read()
201         else:
202             out = ''
203         if procs[i].stderr is not None:
204             err = procs[i].stderr.read()
205         else:
206             err = ''
207         procData[i] = (out,err)
208
209     # Read stderr out of the temp files.
210     for i,f in stderrTempFiles:
211         f.seek(0, 0)
212         procData[i] = (procData[i][0], f.read())
213
214     exitCode = None
215     for i,(out,err) in enumerate(procData):
216         res = procs[i].wait()
217         # Detect Ctrl-C in subprocess.
218         if res == -signal.SIGINT:
219             raise KeyboardInterrupt
220
221         results.append((cmd.commands[i], out, err, res))
222         if cmd.pipe_err:
223             # Python treats the exit code as a signed char.
224             if res < 0:
225                 exitCode = min(exitCode, res)
226             else:
227                 exitCode = max(exitCode, res)
228         else:
229             exitCode = res
230
231     # Remove any named temporary files we created.
232     for f in named_temp_files:
233         try:
234             os.remove(f)
235         except OSError:
236             pass
237
238     if cmd.negate:
239         exitCode = not exitCode
240
241     return exitCode
242
243 def executeScriptInternal(test, litConfig, tmpBase, commands, cwd):
244     ln = ' &&\n'.join(commands)
245     try:
246         cmd = ShUtil.ShParser(ln, litConfig.isWindows).parse()
247     except:
248         return (Test.FAIL, "shell parser error on: %r" % ln)
249
250     results = []
251     try:
252         exitCode = executeShCmd(cmd, test.config, cwd, results)
253     except InternalShellError,e:
254         out = ''
255         err = e.message
256         exitCode = 255
257
258     out = err = ''
259     for i,(cmd, cmd_out,cmd_err,res) in enumerate(results):
260         out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
261         out += 'Command %d Result: %r\n' % (i, res)
262         out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
263         out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
264
265     return out, err, exitCode
266
267 def executeTclScriptInternal(test, litConfig, tmpBase, commands, cwd):
268     import TclUtil
269     cmds = []
270     for ln in commands:
271         # Given the unfortunate way LLVM's test are written, the line gets
272         # backslash substitution done twice.
273         ln = TclUtil.TclLexer(ln).lex_unquoted(process_all = True)
274
275         try:
276             tokens = list(TclUtil.TclLexer(ln).lex())
277         except:
278             return (Test.FAIL, "Tcl lexer error on: %r" % ln)
279
280         # Validate there are no control tokens.
281         for t in tokens:
282             if not isinstance(t, str):
283                 return (Test.FAIL,
284                         "Invalid test line: %r containing %r" % (ln, t))
285
286         try:
287             cmds.append(TclUtil.TclExecCommand(tokens).parse_pipeline())
288         except:
289             return (Test.FAIL, "Tcl 'exec' parse error on: %r" % ln)
290
291     if litConfig.useValgrind:
292         for pipeline in cmds:
293             if pipeline.commands:
294                 # Only valgrind the first command in each pipeline, to avoid
295                 # valgrinding things like grep, not, and FileCheck.
296                 cmd = pipeline.commands[0]
297                 cmd.args = litConfig.valgrindArgs + cmd.args
298
299     cmd = cmds[0]
300     for c in cmds[1:]:
301         cmd = ShUtil.Seq(cmd, '&&', c)
302
303     # FIXME: This is lame, we shouldn't need bash. See PR5240.
304     bashPath = litConfig.getBashPath()
305     if litConfig.useTclAsSh and bashPath:
306         script = tmpBase + '.script'
307
308         # Write script file
309         f = open(script,'w')
310         print >>f, 'set -o pipefail'
311         cmd.toShell(f, pipefail = True)
312         f.close()
313
314         if 0:
315             print >>sys.stdout, cmd
316             print >>sys.stdout, open(script).read()
317             print >>sys.stdout
318             return '', '', 0
319
320         command = [litConfig.getBashPath(), script]
321         out,err,exitCode = executeCommand(command, cwd=cwd,
322                                           env=test.config.environment)
323
324         return out,err,exitCode
325     else:
326         results = []
327         try:
328             exitCode = executeShCmd(cmd, test.config, cwd, results)
329         except InternalShellError,e:
330             results.append((e.command, '', e.message + '\n', 255))
331             exitCode = 255
332
333     out = err = ''
334
335     for i,(cmd, cmd_out, cmd_err, res) in enumerate(results):
336         out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
337         out += 'Command %d Result: %r\n' % (i, res)
338         out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
339         out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
340
341     return out, err, exitCode
342
343 def executeScript(test, litConfig, tmpBase, commands, cwd):
344     bashPath = litConfig.getBashPath();
345     isWin32CMDEXE = (litConfig.isWindows and not bashPath)
346     script = tmpBase + '.script'
347     if isWin32CMDEXE:
348         script += '.bat'
349
350     # Write script file
351     f = open(script,'w')
352     if isWin32CMDEXE:
353         f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands))
354     else:
355         f.write(' &&\n'.join(commands))
356     f.write('\n')
357     f.close()
358
359     if isWin32CMDEXE:
360         command = ['cmd','/c', script]
361     else:
362         if bashPath:
363             command = [bashPath, script]
364         else:
365             command = ['/bin/sh', script]
366         if litConfig.useValgrind:
367             # FIXME: Running valgrind on sh is overkill. We probably could just
368             # run on clang with no real loss.
369             command = litConfig.valgrindArgs + command
370
371     return executeCommand(command, cwd=cwd, env=test.config.environment)
372
373 def isExpectedFail(test, xfails):
374     # Check if any of the xfails match an available feature or the target.
375     for item in xfails:
376         # If this is the wildcard, it always fails.
377         if item == '*':
378             return True
379
380         # If this is an exact match for one of the features, it fails.
381         if item in test.config.available_features:
382             return True
383
384         # If this is a part of the target triple, it fails.
385         if item in test.suite.config.target_triple:
386             return True
387
388     return False
389
390 def parseIntegratedTestScript(test, normalize_slashes=False,
391                               extra_substitutions=[]):
392     """parseIntegratedTestScript - Scan an LLVM/Clang style integrated test
393     script and extract the lines to 'RUN' as well as 'XFAIL' and 'REQUIRES'
394     information. The RUN lines also will have variable substitution performed.
395     """
396
397     # Get the temporary location, this is always relative to the test suite
398     # root, not test source root.
399     #
400     # FIXME: This should not be here?
401     sourcepath = test.getSourcePath()
402     sourcedir = os.path.dirname(sourcepath)
403     execpath = test.getExecPath()
404     execdir,execbase = os.path.split(execpath)
405     tmpDir = os.path.join(execdir, 'Output')
406     tmpBase = os.path.join(tmpDir, execbase)
407     if test.index is not None:
408         tmpBase += '_%d' % test.index
409
410     # Normalize slashes, if requested.
411     if normalize_slashes:
412         sourcepath = sourcepath.replace('\\', '/')
413         sourcedir = sourcedir.replace('\\', '/')
414         tmpDir = tmpDir.replace('\\', '/')
415         tmpBase = tmpBase.replace('\\', '/')
416
417     # We use #_MARKER_# to hide %% while we do the other substitutions.
418     substitutions = list(extra_substitutions)
419     substitutions.extend([('%%', '#_MARKER_#')])
420     substitutions.extend(test.config.substitutions)
421     substitutions.extend([('%s', sourcepath),
422                           ('%S', sourcedir),
423                           ('%p', sourcedir),
424                           ('%{pathsep}', os.pathsep),
425                           ('%t', tmpBase + '.tmp'),
426                           ('%T', tmpDir),
427                           # FIXME: Remove this once we kill DejaGNU.
428                           ('%abs_tmp', tmpBase + '.tmp'),
429                           ('#_MARKER_#', '%')])
430
431     # Collect the test lines from the script.
432     script = []
433     xfails = []
434     requires = []
435     line_number = 0
436     for ln in open(sourcepath):
437         line_number += 1
438         if 'RUN:' in ln:
439             # Isolate the command to run.
440             index = ln.index('RUN:')
441             ln = ln[index+4:]
442
443             # Trim trailing whitespace.
444             ln = ln.rstrip()
445
446             # Substitute line number expressions
447             ln = re.sub('%\(line\)', str(line_number), ln)
448             def replace_line_number(match):
449                 if match.group(1) == '+':
450                     return str(line_number + int(match.group(2)))
451                 if match.group(1) == '-':
452                     return str(line_number - int(match.group(2)))
453             ln = re.sub('%\(line *([\+-]) *(\d+)\)', replace_line_number, ln)
454
455             # Collapse lines with trailing '\\'.
456             if script and script[-1][-1] == '\\':
457                 script[-1] = script[-1][:-1] + ln
458             else:
459                 script.append(ln)
460         elif 'XFAIL:' in ln:
461             items = ln[ln.index('XFAIL:') + 6:].split(',')
462             xfails.extend([s.strip() for s in items])
463         elif 'REQUIRES:' in ln:
464             items = ln[ln.index('REQUIRES:') + 9:].split(',')
465             requires.extend([s.strip() for s in items])
466         elif 'END.' in ln:
467             # Check for END. lines.
468             if ln[ln.index('END.'):].strip() == 'END.':
469                 break
470
471     # Apply substitutions to the script.  Allow full regular
472     # expression syntax.  Replace each matching occurrence of regular
473     # expression pattern a with substitution b in line ln.
474     def processLine(ln):
475         # Apply substitutions
476         for a,b in substitutions:
477             if kIsWindows:
478                 b = b.replace("\\","\\\\")
479             ln = re.sub(a, b, ln)
480
481         # Strip the trailing newline and any extra whitespace.
482         return ln.strip()
483     script = map(processLine, script)
484
485     # Verify the script contains a run line.
486     if not script:
487         return (Test.UNRESOLVED, "Test has no run line!")
488
489     # Check for unterminated run lines.
490     if script[-1][-1] == '\\':
491         return (Test.UNRESOLVED, "Test has unterminated run lines (with '\\')")
492
493     # Check that we have the required features:
494     missing_required_features = [f for f in requires
495                                  if f not in test.config.available_features]
496     if missing_required_features:
497         msg = ', '.join(missing_required_features)
498         return (Test.UNSUPPORTED,
499                 "Test requires the following features: %s" % msg)
500
501     isXFail = isExpectedFail(test, xfails)
502     return script,isXFail,tmpBase,execdir
503
504 def formatTestOutput(status, out, err, exitCode, failDueToStderr, script):
505     output = StringIO.StringIO()
506     print >>output, "Script:"
507     print >>output, "--"
508     print >>output, '\n'.join(script)
509     print >>output, "--"
510     print >>output, "Exit Code: %r" % exitCode,
511     if failDueToStderr:
512         print >>output, "(but there was output on stderr)"
513     else:
514         print >>output
515     if out:
516         print >>output, "Command Output (stdout):"
517         print >>output, "--"
518         output.write(out)
519         print >>output, "--"
520     if err:
521         print >>output, "Command Output (stderr):"
522         print >>output, "--"
523         output.write(err)
524         print >>output, "--"
525     return (status, output.getvalue())
526
527 def executeTclTest(test, litConfig):
528     if test.config.unsupported:
529         return (Test.UNSUPPORTED, 'Test is unsupported')
530
531     # Parse the test script, normalizing slashes in substitutions on Windows
532     # (since otherwise Tcl style lexing will treat them as escapes).
533     res = parseIntegratedTestScript(test, normalize_slashes=kIsWindows)
534     if len(res) == 2:
535         return res
536
537     script, isXFail, tmpBase, execdir = res
538
539     if litConfig.noExecute:
540         return (Test.PASS, '')
541
542     # Create the output directory if it does not already exist.
543     Util.mkdir_p(os.path.dirname(tmpBase))
544
545     res = executeTclScriptInternal(test, litConfig, tmpBase, script, execdir)
546     if len(res) == 2:
547         return res
548
549     # Test for failure. In addition to the exit code, Tcl commands are
550     # considered to fail if there is any standard error output.
551     out,err,exitCode = res
552     if isXFail:
553         ok = exitCode != 0 or err and not litConfig.ignoreStdErr
554         if ok:
555             status = Test.XFAIL
556         else:
557             status = Test.XPASS
558     else:
559         ok = exitCode == 0 and (not err or litConfig.ignoreStdErr)
560         if ok:
561             status = Test.PASS
562         else:
563             status = Test.FAIL
564
565     if ok:
566         return (status,'')
567
568     # Set a flag for formatTestOutput so it can explain why the test was
569     # considered to have failed, despite having an exit code of 0.
570     failDueToStderr = exitCode == 0 and err and not litConfig.ignoreStdErr
571
572     return formatTestOutput(status, out, err, exitCode, failDueToStderr, script)
573
574 def executeShTest(test, litConfig, useExternalSh,
575                   extra_substitutions=[]):
576     if test.config.unsupported:
577         return (Test.UNSUPPORTED, 'Test is unsupported')
578
579     res = parseIntegratedTestScript(test, useExternalSh, extra_substitutions)
580     if len(res) == 2:
581         return res
582
583     script, isXFail, tmpBase, execdir = res
584
585     if litConfig.noExecute:
586         return (Test.PASS, '')
587
588     # Create the output directory if it does not already exist.
589     Util.mkdir_p(os.path.dirname(tmpBase))
590
591     if useExternalSh:
592         res = executeScript(test, litConfig, tmpBase, script, execdir)
593     else:
594         res = executeScriptInternal(test, litConfig, tmpBase, script, execdir)
595     if len(res) == 2:
596         return res
597
598     out,err,exitCode = res
599     if isXFail:
600         ok = exitCode != 0
601         if ok:
602             status = Test.XFAIL
603         else:
604             status = Test.XPASS
605     else:
606         ok = exitCode == 0
607         if ok:
608             status = Test.PASS
609         else:
610             status = Test.FAIL
611
612     if ok:
613         return (status,'')
614
615     # Sh tests are not considered to fail just from stderr output.
616     failDueToStderr = False
617
618     return formatTestOutput(status, out, err, exitCode, failDueToStderr, script)