1 // Copyright 2019 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "common/SystemUtils.h"
16
17 #include "common/Assert.h"
18 #include "common/Log.h"
19
20 #if defined(DAWN_PLATFORM_WINDOWS)
21 # include <Windows.h>
22 # include <vector>
23 #elif defined(DAWN_PLATFORM_LINUX)
24 # include <dlfcn.h>
25 # include <limits.h>
26 # include <unistd.h>
27 # include <cstdlib>
28 #elif defined(DAWN_PLATFORM_MACOS) || defined(DAWN_PLATFORM_IOS)
29 # include <dlfcn.h>
30 # include <mach-o/dyld.h>
31 # include <vector>
32 #endif
33
34 #include <array>
35
36 #if defined(DAWN_PLATFORM_WINDOWS)
GetPathSeparator()37 const char* GetPathSeparator() {
38 return "\\";
39 }
40
GetEnvironmentVar(const char * variableName)41 std::pair<std::string, bool> GetEnvironmentVar(const char* variableName) {
42 // First pass a size of 0 to get the size of variable value.
43 DWORD sizeWithNullTerminator = GetEnvironmentVariableA(variableName, nullptr, 0);
44 if (sizeWithNullTerminator == 0) {
45 DWORD err = GetLastError();
46 if (err != ERROR_ENVVAR_NOT_FOUND) {
47 dawn::WarningLog() << "GetEnvironmentVariableA failed with code " << err;
48 }
49 return std::make_pair(std::string(), false);
50 }
51
52 // Then get variable value with its actual size.
53 std::vector<char> buffer(sizeWithNullTerminator);
54 DWORD sizeStored =
55 GetEnvironmentVariableA(variableName, buffer.data(), static_cast<DWORD>(buffer.size()));
56 if (sizeStored + 1 != sizeWithNullTerminator) {
57 DWORD err = GetLastError();
58 if (err) {
59 dawn::WarningLog() << "GetEnvironmentVariableA failed with code " << err;
60 }
61 return std::make_pair(std::string(), false);
62 }
63 return std::make_pair(std::string(buffer.data(), sizeStored), true);
64 }
65
SetEnvironmentVar(const char * variableName,const char * value)66 bool SetEnvironmentVar(const char* variableName, const char* value) {
67 return SetEnvironmentVariableA(variableName, value) == TRUE;
68 }
69 #elif defined(DAWN_PLATFORM_POSIX)
GetPathSeparator()70 const char* GetPathSeparator() {
71 return "/";
72 }
73
GetEnvironmentVar(const char * variableName)74 std::pair<std::string, bool> GetEnvironmentVar(const char* variableName) {
75 char* value = getenv(variableName);
76 return value == nullptr ? std::make_pair(std::string(), false)
77 : std::make_pair(std::string(value), true);
78 }
79
SetEnvironmentVar(const char * variableName,const char * value)80 bool SetEnvironmentVar(const char* variableName, const char* value) {
81 if (value == nullptr) {
82 return unsetenv(variableName) == 0;
83 }
84 return setenv(variableName, value, 1) == 0;
85 }
86 #else
87 # error "Implement Get/SetEnvironmentVar for your platform."
88 #endif
89
90 #if defined(DAWN_PLATFORM_WINDOWS)
GetExecutablePath()91 std::string GetExecutablePath() {
92 std::array<char, MAX_PATH> executableFileBuf;
93 DWORD executablePathLen = GetModuleFileNameA(nullptr, executableFileBuf.data(),
94 static_cast<DWORD>(executableFileBuf.size()));
95 return executablePathLen > 0 ? std::string(executableFileBuf.data()) : "";
96 }
97 #elif defined(DAWN_PLATFORM_LINUX)
GetExecutablePath()98 std::string GetExecutablePath() {
99 std::array<char, PATH_MAX> path;
100 ssize_t result = readlink("/proc/self/exe", path.data(), PATH_MAX - 1);
101 if (result < 0 || static_cast<size_t>(result) >= PATH_MAX - 1) {
102 return "";
103 }
104
105 path[result] = '\0';
106 return path.data();
107 }
108 #elif defined(DAWN_PLATFORM_MACOS) || defined(DAWN_PLATFORM_IOS)
GetExecutablePath()109 std::string GetExecutablePath() {
110 uint32_t size = 0;
111 _NSGetExecutablePath(nullptr, &size);
112
113 std::vector<char> buffer(size + 1);
114 if (_NSGetExecutablePath(buffer.data(), &size) != 0) {
115 return "";
116 }
117
118 buffer[size] = '\0';
119 return buffer.data();
120 }
121 #elif defined(DAWN_PLATFORM_FUCHSIA)
GetExecutablePath()122 std::string GetExecutablePath() {
123 // TODO: Implement on Fuchsia
124 return "";
125 }
126 #elif defined(DAWN_PLATFORM_EMSCRIPTEN)
GetExecutablePath()127 std::string GetExecutablePath() {
128 UNREACHABLE();
129 return "";
130 }
131 #else
132 # error "Implement GetExecutablePath for your platform."
133 #endif
134
GetExecutableDirectory()135 std::string GetExecutableDirectory() {
136 std::string exePath = GetExecutablePath();
137 size_t lastPathSepLoc = exePath.find_last_of(GetPathSeparator());
138 return lastPathSepLoc != std::string::npos ? exePath.substr(0, lastPathSepLoc + 1) : "";
139 }
140
141 #if defined(DAWN_PLATFORM_LINUX) || defined(DAWN_PLATFORM_MACOS) || defined(DAWN_PLATFORM_IOS)
GetModulePath()142 std::string GetModulePath() {
143 static int placeholderSymbol = 0;
144 Dl_info dlInfo;
145 if (dladdr(&placeholderSymbol, &dlInfo) == 0) {
146 return "";
147 }
148
149 std::array<char, PATH_MAX> absolutePath;
150 if (realpath(dlInfo.dli_fname, absolutePath.data()) == NULL) {
151 return "";
152 }
153 return absolutePath.data();
154 }
155 #elif defined(DAWN_PLATFORM_WINDOWS)
GetModulePath()156 std::string GetModulePath() {
157 UNREACHABLE();
158 return "";
159 }
160 #elif defined(DAWN_PLATFORM_FUCHSIA)
GetModulePath()161 std::string GetModulePath() {
162 UNREACHABLE();
163 return "";
164 }
165 #elif defined(DAWN_PLATFORM_EMSCRIPTEN)
GetModulePath()166 std::string GetModulePath() {
167 UNREACHABLE();
168 return "";
169 }
170 #else
171 # error "Implement GetModulePath for your platform."
172 #endif
173
GetModuleDirectory()174 std::string GetModuleDirectory() {
175 std::string modPath = GetModulePath();
176 size_t lastPathSepLoc = modPath.find_last_of(GetPathSeparator());
177 return lastPathSepLoc != std::string::npos ? modPath.substr(0, lastPathSepLoc + 1) : "";
178 }
179
180 // ScopedEnvironmentVar
181
ScopedEnvironmentVar(const char * variableName,const char * value)182 ScopedEnvironmentVar::ScopedEnvironmentVar(const char* variableName, const char* value)
183 : mName(variableName),
184 mOriginalValue(GetEnvironmentVar(variableName)),
185 mIsSet(SetEnvironmentVar(variableName, value)) {
186 }
187
~ScopedEnvironmentVar()188 ScopedEnvironmentVar::~ScopedEnvironmentVar() {
189 if (mIsSet) {
190 bool success = SetEnvironmentVar(
191 mName.c_str(), mOriginalValue.second ? mOriginalValue.first.c_str() : nullptr);
192 // If we set the environment variable in the constructor, we should never fail restoring it.
193 ASSERT(success);
194 }
195 }
196
Set(const char * variableName,const char * value)197 bool ScopedEnvironmentVar::Set(const char* variableName, const char* value) {
198 ASSERT(!mIsSet);
199 mName = variableName;
200 mOriginalValue = GetEnvironmentVar(variableName);
201 mIsSet = SetEnvironmentVar(variableName, value);
202 return mIsSet;
203 }
204