1 | //===-- lib/runtime/environment.cpp -----------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "flang-rt/runtime/environment.h" |
10 | #include "environment-default-list.h" |
11 | #include "memory.h" |
12 | #include "flang-rt/runtime/tools.h" |
13 | #include <cstdio> |
14 | #include <cstdlib> |
15 | #include <cstring> |
16 | #include <limits> |
17 | |
18 | #ifdef _WIN32 |
19 | extern char **_environ; |
20 | #else |
21 | extern char **environ; |
22 | #endif |
23 | |
24 | namespace Fortran::runtime { |
25 | |
26 | #ifndef FLANG_RUNTIME_NO_GLOBAL_VAR_DEFS |
27 | RT_OFFLOAD_VAR_GROUP_BEGIN |
28 | RT_VAR_ATTRS ExecutionEnvironment executionEnvironment; |
29 | RT_OFFLOAD_VAR_GROUP_END |
30 | #endif // FLANG_RUNTIME_NO_GLOBAL_VAR_DEFS |
31 | |
32 | static void SetEnvironmentDefaults(const EnvironmentDefaultList *envDefaults) { |
33 | if (!envDefaults) { |
34 | return; |
35 | } |
36 | |
37 | for (int itemIndex = 0; itemIndex < envDefaults->numItems; ++itemIndex) { |
38 | const char *name = envDefaults->item[itemIndex].name; |
39 | const char *value = envDefaults->item[itemIndex].value; |
40 | #ifdef _WIN32 |
41 | if (auto *x{std::getenv(name)}) { |
42 | continue; |
43 | } |
44 | if (_putenv_s(name, value) != 0) { |
45 | #else |
46 | if (setenv(name: name, value: value, /*overwrite=*/replace: 0) == -1) { |
47 | #endif |
48 | Fortran::runtime::Terminator{__FILE__, __LINE__}.Crash( |
49 | std::strerror(errno)); |
50 | } |
51 | } |
52 | } |
53 | |
54 | RT_OFFLOAD_API_GROUP_BEGIN |
55 | Fortran::common::optional<Convert> GetConvertFromString( |
56 | const char *x, std::size_t n) { |
57 | static const char *keywords[]{ |
58 | "UNKNOWN" , "NATIVE" , "LITTLE_ENDIAN" , "BIG_ENDIAN" , "SWAP" , nullptr}; |
59 | switch (IdentifyValue(x, n, keywords)) { |
60 | case 0: |
61 | return Convert::Unknown; |
62 | case 1: |
63 | return Convert::Native; |
64 | case 2: |
65 | return Convert::LittleEndian; |
66 | case 3: |
67 | return Convert::BigEndian; |
68 | case 4: |
69 | return Convert::Swap; |
70 | default: |
71 | return Fortran::common::nullopt; |
72 | } |
73 | } |
74 | RT_OFFLOAD_API_GROUP_END |
75 | |
76 | void ExecutionEnvironment::Configure(int ac, const char *av[], |
77 | const char *env[], const EnvironmentDefaultList *envDefaults) { |
78 | argc = ac; |
79 | argv = av; |
80 | SetEnvironmentDefaults(envDefaults); |
81 | #ifdef _WIN32 |
82 | envp = _environ; |
83 | #else |
84 | envp = environ; |
85 | #endif |
86 | listDirectedOutputLineLengthLimit = 79; // PGI default |
87 | defaultOutputRoundingMode = |
88 | decimal::FortranRounding::RoundNearest; // RP(==RN) |
89 | conversion = Convert::Unknown; |
90 | |
91 | if (auto *x{std::getenv("FORT_FMT_RECL" )}) { |
92 | char *end; |
93 | auto n{std::strtol(x, &end, 10)}; |
94 | if (n > 0 && n < std::numeric_limits<int>::max() && *end == '\0') { |
95 | listDirectedOutputLineLengthLimit = n; |
96 | } else { |
97 | std::fprintf( |
98 | stderr, "Fortran runtime: FORT_FMT_RECL=%s is invalid; ignored\n" , x); |
99 | } |
100 | } |
101 | |
102 | if (auto *x{std::getenv("FORT_CONVERT" )}) { |
103 | if (auto convert{GetConvertFromString(x, std::strlen(x))}) { |
104 | conversion = *convert; |
105 | } else { |
106 | std::fprintf( |
107 | stderr, "Fortran runtime: FORT_CONVERT=%s is invalid; ignored\n" , x); |
108 | } |
109 | } |
110 | |
111 | if (auto *x{std::getenv("NO_STOP_MESSAGE" )}) { |
112 | char *end; |
113 | auto n{std::strtol(x, &end, 10)}; |
114 | if (n >= 0 && n <= 1 && *end == '\0') { |
115 | noStopMessage = n != 0; |
116 | } else { |
117 | std::fprintf(stderr, |
118 | "Fortran runtime: NO_STOP_MESSAGE=%s is invalid; ignored\n" , x); |
119 | } |
120 | } |
121 | |
122 | if (auto *x{std::getenv("DEFAULT_UTF8" )}) { |
123 | char *end; |
124 | auto n{std::strtol(x, &end, 10)}; |
125 | if (n >= 0 && n <= 1 && *end == '\0') { |
126 | defaultUTF8 = n != 0; |
127 | } else { |
128 | std::fprintf( |
129 | stderr, "Fortran runtime: DEFAULT_UTF8=%s is invalid; ignored\n" , x); |
130 | } |
131 | } |
132 | |
133 | if (auto *x{std::getenv("FORT_CHECK_POINTER_DEALLOCATION" )}) { |
134 | char *end; |
135 | auto n{std::strtol(x, &end, 10)}; |
136 | if (n >= 0 && n <= 1 && *end == '\0') { |
137 | checkPointerDeallocation = n != 0; |
138 | } else { |
139 | std::fprintf(stderr, |
140 | "Fortran runtime: FORT_CHECK_POINTER_DEALLOCATION=%s is invalid; " |
141 | "ignored\n" , |
142 | x); |
143 | } |
144 | } |
145 | |
146 | if (auto *x{std::getenv("ACC_OFFLOAD_STACK_SIZE" )}) { |
147 | char *end; |
148 | auto n{std::strtoul(x, &end, 10)}; |
149 | if (n > 0 && n < std::numeric_limits<std::size_t>::max() && *end == '\0') { |
150 | cudaStackLimit = n; |
151 | } else { |
152 | std::fprintf(stderr, |
153 | "Fortran runtime: ACC_OFFLOAD_STACK_SIZE=%s is invalid; ignored\n" , |
154 | x); |
155 | } |
156 | } |
157 | |
158 | if (auto *x{std::getenv("NV_CUDAFOR_DEVICE_IS_MANAGED" )}) { |
159 | char *end; |
160 | auto n{std::strtol(x, &end, 10)}; |
161 | if (n >= 0 && n <= 1 && *end == '\0') { |
162 | cudaDeviceIsManaged = n != 0; |
163 | } else { |
164 | std::fprintf(stderr, |
165 | "Fortran runtime: NV_CUDAFOR_DEVICE_IS_MANAGED=%s is invalid; " |
166 | "ignored\n" , |
167 | x); |
168 | } |
169 | } |
170 | |
171 | // TODO: Set RP/ROUND='PROCESSOR_DEFINED' from environment |
172 | } |
173 | |
174 | const char *ExecutionEnvironment::GetEnv( |
175 | const char *name, std::size_t name_length, const Terminator &terminator) { |
176 | RUNTIME_CHECK(terminator, name && name_length); |
177 | |
178 | OwningPtr<char> cStyleName{ |
179 | SaveDefaultCharacter(name, name_length, terminator)}; |
180 | RUNTIME_CHECK(terminator, cStyleName); |
181 | |
182 | return std::getenv(cStyleName.get()); |
183 | } |
184 | |
185 | std::int32_t ExecutionEnvironment::SetEnv(const char *name, |
186 | std::size_t name_length, const char *value, std::size_t value_length, |
187 | const Terminator &terminator) { |
188 | |
189 | RUNTIME_CHECK(terminator, name && name_length && value && value_length); |
190 | |
191 | OwningPtr<char> cStyleName{ |
192 | SaveDefaultCharacter(name, name_length, terminator)}; |
193 | RUNTIME_CHECK(terminator, cStyleName); |
194 | |
195 | OwningPtr<char> cStyleValue{ |
196 | SaveDefaultCharacter(value, value_length, terminator)}; |
197 | RUNTIME_CHECK(terminator, cStyleValue); |
198 | |
199 | std::int32_t status{0}; |
200 | |
201 | #ifdef _WIN32 |
202 | |
203 | status = _putenv_s(cStyleName.get(), cStyleValue.get()); |
204 | |
205 | #else |
206 | |
207 | constexpr int overwrite = 1; |
208 | status = setenv(cStyleName.get(), cStyleValue.get(), overwrite); |
209 | |
210 | #endif |
211 | |
212 | if (status != 0) { |
213 | status = errno; |
214 | } |
215 | |
216 | return status; |
217 | } |
218 | |
219 | std::int32_t ExecutionEnvironment::UnsetEnv( |
220 | const char *name, std::size_t name_length, const Terminator &terminator) { |
221 | |
222 | RUNTIME_CHECK(terminator, name && name_length); |
223 | |
224 | OwningPtr<char> cStyleName{ |
225 | SaveDefaultCharacter(name, name_length, terminator)}; |
226 | RUNTIME_CHECK(terminator, cStyleName); |
227 | |
228 | std::int32_t status{0}; |
229 | |
230 | #ifdef _WIN32 |
231 | |
232 | // Passing empty string as value will unset the variable |
233 | status = _putenv_s(cStyleName.get(), "" ); |
234 | |
235 | #else |
236 | |
237 | status = unsetenv(cStyleName.get()); |
238 | |
239 | #endif |
240 | |
241 | if (status != 0) { |
242 | status = errno; |
243 | } |
244 | |
245 | return status; |
246 | } |
247 | |
248 | } // namespace Fortran::runtime |
249 | |