diff --git a/llvm/docs/CommandGuide/lit.rst b/llvm/docs/CommandGuide/lit.rst index 15c249d8e6d31..359e0c3e81d0e 100644 --- a/llvm/docs/CommandGuide/lit.rst +++ b/llvm/docs/CommandGuide/lit.rst @@ -664,6 +664,7 @@ TestRunner.py: Otherwise, %t but with a single leading ``/`` removed. %:T On Windows, %/T but a ``:`` is removed if its the second character. Otherwise, %T but with a single leading ``/`` removed. + %{readfile:} Reads the file specified. ======================= ============== Other substitutions are provided that are variations on this base set and diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py index 90c2c6479b004..b4d7c98692337 100644 --- a/llvm/utils/lit/lit/TestRunner.py +++ b/llvm/utils/lit/lit/TestRunner.py @@ -720,6 +720,30 @@ def processRedirects(cmd, stdin_source, cmd_shenv, opened_files): return std_fds +def _expandLateSubstitutions(cmd, arguments, cwd): + for i, arg in enumerate(arguments): + if not isinstance(arg, str): + continue + + def _replaceReadFile(match): + filePath = match.group(1) + if not os.path.isabs(filePath): + filePath = os.path.join(cwd, filePath) + try: + with open(filePath) as fileHandle: + return fileHandle.read() + except FileNotFoundError: + raise InternalShellError( + cmd, + "File specified in readfile substitution does not exist: %s" + % filePath, + ) + + arguments[i] = re.sub(r"%{readfile:([^}]*)}", _replaceReadFile, arg) + + return arguments + + def _executeShCmd(cmd, shenv, results, timeoutHelper): if timeoutHelper.timeoutReached(): # Prevent further recursion if the timeout has been hit @@ -834,6 +858,9 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper): # Ensure args[0] is hashable. args[0] = expand_glob(args[0], cmd_shenv.cwd)[0] + # Expand all late substitutions. + args = _expandLateSubstitutions(j, args, cmd_shenv.cwd) + 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 diff --git a/llvm/utils/lit/tests/Inputs/shtest-readfile/absolute-paths.txt b/llvm/utils/lit/tests/Inputs/shtest-readfile/absolute-paths.txt new file mode 100644 index 0000000000000..d11feeba68672 --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/shtest-readfile/absolute-paths.txt @@ -0,0 +1,6 @@ +## Tests that readfile works with absolute paths. +# RUN: echo -n "hello" > %t +# RUN: echo %{readfile:%t} + +## Fail the test so we can assert on the output. +# RUN: not echo return diff --git a/llvm/utils/lit/tests/Inputs/shtest-readfile/file-does-not-exist.txt b/llvm/utils/lit/tests/Inputs/shtest-readfile/file-does-not-exist.txt new file mode 100644 index 0000000000000..1151b75f2fa00 --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/shtest-readfile/file-does-not-exist.txt @@ -0,0 +1,4 @@ +## Test that readfile reports information appropriately when the file specified +## does not exist. + +# RUN: echo %{readfile:/file/does/not/exist} diff --git a/llvm/utils/lit/tests/Inputs/shtest-readfile/lit.cfg b/llvm/utils/lit/tests/Inputs/shtest-readfile/lit.cfg new file mode 100644 index 0000000000000..cf453e1ea786f --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/shtest-readfile/lit.cfg @@ -0,0 +1,7 @@ +import lit.formats + +config.name = "shtest-readfile" +config.suffixes = [".txt"] +config.test_format = lit.formats.ShTest(execute_external=False) +config.test_source_root = None +config.test_exec_root = None diff --git a/llvm/utils/lit/tests/Inputs/shtest-readfile/relative-paths.txt b/llvm/utils/lit/tests/Inputs/shtest-readfile/relative-paths.txt new file mode 100644 index 0000000000000..08a8170773a21 --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/shtest-readfile/relative-paths.txt @@ -0,0 +1,7 @@ +## Tests that readfile works with relative paths. +# RUN: mkdir -p rel_path_test_folder +# RUN: echo -n "hello" > rel_path_test_folder/test_file +# RUN: echo %{readfile:rel_path_test_folder/test_file} + +## Fail the test so we can assert on the output. +# RUN: not echo return diff --git a/llvm/utils/lit/tests/Inputs/shtest-readfile/two-same-line.txt b/llvm/utils/lit/tests/Inputs/shtest-readfile/two-same-line.txt new file mode 100644 index 0000000000000..7a8eb2c3e5332 --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/shtest-readfile/two-same-line.txt @@ -0,0 +1,8 @@ +## Tests that readfile works with two substitutions on the same line to ensure the +## regular expressions are set up correctly. +# RUN: echo -n "hello" > %t.1 +# RUN: echo -n "bye" > %t.2 +# RUN: echo %{readfile:%t.1} %{readfile:%t.2} + +## Fail the test so we can assert on the output. +# RUN: not echo return diff --git a/llvm/utils/lit/tests/shtest-readfile.py b/llvm/utils/lit/tests/shtest-readfile.py new file mode 100644 index 0000000000000..91250b2afb3f2 --- /dev/null +++ b/llvm/utils/lit/tests/shtest-readfile.py @@ -0,0 +1,21 @@ +## Tests the readfile substitution. + +# RUN: not %{lit} -a -v %{inputs}/shtest-readfile | FileCheck -match-full-lines -DTEMP_PATH=%S%{fs-sep}Inputs%{fs-sep}shtest-readfile%{fs-sep}Output %s + +# CHECK: -- Testing: 4 tests{{.*}} + +# CHECK-LABEL: FAIL: shtest-readfile :: absolute-paths.txt ({{[^)]*}}) +# CHECK: echo hello +# CHECK: # executed command: echo '%{readfile:[[TEMP_PATH]]{{[\\\/]}}absolute-paths.txt.tmp}' + +# CHECK-LABEL: FAIL: shtest-readfile :: file-does-not-exist.txt ({{[^)]*}}) +# CHECK: # executed command: @echo 'echo %{readfile:/file/does/not/exist}' +# CHECK: # | File specified in readfile substitution does not exist: /file/does/not/exist + +# CHECK-LABEL: FAIL: shtest-readfile :: relative-paths.txt ({{[^)]*}}) +# CHECK: echo hello +# CHECK: # executed command: echo '%{readfile:rel_path_test_folder/test_file}' + +# CHECK-LABEL: FAIL: shtest-readfile :: two-same-line.txt ({{[^)]*}}) +# CHECK: echo hello bye +# CHECK: # executed command: echo '%{readfile:[[TEMP_PATH]]{{[\\\/]}}two-same-line.txt.tmp.1}' '%{readfile:[[TEMP_PATH]]{{[\\\/]}}two-same-line.txt.tmp.2}'