[flang-commits] [flang] [flang] Fix execute_command_line cmdstat is not set when error occurs (PR #93023)

Yi Wu via flang-commits flang-commits at lists.llvm.org
Thu Jun 20 07:25:57 PDT 2024


https://github.com/yi-wu-arm updated https://github.com/llvm/llvm-project/pull/93023

>From d57fb6270e544036e2087d47028c980c12dd7f9e Mon Sep 17 00:00:00 2001
From: YI WU <YI.WU2 at arm.com>
Date: Wed, 22 May 2024 12:06:24 +0100
Subject: [PATCH 01/11] [flang] Fix execute_command_line cmdstat is not set
 when error occurs

Fixes: https://github.com/llvm/llvm-project/issues/92929
---
 flang/docs/Intrinsics.md  |  2 --
 flang/runtime/execute.cpp | 14 ++++++++------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index 8853d4d9e1c79..a0671bd36e870 100644
--- a/flang/docs/Intrinsics.md
+++ b/flang/docs/Intrinsics.md
@@ -899,8 +899,6 @@ used in constant expressions have currently no folding support at all.
     - 1: Fork Error (occurs only on POSIX-compatible systems).
     - 2: Execution Error (command exits with status -1).
     - 3: Invalid Command Error (determined by the exit code depending on the system).
-      - On Windows: exit code is 1.
-      - On POSIX-compatible systems: exit code is 127 or 126.
     - 4: Signal error (either stopped or killed by signal, occurs only on POSIX-compatible systems).
   - 0: Otherwise.
 - Asynchronous execution:
diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index 0f5bc5059e21d..b9543e6582bae 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -72,14 +72,14 @@ int TerminationCheck(int status, const Descriptor *cmdstat,
       CheckAndCopyCharsToDescriptor(cmdmsg, "Execution error");
     }
   }
+
 #ifdef _WIN32
   // On WIN32 API std::system returns exit status directly
   int exitStatusVal{status};
-  if (exitStatusVal == 1) {
 #else
   int exitStatusVal{WEXITSTATUS(status)};
-  if (exitStatusVal == 127 || exitStatusVal == 126) {
 #endif
+  if (exitStatusVal != 0) {
     if (!cmdstat) {
       terminator.Crash(
           "Invalid command quit with exit status code: %d", exitStatusVal);
@@ -88,23 +88,25 @@ int TerminationCheck(int status, const Descriptor *cmdstat,
       CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
     }
   }
+
 #if defined(WIFSIGNALED) && defined(WTERMSIG)
   if (WIFSIGNALED(status)) {
     if (!cmdstat) {
-      terminator.Crash("killed by signal: %d", WTERMSIG(status));
+      terminator.Crash("Killed by signal: %d", WTERMSIG(status));
     } else {
       StoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator);
-      CheckAndCopyCharsToDescriptor(cmdmsg, "killed by signal");
+      CheckAndCopyCharsToDescriptor(cmdmsg, "Killed by signal");
     }
   }
 #endif
+
 #if defined(WIFSTOPPED) && defined(WSTOPSIG)
   if (WIFSTOPPED(status)) {
     if (!cmdstat) {
-      terminator.Crash("stopped by signal: %d", WSTOPSIG(status));
+      terminator.Crash("Stopped by signal: %d", WSTOPSIG(status));
     } else {
       StoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator);
-      CheckAndCopyCharsToDescriptor(cmdmsg, "stopped by signal");
+      CheckAndCopyCharsToDescriptor(cmdmsg, "Stopped by signal");
     }
   }
 #endif

>From ca86946dc49f1f45ff5ca7621ae67e845bb812a5 Mon Sep 17 00:00:00 2001
From: YI WU <YI.WU2 at arm.com>
Date: Tue, 28 May 2024 10:11:40 +0100
Subject: [PATCH 02/11] cmdstat will set to 3 when error then set to specific
 value

---
 flang/runtime/execute.cpp | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index b9543e6582bae..b430137e7f143 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -64,15 +64,6 @@ void CheckAndStoreIntToDescriptor(
 // the CMDSTAT variable is not present, error termination is initiated.
 int TerminationCheck(int status, const Descriptor *cmdstat,
     const Descriptor *cmdmsg, Terminator &terminator) {
-  if (status == -1) {
-    if (!cmdstat) {
-      terminator.Crash("Execution error with system status code: %d", status);
-    } else {
-      StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
-      CheckAndCopyCharsToDescriptor(cmdmsg, "Execution error");
-    }
-  }
-
 #ifdef _WIN32
   // On WIN32 API std::system returns exit status directly
   int exitStatusVal{status};
@@ -88,6 +79,14 @@ int TerminationCheck(int status, const Descriptor *cmdstat,
       CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
     }
   }
+  if (status == -1) {
+    if (!cmdstat) {
+      terminator.Crash("Execution error with system status code: %d", status);
+    } else {
+      StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
+      CheckAndCopyCharsToDescriptor(cmdmsg, "Execution error");
+    }
+  }
 
 #if defined(WIFSIGNALED) && defined(WTERMSIG)
   if (WIFSIGNALED(status)) {

>From 20ec03664a27809ffc0d6410485df0878f99b552 Mon Sep 17 00:00:00 2001
From: YI WU <YI.WU2 at arm.com>
Date: Tue, 28 May 2024 20:00:10 +0100
Subject: [PATCH 03/11] slightly more detailed cmdmsg

---
 flang/runtime/execute.cpp               | 3 ++-
 flang/unittests/Runtime/CommandTest.cpp | 6 ++++--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index b430137e7f143..c937a688a3bb1 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -76,7 +76,8 @@ int TerminationCheck(int status, const Descriptor *cmdstat,
           "Invalid command quit with exit status code: %d", exitStatusVal);
     } else {
       StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
-      CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
+      CheckAndCopyCharsToDescriptor(cmdmsg,
+          "Invalid command line: check for exitstat and console printout");
     }
   }
   if (status == -1) {
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 08daa4ba37f26..591911d1df2c7 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -344,7 +344,8 @@ TEST_F(ZeroArguments, ECLInvalidCommandErrorSync) {
   bool wait{true};
   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
   OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
-  OwningPtr<Descriptor> cmdMsg{CharDescriptor("Message ChangedXXXXXXXXX")};
+  OwningPtr<Descriptor> cmdMsg{CharDescriptor(
+      "cmdmsg will not modify the remaining buffer XXXXXXXXXXXXXXXXXXXXX")};
 
   RTNAME(ExecuteCommandLine)
   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
@@ -354,7 +355,8 @@ TEST_F(ZeroArguments, ECLInvalidCommandErrorSync) {
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
 #endif
   CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
-  CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXX");
+  CheckDescriptorEqStr(cmdMsg.get(),
+      "Invalid command line: check for exitstat and console printoutXXXX");
 }
 
 TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {

>From d9b347e21561e11dfb5c63e106d78e95ab2e7662 Mon Sep 17 00:00:00 2001
From: YI WU <YI.WU2 at arm.com>
Date: Thu, 30 May 2024 14:23:59 +0100
Subject: [PATCH 04/11] Revert "slightly more detailed cmdmsg"

This reverts commit 3b285a1024f3115a6af6da1b0d7d637548a0bf1c.
---
 flang/runtime/execute.cpp               | 3 +--
 flang/unittests/Runtime/CommandTest.cpp | 6 ++----
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index c937a688a3bb1..b430137e7f143 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -76,8 +76,7 @@ int TerminationCheck(int status, const Descriptor *cmdstat,
           "Invalid command quit with exit status code: %d", exitStatusVal);
     } else {
       StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
-      CheckAndCopyCharsToDescriptor(cmdmsg,
-          "Invalid command line: check for exitstat and console printout");
+      CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
     }
   }
   if (status == -1) {
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 591911d1df2c7..08daa4ba37f26 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -344,8 +344,7 @@ TEST_F(ZeroArguments, ECLInvalidCommandErrorSync) {
   bool wait{true};
   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
   OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
-  OwningPtr<Descriptor> cmdMsg{CharDescriptor(
-      "cmdmsg will not modify the remaining buffer XXXXXXXXXXXXXXXXXXXXX")};
+  OwningPtr<Descriptor> cmdMsg{CharDescriptor("Message ChangedXXXXXXXXX")};
 
   RTNAME(ExecuteCommandLine)
   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
@@ -355,8 +354,7 @@ TEST_F(ZeroArguments, ECLInvalidCommandErrorSync) {
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
 #endif
   CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
-  CheckDescriptorEqStr(cmdMsg.get(),
-      "Invalid command line: check for exitstat and console printoutXXXX");
+  CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXX");
 }
 
 TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {

>From 7bd1b8ddf823112bdbb8f33e40340d5ef8401a35 Mon Sep 17 00:00:00 2001
From: YI WU <YI.WU2 at arm.com>
Date: Thu, 30 May 2024 14:32:15 +0100
Subject: [PATCH 05/11] add cmdmsg to common exit code 1, 126, 127 for Linux

for Windows, all three of them has a exit code of 1
so can't write cmdmsg for them
---
 flang/runtime/execute.cpp               | 31 +++++++++++++++
 flang/unittests/Runtime/CommandTest.cpp | 50 +++++++++++++++++++++++--
 2 files changed, 77 insertions(+), 4 deletions(-)

diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index b430137e7f143..fe85971052a1e 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -79,6 +79,37 @@ int TerminationCheck(int status, const Descriptor *cmdstat,
       CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
     }
   }
+
+#ifndef _WIN32
+  if (exitStatusVal == 1) {
+    if (!cmdstat) {
+      terminator.Crash("General Error with exit status code: 1");
+    } else {
+      StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
+      CheckAndCopyCharsToDescriptor(cmdmsg,
+          "General Error: a catch-all exit code for a variety of general "
+          "errors.");
+    }
+  } else if (exitStatusVal == 126) {
+    if (!cmdstat) {
+      terminator.Crash("Command cannot execute with exit status code: 126");
+    } else {
+      StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
+      CheckAndCopyCharsToDescriptor(cmdmsg,
+          "Command cannot execute: command was found, but it could not be "
+          "executed.");
+    }
+  } else if (exitStatusVal == 127) {
+    if (!cmdstat) {
+      terminator.Crash("Command not found with exit status code: 127");
+    } else {
+      StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
+      CheckAndCopyCharsToDescriptor(cmdmsg,
+          "Command not found: command was not found in the system's PATH");
+    }
+  }
+#endif
+
   if (status == -1) {
     if (!cmdstat) {
       terminator.Crash("Execution error with system status code: %d", status);
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 08daa4ba37f26..25a878039ff0f 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -339,22 +339,64 @@ TEST_F(ZeroArguments, ECLValidCommandStatusSetSync) {
   CheckDescriptorEqStr(cmdMsg.get(), "No change");
 }
 
-TEST_F(ZeroArguments, ECLInvalidCommandErrorSync) {
-  OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
+TEST_F(ZeroArguments, ECLGeneralErrorCommandErrorSync) {
+  OwningPtr<Descriptor> command{CharDescriptor("cat general-error")};
+  bool wait{true};
+  OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
+  OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
+  OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXX")};
+
+  RTNAME(ExecuteCommandLine)
+  (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
+#ifdef _WIN32
+  CheckDescriptorEqInt(exitStat.get(), 1);
+  CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");
+#else
+  CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
+  CheckDescriptorEqStr(cmdMsg.get(), "General Error: a catch-");
+#endif
+  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
+}
+
+TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
+  OwningPtr<Descriptor> command{CharDescriptor("cd NotExecutedCommand")};
+  bool wait{true};
+  OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
+  OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
+  OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXX")};
+
+  RTNAME(ExecuteCommandLine)
+  (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
+#ifdef _WIN32
+  CheckDescriptorEqInt(exitStat.get(), 1);
+  CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");
+#else
+  CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 126);
+  CheckDescriptorEqStr(cmdMsg.get(), "Command cannot execute:");
+#endif
+  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
+}
+
+TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) {
+  OwningPtr<Descriptor> command{CharDescriptor("NotFoundCommand")};
   bool wait{true};
   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
   OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
-  OwningPtr<Descriptor> cmdMsg{CharDescriptor("Message ChangedXXXXXXXXX")};
+  OwningPtr<Descriptor> cmdMsg{CharDescriptor(
+      "cmdmsg will not modify the remaining buffer XXXXXXXXXXXXXXXXXXXX")};
 
   RTNAME(ExecuteCommandLine)
   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
 #ifdef _WIN32
   CheckDescriptorEqInt(exitStat.get(), 1);
+  CheckDescriptorEqStr(cmdMsg.get(),
+      "Invalid command lineXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
 #else
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
+  CheckDescriptorEqStr(cmdMsg.get(),
+      "Command not found: command was not found in the system's PATHXXX");
 #endif
   CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
-  CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXX");
 }
 
 TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {

>From 48a73ae28ff503c6db0204215012fb998c8beab4 Mon Sep 17 00:00:00 2001
From: YI WU <YI.WU2 at arm.com>
Date: Thu, 30 May 2024 15:18:26 +0100
Subject: [PATCH 06/11] rewrite if-else flow

---
 flang/runtime/execute.cpp               | 23 ++++++++++-------------
 flang/unittests/Runtime/CommandTest.cpp |  2 +-
 2 files changed, 11 insertions(+), 14 deletions(-)

diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index fe85971052a1e..0718ce07a4ab7 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -69,18 +69,6 @@ int TerminationCheck(int status, const Descriptor *cmdstat,
   int exitStatusVal{status};
 #else
   int exitStatusVal{WEXITSTATUS(status)};
-#endif
-  if (exitStatusVal != 0) {
-    if (!cmdstat) {
-      terminator.Crash(
-          "Invalid command quit with exit status code: %d", exitStatusVal);
-    } else {
-      StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
-      CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
-    }
-  }
-
-#ifndef _WIN32
   if (exitStatusVal == 1) {
     if (!cmdstat) {
       terminator.Crash("General Error with exit status code: 1");
@@ -107,8 +95,17 @@ int TerminationCheck(int status, const Descriptor *cmdstat,
       CheckAndCopyCharsToDescriptor(cmdmsg,
           "Command not found: command was not found in the system's PATH");
     }
-  }
+  } else
 #endif
+  if (exitStatusVal != 0) {
+    if (!cmdstat) {
+      terminator.Crash(
+          "Invalid command quit with exit status code: %d", exitStatusVal);
+    } else {
+      StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
+      CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
+    }
+  }
 
   if (status == -1) {
     if (!cmdstat) {
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 25a878039ff0f..a4518eb3c2c97 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -340,7 +340,7 @@ TEST_F(ZeroArguments, ECLValidCommandStatusSetSync) {
 }
 
 TEST_F(ZeroArguments, ECLGeneralErrorCommandErrorSync) {
-  OwningPtr<Descriptor> command{CharDescriptor("cat general-error")};
+  OwningPtr<Descriptor> command{CharDescriptor("cat GeneralErrorCommand")};
   bool wait{true};
   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
   OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};

>From 424a4d263528fe65c8d7c9618058205f46143eeb Mon Sep 17 00:00:00 2001
From: YI WU <YI.WU2 at arm.com>
Date: Thu, 30 May 2024 17:09:56 +0100
Subject: [PATCH 07/11] test fixes Windows

---
 flang/runtime/execute.cpp               | 14 ++++-----
 flang/unittests/Runtime/CommandTest.cpp | 42 ++++++++++++++++---------
 2 files changed, 34 insertions(+), 22 deletions(-)

diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index 0718ce07a4ab7..cb93da6336599 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -62,13 +62,13 @@ void CheckAndStoreIntToDescriptor(
 
 // If a condition occurs that would assign a nonzero value to CMDSTAT but
 // the CMDSTAT variable is not present, error termination is initiated.
-int TerminationCheck(int status, const Descriptor *cmdstat,
+std::int64_t  TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
     const Descriptor *cmdmsg, Terminator &terminator) {
 #ifdef _WIN32
   // On WIN32 API std::system returns exit status directly
-  int exitStatusVal{status};
+  std::int64_t exitStatusVal{status};
 #else
-  int exitStatusVal{WEXITSTATUS(status)};
+  std::int64_t exitStatusVal{WEXITSTATUS(status)};
   if (exitStatusVal == 1) {
     if (!cmdstat) {
       terminator.Crash("General Error with exit status code: 1");
@@ -163,8 +163,8 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
 
   if (wait) {
     // either wait is not specified or wait is true: synchronous mode
-    int status{std::system(newCmd)};
-    int exitStatusVal{TerminationCheck(status, cmdstat, cmdmsg, terminator)};
+    std::int64_t status{std::system(newCmd)};
+    std::int64_t exitStatusVal{TerminationCheck(status, cmdstat, cmdmsg, terminator)};
     // If sync, assigned processor-dependent exit status. Otherwise unchanged
     CheckAndStoreIntToDescriptor(exitstat, exitStatusVal, terminator);
   } else {
@@ -202,7 +202,7 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
         terminator.Crash(
             "CreateProcess failed with error code: %lu.", GetLastError());
       } else {
-        StoreIntToDescriptor(cmdstat, (uint32_t)GetLastError(), terminator);
+        StoreIntToDescriptor(cmdstat, (std::int64_t)GetLastError(), terminator);
         CheckAndCopyCharsToDescriptor(cmdmsg, "CreateProcess failed.");
       }
     }
@@ -230,7 +230,7 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
         }
         exit(EXIT_FAILURE);
       }
-      int status{std::system(newCmd)};
+      std::int64_t status{std::system(newCmd)};
       TerminationCheck(status, cmdstat, cmdmsg, terminator);
       exit(status);
     }
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index a4518eb3c2c97..38060a5ba30c8 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -349,7 +349,7 @@ TEST_F(ZeroArguments, ECLGeneralErrorCommandErrorSync) {
   RTNAME(ExecuteCommandLine)
   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
 #ifdef _WIN32
-  CheckDescriptorEqInt(exitStat.get(), 1);
+  CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
   CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");
 #else
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
@@ -359,7 +359,9 @@ TEST_F(ZeroArguments, ECLGeneralErrorCommandErrorSync) {
 }
 
 TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
-  OwningPtr<Descriptor> command{CharDescriptor("cd NotExecutedCommand")};
+  OwningPtr<Descriptor> command{CharDescriptor(
+      "touch NotExecutedCommandFile && chmod -x NotExecutedCommandFile && "
+      "./NotExecutedCommandFile")};
   bool wait{true};
   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
   OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
@@ -368,13 +370,28 @@ TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
   RTNAME(ExecuteCommandLine)
   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
 #ifdef _WIN32
-  CheckDescriptorEqInt(exitStat.get(), 1);
+  CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
   CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");
 #else
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 126);
   CheckDescriptorEqStr(cmdMsg.get(), "Command cannot execute:");
 #endif
   CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
+
+  // removing the file should have no error on Linux, have error on Windows
+  OwningPtr<Descriptor> commandClean{CharDescriptor("rm -f NotExecutedCommandFile")};
+  OwningPtr<Descriptor> cmdMsgNoErr{CharDescriptor("No Error")};
+  RTNAME(ExecuteCommandLine)
+  (*commandClean.get(), wait, exitStat.get(), cmdStat.get(), cmdMsgNoErr.get());
+#ifdef _WIN32
+  CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
+  CheckDescriptorEqStr(cmdMsgNoErr.get(), "Invalid ");
+    CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
+#else
+  CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
+  CheckDescriptorEqStr(cmdMsgNoErr.get(), "No Error");
+    CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
+#endif
 }
 
 TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) {
@@ -388,9 +405,9 @@ TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) {
   RTNAME(ExecuteCommandLine)
   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
 #ifdef _WIN32
-  CheckDescriptorEqInt(exitStat.get(), 1);
+  CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
   CheckDescriptorEqStr(cmdMsg.get(),
-      "Invalid command lineXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
+      "Invalid command linefy the remaining buffer XXXXXXXXXXXXXXXXXXXX");
 #else
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
   CheckDescriptorEqStr(cmdMsg.get(),
@@ -402,19 +419,17 @@ TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) {
 TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {
   OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
   bool wait{true};
-  OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
   OwningPtr<Descriptor> cmdMsg{CharDescriptor("No Change")};
 
 #ifdef _WIN32
   EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
-                   *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()),
+                   *command.get(), wait, nullptr, nullptr, cmdMsg.get()),
       "Invalid command quit with exit status code: 1");
 #else
   EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
-                   *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()),
-      "Invalid command quit with exit status code: 127");
+                   *command.get(), wait, nullptr, nullptr, cmdMsg.get()),
+      "Command not found with exit status code: 127");
 #endif
-  CheckDescriptorEqInt(exitStat.get(), 404);
   CheckDescriptorEqStr(cmdMsg.get(), "No Change");
 }
 
@@ -428,7 +443,7 @@ TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) {
   RTNAME(ExecuteCommandLine)
   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
 
-  CheckDescriptorEqInt(exitStat.get(), 404);
+  CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 404);
   CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
   CheckDescriptorEqStr(cmdMsg.get(), "No change");
 }
@@ -436,13 +451,10 @@ TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) {
 TEST_F(ZeroArguments, ECLInvalidCommandParentNotTerminatedAsync) {
   OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
   bool wait{false};
-  OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
   OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
 
   EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
-      *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()));
-
-  CheckDescriptorEqInt(exitStat.get(), 404);
+      *command.get(), wait, nullptr, nullptr, cmdMsg.get()));
   CheckDescriptorEqStr(cmdMsg.get(), "No change");
 }
 

>From 5344dc5f1715d98128197d1eb70b6069c7bc5c34 Mon Sep 17 00:00:00 2001
From: YI WU <YI.WU2 at arm.com>
Date: Fri, 31 May 2024 10:44:38 +0100
Subject: [PATCH 08/11] clang format

---
 flang/runtime/execute.cpp               | 5 +++--
 flang/unittests/Runtime/CommandTest.cpp | 7 ++++---
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index cb93da6336599..3dd135507728b 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -62,7 +62,7 @@ void CheckAndStoreIntToDescriptor(
 
 // If a condition occurs that would assign a nonzero value to CMDSTAT but
 // the CMDSTAT variable is not present, error termination is initiated.
-std::int64_t  TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
+std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
     const Descriptor *cmdmsg, Terminator &terminator) {
 #ifdef _WIN32
   // On WIN32 API std::system returns exit status directly
@@ -164,7 +164,8 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
   if (wait) {
     // either wait is not specified or wait is true: synchronous mode
     std::int64_t status{std::system(newCmd)};
-    std::int64_t exitStatusVal{TerminationCheck(status, cmdstat, cmdmsg, terminator)};
+    std::int64_t exitStatusVal{
+        TerminationCheck(status, cmdstat, cmdmsg, terminator)};
     // If sync, assigned processor-dependent exit status. Otherwise unchanged
     CheckAndStoreIntToDescriptor(exitstat, exitStatusVal, terminator);
   } else {
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 38060a5ba30c8..27dce144b8411 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -379,18 +379,19 @@ TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
   CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
 
   // removing the file should have no error on Linux, have error on Windows
-  OwningPtr<Descriptor> commandClean{CharDescriptor("rm -f NotExecutedCommandFile")};
+  OwningPtr<Descriptor> commandClean{
+      CharDescriptor("rm -f NotExecutedCommandFile")};
   OwningPtr<Descriptor> cmdMsgNoErr{CharDescriptor("No Error")};
   RTNAME(ExecuteCommandLine)
   (*commandClean.get(), wait, exitStat.get(), cmdStat.get(), cmdMsgNoErr.get());
 #ifdef _WIN32
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
   CheckDescriptorEqStr(cmdMsgNoErr.get(), "Invalid ");
-    CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
+  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
 #else
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
   CheckDescriptorEqStr(cmdMsgNoErr.get(), "No Error");
-    CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
+  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
 #endif
 }
 

>From c6d4ac03f1dc47254d1e7cb95d039d5627e1e0c4 Mon Sep 17 00:00:00 2001
From: YI WU <YI.WU2 at arm.com>
Date: Fri, 14 Jun 2024 12:33:11 +0100
Subject: [PATCH 09/11] add cmdstat and cmdmsg for common linux exit code 1 126
 127

---
 flang/docs/Intrinsics.md                | 17 +++--
 flang/runtime/execute.cpp               | 99 +++++++++++++++++--------
 flang/unittests/Runtime/CommandTest.cpp | 36 ++++-----
 3 files changed, 98 insertions(+), 54 deletions(-)

diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index a0671bd36e870..d1f7cd8372e24 100644
--- a/flang/docs/Intrinsics.md
+++ b/flang/docs/Intrinsics.md
@@ -893,14 +893,17 @@ used in constant expressions have currently no folding support at all.
 ##### `CMDSTAT`:
 
 - Synchronous execution:
-  - -2: No error condition occurs, but `WAIT` is present with the value `false`, and the processor does not support asynchronous execution.
-  - -1: The processor does not support command line execution.
+  - -2: `ASYNC_NO_SUPPORT_ERR` - No error condition occurs, but `WAIT` is present with the value `false`, and the processor does not support asynchronous execution.
+  - -1: `NO_SUPPORT_ERR` - The processor does not support command line execution. (system returns -1 with errno `ENOENT`)
+  - 0: `CMD_EXECUTED` - Command executed with no error.
   - \+ (positive value): An error condition occurs.
-    - 1: Fork Error (occurs only on POSIX-compatible systems).
-    - 2: Execution Error (command exits with status -1).
-    - 3: Invalid Command Error (determined by the exit code depending on the system).
-    - 4: Signal error (either stopped or killed by signal, occurs only on POSIX-compatible systems).
-  - 0: Otherwise.
+    - 1: `FORK_ERR` - Fork Error (occurs only on POSIX-compatible systems).
+    - 2: `EXECL_ERR` - Execution Error (system returns -1 with other errno).
+    - 3: `COMMAND_EXECUTION_ERR` - Invalid Command Error (exit code 1).
+    - 4: `COMMAND_CANNOT_EXECUTE_ERR` - Command Cannot Execute Error (Linux exit code 126).
+    - 5: `COMMAND_NOT_FOUND_ERR` - Command Not Found Error (Linux exit code 127).
+    - 6: `INVALID_CL_ERR` - Invalid Command Line Error (covers all other non-zero exit codes).
+    - 7: `SIGNAL_ERR` - Signal error (either stopped or killed by signal, occurs only on POSIX-compatible systems).
 - Asynchronous execution:
   - 0 will always be assigned.
 
diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index 3dd135507728b..3b6f17cc286cd 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -13,8 +13,10 @@
 #include "tools.h"
 #include "flang/Runtime/descriptor.h"
 #include <cstdlib>
+#include <errno.h>
 #include <future>
 #include <limits>
+
 #ifdef _WIN32
 #include "flang/Common/windows-include.h"
 #else
@@ -32,13 +34,16 @@ namespace Fortran::runtime {
 // and the processor does not support asynchronous execution. Otherwise it is
 // assigned the value 0
 enum CMD_STAT {
-  ASYNC_NO_SUPPORT_ERR = -2,
-  NO_SUPPORT_ERR = -1,
-  CMD_EXECUTED = 0,
-  FORK_ERR = 1,
-  EXECL_ERR = 2,
-  INVALID_CL_ERR = 3,
-  SIGNAL_ERR = 4
+  ASYNC_NO_SUPPORT_ERR = -2, // system returns -1 with ENOENT
+  NO_SUPPORT_ERR = -1, // Linux setsid() returns -1
+  CMD_EXECUTED = 0, // command executed with no error
+  FORK_ERR = 1, // Linux fork() returns < 0
+  EXECL_ERR = 2, // system returns -1 with other errno
+  COMMAND_EXECUTION_ERR = 3, // exit code 1
+  COMMAND_CANNOT_EXECUTE_ERR = 4, // Linux exit code 126
+  COMMAND_NOT_FOUND_ERR = 5, // Linux exit code 127
+  INVALID_CL_ERR = 6, // cover all other non-zero exit code
+  SIGNAL_ERR = 7
 };
 
 // Override CopyCharsToDescriptor in tools.h, pass string directly
@@ -67,37 +72,43 @@ std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
 #ifdef _WIN32
   // On WIN32 API std::system returns exit status directly
   std::int64_t exitStatusVal{status};
+  if (exitStatusVal != 0) {
+    if (!cmdstat) {
+      terminator.Crash(
+          "Invalid command quit with exit status code: %d", exitStatusVal);
+    } else {
+      StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
+      CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
+    }
+  }
 #else
   std::int64_t exitStatusVal{WEXITSTATUS(status)};
   if (exitStatusVal == 1) {
     if (!cmdstat) {
-      terminator.Crash("General Error with exit status code: 1");
+      terminator.Crash("Command line execution failed with exit code: 1.");
     } else {
-      StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
-      CheckAndCopyCharsToDescriptor(cmdmsg,
-          "General Error: a catch-all exit code for a variety of general "
-          "errors.");
+      StoreIntToDescriptor(cmdstat, COMMAND_EXECUTION_ERR, terminator);
+      CheckAndCopyCharsToDescriptor(
+          cmdmsg, "Command line execution failed with exit code: 1.");
     }
   } else if (exitStatusVal == 126) {
     if (!cmdstat) {
-      terminator.Crash("Command cannot execute with exit status code: 126");
+      terminator.Crash("Command cannot be executed with exit code: 126.");
     } else {
-      StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
-      CheckAndCopyCharsToDescriptor(cmdmsg,
-          "Command cannot execute: command was found, but it could not be "
-          "executed.");
+      StoreIntToDescriptor(cmdstat, COMMAND_CANNOT_EXECUTE_ERR, terminator);
+      CheckAndCopyCharsToDescriptor(
+          cmdmsg, "Command cannot be executed with exit code: 126.");
     }
   } else if (exitStatusVal == 127) {
     if (!cmdstat) {
-      terminator.Crash("Command not found with exit status code: 127");
+      terminator.Crash("Command not found with exit code: 127.");
     } else {
-      StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
-      CheckAndCopyCharsToDescriptor(cmdmsg,
-          "Command not found: command was not found in the system's PATH");
+      StoreIntToDescriptor(cmdstat, COMMAND_NOT_FOUND_ERR, terminator);
+      CheckAndCopyCharsToDescriptor(
+          cmdmsg, "Command not found with exit code: 127.");
     }
-  } else
-#endif
-  if (exitStatusVal != 0) {
+    // capture all other nonzero exit code
+  } else if (exitStatusVal != 0) {
     if (!cmdstat) {
       terminator.Crash(
           "Invalid command quit with exit status code: %d", exitStatusVal);
@@ -106,13 +117,43 @@ std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
       CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
     }
   }
+#endif
 
+  // On both Windows and Linux, errno is set when system returns -1.
   if (status == -1) {
-    if (!cmdstat) {
-      terminator.Crash("Execution error with system status code: %d", status);
+    // On Windows, ENOENT means the command interpreter can't be found.
+    // On Linux, system calls execl with filepath "/bin/sh", ENOENT means the
+    // file pathname does not exist.
+    if (errno = ENOENT) {
+      if (!cmdstat) {
+        terminator.Crash("Command line execution is not supported, system "
+                         "returns -1 with errno ENOENT.");
+      } else {
+        StoreIntToDescriptor(cmdstat, NO_SUPPORT_ERR, terminator);
+        CheckAndCopyCharsToDescriptor(cmdmsg,
+            "Command line execution is not supported, system returns -1 with "
+            "errno ENOENT.");
+      }
     } else {
-      StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
-      CheckAndCopyCharsToDescriptor(cmdmsg, "Execution error");
+      char err_buffer[30];
+      char msg[]{"Execution error with system status code: -1, errno: "};
+#ifdef _WIN32
+      if (strerror_s(err_buffer, sizeof(err_buffer), errno) != 0)
+#else
+      if (strerror_r(errno, err_buffer, sizeof(err_buffer)) != 0)
+#endif
+        terminator.Crash("errno to char errno failed.");
+      char *newMsg{static_cast<char *>(AllocateMemoryOrCrash(
+          terminator, std::strlen(msg) + std::strlen(err_buffer) + 1))};
+      std::strcat(newMsg, err_buffer);
+
+      if (!cmdstat) {
+        terminator.Crash(newMsg);
+      } else {
+        StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
+        CheckAndCopyCharsToDescriptor(cmdmsg, newMsg);
+      }
+      FreeMemory(newMsg);
     }
   }
 
@@ -203,7 +244,7 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
         terminator.Crash(
             "CreateProcess failed with error code: %lu.", GetLastError());
       } else {
-        StoreIntToDescriptor(cmdstat, (std::int64_t)GetLastError(), terminator);
+        StoreIntToDescriptor(cmdstat, ASYNC_NO_SUPPORT_ERR, terminator);
         CheckAndCopyCharsToDescriptor(cmdmsg, "CreateProcess failed.");
       }
     }
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 27dce144b8411..d2f5221ca696f 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -311,8 +311,8 @@ TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
 TEST_F(ZeroArguments, ECLValidCommandAndPadSync) {
   OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
   bool wait{true};
-  OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
-  OwningPtr<Descriptor> cmdStat{EmptyIntDescriptor()};
+  OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
+  OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
   OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
 
   RTNAME(ExecuteCommandLine)
@@ -344,18 +344,19 @@ TEST_F(ZeroArguments, ECLGeneralErrorCommandErrorSync) {
   bool wait{true};
   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
   OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
-  OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXX")};
+  OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXXXXXXXX")};
 
   RTNAME(ExecuteCommandLine)
   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
 #ifdef _WIN32
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
-  CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");
+  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
+  CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXXXX");
 #else
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
-  CheckDescriptorEqStr(cmdMsg.get(), "General Error: a catch-");
-#endif
   CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
+  CheckDescriptorEqStr(cmdMsg.get(), "Command line execution failed");
+#endif
 }
 
 TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
@@ -371,12 +372,13 @@ TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
 #ifdef _WIN32
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
+  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
   CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");
 #else
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 126);
-  CheckDescriptorEqStr(cmdMsg.get(), "Command cannot execute:");
+  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 4);
+  CheckDescriptorEqStr(cmdMsg.get(), "Command cannot be execu");
 #endif
-  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
 
   // removing the file should have no error on Linux, have error on Windows
   OwningPtr<Descriptor> commandClean{
@@ -387,7 +389,7 @@ TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
 #ifdef _WIN32
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
   CheckDescriptorEqStr(cmdMsgNoErr.get(), "Invalid ");
-  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
+  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
 #else
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
   CheckDescriptorEqStr(cmdMsgNoErr.get(), "No Error");
@@ -400,21 +402,19 @@ TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) {
   bool wait{true};
   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
   OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
-  OwningPtr<Descriptor> cmdMsg{CharDescriptor(
-      "cmdmsg will not modify the remaining buffer XXXXXXXXXXXXXXXXXXXX")};
+  OwningPtr<Descriptor> cmdMsg{CharDescriptor("unmodified buffer XXXXXXXXX")};
 
   RTNAME(ExecuteCommandLine)
   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
 #ifdef _WIN32
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
-  CheckDescriptorEqStr(cmdMsg.get(),
-      "Invalid command linefy the remaining buffer XXXXXXXXXXXXXXXXXXXX");
+  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
+  CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXX");
 #else
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
-  CheckDescriptorEqStr(cmdMsg.get(),
-      "Command not found: command was not found in the system's PATHXXX");
+  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 5);
+  CheckDescriptorEqStr(cmdMsg.get(), "Command not found with exit");
 #endif
-  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
 }
 
 TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {
@@ -429,7 +429,7 @@ TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {
 #else
   EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
                    *command.get(), wait, nullptr, nullptr, cmdMsg.get()),
-      "Command not found with exit status code: 127");
+      "Command not found with exit code: 127.");
 #endif
   CheckDescriptorEqStr(cmdMsg.get(), "No Change");
 }
@@ -444,7 +444,7 @@ TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) {
   RTNAME(ExecuteCommandLine)
   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
 
-  CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 404);
+  CheckDescriptorEqInt(exitStat.get(), 404);
   CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
   CheckDescriptorEqStr(cmdMsg.get(), "No change");
 }

>From 1eea4e1d2c0a191220dc737ee92151dbed87ae6b Mon Sep 17 00:00:00 2001
From: YI WU <YI.WU2 at arm.com>
Date: Fri, 14 Jun 2024 20:03:23 +0100
Subject: [PATCH 10/11] test fixes

---
 flang/runtime/execute.cpp               | 2 +-
 flang/unittests/Runtime/CommandTest.cpp | 9 +--------
 2 files changed, 2 insertions(+), 9 deletions(-)

diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index 3b6f17cc286cd..a429c3a462d77 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -124,7 +124,7 @@ std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
     // On Windows, ENOENT means the command interpreter can't be found.
     // On Linux, system calls execl with filepath "/bin/sh", ENOENT means the
     // file pathname does not exist.
-    if (errno = ENOENT) {
+    if (errno == ENOENT) {
       if (!cmdstat) {
         terminator.Crash("Command line execution is not supported, system "
                          "returns -1 with errno ENOENT.");
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index d2f5221ca696f..20bd7a5b5ff35 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -378,19 +378,12 @@ TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 126);
   CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 4);
   CheckDescriptorEqStr(cmdMsg.get(), "Command cannot be execu");
-#endif
-
-  // removing the file should have no error on Linux, have error on Windows
+  // removing the file only on Linux (file is not created on Win)
   OwningPtr<Descriptor> commandClean{
       CharDescriptor("rm -f NotExecutedCommandFile")};
   OwningPtr<Descriptor> cmdMsgNoErr{CharDescriptor("No Error")};
   RTNAME(ExecuteCommandLine)
   (*commandClean.get(), wait, exitStat.get(), cmdStat.get(), cmdMsgNoErr.get());
-#ifdef _WIN32
-  CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
-  CheckDescriptorEqStr(cmdMsgNoErr.get(), "Invalid ");
-  CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
-#else
   CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
   CheckDescriptorEqStr(cmdMsgNoErr.get(), "No Error");
   CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);

>From 9fe9f592e5f9bb52e6322df13f4af84a65a86870 Mon Sep 17 00:00:00 2001
From: YI WU <YI.WU2 at arm.com>
Date: Thu, 20 Jun 2024 15:22:28 +0100
Subject: [PATCH 11/11] move status==-1 back to its original place

---
 flang/runtime/execute.cpp | 76 +++++++++++++++++++--------------------
 1 file changed, 38 insertions(+), 38 deletions(-)

diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index a429c3a462d77..c7f8f386d81f4 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -69,6 +69,44 @@ void CheckAndStoreIntToDescriptor(
 // the CMDSTAT variable is not present, error termination is initiated.
 std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
     const Descriptor *cmdmsg, Terminator &terminator) {
+  // On both Windows and Linux, errno is set when system returns -1.
+  if (status == -1) {
+    // On Windows, ENOENT means the command interpreter can't be found.
+    // On Linux, system calls execl with filepath "/bin/sh", ENOENT means the
+    // file pathname does not exist.
+    if (errno == ENOENT) {
+      if (!cmdstat) {
+        terminator.Crash("Command line execution is not supported, system "
+                         "returns -1 with errno ENOENT.");
+      } else {
+        StoreIntToDescriptor(cmdstat, NO_SUPPORT_ERR, terminator);
+        CheckAndCopyCharsToDescriptor(cmdmsg,
+            "Command line execution is not supported, system returns -1 with "
+            "errno ENOENT.");
+      }
+    } else {
+      char err_buffer[30];
+      char msg[]{"Execution error with system status code: -1, errno: "};
+#ifdef _WIN32
+      if (strerror_s(err_buffer, sizeof(err_buffer), errno) != 0)
+#else
+      if (strerror_r(errno, err_buffer, sizeof(err_buffer)) != 0)
+#endif
+        terminator.Crash("errno to char msg failed.");
+      char *newMsg{static_cast<char *>(AllocateMemoryOrCrash(
+          terminator, std::strlen(msg) + std::strlen(err_buffer) + 1))};
+      std::strcat(newMsg, err_buffer);
+
+      if (!cmdstat) {
+        terminator.Crash(newMsg);
+      } else {
+        StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
+        CheckAndCopyCharsToDescriptor(cmdmsg, newMsg);
+      }
+      FreeMemory(newMsg);
+    }
+  }
+
 #ifdef _WIN32
   // On WIN32 API std::system returns exit status directly
   std::int64_t exitStatusVal{status};
@@ -119,44 +157,6 @@ std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
   }
 #endif
 
-  // On both Windows and Linux, errno is set when system returns -1.
-  if (status == -1) {
-    // On Windows, ENOENT means the command interpreter can't be found.
-    // On Linux, system calls execl with filepath "/bin/sh", ENOENT means the
-    // file pathname does not exist.
-    if (errno == ENOENT) {
-      if (!cmdstat) {
-        terminator.Crash("Command line execution is not supported, system "
-                         "returns -1 with errno ENOENT.");
-      } else {
-        StoreIntToDescriptor(cmdstat, NO_SUPPORT_ERR, terminator);
-        CheckAndCopyCharsToDescriptor(cmdmsg,
-            "Command line execution is not supported, system returns -1 with "
-            "errno ENOENT.");
-      }
-    } else {
-      char err_buffer[30];
-      char msg[]{"Execution error with system status code: -1, errno: "};
-#ifdef _WIN32
-      if (strerror_s(err_buffer, sizeof(err_buffer), errno) != 0)
-#else
-      if (strerror_r(errno, err_buffer, sizeof(err_buffer)) != 0)
-#endif
-        terminator.Crash("errno to char errno failed.");
-      char *newMsg{static_cast<char *>(AllocateMemoryOrCrash(
-          terminator, std::strlen(msg) + std::strlen(err_buffer) + 1))};
-      std::strcat(newMsg, err_buffer);
-
-      if (!cmdstat) {
-        terminator.Crash(newMsg);
-      } else {
-        StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
-        CheckAndCopyCharsToDescriptor(cmdmsg, newMsg);
-      }
-      FreeMemory(newMsg);
-    }
-  }
-
 #if defined(WIFSIGNALED) && defined(WTERMSIG)
   if (WIFSIGNALED(status)) {
     if (!cmdstat) {



More information about the flang-commits mailing list