Index: llvm/trunk/docs/CommandGuide/lit.rst =================================================================== --- llvm/trunk/docs/CommandGuide/lit.rst +++ llvm/trunk/docs/CommandGuide/lit.rst @@ -85,6 +85,8 @@ Echo all commands to stdout, as they are being executed. This can be valuable for debugging test failures, as the last echoed command will be the one which has failed. + To help you find the source RUN line, :program:`lit` inserts a no-op ``:`` + command with argument ``'RUN: at line N'`` before each command pipeline. This option implies ``--verbose``. .. option:: -a, --show-all Index: llvm/trunk/utils/lit/lit/TestRunner.py =================================================================== --- llvm/trunk/utils/lit/lit/TestRunner.py +++ llvm/trunk/utils/lit/lit/TestRunner.py @@ -789,6 +789,13 @@ 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 = [] @@ -1318,7 +1325,8 @@ def parseLine(self, line_number, line): try: self.parsed_lines += [(line_number, line)] - self.value = self.parser(line_number, line, self.value) + self.value = self.parser(line_number, line, self.value, + self.keyword) except ValueError as e: raise ValueError(str(e) + ("\nin %s directive on test line %d" % (self.keyword, line_number))) @@ -1327,12 +1335,12 @@ return self.value @staticmethod - def _handleTag(line_number, line, output): + def _handleTag(line_number, line, output, keyword): """A helper for parsing TAG type keywords""" return (not line.strip() or output) @staticmethod - def _handleCommand(line_number, line, output): + def _handleCommand(line_number, line, output, keyword): """A helper for parsing COMMAND type keywords""" # Trim trailing whitespace. line = line.rstrip() @@ -1351,11 +1359,15 @@ else: if output is None: output = [] + line = ": '{keyword} at line {line}'; {real_command}".format( + keyword=keyword, + line=line_number, + real_command=line) output.append(line) return output @staticmethod - def _handleList(line_number, line, output): + def _handleList(line_number, line, output, keyword): """A parser for LIST type keywords""" if output is None: output = [] @@ -1363,7 +1375,7 @@ return output @staticmethod - def _handleBooleanExpr(line_number, line, output): + def _handleBooleanExpr(line_number, line, output, keyword): """A parser for BOOLEAN_EXPR type keywords""" if output is None: output = [] @@ -1376,17 +1388,18 @@ return output @staticmethod - def _handleRequiresAny(line_number, line, output): + def _handleRequiresAny(line_number, line, output, keyword): """A custom parser to transform REQUIRES-ANY: into REQUIRES:""" # Extract the conditions specified in REQUIRES-ANY: as written. conditions = [] - IntegratedTestKeywordParser._handleList(line_number, line, conditions) + IntegratedTestKeywordParser._handleList(line_number, line, conditions, + keyword) # Output a `REQUIRES: a || b || c` expression in its place. expression = ' || '.join(conditions) - IntegratedTestKeywordParser._handleBooleanExpr(line_number, - expression, output) + IntegratedTestKeywordParser._handleBooleanExpr(line_number, expression, + output, keyword) return output def parseIntegratedTestScript(test, additional_parsers=[], Index: llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/external-shell/basic.txt =================================================================== --- llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/external-shell/basic.txt +++ llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/external-shell/basic.txt @@ -0,0 +1,3 @@ +# RUN: true +# RUN: false +# RUN: true Index: llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/external-shell/line-continuation.txt =================================================================== --- llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/external-shell/line-continuation.txt +++ llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/external-shell/line-continuation.txt @@ -0,0 +1,11 @@ +# RUN: : first line continued \ +# RUN: to second line +# RUN: echo 'foo bar' \ +# RUN: | FileCheck %s +# RUN: echo \ +# RUN: 'foo baz' \ +# RUN: | FileCheck %s +# RUN: echo 'foo bar' \ +# RUN: | FileCheck %s + +# CHECK: foo bar Index: llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/external-shell/lit.local.cfg =================================================================== --- llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/external-shell/lit.local.cfg +++ llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/external-shell/lit.local.cfg @@ -0,0 +1,2 @@ +import lit.formats +config.test_format = lit.formats.ShTest(execute_external=True) Index: llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/internal-shell/basic.txt =================================================================== --- llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/internal-shell/basic.txt +++ llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/internal-shell/basic.txt @@ -0,0 +1,3 @@ +# RUN: true +# RUN: false +# RUN: true Index: llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/internal-shell/line-continuation.txt =================================================================== --- llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/internal-shell/line-continuation.txt +++ llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/internal-shell/line-continuation.txt @@ -0,0 +1,11 @@ +# RUN: : first line continued \ +# RUN: to second line +# RUN: echo 'foo bar' \ +# RUN: | FileCheck %s +# RUN: echo \ +# RUN: 'foo baz' \ +# RUN: | FileCheck %s +# RUN: echo 'foo bar' \ +# RUN: | FileCheck %s + +# CHECK: foo bar Index: llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/internal-shell/lit.local.cfg =================================================================== --- llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/internal-shell/lit.local.cfg +++ llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/internal-shell/lit.local.cfg @@ -0,0 +1,2 @@ +import lit.formats +config.test_format = lit.formats.ShTest(execute_external=False) Index: llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/lit.cfg =================================================================== --- llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/lit.cfg +++ llvm/trunk/utils/lit/tests/Inputs/shtest-run-at-line/lit.cfg @@ -0,0 +1,3 @@ +import lit.formats +config.name = 'shtest-run-at-line' +config.suffixes = ['.txt'] Index: llvm/trunk/utils/lit/tests/Inputs/shtest-shell/colon-error.txt =================================================================== --- llvm/trunk/utils/lit/tests/Inputs/shtest-shell/colon-error.txt +++ llvm/trunk/utils/lit/tests/Inputs/shtest-shell/colon-error.txt @@ -0,0 +1,3 @@ +# Check error on an unsupported ":". (cannot be part of a pipeline) +# +# RUN: : | echo "hello" Index: llvm/trunk/utils/lit/tests/max-failures.py =================================================================== --- llvm/trunk/utils/lit/tests/max-failures.py +++ llvm/trunk/utils/lit/tests/max-failures.py @@ -8,7 +8,7 @@ # # END. -# CHECK: Failing Tests (26) +# CHECK: Failing Tests (27) # CHECK: Failing Tests (1) # CHECK: Failing Tests (2) # CHECK: error: Setting --max-failures to 0 does not have any effect. Index: llvm/trunk/utils/lit/tests/shtest-format.py =================================================================== --- llvm/trunk/utils/lit/tests/shtest-format.py +++ llvm/trunk/utils/lit/tests/shtest-format.py @@ -39,6 +39,7 @@ # # CHECK: Command Output (stdout): # CHECK-NEXT: -- +# CHECK-NEXT: $ ":" "RUN: at line 1" # CHECK-NEXT: $ "printf" # CHECK-NEXT: # command output: # CHECK-NEXT: line 1: failed test output on stdout Index: llvm/trunk/utils/lit/tests/shtest-output-printing.py =================================================================== --- llvm/trunk/utils/lit/tests/shtest-output-printing.py +++ llvm/trunk/utils/lit/tests/shtest-output-printing.py @@ -16,12 +16,15 @@ # # CHECK: Command Output # CHECK-NEXT: -- +# CHECK-NEXT: $ ":" "RUN: at line 1" # CHECK-NEXT: $ "true" +# CHECK-NEXT: $ ":" "RUN: at line 2" # CHECK-NEXT: $ "echo" "hi" # CHECK-NEXT: # command output: # CHECK-NEXT: hi # -# CHECK: $ "wc" "missing-file" +# CHECK: $ ":" "RUN: at line 3" +# CHECK-NEXT: $ "wc" "missing-file" # CHECK-NEXT: # redirected output from '{{.*(/|\\\\)}}basic.txt.tmp.out': # CHECK-NEXT: missing-file{{.*}} No such file or directory # CHECK: note: command had no output on stdout or stderr Index: llvm/trunk/utils/lit/tests/shtest-run-at-line.py =================================================================== --- llvm/trunk/utils/lit/tests/shtest-run-at-line.py +++ llvm/trunk/utils/lit/tests/shtest-run-at-line.py @@ -0,0 +1,74 @@ +# Check that -vv makes the line number of the failing RUN command clear. +# (-v is actually sufficient in the case of the internal shell.) +# +# RUN: not %{lit} -j 1 -vv %{inputs}/shtest-run-at-line > %t.out +# RUN: FileCheck --input-file %t.out %s +# +# END. + + +# CHECK: Testing: 4 tests + + +# In the case of the external shell, we check for only RUN lines in stderr in +# case some shell implementations format "set -x" output differently. + +# CHECK-LABEL: FAIL: shtest-run-at-line :: external-shell/basic.txt + +# CHECK: Script: +# CHECK: : 'RUN: at line 1'; true +# CHECK-NEXT: : 'RUN: at line 2'; false +# CHECK-NEXT: : 'RUN: at line 3'; true + +# CHECK: Command Output (stderr) +# CHECK: RUN: at line 1 +# CHECK: RUN: at line 2 +# CHECK-NOT: RUN + +# CHECK-LABEL: FAIL: shtest-run-at-line :: external-shell/line-continuation.txt + +# CHECK: Script: +# CHECK: : 'RUN: at line 1'; : first line continued to second line +# CHECK-NEXT: : 'RUN: at line 3'; echo 'foo bar' | FileCheck +# CHECK-NEXT: : 'RUN: at line 5'; echo 'foo baz' | FileCheck +# CHECK-NEXT: : 'RUN: at line 8'; echo 'foo bar' | FileCheck + +# CHECK: Command Output (stderr) +# CHECK: RUN: at line 1 +# CHECK: RUN: at line 3 +# CHECK: RUN: at line 5 +# CHECK-NOT: RUN + + +# CHECK-LABEL: FAIL: shtest-run-at-line :: internal-shell/basic.txt + +# CHECK: Script: +# CHECK: : 'RUN: at line 1'; true +# CHECK-NEXT: : 'RUN: at line 2'; false +# CHECK-NEXT: : 'RUN: at line 3'; true + +# CHECK: Command Output (stdout) +# CHECK: $ ":" "RUN: at line 1" +# CHECK-NEXT: $ "true" +# CHECK-NEXT: $ ":" "RUN: at line 2" +# CHECK-NEXT: $ "false" +# CHECK-NOT: RUN + +# CHECK-LABEL: FAIL: shtest-run-at-line :: internal-shell/line-continuation.txt + +# CHECK: Script: +# CHECK: : 'RUN: at line 1'; : first line continued to second line +# CHECK-NEXT: : 'RUN: at line 3'; echo 'foo bar' | FileCheck +# CHECK-NEXT: : 'RUN: at line 5'; echo 'foo baz' | FileCheck +# CHECK-NEXT: : 'RUN: at line 8'; echo 'foo bar' | FileCheck + +# CHECK: Command Output (stdout) +# CHECK: $ ":" "RUN: at line 1" +# CHECK-NEXT: $ ":" "first" "line" "continued" "to" "second" "line" +# CHECK-NEXT: $ ":" "RUN: at line 3" +# CHECK-NEXT: $ "echo" "foo bar" +# CHECK-NEXT: $ "FileCheck" "{{.*}}" +# CHECK-NEXT: $ ":" "RUN: at line 5" +# CHECK-NEXT: $ "echo" "foo baz" +# CHECK-NEXT: $ "FileCheck" "{{.*}}" +# CHECK-NOT: RUN Index: llvm/trunk/utils/lit/tests/shtest-shell.py =================================================================== --- llvm/trunk/utils/lit/tests/shtest-shell.py +++ llvm/trunk/utils/lit/tests/shtest-shell.py @@ -26,6 +26,14 @@ # CHECK: error: command failed with exit status: 1 # CHECK: *** +# CHECK: FAIL: shtest-shell :: colon-error.txt +# CHECK: *** TEST 'shtest-shell :: colon-error.txt' FAILED *** +# CHECK: $ ":" +# CHECK: # command stderr: +# CHECK: Unsupported: ':' cannot be part of a pipeline +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + # CHECK: FAIL: shtest-shell :: diff-error-0.txt # CHECK: *** TEST 'shtest-shell :: diff-error-0.txt' FAILED *** # CHECK: $ "diff" "diff-error-0.txt" "diff-error-0.txt" @@ -153,7 +161,7 @@ # # CHECK: FAIL: shtest-shell :: error-1.txt # CHECK: *** TEST 'shtest-shell :: error-1.txt' FAILED *** -# CHECK: shell parser error on: 'echo "missing quote' +# CHECK: shell parser error on: ': \'RUN: at line 3\'; echo "missing quote' # CHECK: *** # CHECK: FAIL: shtest-shell :: error-2.txt @@ -219,4 +227,4 @@ # CHECK: PASS: shtest-shell :: sequencing-0.txt # CHECK: XFAIL: shtest-shell :: sequencing-1.txt # CHECK: PASS: shtest-shell :: valid-shell.txt -# CHECK: Failing Tests (26) +# CHECK: Failing Tests (27) Index: llvm/trunk/utils/lit/tests/unit/TestRunner.py =================================================================== --- llvm/trunk/utils/lit/tests/unit/TestRunner.py +++ llvm/trunk/utils/lit/tests/unit/TestRunner.py @@ -47,7 +47,7 @@ @staticmethod def make_parsers(): - def custom_parse(line_number, line, output): + def custom_parse(line_number, line, output, keyword): if output is None: output = [] output += [part for part in line.split(' ') if part.strip()] @@ -99,8 +99,8 @@ cmd_parser = self.get_parser(parsers, 'MY_RUN:') value = cmd_parser.getValue() self.assertEqual(len(value), 2) # there are only two run lines - self.assertEqual(value[0].strip(), 'baz') - self.assertEqual(value[1].strip(), 'foo bar') + self.assertEqual(value[0].strip(), ": 'MY_RUN: at line 4'; baz") + self.assertEqual(value[1].strip(), ": 'MY_RUN: at line 7'; foo bar") def test_custom(self): parsers = self.make_parsers()