forked from OSchip/llvm-project
[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
This commit is contained in:
parent
177176f75c
commit
824bf90819
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "terminator.h"
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace Fortran::runtime {
|
||||
|
|
|
@ -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 @@ protected:
|
|||
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));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue