Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions llvm/docs/CommandGuide/lit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
41 changes: 41 additions & 0 deletions llvm/utils/lit/lit/TestRunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,26 @@ 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 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
Expand Down Expand Up @@ -834,6 +854,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
Expand Down Expand Up @@ -2389,6 +2412,20 @@ def runOnce(
status, output, attempts=i + 1, max_allowed_attempts=attempts
)

def _expandLateSubstitutionsExternal(commandLine):
filePaths = []
def _replaceReadFile(match):
filePath = match.group(1)
filePaths.append(filePath)
return "$(cat %s)" % filePath

commandLine = re.sub(r"%{readfile:([^}]*)}", _replaceReadFile, commandLine)
# Add test commands before the command to check if the file exists as
# cat inside a subshell will never return a non-zero exit code outside
# of the subshell.
for filePath in filePaths:
commandLine = "%s && test -e %s" % (commandLine, filePath)
return commandLine

def executeShTest(
test, litConfig, useExternalSh, extra_substitutions=[], preamble_commands=[]
Expand Down Expand Up @@ -2420,4 +2457,8 @@ def executeShTest(
recursion_limit=test.config.recursiveExpansionLimit,
)

if useExternalSh:
for index, command in enumerate(script):
script[index] = _expandLateSubstitutionsExternal(command)

return _runShTest(test, litConfig, useExternalSh, script, tmpBase)
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Test that readfile reports information appropriately when the file specified
## does not exist.

# RUN: echo %{readfile:/file/does/not/exist}
19 changes: 19 additions & 0 deletions llvm/utils/lit/tests/Inputs/shtest-readfile/lit.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import os

import lit.formats
import lit.util

config.name = "shtest-readfile"
config.suffixes = [".txt"]
lit_shell_env = os.environ.get("LIT_USE_INTERNAL_SHELL")
use_lit_shell = lit.util.pythonize_bool(lit_shell_env)
config.test_format = lit.formats.ShTest(execute_external=not use_lit_shell)
config.test_source_root = None
config.test_exec_root = None

# If we are testing with the external shell, remove the fake-externals from
# PATH so that we use mkdir in the tests.
if not use_lit_shell:
path_parts = config.environment["PATH"].split(os.path.pathsep)
path_parts = [path_part for path_part in path_parts if "fake-externals" not in path_part]
config.environment["PATH"] = os.path.pathsep.join(path_parts)
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions llvm/utils/lit/tests/Inputs/shtest-readfile/two-same-line.txt
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions llvm/utils/lit/tests/shtest-readfile-external.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Tests the readfile substitution.

# RUN: env LIT_USE_INTERNAL_SHELL=0 not %{lit} -a -v %{inputs}/shtest-readfile | FileCheck -match-full-lines -DTEMP_PATH=%S/Inputs/shtest-readfile/Output %s

# CHECK: -- Testing: 4 tests{{.*}}

# CHECK-LABEL: FAIL: shtest-readfile :: absolute-paths.txt ({{[^)]*}})
# CHECK: echo $(cat [[TEMP_PATH]]/absolute-paths.txt.tmp) && test -e /home/gha/llvm-project/build/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp {{.*}}
# CHECK: + echo hello

# CHECK-LABEL: FAIL: shtest-readfile :: file-does-not-exist.txt ({{[^)]*}})
# CHECK: echo $(cat /file/does/not/exist) && test -e /file/does/not/exist {{.*}}
# CHECK: cat: /file/does/not/exist: No such file or directory

# CHECK-LABEL: FAIL: shtest-readfile :: relative-paths.txt ({{[^)]*}})
# CHECK: echo $(cat rel_path_test_folder/test_file) && test -e rel_path_test_folder/test_file {{.*}}
# CHECK: + echo hello

# CHECK-LABEL: FAIL: shtest-readfile :: two-same-line.txt ({{[^)]*}})
# CHECK: echo $(cat /home/gha/llvm-project/build/utils/lit/tests/Inputs/shtest-readfile/Output/two-same-line.txt.tmp.1) $(cat /home/gha/llvm-project/build/utils/lit/tests/Inputs/shtest-readfile/Output/two-same-line.txt.tmp.2) && test -e /home/gha/llvm-project/build/utils/lit/tests/Inputs/shtest-readfile/Output/two-same-line.txt.tmp.1 && test -e /home/gha/llvm-project/build/utils/lit/tests/Inputs/shtest-readfile/Output/two-same-line.txt.tmp.2 {{.*}}
# CHECK: + echo hello bye
21 changes: 21 additions & 0 deletions llvm/utils/lit/tests/shtest-readfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Tests the readfile substitution.

# RUN: env LIT_USE_INTERNAL_SHELL=1 not %{lit} -a -v %{inputs}/shtest-readfile | FileCheck -match-full-lines -DTEMP_PATH=%S/Inputs/shtest-readfile/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 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}'
Loading