Skip to content

Commit

Permalink
Merge pull request #65438 from neikeq/replace-libnethost-dependency
Browse files Browse the repository at this point in the history
Replace libnethost dependency to find hostfxr
  • Loading branch information
akien-mga committed Sep 7, 2022
2 parents edf8749 + f784fb2 commit 6b92dbf
Show file tree
Hide file tree
Showing 11 changed files with 1,048 additions and 393 deletions.
21 changes: 6 additions & 15 deletions methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -819,21 +819,12 @@ def __getitem__(self, k: str):
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")
Expand Down
4 changes: 2 additions & 2 deletions misc/scripts/clang_format.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
290 changes: 0 additions & 290 deletions modules/mono/build_scripts/mono_configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
20 changes: 5 additions & 15 deletions modules/mono/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,13 @@


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):
Expand Down
Loading

0 comments on commit 6b92dbf

Please sign in to comment.