Path: blob/master/modules/mono/editor/hostfxr_resolver.cpp
10278 views
/**************************************************************************/1/* hostfxr_resolver.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930/*31Adapted to Godot from the nethost library: https://github.com/dotnet/runtime/tree/main/src/native/corehost32*/3334/*35The MIT License (MIT)3637Copyright (c) .NET Foundation and Contributors3839All rights reserved.4041Permission is hereby granted, free of charge, to any person obtaining a copy42of this software and associated documentation files (the "Software"), to deal43in the Software without restriction, including without limitation the rights44to use, copy, modify, merge, publish, distribute, sublicense, and/or sell45copies of the Software, and to permit persons to whom the Software is46furnished to do so, subject to the following conditions:4748The above copyright notice and this permission notice shall be included in all49copies or substantial portions of the Software.5051THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR52IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,53FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE54AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER55LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,56OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE57SOFTWARE.58*/5960#include "hostfxr_resolver.h"6162#include "../utils/path_utils.h"63#include "semver.h"6465#include "core/config/engine.h"66#include "core/io/dir_access.h"67#include "core/io/file_access.h"68#include "core/os/os.h"6970#ifdef WINDOWS_ENABLED71#define WIN32_LEAN_AND_MEAN72#include <windows.h>73#endif7475// We don't use libnethost as it gives us issues with some compilers.76// This file tries to mimic libnethost's hostfxr_resolver search logic. We try to use the77// same function names for easier comparing in case we need to update this in the future.7879namespace {8081String get_hostfxr_file_name() {82#if defined(WINDOWS_ENABLED)83return "hostfxr.dll";84#elif defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)85return "libhostfxr.dylib";86#else87return "libhostfxr.so";88#endif89}9091bool get_latest_fxr(const String &fxr_root, String &r_fxr_path) {92godotsharp::SemVerParser sem_ver_parser;9394bool found_ver = false;95godotsharp::SemVer latest_ver;96String latest_ver_str;9798Ref<DirAccess> da = DirAccess::open(fxr_root);99da->list_dir_begin();100for (String dir = da->get_next(); !dir.is_empty(); dir = da->get_next()) {101if (!da->current_is_dir() || dir == "." || dir == "..") {102continue;103}104105String ver = dir.get_file();106107godotsharp::SemVer fx_ver;108if (sem_ver_parser.parse(ver, fx_ver)) {109if (!found_ver || fx_ver > latest_ver) {110latest_ver = fx_ver;111latest_ver_str = ver;112found_ver = true;113}114}115}116117if (!found_ver) {118return false;119}120121String fxr_with_ver = Path::join(fxr_root, latest_ver_str);122String hostfxr_file_path = Path::join(fxr_with_ver, get_hostfxr_file_name());123124ERR_FAIL_COND_V_MSG(!FileAccess::exists(hostfxr_file_path), false, "Missing hostfxr library in directory: " + fxr_with_ver);125126r_fxr_path = hostfxr_file_path;127128return true;129}130131#ifdef WINDOWS_ENABLED132BOOL is_wow64() {133BOOL wow64 = FALSE;134if (!IsWow64Process(GetCurrentProcess(), &wow64)) {135wow64 = FALSE;136}137return wow64;138}139#endif140141static const char *arch_name_map[][2] = {142{ "arm32", "arm" },143{ "arm64", "arm64" },144{ "rv64", "riscv64" },145{ "x86_64", "x64" },146{ "x86_32", "x86" },147{ nullptr, nullptr }148};149150String get_dotnet_arch() {151String arch = Engine::get_singleton()->get_architecture_name();152153int idx = 0;154while (arch_name_map[idx][0] != nullptr) {155if (arch_name_map[idx][0] == arch) {156return arch_name_map[idx][1];157}158idx++;159}160161return "";162}163164bool get_default_installation_dir(String &r_dotnet_root) {165#if defined(WINDOWS_ENABLED)166String program_files_env;167if (is_wow64()) {168// Running x86 on x64, looking for x86 install169program_files_env = "ProgramFiles(x86)";170} else {171program_files_env = "ProgramFiles";172}173174String program_files_dir = OS::get_singleton()->get_environment(program_files_env);175176if (program_files_dir.is_empty()) {177return false;178}179180#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)181// When emulating x64 on arm182String dotnet_root_emulated = Path::join(program_files_dir, "dotnet", "x64");183if (FileAccess::exists(Path::join(dotnet_root_emulated, "dotnet.exe"))) {184r_dotnet_root = dotnet_root_emulated;185return true;186}187#endif188189r_dotnet_root = Path::join(program_files_dir, "dotnet");190return true;191#elif defined(MACOS_ENABLED)192r_dotnet_root = "/usr/local/share/dotnet";193194#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)195// When emulating x64 on arm196String dotnet_root_emulated = Path::join(r_dotnet_root, "x64");197if (FileAccess::exists(Path::join(dotnet_root_emulated, "dotnet"))) {198r_dotnet_root = dotnet_root_emulated;199return true;200}201#endif202203return true;204#else205r_dotnet_root = "/usr/share/dotnet";206return true;207#endif208}209210#ifndef WINDOWS_ENABLED211bool get_install_location_from_file(const String &p_file_path, String &r_dotnet_root) {212Error err = OK;213Ref<FileAccess> f = FileAccess::open(p_file_path, FileAccess::READ, &err);214215if (f.is_null() || err != OK) {216return false;217}218219String line = f->get_line();220221if (line.is_empty()) {222return false;223}224225r_dotnet_root = line;226return true;227}228#endif229230bool get_dotnet_self_registered_dir(String &r_dotnet_root) {231#if defined(WINDOWS_ENABLED)232String sub_key = "SOFTWARE\\dotnet\\Setup\\InstalledVersions\\" + get_dotnet_arch();233Char16String value = String("InstallLocation").utf16();234235HKEY hkey = nullptr;236LSTATUS result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(sub_key.utf16().get_data()), 0, KEY_READ | KEY_WOW64_32KEY, &hkey);237if (result != ERROR_SUCCESS) {238return false;239}240241DWORD size = 0;242result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, nullptr, &size);243if (result != ERROR_SUCCESS || size == 0) {244RegCloseKey(hkey);245return false;246}247248Vector<WCHAR> buffer;249buffer.resize(size / sizeof(WCHAR));250result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, (LPBYTE)buffer.ptrw(), &size);251if (result != ERROR_SUCCESS) {252RegCloseKey(hkey);253return false;254}255256r_dotnet_root = String::utf16((const char16_t *)buffer.ptr()).replace_char('\\', '/');257RegCloseKey(hkey);258return true;259#else260String install_location_file = Path::join("/etc/dotnet", "install_location_" + get_dotnet_arch().to_lower());261if (get_install_location_from_file(install_location_file, r_dotnet_root)) {262return true;263}264265if (FileAccess::exists(install_location_file)) {266// Don't try with the legacy location, this will fall back to the hard-coded default install location267return false;268}269270String legacy_install_location_file = Path::join("/etc/dotnet", "install_location");271return get_install_location_from_file(legacy_install_location_file, r_dotnet_root);272#endif273}274275bool get_file_path_from_env(const String &p_env_key, String &r_dotnet_root) {276String env_value = OS::get_singleton()->get_environment(p_env_key);277278if (!env_value.is_empty()) {279env_value = Path::realpath(env_value);280281if (DirAccess::exists(env_value)) {282r_dotnet_root = env_value;283return true;284}285}286287return false;288}289290bool get_dotnet_root_from_env(String &r_dotnet_root) {291String dotnet_root_env = "DOTNET_ROOT";292String arch_for_env = get_dotnet_arch();293294if (!arch_for_env.is_empty()) {295// DOTNET_ROOT_<arch>296if (get_file_path_from_env(dotnet_root_env + "_" + arch_for_env.to_upper(), r_dotnet_root)) {297return true;298}299}300301#ifdef WINDOWS_ENABLED302// WoW64-only: DOTNET_ROOT(x86)303if (is_wow64() && get_file_path_from_env("DOTNET_ROOT(x86)", r_dotnet_root)) {304return true;305}306#endif307308// DOTNET_ROOT309return get_file_path_from_env(dotnet_root_env, r_dotnet_root);310}311312} //namespace313314bool godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(const String &p_dotnet_root, String &r_fxr_path) {315String fxr_dir = Path::join(p_dotnet_root, "host", "fxr");316if (!DirAccess::exists(fxr_dir)) {317if (OS::get_singleton()->is_stdout_verbose()) {318ERR_PRINT("The host fxr folder does not exist: " + fxr_dir + ".");319}320return false;321}322return get_latest_fxr(fxr_dir, r_fxr_path);323}324325bool godotsharp::hostfxr_resolver::try_get_path(String &r_dotnet_root, String &r_fxr_path) {326if (!get_dotnet_root_from_env(r_dotnet_root) &&327!get_dotnet_self_registered_dir(r_dotnet_root) &&328!get_default_installation_dir(r_dotnet_root)) {329return false;330}331332return try_get_path_from_dotnet_root(r_dotnet_root, r_fxr_path);333}334335336