[flang-commits] [flang] 824bf90 - [flang] runtime: Read environment variables directly

Diana Picus via flang-commits flang-commits at lists.llvm.org
Thu Oct 28 00:52:42 PDT 2021


Author: Diana Picus
Date: 2021-10-28T07:49:30Z
New Revision: 824bf908194c9267f1f09065ba8e41d7969006ab

URL: https://github.com/llvm/llvm-project/commit/824bf908194c9267f1f09065ba8e41d7969006ab
DIFF: https://github.com/llvm/llvm-project/commit/824bf908194c9267f1f09065ba8e41d7969006ab.diff

LOG: [flang] runtime: Read environment variables directly

Add support for reading environment variables directly, via std::getenv.
This needs to allocate a C-style string to pass into std::getenv. If the
memory allocation for that fails, we terminate.

This also changes the interface for EnvVariableLength to receive the
source file and line so we can crash gracefully.

Note that we are now completely ignoring the envp pointer passed into
ProgramStart, since that could go stale if the environment is modified
during execution.

Differential Revision: https://reviews.llvm.org/D111785

Added: 
    

Modified: 
    flang/include/flang/Runtime/command.h
    flang/runtime/command.cpp
    flang/runtime/environment.cpp
    flang/runtime/environment.h
    flang/runtime/tools.cpp
    flang/unittests/Runtime/CommandTest.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index 45a9f11269c7c..f664599e7385a 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -48,8 +48,8 @@ std::int32_t RTNAME(EnvVariableValue)(const Descriptor &name,
 
 // Try to get the significant length of the environment variable specified by
 // NAME. Returns 0 if it doesn't manage.
-std::int64_t RTNAME(EnvVariableLength)(
-    const Descriptor &name, bool trim_name = true);
+std::int64_t RTNAME(EnvVariableLength)(const Descriptor &name,
+    bool trim_name = true, const char *sourceFile = nullptr, int line = 0);
 }
 } // namespace Fortran::runtime
 

diff  --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index 81daf64b6b7e7..325fc274e3254 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -9,6 +9,7 @@
 #include "flang/Runtime/command.h"
 #include "environment.h"
 #include "stat.h"
+#include "terminator.h"
 #include "flang/Runtime/descriptor.h"
 #include <cstdlib>
 #include <limits>
@@ -89,15 +90,17 @@ static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
   return s + 1;
 }
 
-std::int64_t RTNAME(EnvVariableLength)(const Descriptor &name, bool trim_name) {
+std::int64_t RTNAME(EnvVariableLength)(
+    const Descriptor &name, bool trim_name, const char *sourceFile, int line) {
   std::size_t nameLength{
       trim_name ? LengthWithoutTrailingSpaces(name) : name.ElementBytes()};
   if (nameLength == 0) {
     return 0;
   }
 
-  const char *value{
-      executionEnvironment.GetEnv(name.OffsetElement(), nameLength)};
+  Terminator terminator{sourceFile, line};
+  const char *value{executionEnvironment.GetEnv(
+      name.OffsetElement(), nameLength, terminator)};
   if (!value) {
     return 0;
   }

diff  --git a/flang/runtime/environment.cpp b/flang/runtime/environment.cpp
index 2ba8faf6882ce..491906b0ff0f4 100644
--- a/flang/runtime/environment.cpp
+++ b/flang/runtime/environment.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "environment.h"
+#include "memory.h"
 #include "tools.h"
 #include <cstdio>
 #include <cstdlib>
@@ -70,26 +71,13 @@ void ExecutionEnvironment::Configure(
 }
 
 const char *ExecutionEnvironment::GetEnv(
-    const char *name, std::size_t name_length) {
-  if (!envp) {
-    // TODO: Ask std::getenv.
-    return nullptr;
-  }
+    const char *name, std::size_t name_length, const Terminator &terminator) {
+  RUNTIME_CHECK(terminator, name && name_length);
 
-  // envp is an array of strings of the form "name=value".
-  for (const char **var{envp}; *var != nullptr; ++var) {
-    const char *eq{std::strchr(*var, '=')};
-    if (!eq) {
-      // Found a malformed environment string, just ignore it.
-      continue;
-    }
-    if (static_cast<std::size_t>(eq - *var) != name_length) {
-      continue;
-    }
-    if (std::memcmp(*var, name, name_length) == 0) {
-      return eq + 1;
-    }
-  }
-  return nullptr;
+  OwningPtr<char> cStyleName{
+      SaveDefaultCharacter(name, name_length, terminator)};
+  RUNTIME_CHECK(terminator, cStyleName);
+
+  return std::getenv(cStyleName.get());
 }
 } // namespace Fortran::runtime

diff  --git a/flang/runtime/environment.h b/flang/runtime/environment.h
index 1d04cb110b378..bc3a4e3dffe1a 100644
--- a/flang/runtime/environment.h
+++ b/flang/runtime/environment.h
@@ -14,6 +14,8 @@
 
 namespace Fortran::runtime {
 
+class Terminator;
+
 #if FLANG_BIG_ENDIAN
 constexpr bool isHostLittleEndian{false};
 #elif FLANG_LITTLE_ENDIAN
@@ -29,7 +31,8 @@ std::optional<Convert> GetConvertFromString(const char *, std::size_t);
 
 struct ExecutionEnvironment {
   void Configure(int argc, const char *argv[], const char *envp[]);
-  const char *GetEnv(const char *name, std::size_t name_length);
+  const char *GetEnv(
+      const char *name, std::size_t name_length, const Terminator &terminator);
 
   int argc;
   const char **argv;

diff  --git a/flang/runtime/tools.cpp b/flang/runtime/tools.cpp
index f281f134c0eb3..f9b804e1ff173 100644
--- a/flang/runtime/tools.cpp
+++ b/flang/runtime/tools.cpp
@@ -10,6 +10,7 @@
 #include "terminator.h"
 #include <algorithm>
 #include <cstdint>
+#include <cstdlib>
 #include <cstring>
 
 namespace Fortran::runtime {

diff  --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 194860f8797a7..cfbbd8a53383c 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -11,6 +11,7 @@
 #include "gtest/gtest.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/main.h"
+#include <cstdlib>
 
 using namespace Fortran::runtime;
 
@@ -41,8 +42,6 @@ class CommandFixture : public ::testing::Test {
     RTNAME(ProgramStart)(argc, argv, {});
   }
 
-  CommandFixture(const char *envp[]) { RTNAME(ProgramStart)(0, nullptr, envp); }
-
   std::string GetPaddedStr(const char *text, std::size_t len) const {
     std::string res{text};
     assert(res.length() <= len && "No room to pad");
@@ -189,19 +188,46 @@ TEST_F(SeveralArguments, ErrMsgTooShort) {
   CheckDescriptorEqStr(errMsg.get(), "Inv");
 }
 
-static const char *env[]{"NAME=value", nullptr};
 class EnvironmentVariables : public CommandFixture {
 protected:
-  EnvironmentVariables() : CommandFixture(env) {}
+  EnvironmentVariables() : CommandFixture(0, nullptr) {
+    SetEnv("NAME", "VALUE");
+  }
+
+  // If we have access to setenv, we can run some more fine-grained tests.
+  template <typename ParamType = char>
+  void SetEnv(const ParamType *name, const ParamType *value,
+      decltype(setenv(name, value, 1)) *Enabled = nullptr) {
+    ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1));
+    canSetEnv = true;
+  }
+
+  // Fallback method if setenv is not available.
+  template <typename Unused = void> void SetEnv(const void *, const void *) {}
+
+  bool EnableFineGrainedTests() const { return canSetEnv; }
+
+private:
+  bool canSetEnv{false};
 };
 
 TEST_F(EnvironmentVariables, Length) {
-  EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME")));
   EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("DOESNT_EXIST")));
 
-  EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME  ")));
-  EXPECT_EQ(0,
-      RTNAME(EnvVariableLength)(*CharDescriptor("NAME "), /*trim_name=*/false));
-
   EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("     ")));
+  EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("")));
+
+  // Test a variable that's expected to exist in the environment.
+  char *path{std::getenv("PATH")};
+  auto expectedLen{static_cast<int64_t>(std::strlen(path))};
+  EXPECT_EQ(expectedLen, RTNAME(EnvVariableLength)(*CharDescriptor("PATH")));
+
+  if (EnableFineGrainedTests()) {
+    EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME")));
+
+    EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME  ")));
+    EXPECT_EQ(0,
+        RTNAME(EnvVariableLength)(
+            *CharDescriptor("NAME "), /*trim_name=*/false));
+  }
 }


        


More information about the flang-commits mailing list