[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