Mono: Editor and export template dependencies and fixes
- Bundle editor dependencies: - 'GodotSharp': Root data directory for the editor - 'Tools': Editor dependencies. Only GodotSharp.dll for now. - 'Api': Prebuilt GodotSharp and GodotSharpEditor API assemblies. - 'Mono': Mono files to bundle with the editor. - 'bin': (Optional, not used for now) Mono bin directory. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - 'lib/mono/4.5': Framework assemblies. - Added build option to copy the required files from the mono installation to 'GodotSharp/Mono'. Enable with 'copy_mono_root=yes'. Disabled by default. - Export template dependencies: - 'data_AppName'/'data_Godot': - 'Mono': Mono files to bundle with the game. - 'etc': Mono configuration files. - 'lib': Mono dependency shared libraries. - The data directory is generated when compiling and must be bundled with the export templates. In the case of OSX, the data directory must be placed inside the 'osx.zip' export template. - In OSX, alternative location for directories (needed for app bundles) are: - 'data_AppName/Mono/etc' --> '../Resources/GodotSharp/Mono/etc' - 'data_AppName/Mono/lib' --> '../Frameworks/GodotSharp/Mono/lib' - The editor can bundle prebuilt API assemblies. - Generate them with a tools build by running: `--generate-cs-core-api <GodotSharp_OutputDir> --generate-cs-editor-api <GodotSharpEditor_OutputDir> <GodotSharp_OutputDir>/bin/Release/GodotSharp.dll` (This command will be simplified in the future and both projects will be in the same solution) - Build the solutions and copy the output files to '#bin/GodotSharp/Api'. - Fixed API assembly being added twice during the export process.
This commit is contained in:
parent
2e87703136
commit
d7ece43b74
15 changed files with 651 additions and 239 deletions
|
@ -244,16 +244,13 @@ def mono_build_solution(source, target, env):
|
|||
|
||||
copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
|
||||
|
||||
output_dir = Dir('#bin').abspath
|
||||
assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath
|
||||
if env['tools']:
|
||||
output_dir = Dir('#bin').abspath
|
||||
editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
|
||||
|
||||
mono_sln_builder = Builder(action=mono_build_solution)
|
||||
env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
|
||||
env_mono.MonoBuildSolution(
|
||||
os.path.join(assemblies_output_dir, 'GodotSharpTools.dll'),
|
||||
'editor/GodotSharpTools/GodotSharpTools.sln'
|
||||
)
|
||||
|
||||
if os.path.normpath(output_dir) != os.path.normpath(assemblies_output_dir):
|
||||
rel_assemblies_output_dir = os.path.relpath(assemblies_output_dir, output_dir)
|
||||
env_mono.Append(CPPDEFINES={'GD_MONO_EDITOR_ASSEMBLIES_DIR': rel_assemblies_output_dir})
|
||||
mono_sln_builder = Builder(action=mono_build_solution)
|
||||
env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
|
||||
env_mono.MonoBuildSolution(
|
||||
os.path.join(editor_tools_dir, 'GodotSharpTools.dll'),
|
||||
'editor/GodotSharpTools/GodotSharpTools.sln'
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ import sys
|
|||
import subprocess
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables
|
||||
from SCons.Script import BoolVariable, Dir, Environment, File, SCons, Variables
|
||||
|
||||
|
||||
monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py')
|
||||
|
@ -55,30 +55,20 @@ def copy_file(src_dir, dst_dir, name):
|
|||
copyfile(src_path, dst_path)
|
||||
|
||||
|
||||
def custom_path_is_dir_create(key, val, env):
|
||||
"""Validator to check if Path is a directory, creating it if it does not exist.
|
||||
Similar to PathIsDirCreate, except it uses SCons.Script.Dir() and
|
||||
SCons.Script.File() in order to support the '#' top level directory token.
|
||||
"""
|
||||
# Dir constructor will throw an error if the path points to a file
|
||||
fsDir = Dir(val)
|
||||
if not fsDir.exists:
|
||||
os.makedirs(fsDir.abspath)
|
||||
|
||||
|
||||
def configure(env):
|
||||
env.use_ptrcall = True
|
||||
env.add_module_version_string('mono')
|
||||
|
||||
envvars = Variables()
|
||||
envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
|
||||
envvars.Add(PathVariable('mono_assemblies_output_dir', 'Path to the assemblies output directory', '#bin', custom_path_is_dir_create))
|
||||
envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False))
|
||||
envvars.Update(env)
|
||||
|
||||
bits = env['bits']
|
||||
|
||||
tools_enabled = env['tools']
|
||||
mono_static = env['mono_static']
|
||||
assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath
|
||||
copy_mono_root = env['copy_mono_root']
|
||||
|
||||
mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
|
||||
|
||||
|
@ -151,8 +141,6 @@ def configure(env):
|
|||
raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
|
||||
|
||||
copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll')
|
||||
|
||||
copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
|
||||
else:
|
||||
sharedlib_ext = '.dylib' if sys.platform == 'darwin' else '.so'
|
||||
|
||||
|
@ -204,16 +192,14 @@ def configure(env):
|
|||
|
||||
if sys.platform == 'darwin':
|
||||
env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file])
|
||||
elif sys.platform == 'linux' or sys.platform == 'linux2':
|
||||
env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
|
||||
else:
|
||||
raise RuntimeError('mono-static: Not supported on this platform')
|
||||
env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
|
||||
else:
|
||||
env.Append(LIBS=[mono_lib])
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
env.Append(LIBS=['iconv', 'pthread'])
|
||||
elif sys.platform == 'linux' or sys.platform == 'linux2':
|
||||
else:
|
||||
env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
|
||||
|
||||
if not mono_static:
|
||||
|
@ -223,8 +209,6 @@ def configure(env):
|
|||
raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path)
|
||||
|
||||
copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
|
||||
|
||||
copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
|
||||
else:
|
||||
assert not mono_static
|
||||
|
||||
|
@ -238,7 +222,6 @@ def configure(env):
|
|||
|
||||
mono_lib_path = ''
|
||||
mono_so_name = ''
|
||||
mono_prefix = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
|
||||
|
||||
tmpenv = Environment()
|
||||
tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
|
||||
|
@ -255,16 +238,163 @@ def configure(env):
|
|||
raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
|
||||
|
||||
copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
|
||||
copy_file(os.path.join(mono_prefix, 'lib', 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
|
||||
|
||||
env.Append(LINKFLAGS='-rdynamic')
|
||||
|
||||
if not tools_enabled:
|
||||
if not mono_root:
|
||||
mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
|
||||
|
||||
make_template_dir(env, mono_root)
|
||||
|
||||
if copy_mono_root:
|
||||
if not mono_root:
|
||||
mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
|
||||
|
||||
if tools_enabled:
|
||||
copy_mono_root_files(env, mono_root)
|
||||
else:
|
||||
print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.")
|
||||
|
||||
|
||||
def make_template_dir(env, mono_root):
|
||||
from shutil import rmtree
|
||||
|
||||
platform = env['platform']
|
||||
target = env['target']
|
||||
|
||||
template_dir_name = ''
|
||||
|
||||
if platform == 'windows':
|
||||
template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
|
||||
elif platform == 'osx':
|
||||
template_dir_name = 'data.mono.%s.%s' % (platform, target)
|
||||
elif platform == 'x11':
|
||||
template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
|
||||
else:
|
||||
assert False
|
||||
|
||||
output_dir = Dir('#bin').abspath
|
||||
template_dir = os.path.join(output_dir, template_dir_name)
|
||||
|
||||
template_mono_root_dir = os.path.join(template_dir, 'Mono')
|
||||
|
||||
if os.path.isdir(template_mono_root_dir):
|
||||
rmtree(template_mono_root_dir) # Clean first
|
||||
|
||||
# Copy etc/mono/
|
||||
|
||||
template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
|
||||
copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
|
||||
|
||||
# Copy the required shared libraries
|
||||
|
||||
copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform'])
|
||||
|
||||
|
||||
def copy_mono_root_files(env, mono_root):
|
||||
from glob import glob
|
||||
from shutil import copy
|
||||
from shutil import rmtree
|
||||
|
||||
if not mono_root:
|
||||
raise RuntimeError('Mono installation directory not found')
|
||||
|
||||
output_dir = Dir('#bin').abspath
|
||||
editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono')
|
||||
|
||||
if os.path.isdir(editor_mono_root_dir):
|
||||
rmtree(editor_mono_root_dir) # Clean first
|
||||
|
||||
# Copy etc/mono/
|
||||
|
||||
editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono')
|
||||
copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform'])
|
||||
|
||||
# Copy the required shared libraries
|
||||
|
||||
copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform'])
|
||||
|
||||
# Copy framework assemblies
|
||||
|
||||
mono_framework_dir = os.path.join(mono_root, 'lib', 'mono', '4.5')
|
||||
mono_framework_facades_dir = os.path.join(mono_framework_dir, 'Facades')
|
||||
|
||||
editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5')
|
||||
editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades')
|
||||
|
||||
if not os.path.isdir(editor_mono_framework_dir):
|
||||
os.makedirs(editor_mono_framework_dir)
|
||||
if not os.path.isdir(editor_mono_framework_facades_dir):
|
||||
os.makedirs(editor_mono_framework_facades_dir)
|
||||
|
||||
for assembly in glob(os.path.join(mono_framework_dir, '*.dll')):
|
||||
copy(assembly, editor_mono_framework_dir)
|
||||
for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')):
|
||||
copy(assembly, editor_mono_framework_facades_dir)
|
||||
|
||||
|
||||
def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform):
|
||||
from distutils.dir_util import copy_tree
|
||||
from glob import glob
|
||||
from shutil import copy
|
||||
|
||||
if not os.path.isdir(target_mono_config_dir):
|
||||
os.makedirs(target_mono_config_dir)
|
||||
|
||||
mono_etc_dir = os.path.join(mono_root, 'etc', 'mono')
|
||||
if not os.path.isdir(mono_etc_dir):
|
||||
mono_etc_dir = ''
|
||||
etc_hint_dirs = []
|
||||
if platform != 'windows':
|
||||
etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono']
|
||||
if 'MONO_CFG_DIR' in os.environ:
|
||||
etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')]
|
||||
for etc_hint_dir in etc_hint_dirs:
|
||||
if os.path.isdir(etc_hint_dir):
|
||||
mono_etc_dir = etc_hint_dir
|
||||
break
|
||||
if not mono_etc_dir:
|
||||
raise RuntimeError('Mono installation etc directory not found')
|
||||
|
||||
copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0'))
|
||||
copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0'))
|
||||
copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5'))
|
||||
copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig'))
|
||||
|
||||
for file in glob(os.path.join(mono_etc_dir, '*')):
|
||||
if os.path.isfile(file):
|
||||
copy(file, target_mono_config_dir)
|
||||
|
||||
|
||||
def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform):
|
||||
from shutil import copy
|
||||
|
||||
if platform == 'windows':
|
||||
target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin')
|
||||
|
||||
if not os.path.isdir(target_mono_bin_dir):
|
||||
os.makedirs(target_mono_bin_dir)
|
||||
|
||||
copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll'))
|
||||
else:
|
||||
target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib')
|
||||
|
||||
if not os.path.isdir(target_mono_lib_dir):
|
||||
os.makedirs(target_mono_lib_dir)
|
||||
|
||||
if platform == 'osx':
|
||||
copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib'))
|
||||
elif platform == 'x11':
|
||||
copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so'))
|
||||
copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so'))
|
||||
|
||||
|
||||
def configure_for_mono_version(env, mono_version):
|
||||
if mono_version is None:
|
||||
raise RuntimeError('Mono JIT compiler version not found')
|
||||
print('Found Mono JIT compiler version: ' + str(mono_version))
|
||||
if mono_version >= LooseVersion("5.12.0"):
|
||||
if mono_version >= LooseVersion('5.12.0'):
|
||||
env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS'])
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace GodotSharpTools.Editor
|
||||
{
|
||||
public static class GodotSharpExport
|
||||
{
|
||||
public static void _ExportBegin(string[] features, bool debug, string path, int flags)
|
||||
{
|
||||
var featureSet = new HashSet<string>(features);
|
||||
|
||||
if (PlatformHasTemplateDir(featureSet))
|
||||
{
|
||||
string templateDirName = "data.mono";
|
||||
|
||||
if (featureSet.Contains("Windows"))
|
||||
{
|
||||
templateDirName += ".windows";
|
||||
templateDirName += featureSet.Contains("64") ? ".64" : ".32";
|
||||
}
|
||||
else if (featureSet.Contains("X11"))
|
||||
{
|
||||
templateDirName += ".x11";
|
||||
templateDirName += featureSet.Contains("64") ? ".64" : ".32";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Target platform not supported");
|
||||
}
|
||||
|
||||
templateDirName += debug ? ".debug" : ".release";
|
||||
|
||||
string templateDirPath = Path.Combine(GetTemplatesDir(), templateDirName);
|
||||
|
||||
if (!Directory.Exists(templateDirPath))
|
||||
throw new FileNotFoundException("Data template directory not found");
|
||||
|
||||
string outputDir = new FileInfo(path).Directory.FullName;
|
||||
|
||||
string outputDataDir = Path.Combine(outputDir, GetDataDirName());
|
||||
|
||||
Directory.Delete(outputDataDir, recursive: true); // Clean first
|
||||
Directory.CreateDirectory(outputDataDir);
|
||||
|
||||
foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1)));
|
||||
}
|
||||
|
||||
foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool PlatformHasTemplateDir(HashSet<string> featureSet)
|
||||
{
|
||||
// OSX export templates are contained in a zip, so we place
|
||||
// our custom template inside it and let Godot do the rest.
|
||||
return !featureSet.Contains("OSX");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
extern static string GetTemplatesDir();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
extern static string GetDataDirName();
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@
|
|||
<Compile Include="Project\ProjectUtils.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Utils\OS.cs" />
|
||||
<Compile Include="Editor\GodotSharpExport.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -51,7 +51,7 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi
|
|||
MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_unhandled_exception(exc);
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL_V(String());
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll
|
|||
MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_unhandled_exception(exc);
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL_V(String());
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve
|
|||
MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_unhandled_exception(exc);
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL_V(String());
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
|
|||
klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_unhandled_exception(exc);
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -249,6 +249,18 @@ bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_s
|
|||
|
||||
bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) {
|
||||
|
||||
// Create destination directory if needed
|
||||
if (!DirAccess::exists(p_dst_dir)) {
|
||||
DirAccess *da = DirAccess::create_for_path(p_dst_dir);
|
||||
Error err = da->make_dir_recursive(p_dst_dir);
|
||||
memdelete(da);
|
||||
|
||||
if (err != OK) {
|
||||
show_build_error_dialog("Failed to create destination directory for the API assemblies. Error: " + itos(err));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String assembly_file = p_assembly_name + ".dll";
|
||||
String assembly_src = p_src_dir.plus_file(assembly_file);
|
||||
String assembly_dst = p_dst_dir.plus_file(assembly_file);
|
||||
|
@ -296,9 +308,9 @@ bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
|
|||
String api_name = p_api_type == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
|
||||
String api_build_config = "Release";
|
||||
|
||||
EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4);
|
||||
EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 3);
|
||||
|
||||
pr.step("Generating " + api_name + " solution");
|
||||
pr.step("Generating " + api_name + " solution", 0);
|
||||
|
||||
String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
|
||||
.plus_file(_api_folder_name(APIAssembly::API_CORE))
|
||||
|
@ -336,34 +348,19 @@ bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
|
|||
}
|
||||
}
|
||||
|
||||
pr.step("Building " + api_name + " solution");
|
||||
pr.step("Building " + api_name + " solution", 1);
|
||||
|
||||
if (!GodotSharpBuilds::build_api_sln(api_name, api_sln_dir, api_build_config))
|
||||
return false;
|
||||
|
||||
pr.step("Copying " + api_name + " assembly");
|
||||
|
||||
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
|
||||
|
||||
// Create assemblies directory if needed
|
||||
if (!DirAccess::exists(res_assemblies_dir)) {
|
||||
DirAccess *da = DirAccess::create_for_path(res_assemblies_dir);
|
||||
Error err = da->make_dir_recursive(res_assemblies_dir);
|
||||
memdelete(da);
|
||||
|
||||
if (err != OK) {
|
||||
show_build_error_dialog("Failed to create assemblies directory. Error: " + itos(err));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pr.step("Copying " + api_name + " assembly", 2);
|
||||
|
||||
// Copy the built assembly to the assemblies directory
|
||||
String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config);
|
||||
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
|
||||
if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type))
|
||||
return false;
|
||||
|
||||
pr.step("Done");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -372,15 +369,39 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
|
|||
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
|
||||
return true; // No solution to build
|
||||
|
||||
if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
|
||||
return false;
|
||||
String editor_prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir();
|
||||
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
|
||||
|
||||
if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
|
||||
return false;
|
||||
if (FileAccess::exists(editor_prebuilt_api_dir.plus_file(API_ASSEMBLY_NAME ".dll"))) {
|
||||
EditorProgress pr("mono_copy_prebuilt_api_assemblies",
|
||||
"Copying prebuilt " API_ASSEMBLY_NAME " assemblies...", 1);
|
||||
pr.step("Copying " API_ASSEMBLY_NAME " assembly", 0);
|
||||
|
||||
EditorProgress pr("mono_project_debug_build", "Building project solution...", 2);
|
||||
if (!GodotSharpBuilds::copy_api_assembly(editor_prebuilt_api_dir, res_assemblies_dir,
|
||||
API_ASSEMBLY_NAME, APIAssembly::API_CORE)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
|
||||
return false;
|
||||
}
|
||||
|
||||
pr.step("Building project solution");
|
||||
if (DirAccess::exists(editor_prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"))) {
|
||||
EditorProgress pr("mono_copy_prebuilt_api_assemblies",
|
||||
"Copying prebuilt " EDITOR_API_ASSEMBLY_NAME " assemblies...", 1);
|
||||
pr.step("Copying " EDITOR_API_ASSEMBLY_NAME " assembly", 0);
|
||||
|
||||
if (!GodotSharpBuilds::copy_api_assembly(editor_prebuilt_api_dir, res_assemblies_dir,
|
||||
EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorProgress pr("mono_project_debug_build", "Building project solution...", 1);
|
||||
pr.step("Building project solution", 0);
|
||||
|
||||
MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config);
|
||||
if (!GodotSharpBuilds::get_singleton()->build(build_info)) {
|
||||
|
@ -388,8 +409,6 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
|
|||
return false;
|
||||
}
|
||||
|
||||
pr.step("Done");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -197,6 +197,7 @@ void GodotSharpEditor::register_internal_calls() {
|
|||
mono_add_internal_call("GodotSharpTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName);
|
||||
|
||||
GodotSharpBuilds::register_internal_calls();
|
||||
GodotSharpExport::register_internal_calls();
|
||||
}
|
||||
|
||||
void GodotSharpEditor::show_error_dialog(const String &p_message, const String &p_title) {
|
||||
|
|
|
@ -30,12 +30,37 @@
|
|||
|
||||
#include "godotsharp_export.h"
|
||||
|
||||
#include "core/version.h"
|
||||
|
||||
#include "../csharp_script.h"
|
||||
#include "../godotsharp_defs.h"
|
||||
#include "../godotsharp_dirs.h"
|
||||
#include "../mono_gd/gd_mono_class.h"
|
||||
#include "../mono_gd/gd_mono_marshal.h"
|
||||
#include "godotsharp_builds.h"
|
||||
|
||||
void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) {
|
||||
static MonoString *godot_icall_GodotSharpExport_GetTemplatesDir() {
|
||||
String current_version = VERSION_FULL_CONFIG;
|
||||
String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(current_version);
|
||||
return GDMonoMarshal::mono_string_from_godot(ProjectSettings::get_singleton()->globalize_path(templates_dir));
|
||||
}
|
||||
|
||||
static MonoString *godot_icall_GodotSharpExport_GetDataDirName() {
|
||||
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
||||
return GDMonoMarshal::mono_string_from_godot("data_" + appname_safe);
|
||||
}
|
||||
|
||||
void GodotSharpExport::register_internal_calls() {
|
||||
static bool registered = false;
|
||||
ERR_FAIL_COND(registered);
|
||||
registered = true;
|
||||
|
||||
mono_add_internal_call("GodotSharpTools.Editor.GodotSharpExport::GetTemplatesDir", (void *)godot_icall_GodotSharpExport_GetTemplatesDir);
|
||||
mono_add_internal_call("GodotSharpTools.Editor.GodotSharpExport::GetDataDirName", (void *)godot_icall_GodotSharpExport_GetDataDirName);
|
||||
}
|
||||
|
||||
void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &) {
|
||||
|
||||
if (p_type != CSharpLanguage::get_singleton()->get_type())
|
||||
return;
|
||||
|
@ -56,59 +81,78 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
|
|||
// TODO right now there is no way to stop the export process with an error
|
||||
|
||||
ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
|
||||
ERR_FAIL_NULL(GDMono::get_singleton()->get_tools_domain());
|
||||
ERR_FAIL_NULL(TOOLS_DOMAIN);
|
||||
ERR_FAIL_NULL(GDMono::get_singleton()->get_editor_tools_assembly());
|
||||
|
||||
String build_config = p_debug ? "Debug" : "Release";
|
||||
|
||||
ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config));
|
||||
|
||||
// Add API assemblies
|
||||
// Add dependency assemblies
|
||||
|
||||
String core_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll");
|
||||
ERR_FAIL_COND(!_add_assembly(core_api_dll_path, core_api_dll_path));
|
||||
|
||||
String editor_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
ERR_FAIL_COND(!_add_assembly(editor_api_dll_path, editor_api_dll_path));
|
||||
|
||||
// Add project assembly
|
||||
Map<String, String> dependencies;
|
||||
|
||||
String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
if (project_dll_name.empty()) {
|
||||
project_dll_name = "UnnamedProject";
|
||||
}
|
||||
|
||||
String project_dll_src_path = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config).plus_file(project_dll_name + ".dll");
|
||||
String project_dll_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(project_dll_name + ".dll");
|
||||
ERR_FAIL_COND(!_add_assembly(project_dll_src_path, project_dll_dst_path));
|
||||
String project_dll_src_dir = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config);
|
||||
String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name + ".dll");
|
||||
dependencies.insert(project_dll_name, project_dll_src_path);
|
||||
|
||||
// Add dependencies
|
||||
{
|
||||
MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
|
||||
ERR_FAIL_NULL(export_domain);
|
||||
_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
|
||||
|
||||
MonoDomain *prev_domain = mono_domain_get();
|
||||
MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
|
||||
_GDMONO_SCOPE_DOMAIN_(export_domain);
|
||||
|
||||
ERR_FAIL_COND(!export_domain);
|
||||
ERR_FAIL_COND(!mono_domain_set(export_domain, false));
|
||||
GDMonoAssembly *scripts_assembly = NULL;
|
||||
bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name,
|
||||
project_dll_src_dir, &scripts_assembly, /* refonly: */ true);
|
||||
|
||||
Map<String, String> dependencies;
|
||||
dependencies.insert("mscorlib", GDMono::get_singleton()->get_corlib_assembly()->get_path());
|
||||
ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name);
|
||||
ERR_FAIL_COND(!load_success);
|
||||
|
||||
GDMonoAssembly *scripts_assembly = GDMonoAssembly::load_from(project_dll_name, project_dll_src_path, /* refonly: */ true);
|
||||
|
||||
ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name);
|
||||
ERR_FAIL_COND(!scripts_assembly);
|
||||
|
||||
Error depend_error = _get_assembly_dependencies(scripts_assembly, dependencies);
|
||||
|
||||
GDMono::get_singleton()->finalize_and_unload_domain(export_domain);
|
||||
mono_domain_set(prev_domain, false);
|
||||
|
||||
ERR_FAIL_COND(depend_error != OK);
|
||||
Vector<String> search_dirs;
|
||||
GDMonoAssembly::fill_search_dirs(search_dirs);
|
||||
Error depend_error = _get_assembly_dependencies(scripts_assembly, search_dirs, dependencies);
|
||||
ERR_FAIL_COND(depend_error != OK);
|
||||
}
|
||||
|
||||
for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) {
|
||||
String depend_src_path = E->value();
|
||||
String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file());
|
||||
ERR_FAIL_COND(!_add_assembly(depend_src_path, depend_dst_path));
|
||||
}
|
||||
|
||||
// Mono specific export template extras (data dir)
|
||||
|
||||
GDMonoClass *export_class = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Editor", "GodotSharpExport");
|
||||
ERR_FAIL_NULL(export_class);
|
||||
GDMonoMethod *export_begin_method = export_class->get_method("_ExportBegin", 4);
|
||||
ERR_FAIL_NULL(export_begin_method);
|
||||
|
||||
MonoArray *features = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_features.size());
|
||||
int i = 0;
|
||||
for (const Set<String>::Element *E = p_features.front(); E; E = E->next()) {
|
||||
MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get());
|
||||
mono_array_set(features, MonoString *, i, boxed);
|
||||
i++;
|
||||
}
|
||||
|
||||
MonoBoolean debug = p_debug;
|
||||
MonoString *path = GDMonoMarshal::mono_string_from_godot(p_path);
|
||||
uint32_t flags = p_flags;
|
||||
void *args[4] = { features, &debug, path, &flags };
|
||||
MonoException *exc = NULL;
|
||||
export_begin_method->invoke_raw(NULL, args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_dst_path) {
|
||||
|
@ -125,7 +169,7 @@ bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_d
|
|||
return true;
|
||||
}
|
||||
|
||||
Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies) {
|
||||
Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies) {
|
||||
|
||||
MonoImage *image = p_assembly->get_image();
|
||||
|
||||
|
@ -134,18 +178,48 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, M
|
|||
mono_assembly_get_assemblyref(image, i, ref_aname);
|
||||
String ref_name = mono_assembly_name_get_name(ref_aname);
|
||||
|
||||
if (ref_name == "mscorlib" || r_dependencies.find(ref_name))
|
||||
if (r_dependencies.find(ref_name))
|
||||
continue;
|
||||
|
||||
GDMonoAssembly *ref_assembly = NULL;
|
||||
if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true)) {
|
||||
ERR_EXPLAIN("Cannot load refonly assembly: " + ref_name);
|
||||
String path;
|
||||
bool has_extension = ref_name.ends_with(".dll") || ref_name.ends_with(".exe");
|
||||
|
||||
for (int i = 0; i < p_search_dirs.size(); i++) {
|
||||
const String &search_dir = p_search_dirs[i];
|
||||
|
||||
if (has_extension) {
|
||||
path = search_dir.plus_file(ref_name);
|
||||
if (FileAccess::exists(path)) {
|
||||
GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), search_dir, ref_aname, &ref_assembly, true);
|
||||
if (ref_assembly != NULL)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
path = search_dir.plus_file(ref_name + ".dll");
|
||||
if (FileAccess::exists(path)) {
|
||||
GDMono::get_singleton()->load_assembly_from(ref_name, search_dir, ref_aname, &ref_assembly, true);
|
||||
if (ref_assembly != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
path = search_dir.plus_file(ref_name + ".exe");
|
||||
if (FileAccess::exists(path)) {
|
||||
GDMono::get_singleton()->load_assembly_from(ref_name, search_dir, ref_aname, &ref_assembly, true);
|
||||
if (ref_assembly != NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ref_assembly) {
|
||||
ERR_EXPLAIN("Cannot load assembly (refonly): " + ref_name);
|
||||
ERR_FAIL_V(ERR_CANT_RESOLVE);
|
||||
}
|
||||
|
||||
r_dependencies.insert(ref_name, ref_assembly->get_path());
|
||||
|
||||
Error err = _get_assembly_dependencies(ref_assembly, r_dependencies);
|
||||
Error err = _get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies);
|
||||
if (err != OK)
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -43,13 +43,15 @@ class GodotSharpExport : public EditorExportPlugin {
|
|||
|
||||
bool _add_assembly(const String &p_src_path, const String &p_dst_path);
|
||||
|
||||
Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies);
|
||||
Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies);
|
||||
|
||||
protected:
|
||||
virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features);
|
||||
virtual void _export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags);
|
||||
|
||||
public:
|
||||
static void register_internal_calls();
|
||||
|
||||
GodotSharpExport();
|
||||
~GodotSharpExport();
|
||||
};
|
||||
|
|
|
@ -30,11 +30,11 @@
|
|||
|
||||
#include "godotsharp_dirs.h"
|
||||
|
||||
#include "core/os/dir_access.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "core/os/dir_access.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/version.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#endif
|
||||
|
@ -95,10 +95,18 @@ public:
|
|||
#ifdef TOOLS_ENABLED
|
||||
String mono_solutions_dir;
|
||||
String build_logs_dir;
|
||||
|
||||
String sln_filepath;
|
||||
String csproj_filepath;
|
||||
|
||||
String data_mono_bin_dir;
|
||||
String data_editor_tools_dir;
|
||||
String data_editor_prebuilt_api_dir;
|
||||
#endif
|
||||
|
||||
String data_mono_etc_dir;
|
||||
String data_mono_lib_dir;
|
||||
|
||||
private:
|
||||
_GodotSharpDirs() {
|
||||
res_data_dir = "res://.mono";
|
||||
|
@ -123,10 +131,60 @@ private:
|
|||
name = "UnnamedProject";
|
||||
}
|
||||
|
||||
String base_path = String("res://") + name;
|
||||
String base_path = ProjectSettings::get_singleton()->globalize_path("res://");
|
||||
|
||||
sln_filepath = base_path.plus_file(name + ".sln");
|
||||
csproj_filepath = base_path.plus_file(name + ".csproj");
|
||||
#endif
|
||||
|
||||
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
String data_dir_root = exe_dir.plus_file("GodotSharp");
|
||||
data_editor_tools_dir = data_dir_root.plus_file("Tools");
|
||||
data_editor_prebuilt_api_dir = data_dir_root.plus_file("Api");
|
||||
|
||||
String data_mono_root_dir = data_dir_root.plus_file("Mono");
|
||||
data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
|
||||
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
|
||||
data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
|
||||
|
||||
#ifdef OSX_ENABLED
|
||||
if (!DirAccess::exists(data_editor_tools_dir)) {
|
||||
data_editor_tools_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Tools");
|
||||
}
|
||||
|
||||
if (!DirAccess::exists(data_editor_prebuilt_api_dir)) {
|
||||
data_editor_prebuilt_api_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Api");
|
||||
}
|
||||
|
||||
if (!DirAccess::exists(data_mono_root_dir)) {
|
||||
data_mono_bin_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/bin");
|
||||
data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
|
||||
data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib");
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
String appname = OS::get_singleton()->get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name"));
|
||||
String data_dir_root = exe_dir.plus_file("data_" + appname);
|
||||
if (!DirAccess::exists(data_dir_root)) {
|
||||
data_dir_root = exe_dir.plus_file("data_Godot");
|
||||
}
|
||||
|
||||
String data_mono_root_dir = data_dir_root.plus_file("Mono");
|
||||
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
|
||||
data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
|
||||
|
||||
#ifdef OSX_ENABLED
|
||||
if (!DirAccess::exists(data_mono_root_dir)) {
|
||||
data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
|
||||
data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib");
|
||||
}
|
||||
#endif
|
||||
|
||||
sln_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".sln");
|
||||
csproj_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".csproj");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -192,5 +250,26 @@ String get_project_sln_path() {
|
|||
String get_project_csproj_path() {
|
||||
return _GodotSharpDirs::get_singleton().csproj_filepath;
|
||||
}
|
||||
|
||||
String get_data_mono_bin_dir() {
|
||||
return _GodotSharpDirs::get_singleton().data_mono_bin_dir;
|
||||
}
|
||||
|
||||
String get_data_editor_tools_dir() {
|
||||
return _GodotSharpDirs::get_singleton().data_editor_tools_dir;
|
||||
}
|
||||
|
||||
String get_data_editor_prebuilt_api_dir() {
|
||||
return _GodotSharpDirs::get_singleton().data_editor_prebuilt_api_dir;
|
||||
}
|
||||
#endif
|
||||
|
||||
String get_data_mono_etc_dir() {
|
||||
return _GodotSharpDirs::get_singleton().data_mono_etc_dir;
|
||||
}
|
||||
|
||||
String get_data_mono_lib_dir() {
|
||||
return _GodotSharpDirs::get_singleton().data_mono_lib_dir;
|
||||
}
|
||||
|
||||
} // namespace GodotSharpDirs
|
||||
|
|
|
@ -49,11 +49,18 @@ String get_mono_logs_dir();
|
|||
#ifdef TOOLS_ENABLED
|
||||
String get_mono_solutions_dir();
|
||||
String get_build_logs_dir();
|
||||
String get_custom_project_settings_dir();
|
||||
#endif
|
||||
|
||||
String get_project_sln_path();
|
||||
String get_project_csproj_path();
|
||||
|
||||
String get_data_mono_bin_dir();
|
||||
String get_data_editor_tools_dir();
|
||||
String get_data_editor_prebuilt_api_dir();
|
||||
#endif
|
||||
|
||||
String get_data_mono_etc_dir();
|
||||
String get_data_mono_lib_dir();
|
||||
|
||||
} // namespace GodotSharpDirs
|
||||
|
||||
#endif // GODOTSHARP_DIRS_H
|
||||
|
|
|
@ -162,49 +162,62 @@ void GDMono::initialize() {
|
|||
mono_trace_set_printerr_handler(gdmono_MonoPrintCallback);
|
||||
#endif
|
||||
|
||||
String assembly_rootdir;
|
||||
String config_dir;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef WINDOWS_ENABLED
|
||||
mono_reg_info = MonoRegUtils::find_mono();
|
||||
|
||||
CharString assembly_dir;
|
||||
CharString config_dir;
|
||||
|
||||
if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
|
||||
assembly_dir = mono_reg_info.assembly_dir.utf8();
|
||||
assembly_rootdir = mono_reg_info.assembly_dir;
|
||||
}
|
||||
|
||||
if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
|
||||
config_dir = mono_reg_info.config_dir.utf8();
|
||||
config_dir = mono_reg_info.config_dir;
|
||||
}
|
||||
|
||||
mono_set_dirs(assembly_dir.length() ? assembly_dir.get_data() : NULL,
|
||||
config_dir.length() ? config_dir.get_data() : NULL);
|
||||
#elif OSX_ENABLED
|
||||
mono_set_dirs(NULL, NULL);
|
||||
const char *c_assembly_rootdir = mono_assembly_getrootdir();
|
||||
const char *c_config_dir = mono_get_config_dir();
|
||||
|
||||
{
|
||||
const char *assembly_rootdir = mono_assembly_getrootdir();
|
||||
const char *config_dir = mono_get_config_dir();
|
||||
if (!c_assembly_rootdir || !c_config_dir || !DirAccess::exists(c_assembly_rootdir) || !DirAccess::exists(c_config_dir)) {
|
||||
Vector<const char *> locations;
|
||||
locations.push_back("/Library/Frameworks/Mono.framework/Versions/Current/");
|
||||
locations.push_back("/usr/local/var/homebrew/linked/mono/");
|
||||
|
||||
if (!assembly_rootdir || !config_dir || !DirAccess::exists(assembly_rootdir) || !DirAccess::exists(config_dir)) {
|
||||
Vector<const char *> locations;
|
||||
locations.push_back("/Library/Frameworks/Mono.framework/Versions/Current/");
|
||||
locations.push_back("/usr/local/var/homebrew/linked/mono/");
|
||||
for (int i = 0; i < locations.size(); i++) {
|
||||
String hint_assembly_rootdir = path_join(locations[i], "lib");
|
||||
String hint_mscorlib_path = path_join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll");
|
||||
String hint_config_dir = path_join(locations[i], "etc");
|
||||
|
||||
for (int i = 0; i < locations.size(); i++) {
|
||||
String hint_assembly_rootdir = path_join(locations[i], "lib");
|
||||
String hint_mscorlib_path = path_join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll");
|
||||
String hint_config_dir = path_join(locations[i], "etc");
|
||||
|
||||
if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) {
|
||||
mono_set_dirs(hint_assembly_rootdir.utf8().get_data(), hint_config_dir.utf8().get_data());
|
||||
break;
|
||||
}
|
||||
if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) {
|
||||
need_set_mono_dirs = false;
|
||||
assembly_rootdir = hint_assembly_rootdir;
|
||||
config_dir = hint_config_dir;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
mono_set_dirs(NULL, NULL);
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir();
|
||||
String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (DirAccess::exists(bundled_assembly_rootdir) && DirAccess::exists(bundled_config_dir)) {
|
||||
assembly_rootdir = bundled_assembly_rootdir;
|
||||
config_dir = bundled_config_dir;
|
||||
}
|
||||
#else
|
||||
// These are always the directories in export templates
|
||||
assembly_rootdir = bundled_assembly_rootdir;
|
||||
config_dir = bundled_config_dir;
|
||||
#endif
|
||||
|
||||
// Leak if we call mono_set_dirs more than once
|
||||
mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : NULL,
|
||||
config_dir.length() ? config_dir.utf8().get_data() : NULL);
|
||||
|
||||
GDMonoAssembly::initialize();
|
||||
|
||||
|
@ -262,8 +275,11 @@ void GDMono::initialize() {
|
|||
// Everything is fine with the api assemblies, load the project assembly
|
||||
_load_project_assembly();
|
||||
} else {
|
||||
if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)) ||
|
||||
(editor_api_assembly && editor_api_assembly_out_of_sync)) {
|
||||
if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated))
|
||||
#ifdef TOOLS_ENABLED
|
||||
|| (editor_api_assembly && editor_api_assembly_out_of_sync)
|
||||
#endif
|
||||
) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
// The assembly was successfully loaded, but the full api could not be cached.
|
||||
// This is most likely an outdated assembly loaded because of an invalid version in the
|
||||
|
@ -362,24 +378,34 @@ GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) {
|
|||
|
||||
bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
|
||||
|
||||
return load_assembly_from(p_name, String(), r_assembly, p_refonly);
|
||||
}
|
||||
|
||||
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
|
||||
|
||||
return load_assembly_from(p_name, String(), p_aname, r_assembly, p_refonly);
|
||||
}
|
||||
|
||||
bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, GDMonoAssembly **r_assembly, bool p_refonly) {
|
||||
|
||||
CRASH_COND(!r_assembly);
|
||||
|
||||
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
|
||||
bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
|
||||
bool result = load_assembly_from(p_name, p_basedir, aname, r_assembly, p_refonly);
|
||||
mono_assembly_name_free(aname);
|
||||
mono_free(aname);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
|
||||
bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
|
||||
|
||||
CRASH_COND(!r_assembly);
|
||||
|
||||
print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
|
||||
|
||||
MonoImageOpenStatus status = MONO_IMAGE_OK;
|
||||
MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly);
|
||||
MonoAssembly *assembly = mono_assembly_load_full(p_aname, p_basedir.length() ? p_basedir.utf8().get_data() : NULL, &status, p_refonly);
|
||||
|
||||
if (!assembly)
|
||||
return false;
|
||||
|
@ -480,12 +506,10 @@ bool GDMono::_load_editor_api_assembly() {
|
|||
if (editor_api_assembly)
|
||||
return true;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
|
||||
print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
|
||||
|
||||
|
@ -772,7 +796,7 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
|
|||
|
||||
print_verbose("Mono: Unloading domain `" + domain_name + "`...");
|
||||
|
||||
if (mono_domain_get() != root_domain)
|
||||
if (mono_domain_get() == p_domain)
|
||||
mono_domain_set(root_domain, true);
|
||||
|
||||
mono_gc_collect(mono_gc_max_generation());
|
||||
|
|
|
@ -142,7 +142,7 @@ class GDMono {
|
|||
|
||||
GDMonoLog *gdmono_log;
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
|
||||
MonoRegInfo mono_reg_info;
|
||||
#endif
|
||||
|
||||
|
@ -197,6 +197,9 @@ public:
|
|||
|
||||
bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
||||
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
||||
bool load_assembly_from(const String &p_name, const String &p_basedir, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
||||
bool load_assembly_from(const String &p_name, const String &p_basedir, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
||||
|
||||
Error finalize_and_unload_domain(MonoDomain *p_domain);
|
||||
|
||||
void initialize();
|
||||
|
@ -205,12 +208,14 @@ public:
|
|||
~GDMono();
|
||||
};
|
||||
|
||||
class GDMonoScopeDomain {
|
||||
namespace gdmono {
|
||||
|
||||
class ScopeDomain {
|
||||
|
||||
MonoDomain *prev_domain;
|
||||
|
||||
public:
|
||||
GDMonoScopeDomain(MonoDomain *p_domain) {
|
||||
ScopeDomain(MonoDomain *p_domain) {
|
||||
MonoDomain *prev_domain = mono_domain_get();
|
||||
if (prev_domain != p_domain) {
|
||||
this->prev_domain = prev_domain;
|
||||
|
@ -220,16 +225,36 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
~GDMonoScopeDomain() {
|
||||
~ScopeDomain() {
|
||||
if (prev_domain)
|
||||
mono_domain_set(prev_domain, false);
|
||||
}
|
||||
};
|
||||
|
||||
#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \
|
||||
GDMonoScopeDomain __gdmono__scope__domain__(m_mono_domain); \
|
||||
class ScopeExitDomainUnload {
|
||||
MonoDomain *domain;
|
||||
|
||||
public:
|
||||
ScopeExitDomainUnload(MonoDomain *p_domain) :
|
||||
domain(p_domain) {
|
||||
}
|
||||
|
||||
~ScopeExitDomainUnload() {
|
||||
if (domain)
|
||||
GDMono::get_singleton()->finalize_and_unload_domain(domain);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gdmono
|
||||
|
||||
#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \
|
||||
gdmono::ScopeDomain __gdmono__scope__domain__(m_mono_domain); \
|
||||
(void)__gdmono__scope__domain__;
|
||||
|
||||
#define _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(m_mono_domain) \
|
||||
gdmono::ScopeExitDomainUnload __gdmono__scope__exit__domain__unload__(m_mono_domain); \
|
||||
(void)__gdmono__scope__exit__domain__unload__;
|
||||
|
||||
class _GodotSharp : public Object {
|
||||
GDCLASS(_GodotSharp, Object)
|
||||
|
||||
|
|
|
@ -46,6 +46,29 @@ bool GDMonoAssembly::in_preload = false;
|
|||
|
||||
Vector<String> GDMonoAssembly::search_dirs;
|
||||
|
||||
void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config) {
|
||||
|
||||
const char *rootdir = mono_assembly_getrootdir();
|
||||
if (rootdir) {
|
||||
String framework_dir = String(rootdir).plus_file("mono").plus_file("4.5");
|
||||
r_search_dirs.push_back(framework_dir);
|
||||
r_search_dirs.push_back(framework_dir.plus_file("Facades"));
|
||||
}
|
||||
|
||||
if (p_custom_config.length()) {
|
||||
r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(p_custom_config));
|
||||
} else {
|
||||
r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
|
||||
}
|
||||
|
||||
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
|
||||
r_search_dirs.push_back(OS::get_singleton()->get_resource_dir());
|
||||
r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
|
||||
#ifdef TOOLS_ENABLED
|
||||
r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir());
|
||||
#endif
|
||||
}
|
||||
|
||||
void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, void *user_data) {
|
||||
|
||||
if (no_search)
|
||||
|
@ -93,35 +116,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
|
|||
|
||||
no_search = true; // Avoid the recursion madness
|
||||
|
||||
String path;
|
||||
GDMonoAssembly *res = NULL;
|
||||
|
||||
for (int i = 0; i < search_dirs.size(); i++) {
|
||||
const String &search_dir = search_dirs[i];
|
||||
|
||||
if (has_extension) {
|
||||
path = search_dir.plus_file(name);
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(name.get_basename(), path, refonly);
|
||||
if (res != NULL)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
path = search_dir.plus_file(name + ".dll");
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(name, path, refonly);
|
||||
if (res != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
path = search_dir.plus_file(name + ".exe");
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(name, path, refonly);
|
||||
if (res != NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
GDMonoAssembly *res = _load_assembly_search(name, search_dirs, refonly);
|
||||
|
||||
no_search = false;
|
||||
|
||||
|
@ -130,31 +125,12 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
|
|||
|
||||
static _THREAD_LOCAL_(MonoImage *) image_corlib_loading = NULL;
|
||||
|
||||
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly) {
|
||||
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, void *user_data, bool refonly) {
|
||||
|
||||
(void)user_data; // UNUSED
|
||||
|
||||
if (search_dirs.empty()) {
|
||||
search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
|
||||
search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
|
||||
search_dirs.push_back(OS::get_singleton()->get_resource_dir());
|
||||
search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
|
||||
#ifdef GD_MONO_EDITOR_ASSEMBLIES_DIR
|
||||
search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir().plus_file(_MKSTR(GD_MONO_EDITOR_ASSEMBLIES_DIR)).simplify_path());
|
||||
#endif
|
||||
|
||||
const char *rootdir = mono_assembly_getrootdir();
|
||||
if (rootdir) {
|
||||
search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5"));
|
||||
search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5").plus_file("Facades"));
|
||||
}
|
||||
|
||||
if (assemblies_path) {
|
||||
while (*assemblies_path) {
|
||||
search_dirs.push_back(*assemblies_path);
|
||||
++assemblies_path;
|
||||
}
|
||||
}
|
||||
fill_search_dirs(search_dirs);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -188,27 +164,7 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
|
|||
if (stored_assembly)
|
||||
return (*stored_assembly)->get_assembly();
|
||||
|
||||
String path;
|
||||
|
||||
for (int i = 0; i < search_dirs.size(); i++) {
|
||||
const String &search_dir = search_dirs[i];
|
||||
|
||||
if (has_extension) {
|
||||
path = search_dir.plus_file(name);
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(name.get_basename(), path, refonly);
|
||||
if (res != NULL)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
path = search_dir.plus_file(name + ".dll");
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(name, path, refonly);
|
||||
if (res != NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
res = _load_assembly_search("mscorlib.dll", search_dirs, refonly);
|
||||
}
|
||||
|
||||
no_search = false;
|
||||
|
@ -217,6 +173,43 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
|
|||
return res ? res->get_assembly() : NULL;
|
||||
}
|
||||
|
||||
GDMonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly) {
|
||||
|
||||
GDMonoAssembly *res = NULL;
|
||||
String path;
|
||||
|
||||
bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
|
||||
|
||||
for (int i = 0; i < p_search_dirs.size(); i++) {
|
||||
const String &search_dir = p_search_dirs[i];
|
||||
|
||||
if (has_extension) {
|
||||
path = search_dir.plus_file(p_name);
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(p_name.get_basename(), path, p_refonly);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
path = search_dir.plus_file(p_name + ".dll");
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(p_name, path, p_refonly);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
}
|
||||
|
||||
path = search_dir.plus_file(p_name + ".exe");
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(p_name, path, p_refonly);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) {
|
||||
|
||||
GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
|
||||
|
@ -464,19 +457,6 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
|
|||
return match;
|
||||
}
|
||||
|
||||
GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
|
||||
|
||||
GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
|
||||
if (loaded_asm)
|
||||
return *loaded_asm;
|
||||
|
||||
no_search = true;
|
||||
GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly);
|
||||
no_search = false;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) {
|
||||
|
||||
loaded = false;
|
||||
|
|
|
@ -102,6 +102,7 @@ class GDMonoAssembly {
|
|||
static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
|
||||
|
||||
static GDMonoAssembly *_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly);
|
||||
static GDMonoAssembly *_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly);
|
||||
static void _wrap_mono_assembly(MonoAssembly *assembly);
|
||||
|
||||
friend class GDMono;
|
||||
|
@ -125,7 +126,7 @@ public:
|
|||
|
||||
GDMonoClass *get_object_derived_class(const StringName &p_class);
|
||||
|
||||
static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
|
||||
static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String());
|
||||
|
||||
GDMonoAssembly(const String &p_name, const String &p_path = String());
|
||||
~GDMonoAssembly();
|
||||
|
|
Loading…
Reference in a new issue