C#: Replace libnethost dependency to find hostfxr

We want to replace libnethost as it gives us issues with some compilers.
Our implementation tries to mimic libnethost's hostfxr_resolver search
logic. We try to use the same function names for easier comparing in
case we need to update this in the future.
This commit is contained in:
Ignacio Roldán Etcheverry 2022-09-06 03:23:55 +02:00
parent 4b164b8e47
commit f784fb2000
11 changed files with 1045 additions and 390 deletions

View File

@ -819,21 +819,12 @@ def generate_vs_project(env, num_jobs):
module_configs = ModuleConfigs()
if env.get("module_mono_enabled"):
import modules.mono.build_scripts.mono_configure as mono_configure
app_host_dir = mono_configure.find_dotnet_app_host_dir(env)
if app_host_dir and os.path.isdir(app_host_dir):
mono_defines = [("NETHOST_USE_AS_STATIC",)]
if env["tools"]:
mono_defines += [("GD_MONO_HOT_RELOAD",)]
module_configs.add_mode(
"mono",
includes=app_host_dir,
cli_args="module_mono_enabled=yes",
defines=mono_defines,
)
else:
print(".NET App Host directory not found. Generated project will not have build variants for .NET.")
mono_defines = [("GD_MONO_HOT_RELOAD",)] if env["tools"] else []
module_configs.add_mode(
"mono",
cli_args="module_mono_enabled=yes",
defines=mono_defines,
)
env["MSVSBUILDCOM"] = module_configs.build_commandline("scons")
env["MSVSREBUILDCOM"] = module_configs.build_commandline("scons vsproj=yes")

View File

@ -7,8 +7,8 @@ set -uo pipefail
# Loops through all code files tracked by Git.
git ls-files -- '*.c' '*.h' '*.cpp' '*.hpp' '*.cc' '*.hh' '*.cxx' '*.m' '*.mm' '*.inc' '*.java' '*.glsl' \
':!:.git/*' ':!:thirdparty/*' ':!:platform/android/java/lib/src/com/google/*' ':!:*-so_wrap.*' \
':!:tests/python_build/*' |
':!:.git/*' ':!:thirdparty/*' ':!:*/thirdparty/*' ':!:platform/android/java/lib/src/com/google/*' \
':!:*-so_wrap.*' ':!:tests/python_build/*' |
while read -r f; do
# Run clang-format.
clang-format --Wno-error=unknown -i "$f"

View File

@ -27,293 +27,3 @@ def configure(env, env_mono):
if env["tools"]:
env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"])
app_host_dir = find_dotnet_app_host_dir(env)
def check_app_host_file_exists(file):
file_path = os.path.join(app_host_dir, file)
if not os.path.isfile(file_path):
raise RuntimeError("File not found: " + file_path)
# TODO:
# All libnethost does for us is provide a function to find hostfxr.
# If we could handle that logic ourselves we could void linking it.
# nethost file names:
# static: libnethost.a/lib
# shared: libnethost.a/dylib and nethost.dll
check_app_host_file_exists("libnethost.lib" if os.name == "nt" else "libnethost.a")
check_app_host_file_exists("nethost.h")
check_app_host_file_exists("hostfxr.h")
check_app_host_file_exists("coreclr_delegates.h")
env_mono.Prepend(CPPPATH=app_host_dir)
env.Append(LIBPATH=[app_host_dir])
# Only the editor build links nethost, which is needed to find hostfxr.
# Exported games don't need this logic as hostfxr is bundled with them.
if tools_enabled:
libnethost_path = os.path.join(app_host_dir, "libnethost.lib" if os.name == "nt" else "libnethost.a")
if env["platform"] == "windows":
env_mono.Append(CPPDEFINES=["NETHOST_USE_AS_STATIC"])
if env.msvc:
env.Append(LINKFLAGS="libnethost.lib")
else:
env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"])
else:
is_apple = env["platform"] in ["macos", "ios"]
# is_macos = is_apple and not is_ios
# if is_ios and not is_ios_sim:
# env_mono.Append(CPPDEFINES=["IOS_DEVICE"])
if is_apple:
env.Append(LINKFLAGS=["-Wl,-force_load," + libnethost_path])
else:
env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"])
def find_dotnet_app_host_dir(env):
dotnet_version = "6.0"
dotnet_root = env["dotnet_root"]
if not dotnet_root:
dotnet_cmd = find_dotnet_executable(env["arch"])
if dotnet_cmd:
sdk_path = find_dotnet_sdk(dotnet_cmd, dotnet_version)
if sdk_path:
dotnet_root = os.path.abspath(os.path.join(sdk_path, os.pardir))
if not dotnet_root:
raise RuntimeError("Cannot find .NET Core Sdk")
print("Found .NET Core Sdk root directory: " + dotnet_root)
dotnet_cmd = os.path.join(dotnet_root, "dotnet.exe" if os.name == "nt" else "dotnet")
runtime_identifier = determine_runtime_identifier(env)
# TODO: In the future, if it can't be found this way, we want to obtain it
# from the runtime.{runtime_identifier}.Microsoft.NETCore.DotNetAppHost NuGet package.
app_host_version = find_app_host_version(dotnet_cmd, dotnet_version)
if not app_host_version:
raise RuntimeError("Cannot find .NET app host for version: " + dotnet_version)
def get_runtime_path():
return os.path.join(
dotnet_root,
"packs",
"Microsoft.NETCore.App.Host." + runtime_identifier,
app_host_version,
"runtimes",
runtime_identifier,
"native",
)
app_host_dir = get_runtime_path()
# Some Linux distros use their distro name as the RID in these paths.
# If the initial generic path doesn't exist, try to get the RID from `dotnet --info`.
# The generic RID should still be the first choice. Some platforms like Windows 10
# define the RID as `win10-x64` but still use the generic `win-x64` for directory names.
if not app_host_dir or not os.path.isdir(app_host_dir):
runtime_identifier = find_dotnet_cli_rid(dotnet_cmd)
app_host_dir = get_runtime_path()
return app_host_dir
def determine_runtime_identifier(env):
# The keys are Godot's names, the values are the Microsoft's names.
# List: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog
names_map = {
"windows": "win",
"macos": "osx",
"linuxbsd": "linux",
}
arch_map = {
"x86_64": "x64",
"x86_32": "x86",
"arm64": "arm64",
"arm32": "arm",
}
platform = env["platform"]
if is_desktop(platform):
return "%s-%s" % (names_map[platform], arch_map[env["arch"]])
else:
raise NotImplementedError()
def find_app_host_version(dotnet_cmd, search_version_str):
import subprocess
from distutils.version import LooseVersion
search_version = LooseVersion(search_version_str)
found_match = False
try:
env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US")
lines = subprocess.check_output([dotnet_cmd, "--list-runtimes"], env=env).splitlines()
for line_bytes in lines:
line = line_bytes.decode("utf-8")
if not line.startswith("Microsoft.NETCore.App "):
continue
parts = line.split(" ", 2)
if len(parts) < 3:
continue
version_str = parts[1]
version = LooseVersion(version_str)
if version >= search_version:
search_version = version
found_match = True
if found_match:
return str(search_version)
except (subprocess.CalledProcessError, OSError) as e:
import sys
print(e, file=sys.stderr)
return ""
def find_dotnet_arch(dotnet_cmd):
import subprocess
try:
env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US")
lines = subprocess.check_output([dotnet_cmd, "--info"], env=env).splitlines()
for line_bytes in lines:
line = line_bytes.decode("utf-8")
parts = line.split(":", 1)
if len(parts) < 2:
continue
arch_str = parts[0].strip()
if arch_str != "Architecture":
continue
arch_value = parts[1].strip()
arch_map = {"x64": "x86_64", "x86": "x86_32", "arm64": "arm64", "arm32": "arm32"}
return arch_map[arch_value]
except (subprocess.CalledProcessError, OSError) as e:
import sys
print(e, file=sys.stderr)
return ""
def find_dotnet_sdk(dotnet_cmd, search_version_str):
import subprocess
from distutils.version import LooseVersion
search_version = LooseVersion(search_version_str)
try:
env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US")
lines = subprocess.check_output([dotnet_cmd, "--list-sdks"], env=env).splitlines()
for line_bytes in lines:
line = line_bytes.decode("utf-8")
parts = line.split(" ", 1)
if len(parts) < 2:
continue
version_str = parts[0]
version = LooseVersion(version_str)
if version < search_version:
continue
path_part = parts[1]
return path_part[1 : path_part.find("]")]
except (subprocess.CalledProcessError, OSError) as e:
import sys
print(e, file=sys.stderr)
return ""
def find_dotnet_cli_rid(dotnet_cmd):
import subprocess
try:
env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US")
lines = subprocess.check_output([dotnet_cmd, "--info"], env=env).splitlines()
for line_bytes in lines:
line = line_bytes.decode("utf-8")
if not line.startswith(" RID:"):
continue
parts = line.split()
if len(parts) < 2:
continue
return parts[1]
except (subprocess.CalledProcessError, OSError) as e:
import sys
print(e, file=sys.stderr)
return ""
ENV_PATH_SEP = ";" if os.name == "nt" else ":"
def find_dotnet_executable(arch):
is_windows = os.name == "nt"
windows_exts = os.environ["PATHEXT"].split(ENV_PATH_SEP) if is_windows else None
path_dirs = os.environ["PATH"].split(ENV_PATH_SEP)
search_dirs = path_dirs + [os.getcwd()] # cwd is last in the list
for dir in path_dirs:
search_dirs += [
os.path.join(dir, "x64"),
os.path.join(dir, "x86"),
os.path.join(dir, "arm64"),
os.path.join(dir, "arm32"),
] # search subfolders for cross compiling
# `dotnet --info` may not specify architecture. In such cases,
# we fallback to the first one we find without architecture.
sdk_path_unknown_arch = ""
for dir in search_dirs:
path = os.path.join(dir, "dotnet")
if is_windows:
for extension in windows_exts:
path_with_ext = path + extension
if os.path.isfile(path_with_ext) and os.access(path_with_ext, os.X_OK):
sdk_arch = find_dotnet_arch(path_with_ext)
if sdk_arch == arch or arch == "":
return path_with_ext
elif sdk_arch == "":
sdk_path_unknown_arch = path_with_ext
else:
if os.path.isfile(path) and os.access(path, os.X_OK):
sdk_arch = find_dotnet_arch(path)
if sdk_arch == arch or arch == "":
return path
elif sdk_arch == "":
sdk_path_unknown_arch = path
return sdk_path_unknown_arch

View File

@ -4,23 +4,13 @@ supported_platforms = ["windows", "macos", "linuxbsd"]
def can_build(env, platform):
return not env["arch"].startswith("rv")
if env["arch"].startswith("rv"):
return False
if env["tools"]:
env.module_add_dependencies("mono", ["regex"])
def get_opts(platform):
from SCons.Variables import BoolVariable, PathVariable
default_mono_static = platform in ["ios", "web"]
default_mono_bundles_zlib = platform in ["web"]
return [
PathVariable(
"dotnet_root",
"Path to the .NET Sdk installation directory for the target platform and architecture",
"",
PathVariable.PathAccept,
),
]
return True
def configure(env):

View File

@ -0,0 +1,335 @@
/*************************************************************************/
/* hostfxr_resolver.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Adapted to Godot from the nethost library: https://github.com/dotnet/runtime/tree/main/src/native/corehost
*/
/*
The MIT License (MIT)
Copyright (c) .NET Foundation and Contributors
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "hostfxr_resolver.h"
#include "core/config/engine.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/os/os.h"
#ifdef WINDOWS_ENABLED
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "../utils/path_utils.h"
#include "semver.h"
// We don't use libnethost as it gives us issues with some compilers.
// This file tries to mimic libnethost's hostfxr_resolver search logic. We try to use the
// same function names for easier comparing in case we need to update this in the future.
namespace {
String get_hostfxr_file_name() {
#if defined(WINDOWS_ENABLED) || defined(UWP_ENABLED)
return "hostfxr.dll";
#elif defined(OSX_ENABLED) || defined(IOS_ENABLED)
return "libhostfxr.dylib";
#else
return "libhostfxr.so";
#endif
}
bool get_latest_fxr(const String &fxr_root, String &r_fxr_path) {
godotsharp::SemVerParser sem_ver_parser;
bool found_ver = false;
godotsharp::SemVer latest_ver;
String latest_ver_str;
Ref<DirAccess> da = DirAccess::open(fxr_root);
da->list_dir_begin();
for (String dir = da->get_next(); !dir.is_empty(); dir = da->get_next()) {
if (!da->current_is_dir() || dir == "." || dir == "..") {
continue;
}
String ver = dir.get_file();
godotsharp::SemVer fx_ver;
if (sem_ver_parser.parse(ver, fx_ver)) {
if (!found_ver || fx_ver > latest_ver) {
latest_ver = fx_ver;
latest_ver_str = ver;
found_ver = true;
}
}
}
if (!found_ver) {
return false;
}
String fxr_with_ver = path::join(fxr_root, latest_ver_str);
String hostfxr_file_path = path::join(fxr_with_ver, get_hostfxr_file_name());
ERR_FAIL_COND_V_MSG(!FileAccess::exists(hostfxr_file_path), false, "Missing hostfxr library in directory: " + fxr_with_ver);
r_fxr_path = hostfxr_file_path;
return true;
}
#ifdef WINDOWS_ENABLED
typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
BOOL is_wow64() {
BOOL wow64 = FALSE;
LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
if (fnIsWow64Process) {
if (!fnIsWow64Process(GetCurrentProcess(), &wow64)) {
wow64 = FALSE;
}
}
return wow64;
}
#endif
static const char *arch_name_map[][2] = {
{ "arm32", "arm" },
{ "arm64", "arm64" },
{ "rv64", "riscv64" },
{ "x86_64", "x64" },
{ "x86_32", "x86" },
{ nullptr, nullptr }
};
String get_dotnet_arch() {
String arch = Engine::get_singleton()->get_architecture_name();
int idx = 0;
while (arch_name_map[idx][0] != nullptr) {
if (arch_name_map[idx][0] == arch) {
return arch_name_map[idx][1];
}
idx++;
}
return "";
}
bool get_default_installation_dir(String &r_dotnet_root) {
#if defined(WINDOWS_ENABLED)
String program_files_env;
if (is_wow64()) {
// Running x86 on x64, looking for x86 install
program_files_env = "ProgramFiles(x86)";
} else {
program_files_env = "ProgramFiles";
}
String program_files_dir = OS::get_singleton()->get_environment(program_files_env);
if (program_files_dir.is_empty()) {
return false;
}
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
// When emulating x64 on arm
String dotnet_root_emulated = path::join(program_files_dir, "dotnet", "x64");
if (FileAccess::exists(path::join(dotnet_root_emulated, "dotnet.exe"))) {
r_dotnet_root = dotnet_root_emulated;
return true;
}
#endif
r_dotnet_root = path::join(program_files_dir, "dotnet");
return true;
#elif defined(TARGET_OSX)
r_dotnet_root = "/usr/local/share/dotnet";
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
// When emulating x64 on arm
String dotnet_root_emulated = path::join(r_dotnet_root, "x64");
if (FileAccess::exists(path::join(dotnet_root_emulated, "dotnet"))) {
r_dotnet_root = dotnet_root_emulated;
return true;
}
#endif
return true;
#else
r_dotnet_root = "/usr/share/dotnet";
return true;
#endif
}
bool get_install_location_from_file(const String &p_file_path, String &r_dotnet_root) {
Error err = OK;
Ref<FileAccess> f = FileAccess::open(p_file_path, FileAccess::READ, &err);
if (f.is_null() || err != OK) {
return false;
}
String line = f->get_line();
if (line.is_empty()) {
return false;
}
r_dotnet_root = line;
return true;
}
bool get_dotnet_self_registered_dir(String &r_dotnet_root) {
#if defined(WINDOWS_ENABLED)
String sub_key = "SOFTWARE\\dotnet\\Setup\\InstalledVersions\\" + get_dotnet_arch();
Char16String value = String("InstallLocation").utf16();
HKEY hkey = NULL;
LSTATUS result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(sub_key.utf16().get_data()), 0, KEY_READ | KEY_WOW64_32KEY, &hkey);
if (result != ERROR_SUCCESS) {
return false;
}
DWORD size = 0;
result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, nullptr, &size);
if (result != ERROR_SUCCESS || size == 0) {
RegCloseKey(hkey);
return false;
}
Vector<WCHAR> buffer;
buffer.resize(size / sizeof(WCHAR));
result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, (LPBYTE)buffer.ptrw(), &size);
if (result != ERROR_SUCCESS) {
RegCloseKey(hkey);
return false;
}
r_dotnet_root = String::utf16((const char16_t *)buffer.ptr());
RegCloseKey(hkey);
return true;
#else
String install_location_file = path::join("/etc/dotnet", "install_location_" + get_dotnet_arch().to_lower());
if (get_install_location_from_file(install_location_file, r_dotnet_root)) {
return true;
}
if (FileAccess::exists(install_location_file)) {
// Don't try with the legacy location, this will fall back to the hard-coded default install location
return false;
}
String legacy_install_location_file = path::join("/etc/dotnet", "install_location");
return get_install_location_from_file(legacy_install_location_file, r_dotnet_root);
#endif
}
bool get_file_path_from_env(const String &p_env_key, String &r_dotnet_root) {
String env_value = OS::get_singleton()->get_environment(p_env_key);
if (!env_value.is_empty()) {
env_value = path::realpath(env_value);
if (DirAccess::exists(env_value)) {
r_dotnet_root = env_value;
return true;
}
}
return false;
}
bool get_dotnet_root_from_env(String &r_dotnet_root) {
String dotnet_root_env = "DOTNET_ROOT";
String arch_for_env = get_dotnet_arch();
if (!arch_for_env.is_empty()) {
// DOTNET_ROOT_<arch>
if (get_file_path_from_env(dotnet_root_env + "_" + arch_for_env.to_upper(), r_dotnet_root)) {
return true;
}
}
#ifdef WINDOWS_ENABLED
// WoW64-only: DOTNET_ROOT(x86)
if (is_wow64() && get_file_path_from_env("DOTNET_ROOT(x86)", r_dotnet_root)) {
return true;
}
#endif
// DOTNET_ROOT
return get_file_path_from_env(dotnet_root_env, r_dotnet_root);
}
} //namespace
bool godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(const String &p_dotnet_root, String &r_fxr_path) {
String fxr_dir = path::join(p_dotnet_root, "host", "fxr");
ERR_FAIL_COND_V_MSG(!DirAccess::exists(fxr_dir), false, "The host fxr folder does not exist: " + fxr_dir);
return get_latest_fxr(fxr_dir, r_fxr_path);
}
bool godotsharp::hostfxr_resolver::try_get_path(String &r_dotnet_root, String &r_fxr_path) {
if (!get_dotnet_root_from_env(r_dotnet_root) &&
!get_dotnet_self_registered_dir(r_dotnet_root) &&
!get_default_installation_dir(r_dotnet_root)) {
return false;
}
return try_get_path_from_dotnet_root(r_dotnet_root, r_fxr_path);
}

View File

@ -0,0 +1,45 @@
/*************************************************************************/
/* hostfxr_resolver.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef HOSTFXR_RESOLVER_H
#define HOSTFXR_RESOLVER_H
#include "core/string/ustring.h"
namespace godotsharp {
namespace hostfxr_resolver {
bool try_get_path_from_dotnet_root(const String &p_dotnet_root, String &r_out_fxr_path);
bool try_get_path(String &r_out_dotnet_root, String &r_out_fxr_path);
} //namespace hostfxr_resolver
} //namespace godotsharp
#endif // HOSTFXR_RESOLVER_H

View File

@ -0,0 +1,149 @@
/*************************************************************************/
/* semver.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "semver.h"
bool godotsharp::SemVer::parse_digit_only_field(const String &p_field, uint64_t &r_result) {
if (p_field.is_empty()) {
return false;
}
int64_t integer = 0;
for (int i = 0; i < p_field.length(); i++) {
char32_t c = p_field[i];
if (is_digit(c)) {
bool overflow = ((uint64_t)integer > UINT64_MAX / 10) || ((uint64_t)integer == UINT64_MAX / 10 && c > '5');
ERR_FAIL_COND_V_MSG(overflow, false, "Cannot represent '" + p_field + "' as a 64-bit unsigned integer, since the value is too large.");
integer *= 10;
integer += c - '0';
} else {
return false;
}
}
r_result = (uint64_t)integer;
return true;
}
int godotsharp::SemVer::cmp(const godotsharp::SemVer &p_a, const godotsharp::SemVer &p_b) {
if (p_a.major != p_b.major) {
return p_a.major > p_b.major ? 1 : -1;
}
if (p_a.minor != p_b.minor) {
return p_a.minor > p_b.minor ? 1 : -1;
}
if (p_a.patch != p_b.patch) {
return p_a.patch > p_b.patch ? 1 : -1;
}
if (p_a.prerelease.is_empty() && p_b.prerelease.is_empty()) {
return 0;
}
if (p_a.prerelease.is_empty() || p_b.prerelease.is_empty()) {
return p_a.prerelease.is_empty() ? 1 : -1;
}
if (p_a.prerelease != p_b.prerelease) {
// This could be optimized, but I'm too lazy
Vector<String> a_field_set = p_a.prerelease.split(".");
Vector<String> b_field_set = p_b.prerelease.split(".");
int a_field_count = a_field_set.size();
int b_field_count = b_field_set.size();
int min_field_count = MIN(a_field_count, b_field_count);
for (int i = 0; i < min_field_count; i++) {
const String &a_field = a_field_set[i];
const String &b_field = b_field_set[i];
if (a_field == b_field) {
continue;
}
uint64_t a_num;
bool a_is_digit_only = parse_digit_only_field(a_field, a_num);
uint64_t b_num;
bool b_is_digit_only = parse_digit_only_field(b_field, b_num);
if (a_is_digit_only && b_is_digit_only) {
// Identifiers consisting of only digits are compared numerically.
if (a_num == b_num) {
continue;
}
return a_num > b_num ? 1 : -1;
}
if (a_is_digit_only || b_is_digit_only) {
// Numeric identifiers always have lower precedence than non-numeric identifiers.
return b_is_digit_only ? 1 : -1;
}
// Identifiers with letters or hyphens are compared lexically in ASCII sort order.
return a_field > b_field ? 1 : -1;
}
if (a_field_count != b_field_count) {
// A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal.
return a_field_count > b_field_count ? 1 : -1;
}
}
return 0;
}
bool godotsharp::SemVerParser::parse(const String &p_ver_text, godotsharp::SemVer &r_semver) {
if (!regex.is_valid() && regex.get_pattern().is_empty()) {
regex.compile("^(?P<major>0|[1-9]\\d*)\\.(?P<minor>0|[1-9]\\d*)\\.(?P<patch>0|[1-9]\\d*)(?:-(?P<prerelease>(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$");
ERR_FAIL_COND_V(!regex.is_valid(), false);
}
Ref<RegExMatch> match = regex.search(p_ver_text);
if (match.is_valid()) {
r_semver = SemVer(
match->get_string("major").to_int(),
match->get_string("minor").to_int(),
match->get_string("patch").to_int(),
match->get_string("prerelease"),
match->get_string("buildmetadata"));
return true;
}
return false;
}

View File

@ -0,0 +1,106 @@
/*************************************************************************/
/* semver.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef SEMVER_H
#define SEMVER_H
#include "core/string/ustring.h"
#include "modules/regex/regex.h"
// <sys/sysmacros.h> is included somewhere, which defines major(dev) to gnu_dev_major(dev)
#if defined(major)
#undef major
#endif
#if defined(minor)
#undef minor
#endif
namespace godotsharp {
struct SemVer {
private:
static bool parse_digit_only_field(const String &p_field, uint64_t &r_result);
static int cmp(const SemVer &p_a, const SemVer &p_b);
public:
int major = 0;
int minor = 0;
int patch = 0;
String prerelease;
String build_metadata;
bool operator==(const SemVer &b) const {
return cmp(*this, b) == 0;
}
bool operator!=(const SemVer &b) const {
return !operator==(b);
}
bool operator<(const SemVer &b) const {
return cmp(*this, b) < 0;
}
bool operator>(const SemVer &b) const {
return cmp(*this, b) > 0;
}
bool operator<=(const SemVer &b) const {
return cmp(*this, b) <= 0;
}
bool operator>=(const SemVer &b) const {
return cmp(*this, b) >= 0;
}
SemVer() {}
SemVer(int p_major, int p_minor, int p_patch,
const String &p_prerelease, const String &p_build_metadata) :
major(p_major),
minor(p_minor),
patch(p_patch),
prerelease(p_prerelease),
build_metadata(p_build_metadata) {
}
};
struct SemVerParser {
private:
RegEx regex;
public:
bool parse(const String &p_ver_text, SemVer &r_semver);
};
} //namespace godotsharp
#endif // SEMVER_H

View File

@ -43,12 +43,13 @@
#include "../utils/path_utils.h"
#include "gd_mono_cache.h"
#include "../thirdparty/coreclr_delegates.h"
#include "../thirdparty/hostfxr.h"
#ifdef TOOLS_ENABLED
#include <nethost.h>
#include "../editor/hostfxr_resolver.h"
#endif
#include <coreclr_delegates.h>
#include <hostfxr.h>
#ifdef UNIX_ENABLED
#include <dlfcn.h>
#endif
@ -88,85 +89,43 @@ HostFxrCharString str_to_hostfxr(const String &p_str) {
#endif
}
#ifdef TOOLS_ENABLED
String str_from_hostfxr(const char_t *p_buffer) {
#ifdef _WIN32
return String::utf16((const char16_t *)p_buffer);
#else
return String::utf8((const char *)p_buffer);
#endif
}
#endif
const char_t *get_data(const HostFxrCharString &p_char_str) {
return (const char_t *)p_char_str.get_data();
}
#ifdef TOOLS_ENABLED
String find_hostfxr(size_t p_known_buffer_size, get_hostfxr_parameters *p_get_hostfxr_params) {
// Pre-allocate a large buffer for the path to hostfxr
Vector<char_t> buffer;
buffer.resize(p_known_buffer_size);
int rc = get_hostfxr_path(buffer.ptrw(), &p_known_buffer_size, p_get_hostfxr_params);
ERR_FAIL_COND_V_MSG(rc != 0, String(), "get_hostfxr_path failed with code: " + itos(rc));
return str_from_hostfxr(buffer.ptr());
}
#endif
String find_hostfxr() {
#ifdef TOOLS_ENABLED
const int CoreHostLibMissingFailure = 0x80008083;
const int HostApiBufferTooSmall = 0x80008098;
size_t buffer_size = 0;
int rc = get_hostfxr_path(nullptr, &buffer_size, nullptr);
if (rc == HostApiBufferTooSmall) {
return find_hostfxr(buffer_size, nullptr);
String dotnet_root;
String fxr_path;
if (godotsharp::hostfxr_resolver::try_get_path(dotnet_root, fxr_path)) {
return fxr_path;
}
if (rc == CoreHostLibMissingFailure) {
// Apparently `get_hostfxr_path` doesn't look for dotnet in `PATH`? (I suppose it needs the
// `DOTNET_ROOT` environment variable). If it fails, we try to find the dotnet executable
// in `PATH` ourselves and pass its location as `dotnet_root` to `get_hostfxr_path`.
String dotnet_exe = path::find_executable("dotnet");
// hostfxr_resolver doesn't look for dotnet in `PATH`. If it fails, we try to find the dotnet
// executable in `PATH` here and pass its location as `dotnet_root` to `get_hostfxr_path`.
String dotnet_exe = path::find_executable("dotnet");
if (!dotnet_exe.is_empty()) {
// The file found in PATH may be a symlink
dotnet_exe = path::abspath(path::realpath(dotnet_exe));
if (!dotnet_exe.is_empty()) {
// The file found in PATH may be a symlink
dotnet_exe = path::abspath(path::realpath(dotnet_exe));
// TODO:
// Sometimes, the symlink may not point to the dotnet executable in the dotnet root.
// That's the case with snaps. The snap install should have been found with the
// previous `get_hostfxr_path`, but it would still be better to do this properly
// and use something like `dotnet --list-sdks/runtimes` to find the actual location.
// This way we could also check if the proper sdk or runtime is installed. This would
// allow us to fail gracefully and show some helpful information in the editor.
// TODO:
// Sometimes, the symlink may not point to the dotnet executable in the dotnet root.
// That's the case with snaps. The snap install should have been found with the
// previous `get_hostfxr_path`, but it would still be better to do this properly
// and use something like `dotnet --list-sdks/runtimes` to find the actual location.
// This way we could also check if the proper sdk or runtime is installed. This would
// allow us to fail gracefully and show some helpful information in the editor.
HostFxrCharString dotnet_root = str_to_hostfxr(dotnet_exe.get_base_dir());
get_hostfxr_parameters get_hostfxr_parameters = {
sizeof(get_hostfxr_parameters),
nullptr,
get_data(dotnet_root)
};
buffer_size = 0;
rc = get_hostfxr_path(nullptr, &buffer_size, &get_hostfxr_parameters);
if (rc == HostApiBufferTooSmall) {
return find_hostfxr(buffer_size, &get_hostfxr_parameters);
}
dotnet_root = dotnet_exe.get_base_dir();
if (godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(dotnet_root, fxr_path)) {
return fxr_path;
}
}
if (rc == CoreHostLibMissingFailure) {
ERR_PRINT(String() + ".NET: One of the dependent libraries is missing. " +
"Typically when the `hostfxr`, `hostpolicy` or `coreclr` dynamic " +
"libraries are not present in the expected locations.");
}
ERR_PRINT(String() + ".NET: One of the dependent libraries is missing. " +
"Typically when the `hostfxr`, `hostpolicy` or `coreclr` dynamic " +
"libraries are not present in the expected locations.");
return String();
#else

View File

@ -0,0 +1,47 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#ifndef __CORECLR_DELEGATES_H__
#define __CORECLR_DELEGATES_H__
#include <stdint.h>
#if defined(_WIN32)
#define CORECLR_DELEGATE_CALLTYPE __stdcall
#ifdef _WCHAR_T_DEFINED
typedef wchar_t char_t;
#else
typedef unsigned short char_t;
#endif
#else
#define CORECLR_DELEGATE_CALLTYPE
typedef char char_t;
#endif
#define UNMANAGEDCALLERSONLY_METHOD ((const char_t*)-1)
// Signature of delegate returned by coreclr_delegate_type::load_assembly_and_get_function_pointer
typedef int (CORECLR_DELEGATE_CALLTYPE *load_assembly_and_get_function_pointer_fn)(
const char_t *assembly_path /* Fully qualified path to assembly */,
const char_t *type_name /* Assembly qualified type name */,
const char_t *method_name /* Public static method name compatible with delegateType */,
const char_t *delegate_type_name /* Assembly qualified delegate type name or null
or UNMANAGEDCALLERSONLY_METHOD if the method is marked with
the UnmanagedCallersOnlyAttribute. */,
void *reserved /* Extensibility parameter (currently unused and must be 0) */,
/*out*/ void **delegate /* Pointer where to store the function pointer result */);
// Signature of delegate returned by load_assembly_and_get_function_pointer_fn when delegate_type_name == null (default)
typedef int (CORECLR_DELEGATE_CALLTYPE *component_entry_point_fn)(void *arg, int32_t arg_size_in_bytes);
typedef int (CORECLR_DELEGATE_CALLTYPE *get_function_pointer_fn)(
const char_t *type_name /* Assembly qualified type name */,
const char_t *method_name /* Public static method name compatible with delegateType */,
const char_t *delegate_type_name /* Assembly qualified delegate type name or null,
or UNMANAGEDCALLERSONLY_METHOD if the method is marked with
the UnmanagedCallersOnlyAttribute. */,
void *load_context /* Extensibility parameter (currently unused and must be 0) */,
void *reserved /* Extensibility parameter (currently unused and must be 0) */,
/*out*/ void **delegate /* Pointer where to store the function pointer result */);
#endif // __CORECLR_DELEGATES_H__

323
modules/mono/thirdparty/hostfxr.h vendored Normal file
View File

@ -0,0 +1,323 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#ifndef __HOSTFXR_H__
#define __HOSTFXR_H__
#include <stddef.h>
#include <stdint.h>
#if defined(_WIN32)
#define HOSTFXR_CALLTYPE __cdecl
#ifdef _WCHAR_T_DEFINED
typedef wchar_t char_t;
#else
typedef unsigned short char_t;
#endif
#else
#define HOSTFXR_CALLTYPE
typedef char char_t;
#endif
enum hostfxr_delegate_type
{
hdt_com_activation,
hdt_load_in_memory_assembly,
hdt_winrt_activation,
hdt_com_register,
hdt_com_unregister,
hdt_load_assembly_and_get_function_pointer,
hdt_get_function_pointer,
};
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_main_fn)(const int argc, const char_t **argv);
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_main_startupinfo_fn)(
const int argc,
const char_t **argv,
const char_t *host_path,
const char_t *dotnet_root,
const char_t *app_path);
typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_main_bundle_startupinfo_fn)(
const int argc,
const char_t** argv,
const char_t* host_path,
const char_t* dotnet_root,
const char_t* app_path,
int64_t bundle_header_offset);
typedef void(HOSTFXR_CALLTYPE *hostfxr_error_writer_fn)(const char_t *message);
//
// Sets a callback which is to be used to write errors to.
//
// Parameters:
// error_writer
// A callback function which will be invoked every time an error is to be reported.
// Or nullptr to unregister previously registered callback and return to the default behavior.
// Return value:
// The previously registered callback (which is now unregistered), or nullptr if no previous callback
// was registered
//
// The error writer is registered per-thread, so the registration is thread-local. On each thread
// only one callback can be registered. Subsequent registrations overwrite the previous ones.
//
// By default no callback is registered in which case the errors are written to stderr.
//
// Each call to the error writer is sort of like writing a single line (the EOL character is omitted).
// Multiple calls to the error writer may occur for one failure.
//
// If the hostfxr invokes functions in hostpolicy as part of its operation, the error writer
// will be propagated to hostpolicy for the duration of the call. This means that errors from
// both hostfxr and hostpolicy will be reporter through the same error writer.
//
typedef hostfxr_error_writer_fn(HOSTFXR_CALLTYPE *hostfxr_set_error_writer_fn)(hostfxr_error_writer_fn error_writer);
typedef void* hostfxr_handle;
struct hostfxr_initialize_parameters
{
size_t size;
const char_t *host_path;
const char_t *dotnet_root;
};
//
// Initializes the hosting components for a dotnet command line running an application
//
// Parameters:
// argc
// Number of argv arguments
// argv
// Command-line arguments for running an application (as if through the dotnet executable).
// Only command-line arguments which are accepted by runtime installation are supported, SDK/CLI commands are not supported.
// For example 'app.dll app_argument_1 app_argument_2`.
// parameters
// Optional. Additional parameters for initialization
// host_context_handle
// On success, this will be populated with an opaque value representing the initialized host context
//
// Return value:
// Success - Hosting components were successfully initialized
// HostInvalidState - Hosting components are already initialized
//
// This function parses the specified command-line arguments to determine the application to run. It will
// then find the corresponding .runtimeconfig.json and .deps.json with which to resolve frameworks and
// dependencies and prepare everything needed to load the runtime.
//
// This function only supports arguments for running an application. It does not support SDK commands.
//
// This function does not load the runtime.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_initialize_for_dotnet_command_line_fn)(
int argc,
const char_t **argv,
const struct hostfxr_initialize_parameters *parameters,
/*out*/ hostfxr_handle *host_context_handle);
//
// Initializes the hosting components using a .runtimeconfig.json file
//
// Parameters:
// runtime_config_path
// Path to the .runtimeconfig.json file
// parameters
// Optional. Additional parameters for initialization
// host_context_handle
// On success, this will be populated with an opaque value representing the initialized host context
//
// Return value:
// Success - Hosting components were successfully initialized
// Success_HostAlreadyInitialized - Config is compatible with already initialized hosting components
// Success_DifferentRuntimeProperties - Config has runtime properties that differ from already initialized hosting components
// CoreHostIncompatibleConfig - Config is incompatible with already initialized hosting components
//
// This function will process the .runtimeconfig.json to resolve frameworks and prepare everything needed
// to load the runtime. It will only process the .deps.json from frameworks (not any app/component that
// may be next to the .runtimeconfig.json).
//
// This function does not load the runtime.
//
// If called when the runtime has already been loaded, this function will check if the specified runtime
// config is compatible with the existing runtime.
//
// Both Success_HostAlreadyInitialized and Success_DifferentRuntimeProperties codes are considered successful
// initializations. In the case of Success_DifferentRuntimeProperties, it is left to the consumer to verify that
// the difference in properties is acceptable.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_initialize_for_runtime_config_fn)(
const char_t *runtime_config_path,
const struct hostfxr_initialize_parameters *parameters,
/*out*/ hostfxr_handle *host_context_handle);
//
// Gets the runtime property value for an initialized host context
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
// name
// Runtime property name
// value
// Out parameter. Pointer to a buffer with the property value.
//
// Return value:
// The error code result.
//
// The buffer pointed to by value is owned by the host context. The lifetime of the buffer is only
// guaranteed until any of the below occur:
// - a 'run' method is called for the host context
// - properties are changed via hostfxr_set_runtime_property_value
// - the host context is closed via 'hostfxr_close'
//
// If host_context_handle is nullptr and an active host context exists, this function will get the
// property value for the active host context.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_property_value_fn)(
const hostfxr_handle host_context_handle,
const char_t *name,
/*out*/ const char_t **value);
//
// Sets the value of a runtime property for an initialized host context
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
// name
// Runtime property name
// value
// Value to set
//
// Return value:
// The error code result.
//
// Setting properties is only supported for the first host context, before the runtime has been loaded.
//
// If the property already exists in the host context, it will be overwritten. If value is nullptr, the
// property will be removed.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_set_runtime_property_value_fn)(
const hostfxr_handle host_context_handle,
const char_t *name,
const char_t *value);
//
// Gets all the runtime properties for an initialized host context
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
// count
// [in] Size of the keys and values buffers
// [out] Number of properties returned (size of keys/values buffers used). If the input value is too
// small or keys/values is nullptr, this is populated with the number of available properties
// keys
// Array of pointers to buffers with runtime property keys
// values
// Array of pointers to buffers with runtime property values
//
// Return value:
// The error code result.
//
// The buffers pointed to by keys and values are owned by the host context. The lifetime of the buffers is only
// guaranteed until any of the below occur:
// - a 'run' method is called for the host context
// - properties are changed via hostfxr_set_runtime_property_value
// - the host context is closed via 'hostfxr_close'
//
// If host_context_handle is nullptr and an active host context exists, this function will get the
// properties for the active host context.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_properties_fn)(
const hostfxr_handle host_context_handle,
/*inout*/ size_t * count,
/*out*/ const char_t **keys,
/*out*/ const char_t **values);
//
// Load CoreCLR and run the application for an initialized host context
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
//
// Return value:
// If the app was successfully run, the exit code of the application. Otherwise, the error code result.
//
// The host_context_handle must have been initialized using hostfxr_initialize_for_dotnet_command_line.
//
// This function will not return until the managed application exits.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_run_app_fn)(const hostfxr_handle host_context_handle);
//
// Gets a typed delegate from the currently loaded CoreCLR or from a newly created one.
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
// type
// Type of runtime delegate requested
// delegate
// An out parameter that will be assigned the delegate.
//
// Return value:
// The error code result.
//
// If the host_context_handle was initialized using hostfxr_initialize_for_runtime_config,
// then all delegate types are supported.
// If the host_context_handle was initialized using hostfxr_initialize_for_dotnet_command_line,
// then only the following delegate types are currently supported:
// hdt_load_assembly_and_get_function_pointer
// hdt_get_function_pointer
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_delegate_fn)(
const hostfxr_handle host_context_handle,
enum hostfxr_delegate_type type,
/*out*/ void **delegate);
//
// Closes an initialized host context
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
//
// Return value:
// The error code result.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_close_fn)(const hostfxr_handle host_context_handle);
struct hostfxr_dotnet_environment_sdk_info
{
size_t size;
const char_t* version;
const char_t* path;
};
typedef void(HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_result_fn)(
const struct hostfxr_dotnet_environment_info* info,
void* result_context);
struct hostfxr_dotnet_environment_framework_info
{
size_t size;
const char_t* name;
const char_t* version;
const char_t* path;
};
struct hostfxr_dotnet_environment_info
{
size_t size;
const char_t* hostfxr_version;
const char_t* hostfxr_commit_hash;
size_t sdk_count;
const struct hostfxr_dotnet_environment_sdk_info* sdks;
size_t framework_count;
const struct hostfxr_dotnet_environment_framework_info* frameworks;
};
#endif //__HOSTFXR_H__