mono_configure.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import os
  2. import os.path
  3. def is_desktop(platform):
  4. return platform in ["windows", "macos", "linuxbsd", "server", "uwp", "haiku"]
  5. def is_unix_like(platform):
  6. return platform in ["macos", "linuxbsd", "server", "android", "haiku", "ios"]
  7. def module_supports_tools_on(platform):
  8. return is_desktop(platform)
  9. def configure(env, env_mono):
  10. # is_android = env["platform"] == "android"
  11. # is_javascript = env["platform"] == "javascript"
  12. # is_ios = env["platform"] == "ios"
  13. # is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"]
  14. tools_enabled = env["tools"]
  15. if tools_enabled and not module_supports_tools_on(env["platform"]):
  16. raise RuntimeError("This module does not currently support building for this platform with tools enabled")
  17. if env["tools"] or env["target"] != "release":
  18. env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"])
  19. app_host_dir = find_dotnet_app_host_dir(env)
  20. def check_app_host_file_exists(file):
  21. file_path = os.path.join(app_host_dir, file)
  22. if not os.path.isfile(file_path):
  23. raise RuntimeError("File not found: " + file_path)
  24. # TODO:
  25. # All libnethost does for us is provide a function to find hostfxr.
  26. # If we could handle that logic ourselves we could void linking it.
  27. # nethost file names:
  28. # static: libnethost.a/lib
  29. # shared: libnethost.a/dylib and nethost.dll
  30. check_app_host_file_exists("libnethost.lib" if os.name == "nt" else "libnethost.a")
  31. check_app_host_file_exists("nethost.h")
  32. check_app_host_file_exists("hostfxr.h")
  33. check_app_host_file_exists("coreclr_delegates.h")
  34. env.Append(LIBPATH=[app_host_dir])
  35. env_mono.Prepend(CPPPATH=app_host_dir)
  36. libnethost_path = os.path.join(app_host_dir, "libnethost.lib" if os.name == "nt" else "libnethost.a")
  37. if env["platform"] == "windows":
  38. env_mono.Append(CPPDEFINES=["NETHOST_USE_AS_STATIC"])
  39. if env.msvc:
  40. env.Append(LINKFLAGS="libnethost.lib")
  41. else:
  42. env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"])
  43. else:
  44. is_apple = env["platform"] in ["macos", "ios"]
  45. # is_macos = is_apple and not is_ios
  46. # if is_ios and not is_ios_sim:
  47. # env_mono.Append(CPPDEFINES=["IOS_DEVICE"])
  48. if is_apple:
  49. env.Append(LINKFLAGS=["-Wl,-force_load," + libnethost_path])
  50. else:
  51. env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"])
  52. def find_dotnet_app_host_dir(env):
  53. dotnet_root = env["dotnet_root"]
  54. if not dotnet_root:
  55. dotnet_exe = find_executable("dotnet")
  56. if dotnet_exe:
  57. dotnet_exe_realpath = os.path.realpath(dotnet_exe) # Eliminate symbolic links
  58. dotnet_root = os.path.abspath(os.path.join(dotnet_exe_realpath, os.pardir))
  59. else:
  60. raise RuntimeError("Cannot find .NET Core Sdk")
  61. print("Found .NET Core Sdk root directory: " + dotnet_root)
  62. dotnet_cmd = os.path.join(dotnet_root, "dotnet.exe" if os.name == "nt" else "dotnet")
  63. runtime_identifier = determine_runtime_identifier(env)
  64. # TODO: In the future, if it can't be found this way, we want to obtain it
  65. # from the runtime.{runtime_identifier}.Microsoft.NETCore.DotNetAppHost NuGet package.
  66. app_host_search_version = "5.0"
  67. app_host_version = find_app_host_version(dotnet_cmd, app_host_search_version)
  68. if not app_host_version:
  69. raise RuntimeError("Cannot find .NET app host for version: " + app_host_search_version)
  70. def get_runtime_path():
  71. return os.path.join(
  72. dotnet_root,
  73. "packs",
  74. "Microsoft.NETCore.App.Host." + runtime_identifier,
  75. app_host_version,
  76. "runtimes",
  77. runtime_identifier,
  78. "native",
  79. )
  80. app_host_dir = get_runtime_path()
  81. # Some Linux distros use their distro name as the RID in these paths.
  82. # If the initial generic path doesn't exist, try to get the RID from `dotnet --info`.
  83. # The generic RID should still be the first choice. Some platforms like Windows 10
  84. # define the RID as `win10-x64` but still use the generic `win-x64` for directory names.
  85. if not app_host_dir or not os.path.isdir(app_host_dir):
  86. runtime_identifier = find_dotnet_cli_rid(dotnet_cmd)
  87. app_host_dir = get_runtime_path()
  88. return app_host_dir
  89. def determine_runtime_identifier(env):
  90. names_map = {
  91. "windows": "win",
  92. "macos": "osx",
  93. "linuxbsd": "linux",
  94. "server": "linux", # FIXME: Is server linux only, or also macos?
  95. }
  96. # .NET RID architectures: x86, x64, arm, or arm64
  97. platform = env["platform"]
  98. if is_desktop(platform):
  99. if env["arch"] in ["arm", "arm32"]:
  100. rid = "arm"
  101. elif env["arch"] == "arm64":
  102. rid = "arm64"
  103. else:
  104. bits = env["bits"]
  105. bit_arch_map = {"64": "x64", "32": "x86"}
  106. rid = bit_arch_map[bits]
  107. return "%s-%s" % (names_map[platform], rid)
  108. else:
  109. raise NotImplementedError()
  110. def find_app_host_version(dotnet_cmd, search_version):
  111. import subprocess
  112. try:
  113. lines = subprocess.check_output([dotnet_cmd, "--list-runtimes"]).splitlines()
  114. for line_bytes in lines:
  115. line = line_bytes.decode("utf-8")
  116. if not line.startswith("Microsoft.NETCore.App "):
  117. continue
  118. parts = line.split(" ")
  119. if len(parts) < 2:
  120. continue
  121. version = parts[1]
  122. # Look for 6.0.0 or 6.0.0-*
  123. if version.startswith(search_version + "."):
  124. return version
  125. except (subprocess.CalledProcessError, OSError):
  126. pass
  127. return ""
  128. def find_dotnet_cli_rid(dotnet_cmd):
  129. import subprocess
  130. try:
  131. env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US")
  132. lines = subprocess.check_output([dotnet_cmd, "--info"], env=env).splitlines()
  133. for line_bytes in lines:
  134. line = line_bytes.decode("utf-8")
  135. if not line.startswith(" RID:"):
  136. continue
  137. parts = line.split()
  138. if len(parts) < 2:
  139. continue
  140. return parts[1]
  141. except (subprocess.CalledProcessError, OSError) as e:
  142. import sys
  143. print(e, file=sys.stderr)
  144. return ""
  145. ENV_PATH_SEP = ";" if os.name == "nt" else ":"
  146. def find_executable(name):
  147. is_windows = os.name == "nt"
  148. windows_exts = os.environ["PATHEXT"].split(ENV_PATH_SEP) if is_windows else None
  149. path_dirs = os.environ["PATH"].split(ENV_PATH_SEP)
  150. search_dirs = path_dirs + [os.getcwd()] # cwd is last in the list
  151. for dir in search_dirs:
  152. path = os.path.join(dir, name)
  153. if is_windows:
  154. for extension in windows_exts:
  155. path_with_ext = path + extension
  156. if os.path.isfile(path_with_ext) and os.access(path_with_ext, os.X_OK):
  157. return path_with_ext
  158. else:
  159. if os.path.isfile(path) and os.access(path, os.X_OK):
  160. return path
  161. return ""