[llvm] 271eee9 - [lit] Stop bare env from short-circuiting the pipeline (#214512)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 10 20:28:46 PDT 2026
Author: Prasoon Kumar
Date: 2026-08-11T08:58:41+05:30
New Revision: 271eee9a5520bc6af6f474d1a2abd9f2c2b7115e
URL: https://github.com/llvm/llvm-project/commit/271eee9a5520bc6af6f474d1a2abd9f2c2b7115e
DIFF: https://github.com/llvm/llvm-project/commit/271eee9a5520bc6af6f474d1a2abd9f2c2b7115e.diff
LOG: [lit] Stop bare env from short-circuiting the pipeline (#214512)
env with no trailing subcommand returns early from _executeShCmd,
skipping the rest of the pipeline. A RUN line like env | FileCheck never
runs FileCheck.
Runs it as an in-process pipeline stage instead, reusing the existing
InProcessPipe implementation.
Fixes #115578.
Also fixes six Inputs/shtest-env-positive fixtures that could never have
passed once FileCheck actually ran. Five expected KEY = VALUE from
FileCheck while env has always printed KEY=VALUE. Two RUN lines used a
check-prefix that matched no CHECK line in the file. One needed
--allow-empty since env -i produces no output at all.
Added:
Modified:
llvm/utils/lit/lit/TestRunner.py
llvm/utils/lit/tests/Inputs/shtest-env-positive/env-calls-env.txt
llvm/utils/lit/tests/Inputs/shtest-env-positive/env-i.txt
llvm/utils/lit/tests/Inputs/shtest-env-positive/env-no-subcommand.txt
llvm/utils/lit/tests/Inputs/shtest-env-positive/env-u.txt
llvm/utils/lit/tests/Inputs/shtest-env-positive/env.txt
llvm/utils/lit/tests/Inputs/shtest-env-positive/mixed.txt
Removed:
################################################################################
diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index 681a56d9b45db..30a39745d01b9 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -451,6 +451,33 @@ def _should_run_inproc(
return builtin_fn is not None and not not_crash and cmd_shenv is shenv
+def _make_env_print_fn(env: dict) -> RunFn:
+ """Builds a RunFn that writes env's environment as sorted KEY=VALUE lines.
+
+ Args:
+ env: The shell environment to print, already updated by updateEnv()
+ for this stage's KEY=VALUE/-u/-i arguments.
+
+ Returns:
+ A RunFn ignoring argv/stdin/stderr/cwd, writing the sorted
+ environment to stdout and returning 0.
+ """
+
+ def run(
+ argv: List[str],
+ stdin: BinaryIO,
+ stdout: IO[bytes] | ByteWriter | None,
+ stderr: IO[bytes] | ByteWriter | None,
+ cwd: str,
+ ) -> int:
+ assert stdout is not None
+ env_str = "\n".join(f"{key}={value}" for key, value in sorted(env.items()))
+ stdout.write(env_str.encode())
+ return 0
+
+ return run
+
+
def _run_inproc_stage(
args: List[str],
builtin_fn: RunFn,
@@ -582,6 +609,7 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper):
not_args = []
not_count = 0
not_crash = False
+ bare_env = False
# Expand all late substitutions.
args = _expandLateSubstitutions(
@@ -605,16 +633,9 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper):
)
args = updateEnv(cmd_shenv, args)
if not args:
- # Return the environment variables if no argument is provided.
- env_str = "\n".join(
- f"{key}={value}" for key, value in sorted(cmd_shenv.env.items())
- )
- results.append(
- ShellCommandResult(
- j, env_str, "", 0, timeoutHelper.timeoutReached(), []
- )
- )
- return 0
+ # 'env' by itself is this stage's command
+ bare_env = True
+ break
elif args[0] == "not":
not_args.append(args.pop(0))
not_count += 1
@@ -631,48 +652,59 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper):
else:
break
- # Handle in-process builtins.
- #
- # Handle "echo" as a builtin if it is not part of a pipeline. This
- # greatly speeds up tests that construct input files by repeatedly
- # echo-appending to a file.
- # FIXME: Standardize on the builtin echo implementation. We can use a
- # temporary file to sidestep blocking pipe write issues.
-
- # Ensure args[0] is hashable.
- args[0] = expand_glob(args[0], cmd_shenv.cwd)[0]
-
- 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
- # approach of complaining.
- if not cmd_shenv is shenv:
- raise InternalShellError(
- j, "Error: 'env' cannot call '{}'".format(args[0])
- )
- if not_crash:
- raise InternalShellError(
- j, "Error: 'not --crash' cannot call" " '{}'".format(args[0])
- )
- if len(cmd.commands) != 1:
- raise InternalShellError(
- j,
- "Unsupported: '{}' cannot be part" " of a pipeline".format(args[0]),
- )
- result = inproc_builtin(Command(args, j.redirects), cmd_shenv)
- if not_count % 2:
- result.exitCode = int(not result.exitCode)
- result.command.args = j.args
- results.append(result)
- return result.exitCode
-
- builtin_fn = pipeline_builtins.get(args[0])
- use_inproc = _should_run_inproc(builtin_fn, not_crash, cmd_shenv, shenv)
- if not use_inproc and args[0] in builtin_commands:
- args.insert(0, sys.executable)
- cmd_shenv.env["PYTHONPATH"] = os.path.dirname(os.path.abspath(__file__))
- args[1] = os.path.join(builtin_commands_dir, args[1] + ".py")
-
+ if bare_env:
+ # A bare 'env' (no subcommand) leaves args empty. Without this
+ # handling, the stage would have no command to run, stopping
+ # the pipeline and dropping downstream stages (e.g. a trailing
+ # 'FileCheck', lit issue #115578). Instead, execute in-process
+ # to print the environment as this stage's output, so
+ # downstream commands still receive input and run.
+ args = ["env"]
+ builtin_fn = _make_env_print_fn(cmd_shenv.env)
+ use_inproc = True
+ else:
+ # Handle in-process builtins.
+ #
+ # Handle "echo" as a builtin if it is not part of a pipeline. This
+ # greatly speeds up tests that construct input files by repeatedly
+ # echo-appending to a file.
+ # FIXME: Standardize on the builtin echo implementation. We can use a
+ # temporary file to sidestep blocking pipe write issues.
+
+ # Ensure args[0] is hashable.
+ args[0] = expand_glob(args[0], cmd_shenv.cwd)[0]
+
+ 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
+ # approach of complaining.
+ if not cmd_shenv is shenv:
+ raise InternalShellError(
+ j, "Error: 'env' cannot call '{}'".format(args[0])
+ )
+ if not_crash:
+ raise InternalShellError(
+ j, "Error: 'not --crash' cannot call" " '{}'".format(args[0])
+ )
+ if len(cmd.commands) != 1:
+ raise InternalShellError(
+ j,
+ "Unsupported: '{}' cannot be part"
+ " of a pipeline".format(args[0]),
+ )
+ result = inproc_builtin(Command(args, j.redirects), cmd_shenv)
+ if not_count % 2:
+ result.exitCode = int(not result.exitCode)
+ result.command.args = j.args
+ results.append(result)
+ return result.exitCode
+
+ builtin_fn = pipeline_builtins.get(args[0])
+ use_inproc = _should_run_inproc(builtin_fn, not_crash, cmd_shenv, shenv)
+ if not use_inproc and args[0] in builtin_commands:
+ args.insert(0, sys.executable)
+ cmd_shenv.env["PYTHONPATH"] = os.path.dirname(os.path.abspath(__file__))
+ args[1] = os.path.join(builtin_commands_dir, args[1] + ".py")
# We had to search through the 'not' commands to find all the 'env'
# commands and any other in-process builtin command. We don't want to
diff --git a/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-calls-env.txt b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-calls-env.txt
index ee40c60a1e4b6..bf96212468998 100644
--- a/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-calls-env.txt
+++ b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-calls-env.txt
@@ -3,14 +3,14 @@
## Check that internal env can call internal env.
# RUN: env env | FileCheck -check-prefix=CHECK-2-EMPTY-ARGS %s
#
-# CHECK-2-EMPTY-ARGS: BAR = 2
-# CHECK-2-EMPTY-ARGS: FOO = 1
+# CHECK-2-EMPTY-ARGS: BAR=2
+# CHECK-2-EMPTY-ARGS: FOO=1
## Check setting variables in a nested env call.
# RUN: env FOO=2 env BAR=1 | FileCheck -check-prefix=CHECK-2-VAL %s
#
-# CHECK-2-VAL: BAR = 1
-# CHECK-2-VAL: FOO = 2
+# CHECK-2-VAL: BAR=1
+# CHECK-2-VAL: FOO=2
## Check unsetting variables in a nested env call.
# RUN: env -u FOO env -u BAR | FileCheck -check-prefix=CHECK-2-U %s
@@ -22,11 +22,11 @@
# RUN: env -u FOO BAR=1 env -u BAR FOO=2 | FileCheck -check-prefix=CHECK-2-U-VAL %s
#
# CHECK-2-U-VAL-NOT: BAR
-# CHECK-2-U-VAL: FOO = 2
+# CHECK-2-U-VAL: FOO=2
## Check setting, unsetting, and adding a new variable in nested env calls.
# RUN: env -u FOO BAR=1 env -u BAR FOO=2 env BAZ=3 | FileCheck -check-prefix=CHECK-3 %s
#
# CHECK-3-NOT: BAR
-# CHECK-3: BAZ = 3
-# CHECK-3: FOO = 2
+# CHECK-3: BAZ=3
+# CHECK-3: FOO=2
diff --git a/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-i.txt b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-i.txt
index 2a66db1d48d05..0ea9110a2cac1 100644
--- a/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-i.txt
+++ b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-i.txt
@@ -5,19 +5,19 @@
# RUN: env | FileCheck --check-prefix=CHECK-ENV-PRESET %s
## Check clearing the entire environment.
#
-# RUN: env -i | FileCheck --check-prefix=CHECK-ENV-CLEAR-1 %s
+# RUN: env -i | FileCheck --check-prefix=CHECK-ENV-CLEAR --allow-empty %s
#
## Check setting a variable in a clear environment.
#
-# RUN: env -i BAZ=3 | FileCheck --check-prefix=CHECK-ENV-ONE-1 %s
+# RUN: env -i BAZ=3 | FileCheck --check-prefix=CHECK-ENV-ONE %s
#
-# CHECK-ENV-PRESET: BAR = 2
-# CHECK-ENV-PRESET: FOO = 1
+# CHECK-ENV-PRESET: BAR=2
+# CHECK-ENV-PRESET: FOO=1
# CHECK-ENV-CLEAR-NOT: BAR
# CHECK-ENV-CLEAR-NOT: FOO
# CHECK-ENV-ONE-NOT: BAR
-# CHECK-ENV-ONE: BAZ = 3
+# CHECK-ENV-ONE: BAZ=3
# CHECK-ENV-ONE-NOT: FOO
diff --git a/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-no-subcommand.txt b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-no-subcommand.txt
index dded9069c44f2..235b744b52acb 100644
--- a/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-no-subcommand.txt
+++ b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-no-subcommand.txt
@@ -1,5 +1,4 @@
## Tests the env command in various scenarios: without arguments, setting, unsetting, and mixing envrionment variables.
-# FIXME: All of these tests are broken and will not even call FileCheck.
## Check default environment.
# RUN: env | FileCheck -check-prefix=NO-ARGS %s
@@ -30,7 +29,7 @@
# MIXED-SET-UNSET: QUX=3
## Mixed set and unset with additional variable.
-# RUN: env -u FOO BAR=1 -u BAR FOO=2 BAZ=4 | FileCheck -check-prefix=MIXED-SET-UNSET-ADD-3 %s
+# RUN: env -u FOO BAR=1 -u BAR FOO=2 BAZ=4 | FileCheck -check-prefix=MIXED-SET-UNSET-ADD %s
#
# MIXED-SET-UNSET-ADD-NOT: BAR
# MIXED-SET-UNSET-ADD: BAZ=4
diff --git a/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-u.txt b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-u.txt
index 2945639c0642d..810d11ceb7c49 100644
--- a/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-u.txt
+++ b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-u.txt
@@ -12,10 +12,10 @@
#
# RUN: env -u FOO -u BAR | FileCheck --check-prefix=CHECK-ENV-UNSET-MULTIPLE %s
-# CHECK-ENV-PRESET: BAR = 2
-# CHECK-ENV-PRESET: FOO = 1
+# CHECK-ENV-PRESET: BAR=2
+# CHECK-ENV-PRESET: FOO=1
-# CHECK-ENV-UNSET-1: BAR = 2
+# CHECK-ENV-UNSET-1: BAR=2
# CHECK-ENV-UNSET-1-NOT: FOO
# CHECK-ENV-UNSET-MULTIPLE-NOT: BAR
diff --git a/llvm/utils/lit/tests/Inputs/shtest-env-positive/env.txt b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env.txt
index 74a2a65d260f4..4dff8ab4370e5 100644
--- a/llvm/utils/lit/tests/Inputs/shtest-env-positive/env.txt
+++ b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env.txt
@@ -8,8 +8,8 @@
#
# RUN: env A_FOO=1 B_BAR=2 C_OOF=3 | FileCheck --check-prefix=CHECK-ENV-MULTIPLE %s
-# CHECK-ENV-1: A_FOO = 999
+# CHECK-ENV-1: A_FOO=999
-# CHECK-ENV-MULTIPLE: A_FOO = 1
-# CHECK-ENV-MULTIPLE: B_BAR = 2
-# CHECK-ENV-MULTIPLE: C_OOF = 3
+# CHECK-ENV-MULTIPLE: A_FOO=1
+# CHECK-ENV-MULTIPLE: B_BAR=2
+# CHECK-ENV-MULTIPLE: C_OOF=3
diff --git a/llvm/utils/lit/tests/Inputs/shtest-env-positive/mixed.txt b/llvm/utils/lit/tests/Inputs/shtest-env-positive/mixed.txt
index c2c4e8bfdfc8b..7ee4f5226a926 100644
--- a/llvm/utils/lit/tests/Inputs/shtest-env-positive/mixed.txt
+++ b/llvm/utils/lit/tests/Inputs/shtest-env-positive/mixed.txt
@@ -8,11 +8,11 @@
#
# RUN: env A_FOO=1 -u FOO B_BAR=2 -u BAR C_OOF=3 | FileCheck --check-prefix=CHECK-ENV-MULTIPLE %s
-# CHECK-ENV-1: A_FOO = 999
+# CHECK-ENV-1: A_FOO=999
# CHECK-ENV-1-NOT: FOO
-# CHECK-ENV-MULTIPLE: A_FOO = 1
+# CHECK-ENV-MULTIPLE: A_FOO=1
# CHECK-ENV-MULTIPLE-NOT: BAR
-# CHECK-ENV-MULTIPLE: B_BAR = 2
-# CHECK-ENV-MULTIPLE: C_OOF = 3
+# CHECK-ENV-MULTIPLE: B_BAR=2
+# CHECK-ENV-MULTIPLE: C_OOF=3
# CHECK-ENV-MULTIPLE-NOT: FOO
More information about the llvm-commits
mailing list