Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/mono/editor/hostfxr_resolver.cpp
10278 views
1
/**************************************************************************/
2
/* hostfxr_resolver.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
/*
32
Adapted to Godot from the nethost library: https://github.com/dotnet/runtime/tree/main/src/native/corehost
33
*/
34
35
/*
36
The MIT License (MIT)
37
38
Copyright (c) .NET Foundation and Contributors
39
40
All rights reserved.
41
42
Permission is hereby granted, free of charge, to any person obtaining a copy
43
of this software and associated documentation files (the "Software"), to deal
44
in the Software without restriction, including without limitation the rights
45
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
46
copies of the Software, and to permit persons to whom the Software is
47
furnished to do so, subject to the following conditions:
48
49
The above copyright notice and this permission notice shall be included in all
50
copies or substantial portions of the Software.
51
52
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
53
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
54
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
55
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
56
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
57
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
58
SOFTWARE.
59
*/
60
61
#include "hostfxr_resolver.h"
62
63
#include "../utils/path_utils.h"
64
#include "semver.h"
65
66
#include "core/config/engine.h"
67
#include "core/io/dir_access.h"
68
#include "core/io/file_access.h"
69
#include "core/os/os.h"
70
71
#ifdef WINDOWS_ENABLED
72
#define WIN32_LEAN_AND_MEAN
73
#include <windows.h>
74
#endif
75
76
// We don't use libnethost as it gives us issues with some compilers.
77
// This file tries to mimic libnethost's hostfxr_resolver search logic. We try to use the
78
// same function names for easier comparing in case we need to update this in the future.
79
80
namespace {
81
82
String get_hostfxr_file_name() {
83
#if defined(WINDOWS_ENABLED)
84
return "hostfxr.dll";
85
#elif defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
86
return "libhostfxr.dylib";
87
#else
88
return "libhostfxr.so";
89
#endif
90
}
91
92
bool get_latest_fxr(const String &fxr_root, String &r_fxr_path) {
93
godotsharp::SemVerParser sem_ver_parser;
94
95
bool found_ver = false;
96
godotsharp::SemVer latest_ver;
97
String latest_ver_str;
98
99
Ref<DirAccess> da = DirAccess::open(fxr_root);
100
da->list_dir_begin();
101
for (String dir = da->get_next(); !dir.is_empty(); dir = da->get_next()) {
102
if (!da->current_is_dir() || dir == "." || dir == "..") {
103
continue;
104
}
105
106
String ver = dir.get_file();
107
108
godotsharp::SemVer fx_ver;
109
if (sem_ver_parser.parse(ver, fx_ver)) {
110
if (!found_ver || fx_ver > latest_ver) {
111
latest_ver = fx_ver;
112
latest_ver_str = ver;
113
found_ver = true;
114
}
115
}
116
}
117
118
if (!found_ver) {
119
return false;
120
}
121
122
String fxr_with_ver = Path::join(fxr_root, latest_ver_str);
123
String hostfxr_file_path = Path::join(fxr_with_ver, get_hostfxr_file_name());
124
125
ERR_FAIL_COND_V_MSG(!FileAccess::exists(hostfxr_file_path), false, "Missing hostfxr library in directory: " + fxr_with_ver);
126
127
r_fxr_path = hostfxr_file_path;
128
129
return true;
130
}
131
132
#ifdef WINDOWS_ENABLED
133
BOOL is_wow64() {
134
BOOL wow64 = FALSE;
135
if (!IsWow64Process(GetCurrentProcess(), &wow64)) {
136
wow64 = FALSE;
137
}
138
return wow64;
139
}
140
#endif
141
142
static const char *arch_name_map[][2] = {
143
{ "arm32", "arm" },
144
{ "arm64", "arm64" },
145
{ "rv64", "riscv64" },
146
{ "x86_64", "x64" },
147
{ "x86_32", "x86" },
148
{ nullptr, nullptr }
149
};
150
151
String get_dotnet_arch() {
152
String arch = Engine::get_singleton()->get_architecture_name();
153
154
int idx = 0;
155
while (arch_name_map[idx][0] != nullptr) {
156
if (arch_name_map[idx][0] == arch) {
157
return arch_name_map[idx][1];
158
}
159
idx++;
160
}
161
162
return "";
163
}
164
165
bool get_default_installation_dir(String &r_dotnet_root) {
166
#if defined(WINDOWS_ENABLED)
167
String program_files_env;
168
if (is_wow64()) {
169
// Running x86 on x64, looking for x86 install
170
program_files_env = "ProgramFiles(x86)";
171
} else {
172
program_files_env = "ProgramFiles";
173
}
174
175
String program_files_dir = OS::get_singleton()->get_environment(program_files_env);
176
177
if (program_files_dir.is_empty()) {
178
return false;
179
}
180
181
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
182
// When emulating x64 on arm
183
String dotnet_root_emulated = Path::join(program_files_dir, "dotnet", "x64");
184
if (FileAccess::exists(Path::join(dotnet_root_emulated, "dotnet.exe"))) {
185
r_dotnet_root = dotnet_root_emulated;
186
return true;
187
}
188
#endif
189
190
r_dotnet_root = Path::join(program_files_dir, "dotnet");
191
return true;
192
#elif defined(MACOS_ENABLED)
193
r_dotnet_root = "/usr/local/share/dotnet";
194
195
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
196
// When emulating x64 on arm
197
String dotnet_root_emulated = Path::join(r_dotnet_root, "x64");
198
if (FileAccess::exists(Path::join(dotnet_root_emulated, "dotnet"))) {
199
r_dotnet_root = dotnet_root_emulated;
200
return true;
201
}
202
#endif
203
204
return true;
205
#else
206
r_dotnet_root = "/usr/share/dotnet";
207
return true;
208
#endif
209
}
210
211
#ifndef WINDOWS_ENABLED
212
bool get_install_location_from_file(const String &p_file_path, String &r_dotnet_root) {
213
Error err = OK;
214
Ref<FileAccess> f = FileAccess::open(p_file_path, FileAccess::READ, &err);
215
216
if (f.is_null() || err != OK) {
217
return false;
218
}
219
220
String line = f->get_line();
221
222
if (line.is_empty()) {
223
return false;
224
}
225
226
r_dotnet_root = line;
227
return true;
228
}
229
#endif
230
231
bool get_dotnet_self_registered_dir(String &r_dotnet_root) {
232
#if defined(WINDOWS_ENABLED)
233
String sub_key = "SOFTWARE\\dotnet\\Setup\\InstalledVersions\\" + get_dotnet_arch();
234
Char16String value = String("InstallLocation").utf16();
235
236
HKEY hkey = nullptr;
237
LSTATUS result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(sub_key.utf16().get_data()), 0, KEY_READ | KEY_WOW64_32KEY, &hkey);
238
if (result != ERROR_SUCCESS) {
239
return false;
240
}
241
242
DWORD size = 0;
243
result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, nullptr, &size);
244
if (result != ERROR_SUCCESS || size == 0) {
245
RegCloseKey(hkey);
246
return false;
247
}
248
249
Vector<WCHAR> buffer;
250
buffer.resize(size / sizeof(WCHAR));
251
result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, (LPBYTE)buffer.ptrw(), &size);
252
if (result != ERROR_SUCCESS) {
253
RegCloseKey(hkey);
254
return false;
255
}
256
257
r_dotnet_root = String::utf16((const char16_t *)buffer.ptr()).replace_char('\\', '/');
258
RegCloseKey(hkey);
259
return true;
260
#else
261
String install_location_file = Path::join("/etc/dotnet", "install_location_" + get_dotnet_arch().to_lower());
262
if (get_install_location_from_file(install_location_file, r_dotnet_root)) {
263
return true;
264
}
265
266
if (FileAccess::exists(install_location_file)) {
267
// Don't try with the legacy location, this will fall back to the hard-coded default install location
268
return false;
269
}
270
271
String legacy_install_location_file = Path::join("/etc/dotnet", "install_location");
272
return get_install_location_from_file(legacy_install_location_file, r_dotnet_root);
273
#endif
274
}
275
276
bool get_file_path_from_env(const String &p_env_key, String &r_dotnet_root) {
277
String env_value = OS::get_singleton()->get_environment(p_env_key);
278
279
if (!env_value.is_empty()) {
280
env_value = Path::realpath(env_value);
281
282
if (DirAccess::exists(env_value)) {
283
r_dotnet_root = env_value;
284
return true;
285
}
286
}
287
288
return false;
289
}
290
291
bool get_dotnet_root_from_env(String &r_dotnet_root) {
292
String dotnet_root_env = "DOTNET_ROOT";
293
String arch_for_env = get_dotnet_arch();
294
295
if (!arch_for_env.is_empty()) {
296
// DOTNET_ROOT_<arch>
297
if (get_file_path_from_env(dotnet_root_env + "_" + arch_for_env.to_upper(), r_dotnet_root)) {
298
return true;
299
}
300
}
301
302
#ifdef WINDOWS_ENABLED
303
// WoW64-only: DOTNET_ROOT(x86)
304
if (is_wow64() && get_file_path_from_env("DOTNET_ROOT(x86)", r_dotnet_root)) {
305
return true;
306
}
307
#endif
308
309
// DOTNET_ROOT
310
return get_file_path_from_env(dotnet_root_env, r_dotnet_root);
311
}
312
313
} //namespace
314
315
bool godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(const String &p_dotnet_root, String &r_fxr_path) {
316
String fxr_dir = Path::join(p_dotnet_root, "host", "fxr");
317
if (!DirAccess::exists(fxr_dir)) {
318
if (OS::get_singleton()->is_stdout_verbose()) {
319
ERR_PRINT("The host fxr folder does not exist: " + fxr_dir + ".");
320
}
321
return false;
322
}
323
return get_latest_fxr(fxr_dir, r_fxr_path);
324
}
325
326
bool godotsharp::hostfxr_resolver::try_get_path(String &r_dotnet_root, String &r_fxr_path) {
327
if (!get_dotnet_root_from_env(r_dotnet_root) &&
328
!get_dotnet_self_registered_dir(r_dotnet_root) &&
329
!get_default_installation_dir(r_dotnet_root)) {
330
return false;
331
}
332
333
return try_get_path_from_dotnet_root(r_dotnet_root, r_fxr_path);
334
}
335
336