[llvm] [lit] Add readfile substitution (PR #158441)
Aiden Grossman via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 17 09:18:41 PDT 2025
https://github.com/boomanaiden154 updated https://github.com/llvm/llvm-project/pull/158441
>From 98b433083ec1f9dab8343191ac1c2451cac3373b Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sat, 13 Sep 2025 21:44:58 +0000
Subject: [PATCH 1/4] =?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
---
llvm/docs/CommandGuide/lit.rst | 1 +
llvm/test/tools/llvm-cgdata/empty.test | 1 +
llvm/utils/lit/lit/TestRunner.py | 17 +++++++++++++++++
.../Inputs/shtest-readfile/.lit_test_times.txt | 1 +
.../Output/absolute-paths.txt.tmp | 1 +
.../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 +++++++++++++++++
10 files changed, 67 insertions(+)
create mode 100644 llvm/utils/lit/tests/Inputs/shtest-readfile/.lit_test_times.txt
create mode 100644 llvm/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp
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/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..8d565e8bad53a 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -719,6 +719,20 @@ 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():
@@ -834,6 +848,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/.lit_test_times.txt b/llvm/utils/lit/tests/Inputs/shtest-readfile/.lit_test_times.txt
new file mode 100644
index 0000000000000..9802a6bcd406a
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-readfile/.lit_test_times.txt
@@ -0,0 +1 @@
+-4.514933e-03 absolute-paths.txt
diff --git a/llvm/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp b/llvm/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp
new file mode 100644
index 0000000000000..b6fc4c620b67d
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp
@@ -0,0 +1 @@
+hello
\ No newline at end of file
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 7901d972ce08bf10617f9e07c90d2f83746dc52e Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sat, 13 Sep 2025 21:51:22 +0000
Subject: [PATCH 2/4] formatting
Created using spr 1.3.6
---
llvm/utils/lit/lit/TestRunner.py | 6 ++++--
.../lit/tests/Inputs/shtest-readfile/.lit_test_times.txt | 1 -
.../Inputs/shtest-readfile/Output/absolute-paths.txt.tmp | 1 -
3 files changed, 4 insertions(+), 4 deletions(-)
delete mode 100644 llvm/utils/lit/tests/Inputs/shtest-readfile/.lit_test_times.txt
delete mode 100644 llvm/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp
diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index 8d565e8bad53a..0268c4801aa07 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -719,19 +719,21 @@ 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):
diff --git a/llvm/utils/lit/tests/Inputs/shtest-readfile/.lit_test_times.txt b/llvm/utils/lit/tests/Inputs/shtest-readfile/.lit_test_times.txt
deleted file mode 100644
index 9802a6bcd406a..0000000000000
--- a/llvm/utils/lit/tests/Inputs/shtest-readfile/.lit_test_times.txt
+++ /dev/null
@@ -1 +0,0 @@
--4.514933e-03 absolute-paths.txt
diff --git a/llvm/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp b/llvm/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp
deleted file mode 100644
index b6fc4c620b67d..0000000000000
--- a/llvm/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp
+++ /dev/null
@@ -1 +0,0 @@
-hello
\ No newline at end of file
>From 82bf25b01ade95c5a44d22677d19eda10f7257b8 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sat, 13 Sep 2025 21:57:05 +0000
Subject: [PATCH 3/4] formatting
Created using spr 1.3.6
---
llvm/utils/lit/lit/TestRunner.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index 0268c4801aa07..5daa8887e4c80 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -736,6 +736,7 @@ def _replaceReadFile(match):
return arguments
+
def _executeShCmd(cmd, shenv, results, timeoutHelper):
if timeoutHelper.timeoutReached():
# Prevent further recursion if the timeout has been hit
>From 1599e056b4f11f1b61a050b5907b0bbfc3e66d9a Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Wed, 17 Sep 2025 16:18:30 +0000
Subject: [PATCH 4/4] feedback
Created using spr 1.3.6
---
llvm/utils/lit/lit/TestRunner.py | 12 ++++++++----
.../Inputs/shtest-readfile/file-does-not-exist.txt | 4 ++++
llvm/utils/lit/tests/shtest-readfile.py | 6 +++++-
3 files changed, 17 insertions(+), 5 deletions(-)
create mode 100644 llvm/utils/lit/tests/Inputs/shtest-readfile/file-does-not-exist.txt
diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index 19462431e7deb..daa051dcfc4b1 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -720,7 +720,7 @@ def processRedirects(cmd, stdin_source, cmd_shenv, opened_files):
return std_fds
-def _expandLateSubstitutions(arguments, cwd):
+def _expandLateSubstitutions(cmd, arguments, cwd):
for i, arg in enumerate(arguments):
if not isinstance(arg, str):
continue
@@ -729,8 +729,12 @@ 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()
+ try:
+ with open(filePath) as fileHandle:
+ return fileHandle.read()
+ except FileNotFoundError as error:
+ print(error)
+ raise InternalShellError(cmd, "File does not exist: %s" % filePath)
arguments[i] = re.sub(r"%{readfile:([^}]*)}", _replaceReadFile, arg)
@@ -852,7 +856,7 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper):
args[0] = expand_glob(args[0], cmd_shenv.cwd)[0]
# Expand all late substitutions.
- args = _expandLateSubstitutions(args, cmd_shenv.cwd)
+ 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):
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/shtest-readfile.py b/llvm/utils/lit/tests/shtest-readfile.py
index 245ea371bc14d..c25a643b4eff8 100644
--- a/llvm/utils/lit/tests/shtest-readfile.py
+++ b/llvm/utils/lit/tests/shtest-readfile.py
@@ -2,12 +2,16 @@
# RUN: not %{lit} -a -v %{inputs}/shtest-readfile | FileCheck -match-full-lines -DTEMP_PATH=%S/Inputs/shtest-readfile/Output %s
-# CHECK: -- Testing: 3 tests{{.*}}
+# 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 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}'
More information about the llvm-commits
mailing list