Index: llvm/utils/lit/lit/TestRunner.py =================================================================== --- llvm/utils/lit/lit/TestRunner.py +++ llvm/utils/lit/lit/TestRunner.py @@ -262,6 +262,27 @@ env.env[key] = val return args[arg_idx_next:] +def executeBuiltinCd(cmd, shenv): + """executeBuiltinCd - Change the current directory.""" + if len(cmd.args) != 2: + raise InternalShellError("'cd' supports only one argument") + newdir = cmd.args[1] + # Update the cwd in the parent environment. + if os.path.isabs(newdir): + shenv.cwd = newdir + else: + shenv.cwd = os.path.realpath(os.path.join(shenv.cwd, newdir)) + # The cd builtin always succeeds. If the directory does not exist, the + # following Popen calls will fail instead. + return ShellCommandResult(cmd, "", "", 0, False) + +def executeBuiltinExport(cmd, shenv): + """executeBuiltinExport - Set an environment variable.""" + if len(cmd.args) != 2: + raise InternalShellError("'export' supports only one argument") + updateEnv(shenv, cmd.args) + return ShellCommandResult(cmd, "", "", 0, False) + def executeBuiltinEcho(cmd, shenv): """Interpret a redirected echo command""" opened_files = [] @@ -321,9 +342,8 @@ for (name, mode, f, path) in opened_files: f.close() - if not is_redirected: - return stdout.getvalue() - return "" + output = "" if is_redirected else stdout.getvalue() + return ShellCommandResult(cmd, output, "", 0, False) def executeBuiltinMkdir(cmd, cmd_shenv): """executeBuiltinMkdir - Create new directories.""" @@ -670,6 +690,10 @@ exitCode = 1 return ShellCommandResult(cmd, "", stderr.getvalue(), exitCode, False) +def executeBuiltinColon(cmd, cmd_shenv): + """executeBuiltinColon - Discard arguments and exit with status 0.""" + return ShellCommandResult(cmd, "", "", 0, False) + def processRedirects(cmd, stdin_source, cmd_shenv, opened_files): """Return the standard fds for cmd after applying redirects @@ -795,72 +819,6 @@ raise ValueError('Unknown shell command: %r' % cmd.op) assert isinstance(cmd, ShUtil.Pipeline) - # Handle shell builtins first. - if cmd.commands[0].args[0] == 'cd': - if len(cmd.commands) != 1: - raise ValueError("'cd' cannot be part of a pipeline") - if len(cmd.commands[0].args) != 2: - raise ValueError("'cd' supports only one argument") - newdir = cmd.commands[0].args[1] - # Update the cwd in the parent environment. - if os.path.isabs(newdir): - shenv.cwd = newdir - else: - shenv.cwd = os.path.realpath(os.path.join(shenv.cwd, newdir)) - # The cd builtin always succeeds. If the directory does not exist, the - # 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") - if len(cmd.commands[0].args) != 2: - raise ValueError("'export' supports only one argument") - updateEnv(shenv, cmd.commands[0].args) - return 0 - - if cmd.commands[0].args[0] == 'mkdir': - if len(cmd.commands) != 1: - raise InternalShellError(cmd.commands[0], "Unsupported: 'mkdir' " - "cannot be part of a pipeline") - cmdResult = executeBuiltinMkdir(cmd.commands[0], shenv) - results.append(cmdResult) - return cmdResult.exitCode - - if cmd.commands[0].args[0] == 'diff': - if len(cmd.commands) != 1: - raise InternalShellError(cmd.commands[0], "Unsupported: 'diff' " - "cannot be part of a pipeline") - cmdResult = executeBuiltinDiff(cmd.commands[0], shenv) - results.append(cmdResult) - return cmdResult.exitCode - - if cmd.commands[0].args[0] == 'rm': - if len(cmd.commands) != 1: - raise InternalShellError(cmd.commands[0], "Unsupported: 'rm' " - "cannot be part of a pipeline") - cmdResult = executeBuiltinRm(cmd.commands[0], shenv) - results.append(cmdResult) - return cmdResult.exitCode - - if cmd.commands[0].args[0] == ':': - if len(cmd.commands) != 1: - raise InternalShellError(cmd.commands[0], "Unsupported: ':' " - "cannot be part of a pipeline") - results.append(ShellCommandResult(cmd.commands[0], '', '', 0, False)) - return 0; - procs = [] default_stdin = subprocess.PIPE stderrTempFiles = [] @@ -868,6 +826,13 @@ named_temp_files = [] builtin_commands = set(['cat']) builtin_commands_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "builtin_commands") + inproc_builtins = {'cd': executeBuiltinCd, + 'export': executeBuiltinExport, + 'echo': executeBuiltinEcho, + 'mkdir': executeBuiltinMkdir, + 'diff': executeBuiltinDiff, + 'rm': executeBuiltinRm, + ':': executeBuiltinColon} # To avoid deadlock, we use a single stderr stream for piped # output. This is null until we have seen some output using # stderr. @@ -885,6 +850,27 @@ raise InternalShellError(j, "Error: 'env' requires a subcommand") + # Handle in-process builtins. + # + # 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. + inproc_builtin = inproc_builtins.get(args[0], None) + if inproc_builtin and (args[0] != 'echo' or len(cmd.commands) == 1): + # env calling an in-process builtin is useless, so we take the safe + # approach of complaining. + if not cmd_shenv is shenv: + raise InternalShellError(j, "Error: 'env' cannot call '{}'" + .format(args[0])) + if len(cmd.commands) != 1: + raise InternalShellError(j, "Unsupported: '{}' cannot be part" + " of a pipeline".format(args[0])) + result = inproc_builtin(j, cmd_shenv) + results.append(result) + return result.exitCode + stdin, stdout, stderr = processRedirects(j, default_stdin, cmd_shenv, opened_files) Index: llvm/utils/lit/tests/Inputs/shtest-env/env-calls-cd.txt =================================================================== --- /dev/null +++ llvm/utils/lit/tests/Inputs/shtest-env/env-calls-cd.txt @@ -0,0 +1 @@ +# RUN: env -u FOO BAR=3 cd foobar Index: llvm/utils/lit/tests/Inputs/shtest-env/env-calls-colon.txt =================================================================== --- /dev/null +++ llvm/utils/lit/tests/Inputs/shtest-env/env-calls-colon.txt @@ -0,0 +1 @@ +# RUN: env -u FOO BAR=3 : Index: llvm/utils/lit/tests/Inputs/shtest-env/env-calls-diff.txt =================================================================== --- /dev/null +++ llvm/utils/lit/tests/Inputs/shtest-env/env-calls-diff.txt @@ -0,0 +1 @@ +# RUN: env -u FOO BAR=3 diff foo.txt bar.txt Index: llvm/utils/lit/tests/Inputs/shtest-env/env-calls-echo.txt =================================================================== --- /dev/null +++ llvm/utils/lit/tests/Inputs/shtest-env/env-calls-echo.txt @@ -0,0 +1 @@ +# RUN: env -u FOO BAR=3 echo hello world Index: llvm/utils/lit/tests/Inputs/shtest-env/env-calls-export.txt =================================================================== --- /dev/null +++ llvm/utils/lit/tests/Inputs/shtest-env/env-calls-export.txt @@ -0,0 +1 @@ +# RUN: env -u FOO BAR=3 export BAZ=3 Index: llvm/utils/lit/tests/Inputs/shtest-env/env-calls-mkdir.txt =================================================================== --- /dev/null +++ llvm/utils/lit/tests/Inputs/shtest-env/env-calls-mkdir.txt @@ -0,0 +1 @@ +# RUN: env -u FOO BAR=3 mkdir foobar Index: llvm/utils/lit/tests/Inputs/shtest-env/env-calls-rm.txt =================================================================== --- /dev/null +++ llvm/utils/lit/tests/Inputs/shtest-env/env-calls-rm.txt @@ -0,0 +1 @@ +# RUN: env -u FOO BAR=3 rm foobar Index: llvm/utils/lit/tests/shtest-env.py =================================================================== --- llvm/utils/lit/tests/shtest-env.py +++ llvm/utils/lit/tests/shtest-env.py @@ -7,7 +7,7 @@ # Make sure env commands are included in printed commands. -# CHECK: -- Testing: 7 tests{{.*}} +# CHECK: -- Testing: 14 tests{{.*}} # CHECK: FAIL: shtest-env :: env-args-last-is-assign.txt ({{[^)]*}}) # CHECK: Error: 'env' requires a subcommand @@ -25,19 +25,57 @@ # CHECK: Error: 'env' requires a subcommand # CHECK: error: command failed with exit status: {{.*}} +# CHECK: FAIL: shtest-env :: env-calls-cd.txt ({{[^)]*}}) +# CHECK: $ "env" "-u" "FOO" "BAR=3" "cd" "foobar" +# CHECK: Error: 'env' cannot call 'cd' +# CHECK: error: command failed with exit status: {{.*}} + +# CHECK: FAIL: shtest-env :: env-calls-colon.txt ({{[^)]*}}) +# CHECK: $ "env" "-u" "FOO" "BAR=3" ":" +# CHECK: Error: 'env' cannot call ':' +# CHECK: error: command failed with exit status: {{.*}} + +# CHECK: FAIL: shtest-env :: env-calls-diff.txt ({{[^)]*}}) +# CHECK: $ "env" "-u" "FOO" "BAR=3" "diff" "foo.txt" "bar.txt" +# CHECK: Error: 'env' cannot call 'diff' +# CHECK: error: command failed with exit status: {{.*}} + +# CHECK: FAIL: shtest-env :: env-calls-echo.txt ({{[^)]*}}) +# CHECK: $ "env" "-u" "FOO" "BAR=3" "echo" "hello" "world" +# CHECK: Error: 'env' cannot call 'echo' +# CHECK: error: command failed with exit status: {{.*}} + +# CHECK: FAIL: shtest-env :: env-calls-export.txt ({{[^)]*}}) +# CHECK: $ "env" "-u" "FOO" "BAR=3" "export" "BAZ=3" +# CHECK: Error: 'env' cannot call 'export' +# CHECK: error: command failed with exit status: {{.*}} + +# CHECK: FAIL: shtest-env :: env-calls-mkdir.txt ({{[^)]*}}) +# CHECK: $ "env" "-u" "FOO" "BAR=3" "mkdir" "foobar" +# CHECK: Error: 'env' cannot call 'mkdir' +# CHECK: error: command failed with exit status: {{.*}} + +# CHECK: FAIL: shtest-env :: env-calls-rm.txt ({{[^)]*}}) +# CHECK: $ "env" "-u" "FOO" "BAR=3" "rm" "foobar" +# CHECK: Error: 'env' cannot call 'rm' +# CHECK: error: command failed with exit status: {{.*}} + # CHECK: PASS: shtest-env :: env-u.txt ({{[^)]*}}) # CHECK: $ "{{[^"]*}}" "print_environment.py" # CHECK: $ "env" "-u" "FOO" "{{[^"]*}}" "print_environment.py" # CHECK: $ "env" "-u" "FOO" "-u" "BAR" "{{[^"]*}}" "print_environment.py" +# CHECK-NOT: ${{.*}}print_environment.py # CHECK: PASS: shtest-env :: env.txt ({{[^)]*}}) # CHECK: $ "env" "A_FOO=999" "{{[^"]*}}" "print_environment.py" # CHECK: $ "env" "A_FOO=1" "B_BAR=2" "C_OOF=3" "{{[^"]*}}" "print_environment.py" +# CHECK-NOT: ${{.*}}print_environment.py # CHECK: PASS: shtest-env :: mixed.txt ({{[^)]*}}) # CHECK: $ "env" "A_FOO=999" "-u" "FOO" "{{[^"]*}}" "print_environment.py" # CHECK: $ "env" "A_FOO=1" "-u" "FOO" "B_BAR=2" "-u" "BAR" "C_OOF=3" "{{[^"]*}}" "print_environment.py" +# CHECK-NOT: ${{.*}}print_environment.py # CHECK: Expected Passes : 3 -# CHECK: Unexpected Failures: 4 +# CHECK: Unexpected Failures: 11 # CHECK-NOT: {{.}}