https://github.com/boomanaiden154 updated 
https://github.com/llvm/llvm-project/pull/158447

>From 5bd8d4f925f3b5f82d85ef693861b6b1067d9f38 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengross...@google.com>
Date: Sat, 13 Sep 2025 22:54:58 +0000
Subject: [PATCH 1/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?=
 =?UTF-8?q?itial=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.6
---
 clang/test/Misc/dev-fd-fs.c                    | 1 -
 llvm/utils/lit/lit/builtin_commands/cat.py     | 3 +++
 llvm/utils/lit/tests/Inputs/shtest-cat/cat.txt | 4 ++++
 3 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/clang/test/Misc/dev-fd-fs.c b/clang/test/Misc/dev-fd-fs.c
index ea94d950b0716..b989ab8a439cf 100644
--- a/clang/test/Misc/dev-fd-fs.c
+++ b/clang/test/Misc/dev-fd-fs.c
@@ -1,6 +1,5 @@
 // Check that we can operate on files from /dev/fd.
 // REQUIRES: dev-fd-fs
-// REQUIRES: shell
 
 // Check reading from named pipes. We cat the input here instead of redirecting
 // it to ensure that /dev/fd/0 is a named pipe, not just a redirected file.
diff --git a/llvm/utils/lit/lit/builtin_commands/cat.py 
b/llvm/utils/lit/lit/builtin_commands/cat.py
index ddab555662045..2797e0cbb4154 100644
--- a/llvm/utils/lit/lit/builtin_commands/cat.py
+++ b/llvm/utils/lit/lit/builtin_commands/cat.py
@@ -49,6 +49,9 @@ def main(argv):
             import os, msvcrt
 
             msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
+    if len(filenames) == 0:
+        sys.stdout.write(sys.stdin.read())
+        sys.exit(0)
     for filename in filenames:
         try:
             contents = None
diff --git a/llvm/utils/lit/tests/Inputs/shtest-cat/cat.txt 
b/llvm/utils/lit/tests/Inputs/shtest-cat/cat.txt
index 4014b0fca1f24..c5b5d247c2f95 100644
--- a/llvm/utils/lit/tests/Inputs/shtest-cat/cat.txt
+++ b/llvm/utils/lit/tests/Inputs/shtest-cat/cat.txt
@@ -70,3 +70,7 @@
 # 
NP-CAT-OUTPUT-NEXT:M-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM-RM-SM-TM-UM-VM-WM-XM-YM-ZM-[
 # 
NP-CAT-OUTPUT-NEXT:M-\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM-o
 # NP-CAT-OUTPUT-NEXT:M-pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?
+
+## Test that cat will pipe stdin to stdout if no other files are specified.
+# RUN: echo test | cat | FileCheck --check-prefix=CAT-STDIN %s
+# CAT-STDIN: test

>From 572975066e843b76e51020bcf6abc7822d3dfb75 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengross...@google.com>
Date: Sat, 13 Sep 2025 23:14:52 +0000
Subject: [PATCH 2/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
 =?UTF-8?q?anges=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.6

[skip ci]
---
 clang/test/ClangScanDeps/pr61006.cppm         |  3 ++-
 clang/test/ClangScanDeps/resource_directory.c |  9 ++++-----
 clang/test/Driver/env.c                       |  5 +++--
 clang/test/Driver/program-path-priority.c     | 16 +++++++--------
 clang/test/Modules/relative-resource-dir.m    |  6 +++---
 llvm/docs/CommandGuide/lit.rst                |  1 +
 llvm/test/tools/llvm-cgdata/empty.test        |  1 +
 llvm/utils/lit/lit/TestRunner.py              | 20 +++++++++++++++++++
 .../Inputs/shtest-readfile/absolute-paths.txt |  6 ++++++
 .../lit/tests/Inputs/shtest-readfile/lit.cfg  |  8 ++++++++
 .../Inputs/shtest-readfile/relative-paths.txt |  7 +++++++
 .../Inputs/shtest-readfile/two-same-line.txt  |  8 ++++++++
 llvm/utils/lit/tests/shtest-readfile.py       | 17 ++++++++++++++++
 13 files changed, 88 insertions(+), 19 deletions(-)
 create mode 100644 
llvm/utils/lit/tests/Inputs/shtest-readfile/absolute-paths.txt
 create mode 100644 llvm/utils/lit/tests/Inputs/shtest-readfile/lit.cfg
 create mode 100644 
llvm/utils/lit/tests/Inputs/shtest-readfile/relative-paths.txt
 create mode 100644 
llvm/utils/lit/tests/Inputs/shtest-readfile/two-same-line.txt
 create mode 100644 llvm/utils/lit/tests/shtest-readfile.py

diff --git a/clang/test/ClangScanDeps/pr61006.cppm 
b/clang/test/ClangScanDeps/pr61006.cppm
index f75edd38c81ba..f10bc1e673987 100644
--- a/clang/test/ClangScanDeps/pr61006.cppm
+++ b/clang/test/ClangScanDeps/pr61006.cppm
@@ -6,7 +6,8 @@
 // RUN: mkdir -p %t
 // RUN: split-file %s %t
 //
-// RUN: EXPECTED_RESOURCE_DIR=`%clang -print-resource-dir` && \
+// RUN: %clang -print-resource-dir | tr -d '\n' > %t/resource-dir
+// RUN: env EXPECTED_RESOURCE_DIR=%{readfile:%t/resource-dir} && \
 // RUN: ln -s %clang++ %t/clang++ && \
 // RUN: sed "s|EXPECTED_RESOURCE_DIR|$EXPECTED_RESOURCE_DIR|g; s|DIR|%/t|g" 
%t/P1689.json.in > %t/P1689.json && \
 // RUN: clang-scan-deps -compilation-database %t/P1689.json -format=p1689 | 
FileCheck %t/a.cpp -DPREFIX=%/t && \
diff --git a/clang/test/ClangScanDeps/resource_directory.c 
b/clang/test/ClangScanDeps/resource_directory.c
index 55d5d90bbcdea..6183e8aefacfa 100644
--- a/clang/test/ClangScanDeps/resource_directory.c
+++ b/clang/test/ClangScanDeps/resource_directory.c
@@ -12,14 +12,14 @@
 // then verify `%clang-scan-deps` arrives at the same path by calling the
 // `Driver::GetResourcesPath` function.
 //
-// RUN: EXPECTED_RESOURCE_DIR=`%clang -print-resource-dir`
+// RUN: %clang -print-resource-dir | tr -d '\n' > %t/resource-dir
 // RUN: sed -e "s|CLANG|%clang|g" -e "s|DIR|%/t|g" \
 // RUN:   %S/Inputs/resource_directory/cdb.json.template > %t/cdb_path.json
 //
 // RUN: clang-scan-deps -compilation-database %t/cdb_path.json --format 
experimental-full \
 // RUN:   --resource-dir-recipe modify-compiler-path > %t/result_path.json
 // RUN: cat %t/result_path.json | sed 's:\\\\\?:/:g' \
-// RUN:   | FileCheck %s --check-prefix=CHECK-PATH 
-DEXPECTED_RESOURCE_DIR="$EXPECTED_RESOURCE_DIR"
+// RUN:   | FileCheck %s --check-prefix=CHECK-PATH 
-DEXPECTED_RESOURCE_DIR="%{readfile:%t/resource-dir}"
 // CHECK-PATH:      "-resource-dir"
 // CHECK-PATH-NEXT: "[[EXPECTED_RESOURCE_DIR]]"
 
@@ -31,9 +31,8 @@
 // Here we hard-code the expected path into `%t/compiler` and then verify
 // `%clang-scan-deps` arrives at the path by actually running the executable.
 //
-// RUN: EXPECTED_RESOURCE_DIR="/custom/compiler/resources"
 // RUN: echo "#!/bin/sh"                      > %t/compiler
-// RUN: echo "echo '$EXPECTED_RESOURCE_DIR'" >> %t/compiler
+// RUN: echo "echo '/custom/compiler/resources'" >> %t/compiler
 // RUN: chmod +x %t/compiler
 // RUN: sed -e "s|CLANG|%/t/compiler|g" -e "s|DIR|%/t|g" \
 // RUN:   %S/Inputs/resource_directory/cdb.json.template > 
%t/cdb_invocation.json
@@ -41,6 +40,6 @@
 // RUN: clang-scan-deps -compilation-database %t/cdb_invocation.json --format 
experimental-full \
 // RUN:   --resource-dir-recipe invoke-compiler > %t/result_invocation.json
 // RUN: cat %t/result_invocation.json | sed 's:\\\\\?:/:g' \
-// RUN:   | FileCheck %s --check-prefix=CHECK-PATH 
-DEXPECTED_RESOURCE_DIR="$EXPECTED_RESOURCE_DIR"
+// RUN:   | FileCheck %s --check-prefix=CHECK-PATH 
-DEXPECTED_RESOURCE_DIR="/custom/compiler/resources"
 // CHECK-INVOCATION:      "-resource-dir"
 // CHECK-INVOCATION-NEXT: "[[EXPECTED_RESOURCE_DIR]]"
diff --git a/clang/test/Driver/env.c b/clang/test/Driver/env.c
index b3345ae09ffef..68ded45385702 100644
--- a/clang/test/Driver/env.c
+++ b/clang/test/Driver/env.c
@@ -1,13 +1,14 @@
 // Some assertions in this test use Linux style (/) file paths.
 // UNSUPPORTED: system-windows
+// RUN: bash -c env | grep LD_LIBRARY_PATH | tr -d '\n' > /tmp/ld_library_path
 // The PATH variable is heavily used when trying to find a linker.
-// RUN: env -i LC_ALL=C LD_LIBRARY_PATH="$LD_LIBRARY_PATH" 
CLANG_NO_DEFAULT_CONFIG=1 \
+// RUN: env -i LC_ALL=C LD_LIBRARY_PATH="%{readfile:/tmp/ld_library_path}" 
CLANG_NO_DEFAULT_CONFIG=1 \
 // RUN:   %clang %s -### -o %t.o --target=i386-unknown-linux \
 // RUN:     --sysroot=%S/Inputs/basic_linux_tree \
 // RUN:     --rtlib=platform --unwindlib=platform -no-pie \
 // RUN:     2>&1 | FileCheck --check-prefix=CHECK-LD-32 %s
 //
-// RUN: env -i LC_ALL=C PATH="" LD_LIBRARY_PATH="$LD_LIBRARY_PATH" 
CLANG_NO_DEFAULT_CONFIG=1 \
+// RUN: env -i LC_ALL=C PATH="" 
LD_LIBRARY_PATH="%{readfile:/tmp/ld_library_path}" CLANG_NO_DEFAULT_CONFIG=1 \
 // RUN:   %clang %s -### -o %t.o --target=i386-unknown-linux \
 // RUN:     --sysroot=%S/Inputs/basic_linux_tree \
 // RUN:     --rtlib=platform --unwindlib=platform -no-pie \
diff --git a/clang/test/Driver/program-path-priority.c 
b/clang/test/Driver/program-path-priority.c
index b88c0f29f7f33..bb434482f90d4 100644
--- a/clang/test/Driver/program-path-priority.c
+++ b/clang/test/Driver/program-path-priority.c
@@ -87,8 +87,8 @@
 
 /// <default-triple>-gcc has lowest priority so <triple>-gcc
 /// on PATH beats default triple in program path
-// RUN: DEFAULT_TRIPLE=`%t/clang --version | grep "Target:" | cut -d ' ' -f2`
-// RUN: touch %t/$DEFAULT_TRIPLE-gcc && chmod +x %t/$DEFAULT_TRIPLE-gcc
+// RUN: %t/clang --version | grep "Target:" | cut -d ' ' -f2 > 
%t.default_triple
+// RUN: touch %t/%{readfile:%t.default_triple}-gcc && chmod +x 
%t/%{readfile:%t.default_triple}-gcc
 // RUN: touch %t/%target_triple-gcc && chmod +x %t/%target_triple-gcc
 // RUN: env "PATH=%t/env/" %t/clang -### -target notreal-none-elf %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=DEFAULT_TRIPLE_GCC %s
@@ -101,7 +101,7 @@
 // DEFAULT_TRIPLE_NO_NOTREAL: env/gcc"
 // DEFAULT_TRIPLE_NO_NOTREAL-NOT: -gcc"
 
-/// Pick "gcc" as a fallback. Don't pick $DEFAULT_TRIPLE-gcc.
+/// Pick "gcc" as a fallback. Don't pick DEFAULT_TRIPLE-gcc.
 // RUN: rm %t/env/gcc
 // RUN: env "PATH=%t/env/" %t/clang -### -target notreal-none-elf %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=DEFAULT_TRIPLE_NO_OTHERS %s
@@ -110,9 +110,9 @@
 /// -B paths are searched separately so default triple will win
 /// if put in one of those even if other paths have higher priority names
 // RUN: mkdir -p %t/prefix
-/// One of these will fail when $DEFAULT_TRIPLE == %target_triple
-// RUN: test -f %t/$DEFAULT_TRIPLE-gcc && \
-// RUN:   mv %t/$DEFAULT_TRIPLE-gcc %t/prefix || true
+/// One of these will fail when %{readfile:%t.default_triple} == %target_triple
+// RUN: test -f %t/%{readfile:%t.default_triple}-gcc && \
+// RUN:   mv %t/%{readfile:%t.default_triple}-gcc %t/prefix || true
 // RUN: test -f %t/%target_triple-gcc && \
 // RUN:   mv %t/%target_triple-gcc %t/prefix || true
 // RUN: touch %t/notreal-none-elf-gcc && chmod +x %t/notreal-none-elf-gcc
@@ -123,8 +123,8 @@
 // DEFAULT_TRIPLE_IN_PREFIX-NOT: notreal-none-elf-gcc"
 
 /// Only if there is nothing in the prefix will we search other paths
-/// -f in case $DEFAULT_TRIPLE == %target_triple
-// RUN: rm -f %t/prefix/$DEFAULT_TRIPLE-gcc %t/prefix/%target_triple-gcc 
%t/prefix/gcc
+/// -f in case %{readfile:%t.default_triple} == %target_triple
+// RUN: rm -f %t/prefix/%{readfile:%t.default_triple}-gcc 
%t/prefix/%target_triple-gcc %t/prefix/gcc
 // RUN: env "PATH=" %t/clang -### -canonical-prefixes 
--target=notreal-none-elf %s -B %t/prefix 2>&1 | \
 // RUN:   FileCheck --check-prefix=EMPTY_PREFIX_DIR1 %s
 // EMPTY_PREFIX_DIR1: gcc"
diff --git a/clang/test/Modules/relative-resource-dir.m 
b/clang/test/Modules/relative-resource-dir.m
index 4f88150a2ba4c..96f2d8efc7860 100644
--- a/clang/test/Modules/relative-resource-dir.m
+++ b/clang/test/Modules/relative-resource-dir.m
@@ -1,9 +1,9 @@
 // UNSUPPORTED: target={{.*}}-zos{{.*}}, target={{.*}}-aix{{.*}}
-// REQUIRES: shell
 
-// RUN: EXPECTED_RESOURCE_DIR=`%clang -print-resource-dir` && \
+// RUN: %clang -print-resource-dir | tr -d '\n' > %t.resource_dir
+// RUN: env EXPECTED_RESOURCE_DIR="%{readfile:%t.resource_dir}" && \
 // RUN:  mkdir -p %t && rm -rf %t/resource-dir && \
-// RUN:  cp -R $EXPECTED_RESOURCE_DIR %t/resource-dir
+// RUN:  cp -R %{readfile:%t.resource_dir} %t/resource-dir
 // RUN: cd %t && %clang -cc1 -x objective-c -fmodules -fmodule-format=obj \
 // RUN:   -fimplicit-module-maps -fmodules-cache-path=%t.mcp \
 // RUN:   -fbuiltin-headers-in-system-modules \
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:<filename>}  Reads the file specified.
  ======================= ==============
 
 Other substitutions are provided that are variations on this base set and
diff --git a/llvm/test/tools/llvm-cgdata/empty.test 
b/llvm/test/tools/llvm-cgdata/empty.test
index 52d0dfb87623f..7e42db5ed8512 100644
--- a/llvm/test/tools/llvm-cgdata/empty.test
+++ b/llvm/test/tools/llvm-cgdata/empty.test
@@ -35,3 +35,4 @@ RUN: printf '\000\000\000\000' >> %t_header.cgdata
 RUN: printf '\040\000\000\000\000\000\000\000' >> %t_header.cgdata
 RUN: printf '\040\000\000\000\000\000\000\000' >> %t_header.cgdata
 RUN: diff %t_header.cgdata %t_emptyheader.cgdata
+RUN: echo %{readfile:/tmp/test} > /tmp/test
diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index 90c2c6479b004..5daa8887e4c80 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -720,6 +720,23 @@ def processRedirects(cmd, stdin_source, cmd_shenv, 
opened_files):
     return std_fds
 
 
+def _expandLateSubstitutions(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)
+            with open(filePath) as fileHandle:
+                return fileHandle.read()
+
+        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 +851,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(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..4246064cf7bfc
--- /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/lit.cfg 
b/llvm/utils/lit/tests/Inputs/shtest-readfile/lit.cfg
new file mode 100644
index 0000000000000..25651f2cd4832
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-readfile/lit.cfg
@@ -0,0 +1,8 @@
+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
+config.substitutions.append(("%{python}", '"%s"' % (sys.executable)))
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..3d203d411379d
--- /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..6855d27d66d35
--- /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 setup 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..8ba8e53b32966
--- /dev/null
+++ b/llvm/utils/lit/tests/shtest-readfile.py
@@ -0,0 +1,17 @@
+## Tests the readfile substitution
+
+# RUN: not %{lit} -a -v %{inputs}/shtest-readfile | FileCheck 
-match-full-lines %s
+
+# CHECK: -- Testing: 3 tests{{.*}}
+
+# CHECK-LABEL: FAIL: shtest-readfile :: absolute-paths.txt ({{[^)]*}})
+# CHECK: echo hello
+# CHECK: # executed command: echo '%{readfile:{{.*}}}'
+
+# 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:{{.*}}.1}' '%{readfile:{{.*}}.2}'

>From e238732d75577598668a26f5b9ab63b75da6e04a Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengross...@google.com>
Date: Sat, 13 Sep 2025 23:15:38 +0000
Subject: [PATCH 3/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
 =?UTF-8?q?anges=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.6

[skip ci]
---
 clang/test/ClangScanDeps/pr61006.cppm         |  3 ++-
 clang/test/ClangScanDeps/resource_directory.c |  9 ++++-----
 clang/test/Driver/env.c                       |  5 +++--
 clang/test/Driver/program-path-priority.c     | 16 +++++++--------
 clang/test/Modules/relative-resource-dir.m    |  6 +++---
 llvm/docs/CommandGuide/lit.rst                |  1 +
 llvm/test/tools/llvm-cgdata/empty.test        |  1 +
 llvm/utils/lit/lit/TestRunner.py              | 20 +++++++++++++++++++
 .../Inputs/shtest-readfile/absolute-paths.txt |  6 ++++++
 .../lit/tests/Inputs/shtest-readfile/lit.cfg  |  8 ++++++++
 .../Inputs/shtest-readfile/relative-paths.txt |  7 +++++++
 .../Inputs/shtest-readfile/two-same-line.txt  |  8 ++++++++
 llvm/utils/lit/tests/shtest-readfile.py       | 17 ++++++++++++++++
 13 files changed, 88 insertions(+), 19 deletions(-)
 create mode 100644 
llvm/utils/lit/tests/Inputs/shtest-readfile/absolute-paths.txt
 create mode 100644 llvm/utils/lit/tests/Inputs/shtest-readfile/lit.cfg
 create mode 100644 
llvm/utils/lit/tests/Inputs/shtest-readfile/relative-paths.txt
 create mode 100644 
llvm/utils/lit/tests/Inputs/shtest-readfile/two-same-line.txt
 create mode 100644 llvm/utils/lit/tests/shtest-readfile.py

diff --git a/clang/test/ClangScanDeps/pr61006.cppm 
b/clang/test/ClangScanDeps/pr61006.cppm
index f75edd38c81ba..f10bc1e673987 100644
--- a/clang/test/ClangScanDeps/pr61006.cppm
+++ b/clang/test/ClangScanDeps/pr61006.cppm
@@ -6,7 +6,8 @@
 // RUN: mkdir -p %t
 // RUN: split-file %s %t
 //
-// RUN: EXPECTED_RESOURCE_DIR=`%clang -print-resource-dir` && \
+// RUN: %clang -print-resource-dir | tr -d '\n' > %t/resource-dir
+// RUN: env EXPECTED_RESOURCE_DIR=%{readfile:%t/resource-dir} && \
 // RUN: ln -s %clang++ %t/clang++ && \
 // RUN: sed "s|EXPECTED_RESOURCE_DIR|$EXPECTED_RESOURCE_DIR|g; s|DIR|%/t|g" 
%t/P1689.json.in > %t/P1689.json && \
 // RUN: clang-scan-deps -compilation-database %t/P1689.json -format=p1689 | 
FileCheck %t/a.cpp -DPREFIX=%/t && \
diff --git a/clang/test/ClangScanDeps/resource_directory.c 
b/clang/test/ClangScanDeps/resource_directory.c
index 55d5d90bbcdea..6183e8aefacfa 100644
--- a/clang/test/ClangScanDeps/resource_directory.c
+++ b/clang/test/ClangScanDeps/resource_directory.c
@@ -12,14 +12,14 @@
 // then verify `%clang-scan-deps` arrives at the same path by calling the
 // `Driver::GetResourcesPath` function.
 //
-// RUN: EXPECTED_RESOURCE_DIR=`%clang -print-resource-dir`
+// RUN: %clang -print-resource-dir | tr -d '\n' > %t/resource-dir
 // RUN: sed -e "s|CLANG|%clang|g" -e "s|DIR|%/t|g" \
 // RUN:   %S/Inputs/resource_directory/cdb.json.template > %t/cdb_path.json
 //
 // RUN: clang-scan-deps -compilation-database %t/cdb_path.json --format 
experimental-full \
 // RUN:   --resource-dir-recipe modify-compiler-path > %t/result_path.json
 // RUN: cat %t/result_path.json | sed 's:\\\\\?:/:g' \
-// RUN:   | FileCheck %s --check-prefix=CHECK-PATH 
-DEXPECTED_RESOURCE_DIR="$EXPECTED_RESOURCE_DIR"
+// RUN:   | FileCheck %s --check-prefix=CHECK-PATH 
-DEXPECTED_RESOURCE_DIR="%{readfile:%t/resource-dir}"
 // CHECK-PATH:      "-resource-dir"
 // CHECK-PATH-NEXT: "[[EXPECTED_RESOURCE_DIR]]"
 
@@ -31,9 +31,8 @@
 // Here we hard-code the expected path into `%t/compiler` and then verify
 // `%clang-scan-deps` arrives at the path by actually running the executable.
 //
-// RUN: EXPECTED_RESOURCE_DIR="/custom/compiler/resources"
 // RUN: echo "#!/bin/sh"                      > %t/compiler
-// RUN: echo "echo '$EXPECTED_RESOURCE_DIR'" >> %t/compiler
+// RUN: echo "echo '/custom/compiler/resources'" >> %t/compiler
 // RUN: chmod +x %t/compiler
 // RUN: sed -e "s|CLANG|%/t/compiler|g" -e "s|DIR|%/t|g" \
 // RUN:   %S/Inputs/resource_directory/cdb.json.template > 
%t/cdb_invocation.json
@@ -41,6 +40,6 @@
 // RUN: clang-scan-deps -compilation-database %t/cdb_invocation.json --format 
experimental-full \
 // RUN:   --resource-dir-recipe invoke-compiler > %t/result_invocation.json
 // RUN: cat %t/result_invocation.json | sed 's:\\\\\?:/:g' \
-// RUN:   | FileCheck %s --check-prefix=CHECK-PATH 
-DEXPECTED_RESOURCE_DIR="$EXPECTED_RESOURCE_DIR"
+// RUN:   | FileCheck %s --check-prefix=CHECK-PATH 
-DEXPECTED_RESOURCE_DIR="/custom/compiler/resources"
 // CHECK-INVOCATION:      "-resource-dir"
 // CHECK-INVOCATION-NEXT: "[[EXPECTED_RESOURCE_DIR]]"
diff --git a/clang/test/Driver/env.c b/clang/test/Driver/env.c
index b3345ae09ffef..68ded45385702 100644
--- a/clang/test/Driver/env.c
+++ b/clang/test/Driver/env.c
@@ -1,13 +1,14 @@
 // Some assertions in this test use Linux style (/) file paths.
 // UNSUPPORTED: system-windows
+// RUN: bash -c env | grep LD_LIBRARY_PATH | tr -d '\n' > /tmp/ld_library_path
 // The PATH variable is heavily used when trying to find a linker.
-// RUN: env -i LC_ALL=C LD_LIBRARY_PATH="$LD_LIBRARY_PATH" 
CLANG_NO_DEFAULT_CONFIG=1 \
+// RUN: env -i LC_ALL=C LD_LIBRARY_PATH="%{readfile:/tmp/ld_library_path}" 
CLANG_NO_DEFAULT_CONFIG=1 \
 // RUN:   %clang %s -### -o %t.o --target=i386-unknown-linux \
 // RUN:     --sysroot=%S/Inputs/basic_linux_tree \
 // RUN:     --rtlib=platform --unwindlib=platform -no-pie \
 // RUN:     2>&1 | FileCheck --check-prefix=CHECK-LD-32 %s
 //
-// RUN: env -i LC_ALL=C PATH="" LD_LIBRARY_PATH="$LD_LIBRARY_PATH" 
CLANG_NO_DEFAULT_CONFIG=1 \
+// RUN: env -i LC_ALL=C PATH="" 
LD_LIBRARY_PATH="%{readfile:/tmp/ld_library_path}" CLANG_NO_DEFAULT_CONFIG=1 \
 // RUN:   %clang %s -### -o %t.o --target=i386-unknown-linux \
 // RUN:     --sysroot=%S/Inputs/basic_linux_tree \
 // RUN:     --rtlib=platform --unwindlib=platform -no-pie \
diff --git a/clang/test/Driver/program-path-priority.c 
b/clang/test/Driver/program-path-priority.c
index b88c0f29f7f33..bb434482f90d4 100644
--- a/clang/test/Driver/program-path-priority.c
+++ b/clang/test/Driver/program-path-priority.c
@@ -87,8 +87,8 @@
 
 /// <default-triple>-gcc has lowest priority so <triple>-gcc
 /// on PATH beats default triple in program path
-// RUN: DEFAULT_TRIPLE=`%t/clang --version | grep "Target:" | cut -d ' ' -f2`
-// RUN: touch %t/$DEFAULT_TRIPLE-gcc && chmod +x %t/$DEFAULT_TRIPLE-gcc
+// RUN: %t/clang --version | grep "Target:" | cut -d ' ' -f2 > 
%t.default_triple
+// RUN: touch %t/%{readfile:%t.default_triple}-gcc && chmod +x 
%t/%{readfile:%t.default_triple}-gcc
 // RUN: touch %t/%target_triple-gcc && chmod +x %t/%target_triple-gcc
 // RUN: env "PATH=%t/env/" %t/clang -### -target notreal-none-elf %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=DEFAULT_TRIPLE_GCC %s
@@ -101,7 +101,7 @@
 // DEFAULT_TRIPLE_NO_NOTREAL: env/gcc"
 // DEFAULT_TRIPLE_NO_NOTREAL-NOT: -gcc"
 
-/// Pick "gcc" as a fallback. Don't pick $DEFAULT_TRIPLE-gcc.
+/// Pick "gcc" as a fallback. Don't pick DEFAULT_TRIPLE-gcc.
 // RUN: rm %t/env/gcc
 // RUN: env "PATH=%t/env/" %t/clang -### -target notreal-none-elf %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=DEFAULT_TRIPLE_NO_OTHERS %s
@@ -110,9 +110,9 @@
 /// -B paths are searched separately so default triple will win
 /// if put in one of those even if other paths have higher priority names
 // RUN: mkdir -p %t/prefix
-/// One of these will fail when $DEFAULT_TRIPLE == %target_triple
-// RUN: test -f %t/$DEFAULT_TRIPLE-gcc && \
-// RUN:   mv %t/$DEFAULT_TRIPLE-gcc %t/prefix || true
+/// One of these will fail when %{readfile:%t.default_triple} == %target_triple
+// RUN: test -f %t/%{readfile:%t.default_triple}-gcc && \
+// RUN:   mv %t/%{readfile:%t.default_triple}-gcc %t/prefix || true
 // RUN: test -f %t/%target_triple-gcc && \
 // RUN:   mv %t/%target_triple-gcc %t/prefix || true
 // RUN: touch %t/notreal-none-elf-gcc && chmod +x %t/notreal-none-elf-gcc
@@ -123,8 +123,8 @@
 // DEFAULT_TRIPLE_IN_PREFIX-NOT: notreal-none-elf-gcc"
 
 /// Only if there is nothing in the prefix will we search other paths
-/// -f in case $DEFAULT_TRIPLE == %target_triple
-// RUN: rm -f %t/prefix/$DEFAULT_TRIPLE-gcc %t/prefix/%target_triple-gcc 
%t/prefix/gcc
+/// -f in case %{readfile:%t.default_triple} == %target_triple
+// RUN: rm -f %t/prefix/%{readfile:%t.default_triple}-gcc 
%t/prefix/%target_triple-gcc %t/prefix/gcc
 // RUN: env "PATH=" %t/clang -### -canonical-prefixes 
--target=notreal-none-elf %s -B %t/prefix 2>&1 | \
 // RUN:   FileCheck --check-prefix=EMPTY_PREFIX_DIR1 %s
 // EMPTY_PREFIX_DIR1: gcc"
diff --git a/clang/test/Modules/relative-resource-dir.m 
b/clang/test/Modules/relative-resource-dir.m
index 4f88150a2ba4c..96f2d8efc7860 100644
--- a/clang/test/Modules/relative-resource-dir.m
+++ b/clang/test/Modules/relative-resource-dir.m
@@ -1,9 +1,9 @@
 // UNSUPPORTED: target={{.*}}-zos{{.*}}, target={{.*}}-aix{{.*}}
-// REQUIRES: shell
 
-// RUN: EXPECTED_RESOURCE_DIR=`%clang -print-resource-dir` && \
+// RUN: %clang -print-resource-dir | tr -d '\n' > %t.resource_dir
+// RUN: env EXPECTED_RESOURCE_DIR="%{readfile:%t.resource_dir}" && \
 // RUN:  mkdir -p %t && rm -rf %t/resource-dir && \
-// RUN:  cp -R $EXPECTED_RESOURCE_DIR %t/resource-dir
+// RUN:  cp -R %{readfile:%t.resource_dir} %t/resource-dir
 // RUN: cd %t && %clang -cc1 -x objective-c -fmodules -fmodule-format=obj \
 // RUN:   -fimplicit-module-maps -fmodules-cache-path=%t.mcp \
 // RUN:   -fbuiltin-headers-in-system-modules \
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:<filename>}  Reads the file specified.
  ======================= ==============
 
 Other substitutions are provided that are variations on this base set and
diff --git a/llvm/test/tools/llvm-cgdata/empty.test 
b/llvm/test/tools/llvm-cgdata/empty.test
index 52d0dfb87623f..7e42db5ed8512 100644
--- a/llvm/test/tools/llvm-cgdata/empty.test
+++ b/llvm/test/tools/llvm-cgdata/empty.test
@@ -35,3 +35,4 @@ RUN: printf '\000\000\000\000' >> %t_header.cgdata
 RUN: printf '\040\000\000\000\000\000\000\000' >> %t_header.cgdata
 RUN: printf '\040\000\000\000\000\000\000\000' >> %t_header.cgdata
 RUN: diff %t_header.cgdata %t_emptyheader.cgdata
+RUN: echo %{readfile:/tmp/test} > /tmp/test
diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index 90c2c6479b004..5daa8887e4c80 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -720,6 +720,23 @@ def processRedirects(cmd, stdin_source, cmd_shenv, 
opened_files):
     return std_fds
 
 
+def _expandLateSubstitutions(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)
+            with open(filePath) as fileHandle:
+                return fileHandle.read()
+
+        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 +851,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(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..4246064cf7bfc
--- /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/lit.cfg 
b/llvm/utils/lit/tests/Inputs/shtest-readfile/lit.cfg
new file mode 100644
index 0000000000000..25651f2cd4832
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-readfile/lit.cfg
@@ -0,0 +1,8 @@
+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
+config.substitutions.append(("%{python}", '"%s"' % (sys.executable)))
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..3d203d411379d
--- /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..6855d27d66d35
--- /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 setup 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..8ba8e53b32966
--- /dev/null
+++ b/llvm/utils/lit/tests/shtest-readfile.py
@@ -0,0 +1,17 @@
+## Tests the readfile substitution
+
+# RUN: not %{lit} -a -v %{inputs}/shtest-readfile | FileCheck 
-match-full-lines %s
+
+# CHECK: -- Testing: 3 tests{{.*}}
+
+# CHECK-LABEL: FAIL: shtest-readfile :: absolute-paths.txt ({{[^)]*}})
+# CHECK: echo hello
+# CHECK: # executed command: echo '%{readfile:{{.*}}}'
+
+# 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:{{.*}}.1}' '%{readfile:{{.*}}.2}'

_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to