Index: llvm/utils/lit/lit/TestRunner.py =================================================================== --- llvm/utils/lit/lit/TestRunner.py +++ llvm/utils/lit/lit/TestRunner.py @@ -4,6 +4,7 @@ import platform import tempfile import threading +import StringIO from lit.ShCommands import GlobItem import lit.ShUtil as ShUtil @@ -221,6 +222,58 @@ env.env[key] = val cmd.args = cmd.args[arg_idx+1:] +def executeBuiltinEcho(cmd, shenv): + """Interpret a redirected echo command""" + opened_files = [] + stdin, stdout, stderr = processRedirects(cmd, subprocess.PIPE, shenv, + opened_files) + if stdin != subprocess.PIPE or stderr != subprocess.PIPE: + raise InternalShellError( + cmd, "stdin and stderr redirects not supported for echo") + + # Some tests have un-redirected echo commands to help debug test failures. + # Buffer our output and return it to the caller. + is_redirected = True + if stdout == subprocess.PIPE: + is_redirected = False + stdout = StringIO.StringIO() + + # Implement echo flags. + args = cmd.args[1:] + interpret_escapes = False + write_newline = True + if len(args) >= 1 and args[0].startswith('-'): + flag = args[0] + args = args[1:] + if flag == '-e': + interpret_escapes = True + elif flag == '-n': + write_newline = False + else: + raise InternalShellError(cmd, "unrecognized echo flag: " + flag) + + def maybeUnescape(arg): + if not interpret_escapes: + return arg + # Python string escapes and "echo" escapes are obviously different, but + # this should be enough for the LLVM test suite. + return arg.decode('string_escape') + + if args: + for arg in args[:-1]: + stdout.write(maybeUnescape(arg)) + stdout.write(' ') + stdout.write(maybeUnescape(args[-1])) + if write_newline: + stdout.write('\n') + + for (name, mode, f, path) in opened_files: + f.close() + + if not is_redirected: + return stdout.getvalue() + return "" + def processRedirects(cmd, stdin_source, cmd_shenv, opened_files): """Return the standard fds for cmd after applying redirects @@ -360,6 +413,17 @@ # following Popen calls will fail instead. return 0 + # Handle "echo" as a builtin if it is not part of a pipeline. This greatly + # speeds up tests that construct input files by repeatedly echo-appending to + # a file. + # FIXME: Standardize on the builtin echo implementation. We can use a + # temporary file to sidestep blocking pipe write issues. + if (cmd.commands[0].args[0] == 'echo' and len(cmd.commands) == 1): + output = executeBuiltinEcho(cmd.commands[0], shenv) + results.append(ShellCommandResult(cmd.commands[0], output, "", 0, + False)) + return 0 + if cmd.commands[0].args[0] == 'export': if len(cmd.commands) != 1: raise ValueError("'export' cannot be part of a pipeline")