Merge pull request #30282 from neikeq/editor_in_cs_equals_win
Re-write mono module editor code in C#
This commit is contained in:
commit
6e9cb44004
97 changed files with 5708 additions and 4125 deletions
|
@ -83,6 +83,10 @@ String ProjectSettings::localize_path(const String &p_path) const {
|
|||
// `plus_file("")` is an easy way to ensure we have a trailing '/'.
|
||||
const String res_path = resource_path.plus_file("");
|
||||
|
||||
// DirAccess::get_current_dir() is not guaranteed to return a path that with a trailing '/',
|
||||
// so we must make sure we have it as well in order to compare with 'res_path'.
|
||||
cwd = cwd.plus_file("");
|
||||
|
||||
if (!cwd.begins_with(res_path)) {
|
||||
return p_path;
|
||||
};
|
||||
|
|
|
@ -70,7 +70,7 @@ bool Reference::reference() {
|
|||
if (get_script_instance()) {
|
||||
get_script_instance()->refcount_incremented();
|
||||
}
|
||||
if (instance_binding_count > 0) {
|
||||
if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) {
|
||||
for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
|
||||
if (_script_instance_bindings[i]) {
|
||||
ScriptServer::get_language(i)->refcount_incremented_instance_binding(this);
|
||||
|
@ -91,7 +91,7 @@ bool Reference::unreference() {
|
|||
bool script_ret = get_script_instance()->refcount_decremented();
|
||||
die = die && script_ret;
|
||||
}
|
||||
if (instance_binding_count > 0) {
|
||||
if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) {
|
||||
for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
|
||||
if (_script_instance_bindings[i]) {
|
||||
bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this);
|
||||
|
|
|
@ -220,7 +220,7 @@ EditorSelection *EditorInterface::get_selection() {
|
|||
return EditorNode::get_singleton()->get_editor_selection();
|
||||
}
|
||||
|
||||
EditorSettings *EditorInterface::get_editor_settings() {
|
||||
Ref<EditorSettings> EditorInterface::get_editor_settings() {
|
||||
return EditorSettings::get_singleton();
|
||||
}
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ public:
|
|||
|
||||
EditorSelection *get_selection();
|
||||
//EditorImportExport *get_import_export();
|
||||
EditorSettings *get_editor_settings();
|
||||
Ref<EditorSettings> get_editor_settings();
|
||||
EditorResourcePreview *get_resource_previewer();
|
||||
EditorFileSystem *get_resource_file_system();
|
||||
|
||||
|
|
|
@ -1579,6 +1579,8 @@ void EditorSettings::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_recent_dirs"), &EditorSettings::get_recent_dirs);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("settings_changed"));
|
||||
|
||||
BIND_CONSTANT(NOTIFICATION_EDITOR_SETTINGS_CHANGED);
|
||||
}
|
||||
|
||||
EditorSettings::EditorSettings() {
|
||||
|
|
|
@ -119,7 +119,7 @@ public:
|
|||
|
||||
void set_spatial_node(Spatial *p_node);
|
||||
Spatial *get_spatial_node() const { return spatial_node; }
|
||||
EditorSpatialGizmoPlugin *get_plugin() const { return gizmo_plugin; }
|
||||
Ref<EditorSpatialGizmoPlugin> get_plugin() const { return gizmo_plugin; }
|
||||
Vector3 get_handle_pos(int p_idx) const;
|
||||
bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum);
|
||||
bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false);
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import build_scripts.tls_configure as tls_configure
|
||||
import build_scripts.mono_configure as mono_configure
|
||||
|
||||
Import('env')
|
||||
Import('env_modules')
|
||||
|
||||
|
@ -26,27 +29,36 @@ if env_mono['mono_glue']:
|
|||
|
||||
import os.path
|
||||
if not os.path.isfile('glue/mono_glue.gen.cpp'):
|
||||
raise RuntimeError('Missing mono glue sources. Did you forget to generate them?')
|
||||
raise RuntimeError("Mono glue sources not found. Did you forget to run '--generate-mono-glue'?")
|
||||
|
||||
if env_mono['tools'] or env_mono['target'] != 'release':
|
||||
env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD'])
|
||||
|
||||
# Configure Thread Local Storage
|
||||
|
||||
import build_scripts.tls_configure as tls_configure
|
||||
|
||||
conf = Configure(env_mono)
|
||||
tls_configure.configure(conf)
|
||||
env_mono = conf.Finish()
|
||||
|
||||
# Configure Mono
|
||||
|
||||
import build_scripts.mono_configure as mono_configure
|
||||
|
||||
mono_configure.configure(env, env_mono)
|
||||
|
||||
# Build GodotSharpTools
|
||||
# Build Godot API solution
|
||||
|
||||
import build_scripts.godotsharptools_build as godotsharptools_build
|
||||
if env_mono['tools'] and env_mono['mono_glue']:
|
||||
import build_scripts.api_solution_build as api_solution_build
|
||||
api_solution_build.build(env_mono)
|
||||
|
||||
godotsharptools_build.build(env_mono)
|
||||
# Build GodotTools
|
||||
|
||||
if env_mono['tools']:
|
||||
import build_scripts.godot_tools_build as godot_tools_build
|
||||
if env_mono['mono_glue']:
|
||||
godot_tools_build.build(env_mono)
|
||||
else:
|
||||
# Building without the glue sources so the Godot API solution may be missing.
|
||||
# GodotTools depends on the Godot API solution. As such, we will only build
|
||||
# GodotTools.ProjectEditor which doesn't depend on the Godot API solution and
|
||||
# is required by the bindings generator in order to be able to generated it.
|
||||
godot_tools_build.build_project_editor_only(env_mono)
|
||||
|
|
0
modules/mono/__init__.py
Normal file
0
modules/mono/__init__.py
Normal file
66
modules/mono/build_scripts/api_solution_build.py
Normal file
66
modules/mono/build_scripts/api_solution_build.py
Normal file
|
@ -0,0 +1,66 @@
|
|||
# Build the Godot API solution
|
||||
|
||||
import os
|
||||
|
||||
from SCons.Script import Dir
|
||||
|
||||
|
||||
def build_api_solution(source, target, env):
|
||||
# source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
|
||||
|
||||
module_dir = env['module_dir']
|
||||
|
||||
solution_path = os.path.join(module_dir, 'glue/Managed/Generated/GodotSharp.sln')
|
||||
|
||||
if not os.path.isfile(solution_path):
|
||||
raise RuntimeError("Godot API solution not found. Did you forget to run '--generate-mono-glue'?")
|
||||
|
||||
build_config = env['solution_build_config']
|
||||
|
||||
extra_msbuild_args = ['/p:NoWarn=1591'] # Ignore missing documentation warnings
|
||||
|
||||
from .solution_builder import build_solution
|
||||
build_solution(env, solution_path, build_config, extra_msbuild_args=extra_msbuild_args)
|
||||
|
||||
# Copy targets
|
||||
|
||||
core_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, 'GodotSharp', 'bin', build_config))
|
||||
editor_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, 'GodotSharpEditor', 'bin', build_config))
|
||||
|
||||
dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
|
||||
|
||||
if not os.path.isdir(dst_dir):
|
||||
assert not os.path.isfile(dst_dir)
|
||||
os.makedirs(dst_dir)
|
||||
|
||||
def copy_target(target_path):
|
||||
from shutil import copy
|
||||
filename = os.path.basename(target_path)
|
||||
|
||||
src_path = os.path.join(core_src_dir, filename)
|
||||
if not os.path.isfile(src_path):
|
||||
src_path = os.path.join(editor_src_dir, filename)
|
||||
|
||||
copy(src_path, target_path)
|
||||
|
||||
for scons_target in target:
|
||||
copy_target(str(scons_target))
|
||||
|
||||
|
||||
def build(env_mono):
|
||||
assert env_mono['tools']
|
||||
|
||||
target_filenames = [
|
||||
'GodotSharp.dll', 'GodotSharp.pdb', 'GodotSharp.xml',
|
||||
'GodotSharpEditor.dll', 'GodotSharpEditor.pdb', 'GodotSharpEditor.xml'
|
||||
]
|
||||
|
||||
for build_config in ['Debug', 'Release']:
|
||||
output_dir = Dir('#bin').abspath
|
||||
editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', build_config)
|
||||
|
||||
targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames]
|
||||
|
||||
cmd = env_mono.CommandNoCache(targets, [], build_api_solution,
|
||||
module_dir=os.getcwd(), solution_build_config=build_config)
|
||||
env_mono.AlwaysBuild(cmd)
|
108
modules/mono/build_scripts/godot_tools_build.py
Normal file
108
modules/mono/build_scripts/godot_tools_build.py
Normal file
|
@ -0,0 +1,108 @@
|
|||
# Build GodotTools solution
|
||||
|
||||
import os
|
||||
|
||||
from SCons.Script import Dir
|
||||
|
||||
|
||||
def build_godot_tools(source, target, env):
|
||||
# source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
|
||||
|
||||
module_dir = env['module_dir']
|
||||
|
||||
solution_path = os.path.join(module_dir, 'editor/GodotTools/GodotTools.sln')
|
||||
build_config = 'Debug' if env['target'] == 'debug' else 'Release'
|
||||
|
||||
from . solution_builder import build_solution, nuget_restore
|
||||
nuget_restore(env, solution_path)
|
||||
build_solution(env, solution_path, build_config)
|
||||
|
||||
# Copy targets
|
||||
|
||||
solution_dir = os.path.abspath(os.path.join(solution_path, os.pardir))
|
||||
|
||||
src_dir = os.path.join(solution_dir, 'GodotTools', 'bin', build_config)
|
||||
dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
|
||||
|
||||
if not os.path.isdir(dst_dir):
|
||||
assert not os.path.isfile(dst_dir)
|
||||
os.makedirs(dst_dir)
|
||||
|
||||
def copy_target(target_path):
|
||||
from shutil import copy
|
||||
filename = os.path.basename(target_path)
|
||||
copy(os.path.join(src_dir, filename), target_path)
|
||||
|
||||
for scons_target in target:
|
||||
copy_target(str(scons_target))
|
||||
|
||||
|
||||
def build_godot_tools_project_editor(source, target, env):
|
||||
# source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
|
||||
|
||||
module_dir = env['module_dir']
|
||||
|
||||
project_name = 'GodotTools.ProjectEditor'
|
||||
|
||||
csproj_dir = os.path.join(module_dir, 'editor/GodotTools', project_name)
|
||||
csproj_path = os.path.join(csproj_dir, project_name + '.csproj')
|
||||
build_config = 'Debug' if env['target'] == 'debug' else 'Release'
|
||||
|
||||
from . solution_builder import build_solution, nuget_restore
|
||||
|
||||
# Make sure to restore NuGet packages in the project directory for the project to find it
|
||||
nuget_restore(env, os.path.join(csproj_dir, 'packages.config'), '-PackagesDirectory',
|
||||
os.path.join(csproj_dir, 'packages'))
|
||||
|
||||
build_solution(env, csproj_path, build_config)
|
||||
|
||||
# Copy targets
|
||||
|
||||
src_dir = os.path.join(csproj_dir, 'bin', build_config)
|
||||
dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
|
||||
|
||||
if not os.path.isdir(dst_dir):
|
||||
assert not os.path.isfile(dst_dir)
|
||||
os.makedirs(dst_dir)
|
||||
|
||||
def copy_target(target_path):
|
||||
from shutil import copy
|
||||
filename = os.path.basename(target_path)
|
||||
copy(os.path.join(src_dir, filename), target_path)
|
||||
|
||||
for scons_target in target:
|
||||
copy_target(str(scons_target))
|
||||
|
||||
|
||||
def build(env_mono):
|
||||
assert env_mono['tools']
|
||||
|
||||
output_dir = Dir('#bin').abspath
|
||||
editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
|
||||
editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', 'Debug')
|
||||
|
||||
source_filenames = ['GodotSharp.dll', 'GodotSharpEditor.dll']
|
||||
sources = [os.path.join(editor_api_dir, filename) for filename in source_filenames]
|
||||
|
||||
target_filenames = ['GodotTools.dll', 'GodotTools.BuildLogger.dll', 'GodotTools.ProjectEditor.dll', 'DotNet.Glob.dll', 'GodotTools.Core.dll']
|
||||
|
||||
if env_mono['target'] == 'debug':
|
||||
target_filenames += ['GodotTools.pdb', 'GodotTools.BuildLogger.dll', 'GodotTools.ProjectEditor.dll', 'GodotTools.Core.dll']
|
||||
|
||||
targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames]
|
||||
|
||||
cmd = env_mono.CommandNoCache(targets, sources, build_godot_tools, module_dir=os.getcwd())
|
||||
env_mono.AlwaysBuild(cmd)
|
||||
|
||||
|
||||
def build_project_editor_only(env_mono):
|
||||
assert env_mono['tools']
|
||||
|
||||
output_dir = Dir('#bin').abspath
|
||||
editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
|
||||
|
||||
target_filenames = ['GodotTools.ProjectEditor.dll', 'DotNet.Glob.dll', 'GodotTools.Core.dll']
|
||||
targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames]
|
||||
|
||||
cmd = env_mono.CommandNoCache(targets, [], build_godot_tools_project_editor, module_dir=os.getcwd())
|
||||
env_mono.AlwaysBuild(cmd)
|
|
@ -1,10 +1,8 @@
|
|||
import imp
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
from SCons.Script import Dir, Environment
|
||||
|
||||
if os.name == 'nt':
|
||||
|
@ -58,6 +56,12 @@ def configure(env, env_mono):
|
|||
|
||||
mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
|
||||
|
||||
is_travis = os.environ.get('TRAVIS') == 'true'
|
||||
|
||||
if is_travis:
|
||||
# Travis CI may have a Mono version lower than 5.12
|
||||
env_mono.Append(CPPDEFINES=['NO_PENDING_EXCEPTIONS'])
|
||||
|
||||
if is_android and not env['android_arch'] in android_arch_dirs:
|
||||
raise RuntimeError('This module does not support for the specified \'android_arch\': ' + env['android_arch'])
|
||||
|
||||
|
@ -83,9 +87,6 @@ def configure(env, env_mono):
|
|||
|
||||
print('Found Mono root directory: ' + mono_root)
|
||||
|
||||
mono_version = mono_root_try_find_mono_version(mono_root)
|
||||
configure_for_mono_version(env_mono, mono_version)
|
||||
|
||||
mono_lib_path = os.path.join(mono_root, 'lib')
|
||||
|
||||
env.Append(LIBPATH=mono_lib_path)
|
||||
|
@ -164,9 +165,6 @@ def configure(env, env_mono):
|
|||
if mono_root:
|
||||
print('Found Mono root directory: ' + mono_root)
|
||||
|
||||
mono_version = mono_root_try_find_mono_version(mono_root)
|
||||
configure_for_mono_version(env_mono, mono_version)
|
||||
|
||||
mono_lib_path = os.path.join(mono_root, 'lib')
|
||||
|
||||
env.Append(LIBPATH=mono_lib_path)
|
||||
|
@ -209,9 +207,6 @@ def configure(env, env_mono):
|
|||
# TODO: Add option to force using pkg-config
|
||||
print('Mono root directory not found. Using pkg-config instead')
|
||||
|
||||
mono_version = pkgconfig_try_find_mono_version()
|
||||
configure_for_mono_version(env_mono, mono_version)
|
||||
|
||||
env.ParseConfig('pkg-config monosgen-2 --libs')
|
||||
env_mono.ParseConfig('pkg-config monosgen-2 --cflags')
|
||||
|
||||
|
@ -401,17 +396,6 @@ def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
|
|||
copy_if_exists(os.path.join(mono_root, 'lib', lib_file_name), target_mono_lib_dir)
|
||||
|
||||
|
||||
def configure_for_mono_version(env, mono_version):
|
||||
if mono_version is None:
|
||||
if os.getenv('MONO_VERSION'):
|
||||
mono_version = os.getenv('MONO_VERSION')
|
||||
else:
|
||||
raise RuntimeError("Mono JIT compiler version not found; specify one manually with the 'MONO_VERSION' environment variable")
|
||||
print('Found Mono JIT compiler version: ' + str(mono_version))
|
||||
if mono_version >= LooseVersion('5.12.0'):
|
||||
env.Append(CPPDEFINES=['HAS_PENDING_EXCEPTIONS'])
|
||||
|
||||
|
||||
def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
|
||||
tmpenv = Environment()
|
||||
tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
|
||||
|
@ -421,36 +405,3 @@ def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
|
|||
if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
|
||||
return os.path.join(hint_dir, '..')
|
||||
return ''
|
||||
|
||||
|
||||
def pkgconfig_try_find_mono_version():
|
||||
from compat import decode_utf8
|
||||
|
||||
lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines()
|
||||
greater_version = None
|
||||
for line in lines:
|
||||
try:
|
||||
version = LooseVersion(decode_utf8(line))
|
||||
if greater_version is None or version > greater_version:
|
||||
greater_version = version
|
||||
except ValueError:
|
||||
pass
|
||||
return greater_version
|
||||
|
||||
|
||||
def mono_root_try_find_mono_version(mono_root):
|
||||
from compat import decode_utf8
|
||||
|
||||
mono_bin = os.path.join(mono_root, 'bin')
|
||||
if os.path.isfile(os.path.join(mono_bin, 'mono')):
|
||||
mono_binary = os.path.join(mono_bin, 'mono')
|
||||
elif os.path.isfile(os.path.join(mono_bin, 'mono.exe')):
|
||||
mono_binary = os.path.join(mono_bin, 'mono.exe')
|
||||
else:
|
||||
return None
|
||||
output = subprocess.check_output([mono_binary, '--version'])
|
||||
first_line = decode_utf8(output.splitlines()[0])
|
||||
try:
|
||||
return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())])
|
||||
except (ValueError, IndexError):
|
||||
return None
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Build GodotSharpTools solution
|
||||
|
||||
import os
|
||||
|
||||
from SCons.Script import Builder, Dir
|
||||
|
||||
verbose = False
|
||||
|
||||
|
||||
def find_nuget_unix():
|
||||
|
@ -131,12 +131,46 @@ def find_msbuild_windows(env):
|
|||
return None
|
||||
|
||||
|
||||
def mono_build_solution(source, target, env):
|
||||
import subprocess
|
||||
from shutil import copyfile
|
||||
def run_command(command, args, env_override=None, name=None):
|
||||
def cmd_args_to_str(cmd_args):
|
||||
return ' '.join([arg if not ' ' in arg else '"%s"' % arg for arg in cmd_args])
|
||||
|
||||
sln_path = os.path.abspath(str(source[0]))
|
||||
target_path = os.path.abspath(str(target[0]))
|
||||
args = [command] + args
|
||||
|
||||
if name is None:
|
||||
name = os.path.basename(command)
|
||||
|
||||
if verbose:
|
||||
print("Running '%s': %s" % (name, cmd_args_to_str(args)))
|
||||
|
||||
import subprocess
|
||||
try:
|
||||
if env_override is None:
|
||||
subprocess.check_call(args)
|
||||
else:
|
||||
subprocess.check_call(args, env=env_override)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError("'%s' exited with error code: %s" % (name, e.returncode))
|
||||
|
||||
|
||||
def nuget_restore(env, *args):
|
||||
global verbose
|
||||
verbose = env['verbose']
|
||||
|
||||
# Find NuGet
|
||||
nuget_path = find_nuget_windows(env) if os.name == 'nt' else find_nuget_unix()
|
||||
if nuget_path is None:
|
||||
raise RuntimeError('Cannot find NuGet executable')
|
||||
|
||||
print('NuGet path: ' + nuget_path)
|
||||
|
||||
# Do NuGet restore
|
||||
run_command(nuget_path, ['restore'] + list(args), name='nuget restore')
|
||||
|
||||
|
||||
def build_solution(env, solution_path, build_config, extra_msbuild_args=[]):
|
||||
global verbose
|
||||
verbose = env['verbose']
|
||||
|
||||
framework_path = ''
|
||||
msbuild_env = os.environ.copy()
|
||||
|
@ -175,64 +209,10 @@ def mono_build_solution(source, target, env):
|
|||
|
||||
print('MSBuild path: ' + msbuild_path)
|
||||
|
||||
# Find NuGet
|
||||
nuget_path = find_nuget_windows(env) if os.name == 'nt' else find_nuget_unix()
|
||||
if nuget_path is None:
|
||||
raise RuntimeError('Cannot find NuGet executable')
|
||||
|
||||
print('NuGet path: ' + nuget_path)
|
||||
|
||||
# Do NuGet restore
|
||||
|
||||
try:
|
||||
subprocess.check_call([nuget_path, 'restore', sln_path])
|
||||
except subprocess.CalledProcessError:
|
||||
raise RuntimeError('GodotSharpTools: NuGet restore failed')
|
||||
|
||||
# Build solution
|
||||
|
||||
build_config = 'Release'
|
||||
msbuild_args = [solution_path, '/p:Configuration=' + build_config]
|
||||
msbuild_args += ['/p:FrameworkPathOverride=' + framework_path] if framework_path else []
|
||||
msbuild_args += extra_msbuild_args
|
||||
|
||||
msbuild_args = [
|
||||
msbuild_path,
|
||||
sln_path,
|
||||
'/p:Configuration=' + build_config,
|
||||
]
|
||||
|
||||
if framework_path:
|
||||
msbuild_args += ['/p:FrameworkPathOverride=' + framework_path]
|
||||
|
||||
try:
|
||||
subprocess.check_call(msbuild_args, env=msbuild_env)
|
||||
except subprocess.CalledProcessError:
|
||||
raise RuntimeError('GodotSharpTools: Build failed')
|
||||
|
||||
# Copy files
|
||||
|
||||
src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config))
|
||||
dst_dir = os.path.abspath(os.path.join(target_path, os.pardir))
|
||||
asm_file = 'GodotSharpTools.dll'
|
||||
|
||||
if not os.path.isdir(dst_dir):
|
||||
if os.path.exists(dst_dir):
|
||||
raise RuntimeError('Target directory is a file')
|
||||
os.makedirs(dst_dir)
|
||||
|
||||
copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
|
||||
|
||||
# Dependencies
|
||||
copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll"))
|
||||
|
||||
def build(env_mono):
|
||||
if not env_mono['tools']:
|
||||
return
|
||||
|
||||
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(editor_tools_dir, 'GodotSharpTools.dll'),
|
||||
'editor/GodotSharpTools/GodotSharpTools.sln'
|
||||
)
|
||||
run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name='msbuild')
|
|
@ -42,9 +42,9 @@
|
|||
#include "editor/bindings_generator.h"
|
||||
#include "editor/csharp_project.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/godotsharp_editor.h"
|
||||
#endif
|
||||
|
||||
#include "editor/editor_internal_calls.h"
|
||||
#include "godotsharp_dirs.h"
|
||||
#include "mono_gd/gd_mono_class.h"
|
||||
#include "mono_gd/gd_mono_marshal.h"
|
||||
|
@ -65,8 +65,8 @@ static bool _create_project_solution_if_needed() {
|
|||
if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) {
|
||||
// A solution does not yet exist, create a new one
|
||||
|
||||
CRASH_COND(GodotSharpEditor::get_singleton() == NULL);
|
||||
return GodotSharpEditor::get_singleton()->call("_create_project_solution");
|
||||
CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == NULL);
|
||||
return CSharpLanguage::get_singleton()->get_godotsharp_editor()->call("CreateProjectSolution");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -96,14 +96,6 @@ Error CSharpLanguage::execute_file(const String &p_path) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void gdsharp_editor_init_callback() {
|
||||
|
||||
EditorNode *editor = EditorNode::get_singleton();
|
||||
editor->add_child(memnew(GodotSharpEditor(editor)));
|
||||
}
|
||||
#endif
|
||||
|
||||
void CSharpLanguage::init() {
|
||||
|
||||
gdmono = memnew(GDMono);
|
||||
|
@ -114,14 +106,12 @@ void CSharpLanguage::init() {
|
|||
#endif
|
||||
|
||||
#if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED)
|
||||
if (gdmono->get_editor_tools_assembly() != NULL) {
|
||||
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
|
||||
BindingsGenerator::handle_cmdline_args(cmdline_args);
|
||||
}
|
||||
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
|
||||
BindingsGenerator::handle_cmdline_args(cmdline_args);
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorNode::add_init_callback(&gdsharp_editor_init_callback);
|
||||
EditorNode::add_init_callback(&_editor_init_callback);
|
||||
|
||||
GLOBAL_DEF("mono/export/include_scripts_content", false);
|
||||
#endif
|
||||
|
@ -664,7 +654,7 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft
|
|||
CRASH_COND(!Engine::get_singleton()->is_editor_hint());
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
MonoReloadNode::get_singleton()->restart_reload_timer();
|
||||
get_godotsharp_editor()->get_node(NodePath("HotReloadAssemblyWatcher"))->call("RestartTimer");
|
||||
#endif
|
||||
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
|
@ -731,58 +721,93 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
SCOPED_MUTEX_LOCK(script_instances_mutex);
|
||||
|
||||
for (SelfList<CSharpScript> *elem = script_list.first(); elem; elem = elem->next()) {
|
||||
if (elem->self()->get_path().is_resource_file()) {
|
||||
// Cast to CSharpScript to avoid being erased by accident
|
||||
scripts.push_back(Ref<CSharpScript>(elem->self()));
|
||||
}
|
||||
// Cast to CSharpScript to avoid being erased by accident
|
||||
scripts.push_back(Ref<CSharpScript>(elem->self()));
|
||||
}
|
||||
}
|
||||
|
||||
List<Ref<CSharpScript> > to_reload;
|
||||
|
||||
// We need to keep reference instances alive during reloading
|
||||
List<Ref<Reference> > ref_instances;
|
||||
|
||||
for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
|
||||
CSharpScriptBinding &script_binding = E->value();
|
||||
Reference *ref = Object::cast_to<Reference>(script_binding.owner);
|
||||
if (ref) {
|
||||
ref_instances.push_back(Ref<Reference>(ref));
|
||||
}
|
||||
}
|
||||
|
||||
// As scripts are going to be reloaded, must proceed without locking here
|
||||
|
||||
scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order
|
||||
|
||||
for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) {
|
||||
|
||||
Ref<CSharpScript> &script = E->get();
|
||||
|
||||
to_reload.push_back(script);
|
||||
|
||||
if (script->get_path().empty()) {
|
||||
script->tied_class_name_for_reload = script->script_class->get_name();
|
||||
script->tied_class_namespace_for_reload = script->script_class->get_namespace();
|
||||
}
|
||||
|
||||
// Script::instances are deleted during managed object disposal, which happens on domain finalize.
|
||||
// Only placeholders are kept. Therefore we need to keep a copy before that happens.
|
||||
|
||||
for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) {
|
||||
script->pending_reload_instances.insert(F->get()->get_instance_id());
|
||||
Object *obj = F->get();
|
||||
script->pending_reload_instances.insert(obj->get_instance_id());
|
||||
|
||||
Reference *ref = Object::cast_to<Reference>(obj);
|
||||
if (ref) {
|
||||
ref_instances.push_back(Ref<Reference>(ref));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
for (Set<PlaceHolderScriptInstance *>::Element *F = script->placeholders.front(); F; F = F->next()) {
|
||||
script->pending_reload_instances.insert(F->get()->get_owner()->get_instance_id());
|
||||
Object *obj = F->get()->get_owner();
|
||||
script->pending_reload_instances.insert(obj->get_instance_id());
|
||||
|
||||
Reference *ref = Object::cast_to<Reference>(obj);
|
||||
if (ref) {
|
||||
ref_instances.push_back(Ref<Reference>(ref));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// FIXME: What about references? Need to keep them alive if only managed code references them.
|
||||
|
||||
// Save state and remove script from instances
|
||||
Map<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
|
||||
|
||||
while (script->instances.front()) {
|
||||
Object *obj = script->instances.front()->get();
|
||||
// Save instance info
|
||||
CSharpScript::StateBackup state;
|
||||
for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) {
|
||||
Object *obj = F->get();
|
||||
|
||||
ERR_CONTINUE(!obj->get_script_instance());
|
||||
|
||||
// TODO: Proper state backup (Not only variants, serialize managed state of scripts)
|
||||
obj->get_script_instance()->get_property_state(state.properties);
|
||||
CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance());
|
||||
|
||||
Ref<MonoGCHandle> gchandle = CAST_CSHARP_INSTANCE(obj->get_script_instance())->gchandle;
|
||||
if (gchandle.is_valid())
|
||||
gchandle->release();
|
||||
// Call OnBeforeSerialize
|
||||
if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener)))
|
||||
obj->get_script_instance()->call_multilevel(string_names.on_before_serialize);
|
||||
|
||||
// Save instance info
|
||||
CSharpScript::StateBackup state;
|
||||
|
||||
// TODO: Proper state backup (Not only variants, serialize managed state of scripts)
|
||||
csi->get_properties_state_for_reloading(state.properties);
|
||||
|
||||
owners_map[obj->get_instance_id()] = state;
|
||||
}
|
||||
}
|
||||
|
||||
// After the state of all instances is saved, clear scripts and script instances
|
||||
for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) {
|
||||
Ref<CSharpScript> &script = E->get();
|
||||
|
||||
while (script->instances.front()) {
|
||||
Object *obj = script->instances.front()->get();
|
||||
obj->set_script(RefPtr()); // Remove script and existing script instances (placeholder are not removed before domain reload)
|
||||
}
|
||||
|
||||
|
@ -825,26 +850,76 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
scr->pending_reload_state.erase(obj_id);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (List<Ref<CSharpScript> >::Element *E = to_reload.front(); E; E = E->next()) {
|
||||
List<Ref<CSharpScript> > to_reload_state;
|
||||
|
||||
Ref<CSharpScript> scr = E->get();
|
||||
for (List<Ref<CSharpScript> >::Element *E = to_reload.front(); E; E = E->next()) {
|
||||
Ref<CSharpScript> script = E->get();
|
||||
|
||||
if (!script->get_path().empty()) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
scr->exports_invalidated = true;
|
||||
script->exports_invalidated = true;
|
||||
#endif
|
||||
scr->signals_invalidated = true;
|
||||
scr->reload(p_soft_reload);
|
||||
scr->update_exports();
|
||||
script->signals_invalidated = true;
|
||||
|
||||
script->reload(p_soft_reload);
|
||||
script->update_exports();
|
||||
} else {
|
||||
const StringName &class_namespace = script->tied_class_namespace_for_reload;
|
||||
const StringName &class_name = script->tied_class_name_for_reload;
|
||||
GDMonoAssembly *project_assembly = gdmono->get_project_assembly();
|
||||
GDMonoAssembly *tools_assembly = gdmono->get_tools_assembly();
|
||||
|
||||
// Search in project and tools assemblies first as those are the most likely to have the class
|
||||
GDMonoClass *script_class = (project_assembly ? project_assembly->get_class(class_namespace, class_name) : NULL);
|
||||
if (!script_class) {
|
||||
script_class = (tools_assembly ? tools_assembly->get_class(class_namespace, class_name) : NULL);
|
||||
}
|
||||
if (!script_class) {
|
||||
script_class = gdmono->get_class(class_namespace, class_name);
|
||||
}
|
||||
|
||||
if (!script_class) {
|
||||
// The class was removed, can't reload
|
||||
script->pending_reload_instances.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(script_class);
|
||||
if (!obj_type) {
|
||||
// The class no longer inherits Godot.Object, can't reload
|
||||
script->pending_reload_instances.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
GDMonoClass *native = GDMonoUtils::get_class_native_base(script_class);
|
||||
|
||||
Ref<CSharpScript> new_script = CSharpScript::create_for_managed_type(script_class, native);
|
||||
CRASH_COND(new_script.is_null());
|
||||
|
||||
new_script->pending_reload_instances = script->pending_reload_instances;
|
||||
new_script->pending_reload_state = script->pending_reload_state;
|
||||
script = new_script;
|
||||
}
|
||||
|
||||
String native_name = NATIVE_GDMONOCLASS_NAME(script->native);
|
||||
|
||||
{
|
||||
for (Set<ObjectID>::Element *F = scr->pending_reload_instances.front(); F; F = F->next()) {
|
||||
for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
|
||||
ObjectID obj_id = F->get();
|
||||
Object *obj = ObjectDB::get_instance(obj_id);
|
||||
|
||||
if (!obj) {
|
||||
scr->pending_reload_state.erase(obj_id);
|
||||
script->pending_reload_state.erase(obj_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ClassDB::is_parent_class(obj->get_class_name(), native_name)) {
|
||||
// No longer inherits the same compatible type, can't reload
|
||||
script->pending_reload_state.erase(obj_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -856,28 +931,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
// Non-placeholder script instances are removed in godot_icall_Object_Disposed.
|
||||
CRASH_COND(!si->is_placeholder());
|
||||
|
||||
if (scr->is_tool() || ScriptServer::is_scripting_enabled()) {
|
||||
if (script->is_tool() || ScriptServer::is_scripting_enabled()) {
|
||||
// Replace placeholder with a script instance
|
||||
|
||||
CSharpScript::StateBackup &state_backup = scr->pending_reload_state[obj_id];
|
||||
CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
|
||||
|
||||
// Backup placeholder script instance state before replacing it with a script instance
|
||||
si->get_property_state(state_backup.properties);
|
||||
|
||||
ScriptInstance *script_instance = scr->instance_create(obj);
|
||||
ScriptInstance *script_instance = script->instance_create(obj);
|
||||
|
||||
if (script_instance) {
|
||||
scr->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si));
|
||||
script->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si));
|
||||
obj->set_script_instance(script_instance);
|
||||
}
|
||||
|
||||
// TODO: Restore serialized state
|
||||
|
||||
for (List<Pair<StringName, Variant> >::Element *G = state_backup.properties.front(); G; G = G->next()) {
|
||||
script_instance->set(G->get().first, G->get().second);
|
||||
}
|
||||
|
||||
scr->pending_reload_state.erase(obj_id);
|
||||
}
|
||||
|
||||
continue;
|
||||
|
@ -887,19 +954,42 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
#endif
|
||||
// Re-create script instance
|
||||
|
||||
obj->set_script(scr.get_ref_ptr()); // will create the script instance as well
|
||||
obj->set_script(script.get_ref_ptr()); // will create the script instance as well
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Restore serialized state
|
||||
to_reload_state.push_back(script);
|
||||
}
|
||||
|
||||
for (List<Pair<StringName, Variant> >::Element *G = scr->pending_reload_state[obj_id].properties.front(); G; G = G->next()) {
|
||||
obj->get_script_instance()->set(G->get().first, G->get().second);
|
||||
}
|
||||
for (List<Ref<CSharpScript> >::Element *E = to_reload_state.front(); E; E = E->next()) {
|
||||
Ref<CSharpScript> script = E->get();
|
||||
|
||||
scr->pending_reload_state.erase(obj_id);
|
||||
for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
|
||||
ObjectID obj_id = F->get();
|
||||
Object *obj = ObjectDB::get_instance(obj_id);
|
||||
|
||||
if (!obj) {
|
||||
script->pending_reload_state.erase(obj_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
scr->pending_reload_instances.clear();
|
||||
ERR_CONTINUE(!obj->get_script_instance());
|
||||
|
||||
// TODO: Restore serialized state
|
||||
|
||||
CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
|
||||
|
||||
for (List<Pair<StringName, Variant> >::Element *G = state_backup.properties.front(); G; G = G->next()) {
|
||||
obj->get_script_instance()->set(G->get().first, G->get().second);
|
||||
}
|
||||
|
||||
// Call OnAfterDeserialization
|
||||
CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance());
|
||||
if (csi && csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener)))
|
||||
obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize);
|
||||
}
|
||||
|
||||
script->pending_reload_instances.clear();
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
@ -964,12 +1054,12 @@ void CSharpLanguage::get_recognized_extensions(List<String> *p_extensions) const
|
|||
#ifdef TOOLS_ENABLED
|
||||
Error CSharpLanguage::open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) {
|
||||
|
||||
return GodotSharpEditor::get_singleton()->open_in_external_editor(p_script, p_line, p_col);
|
||||
return (Error)(int)get_godotsharp_editor()->call("OpenInExternalEditor", p_script, p_line, p_col);
|
||||
}
|
||||
|
||||
bool CSharpLanguage::overrides_external_editor() {
|
||||
|
||||
return GodotSharpEditor::get_singleton()->overrides_external_editor();
|
||||
return get_godotsharp_editor()->call("OverridesExternalEditor");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1027,6 +1117,34 @@ void CSharpLanguage::_on_scripts_domain_unloaded() {
|
|||
scripts_metadata_invalidated = true;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void CSharpLanguage::_editor_init_callback() {
|
||||
|
||||
register_editor_internal_calls();
|
||||
|
||||
// Initialize GodotSharpEditor
|
||||
|
||||
GDMonoClass *editor_klass = GDMono::get_singleton()->get_tools_assembly()->get_class("GodotTools", "GodotSharpEditor");
|
||||
CRASH_COND(editor_klass == NULL);
|
||||
|
||||
MonoObject *mono_object = mono_object_new(mono_domain_get(), editor_klass->get_mono_ptr());
|
||||
CRASH_COND(mono_object == NULL);
|
||||
|
||||
MonoException *exc = NULL;
|
||||
GDMonoUtils::runtime_object_init(mono_object, editor_klass, &exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(GDMonoMarshal::mono_object_to_variant(mono_object));
|
||||
CRASH_COND(godotsharp_editor == NULL);
|
||||
|
||||
// Enable it as a plugin
|
||||
EditorNode::add_editor_plugin(godotsharp_editor);
|
||||
godotsharp_editor->enable_plugin();
|
||||
|
||||
get_singleton()->godotsharp_editor = godotsharp_editor;
|
||||
}
|
||||
#endif
|
||||
|
||||
void CSharpLanguage::set_language_index(int p_idx) {
|
||||
|
||||
ERR_FAIL_COND(lang_idx != -1);
|
||||
|
@ -1084,6 +1202,8 @@ CSharpLanguage::CSharpLanguage() {
|
|||
lang_idx = -1;
|
||||
|
||||
scripts_metadata_invalidated = true;
|
||||
|
||||
godotsharp_editor = NULL;
|
||||
}
|
||||
|
||||
CSharpLanguage::~CSharpLanguage() {
|
||||
|
@ -1139,6 +1259,7 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
|
|||
r_script_binding.type_name = type_name;
|
||||
r_script_binding.wrapper_class = type_class; // cache
|
||||
r_script_binding.gchandle = MonoGCHandle::create_strong(mono_object);
|
||||
r_script_binding.owner = p_object;
|
||||
|
||||
// Tie managed to unmanaged
|
||||
Reference *ref = Object::cast_to<Reference>(p_object);
|
||||
|
@ -1223,6 +1344,9 @@ void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) {
|
|||
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
|
||||
Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
|
||||
|
||||
if (!script_binding.inited)
|
||||
return;
|
||||
|
||||
if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
|
||||
// The reference count was increased after the managed side was the only one referencing our owner.
|
||||
// This means the owner is being referenced again by the unmanaged side,
|
||||
|
@ -1247,14 +1371,17 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
|
|||
CRASH_COND(!ref_owner);
|
||||
#endif
|
||||
|
||||
int refcount = ref_owner->reference_get_count();
|
||||
|
||||
void *data = p_object->get_script_instance_binding(get_language_index());
|
||||
CRASH_COND(!data);
|
||||
|
||||
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
|
||||
Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
|
||||
|
||||
int refcount = ref_owner->reference_get_count();
|
||||
|
||||
if (!script_binding.inited)
|
||||
return refcount == 0;
|
||||
|
||||
if (refcount == 1 && gchandle.is_valid() && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
|
||||
// If owner owner is no longer referenced by the unmanaged side,
|
||||
// the managed instance takes responsibility of deleting the owner when GCed.
|
||||
|
@ -1417,6 +1544,31 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Variant> > &r_state) {
|
||||
|
||||
List<PropertyInfo> pinfo;
|
||||
get_property_list(&pinfo);
|
||||
|
||||
for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
|
||||
Pair<StringName, Variant> state_pair;
|
||||
state_pair.first = E->get().name;
|
||||
|
||||
ManagedType managedType;
|
||||
|
||||
GDMonoField *field = script->script_class->get_field(state_pair.first);
|
||||
if (!field)
|
||||
continue; // Properties ignored. We get the property baking fields instead.
|
||||
|
||||
managedType = field->get_type();
|
||||
|
||||
if (GDMonoMarshal::managed_to_variant_type(managedType) != Variant::NIL) { // If we can marshal it
|
||||
if (get(state_pair.first, state_pair.second)) {
|
||||
r_state.push_back(state_pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
|
||||
|
||||
for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) {
|
||||
|
@ -1614,17 +1766,18 @@ MonoObject *CSharpInstance::_internal_new_managed() {
|
|||
ERR_FAIL_NULL_V(owner, NULL);
|
||||
ERR_FAIL_COND_V(script.is_null(), NULL);
|
||||
|
||||
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script->script_class->get_mono_ptr());
|
||||
MonoObject *mono_object = mono_object_new(mono_domain_get(), script->script_class->get_mono_ptr());
|
||||
|
||||
if (!mono_object) {
|
||||
// Important to clear this before destroying the script instance here
|
||||
script = Ref<CSharpScript>();
|
||||
owner = NULL;
|
||||
|
||||
bool die = _unreference_owner_unsafe();
|
||||
// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
|
||||
CRASH_COND(die == true);
|
||||
|
||||
owner = NULL;
|
||||
|
||||
ERR_EXPLAIN("Failed to allocate memory for the object");
|
||||
ERR_FAIL_V(NULL);
|
||||
}
|
||||
|
@ -1940,7 +2093,16 @@ CSharpInstance::~CSharpInstance() {
|
|||
CRASH_COND(data == NULL);
|
||||
|
||||
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
|
||||
CRASH_COND(!script_binding.inited);
|
||||
|
||||
if (!script_binding.inited) {
|
||||
SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->get_language_bind_mutex());
|
||||
|
||||
if (!script_binding.inited) { // Other thread may have set it up
|
||||
// Already had a binding that needs to be setup
|
||||
CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, owner);
|
||||
CRASH_COND(!script_binding.inited);
|
||||
}
|
||||
}
|
||||
|
||||
bool die = _unreference_owner_unsafe();
|
||||
CRASH_COND(die == true); // The "instance binding" should be holding a reference
|
||||
|
@ -1984,6 +2146,52 @@ void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List
|
|||
}
|
||||
#endif
|
||||
|
||||
void CSharpScript::_update_member_info_no_exports() {
|
||||
|
||||
if (exports_invalidated) {
|
||||
exports_invalidated = false;
|
||||
|
||||
member_info.clear();
|
||||
|
||||
GDMonoClass *top = script_class;
|
||||
|
||||
while (top && top != native) {
|
||||
PropertyInfo prop_info;
|
||||
bool exported;
|
||||
|
||||
const Vector<GDMonoField *> &fields = top->get_all_fields();
|
||||
|
||||
for (int i = fields.size() - 1; i >= 0; i--) {
|
||||
GDMonoField *field = fields[i];
|
||||
|
||||
if (_get_member_export(field, /* inspect export: */ false, prop_info, exported)) {
|
||||
StringName member_name = field->get_name();
|
||||
|
||||
member_info[member_name] = prop_info;
|
||||
exported_members_cache.push_front(prop_info);
|
||||
exported_members_defval_cache[member_name] = Variant();
|
||||
}
|
||||
}
|
||||
|
||||
const Vector<GDMonoProperty *> &properties = top->get_all_properties();
|
||||
|
||||
for (int i = properties.size() - 1; i >= 0; i--) {
|
||||
GDMonoProperty *property = properties[i];
|
||||
|
||||
if (_get_member_export(property, /* inspect export: */ false, prop_info, exported)) {
|
||||
StringName member_name = property->get_name();
|
||||
|
||||
member_info[member_name] = prop_info;
|
||||
exported_members_cache.push_front(prop_info);
|
||||
exported_members_defval_cache[member_name] = Variant();
|
||||
}
|
||||
}
|
||||
|
||||
top = top->get_parent_class();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CSharpScript::_update_exports() {
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
@ -2008,7 +2216,7 @@ bool CSharpScript::_update_exports() {
|
|||
|
||||
// Here we create a temporary managed instance of the class to get the initial values
|
||||
|
||||
MonoObject *tmp_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr());
|
||||
MonoObject *tmp_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr());
|
||||
|
||||
if (!tmp_object) {
|
||||
ERR_PRINT("Failed to allocate temporary MonoObject");
|
||||
|
@ -2049,18 +2257,18 @@ bool CSharpScript::_update_exports() {
|
|||
for (int i = fields.size() - 1; i >= 0; i--) {
|
||||
GDMonoField *field = fields[i];
|
||||
|
||||
if (_get_member_export(field, prop_info, exported)) {
|
||||
StringName name = field->get_name();
|
||||
if (_get_member_export(field, /* inspect export: */ true, prop_info, exported)) {
|
||||
StringName member_name = field->get_name();
|
||||
|
||||
if (exported) {
|
||||
member_info[name] = prop_info;
|
||||
member_info[member_name] = prop_info;
|
||||
exported_members_cache.push_front(prop_info);
|
||||
|
||||
if (tmp_object) {
|
||||
exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object));
|
||||
exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object));
|
||||
}
|
||||
} else {
|
||||
member_info[name] = prop_info;
|
||||
member_info[member_name] = prop_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2070,25 +2278,25 @@ bool CSharpScript::_update_exports() {
|
|||
for (int i = properties.size() - 1; i >= 0; i--) {
|
||||
GDMonoProperty *property = properties[i];
|
||||
|
||||
if (_get_member_export(property, prop_info, exported)) {
|
||||
StringName name = property->get_name();
|
||||
if (_get_member_export(property, /* inspect export: */ true, prop_info, exported)) {
|
||||
StringName member_name = property->get_name();
|
||||
|
||||
if (exported) {
|
||||
member_info[name] = prop_info;
|
||||
member_info[member_name] = prop_info;
|
||||
exported_members_cache.push_front(prop_info);
|
||||
|
||||
if (tmp_object) {
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret = property->get_value(tmp_object, &exc);
|
||||
if (exc) {
|
||||
exported_members_defval_cache[name] = Variant();
|
||||
exported_members_defval_cache[member_name] = Variant();
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
} else {
|
||||
exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret);
|
||||
exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(ret);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
member_info[name] = prop_info;
|
||||
member_info[member_name] = prop_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2197,7 +2405,7 @@ bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Ve
|
|||
* Returns false if there was an error, otherwise true.
|
||||
* If there was an error, r_prop_info and r_exported are not assigned any value.
|
||||
*/
|
||||
bool CSharpScript::_get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) {
|
||||
bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported) {
|
||||
|
||||
// Goddammit, C++. All I wanted was some nested functions.
|
||||
#define MEMBER_FULL_QUALIFIED_NAME(m_member) \
|
||||
|
@ -2222,26 +2430,30 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, PropertyInfo &
|
|||
CRASH_NOW();
|
||||
}
|
||||
|
||||
Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type);
|
||||
|
||||
if (!p_member->has_attribute(CACHED_CLASS(ExportAttribute))) {
|
||||
r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
|
||||
r_exported = false;
|
||||
return true;
|
||||
}
|
||||
bool exported = p_member->has_attribute(CACHED_CLASS(ExportAttribute));
|
||||
|
||||
if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) {
|
||||
GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member);
|
||||
if (!property->has_getter()) {
|
||||
ERR_PRINTS("Read-only property cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
|
||||
if (exported)
|
||||
ERR_PRINTS("Read-only property cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
|
||||
return false;
|
||||
}
|
||||
if (!property->has_setter()) {
|
||||
ERR_PRINTS("Set-only property (without getter) cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
|
||||
if (exported)
|
||||
ERR_PRINTS("Write-only property (without getter) cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type);
|
||||
|
||||
if (!p_inspect_export || !exported) {
|
||||
r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
|
||||
r_exported = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
|
||||
|
||||
PropertyHint hint = PROPERTY_HINT_NONE;
|
||||
|
@ -2463,9 +2675,9 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GD
|
|||
|
||||
// This method should not fail
|
||||
|
||||
CRASH_COND(!p_class);
|
||||
CRASH_COND(p_class == NULL);
|
||||
|
||||
// TODO: Cache the 'CSharpScript' associated with this 'p_class' instead of allocating a new one every time
|
||||
// TODO OPTIMIZE: Cache the 'CSharpScript' associated with this 'p_class' instead of allocating a new one every time
|
||||
Ref<CSharpScript> script = memnew(CSharpScript);
|
||||
|
||||
script->name = p_class->get_name();
|
||||
|
@ -2479,6 +2691,20 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GD
|
|||
if (base != script->native)
|
||||
script->base = base;
|
||||
|
||||
script->valid = true;
|
||||
script->tool = script->script_class->has_attribute(CACHED_CLASS(ToolAttribute));
|
||||
|
||||
if (!script->tool) {
|
||||
GDMonoClass *nesting_class = script->script_class->get_nesting_class();
|
||||
script->tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute));
|
||||
}
|
||||
|
||||
#if TOOLS_ENABLED
|
||||
if (!script->tool) {
|
||||
script->tool = script->script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
// For debug builds, we must fetch from all native base methods as well.
|
||||
// Native base methods must be fetched before the current class.
|
||||
|
@ -2507,6 +2733,7 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GD
|
|||
}
|
||||
|
||||
script->load_script_signals(script->script_class, script->native);
|
||||
script->_update_member_info_no_exports();
|
||||
|
||||
return script;
|
||||
}
|
||||
|
@ -2516,7 +2743,8 @@ bool CSharpScript::can_instance() const {
|
|||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
|
||||
if (get_path().find("::") == -1) { // Ignore if built-in script. Can happen if the file is deleted...
|
||||
// Hack to lower the risk of attached scripts not being added to the C# project
|
||||
if (!get_path().empty() && get_path().find("::") == -1) { // Ignore if built-in script. Can happen if the file is deleted...
|
||||
if (_create_project_solution_if_needed()) {
|
||||
CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(),
|
||||
"Compile",
|
||||
|
@ -2568,7 +2796,9 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
|
|||
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
|
||||
if (ctor == NULL) {
|
||||
if (p_argcount == 0) {
|
||||
ERR_PRINTS("Cannot create script instance because the class does not define a parameterless constructor: " + get_path());
|
||||
String path = get_path();
|
||||
ERR_PRINTS("Cannot create script instance. The class '" + script_class->get_full_name() +
|
||||
"' does not define a parameterless constructor." + (path.empty() ? String() : ". Path: " + path));
|
||||
}
|
||||
|
||||
ERR_EXPLAIN("Constructor not found");
|
||||
|
@ -2610,7 +2840,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
|
|||
|
||||
/* STEP 2, INITIALIZE AND CONSTRUCT */
|
||||
|
||||
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr());
|
||||
MonoObject *mono_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr());
|
||||
|
||||
if (!mono_object) {
|
||||
// Important to clear this before destroying the script instance here
|
||||
|
@ -2691,7 +2921,7 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
|
|||
#endif
|
||||
|
||||
if (native) {
|
||||
String native_name = native->get_name();
|
||||
String native_name = NATIVE_GDMONOCLASS_NAME(native);
|
||||
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, "Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
|
||||
|
@ -2817,11 +3047,22 @@ Error CSharpScript::reload(bool p_keep_state) {
|
|||
|
||||
if (script_class) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
print_verbose("Found class " + script_class->get_namespace() + "." + script_class->get_name() + " for script " + get_path());
|
||||
print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path());
|
||||
#endif
|
||||
|
||||
tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute));
|
||||
|
||||
if (!tool) {
|
||||
GDMonoClass *nesting_class = script_class->get_nesting_class();
|
||||
tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute));
|
||||
}
|
||||
|
||||
#if TOOLS_ENABLED
|
||||
if (!tool) {
|
||||
tool = script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly();
|
||||
}
|
||||
#endif
|
||||
|
||||
native = GDMonoUtils::get_class_native_base(script_class);
|
||||
|
||||
CRASH_COND(native == NULL);
|
||||
|
@ -3019,7 +3260,8 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p
|
|||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint() && mono_domain_get() == NULL) {
|
||||
MonoDomain *domain = mono_domain_get();
|
||||
if (Engine::get_singleton()->is_editor_hint() && domain == NULL) {
|
||||
|
||||
CRASH_COND(Thread::get_caller_id() == Thread::get_main_id());
|
||||
|
||||
|
@ -3027,8 +3269,8 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p
|
|||
// because this may be called by one of the editor's worker threads.
|
||||
// Attach this thread temporarily to reload the script.
|
||||
|
||||
if (SCRIPTS_DOMAIN) {
|
||||
MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN);
|
||||
if (domain) {
|
||||
MonoThread *mono_thread = mono_thread_attach(domain);
|
||||
CRASH_COND(mono_thread == NULL);
|
||||
script->reload();
|
||||
mono_thread_detach(mono_thread);
|
||||
|
@ -3128,5 +3370,7 @@ CSharpLanguage::StringNameCache::StringNameCache() {
|
|||
_get_property_list = StaticCString::create("_get_property_list");
|
||||
_notification = StaticCString::create("_notification");
|
||||
_script_source = StaticCString::create("script/source");
|
||||
on_before_serialize = StaticCString::create("OnBeforeSerialize");
|
||||
on_after_deserialize = StaticCString::create("OnAfterDeserialize");
|
||||
dotctor = StaticCString::create(".ctor");
|
||||
}
|
||||
|
|
|
@ -41,6 +41,10 @@
|
|||
#include "mono_gd/gd_mono_header.h"
|
||||
#include "mono_gd/gd_mono_internals.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_plugin.h"
|
||||
#endif
|
||||
|
||||
class CSharpScript;
|
||||
class CSharpInstance;
|
||||
class CSharpLanguage;
|
||||
|
@ -92,6 +96,8 @@ class CSharpScript : public Script {
|
|||
|
||||
Set<ObjectID> pending_reload_instances;
|
||||
Map<ObjectID, StateBackup> pending_reload_state;
|
||||
StringName tied_class_name_for_reload;
|
||||
StringName tied_class_namespace_for_reload;
|
||||
#endif
|
||||
|
||||
String source;
|
||||
|
@ -125,9 +131,10 @@ class CSharpScript : public Script {
|
|||
void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
|
||||
bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms);
|
||||
|
||||
void _update_member_info_no_exports();
|
||||
bool _update_exports();
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool _get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported);
|
||||
bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported);
|
||||
static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
|
||||
#endif
|
||||
|
||||
|
@ -226,6 +233,8 @@ class CSharpInstance : public ScriptInstance {
|
|||
|
||||
MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
|
||||
|
||||
void get_properties_state_for_reloading(List<Pair<StringName, Variant> > &r_state);
|
||||
|
||||
public:
|
||||
MonoObject *get_mono_object() const;
|
||||
|
||||
|
@ -276,6 +285,7 @@ struct CSharpScriptBinding {
|
|||
StringName type_name;
|
||||
GDMonoClass *wrapper_class;
|
||||
Ref<MonoGCHandle> gchandle;
|
||||
Object *owner;
|
||||
};
|
||||
|
||||
class CSharpLanguage : public ScriptLanguage {
|
||||
|
@ -305,6 +315,8 @@ class CSharpLanguage : public ScriptLanguage {
|
|||
StringName _notification;
|
||||
StringName _script_source;
|
||||
StringName dotctor; // .ctor
|
||||
StringName on_before_serialize; // OnBeforeSerialize
|
||||
StringName on_after_deserialize; // OnAfterDeserialize
|
||||
|
||||
StringNameCache();
|
||||
};
|
||||
|
@ -324,6 +336,12 @@ class CSharpLanguage : public ScriptLanguage {
|
|||
friend class GDMono;
|
||||
void _on_scripts_domain_unloaded();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorPlugin *godotsharp_editor;
|
||||
|
||||
static void _editor_init_callback();
|
||||
#endif
|
||||
|
||||
public:
|
||||
StringNameCache string_names;
|
||||
|
||||
|
@ -336,6 +354,8 @@ public:
|
|||
|
||||
_FORCE_INLINE_ static CSharpLanguage *get_singleton() { return singleton; }
|
||||
|
||||
_FORCE_INLINE_ EditorPlugin *get_godotsharp_editor() const { return godotsharp_editor; }
|
||||
|
||||
static void release_script_gchandle(Ref<MonoGCHandle> &p_gchandle);
|
||||
static void release_script_gchandle(MonoObject *p_expected_obj, Ref<MonoGCHandle> &p_gchandle);
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
# nuget packages
|
||||
packages
|
|
@ -1,425 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using Microsoft.Build.Framework;
|
||||
|
||||
namespace GodotSharpTools.Build
|
||||
{
|
||||
public class BuildInstance : IDisposable
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private extern static string godot_icall_BuildInstance_get_MSBuildPath();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private extern static string godot_icall_BuildInstance_get_MonoWindowsBinDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private extern static bool godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private extern static bool godot_icall_BuildInstance_get_PrintBuildOutput();
|
||||
|
||||
private static string GetMSBuildPath()
|
||||
{
|
||||
string msbuildPath = godot_icall_BuildInstance_get_MSBuildPath();
|
||||
|
||||
if (msbuildPath == null)
|
||||
throw new FileNotFoundException("Cannot find the MSBuild executable.");
|
||||
|
||||
return msbuildPath;
|
||||
}
|
||||
|
||||
private static string MonoWindowsBinDir
|
||||
{
|
||||
get
|
||||
{
|
||||
string monoWinBinDir = godot_icall_BuildInstance_get_MonoWindowsBinDir();
|
||||
|
||||
if (monoWinBinDir == null)
|
||||
throw new FileNotFoundException("Cannot find the Windows Mono binaries directory.");
|
||||
|
||||
return monoWinBinDir;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool UsingMonoMSBuildOnWindows
|
||||
{
|
||||
get
|
||||
{
|
||||
return godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool PrintBuildOutput
|
||||
{
|
||||
get
|
||||
{
|
||||
return godot_icall_BuildInstance_get_PrintBuildOutput();
|
||||
}
|
||||
}
|
||||
|
||||
private string solution;
|
||||
private string config;
|
||||
|
||||
private Process process;
|
||||
|
||||
private int exitCode;
|
||||
public int ExitCode { get { return exitCode; } }
|
||||
|
||||
public bool IsRunning { get { return process != null && !process.HasExited; } }
|
||||
|
||||
public BuildInstance(string solution, string config)
|
||||
{
|
||||
this.solution = solution;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
|
||||
{
|
||||
List<string> customPropertiesList = new List<string>();
|
||||
|
||||
if (customProperties != null)
|
||||
customPropertiesList.AddRange(customProperties);
|
||||
|
||||
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
|
||||
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs);
|
||||
|
||||
bool redirectOutput = !IsDebugMSBuildRequested() && !PrintBuildOutput;
|
||||
|
||||
if (!redirectOutput) // TODO: or if stdout verbose
|
||||
Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}");
|
||||
|
||||
startInfo.RedirectStandardOutput = redirectOutput;
|
||||
startInfo.RedirectStandardError = redirectOutput;
|
||||
startInfo.UseShellExecute = false;
|
||||
|
||||
if (UsingMonoMSBuildOnWindows)
|
||||
{
|
||||
// These environment variables are required for Mono's MSBuild to find the compilers.
|
||||
// We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
|
||||
string monoWinBinDir = MonoWindowsBinDir;
|
||||
startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
|
||||
startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
|
||||
startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
|
||||
}
|
||||
|
||||
// Needed when running from Developer Command Prompt for VS
|
||||
RemovePlatformVariable(startInfo.EnvironmentVariables);
|
||||
|
||||
using (Process process = new Process())
|
||||
{
|
||||
process.StartInfo = startInfo;
|
||||
|
||||
process.Start();
|
||||
|
||||
if (redirectOutput)
|
||||
{
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
}
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
exitCode = process.ExitCode;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool BuildAsync(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
|
||||
{
|
||||
if (process != null)
|
||||
throw new InvalidOperationException("Already in use");
|
||||
|
||||
List<string> customPropertiesList = new List<string>();
|
||||
|
||||
if (customProperties != null)
|
||||
customPropertiesList.AddRange(customProperties);
|
||||
|
||||
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
|
||||
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs);
|
||||
|
||||
bool redirectOutput = !IsDebugMSBuildRequested() && !PrintBuildOutput;
|
||||
|
||||
if (!redirectOutput) // TODO: or if stdout verbose
|
||||
Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}");
|
||||
|
||||
startInfo.RedirectStandardOutput = redirectOutput;
|
||||
startInfo.RedirectStandardError = redirectOutput;
|
||||
startInfo.UseShellExecute = false;
|
||||
|
||||
if (UsingMonoMSBuildOnWindows)
|
||||
{
|
||||
// These environment variables are required for Mono's MSBuild to find the compilers.
|
||||
// We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
|
||||
string monoWinBinDir = MonoWindowsBinDir;
|
||||
startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
|
||||
startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
|
||||
startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
|
||||
}
|
||||
|
||||
// Needed when running from Developer Command Prompt for VS
|
||||
RemovePlatformVariable(startInfo.EnvironmentVariables);
|
||||
|
||||
process = new Process();
|
||||
process.StartInfo = startInfo;
|
||||
process.EnableRaisingEvents = true;
|
||||
process.Exited += new EventHandler(BuildProcess_Exited);
|
||||
|
||||
process.Start();
|
||||
|
||||
if (redirectOutput)
|
||||
{
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, List<string> customProperties)
|
||||
{
|
||||
string arguments = string.Format(@"""{0}"" /v:normal /t:Rebuild ""/p:{1}"" ""/l:{2},{3};{4}""",
|
||||
solution,
|
||||
"Configuration=" + config,
|
||||
typeof(GodotBuildLogger).FullName,
|
||||
loggerAssemblyPath,
|
||||
loggerOutputDir
|
||||
);
|
||||
|
||||
foreach (string customProperty in customProperties)
|
||||
{
|
||||
arguments += " /p:" + customProperty;
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
private void RemovePlatformVariable(StringDictionary environmentVariables)
|
||||
{
|
||||
// EnvironmentVariables is case sensitive? Seriously?
|
||||
|
||||
List<string> platformEnvironmentVariables = new List<string>();
|
||||
|
||||
foreach (string env in environmentVariables.Keys)
|
||||
{
|
||||
if (env.ToUpper() == "PLATFORM")
|
||||
platformEnvironmentVariables.Add(env);
|
||||
}
|
||||
|
||||
foreach (string env in platformEnvironmentVariables)
|
||||
environmentVariables.Remove(env);
|
||||
}
|
||||
|
||||
private void BuildProcess_Exited(object sender, System.EventArgs e)
|
||||
{
|
||||
exitCode = process.ExitCode;
|
||||
|
||||
godot_icall_BuildInstance_ExitCallback(solution, config, exitCode);
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private static bool IsDebugMSBuildRequested()
|
||||
{
|
||||
return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1";
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (process != null)
|
||||
{
|
||||
process.Dispose();
|
||||
process = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GodotBuildLogger : ILogger
|
||||
{
|
||||
public string Parameters { get; set; }
|
||||
public LoggerVerbosity Verbosity { get; set; }
|
||||
|
||||
public void Initialize(IEventSource eventSource)
|
||||
{
|
||||
if (null == Parameters)
|
||||
throw new LoggerException("Log directory was not set.");
|
||||
|
||||
string[] parameters = Parameters.Split(new[] { ';' });
|
||||
|
||||
string logDir = parameters[0];
|
||||
|
||||
if (String.IsNullOrEmpty(logDir))
|
||||
throw new LoggerException("Log directory was not set.");
|
||||
|
||||
if (parameters.Length > 1)
|
||||
throw new LoggerException("Too many parameters passed.");
|
||||
|
||||
string logFile = Path.Combine(logDir, "msbuild_log.txt");
|
||||
string issuesFile = Path.Combine(logDir, "msbuild_issues.csv");
|
||||
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(logDir))
|
||||
Directory.CreateDirectory(logDir);
|
||||
|
||||
this.logStreamWriter = new StreamWriter(logFile);
|
||||
this.issuesStreamWriter = new StreamWriter(issuesFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if
|
||||
(
|
||||
ex is UnauthorizedAccessException
|
||||
|| ex is ArgumentNullException
|
||||
|| ex is PathTooLongException
|
||||
|| ex is DirectoryNotFoundException
|
||||
|| ex is NotSupportedException
|
||||
|| ex is ArgumentException
|
||||
|| ex is SecurityException
|
||||
|| ex is IOException
|
||||
)
|
||||
{
|
||||
throw new LoggerException("Failed to create log file: " + ex.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unexpected failure
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
eventSource.ProjectStarted += new ProjectStartedEventHandler(eventSource_ProjectStarted);
|
||||
eventSource.TaskStarted += new TaskStartedEventHandler(eventSource_TaskStarted);
|
||||
eventSource.MessageRaised += new BuildMessageEventHandler(eventSource_MessageRaised);
|
||||
eventSource.WarningRaised += new BuildWarningEventHandler(eventSource_WarningRaised);
|
||||
eventSource.ErrorRaised += new BuildErrorEventHandler(eventSource_ErrorRaised);
|
||||
eventSource.ProjectFinished += new ProjectFinishedEventHandler(eventSource_ProjectFinished);
|
||||
}
|
||||
|
||||
void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
|
||||
{
|
||||
string line = String.Format("{0}({1},{2}): error {3}: {4}", e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message);
|
||||
|
||||
if (e.ProjectFile.Length > 0)
|
||||
line += string.Format(" [{0}]", e.ProjectFile);
|
||||
|
||||
WriteLine(line);
|
||||
|
||||
string errorLine = String.Format(@"error,{0},{1},{2},{3},{4},{5}",
|
||||
e.File.CsvEscape(), e.LineNumber, e.ColumnNumber,
|
||||
e.Code.CsvEscape(), e.Message.CsvEscape(), e.ProjectFile.CsvEscape());
|
||||
issuesStreamWriter.WriteLine(errorLine);
|
||||
}
|
||||
|
||||
void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
|
||||
{
|
||||
string line = String.Format("{0}({1},{2}): warning {3}: {4}", e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, e.ProjectFile);
|
||||
|
||||
if (e.ProjectFile != null && e.ProjectFile.Length > 0)
|
||||
line += string.Format(" [{0}]", e.ProjectFile);
|
||||
|
||||
WriteLine(line);
|
||||
|
||||
string warningLine = String.Format(@"warning,{0},{1},{2},{3},{4},{5}",
|
||||
e.File.CsvEscape(), e.LineNumber, e.ColumnNumber,
|
||||
e.Code.CsvEscape(), e.Message.CsvEscape(), e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty);
|
||||
issuesStreamWriter.WriteLine(warningLine);
|
||||
}
|
||||
|
||||
void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
|
||||
{
|
||||
// BuildMessageEventArgs adds Importance to BuildEventArgs
|
||||
// Let's take account of the verbosity setting we've been passed in deciding whether to log the message
|
||||
if ((e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal))
|
||||
|| (e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal))
|
||||
|| (e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed))
|
||||
)
|
||||
{
|
||||
WriteLineWithSenderAndMessage(String.Empty, e);
|
||||
}
|
||||
}
|
||||
|
||||
void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
|
||||
{
|
||||
// TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName
|
||||
// To keep this log clean, this logger will ignore these events.
|
||||
}
|
||||
|
||||
void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
|
||||
{
|
||||
WriteLine(e.Message);
|
||||
indent++;
|
||||
}
|
||||
|
||||
void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
|
||||
{
|
||||
indent--;
|
||||
WriteLine(e.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a line to the log, adding the SenderName
|
||||
/// </summary>
|
||||
private void WriteLineWithSender(string line, BuildEventArgs e)
|
||||
{
|
||||
if (0 == String.Compare(e.SenderName, "MSBuild", true /*ignore case*/))
|
||||
{
|
||||
// Well, if the sender name is MSBuild, let's leave it out for prettiness
|
||||
WriteLine(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLine(e.SenderName + ": " + line);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a line to the log, adding the SenderName and Message
|
||||
/// (these parameters are on all MSBuild event argument objects)
|
||||
/// </summary>
|
||||
private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e)
|
||||
{
|
||||
if (0 == String.Compare(e.SenderName, "MSBuild", true /*ignore case*/))
|
||||
{
|
||||
// Well, if the sender name is MSBuild, let's leave it out for prettiness
|
||||
WriteLine(line + e.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLine(e.SenderName + ": " + line + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteLine(string line)
|
||||
{
|
||||
for (int i = indent; i > 0; i--)
|
||||
{
|
||||
logStreamWriter.Write("\t");
|
||||
}
|
||||
logStreamWriter.WriteLine(line);
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
logStreamWriter.Close();
|
||||
issuesStreamWriter.Close();
|
||||
}
|
||||
|
||||
public bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity)
|
||||
{
|
||||
return this.Verbosity >= checkVerbosity;
|
||||
}
|
||||
|
||||
private StreamWriter logStreamWriter;
|
||||
private StreamWriter issuesStreamWriter;
|
||||
private int indent;
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
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 ? ".release_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());
|
||||
|
||||
if (Directory.Exists(outputDataDir))
|
||||
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.Any(f => new[] {"OSX", "Android"}.Contains(f));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
extern static string GetTemplatesDir();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
extern static string GetDataDirName();
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpTools", "GodotSharpTools.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,62 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace GodotSharpTools.Utils
|
||||
{
|
||||
public static class OS
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
extern static string GetPlatformName();
|
||||
|
||||
const string HaikuName = "Haiku";
|
||||
const string OSXName = "OSX";
|
||||
const string ServerName = "Server";
|
||||
const string UWPName = "UWP";
|
||||
const string WindowsName = "Windows";
|
||||
const string X11Name = "X11";
|
||||
|
||||
public static bool IsHaiku()
|
||||
{
|
||||
return HaikuName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsOSX()
|
||||
{
|
||||
return OSXName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsServer()
|
||||
{
|
||||
return ServerName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsUWP()
|
||||
{
|
||||
return UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsWindows()
|
||||
{
|
||||
return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsX11()
|
||||
{
|
||||
return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
static bool? IsUnixCache = null;
|
||||
static readonly string[] UnixPlatforms = new string[] { HaikuName, OSXName, ServerName, X11Name };
|
||||
|
||||
public static bool IsUnix()
|
||||
{
|
||||
if (IsUnixCache.HasValue)
|
||||
return IsUnixCache.Value;
|
||||
|
||||
string osName = GetPlatformName();
|
||||
IsUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase));
|
||||
return IsUnixCache.Value;
|
||||
}
|
||||
}
|
||||
}
|
356
modules/mono/editor/GodotTools/.gitignore
vendored
Normal file
356
modules/mono/editor/GodotTools/.gitignore
vendored
Normal file
|
@ -0,0 +1,356 @@
|
|||
# Rider
|
||||
.idea/
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
using Microsoft.Build.Framework;
|
||||
using GodotTools.Core;
|
||||
|
||||
namespace GodotTools.BuildLogger
|
||||
{
|
||||
public class GodotBuildLogger : ILogger
|
||||
{
|
||||
public static readonly string AssemblyPath = Path.GetFullPath(typeof(GodotBuildLogger).Assembly.Location);
|
||||
|
||||
public string Parameters { get; set; }
|
||||
public LoggerVerbosity Verbosity { get; set; }
|
||||
|
||||
public void Initialize(IEventSource eventSource)
|
||||
{
|
||||
if (null == Parameters)
|
||||
throw new LoggerException("Log directory was not set.");
|
||||
|
||||
var parameters = Parameters.Split(new[] {';'});
|
||||
|
||||
string logDir = parameters[0];
|
||||
|
||||
if (string.IsNullOrEmpty(logDir))
|
||||
throw new LoggerException("Log directory was not set.");
|
||||
|
||||
if (parameters.Length > 1)
|
||||
throw new LoggerException("Too many parameters passed.");
|
||||
|
||||
string logFile = Path.Combine(logDir, "msbuild_log.txt");
|
||||
string issuesFile = Path.Combine(logDir, "msbuild_issues.csv");
|
||||
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(logDir))
|
||||
Directory.CreateDirectory(logDir);
|
||||
|
||||
logStreamWriter = new StreamWriter(logFile);
|
||||
issuesStreamWriter = new StreamWriter(issuesFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is UnauthorizedAccessException
|
||||
|| ex is ArgumentNullException
|
||||
|| ex is PathTooLongException
|
||||
|| ex is DirectoryNotFoundException
|
||||
|| ex is NotSupportedException
|
||||
|| ex is ArgumentException
|
||||
|| ex is SecurityException
|
||||
|| ex is IOException)
|
||||
{
|
||||
throw new LoggerException("Failed to create log file: " + ex.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unexpected failure
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
eventSource.ProjectStarted += eventSource_ProjectStarted;
|
||||
eventSource.TaskStarted += eventSource_TaskStarted;
|
||||
eventSource.MessageRaised += eventSource_MessageRaised;
|
||||
eventSource.WarningRaised += eventSource_WarningRaised;
|
||||
eventSource.ErrorRaised += eventSource_ErrorRaised;
|
||||
eventSource.ProjectFinished += eventSource_ProjectFinished;
|
||||
}
|
||||
|
||||
void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
|
||||
{
|
||||
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}";
|
||||
|
||||
if (e.ProjectFile.Length > 0)
|
||||
line += $" [{e.ProjectFile}]";
|
||||
|
||||
WriteLine(line);
|
||||
|
||||
string errorLine = $@"error,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
|
||||
$@"{e.Code.CsvEscape()},{e.Message.CsvEscape()},{e.ProjectFile.CsvEscape()}";
|
||||
issuesStreamWriter.WriteLine(errorLine);
|
||||
}
|
||||
|
||||
void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
|
||||
{
|
||||
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): warning {e.Code}: {e.Message}";
|
||||
|
||||
if (!string.IsNullOrEmpty(e.ProjectFile))
|
||||
line += $" [{e.ProjectFile}]";
|
||||
|
||||
WriteLine(line);
|
||||
|
||||
string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber},{e.Code.CsvEscape()}," +
|
||||
$@"{e.Message.CsvEscape()},{(e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty)}";
|
||||
issuesStreamWriter.WriteLine(warningLine);
|
||||
}
|
||||
|
||||
private void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
|
||||
{
|
||||
// BuildMessageEventArgs adds Importance to BuildEventArgs
|
||||
// Let's take account of the verbosity setting we've been passed in deciding whether to log the message
|
||||
if (e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal)
|
||||
|| e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal)
|
||||
|| e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed))
|
||||
{
|
||||
WriteLineWithSenderAndMessage(string.Empty, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
|
||||
{
|
||||
// TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName
|
||||
// To keep this log clean, this logger will ignore these events.
|
||||
}
|
||||
|
||||
private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
|
||||
{
|
||||
WriteLine(e.Message);
|
||||
indent++;
|
||||
}
|
||||
|
||||
private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
|
||||
{
|
||||
indent--;
|
||||
WriteLine(e.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a line to the log, adding the SenderName
|
||||
/// </summary>
|
||||
private void WriteLineWithSender(string line, BuildEventArgs e)
|
||||
{
|
||||
if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Well, if the sender name is MSBuild, let's leave it out for prettiness
|
||||
WriteLine(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLine(e.SenderName + ": " + line);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a line to the log, adding the SenderName and Message
|
||||
/// (these parameters are on all MSBuild event argument objects)
|
||||
/// </summary>
|
||||
private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e)
|
||||
{
|
||||
if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Well, if the sender name is MSBuild, let's leave it out for prettiness
|
||||
WriteLine(line + e.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLine(e.SenderName + ": " + line + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteLine(string line)
|
||||
{
|
||||
for (int i = indent; i > 0; i--)
|
||||
{
|
||||
logStreamWriter.Write("\t");
|
||||
}
|
||||
|
||||
logStreamWriter.WriteLine(line);
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
logStreamWriter.Close();
|
||||
issuesStreamWriter.Close();
|
||||
}
|
||||
|
||||
private bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity)
|
||||
{
|
||||
return Verbosity >= checkVerbosity;
|
||||
}
|
||||
|
||||
private StreamWriter logStreamWriter;
|
||||
private StreamWriter issuesStreamWriter;
|
||||
private int indent;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{6CE9A984-37B1-4F8A-8FE9-609F05F071B3}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GodotTools.BuildLogger</RootNamespace>
|
||||
<AssemblyName>GodotTools.BuildLogger</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Build.Framework" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="GodotBuildLogger.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj">
|
||||
<Project>{639e48bd-44e5-4091-8edd-22d36dc0768d}</Project>
|
||||
<Name>GodotTools.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,35 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GodotTools.BuildLogger")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("Godot Engine contributors")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("6CE9A984-37B1-4F8A-8FE9-609F05F071B3")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>GodotTools.Core</RootNamespace>
|
||||
<AssemblyName>GodotTools.Core</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ProcessExtensions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="StringExtensions.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GodotTools.Core
|
||||
{
|
||||
public static class ProcessExtensions
|
||||
{
|
||||
public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
|
||||
void ProcessExited(object sender, EventArgs e)
|
||||
{
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
|
||||
process.EnableRaisingEvents = true;
|
||||
process.Exited += ProcessExited;
|
||||
|
||||
try
|
||||
{
|
||||
if (process.HasExited)
|
||||
return;
|
||||
|
||||
using (cancellationToken.Register(() => tcs.TrySetCanceled()))
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
process.Exited -= ProcessExited;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
|
||||
[assembly: AssemblyTitle("GodotTools.Core")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("Godot Engine contributors")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes are used to specify the signing key for the assembly,
|
||||
// if desired. See the Mono documentation for more information about signing.
|
||||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("")]
|
|
@ -1,7 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace GodotSharpTools
|
||||
namespace GodotTools.Core
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
|
@ -25,7 +26,7 @@ namespace GodotSharpTools
|
|||
|
||||
path = path.Replace('\\', '/');
|
||||
|
||||
string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] parts = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim();
|
||||
|
||||
|
@ -37,18 +38,40 @@ namespace GodotSharpTools
|
|||
public static bool IsAbsolutePath(this string path)
|
||||
{
|
||||
return path.StartsWith("/", StringComparison.Ordinal) ||
|
||||
path.StartsWith("\\", StringComparison.Ordinal) ||
|
||||
path.StartsWith(driveRoot, StringComparison.Ordinal);
|
||||
path.StartsWith("\\", StringComparison.Ordinal) ||
|
||||
path.StartsWith(driveRoot, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public static string CsvEscape(this string value, char delimiter = ',')
|
||||
{
|
||||
bool hasSpecialChar = value.IndexOfAny(new char[] { '\"', '\n', '\r', delimiter }) != -1;
|
||||
bool hasSpecialChar = value.IndexOfAny(new char[] {'\"', '\n', '\r', delimiter}) != -1;
|
||||
|
||||
if (hasSpecialChar)
|
||||
return "\"" + value.Replace("\"", "\"\"") + "\"";
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static string ToSafeDirName(this string dirName, bool allowDirSeparator)
|
||||
{
|
||||
var invalidChars = new List<string> {":", "*", "?", "\"", "<", ">", "|"};
|
||||
|
||||
if (allowDirSeparator)
|
||||
{
|
||||
// Directory separators are allowed, but disallow ".." to avoid going up the filesystem
|
||||
invalidChars.Add("..");
|
||||
}
|
||||
else
|
||||
{
|
||||
invalidChars.Add("/");
|
||||
}
|
||||
|
||||
string safeDirName = dirName.Replace("\\", "/").Trim();
|
||||
|
||||
foreach (string invalidChar in invalidChars)
|
||||
safeDirName = safeDirName.Replace(invalidChar, "-");
|
||||
|
||||
return safeDirName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
namespace GodotTools
|
||||
{
|
||||
public static class ApiAssemblyNames
|
||||
{
|
||||
public const string SolutionName = "GodotSharp";
|
||||
public const string Core = "GodotSharp";
|
||||
public const string Editor = "GodotSharpEditor";
|
||||
}
|
||||
|
||||
public enum ApiAssemblyType
|
||||
{
|
||||
Core,
|
||||
Editor
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public static class ApiSolutionGenerator
|
||||
{
|
||||
public static void GenerateApiSolution(string solutionDir,
|
||||
string coreProjDir, IEnumerable<string> coreCompileItems,
|
||||
string editorProjDir, IEnumerable<string> editorCompileItems)
|
||||
{
|
||||
var solution = new DotNetSolution(ApiAssemblyNames.SolutionName);
|
||||
|
||||
solution.DirectoryPath = solutionDir;
|
||||
|
||||
// GodotSharp project
|
||||
|
||||
const string coreApiAssemblyName = ApiAssemblyNames.Core;
|
||||
|
||||
string coreGuid = ProjectGenerator.GenCoreApiProject(coreProjDir, coreCompileItems);
|
||||
|
||||
var coreProjInfo = new DotNetSolution.ProjectInfo
|
||||
{
|
||||
Guid = coreGuid,
|
||||
PathRelativeToSolution = Path.Combine(coreApiAssemblyName, $"{coreApiAssemblyName}.csproj")
|
||||
};
|
||||
coreProjInfo.Configs.Add("Debug");
|
||||
coreProjInfo.Configs.Add("Release");
|
||||
|
||||
solution.AddNewProject(coreApiAssemblyName, coreProjInfo);
|
||||
|
||||
// GodotSharpEditor project
|
||||
|
||||
const string editorApiAssemblyName = ApiAssemblyNames.Editor;
|
||||
|
||||
string editorGuid = ProjectGenerator.GenEditorApiProject(editorProjDir,
|
||||
$"../{coreApiAssemblyName}/{coreApiAssemblyName}.csproj", editorCompileItems);
|
||||
|
||||
var editorProjInfo = new DotNetSolution.ProjectInfo();
|
||||
editorProjInfo.Guid = editorGuid;
|
||||
editorProjInfo.PathRelativeToSolution = Path.Combine(editorApiAssemblyName, $"{editorApiAssemblyName}.csproj");
|
||||
editorProjInfo.Configs.Add("Debug");
|
||||
editorProjInfo.Configs.Add("Release");
|
||||
|
||||
solution.AddNewProject(editorApiAssemblyName, editorProjInfo);
|
||||
|
||||
// Save solution
|
||||
|
||||
solution.Save();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
using GodotTools.Core;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public class DotNetSolution
|
||||
{
|
||||
private string directoryPath;
|
||||
private readonly Dictionary<string, ProjectInfo> projects = new Dictionary<string, ProjectInfo>();
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public string DirectoryPath
|
||||
{
|
||||
get => directoryPath;
|
||||
set => directoryPath = value.IsAbsolutePath() ? value : Path.GetFullPath(value);
|
||||
}
|
||||
|
||||
public class ProjectInfo
|
||||
{
|
||||
public string Guid;
|
||||
public string PathRelativeToSolution;
|
||||
public List<string> Configs = new List<string>();
|
||||
}
|
||||
|
||||
public void AddNewProject(string name, ProjectInfo projectInfo)
|
||||
{
|
||||
projects[name] = projectInfo;
|
||||
}
|
||||
|
||||
public bool HasProject(string name)
|
||||
{
|
||||
return projects.ContainsKey(name);
|
||||
}
|
||||
|
||||
public ProjectInfo GetProjectInfo(string name)
|
||||
{
|
||||
return projects[name];
|
||||
}
|
||||
|
||||
public bool RemoveProject(string name)
|
||||
{
|
||||
return projects.Remove(name);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
if (!Directory.Exists(DirectoryPath))
|
||||
throw new FileNotFoundException("The solution directory does not exist.");
|
||||
|
||||
string projectsDecl = string.Empty;
|
||||
string slnPlatformsCfg = string.Empty;
|
||||
string projPlatformsCfg = string.Empty;
|
||||
|
||||
bool isFirstProject = true;
|
||||
|
||||
foreach (var pair in projects)
|
||||
{
|
||||
string name = pair.Key;
|
||||
ProjectInfo projectInfo = pair.Value;
|
||||
|
||||
if (!isFirstProject)
|
||||
projectsDecl += "\n";
|
||||
|
||||
projectsDecl += string.Format(ProjectDeclaration,
|
||||
name, projectInfo.PathRelativeToSolution.Replace("/", "\\"), projectInfo.Guid);
|
||||
|
||||
for (int i = 0; i < projectInfo.Configs.Count; i++)
|
||||
{
|
||||
string config = projectInfo.Configs[i];
|
||||
|
||||
if (i != 0 || !isFirstProject)
|
||||
{
|
||||
slnPlatformsCfg += "\n";
|
||||
projPlatformsCfg += "\n";
|
||||
}
|
||||
|
||||
slnPlatformsCfg += string.Format(SolutionPlatformsConfig, config);
|
||||
projPlatformsCfg += string.Format(ProjectPlatformsConfig, projectInfo.Guid, config);
|
||||
}
|
||||
|
||||
isFirstProject = false;
|
||||
}
|
||||
|
||||
string solutionPath = Path.Combine(DirectoryPath, Name + ".sln");
|
||||
string content = string.Format(SolutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg);
|
||||
|
||||
File.WriteAllText(solutionPath, content);
|
||||
}
|
||||
|
||||
public DotNetSolution(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
const string SolutionTemplate =
|
||||
@"Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
{0}
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
{1}
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
";
|
||||
|
||||
const string ProjectDeclaration =
|
||||
@"Project(""{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}"") = ""{0}"", ""{1}"", ""{{{2}}}""
|
||||
EndProject";
|
||||
|
||||
const string SolutionPlatformsConfig =
|
||||
@" {0}|Any CPU = {0}|Any CPU";
|
||||
|
||||
const string ProjectPlatformsConfig =
|
||||
@" {{{0}}}.{1}|Any CPU.ActiveCfg = {1}|Any CPU
|
||||
{{{0}}}.{1}|Any CPU.Build.0 = {1}|Any CPU";
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>GodotSharpTools</RootNamespace>
|
||||
<AssemblyName>GodotSharpTools</AssemblyName>
|
||||
<RootNamespace>GodotTools.ProjectEditor</RootNamespace>
|
||||
<AssemblyName>GodotTools.ProjectEditor</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath>
|
||||
</PropertyGroup>
|
||||
|
@ -21,7 +21,6 @@
|
|||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
|
@ -31,25 +30,35 @@
|
|||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.Build" />
|
||||
<Reference Include="Microsoft.Build.Framework" />
|
||||
<Reference Include="DotNet.Glob, Version=2.1.1.0, Culture=neutral, PublicKeyToken=b68cc888b4f632d1, processorArchitecture=MSIL">
|
||||
<HintPath>packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath>
|
||||
<!--
|
||||
When building Godot with 'mono_glue=no' SCons will build this project alone instead of the
|
||||
entire solution. $(SolutionDir) is not defined in that case, so we need to workaround that.
|
||||
We make SCons restore the NuGet packages in the project directory instead in this case.
|
||||
-->
|
||||
<HintPath Condition=" '$(SolutionDir)' != '' ">$(SolutionDir)\packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath>
|
||||
<HintPath>$(ProjectDir)\packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath>
|
||||
<HintPath>packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath> <!-- Are you happy CI? -->
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="StringExtensions.cs" />
|
||||
<Compile Include="Build\BuildSystem.cs" />
|
||||
<Compile Include="Editor\MonoDevelopInstance.cs" />
|
||||
<Compile Include="Project\ProjectExtensions.cs" />
|
||||
<Compile Include="Project\IdentifierUtils.cs" />
|
||||
<Compile Include="Project\ProjectGenerator.cs" />
|
||||
<Compile Include="Project\ProjectUtils.cs" />
|
||||
<Compile Include="ApiAssembliesInfo.cs" />
|
||||
<Compile Include="ApiSolutionGenerator.cs" />
|
||||
<Compile Include="DotNetSolution.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Utils\OS.cs" />
|
||||
<Compile Include="Editor\GodotSharpExport.cs" />
|
||||
<Compile Include="IdentifierUtils.cs" />
|
||||
<Compile Include="ProjectExtensions.cs" />
|
||||
<Compile Include="ProjectGenerator.cs" />
|
||||
<Compile Include="ProjectUtils.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj">
|
||||
<Project>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</Project>
|
||||
<Name>GodotTools.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
</Project>
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace GodotSharpTools.Project
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public static class IdentifierUtils
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ namespace GodotSharpTools.Project
|
|||
if (string.IsNullOrEmpty(qualifiedIdentifier))
|
||||
throw new ArgumentException($"{nameof(qualifiedIdentifier)} cannot be empty", nameof(qualifiedIdentifier));
|
||||
|
||||
string[] identifiers = qualifiedIdentifier.Split(new[] { '.' });
|
||||
string[] identifiers = qualifiedIdentifier.Split('.');
|
||||
|
||||
for (int i = 0; i < identifiers.Length; i++)
|
||||
{
|
||||
|
@ -66,8 +66,6 @@ namespace GodotSharpTools.Project
|
|||
if (identifierBuilder.Length > startIndex || @char == '_')
|
||||
identifierBuilder.Append(@char);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,14 +95,14 @@ namespace GodotSharpTools.Project
|
|||
}
|
||||
else
|
||||
{
|
||||
if (_doubleUnderscoreKeywords.Contains(value))
|
||||
if (DoubleUnderscoreKeywords.Contains(value))
|
||||
return true;
|
||||
}
|
||||
|
||||
return _keywords.Contains(value);
|
||||
return Keywords.Contains(value);
|
||||
}
|
||||
|
||||
static HashSet<string> _doubleUnderscoreKeywords = new HashSet<string>
|
||||
private static readonly HashSet<string> DoubleUnderscoreKeywords = new HashSet<string>
|
||||
{
|
||||
"__arglist",
|
||||
"__makeref",
|
||||
|
@ -112,7 +110,7 @@ namespace GodotSharpTools.Project
|
|||
"__refvalue",
|
||||
};
|
||||
|
||||
static HashSet<string> _keywords = new HashSet<string>
|
||||
private static readonly HashSet<string> Keywords = new HashSet<string>
|
||||
{
|
||||
"as",
|
||||
"do",
|
|
@ -1,8 +1,9 @@
|
|||
using GodotTools.Core;
|
||||
using System;
|
||||
using DotNet.Globbing;
|
||||
using Microsoft.Build.Construction;
|
||||
|
||||
namespace GodotSharpTools.Project
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public static class ProjectExtensions
|
||||
{
|
|
@ -1,17 +1,19 @@
|
|||
using GodotTools.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Build.Construction;
|
||||
|
||||
namespace GodotSharpTools.Project
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public static class ProjectGenerator
|
||||
{
|
||||
public const string CoreApiProjectName = "GodotSharp";
|
||||
public const string EditorApiProjectName = "GodotSharpEditor";
|
||||
const string CoreApiProjectGuid = "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}";
|
||||
const string EditorApiProjectGuid = "{8FBEC238-D944-4074-8548-B3B524305905}";
|
||||
private const string CoreApiProjectName = "GodotSharp";
|
||||
private const string EditorApiProjectName = "GodotSharpEditor";
|
||||
private const string CoreApiProjectGuid = "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}";
|
||||
private const string EditorApiProjectGuid = "{8FBEC238-D944-4074-8548-B3B524305905}";
|
||||
|
||||
public static string GenCoreApiProject(string dir, string[] compileItems)
|
||||
public static string GenCoreApiProject(string dir, IEnumerable<string> compileItems)
|
||||
{
|
||||
string path = Path.Combine(dir, CoreApiProjectName + ".csproj");
|
||||
|
||||
|
@ -24,8 +26,8 @@ namespace GodotSharpTools.Project
|
|||
mainGroup.SetProperty("BaseIntermediateOutputPath", "obj");
|
||||
|
||||
GenAssemblyInfoFile(root, dir, CoreApiProjectName,
|
||||
new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProjectName + "\")]" },
|
||||
new string[] { "System.Runtime.CompilerServices" });
|
||||
new[] {"[assembly: InternalsVisibleTo(\"" + EditorApiProjectName + "\")]"},
|
||||
new[] {"System.Runtime.CompilerServices"});
|
||||
|
||||
foreach (var item in compileItems)
|
||||
{
|
||||
|
@ -37,7 +39,7 @@ namespace GodotSharpTools.Project
|
|||
return CoreApiProjectGuid;
|
||||
}
|
||||
|
||||
public static string GenEditorApiProject(string dir, string coreApiProjPath, string[] compileItems)
|
||||
public static string GenEditorApiProject(string dir, string coreApiProjPath, IEnumerable<string> compileItems)
|
||||
{
|
||||
string path = Path.Combine(dir, EditorApiProjectName + ".csproj");
|
||||
|
||||
|
@ -64,7 +66,7 @@ namespace GodotSharpTools.Project
|
|||
return EditorApiProjectGuid;
|
||||
}
|
||||
|
||||
public static string GenGameProject(string dir, string name, string[] compileItems)
|
||||
public static string GenGameProject(string dir, string name, IEnumerable<string> compileItems)
|
||||
{
|
||||
string path = Path.Combine(dir, name + ".csproj");
|
||||
|
||||
|
@ -74,6 +76,8 @@ namespace GodotSharpTools.Project
|
|||
mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
|
||||
mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
|
||||
mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
|
||||
mainGroup.SetProperty("ApiConfiguration", "Debug").Condition = " '$(Configuration)' != 'Release' ";
|
||||
mainGroup.SetProperty("ApiConfiguration", "Release").Condition = " '$(Configuration)' == 'Release' ";
|
||||
|
||||
var toolsGroup = root.AddPropertyGroup();
|
||||
toolsGroup.Condition = " '$(Configuration)|$(Platform)' == 'Tools|AnyCPU' ";
|
||||
|
@ -86,11 +90,13 @@ namespace GodotSharpTools.Project
|
|||
toolsGroup.AddProperty("ConsolePause", "false");
|
||||
|
||||
var coreApiRef = root.AddItem("Reference", CoreApiProjectName);
|
||||
coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", CoreApiProjectName + ".dll"));
|
||||
coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProjectName + ".dll"));
|
||||
coreApiRef.AddMetadata("Private", "False");
|
||||
|
||||
var editorApiRef = root.AddItem("Reference", EditorApiProjectName);
|
||||
editorApiRef.Condition = " '$(Configuration)' == 'Tools' ";
|
||||
editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", EditorApiProjectName + ".dll"));
|
||||
editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProjectName + ".dll"));
|
||||
editorApiRef.AddMetadata("Private", "False");
|
||||
|
||||
|
@ -108,7 +114,6 @@ namespace GodotSharpTools.Project
|
|||
|
||||
public static void GenAssemblyInfoFile(ProjectRootElement root, string dir, string name, string[] assemblyLines = null, string[] usingDirectives = null)
|
||||
{
|
||||
|
||||
string propertiesDir = Path.Combine(dir, "Properties");
|
||||
if (!Directory.Exists(propertiesDir))
|
||||
Directory.CreateDirectory(propertiesDir);
|
||||
|
@ -124,12 +129,9 @@ namespace GodotSharpTools.Project
|
|||
string assemblyLinesText = string.Empty;
|
||||
|
||||
if (assemblyLines != null)
|
||||
{
|
||||
foreach (var assemblyLine in assemblyLines)
|
||||
assemblyLinesText += string.Join("\n", assemblyLines) + "\n";
|
||||
}
|
||||
assemblyLinesText += string.Join("\n", assemblyLines) + "\n";
|
||||
|
||||
string content = string.Format(assemblyInfoTemplate, usingDirectivesText, name, assemblyLinesText);
|
||||
string content = string.Format(AssemblyInfoTemplate, usingDirectivesText, name, assemblyLinesText);
|
||||
|
||||
string assemblyInfoFile = Path.Combine(propertiesDir, "AssemblyInfo.cs");
|
||||
|
||||
|
@ -194,8 +196,8 @@ namespace GodotSharpTools.Project
|
|||
}
|
||||
}
|
||||
|
||||
private const string assemblyInfoTemplate =
|
||||
@"using System.Reflection;{0}
|
||||
private const string AssemblyInfoTemplate =
|
||||
@"using System.Reflection;{0}
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
|
@ -1,9 +1,10 @@
|
|||
using GodotTools.Core;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using DotNet.Globbing;
|
||||
using Microsoft.Build.Construction;
|
||||
|
||||
namespace GodotSharpTools.Project
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public static class ProjectUtils
|
||||
{
|
||||
|
@ -53,7 +54,7 @@ namespace GodotSharpTools.Project
|
|||
|
||||
var glob = Glob.Parse(normalizedInclude, globOptions);
|
||||
|
||||
// TODO Check somehow if path has no blog to avoid the following loop...
|
||||
// TODO Check somehow if path has no blob to avoid the following loop...
|
||||
|
||||
foreach (var existingFile in existingFiles)
|
||||
{
|
|
@ -4,12 +4,12 @@ using System.Runtime.CompilerServices;
|
|||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
|
||||
[assembly: AssemblyTitle("GodotSharpTools")]
|
||||
[assembly: AssemblyTitle("GodotTools.ProjectEditor")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("ignacio")]
|
||||
[assembly: AssemblyCopyright("Godot Engine contributors")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="DotNet.Glob" version="2.1.1" targetFramework="net45" />
|
||||
</packages>
|
172
modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
Normal file
172
modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
Normal file
|
@ -0,0 +1,172 @@
|
|||
using GodotTools.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using GodotTools.BuildLogger;
|
||||
using GodotTools.Internals;
|
||||
using GodotTools.Utils;
|
||||
using Directory = System.IO.Directory;
|
||||
|
||||
namespace GodotTools.Build
|
||||
{
|
||||
public static class BuildSystem
|
||||
{
|
||||
private static string GetMsBuildPath()
|
||||
{
|
||||
string msbuildPath = MsBuildFinder.FindMsBuild();
|
||||
|
||||
if (msbuildPath == null)
|
||||
throw new FileNotFoundException("Cannot find the MSBuild executable.");
|
||||
|
||||
return msbuildPath;
|
||||
}
|
||||
|
||||
private static string MonoWindowsBinDir
|
||||
{
|
||||
get
|
||||
{
|
||||
string monoWinBinDir = Path.Combine(Internal.MonoWindowsInstallRoot, "bin");
|
||||
|
||||
if (!Directory.Exists(monoWinBinDir))
|
||||
throw new FileNotFoundException("Cannot find the Windows Mono install bin directory.");
|
||||
|
||||
return monoWinBinDir;
|
||||
}
|
||||
}
|
||||
|
||||
private static Godot.EditorSettings EditorSettings =>
|
||||
GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
|
||||
|
||||
private static bool UsingMonoMsBuildOnWindows
|
||||
{
|
||||
get
|
||||
{
|
||||
if (OS.IsWindows())
|
||||
{
|
||||
return (GodotSharpBuilds.BuildTool) EditorSettings.GetSetting("mono/builds/build_tool")
|
||||
== GodotSharpBuilds.BuildTool.MsBuildMono;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool PrintBuildOutput =>
|
||||
(bool) EditorSettings.GetSetting("mono/builds/print_build_output");
|
||||
|
||||
private static Process LaunchBuild(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
|
||||
{
|
||||
var customPropertiesList = new List<string>();
|
||||
|
||||
if (customProperties != null)
|
||||
customPropertiesList.AddRange(customProperties);
|
||||
|
||||
string compilerArgs = BuildArguments(solution, config, loggerOutputDir, customPropertiesList);
|
||||
|
||||
var startInfo = new ProcessStartInfo(GetMsBuildPath(), compilerArgs);
|
||||
|
||||
bool redirectOutput = !IsDebugMsBuildRequested() && !PrintBuildOutput;
|
||||
|
||||
if (!redirectOutput || Godot.OS.IsStdoutVerbose())
|
||||
Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}");
|
||||
|
||||
startInfo.RedirectStandardOutput = redirectOutput;
|
||||
startInfo.RedirectStandardError = redirectOutput;
|
||||
startInfo.UseShellExecute = false;
|
||||
|
||||
if (UsingMonoMsBuildOnWindows)
|
||||
{
|
||||
// These environment variables are required for Mono's MSBuild to find the compilers.
|
||||
// We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
|
||||
string monoWinBinDir = MonoWindowsBinDir;
|
||||
startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
|
||||
startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
|
||||
startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
|
||||
}
|
||||
|
||||
// Needed when running from Developer Command Prompt for VS
|
||||
RemovePlatformVariable(startInfo.EnvironmentVariables);
|
||||
|
||||
var process = new Process {StartInfo = startInfo};
|
||||
|
||||
process.Start();
|
||||
|
||||
if (redirectOutput)
|
||||
{
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
}
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
public static int Build(MonoBuildInfo monoBuildInfo)
|
||||
{
|
||||
return Build(monoBuildInfo.Solution, monoBuildInfo.Configuration,
|
||||
monoBuildInfo.LogsDirPath, monoBuildInfo.CustomProperties);
|
||||
}
|
||||
|
||||
public static async Task<int> BuildAsync(MonoBuildInfo monoBuildInfo)
|
||||
{
|
||||
return await BuildAsync(monoBuildInfo.Solution, monoBuildInfo.Configuration,
|
||||
monoBuildInfo.LogsDirPath, monoBuildInfo.CustomProperties);
|
||||
}
|
||||
|
||||
public static int Build(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
|
||||
{
|
||||
using (var process = LaunchBuild(solution, config, loggerOutputDir, customProperties))
|
||||
{
|
||||
process.WaitForExit();
|
||||
|
||||
return process.ExitCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<int> BuildAsync(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
|
||||
{
|
||||
using (var process = LaunchBuild(solution, config, loggerOutputDir, customProperties))
|
||||
{
|
||||
await process.WaitForExitAsync();
|
||||
|
||||
return process.ExitCode;
|
||||
}
|
||||
}
|
||||
|
||||
private static string BuildArguments(string solution, string config, string loggerOutputDir, List<string> customProperties)
|
||||
{
|
||||
string arguments = $@"""{solution}"" /v:normal /t:Rebuild ""/p:{"Configuration=" + config}"" " +
|
||||
$@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{loggerOutputDir}""";
|
||||
|
||||
foreach (string customProperty in customProperties)
|
||||
{
|
||||
arguments += " /p:" + customProperty;
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
private static void RemovePlatformVariable(StringDictionary environmentVariables)
|
||||
{
|
||||
// EnvironmentVariables is case sensitive? Seriously?
|
||||
|
||||
var platformEnvironmentVariables = new List<string>();
|
||||
|
||||
foreach (string env in environmentVariables.Keys)
|
||||
{
|
||||
if (env.ToUpper() == "PLATFORM")
|
||||
platformEnvironmentVariables.Add(env);
|
||||
}
|
||||
|
||||
foreach (string env in platformEnvironmentVariables)
|
||||
environmentVariables.Remove(env);
|
||||
}
|
||||
|
||||
private static bool IsDebugMsBuildRequested()
|
||||
{
|
||||
return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1";
|
||||
}
|
||||
}
|
||||
}
|
210
modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
Normal file
210
modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
Normal file
|
@ -0,0 +1,210 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Godot;
|
||||
using GodotTools.Internals;
|
||||
using Directory = System.IO.Directory;
|
||||
using Environment = System.Environment;
|
||||
using File = System.IO.File;
|
||||
using Path = System.IO.Path;
|
||||
using OS = GodotTools.Utils.OS;
|
||||
|
||||
namespace GodotTools.Build
|
||||
{
|
||||
public static class MsBuildFinder
|
||||
{
|
||||
private static string _msbuildToolsPath = string.Empty;
|
||||
private static string _msbuildUnixPath = string.Empty;
|
||||
private static string _xbuildUnixPath = string.Empty;
|
||||
|
||||
public static string FindMsBuild()
|
||||
{
|
||||
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
|
||||
var buildTool = (GodotSharpBuilds.BuildTool) editorSettings.GetSetting("mono/builds/build_tool");
|
||||
|
||||
if (OS.IsWindows())
|
||||
{
|
||||
switch (buildTool)
|
||||
{
|
||||
case GodotSharpBuilds.BuildTool.MsBuildVs:
|
||||
{
|
||||
if (_msbuildToolsPath.Empty() || !File.Exists(_msbuildToolsPath))
|
||||
{
|
||||
// Try to search it again if it wasn't found last time or if it was removed from its location
|
||||
_msbuildToolsPath = FindMsBuildToolsPathOnWindows();
|
||||
|
||||
if (_msbuildToolsPath.Empty())
|
||||
{
|
||||
throw new FileNotFoundException($"Cannot find executable for '{GodotSharpBuilds.PropNameMsbuildVs}'. Tried with path: {_msbuildToolsPath}");
|
||||
}
|
||||
}
|
||||
|
||||
if (!_msbuildToolsPath.EndsWith("\\"))
|
||||
_msbuildToolsPath += "\\";
|
||||
|
||||
return Path.Combine(_msbuildToolsPath, "MSBuild.exe");
|
||||
}
|
||||
|
||||
case GodotSharpBuilds.BuildTool.MsBuildMono:
|
||||
{
|
||||
string msbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "msbuild.bat");
|
||||
|
||||
if (!File.Exists(msbuildPath))
|
||||
{
|
||||
throw new FileNotFoundException($"Cannot find executable for '{GodotSharpBuilds.PropNameMsbuildMono}'. Tried with path: {msbuildPath}");
|
||||
}
|
||||
|
||||
return msbuildPath;
|
||||
}
|
||||
|
||||
case GodotSharpBuilds.BuildTool.XBuild:
|
||||
{
|
||||
string xbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "xbuild.bat");
|
||||
|
||||
if (!File.Exists(xbuildPath))
|
||||
{
|
||||
throw new FileNotFoundException($"Cannot find executable for '{GodotSharpBuilds.PropNameXbuild}'. Tried with path: {xbuildPath}");
|
||||
}
|
||||
|
||||
return xbuildPath;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid build tool in editor settings");
|
||||
}
|
||||
}
|
||||
|
||||
if (OS.IsUnix())
|
||||
{
|
||||
if (buildTool == GodotSharpBuilds.BuildTool.XBuild)
|
||||
{
|
||||
if (_xbuildUnixPath.Empty() || !File.Exists(_xbuildUnixPath))
|
||||
{
|
||||
// Try to search it again if it wasn't found last time or if it was removed from its location
|
||||
_xbuildUnixPath = FindBuildEngineOnUnix("msbuild");
|
||||
}
|
||||
|
||||
if (_xbuildUnixPath.Empty())
|
||||
{
|
||||
throw new FileNotFoundException($"Cannot find binary for '{GodotSharpBuilds.PropNameXbuild}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_msbuildUnixPath.Empty() || !File.Exists(_msbuildUnixPath))
|
||||
{
|
||||
// Try to search it again if it wasn't found last time or if it was removed from its location
|
||||
_msbuildUnixPath = FindBuildEngineOnUnix("msbuild");
|
||||
}
|
||||
|
||||
if (_msbuildUnixPath.Empty())
|
||||
{
|
||||
throw new FileNotFoundException($"Cannot find binary for '{GodotSharpBuilds.PropNameMsbuildMono}'");
|
||||
}
|
||||
}
|
||||
|
||||
return buildTool != GodotSharpBuilds.BuildTool.XBuild ? _msbuildUnixPath : _xbuildUnixPath;
|
||||
}
|
||||
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
private static IEnumerable<string> MsBuildHintDirs
|
||||
{
|
||||
get
|
||||
{
|
||||
var result = new List<string>();
|
||||
|
||||
if (OS.IsOSX())
|
||||
{
|
||||
result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
|
||||
result.Add("/usr/local/var/homebrew/linked/mono/bin/");
|
||||
}
|
||||
|
||||
result.Add("/opt/novell/mono/bin/");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static string FindBuildEngineOnUnix(string name)
|
||||
{
|
||||
string ret = OS.PathWhich(name);
|
||||
|
||||
if (!ret.Empty())
|
||||
return ret;
|
||||
|
||||
string retFallback = OS.PathWhich($"{name}.exe");
|
||||
|
||||
if (!retFallback.Empty())
|
||||
return retFallback;
|
||||
|
||||
foreach (string hintDir in MsBuildHintDirs)
|
||||
{
|
||||
string hintPath = Path.Combine(hintDir, name);
|
||||
|
||||
if (File.Exists(hintPath))
|
||||
return hintPath;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static string FindMsBuildToolsPathOnWindows()
|
||||
{
|
||||
if (!OS.IsWindows())
|
||||
throw new PlatformNotSupportedException();
|
||||
|
||||
// Try to find 15.0 with vswhere
|
||||
|
||||
string vsWherePath = Environment.GetEnvironmentVariable(Internal.GodotIs32Bits() ? "ProgramFiles" : "ProgramFiles(x86)");
|
||||
vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
|
||||
|
||||
var vsWhereArgs = new[] {"-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"};
|
||||
|
||||
var outputArray = new Godot.Collections.Array<string>();
|
||||
int exitCode = Godot.OS.Execute(vsWherePath, vsWhereArgs,
|
||||
blocking: true, output: (Godot.Collections.Array) outputArray);
|
||||
|
||||
if (exitCode == 0)
|
||||
return string.Empty;
|
||||
|
||||
if (outputArray.Count == 0)
|
||||
return string.Empty;
|
||||
|
||||
var lines = outputArray[1].Split('\n');
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
int sepIdx = line.IndexOf(':');
|
||||
|
||||
if (sepIdx <= 0)
|
||||
continue;
|
||||
|
||||
string key = line.Substring(0, sepIdx); // No need to trim
|
||||
|
||||
if (key != "installationPath")
|
||||
continue;
|
||||
|
||||
string value = line.Substring(sepIdx + 1).StripEdges();
|
||||
|
||||
if (value.Empty())
|
||||
throw new FormatException("installationPath value is empty");
|
||||
|
||||
if (!value.EndsWith("\\"))
|
||||
value += "\\";
|
||||
|
||||
// Since VS2019, the directory is simply named "Current"
|
||||
string msbuildDir = Path.Combine(value, "MSBuild\\Current\\Bin");
|
||||
|
||||
if (Directory.Exists(msbuildDir))
|
||||
return msbuildDir;
|
||||
|
||||
// Directory name "15.0" is used in VS 2017
|
||||
return Path.Combine(value, "MSBuild\\15.0\\Bin");
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
115
modules/mono/editor/GodotTools/GodotTools/CSharpProject.cs
Normal file
115
modules/mono/editor/GodotTools/GodotTools/CSharpProject.cs
Normal file
|
@ -0,0 +1,115 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
using GodotTools.Internals;
|
||||
using GodotTools.ProjectEditor;
|
||||
using File = GodotTools.Utils.File;
|
||||
using Directory = GodotTools.Utils.Directory;
|
||||
|
||||
namespace GodotTools
|
||||
{
|
||||
public static class CSharpProject
|
||||
{
|
||||
public static string GenerateGameProject(string dir, string name, IEnumerable<string> files = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ProjectGenerator.GenGameProject(dir, name, files);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GD.PushError(e.ToString());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddItem(string projectPath, string itemType, string include)
|
||||
{
|
||||
if (!(bool) Internal.GlobalDef("mono/project/auto_update_project", true))
|
||||
return;
|
||||
|
||||
ProjectUtils.AddItemToProjectChecked(projectPath, itemType, include);
|
||||
}
|
||||
|
||||
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
private static ulong ConvertToTimestamp(this DateTime value)
|
||||
{
|
||||
TimeSpan elapsedTime = value - Epoch;
|
||||
return (ulong) elapsedTime.TotalSeconds;
|
||||
}
|
||||
|
||||
public static void GenerateScriptsMetadata(string projectPath, string outputPath)
|
||||
{
|
||||
if (File.Exists(outputPath))
|
||||
File.Delete(outputPath);
|
||||
|
||||
var oldDict = Internal.GetScriptsMetadataOrNothing();
|
||||
var newDict = new Godot.Collections.Dictionary<string, object>();
|
||||
|
||||
foreach (var includeFile in ProjectUtils.GetIncludeFiles(projectPath, "Compile"))
|
||||
{
|
||||
string projectIncludeFile = ("res://" + includeFile).SimplifyGodotPath();
|
||||
|
||||
ulong modifiedTime = File.GetLastWriteTime(projectIncludeFile).ConvertToTimestamp();
|
||||
|
||||
if (oldDict.TryGetValue(projectIncludeFile, out var oldFileVar))
|
||||
{
|
||||
var oldFileDict = (Dictionary) oldFileVar;
|
||||
|
||||
if (ulong.TryParse((string) oldFileDict["modified_time"], out ulong storedModifiedTime))
|
||||
{
|
||||
if (storedModifiedTime == modifiedTime)
|
||||
{
|
||||
// No changes so no need to parse again
|
||||
newDict[projectIncludeFile] = oldFileDict;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScriptClassParser.ParseFileOrThrow(projectIncludeFile, out var classes);
|
||||
|
||||
string searchName = System.IO.Path.GetFileNameWithoutExtension(projectIncludeFile);
|
||||
|
||||
var classDict = new Dictionary();
|
||||
|
||||
foreach (var classDecl in classes)
|
||||
{
|
||||
if (classDecl.BaseCount == 0)
|
||||
continue; // Does not inherit nor implement anything, so it can't be a script class
|
||||
|
||||
string classCmp = classDecl.Nested ?
|
||||
classDecl.Name.Substring(classDecl.Name.LastIndexOf(".", StringComparison.Ordinal) + 1) :
|
||||
classDecl.Name;
|
||||
|
||||
if (classCmp != searchName)
|
||||
continue;
|
||||
|
||||
classDict["namespace"] = classDecl.Namespace;
|
||||
classDict["class_name"] = classDecl.Name;
|
||||
classDict["nested"] = classDecl.Nested;
|
||||
break;
|
||||
}
|
||||
|
||||
if (classDict.Count == 0)
|
||||
continue; // Not found
|
||||
|
||||
newDict[projectIncludeFile] = new Dictionary {["modified_time"] = $"{modifiedTime}", ["class"] = classDict};
|
||||
}
|
||||
|
||||
if (newDict.Count > 0)
|
||||
{
|
||||
string json = JSON.Print(newDict);
|
||||
|
||||
string baseDir = outputPath.GetBaseDir();
|
||||
|
||||
if (!Directory.Exists(baseDir))
|
||||
Directory.CreateDirectory(baseDir);
|
||||
|
||||
File.WriteAllText(outputPath, json);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
396
modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs
Normal file
396
modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs
Normal file
|
@ -0,0 +1,396 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using GodotTools.Build;
|
||||
using GodotTools.Internals;
|
||||
using GodotTools.Utils;
|
||||
using Error = Godot.Error;
|
||||
using File = GodotTools.Utils.File;
|
||||
using Directory = GodotTools.Utils.Directory;
|
||||
|
||||
namespace GodotTools
|
||||
{
|
||||
public static class GodotSharpBuilds
|
||||
{
|
||||
private static readonly List<MonoBuildInfo> BuildsInProgress = new List<MonoBuildInfo>();
|
||||
|
||||
public const string PropNameMsbuildMono = "MSBuild (Mono)";
|
||||
public const string PropNameMsbuildVs = "MSBuild (VS Build Tools)";
|
||||
public const string PropNameXbuild = "xbuild (Deprecated)";
|
||||
|
||||
public const string MsBuildIssuesFileName = "msbuild_issues.csv";
|
||||
public const string MsBuildLogFileName = "msbuild_log.txt";
|
||||
|
||||
public enum BuildTool
|
||||
{
|
||||
MsBuildMono,
|
||||
MsBuildVs,
|
||||
XBuild // Deprecated
|
||||
}
|
||||
|
||||
private static void RemoveOldIssuesFile(MonoBuildInfo buildInfo)
|
||||
{
|
||||
var issuesFile = GetIssuesFilePath(buildInfo);
|
||||
|
||||
if (!File.Exists(issuesFile))
|
||||
return;
|
||||
|
||||
File.Delete(issuesFile);
|
||||
}
|
||||
|
||||
private static string _ApiFolderName(ApiAssemblyType apiType)
|
||||
{
|
||||
ulong apiHash = apiType == ApiAssemblyType.Core ?
|
||||
Internal.GetCoreApiHash() :
|
||||
Internal.GetEditorApiHash();
|
||||
return $"{apiHash}_{BindingsGenerator.Version}_{BindingsGenerator.CsGlueVersion}";
|
||||
}
|
||||
|
||||
private static void ShowBuildErrorDialog(string message)
|
||||
{
|
||||
GodotSharpEditor.Instance.ShowErrorDialog(message, "Build error");
|
||||
GodotSharpEditor.Instance.MonoBottomPanel.ShowBuildTab();
|
||||
}
|
||||
|
||||
public static void RestartBuild(MonoBuildTab buildTab) => throw new NotImplementedException();
|
||||
public static void StopBuild(MonoBuildTab buildTab) => throw new NotImplementedException();
|
||||
|
||||
private static string GetLogFilePath(MonoBuildInfo buildInfo)
|
||||
{
|
||||
return Path.Combine(buildInfo.LogsDirPath, MsBuildLogFileName);
|
||||
}
|
||||
|
||||
private static string GetIssuesFilePath(MonoBuildInfo buildInfo)
|
||||
{
|
||||
return Path.Combine(Godot.ProjectSettings.LocalizePath(buildInfo.LogsDirPath), MsBuildIssuesFileName);
|
||||
}
|
||||
|
||||
private static void PrintVerbose(string text)
|
||||
{
|
||||
if (Godot.OS.IsStdoutVerbose())
|
||||
Godot.GD.Print(text);
|
||||
}
|
||||
|
||||
public static bool Build(MonoBuildInfo buildInfo)
|
||||
{
|
||||
if (BuildsInProgress.Contains(buildInfo))
|
||||
throw new InvalidOperationException("A build is already in progress");
|
||||
|
||||
BuildsInProgress.Add(buildInfo);
|
||||
|
||||
try
|
||||
{
|
||||
MonoBuildTab buildTab = GodotSharpEditor.Instance.MonoBottomPanel.GetBuildTabFor(buildInfo);
|
||||
buildTab.OnBuildStart();
|
||||
|
||||
// Required in order to update the build tasks list
|
||||
Internal.GodotMainIteration();
|
||||
|
||||
try
|
||||
{
|
||||
RemoveOldIssuesFile(buildInfo);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
|
||||
Console.Error.WriteLine(e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int exitCode = BuildSystem.Build(buildInfo);
|
||||
|
||||
if (exitCode != 0)
|
||||
PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
|
||||
|
||||
buildTab.OnBuildExit(exitCode == 0 ? MonoBuildTab.BuildResults.Success : MonoBuildTab.BuildResults.Error);
|
||||
|
||||
return exitCode == 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
|
||||
Console.Error.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
BuildsInProgress.Remove(buildInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> BuildAsync(MonoBuildInfo buildInfo)
|
||||
{
|
||||
if (BuildsInProgress.Contains(buildInfo))
|
||||
throw new InvalidOperationException("A build is already in progress");
|
||||
|
||||
BuildsInProgress.Add(buildInfo);
|
||||
|
||||
try
|
||||
{
|
||||
MonoBuildTab buildTab = GodotSharpEditor.Instance.MonoBottomPanel.GetBuildTabFor(buildInfo);
|
||||
|
||||
try
|
||||
{
|
||||
RemoveOldIssuesFile(buildInfo);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
|
||||
Console.Error.WriteLine(e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int exitCode = await BuildSystem.BuildAsync(buildInfo);
|
||||
|
||||
if (exitCode != 0)
|
||||
PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
|
||||
|
||||
buildTab.OnBuildExit(exitCode == 0 ? MonoBuildTab.BuildResults.Success : MonoBuildTab.BuildResults.Error);
|
||||
|
||||
return exitCode == 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
|
||||
Console.Error.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
BuildsInProgress.Remove(buildInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool BuildApiSolution(string apiSlnDir, string config)
|
||||
{
|
||||
string apiSlnFile = Path.Combine(apiSlnDir, $"{ApiAssemblyNames.SolutionName}.sln");
|
||||
|
||||
string coreApiAssemblyDir = Path.Combine(apiSlnDir, ApiAssemblyNames.Core, "bin", config);
|
||||
string coreApiAssemblyFile = Path.Combine(coreApiAssemblyDir, $"{ApiAssemblyNames.Core}.dll");
|
||||
|
||||
string editorApiAssemblyDir = Path.Combine(apiSlnDir, ApiAssemblyNames.Editor, "bin", config);
|
||||
string editorApiAssemblyFile = Path.Combine(editorApiAssemblyDir, $"{ApiAssemblyNames.Editor}.dll");
|
||||
|
||||
if (File.Exists(coreApiAssemblyFile) && File.Exists(editorApiAssemblyFile))
|
||||
return true; // The assemblies are in the output folder; assume the solution is already built
|
||||
|
||||
var apiBuildInfo = new MonoBuildInfo(apiSlnFile, config);
|
||||
|
||||
// TODO Replace this global NoWarn with '#pragma warning' directives on generated files,
|
||||
// once we start to actively document manually maintained C# classes
|
||||
apiBuildInfo.CustomProperties.Add("NoWarn=1591"); // Ignore missing documentation warnings
|
||||
|
||||
if (Build(apiBuildInfo))
|
||||
return true;
|
||||
|
||||
ShowBuildErrorDialog($"Failed to build {ApiAssemblyNames.SolutionName} solution.");
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool CopyApiAssembly(string srcDir, string dstDir, string assemblyName, ApiAssemblyType apiType)
|
||||
{
|
||||
// Create destination directory if needed
|
||||
if (!Directory.Exists(dstDir))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(dstDir);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
ShowBuildErrorDialog($"Failed to create destination directory for the API assemblies. Exception message: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
string assemblyFile = assemblyName + ".dll";
|
||||
string assemblySrc = Path.Combine(srcDir, assemblyFile);
|
||||
string assemblyDst = Path.Combine(dstDir, assemblyFile);
|
||||
|
||||
if (!File.Exists(assemblyDst) || File.GetLastWriteTime(assemblySrc) > File.GetLastWriteTime(assemblyDst) ||
|
||||
Internal.MetadataIsApiAssemblyInvalidated(apiType))
|
||||
{
|
||||
string xmlFile = $"{assemblyName}.xml";
|
||||
string pdbFile = $"{assemblyName}.pdb";
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(Path.Combine(srcDir, xmlFile), Path.Combine(dstDir, xmlFile));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Godot.GD.PushWarning(e.ToString());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(Path.Combine(srcDir, pdbFile), Path.Combine(dstDir, pdbFile));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Godot.GD.PushWarning(e.ToString());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(assemblySrc, assemblyDst);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
ShowBuildErrorDialog($"Failed to copy {assemblyFile}. Exception message: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
Internal.MetadataSetApiAssemblyInvalidated(apiType, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool MakeApiAssembly(ApiAssemblyType apiType, string config)
|
||||
{
|
||||
string apiName = apiType == ApiAssemblyType.Core ? ApiAssemblyNames.Core : ApiAssemblyNames.Editor;
|
||||
|
||||
string editorPrebuiltApiDir = Path.Combine(GodotSharpDirs.DataEditorPrebuiltApiDir, config);
|
||||
string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, config);
|
||||
|
||||
if (File.Exists(Path.Combine(editorPrebuiltApiDir, $"{apiName}.dll")))
|
||||
{
|
||||
using (var copyProgress = new EditorProgress("mono_copy_prebuilt_api_assembly", $"Copying prebuilt {apiName} assembly...", 1))
|
||||
{
|
||||
copyProgress.Step($"Copying {apiName} assembly", 0);
|
||||
return CopyApiAssembly(editorPrebuiltApiDir, resAssembliesDir, apiName, apiType);
|
||||
}
|
||||
}
|
||||
|
||||
const string apiSolutionName = ApiAssemblyNames.SolutionName;
|
||||
|
||||
using (var pr = new EditorProgress($"mono_build_release_{apiSolutionName}", $"Building {apiSolutionName} solution...", 3))
|
||||
{
|
||||
pr.Step($"Generating {apiSolutionName} solution", 0);
|
||||
|
||||
string apiSlnDir = Path.Combine(GodotSharpDirs.MonoSolutionsDir, _ApiFolderName(ApiAssemblyType.Core));
|
||||
string apiSlnFile = Path.Combine(apiSlnDir, $"{apiSolutionName}.sln");
|
||||
|
||||
if (!Directory.Exists(apiSlnDir) || !File.Exists(apiSlnFile))
|
||||
{
|
||||
var bindingsGenerator = new BindingsGenerator();
|
||||
|
||||
if (!Godot.OS.IsStdoutVerbose())
|
||||
bindingsGenerator.LogPrintEnabled = false;
|
||||
|
||||
Error err = bindingsGenerator.GenerateCsApi(apiSlnDir);
|
||||
if (err != Error.Ok)
|
||||
{
|
||||
ShowBuildErrorDialog($"Failed to generate {apiSolutionName} solution. Error: {err}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pr.Step($"Building {apiSolutionName} solution", 1);
|
||||
|
||||
if (!BuildApiSolution(apiSlnDir, config))
|
||||
return false;
|
||||
|
||||
pr.Step($"Copying {apiName} assembly", 2);
|
||||
|
||||
// Copy the built assembly to the assemblies directory
|
||||
string apiAssemblyDir = Path.Combine(apiSlnDir, apiName, "bin", config);
|
||||
if (!CopyApiAssembly(apiAssemblyDir, resAssembliesDir, apiName, apiType))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool BuildProjectBlocking(string config, IEnumerable<string> godotDefines)
|
||||
{
|
||||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
return true; // No solution to build
|
||||
|
||||
string apiConfig = config == "Release" ? "Release" : "Debug";
|
||||
|
||||
if (!MakeApiAssembly(ApiAssemblyType.Core, apiConfig))
|
||||
return false;
|
||||
|
||||
if (!MakeApiAssembly(ApiAssemblyType.Editor, apiConfig))
|
||||
return false;
|
||||
|
||||
using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
|
||||
{
|
||||
pr.Step("Building project solution", 0);
|
||||
|
||||
var buildInfo = new MonoBuildInfo(GodotSharpDirs.ProjectSlnPath, config);
|
||||
|
||||
// Add Godot defines
|
||||
string constants = OS.IsWindows() ? "GodotDefineConstants=\"" : "GodotDefineConstants=\\\"";
|
||||
|
||||
foreach (var godotDefine in godotDefines)
|
||||
constants += $"GODOT_{godotDefine.ToUpper().Replace("-", "_").Replace(" ", "_").Replace(";", "_")};";
|
||||
|
||||
if (Internal.GodotIsRealTDouble())
|
||||
constants += "GODOT_REAL_T_IS_DOUBLE;";
|
||||
|
||||
constants += OS.IsWindows() ? "\"" : "\\\"";
|
||||
|
||||
buildInfo.CustomProperties.Add(constants);
|
||||
|
||||
if (!Build(buildInfo))
|
||||
{
|
||||
ShowBuildErrorDialog("Failed to build project solution");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool EditorBuildCallback()
|
||||
{
|
||||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
return true; // No solution to build
|
||||
|
||||
string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor");
|
||||
string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player");
|
||||
|
||||
CSharpProject.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath);
|
||||
|
||||
if (File.Exists(editorScriptsMetadataPath))
|
||||
File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
|
||||
|
||||
var godotDefines = new[]
|
||||
{
|
||||
Godot.OS.GetName(),
|
||||
Internal.GodotIs32Bits() ? "32" : "64"
|
||||
};
|
||||
|
||||
return BuildProjectBlocking("Tools", godotDefines);
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
// Build tool settings
|
||||
|
||||
Internal.EditorDef("mono/builds/build_tool", OS.IsWindows() ? BuildTool.MsBuildVs : BuildTool.MsBuildMono);
|
||||
|
||||
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
|
||||
|
||||
editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
|
||||
{
|
||||
["type"] = Godot.Variant.Type.Int,
|
||||
["name"] = "mono/builds/build_tool",
|
||||
["hint"] = Godot.PropertyHint.Enum,
|
||||
["hint_string"] = OS.IsWindows() ?
|
||||
$"{PropNameMsbuildMono},{PropNameMsbuildVs},{PropNameXbuild}" :
|
||||
$"{PropNameMsbuildMono},{PropNameXbuild}"
|
||||
});
|
||||
|
||||
Internal.EditorDef("mono/builds/print_build_output", false);
|
||||
}
|
||||
}
|
||||
}
|
538
modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
Normal file
538
modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
Normal file
|
@ -0,0 +1,538 @@
|
|||
using Godot;
|
||||
using GodotTools.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using GodotTools.Internals;
|
||||
using GodotTools.ProjectEditor;
|
||||
using File = GodotTools.Utils.File;
|
||||
using Path = System.IO.Path;
|
||||
using OS = GodotTools.Utils.OS;
|
||||
|
||||
namespace GodotTools
|
||||
{
|
||||
public class GodotSharpEditor : EditorPlugin, ISerializationListener
|
||||
{
|
||||
private EditorSettings editorSettings;
|
||||
|
||||
private PopupMenu menuPopup;
|
||||
|
||||
private AcceptDialog errorDialog;
|
||||
private AcceptDialog aboutDialog;
|
||||
private CheckBox aboutDialogCheckBox;
|
||||
|
||||
private ToolButton bottomPanelBtn;
|
||||
|
||||
private MonoDevelopInstance monoDevelopInstance;
|
||||
private MonoDevelopInstance visualStudioForMacInstance;
|
||||
|
||||
public MonoBottomPanel MonoBottomPanel { get; private set; }
|
||||
|
||||
private bool CreateProjectSolution()
|
||||
{
|
||||
using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...", 2)) // TTR("Generating solution...")
|
||||
{
|
||||
pr.Step("Generating C# project..."); // TTR("Generating C# project...")
|
||||
|
||||
string resourceDir = ProjectSettings.GlobalizePath("res://");
|
||||
|
||||
string path = resourceDir;
|
||||
string name = (string) ProjectSettings.GetSetting("application/config/name");
|
||||
if (name.Empty())
|
||||
name = "UnnamedProject";
|
||||
|
||||
string guid = CSharpProject.GenerateGameProject(path, name);
|
||||
|
||||
if (guid.Length > 0)
|
||||
{
|
||||
var solution = new DotNetSolution(name)
|
||||
{
|
||||
DirectoryPath = path
|
||||
};
|
||||
|
||||
var projectInfo = new DotNetSolution.ProjectInfo
|
||||
{
|
||||
Guid = guid,
|
||||
PathRelativeToSolution = name + ".csproj",
|
||||
Configs = new List<string> {"Debug", "Release", "Tools"}
|
||||
};
|
||||
|
||||
solution.AddNewProject(name, projectInfo);
|
||||
|
||||
try
|
||||
{
|
||||
solution.Save();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
ShowErrorDialog($"Failed to save solution. Exception message: {e.Message}"); // TTR
|
||||
return false;
|
||||
}
|
||||
|
||||
string apiConfig = "Debug";
|
||||
|
||||
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Core, apiConfig))
|
||||
return false;
|
||||
|
||||
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Editor, apiConfig))
|
||||
return false;
|
||||
|
||||
pr.Step("Done"); // TTR("Done")
|
||||
|
||||
// Here, after all calls to progress_task_step
|
||||
CallDeferred(nameof(_RemoveCreateSlnMenuOption));
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowErrorDialog("Failed to create C# project."); // TTR
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static int _makeApiSolutionsAttempts = 100;
|
||||
private static bool _makeApiSolutionsRecursionGuard = false;
|
||||
|
||||
private void _MakeApiSolutionsIfNeeded()
|
||||
{
|
||||
// I'm sick entirely of ProgressDialog
|
||||
|
||||
if (Internal.IsMessageQueueFlushing() || Engine.GetMainLoop() == null)
|
||||
{
|
||||
if (_makeApiSolutionsAttempts == 0) // This better never happen or I swear...
|
||||
throw new TimeoutException();
|
||||
|
||||
if (Engine.GetMainLoop() != null)
|
||||
{
|
||||
if (!Engine.GetMainLoop().IsConnected("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded)))
|
||||
Engine.GetMainLoop().Connect("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded));
|
||||
}
|
||||
else
|
||||
{
|
||||
CallDeferred(nameof(_MakeApiSolutionsIfNeededImpl));
|
||||
}
|
||||
|
||||
_makeApiSolutionsAttempts--;
|
||||
return;
|
||||
}
|
||||
|
||||
// Recursion guard needed because signals don't play well with ProgressDialog either, but unlike
|
||||
// the message queue, with signals the collateral damage should be minimal in the worst case.
|
||||
if (!_makeApiSolutionsRecursionGuard)
|
||||
{
|
||||
_makeApiSolutionsRecursionGuard = true;
|
||||
|
||||
// Oneshot signals don't play well with ProgressDialog either, so we do it this way instead
|
||||
if (Engine.GetMainLoop().IsConnected("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded)))
|
||||
Engine.GetMainLoop().Disconnect("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded));
|
||||
|
||||
_MakeApiSolutionsIfNeededImpl();
|
||||
|
||||
_makeApiSolutionsRecursionGuard = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void _MakeApiSolutionsIfNeededImpl()
|
||||
{
|
||||
// If the project has a solution and C# project make sure the API assemblies are present and up to date
|
||||
|
||||
string api_config = "Debug";
|
||||
string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, api_config);
|
||||
|
||||
if (!File.Exists(Path.Combine(resAssembliesDir, $"{ApiAssemblyNames.Core}.dll")) ||
|
||||
Internal.MetadataIsApiAssemblyInvalidated(ApiAssemblyType.Core))
|
||||
{
|
||||
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Core, api_config))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(Path.Combine(resAssembliesDir, $"{ApiAssemblyNames.Editor}.dll")) ||
|
||||
Internal.MetadataIsApiAssemblyInvalidated(ApiAssemblyType.Editor))
|
||||
{
|
||||
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Editor, api_config))
|
||||
return; // Redundant? I don't think so!
|
||||
}
|
||||
}
|
||||
|
||||
private void _RemoveCreateSlnMenuOption()
|
||||
{
|
||||
menuPopup.RemoveItem(menuPopup.GetItemIndex((int) MenuOptions.CreateSln));
|
||||
bottomPanelBtn.Show();
|
||||
}
|
||||
|
||||
private void _ShowAboutDialog()
|
||||
{
|
||||
bool showOnStart = (bool) editorSettings.GetSetting("mono/editor/show_info_on_start");
|
||||
aboutDialogCheckBox.Pressed = showOnStart;
|
||||
aboutDialog.PopupCenteredMinsize();
|
||||
}
|
||||
|
||||
private void _ToggleAboutDialogOnStart(bool enabled)
|
||||
{
|
||||
bool showOnStart = (bool) editorSettings.GetSetting("mono/editor/show_info_on_start");
|
||||
if (showOnStart != enabled)
|
||||
editorSettings.SetSetting("mono/editor/show_info_on_start", enabled);
|
||||
}
|
||||
|
||||
private void _MenuOptionPressed(MenuOptions id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case MenuOptions.CreateSln:
|
||||
CreateProjectSolution();
|
||||
break;
|
||||
case MenuOptions.AboutCSharp:
|
||||
_ShowAboutDialog();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid menu option");
|
||||
}
|
||||
}
|
||||
|
||||
private void _BuildSolutionPressed()
|
||||
{
|
||||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
{
|
||||
if (!CreateProjectSolution())
|
||||
return; // Failed to create solution
|
||||
}
|
||||
|
||||
Instance.MonoBottomPanel.BuildProjectPressed();
|
||||
}
|
||||
|
||||
public override void _Notification(int what)
|
||||
{
|
||||
base._Notification(what);
|
||||
|
||||
if (what == NotificationReady)
|
||||
{
|
||||
bool showInfoDialog = (bool) editorSettings.GetSetting("mono/editor/show_info_on_start");
|
||||
if (showInfoDialog)
|
||||
{
|
||||
aboutDialog.PopupExclusive = true;
|
||||
_ShowAboutDialog();
|
||||
// Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on.
|
||||
aboutDialog.PopupExclusive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum MenuOptions
|
||||
{
|
||||
CreateSln,
|
||||
AboutCSharp,
|
||||
}
|
||||
|
||||
public enum ExternalEditor
|
||||
{
|
||||
None,
|
||||
VisualStudio, // TODO (Windows-only)
|
||||
VisualStudioForMac, // Mac-only
|
||||
MonoDevelop,
|
||||
VsCode
|
||||
}
|
||||
|
||||
public void ShowErrorDialog(string message, string title = "Error")
|
||||
{
|
||||
errorDialog.WindowTitle = title;
|
||||
errorDialog.DialogText = message;
|
||||
errorDialog.PopupCenteredMinsize();
|
||||
}
|
||||
|
||||
private static string _vsCodePath = string.Empty;
|
||||
|
||||
private static readonly string[] VsCodeNames =
|
||||
{
|
||||
"code", "code-oss", "vscode", "vscode-oss", "visual-studio-code", "visual-studio-code-oss"
|
||||
};
|
||||
|
||||
public Error OpenInExternalEditor(Script script, int line, int col)
|
||||
{
|
||||
var editor = (ExternalEditor) editorSettings.GetSetting("mono/editor/external_editor");
|
||||
|
||||
switch (editor)
|
||||
{
|
||||
case ExternalEditor.VsCode:
|
||||
{
|
||||
if (_vsCodePath.Empty() || !File.Exists(_vsCodePath))
|
||||
{
|
||||
// Try to search it again if it wasn't found last time or if it was removed from its location
|
||||
_vsCodePath = VsCodeNames.SelectFirstNotNull(OS.PathWhich, orElse: string.Empty);
|
||||
}
|
||||
|
||||
var args = new List<string>();
|
||||
|
||||
bool osxAppBundleInstalled = false;
|
||||
|
||||
if (OS.IsOSX())
|
||||
{
|
||||
// The package path is '/Applications/Visual Studio Code.app'
|
||||
const string vscodeBundleId = "com.microsoft.VSCode";
|
||||
|
||||
osxAppBundleInstalled = Internal.IsOsxAppBundleInstalled(vscodeBundleId);
|
||||
|
||||
if (osxAppBundleInstalled)
|
||||
{
|
||||
args.Add("-b");
|
||||
args.Add(vscodeBundleId);
|
||||
|
||||
// The reusing of existing windows made by the 'open' command might not choose a wubdiw that is
|
||||
// editing our folder. It's better to ask for a new window and let VSCode do the window management.
|
||||
args.Add("-n");
|
||||
|
||||
// The open process must wait until the application finishes (which is instant in VSCode's case)
|
||||
args.Add("--wait-apps");
|
||||
|
||||
args.Add("--args");
|
||||
}
|
||||
}
|
||||
|
||||
var resourcePath = ProjectSettings.GlobalizePath("res://");
|
||||
args.Add(resourcePath);
|
||||
|
||||
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
|
||||
|
||||
if (line >= 0)
|
||||
{
|
||||
args.Add("-g");
|
||||
args.Add($"{scriptPath}:{line + 1}:{col}");
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Add(scriptPath);
|
||||
}
|
||||
|
||||
string command;
|
||||
|
||||
if (OS.IsOSX())
|
||||
{
|
||||
if (!osxAppBundleInstalled && _vsCodePath.Empty())
|
||||
{
|
||||
GD.PushError("Cannot find code editor: VSCode");
|
||||
return Error.FileNotFound;
|
||||
}
|
||||
|
||||
command = osxAppBundleInstalled ? "/usr/bin/open" : _vsCodePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_vsCodePath.Empty())
|
||||
{
|
||||
GD.PushError("Cannot find code editor: VSCode");
|
||||
return Error.FileNotFound;
|
||||
}
|
||||
|
||||
command = _vsCodePath;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
OS.RunProcess(command, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GD.PushError($"Error when trying to run code editor: VSCode. Exception message: '{e.Message}'");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ExternalEditor.VisualStudioForMac:
|
||||
goto case ExternalEditor.MonoDevelop;
|
||||
case ExternalEditor.MonoDevelop:
|
||||
{
|
||||
MonoDevelopInstance GetMonoDevelopInstance(string solutionPath)
|
||||
{
|
||||
if (OS.IsOSX() && editor == ExternalEditor.VisualStudioForMac)
|
||||
{
|
||||
if (visualStudioForMacInstance == null)
|
||||
visualStudioForMacInstance = new MonoDevelopInstance(solutionPath, MonoDevelopInstance.EditorId.VisualStudioForMac);
|
||||
|
||||
return visualStudioForMacInstance;
|
||||
}
|
||||
|
||||
if (monoDevelopInstance == null)
|
||||
monoDevelopInstance = new MonoDevelopInstance(solutionPath, MonoDevelopInstance.EditorId.MonoDevelop);
|
||||
|
||||
return monoDevelopInstance;
|
||||
}
|
||||
|
||||
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
|
||||
|
||||
if (line >= 0)
|
||||
scriptPath += $";{line + 1};{col}";
|
||||
|
||||
GetMonoDevelopInstance(GodotSharpDirs.ProjectSlnPath).Execute(scriptPath);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ExternalEditor.None:
|
||||
return Error.Unavailable;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
return Error.Ok;
|
||||
}
|
||||
|
||||
public bool OverridesExternalEditor()
|
||||
{
|
||||
return (ExternalEditor) editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditor.None;
|
||||
}
|
||||
|
||||
public override bool Build()
|
||||
{
|
||||
return GodotSharpBuilds.EditorBuildCallback();
|
||||
}
|
||||
|
||||
public override void EnablePlugin()
|
||||
{
|
||||
base.EnablePlugin();
|
||||
|
||||
if (Instance != null)
|
||||
throw new InvalidOperationException();
|
||||
Instance = this;
|
||||
|
||||
var editorInterface = GetEditorInterface();
|
||||
var editorBaseControl = editorInterface.GetBaseControl();
|
||||
|
||||
editorSettings = editorInterface.GetEditorSettings();
|
||||
|
||||
errorDialog = new AcceptDialog();
|
||||
editorBaseControl.AddChild(errorDialog);
|
||||
|
||||
MonoBottomPanel = new MonoBottomPanel();
|
||||
|
||||
bottomPanelBtn = AddControlToBottomPanel(MonoBottomPanel, "Mono"); // TTR("Mono")
|
||||
|
||||
AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
|
||||
|
||||
menuPopup = new PopupMenu();
|
||||
menuPopup.Hide();
|
||||
menuPopup.SetAsToplevel(true);
|
||||
|
||||
AddToolSubmenuItem("Mono", menuPopup);
|
||||
|
||||
// TODO: Remove or edit this info dialog once Mono support is no longer in alpha
|
||||
{
|
||||
menuPopup.AddItem("About C# support", (int) MenuOptions.AboutCSharp); // TTR("About C# support")
|
||||
aboutDialog = new AcceptDialog();
|
||||
editorBaseControl.AddChild(aboutDialog);
|
||||
aboutDialog.WindowTitle = "Important: C# support is not feature-complete";
|
||||
|
||||
// We don't use DialogText as the default AcceptDialog Label doesn't play well with the TextureRect and CheckBox
|
||||
// we'll add. Instead we add containers and a new autowrapped Label inside.
|
||||
|
||||
// Main VBoxContainer (icon + label on top, checkbox at bottom)
|
||||
var aboutVBox = new VBoxContainer();
|
||||
aboutDialog.AddChild(aboutVBox);
|
||||
|
||||
// HBoxContainer for icon + label
|
||||
var aboutHBox = new HBoxContainer();
|
||||
aboutVBox.AddChild(aboutHBox);
|
||||
|
||||
var aboutIcon = new TextureRect();
|
||||
aboutIcon.Texture = aboutIcon.GetIcon("NodeWarning", "EditorIcons");
|
||||
aboutHBox.AddChild(aboutIcon);
|
||||
|
||||
var aboutLabel = new Label();
|
||||
aboutHBox.AddChild(aboutLabel);
|
||||
aboutLabel.RectMinSize = new Vector2(600, 150) * Internal.EditorScale;
|
||||
aboutLabel.SizeFlagsVertical = (int) Control.SizeFlags.ExpandFill;
|
||||
aboutLabel.Autowrap = true;
|
||||
aboutLabel.Text =
|
||||
"C# support in Godot Engine is in late alpha stage and, while already usable, " +
|
||||
"it is not meant for use in production.\n\n" +
|
||||
"Projects can be exported to Linux, macOS and Windows, but not yet to mobile or web platforms. " +
|
||||
"Bugs and usability issues will be addressed gradually over future releases, " +
|
||||
"potentially including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
|
||||
"If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, MSBuild version, IDE, etc.:\n\n" +
|
||||
" https://github.com/godotengine/godot/issues\n\n" +
|
||||
"Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
|
||||
|
||||
Internal.EditorDef("mono/editor/show_info_on_start", true);
|
||||
|
||||
// CheckBox in main container
|
||||
aboutDialogCheckBox = new CheckBox {Text = "Show this warning when starting the editor"};
|
||||
aboutDialogCheckBox.Connect("toggled", this, nameof(_ToggleAboutDialogOnStart));
|
||||
aboutVBox.AddChild(aboutDialogCheckBox);
|
||||
}
|
||||
|
||||
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
|
||||
{
|
||||
// Defer this task because EditorProgress calls Main::iterarion() and the main loop is not yet initialized.
|
||||
CallDeferred(nameof(_MakeApiSolutionsIfNeeded));
|
||||
}
|
||||
else
|
||||
{
|
||||
bottomPanelBtn.Hide();
|
||||
menuPopup.AddItem("Create C# solution", (int) MenuOptions.CreateSln); // TTR("Create C# solution")
|
||||
}
|
||||
|
||||
menuPopup.Connect("id_pressed", this, nameof(_MenuOptionPressed));
|
||||
|
||||
var buildButton = new ToolButton
|
||||
{
|
||||
Text = "Build",
|
||||
HintTooltip = "Build solution",
|
||||
FocusMode = Control.FocusModeEnum.None
|
||||
};
|
||||
buildButton.Connect("pressed", this, nameof(_BuildSolutionPressed));
|
||||
AddControlToContainer(CustomControlContainer.Toolbar, buildButton);
|
||||
|
||||
// External editor settings
|
||||
Internal.EditorDef("mono/editor/external_editor", ExternalEditor.None);
|
||||
|
||||
string settingsHintStr = "Disabled";
|
||||
|
||||
if (OS.IsWindows())
|
||||
{
|
||||
settingsHintStr += $",MonoDevelop:{(int) ExternalEditor.MonoDevelop}" +
|
||||
$",Visual Studio Code:{(int) ExternalEditor.VsCode}";
|
||||
}
|
||||
else if (OS.IsOSX())
|
||||
{
|
||||
settingsHintStr += $",Visual Studio:{(int) ExternalEditor.VisualStudioForMac}" +
|
||||
$",MonoDevelop:{(int) ExternalEditor.MonoDevelop}" +
|
||||
$",Visual Studio Code:{(int) ExternalEditor.VsCode}";
|
||||
}
|
||||
else if (OS.IsUnix())
|
||||
{
|
||||
settingsHintStr += $",MonoDevelop:{(int) ExternalEditor.MonoDevelop}" +
|
||||
$",Visual Studio Code:{(int) ExternalEditor.VsCode}";
|
||||
}
|
||||
|
||||
editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
|
||||
{
|
||||
["type"] = Variant.Type.Int,
|
||||
["name"] = "mono/editor/external_editor",
|
||||
["hint"] = PropertyHint.Enum,
|
||||
["hint_string"] = settingsHintStr
|
||||
});
|
||||
|
||||
// Export plugin
|
||||
AddExportPlugin(new GodotSharpExport());
|
||||
|
||||
GodotSharpBuilds.Initialize();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
// Singleton
|
||||
|
||||
public static GodotSharpEditor Instance { get; private set; }
|
||||
|
||||
private GodotSharpEditor()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
197
modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs
Normal file
197
modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs
Normal file
|
@ -0,0 +1,197 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GodotTools.Core;
|
||||
using GodotTools.Internals;
|
||||
using Directory = GodotTools.Utils.Directory;
|
||||
using File = GodotTools.Utils.File;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace GodotTools
|
||||
{
|
||||
public class GodotSharpExport : EditorExportPlugin
|
||||
{
|
||||
private void AddFile(string srcPath, string dstPath, bool remap = false)
|
||||
{
|
||||
AddFile(dstPath, File.ReadAllBytes(srcPath), remap);
|
||||
}
|
||||
|
||||
public override void _ExportFile(string path, string type, string[] features)
|
||||
{
|
||||
base._ExportFile(path, type, features);
|
||||
|
||||
if (type != Internal.CSharpLanguageType)
|
||||
return;
|
||||
|
||||
if (Path.GetExtension(path) != $".{Internal.CSharpLanguageExtension}")
|
||||
throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path));
|
||||
|
||||
// TODO What if the source file is not part of the game's C# project
|
||||
|
||||
bool includeScriptsContent = (bool) ProjectSettings.GetSetting("mono/export/include_scripts_content");
|
||||
|
||||
if (!includeScriptsContent)
|
||||
{
|
||||
// We don't want to include the source code on exported games
|
||||
AddFile(path, new byte[] { }, remap: false);
|
||||
Skip();
|
||||
}
|
||||
}
|
||||
|
||||
public override void _ExportBegin(string[] features, bool isDebug, string path, int flags)
|
||||
{
|
||||
base._ExportBegin(features, isDebug, path, flags);
|
||||
|
||||
try
|
||||
{
|
||||
_ExportBeginImpl(features, isDebug, path, flags);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GD.PushError($"Failed to export project. Exception message: {e.Message}");
|
||||
Console.Error.WriteLine(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
|
||||
{
|
||||
// TODO Right now there is no way to stop the export process with an error
|
||||
|
||||
if (File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
{
|
||||
string buildConfig = isDebug ? "Debug" : "Release";
|
||||
|
||||
string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
|
||||
CSharpProject.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);
|
||||
|
||||
AddFile(scriptsMetadataPath, scriptsMetadataPath);
|
||||
|
||||
// Turn export features into defines
|
||||
var godotDefines = features;
|
||||
|
||||
if (!GodotSharpBuilds.BuildProjectBlocking(buildConfig, godotDefines))
|
||||
{
|
||||
GD.PushError("Failed to build project");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add dependency assemblies
|
||||
|
||||
var dependencies = new Godot.Collections.Dictionary<string, string>();
|
||||
|
||||
var projectDllName = (string) ProjectSettings.GetSetting("application/config/name");
|
||||
if (projectDllName.Empty())
|
||||
{
|
||||
projectDllName = "UnnamedProject";
|
||||
}
|
||||
|
||||
string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
|
||||
string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
|
||||
|
||||
dependencies[projectDllName] = projectDllSrcPath;
|
||||
|
||||
{
|
||||
string templatesDir = Internal.FullTemplatesDir;
|
||||
string androidBclDir = Path.Combine(templatesDir, "android-bcl");
|
||||
|
||||
string customLibDir = features.Contains("Android") && Directory.Exists(androidBclDir) ? androidBclDir : string.Empty;
|
||||
|
||||
GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customLibDir, dependencies);
|
||||
}
|
||||
|
||||
string apiConfig = isDebug ? "Debug" : "Release";
|
||||
string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
|
||||
|
||||
foreach (var dependency in dependencies)
|
||||
{
|
||||
string dependSrcPath = dependency.Value;
|
||||
string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile());
|
||||
AddFile(dependSrcPath, dependDstPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Mono specific export template extras (data dir)
|
||||
ExportDataDirectory(features, isDebug, path);
|
||||
}
|
||||
|
||||
private static void ExportDataDirectory(IEnumerable<string> features, bool debug, string path)
|
||||
{
|
||||
var featureSet = new HashSet<string>(features);
|
||||
|
||||
if (!PlatformHasTemplateDir(featureSet))
|
||||
return;
|
||||
|
||||
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 ? ".release_debug" : ".release";
|
||||
|
||||
string templateDirPath = Path.Combine(Internal.FullTemplatesDir, templateDirName);
|
||||
|
||||
if (!Directory.Exists(templateDirPath))
|
||||
throw new FileNotFoundException("Data template directory not found");
|
||||
|
||||
string outputDir = new FileInfo(path).Directory?.FullName ??
|
||||
throw new FileNotFoundException("Base directory not found");
|
||||
|
||||
string outputDataDir = Path.Combine(outputDir, DataDirName);
|
||||
|
||||
if (Directory.Exists(outputDataDir))
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool PlatformHasTemplateDir(IEnumerable<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.Any(f => new[] {"OSX", "Android"}.Contains(f));
|
||||
}
|
||||
|
||||
private static string DataDirName
|
||||
{
|
||||
get
|
||||
{
|
||||
var appName = (string) ProjectSettings.GetSetting("application/config/name");
|
||||
string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false);
|
||||
return $"data_{appNameSafe}";
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath,
|
||||
string buildConfig, string customLibDir, Godot.Collections.Dictionary<string, string> dependencies) =>
|
||||
internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customLibDir, dependencies);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath,
|
||||
string buildConfig, string customLibDir, Godot.Collections.Dictionary<string, string> dependencies);
|
||||
}
|
||||
}
|
81
modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
Normal file
81
modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>GodotTools</RootNamespace>
|
||||
<AssemblyName>GodotTools</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
|
||||
<GodotApiConfiguration>Debug</GodotApiConfiguration>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="GodotSharp">
|
||||
<HintPath>$(GodotSourceRootPath)/bin/GodotSharp/Api/$(GodotApiConfiguration)/GodotSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="GodotSharpEditor">
|
||||
<HintPath>$(GodotSourceRootPath)/bin/GodotSharp/Api/$(GodotApiConfiguration)/GodotSharpEditor.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Build\MsBuildFinder.cs" />
|
||||
<Compile Include="Internals\BindingsGenerator.cs" />
|
||||
<Compile Include="Internals\EditorProgress.cs" />
|
||||
<Compile Include="Internals\GodotSharpDirs.cs" />
|
||||
<Compile Include="Internals\Internal.cs" />
|
||||
<Compile Include="Internals\ScriptClassParser.cs" />
|
||||
<Compile Include="MonoDevelopInstance.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Build\BuildSystem.cs" />
|
||||
<Compile Include="Utils\Directory.cs" />
|
||||
<Compile Include="Utils\File.cs" />
|
||||
<Compile Include="Utils\OS.cs" />
|
||||
<Compile Include="GodotSharpEditor.cs" />
|
||||
<Compile Include="GodotSharpBuilds.cs" />
|
||||
<Compile Include="HotReloadAssemblyWatcher.cs" />
|
||||
<Compile Include="MonoBuildInfo.cs" />
|
||||
<Compile Include="MonoBuildTab.cs" />
|
||||
<Compile Include="MonoBottomPanel.cs" />
|
||||
<Compile Include="GodotSharpExport.cs" />
|
||||
<Compile Include="CSharpProject.cs" />
|
||||
<Compile Include="Utils\CollectionExtensions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GodotTools.BuildLogger\GodotTools.BuildLogger.csproj">
|
||||
<Project>{6ce9a984-37b1-4f8a-8fe9-609f05f071b3}</Project>
|
||||
<Name>GodotTools.BuildLogger</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj">
|
||||
<Project>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</Project>
|
||||
<Name>GodotTools.ProjectEditor</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj">
|
||||
<Project>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</Project>
|
||||
<Name>GodotTools.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Editor" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,47 @@
|
|||
using Godot;
|
||||
using GodotTools.Internals;
|
||||
|
||||
namespace GodotTools
|
||||
{
|
||||
public class HotReloadAssemblyWatcher : Node
|
||||
{
|
||||
private Timer watchTimer;
|
||||
|
||||
public override void _Notification(int what)
|
||||
{
|
||||
if (what == MainLoop.NotificationWmFocusIn)
|
||||
{
|
||||
RestartTimer();
|
||||
|
||||
if (Internal.IsAssembliesReloadingNeeded())
|
||||
Internal.ReloadAssemblies(softReload: false);
|
||||
}
|
||||
}
|
||||
|
||||
private void TimerTimeout()
|
||||
{
|
||||
if (Internal.IsAssembliesReloadingNeeded())
|
||||
Internal.ReloadAssemblies(softReload: false);
|
||||
}
|
||||
|
||||
public void RestartTimer()
|
||||
{
|
||||
watchTimer.Stop();
|
||||
watchTimer.Start();
|
||||
}
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
|
||||
watchTimer = new Timer
|
||||
{
|
||||
OneShot = false,
|
||||
WaitTime = (float) Internal.EditorDef("mono/assembly_watch_interval_sec", 0.5)
|
||||
};
|
||||
watchTimer.Connect("timeout", this, nameof(TimerTimeout));
|
||||
AddChild(watchTimer);
|
||||
watchTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GodotTools.Internals
|
||||
{
|
||||
public class BindingsGenerator : IDisposable
|
||||
{
|
||||
class BindingsGeneratorSafeHandle : SafeHandle
|
||||
{
|
||||
public BindingsGeneratorSafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
internal_Dtor(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private BindingsGeneratorSafeHandle safeHandle;
|
||||
private bool disposed = false;
|
||||
|
||||
public bool LogPrintEnabled
|
||||
{
|
||||
get => internal_LogPrintEnabled(GetPtr());
|
||||
set => internal_SetLogPrintEnabled(GetPtr(), value);
|
||||
}
|
||||
|
||||
public static uint Version => internal_Version();
|
||||
public static uint CsGlueVersion => internal_CsGlueVersion();
|
||||
|
||||
public Godot.Error GenerateCsApi(string outputDir) => internal_GenerateCsApi(GetPtr(), outputDir);
|
||||
|
||||
internal IntPtr GetPtr()
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException(GetType().FullName);
|
||||
|
||||
return safeHandle.DangerousGetHandle();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (safeHandle != null && !safeHandle.IsInvalid)
|
||||
{
|
||||
safeHandle.Dispose();
|
||||
safeHandle = null;
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public BindingsGenerator()
|
||||
{
|
||||
safeHandle = new BindingsGeneratorSafeHandle(internal_Ctor());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern IntPtr internal_Ctor();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_Dtor(IntPtr handle);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_LogPrintEnabled(IntPtr handle);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_SetLogPrintEnabled(IntPtr handle, bool enabled);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern Godot.Error internal_GenerateCsApi(IntPtr handle, string outputDir);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern uint internal_Version();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern uint internal_CsGlueVersion();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Godot;
|
||||
|
||||
namespace GodotTools.Internals
|
||||
{
|
||||
public class EditorProgress : IDisposable
|
||||
{
|
||||
public string Task { get; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_Create(string task, string label, int amount, bool canCancel);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_Dispose(string task);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_Step(string task, string state, int step, bool forceRefresh);
|
||||
|
||||
public EditorProgress(string task, string label, int amount, bool canCancel = false)
|
||||
{
|
||||
Task = task;
|
||||
internal_Create(task, label, amount, canCancel);
|
||||
}
|
||||
|
||||
~EditorProgress()
|
||||
{
|
||||
// Should never rely on the GC to dispose EditorProgress.
|
||||
// It should be disposed immediately when the task finishes.
|
||||
GD.PushError("EditorProgress disposed by the Garbage Collector");
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
internal_Dispose(Task);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void Step(string state, int step = -1, bool forceRefresh = true)
|
||||
{
|
||||
internal_Step(Task, state, step, forceRefresh);
|
||||
}
|
||||
|
||||
public bool TryStep(string state, int step = -1, bool forceRefresh = true)
|
||||
{
|
||||
return internal_Step(Task, state, step, forceRefresh);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace GodotTools.Internals
|
||||
{
|
||||
public static class GodotSharpDirs
|
||||
{
|
||||
public static string ResDataDir => internal_ResDataDir();
|
||||
public static string ResMetadataDir => internal_ResMetadataDir();
|
||||
public static string ResAssembliesBaseDir => internal_ResAssembliesBaseDir();
|
||||
public static string ResAssembliesDir => internal_ResAssembliesDir();
|
||||
public static string ResConfigDir => internal_ResConfigDir();
|
||||
public static string ResTempDir => internal_ResTempDir();
|
||||
public static string ResTempAssembliesBaseDir => internal_ResTempAssembliesBaseDir();
|
||||
public static string ResTempAssembliesDir => internal_ResTempAssembliesDir();
|
||||
|
||||
public static string MonoUserDir => internal_MonoUserDir();
|
||||
public static string MonoLogsDir => internal_MonoLogsDir();
|
||||
|
||||
#region Tools-only
|
||||
public static string MonoSolutionsDir => internal_MonoSolutionsDir();
|
||||
public static string BuildLogsDirs => internal_BuildLogsDirs();
|
||||
|
||||
public static string ProjectSlnPath => internal_ProjectSlnPath();
|
||||
public static string ProjectCsProjPath => internal_ProjectCsProjPath();
|
||||
|
||||
public static string DataEditorToolsDir => internal_DataEditorToolsDir();
|
||||
public static string DataEditorPrebuiltApiDir => internal_DataEditorPrebuiltApiDir();
|
||||
#endregion
|
||||
|
||||
public static string DataMonoEtcDir => internal_DataMonoEtcDir();
|
||||
public static string DataMonoLibDir => internal_DataMonoLibDir();
|
||||
|
||||
#region Windows-only
|
||||
public static string DataMonoBinDir => internal_DataMonoBinDir();
|
||||
#endregion
|
||||
|
||||
|
||||
#region Internal
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_ResDataDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_ResMetadataDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_ResAssembliesBaseDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_ResAssembliesDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_ResConfigDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_ResTempDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_ResTempAssembliesBaseDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_ResTempAssembliesDir();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_MonoUserDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_MonoLogsDir();
|
||||
|
||||
#region Tools-only
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_MonoSolutionsDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_BuildLogsDirs();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_ProjectSlnPath();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_ProjectCsProjPath();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_DataEditorToolsDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_DataEditorPrebuiltApiDir();
|
||||
#endregion
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_DataMonoEtcDir();
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_DataMonoLibDir();
|
||||
|
||||
#region Windows-only
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_DataMonoBinDir();
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
127
modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
Normal file
127
modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
Normal file
|
@ -0,0 +1,127 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace GodotTools.Internals
|
||||
{
|
||||
public static class Internal
|
||||
{
|
||||
public const string CSharpLanguageType = "CSharpScript";
|
||||
public const string CSharpLanguageExtension = "cs";
|
||||
|
||||
public static float EditorScale => internal_EditorScale();
|
||||
|
||||
public static object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) =>
|
||||
internal_GlobalDef(setting, defaultValue, restartIfChanged);
|
||||
|
||||
public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) =>
|
||||
internal_EditorDef(setting, defaultValue, restartIfChanged);
|
||||
|
||||
public static string FullTemplatesDir =>
|
||||
internal_FullTemplatesDir();
|
||||
|
||||
public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path);
|
||||
|
||||
public static bool IsOsxAppBundleInstalled(string bundleId) => internal_IsOsxAppBundleInstalled(bundleId);
|
||||
|
||||
public static bool MetadataIsApiAssemblyInvalidated(ApiAssemblyType apiType) =>
|
||||
internal_MetadataIsApiAssemblyInvalidated(apiType);
|
||||
|
||||
public static void MetadataSetApiAssemblyInvalidated(ApiAssemblyType apiType, bool invalidated) =>
|
||||
internal_MetadataSetApiAssemblyInvalidated(apiType, invalidated);
|
||||
|
||||
public static bool IsMessageQueueFlushing() => internal_IsMessageQueueFlushing();
|
||||
|
||||
public static bool GodotIs32Bits() => internal_GodotIs32Bits();
|
||||
|
||||
public static bool GodotIsRealTDouble() => internal_GodotIsRealTDouble();
|
||||
|
||||
public static void GodotMainIteration() => internal_GodotMainIteration();
|
||||
|
||||
public static ulong GetCoreApiHash() => internal_GetCoreApiHash();
|
||||
|
||||
public static ulong GetEditorApiHash() => internal_GetEditorApiHash();
|
||||
|
||||
public static bool IsAssembliesReloadingNeeded() => internal_IsAssembliesReloadingNeeded();
|
||||
|
||||
public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload);
|
||||
|
||||
public static void ScriptEditorDebuggerReloadScripts() => internal_ScriptEditorDebuggerReloadScripts();
|
||||
|
||||
public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
|
||||
internal_ScriptEditorEdit(resource, line, col, grabFocus);
|
||||
|
||||
public static void EditorNodeShowScriptScreen() => internal_EditorNodeShowScriptScreen();
|
||||
|
||||
public static Dictionary<string, object> GetScriptsMetadataOrNothing() =>
|
||||
internal_GetScriptsMetadataOrNothing(typeof(Dictionary<string, object>));
|
||||
|
||||
public static string MonoWindowsInstallRoot => internal_MonoWindowsInstallRoot();
|
||||
|
||||
// Internal Calls
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern float internal_EditorScale();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern object internal_GlobalDef(string setting, object defaultValue, bool restartIfChanged);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_FullTemplatesDir();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_SimplifyGodotPath(this string path);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_IsOsxAppBundleInstalled(string bundleId);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_MetadataIsApiAssemblyInvalidated(ApiAssemblyType apiType);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_MetadataSetApiAssemblyInvalidated(ApiAssemblyType apiType, bool invalidated);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_IsMessageQueueFlushing();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_GodotIs32Bits();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_GodotIsRealTDouble();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_GodotMainIteration();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern ulong internal_GetCoreApiHash();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern ulong internal_GetEditorApiHash();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_IsAssembliesReloadingNeeded();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_ReloadAssemblies(bool softReload);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_ScriptEditorDebuggerReloadScripts();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_EditorNodeShowScriptScreen();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern Dictionary<string, object> internal_GetScriptsMetadataOrNothing(Type dictType);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_MonoWindowsInstallRoot();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace GodotTools.Internals
|
||||
{
|
||||
public static class ScriptClassParser
|
||||
{
|
||||
public class ClassDecl
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Namespace { get; }
|
||||
public bool Nested { get; }
|
||||
public int BaseCount { get; }
|
||||
|
||||
public ClassDecl(string name, string @namespace, bool nested, int baseCount)
|
||||
{
|
||||
Name = name;
|
||||
Namespace = @namespace;
|
||||
Nested = nested;
|
||||
BaseCount = baseCount;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern Error internal_ParseFile(string filePath, Array<Dictionary> classes);
|
||||
|
||||
public static void ParseFileOrThrow(string filePath, out IEnumerable<ClassDecl> classes)
|
||||
{
|
||||
var classesArray = new Array<Dictionary>();
|
||||
var error = internal_ParseFile(filePath, classesArray);
|
||||
if (error != Error.Ok)
|
||||
throw new Exception($"Failed to determine namespace and class for script: {filePath}. Parse error: {error}");
|
||||
|
||||
var classesList = new List<ClassDecl>();
|
||||
|
||||
foreach (var classDeclDict in classesArray)
|
||||
{
|
||||
classesList.Add(new ClassDecl(
|
||||
(string) classDeclDict["name"],
|
||||
(string) classDeclDict["namespace"],
|
||||
(bool) classDeclDict["nested"],
|
||||
(int) classDeclDict["base_count"]
|
||||
));
|
||||
}
|
||||
|
||||
classes = classesList;
|
||||
}
|
||||
}
|
||||
}
|
342
modules/mono/editor/GodotTools/GodotTools/MonoBottomPanel.cs
Normal file
342
modules/mono/editor/GodotTools/GodotTools/MonoBottomPanel.cs
Normal file
|
@ -0,0 +1,342 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Godot.Collections;
|
||||
using GodotTools.Internals;
|
||||
using File = GodotTools.Utils.File;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace GodotTools
|
||||
{
|
||||
public class MonoBottomPanel : VBoxContainer
|
||||
{
|
||||
private EditorInterface editorInterface;
|
||||
|
||||
private TabContainer panelTabs;
|
||||
|
||||
private VBoxContainer panelBuildsTab;
|
||||
|
||||
private ItemList buildTabsList;
|
||||
private TabContainer buildTabs;
|
||||
|
||||
private ToolButton warningsBtn;
|
||||
private ToolButton errorsBtn;
|
||||
private Button viewLogBtn;
|
||||
|
||||
private void _UpdateBuildTabsList()
|
||||
{
|
||||
buildTabsList.Clear();
|
||||
|
||||
int currentTab = buildTabs.CurrentTab;
|
||||
|
||||
bool noCurrentTab = currentTab < 0 || currentTab >= buildTabs.GetTabCount();
|
||||
|
||||
for (int i = 0; i < buildTabs.GetChildCount(); i++)
|
||||
{
|
||||
var tab = (MonoBuildTab) buildTabs.GetChild(i);
|
||||
|
||||
if (tab == null)
|
||||
continue;
|
||||
|
||||
string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution);
|
||||
itemName += " [" + tab.BuildInfo.Configuration + "]";
|
||||
|
||||
buildTabsList.AddItem(itemName, tab.IconTexture);
|
||||
|
||||
string itemTooltip = "Solution: " + tab.BuildInfo.Solution;
|
||||
itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration;
|
||||
itemTooltip += "\nStatus: ";
|
||||
|
||||
if (tab.BuildExited)
|
||||
itemTooltip += tab.BuildResult == MonoBuildTab.BuildResults.Success ? "Succeeded" : "Errored";
|
||||
else
|
||||
itemTooltip += "Running";
|
||||
|
||||
if (!tab.BuildExited || tab.BuildResult == MonoBuildTab.BuildResults.Error)
|
||||
itemTooltip += $"\nErrors: {tab.ErrorCount}";
|
||||
|
||||
itemTooltip += $"\nWarnings: {tab.WarningCount}";
|
||||
|
||||
buildTabsList.SetItemTooltip(i, itemTooltip);
|
||||
|
||||
if (noCurrentTab || currentTab == i)
|
||||
{
|
||||
buildTabsList.Select(i);
|
||||
_BuildTabsItemSelected(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MonoBuildTab GetBuildTabFor(MonoBuildInfo buildInfo)
|
||||
{
|
||||
foreach (var buildTab in new Array<MonoBuildTab>(buildTabs.GetChildren()))
|
||||
{
|
||||
if (buildTab.BuildInfo.Equals(buildInfo))
|
||||
return buildTab;
|
||||
}
|
||||
|
||||
var newBuildTab = new MonoBuildTab(buildInfo);
|
||||
AddBuildTab(newBuildTab);
|
||||
|
||||
return newBuildTab;
|
||||
}
|
||||
|
||||
private void _BuildTabsItemSelected(int idx)
|
||||
{
|
||||
if (idx < 0 || idx >= buildTabs.GetTabCount())
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
buildTabs.CurrentTab = idx;
|
||||
if (!buildTabs.Visible)
|
||||
buildTabs.Visible = true;
|
||||
|
||||
warningsBtn.Visible = true;
|
||||
errorsBtn.Visible = true;
|
||||
viewLogBtn.Visible = true;
|
||||
}
|
||||
|
||||
private void _BuildTabsNothingSelected()
|
||||
{
|
||||
if (buildTabs.GetTabCount() != 0)
|
||||
{
|
||||
// just in case
|
||||
buildTabs.Visible = false;
|
||||
|
||||
// This callback is called when clicking on the empty space of the list.
|
||||
// ItemList won't deselect the items automatically, so we must do it ourselves.
|
||||
buildTabsList.UnselectAll();
|
||||
}
|
||||
|
||||
warningsBtn.Visible = false;
|
||||
errorsBtn.Visible = false;
|
||||
viewLogBtn.Visible = false;
|
||||
}
|
||||
|
||||
private void _WarningsToggled(bool pressed)
|
||||
{
|
||||
int currentTab = buildTabs.CurrentTab;
|
||||
|
||||
if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
|
||||
throw new InvalidOperationException("No tab selected");
|
||||
|
||||
var buildTab = (MonoBuildTab) buildTabs.GetChild(currentTab);
|
||||
buildTab.WarningsVisible = pressed;
|
||||
buildTab.UpdateIssuesList();
|
||||
}
|
||||
|
||||
private void _ErrorsToggled(bool pressed)
|
||||
{
|
||||
int currentTab = buildTabs.CurrentTab;
|
||||
|
||||
if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
|
||||
throw new InvalidOperationException("No tab selected");
|
||||
|
||||
var buildTab = (MonoBuildTab) buildTabs.GetChild(currentTab);
|
||||
buildTab.ErrorsVisible = pressed;
|
||||
buildTab.UpdateIssuesList();
|
||||
}
|
||||
|
||||
public void BuildProjectPressed()
|
||||
{
|
||||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
return; // No solution to build
|
||||
|
||||
string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor");
|
||||
string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player");
|
||||
|
||||
CSharpProject.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath);
|
||||
|
||||
if (File.Exists(editorScriptsMetadataPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
GD.PushError($"Failed to copy scripts metadata file. Exception message: {e.Message}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var godotDefines = new[]
|
||||
{
|
||||
OS.GetName(),
|
||||
Internal.GodotIs32Bits() ? "32" : "64"
|
||||
};
|
||||
|
||||
bool buildSuccess = GodotSharpBuilds.BuildProjectBlocking("Tools", godotDefines);
|
||||
|
||||
if (!buildSuccess)
|
||||
return;
|
||||
|
||||
// Notify running game for hot-reload
|
||||
Internal.ScriptEditorDebuggerReloadScripts();
|
||||
|
||||
// Hot-reload in the editor
|
||||
GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
|
||||
|
||||
if (Internal.IsAssembliesReloadingNeeded())
|
||||
Internal.ReloadAssemblies(softReload: false);
|
||||
}
|
||||
|
||||
private void _ViewLogPressed()
|
||||
{
|
||||
if (!buildTabsList.IsAnythingSelected())
|
||||
return;
|
||||
|
||||
var selectedItems = buildTabsList.GetSelectedItems();
|
||||
|
||||
if (selectedItems.Length != 1)
|
||||
throw new InvalidOperationException($"Expected 1 selected item, got {selectedItems.Length}");
|
||||
|
||||
int selectedItem = selectedItems[0];
|
||||
|
||||
var buildTab = (MonoBuildTab) buildTabs.GetTabControl(selectedItem);
|
||||
|
||||
OS.ShellOpen(Path.Combine(buildTab.BuildInfo.LogsDirPath, GodotSharpBuilds.MsBuildLogFileName));
|
||||
}
|
||||
|
||||
public override void _Notification(int what)
|
||||
{
|
||||
base._Notification(what);
|
||||
|
||||
if (what == EditorSettings.NotificationEditorSettingsChanged)
|
||||
{
|
||||
var editorBaseControl = editorInterface.GetBaseControl();
|
||||
panelTabs.AddStyleboxOverride("panel", editorBaseControl.GetStylebox("DebuggerPanel", "EditorStyles"));
|
||||
panelTabs.AddStyleboxOverride("tab_fg", editorBaseControl.GetStylebox("DebuggerTabFG", "EditorStyles"));
|
||||
panelTabs.AddStyleboxOverride("tab_bg", editorBaseControl.GetStylebox("DebuggerTabBG", "EditorStyles"));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddBuildTab(MonoBuildTab buildTab)
|
||||
{
|
||||
buildTabs.AddChild(buildTab);
|
||||
RaiseBuildTab(buildTab);
|
||||
}
|
||||
|
||||
public void RaiseBuildTab(MonoBuildTab buildTab)
|
||||
{
|
||||
if (buildTab.GetParent() != buildTabs)
|
||||
throw new InvalidOperationException("Build tab is not in the tabs list");
|
||||
|
||||
buildTabs.MoveChild(buildTab, 0);
|
||||
_UpdateBuildTabsList();
|
||||
}
|
||||
|
||||
public void ShowBuildTab()
|
||||
{
|
||||
for (int i = 0; i < panelTabs.GetTabCount(); i++)
|
||||
{
|
||||
if (panelTabs.GetTabControl(i) == panelBuildsTab)
|
||||
{
|
||||
panelTabs.CurrentTab = i;
|
||||
GodotSharpEditor.Instance.MakeBottomPanelItemVisible(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GD.PushError("Builds tab not found");
|
||||
}
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
|
||||
editorInterface = GodotSharpEditor.Instance.GetEditorInterface();
|
||||
|
||||
var editorBaseControl = editorInterface.GetBaseControl();
|
||||
|
||||
SizeFlagsVertical = (int) SizeFlags.ExpandFill;
|
||||
SetAnchorsAndMarginsPreset(LayoutPreset.Wide);
|
||||
|
||||
panelTabs = new TabContainer
|
||||
{
|
||||
TabAlign = TabContainer.TabAlignEnum.Left,
|
||||
RectMinSize = new Vector2(0, 228) * Internal.EditorScale,
|
||||
SizeFlagsVertical = (int) SizeFlags.ExpandFill
|
||||
};
|
||||
panelTabs.AddStyleboxOverride("panel", editorBaseControl.GetStylebox("DebuggerPanel", "EditorStyles"));
|
||||
panelTabs.AddStyleboxOverride("tab_fg", editorBaseControl.GetStylebox("DebuggerTabFG", "EditorStyles"));
|
||||
panelTabs.AddStyleboxOverride("tab_bg", editorBaseControl.GetStylebox("DebuggerTabBG", "EditorStyles"));
|
||||
AddChild(panelTabs);
|
||||
|
||||
{
|
||||
// Builds tab
|
||||
panelBuildsTab = new VBoxContainer
|
||||
{
|
||||
Name = "Builds", // TTR
|
||||
SizeFlagsHorizontal = (int) SizeFlags.ExpandFill
|
||||
};
|
||||
panelTabs.AddChild(panelBuildsTab);
|
||||
|
||||
var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int) SizeFlags.ExpandFill};
|
||||
panelBuildsTab.AddChild(toolBarHBox);
|
||||
|
||||
var buildProjectBtn = new Button
|
||||
{
|
||||
Text = "Build Project", // TTR
|
||||
FocusMode = FocusModeEnum.None
|
||||
};
|
||||
buildProjectBtn.Connect("pressed", this, nameof(BuildProjectPressed));
|
||||
toolBarHBox.AddChild(buildProjectBtn);
|
||||
|
||||
toolBarHBox.AddSpacer(begin: false);
|
||||
|
||||
warningsBtn = new ToolButton
|
||||
{
|
||||
Text = "Warnings", // TTR
|
||||
ToggleMode = true,
|
||||
Pressed = true,
|
||||
Visible = false,
|
||||
FocusMode = FocusModeEnum.None
|
||||
};
|
||||
warningsBtn.Connect("toggled", this, nameof(_WarningsToggled));
|
||||
toolBarHBox.AddChild(warningsBtn);
|
||||
|
||||
errorsBtn = new ToolButton
|
||||
{
|
||||
Text = "Errors", // TTR
|
||||
ToggleMode = true,
|
||||
Pressed = true,
|
||||
Visible = false,
|
||||
FocusMode = FocusModeEnum.None
|
||||
};
|
||||
errorsBtn.Connect("toggled", this, nameof(_ErrorsToggled));
|
||||
toolBarHBox.AddChild(errorsBtn);
|
||||
|
||||
toolBarHBox.AddSpacer(begin: false);
|
||||
|
||||
viewLogBtn = new Button
|
||||
{
|
||||
Text = "View log", // TTR
|
||||
FocusMode = FocusModeEnum.None,
|
||||
Visible = false
|
||||
};
|
||||
viewLogBtn.Connect("pressed", this, nameof(_ViewLogPressed));
|
||||
toolBarHBox.AddChild(viewLogBtn);
|
||||
|
||||
var hsc = new HSplitContainer
|
||||
{
|
||||
SizeFlagsHorizontal = (int) SizeFlags.ExpandFill,
|
||||
SizeFlagsVertical = (int) SizeFlags.ExpandFill
|
||||
};
|
||||
panelBuildsTab.AddChild(hsc);
|
||||
|
||||
buildTabsList = new ItemList {SizeFlagsHorizontal = (int) SizeFlags.ExpandFill};
|
||||
buildTabsList.Connect("item_selected", this, nameof(_BuildTabsItemSelected));
|
||||
buildTabsList.Connect("nothing_selected", this, nameof(_BuildTabsNothingSelected));
|
||||
hsc.AddChild(buildTabsList);
|
||||
|
||||
buildTabs = new TabContainer
|
||||
{
|
||||
TabAlign = TabContainer.TabAlignEnum.Left,
|
||||
SizeFlagsHorizontal = (int) SizeFlags.ExpandFill,
|
||||
TabsVisible = false
|
||||
};
|
||||
hsc.AddChild(buildTabs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
modules/mono/editor/GodotTools/GodotTools/MonoBuildInfo.cs
Normal file
47
modules/mono/editor/GodotTools/GodotTools/MonoBuildInfo.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using GodotTools.Internals;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace GodotTools
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class MonoBuildInfo : Reference // TODO Remove Reference once we have proper serialization
|
||||
{
|
||||
public string Solution { get; }
|
||||
public string Configuration { get; }
|
||||
public Array<string> CustomProperties { get; } = new Array<string>(); // TODO Use List once we have proper serialization
|
||||
|
||||
public string LogsDirPath => Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is MonoBuildInfo other)
|
||||
return other.Solution == Solution && other.Configuration == Configuration;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 29 + Solution.GetHashCode();
|
||||
hash = hash * 29 + Configuration.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
private MonoBuildInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public MonoBuildInfo(string solution, string configuration)
|
||||
{
|
||||
Solution = solution;
|
||||
Configuration = configuration;
|
||||
}
|
||||
}
|
||||
}
|
260
modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs
Normal file
260
modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs
Normal file
|
@ -0,0 +1,260 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using Godot.Collections;
|
||||
using GodotTools.Internals;
|
||||
using File = GodotTools.Utils.File;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace GodotTools
|
||||
{
|
||||
public class MonoBuildTab : VBoxContainer
|
||||
{
|
||||
public enum BuildResults
|
||||
{
|
||||
Error,
|
||||
Success
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private class BuildIssue : Reference // TODO Remove Reference once we have proper serialization
|
||||
{
|
||||
public bool Warning { get; set; }
|
||||
public string File { get; set; }
|
||||
public int Line { get; set; }
|
||||
public int Column { get; set; }
|
||||
public string Code { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string ProjectFile { get; set; }
|
||||
}
|
||||
|
||||
private readonly Array<BuildIssue> issues = new Array<BuildIssue>(); // TODO Use List once we have proper serialization
|
||||
private ItemList issuesList;
|
||||
|
||||
public bool BuildExited { get; private set; } = false;
|
||||
|
||||
public BuildResults? BuildResult { get; private set; } = null;
|
||||
|
||||
public int ErrorCount { get; private set; } = 0;
|
||||
|
||||
public int WarningCount { get; private set; } = 0;
|
||||
|
||||
public bool ErrorsVisible { get; set; } = true;
|
||||
public bool WarningsVisible { get; set; } = true;
|
||||
|
||||
public Texture IconTexture
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!BuildExited)
|
||||
return GetIcon("Stop", "EditorIcons");
|
||||
|
||||
if (BuildResult == BuildResults.Error)
|
||||
return GetIcon("StatusError", "EditorIcons");
|
||||
|
||||
return GetIcon("StatusSuccess", "EditorIcons");
|
||||
}
|
||||
}
|
||||
|
||||
public MonoBuildInfo BuildInfo { get; private set; }
|
||||
|
||||
private void _LoadIssuesFromFile(string csvFile)
|
||||
{
|
||||
using (var file = new Godot.File())
|
||||
{
|
||||
Error openError = file.Open(csvFile, Godot.File.ModeFlags.Read);
|
||||
|
||||
if (openError != Error.Ok)
|
||||
return;
|
||||
|
||||
while (!file.EofReached())
|
||||
{
|
||||
string[] csvColumns = file.GetCsvLine();
|
||||
|
||||
if (csvColumns.Length == 1 && csvColumns[0].Empty())
|
||||
return;
|
||||
|
||||
if (csvColumns.Length != 7)
|
||||
{
|
||||
GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var issue = new BuildIssue
|
||||
{
|
||||
Warning = csvColumns[0] == "warning",
|
||||
File = csvColumns[1],
|
||||
Line = int.Parse(csvColumns[2]),
|
||||
Column = int.Parse(csvColumns[3]),
|
||||
Code = csvColumns[4],
|
||||
Message = csvColumns[5],
|
||||
ProjectFile = csvColumns[6]
|
||||
};
|
||||
|
||||
if (issue.Warning)
|
||||
WarningCount += 1;
|
||||
else
|
||||
ErrorCount += 1;
|
||||
|
||||
issues.Add(issue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _IssueActivated(int idx)
|
||||
{
|
||||
if (idx < 0 || idx >= issuesList.GetItemCount())
|
||||
throw new IndexOutOfRangeException("Item list index out of range");
|
||||
|
||||
// Get correct issue idx from issue list
|
||||
int issueIndex = (int) issuesList.GetItemMetadata(idx);
|
||||
|
||||
if (idx < 0 || idx >= issues.Count)
|
||||
throw new IndexOutOfRangeException("Issue index out of range");
|
||||
|
||||
BuildIssue issue = issues[issueIndex];
|
||||
|
||||
if (issue.ProjectFile.Empty() && issue.File.Empty())
|
||||
return;
|
||||
|
||||
string projectDir = issue.ProjectFile.Length > 0 ? issue.ProjectFile.GetBaseDir() : BuildInfo.Solution.GetBaseDir();
|
||||
|
||||
string file = Path.Combine(projectDir.SimplifyGodotPath(), issue.File.SimplifyGodotPath());
|
||||
|
||||
if (!File.Exists(file))
|
||||
return;
|
||||
|
||||
file = ProjectSettings.LocalizePath(file);
|
||||
|
||||
if (file.StartsWith("res://"))
|
||||
{
|
||||
var script = (Script) ResourceLoader.Load(file, typeHint: Internal.CSharpLanguageType);
|
||||
|
||||
if (script != null && Internal.ScriptEditorEdit(script, issue.Line, issue.Column))
|
||||
Internal.EditorNodeShowScriptScreen();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateIssuesList()
|
||||
{
|
||||
issuesList.Clear();
|
||||
|
||||
using (var warningIcon = GetIcon("Warning", "EditorIcons"))
|
||||
using (var errorIcon = GetIcon("Error", "EditorIcons"))
|
||||
{
|
||||
for (int i = 0; i < issues.Count; i++)
|
||||
{
|
||||
BuildIssue issue = issues[i];
|
||||
|
||||
if (!(issue.Warning ? WarningsVisible : ErrorsVisible))
|
||||
continue;
|
||||
|
||||
string tooltip = string.Empty;
|
||||
tooltip += $"Message: {issue.Message}";
|
||||
|
||||
if (!issue.Code.Empty())
|
||||
tooltip += $"\nCode: {issue.Code}";
|
||||
|
||||
tooltip += $"\nType: {(issue.Warning ? "warning" : "error")}";
|
||||
|
||||
string text = string.Empty;
|
||||
|
||||
if (!issue.File.Empty())
|
||||
{
|
||||
text += $"{issue.File}({issue.Line},{issue.Column}): ";
|
||||
|
||||
tooltip += $"\nFile: {issue.File}";
|
||||
tooltip += $"\nLine: {issue.Line}";
|
||||
tooltip += $"\nColumn: {issue.Column}";
|
||||
}
|
||||
|
||||
if (!issue.ProjectFile.Empty())
|
||||
tooltip += $"\nProject: {issue.ProjectFile}";
|
||||
|
||||
text += issue.Message;
|
||||
|
||||
int lineBreakIdx = text.IndexOf("\n", StringComparison.Ordinal);
|
||||
string itemText = lineBreakIdx == -1 ? text : text.Substring(0, lineBreakIdx);
|
||||
issuesList.AddItem(itemText, issue.Warning ? warningIcon : errorIcon);
|
||||
|
||||
int index = issuesList.GetItemCount() - 1;
|
||||
issuesList.SetItemTooltip(index, tooltip);
|
||||
issuesList.SetItemMetadata(index, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBuildStart()
|
||||
{
|
||||
BuildExited = false;
|
||||
|
||||
issues.Clear();
|
||||
WarningCount = 0;
|
||||
ErrorCount = 0;
|
||||
UpdateIssuesList();
|
||||
|
||||
GodotSharpEditor.Instance.MonoBottomPanel.RaiseBuildTab(this);
|
||||
}
|
||||
|
||||
public void OnBuildExit(BuildResults result)
|
||||
{
|
||||
BuildExited = true;
|
||||
BuildResult = result;
|
||||
|
||||
_LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, GodotSharpBuilds.MsBuildIssuesFileName));
|
||||
UpdateIssuesList();
|
||||
|
||||
GodotSharpEditor.Instance.MonoBottomPanel.RaiseBuildTab(this);
|
||||
}
|
||||
|
||||
public void OnBuildExecFailed(string cause)
|
||||
{
|
||||
BuildExited = true;
|
||||
BuildResult = BuildResults.Error;
|
||||
|
||||
issuesList.Clear();
|
||||
|
||||
var issue = new BuildIssue {Message = cause, Warning = false};
|
||||
|
||||
ErrorCount += 1;
|
||||
issues.Add(issue);
|
||||
|
||||
UpdateIssuesList();
|
||||
|
||||
GodotSharpEditor.Instance.MonoBottomPanel.RaiseBuildTab(this);
|
||||
}
|
||||
|
||||
public void RestartBuild()
|
||||
{
|
||||
if (!BuildExited)
|
||||
throw new InvalidOperationException("Build already started");
|
||||
|
||||
GodotSharpBuilds.RestartBuild(this);
|
||||
}
|
||||
|
||||
public void StopBuild()
|
||||
{
|
||||
if (!BuildExited)
|
||||
throw new InvalidOperationException("Build is not in progress");
|
||||
|
||||
GodotSharpBuilds.StopBuild(this);
|
||||
}
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
|
||||
issuesList = new ItemList {SizeFlagsVertical = (int) SizeFlags.ExpandFill};
|
||||
issuesList.Connect("item_activated", this, nameof(_IssueActivated));
|
||||
AddChild(issuesList);
|
||||
}
|
||||
|
||||
private MonoBuildTab()
|
||||
{
|
||||
}
|
||||
|
||||
public MonoBuildTab(MonoBuildInfo buildInfo)
|
||||
{
|
||||
BuildInfo = buildInfo;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
using GodotTools.Core;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GodotTools.Internals;
|
||||
|
||||
namespace GodotSharpTools.Editor
|
||||
namespace GodotTools
|
||||
{
|
||||
public class MonoDevelopInstance
|
||||
{
|
||||
|
@ -15,24 +15,24 @@ namespace GodotSharpTools.Editor
|
|||
VisualStudioForMac = 1
|
||||
}
|
||||
|
||||
readonly string solutionFile;
|
||||
readonly EditorId editorId;
|
||||
private readonly string solutionFile;
|
||||
private readonly EditorId editorId;
|
||||
|
||||
Process process;
|
||||
private Process process;
|
||||
|
||||
public void Execute(string[] files)
|
||||
public void Execute(params string[] files)
|
||||
{
|
||||
bool newWindow = process == null || process.HasExited;
|
||||
|
||||
List<string> args = new List<string>();
|
||||
var args = new List<string>();
|
||||
|
||||
string command;
|
||||
|
||||
if (Utils.OS.IsOSX())
|
||||
{
|
||||
string bundleId = codeEditorBundleIds[editorId];
|
||||
string bundleId = CodeEditorBundleIds[editorId];
|
||||
|
||||
if (IsApplicationBundleInstalled(bundleId))
|
||||
if (Internal.IsOsxAppBundleInstalled(bundleId))
|
||||
{
|
||||
command = "open";
|
||||
|
||||
|
@ -47,12 +47,12 @@ namespace GodotSharpTools.Editor
|
|||
}
|
||||
else
|
||||
{
|
||||
command = codeEditorPaths[editorId];
|
||||
command = CodeEditorPaths[editorId];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
command = codeEditorPaths[editorId];
|
||||
command = CodeEditorPaths[editorId];
|
||||
}
|
||||
|
||||
args.Add("--ipc-tcp");
|
||||
|
@ -72,7 +72,7 @@ namespace GodotSharpTools.Editor
|
|||
|
||||
if (newWindow)
|
||||
{
|
||||
process = Process.Start(new ProcessStartInfo()
|
||||
process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = command,
|
||||
Arguments = string.Join(" ", args),
|
||||
|
@ -81,12 +81,12 @@ namespace GodotSharpTools.Editor
|
|||
}
|
||||
else
|
||||
{
|
||||
Process.Start(new ProcessStartInfo()
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = command,
|
||||
Arguments = string.Join(" ", args),
|
||||
UseShellExecute = false
|
||||
});
|
||||
})?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,45 +99,42 @@ namespace GodotSharpTools.Editor
|
|||
this.editorId = editorId;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private extern static bool IsApplicationBundleInstalled(string bundleId);
|
||||
|
||||
static readonly IReadOnlyDictionary<EditorId, string> codeEditorPaths;
|
||||
static readonly IReadOnlyDictionary<EditorId, string> codeEditorBundleIds;
|
||||
private static readonly IReadOnlyDictionary<EditorId, string> CodeEditorPaths;
|
||||
private static readonly IReadOnlyDictionary<EditorId, string> CodeEditorBundleIds;
|
||||
|
||||
static MonoDevelopInstance()
|
||||
{
|
||||
if (Utils.OS.IsOSX())
|
||||
{
|
||||
codeEditorPaths = new Dictionary<EditorId, string>
|
||||
CodeEditorPaths = new Dictionary<EditorId, string>
|
||||
{
|
||||
// Rely on PATH
|
||||
{ EditorId.MonoDevelop, "monodevelop" },
|
||||
{ EditorId.VisualStudioForMac, "VisualStudio" }
|
||||
{EditorId.MonoDevelop, "monodevelop"},
|
||||
{EditorId.VisualStudioForMac, "VisualStudio"}
|
||||
};
|
||||
codeEditorBundleIds = new Dictionary<EditorId, string>
|
||||
CodeEditorBundleIds = new Dictionary<EditorId, string>
|
||||
{
|
||||
// TODO EditorId.MonoDevelop
|
||||
{ EditorId.VisualStudioForMac, "com.microsoft.visual-studio" }
|
||||
{EditorId.VisualStudioForMac, "com.microsoft.visual-studio"}
|
||||
};
|
||||
}
|
||||
else if (Utils.OS.IsWindows())
|
||||
{
|
||||
codeEditorPaths = new Dictionary<EditorId, string>
|
||||
CodeEditorPaths = new Dictionary<EditorId, string>
|
||||
{
|
||||
// XamarinStudio is no longer a thing, and the latest version is quite old
|
||||
// MonoDevelop is available from source only on Windows. The recommendation
|
||||
// is to use Visual Studio instead. Since there are no official builds, we
|
||||
// will rely on custom MonoDevelop builds being added to PATH.
|
||||
{ EditorId.MonoDevelop, "MonoDevelop.exe" }
|
||||
{EditorId.MonoDevelop, "MonoDevelop.exe"}
|
||||
};
|
||||
}
|
||||
else if (Utils.OS.IsUnix())
|
||||
{
|
||||
codeEditorPaths = new Dictionary<EditorId, string>
|
||||
CodeEditorPaths = new Dictionary<EditorId, string>
|
||||
{
|
||||
// Rely on PATH
|
||||
{ EditorId.MonoDevelop, "monodevelop" }
|
||||
{EditorId.MonoDevelop, "monodevelop"}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
|
||||
[assembly: AssemblyTitle("GodotTools")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("Godot Engine contributors")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes are used to specify the signing key for the assembly,
|
||||
// if desired. See the Mono documentation for more information about signing.
|
||||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("")]
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GodotTools.Utils
|
||||
{
|
||||
public static class CollectionExtensions
|
||||
{
|
||||
public static T SelectFirstNotNull<T>(this IEnumerable<T> enumerable, Func<T, T> predicate, T orElse = null)
|
||||
where T : class
|
||||
{
|
||||
foreach (T elem in enumerable)
|
||||
{
|
||||
if (predicate(elem) != null)
|
||||
return elem;
|
||||
}
|
||||
|
||||
return orElse;
|
||||
}
|
||||
}
|
||||
}
|
40
modules/mono/editor/GodotTools/GodotTools/Utils/Directory.cs
Normal file
40
modules/mono/editor/GodotTools/GodotTools/Utils/Directory.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using System.IO;
|
||||
using Godot;
|
||||
|
||||
namespace GodotTools.Utils
|
||||
{
|
||||
public static class Directory
|
||||
{
|
||||
private static string GlobalizePath(this string path)
|
||||
{
|
||||
return ProjectSettings.GlobalizePath(path);
|
||||
}
|
||||
|
||||
public static bool Exists(string path)
|
||||
{
|
||||
return System.IO.Directory.Exists(path.GlobalizePath());
|
||||
}
|
||||
|
||||
/// Create directory recursively
|
||||
public static DirectoryInfo CreateDirectory(string path)
|
||||
{
|
||||
return System.IO.Directory.CreateDirectory(path.GlobalizePath());
|
||||
}
|
||||
|
||||
public static void Delete(string path, bool recursive)
|
||||
{
|
||||
System.IO.Directory.Delete(path.GlobalizePath(), recursive);
|
||||
}
|
||||
|
||||
|
||||
public static string[] GetDirectories(string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
return System.IO.Directory.GetDirectories(path.GlobalizePath(), searchPattern, searchOption);
|
||||
}
|
||||
|
||||
public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
return System.IO.Directory.GetFiles(path.GlobalizePath(), searchPattern, searchOption);
|
||||
}
|
||||
}
|
||||
}
|
43
modules/mono/editor/GodotTools/GodotTools/Utils/File.cs
Normal file
43
modules/mono/editor/GodotTools/GodotTools/Utils/File.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using Godot;
|
||||
|
||||
namespace GodotTools.Utils
|
||||
{
|
||||
public static class File
|
||||
{
|
||||
private static string GlobalizePath(this string path)
|
||||
{
|
||||
return ProjectSettings.GlobalizePath(path);
|
||||
}
|
||||
|
||||
public static void WriteAllText(string path, string contents)
|
||||
{
|
||||
System.IO.File.WriteAllText(path.GlobalizePath(), contents);
|
||||
}
|
||||
|
||||
public static bool Exists(string path)
|
||||
{
|
||||
return System.IO.File.Exists(path.GlobalizePath());
|
||||
}
|
||||
|
||||
public static DateTime GetLastWriteTime(string path)
|
||||
{
|
||||
return System.IO.File.GetLastWriteTime(path.GlobalizePath());
|
||||
}
|
||||
|
||||
public static void Delete(string path)
|
||||
{
|
||||
System.IO.File.Delete(path.GlobalizePath());
|
||||
}
|
||||
|
||||
public static void Copy(string sourceFileName, string destFileName)
|
||||
{
|
||||
System.IO.File.Copy(sourceFileName.GlobalizePath(), destFileName.GlobalizePath(), overwrite: true);
|
||||
}
|
||||
|
||||
public static byte[] ReadAllBytes(string path)
|
||||
{
|
||||
return System.IO.File.ReadAllBytes(path.GlobalizePath());
|
||||
}
|
||||
}
|
||||
}
|
127
modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
Normal file
127
modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
Normal file
|
@ -0,0 +1,127 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace GodotTools.Utils
|
||||
{
|
||||
public static class OS
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
extern static string GetPlatformName();
|
||||
|
||||
const string HaikuName = "Haiku";
|
||||
const string OSXName = "OSX";
|
||||
const string ServerName = "Server";
|
||||
const string UWPName = "UWP";
|
||||
const string WindowsName = "Windows";
|
||||
const string X11Name = "X11";
|
||||
|
||||
public static bool IsHaiku()
|
||||
{
|
||||
return HaikuName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsOSX()
|
||||
{
|
||||
return OSXName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsServer()
|
||||
{
|
||||
return ServerName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsUWP()
|
||||
{
|
||||
return UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsWindows()
|
||||
{
|
||||
return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsX11()
|
||||
{
|
||||
return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static bool? _isUnixCache;
|
||||
private static readonly string[] UnixPlatforms = {HaikuName, OSXName, ServerName, X11Name};
|
||||
|
||||
public static bool IsUnix()
|
||||
{
|
||||
if (_isUnixCache.HasValue)
|
||||
return _isUnixCache.Value;
|
||||
|
||||
string osName = GetPlatformName();
|
||||
_isUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase));
|
||||
return _isUnixCache.Value;
|
||||
}
|
||||
|
||||
public static char PathSep => IsWindows() ? ';' : ':';
|
||||
|
||||
public static string PathWhich(string name)
|
||||
{
|
||||
string[] windowsExts = IsWindows() ? Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) : null;
|
||||
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
|
||||
|
||||
var searchDirs = new List<string>();
|
||||
|
||||
if (pathDirs != null)
|
||||
searchDirs.AddRange(pathDirs);
|
||||
|
||||
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
|
||||
|
||||
foreach (var dir in searchDirs)
|
||||
{
|
||||
string path = Path.Combine(dir, name);
|
||||
|
||||
if (IsWindows() && windowsExts != null)
|
||||
{
|
||||
foreach (var extension in windowsExts)
|
||||
{
|
||||
string pathWithExtension = path + extension;
|
||||
|
||||
if (File.Exists(pathWithExtension))
|
||||
return pathWithExtension;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (File.Exists(path))
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void RunProcess(string command, IEnumerable<string> arguments)
|
||||
{
|
||||
string CmdLineArgsToString(IEnumerable<string> args)
|
||||
{
|
||||
return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg));
|
||||
}
|
||||
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments))
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false
|
||||
};
|
||||
|
||||
using (Process process = Process.Start(startInfo))
|
||||
{
|
||||
if (process == null)
|
||||
throw new Exception("No process was started");
|
||||
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -861,26 +861,22 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
|
|||
p_output.append("\n#pragma warning restore CS1591\n");
|
||||
}
|
||||
|
||||
Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution) {
|
||||
|
||||
String proj_dir = p_solution_dir.plus_file(CORE_API_ASSEMBLY_NAME);
|
||||
Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir, Vector<String> &r_compile_items) {
|
||||
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
|
||||
|
||||
if (!DirAccess::exists(proj_dir)) {
|
||||
Error err = da->make_dir_recursive(proj_dir);
|
||||
if (!DirAccess::exists(p_proj_dir)) {
|
||||
Error err = da->make_dir_recursive(p_proj_dir);
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
|
||||
}
|
||||
|
||||
da->change_dir(proj_dir);
|
||||
da->change_dir(p_proj_dir);
|
||||
da->make_dir("Core");
|
||||
da->make_dir("ObjectType");
|
||||
|
||||
String core_dir = path_join(proj_dir, "Core");
|
||||
String obj_type_dir = path_join(proj_dir, "ObjectType");
|
||||
|
||||
Vector<String> compile_items;
|
||||
String core_dir = path_join(p_proj_dir, "Core");
|
||||
String obj_type_dir = path_join(p_proj_dir, "ObjectType");
|
||||
|
||||
// Generate source file for global scope constants and enums
|
||||
{
|
||||
|
@ -891,7 +887,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
|
|||
if (save_err != OK)
|
||||
return save_err;
|
||||
|
||||
compile_items.push_back(output_file);
|
||||
r_compile_items.push_back(output_file);
|
||||
}
|
||||
|
||||
for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
|
||||
|
@ -909,7 +905,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
|
|||
if (err != OK)
|
||||
return err;
|
||||
|
||||
compile_items.push_back(output_file);
|
||||
r_compile_items.push_back(output_file);
|
||||
}
|
||||
|
||||
// Generate sources from compressed files
|
||||
|
@ -939,7 +935,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
|
|||
file->store_buffer(data.ptr(), data.size());
|
||||
file->close();
|
||||
|
||||
compile_items.push_back(output_file);
|
||||
r_compile_items.push_back(output_file);
|
||||
}
|
||||
|
||||
StringBuilder cs_icalls_content;
|
||||
|
@ -981,43 +977,27 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
|
|||
if (err != OK)
|
||||
return err;
|
||||
|
||||
compile_items.push_back(internal_methods_file);
|
||||
|
||||
String guid = CSharpProject::generate_core_api_project(proj_dir, compile_items);
|
||||
|
||||
DotNetSolution::ProjectInfo proj_info;
|
||||
proj_info.guid = guid;
|
||||
proj_info.relpath = String(CORE_API_ASSEMBLY_NAME).plus_file(CORE_API_ASSEMBLY_NAME ".csproj");
|
||||
proj_info.configs.push_back("Debug");
|
||||
proj_info.configs.push_back("Release");
|
||||
|
||||
r_solution.add_new_project(CORE_API_ASSEMBLY_NAME, proj_info);
|
||||
|
||||
_log("The solution and C# project for the Core API was generated successfully\n");
|
||||
r_compile_items.push_back(internal_methods_file);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution) {
|
||||
|
||||
String proj_dir = p_solution_dir.plus_file(EDITOR_API_ASSEMBLY_NAME);
|
||||
Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir, Vector<String> &r_compile_items) {
|
||||
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
|
||||
|
||||
if (!DirAccess::exists(proj_dir)) {
|
||||
Error err = da->make_dir_recursive(proj_dir);
|
||||
if (!DirAccess::exists(p_proj_dir)) {
|
||||
Error err = da->make_dir_recursive(p_proj_dir);
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
|
||||
}
|
||||
|
||||
da->change_dir(proj_dir);
|
||||
da->change_dir(p_proj_dir);
|
||||
da->make_dir("Core");
|
||||
da->make_dir("ObjectType");
|
||||
|
||||
String core_dir = path_join(proj_dir, "Core");
|
||||
String obj_type_dir = path_join(proj_dir, "ObjectType");
|
||||
|
||||
Vector<String> compile_items;
|
||||
String core_dir = path_join(p_proj_dir, "Core");
|
||||
String obj_type_dir = path_join(p_proj_dir, "ObjectType");
|
||||
|
||||
for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
|
||||
const TypeInterface &itype = E.get();
|
||||
|
@ -1034,7 +1014,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir
|
|||
if (err != OK)
|
||||
return err;
|
||||
|
||||
compile_items.push_back(output_file);
|
||||
r_compile_items.push_back(output_file);
|
||||
}
|
||||
|
||||
StringBuilder cs_icalls_content;
|
||||
|
@ -1077,58 +1057,56 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir
|
|||
if (err != OK)
|
||||
return err;
|
||||
|
||||
compile_items.push_back(internal_methods_file);
|
||||
|
||||
String guid = CSharpProject::generate_editor_api_project(proj_dir, "../" CORE_API_ASSEMBLY_NAME "/" CORE_API_ASSEMBLY_NAME ".csproj", compile_items);
|
||||
|
||||
DotNetSolution::ProjectInfo proj_info;
|
||||
proj_info.guid = guid;
|
||||
proj_info.relpath = String(EDITOR_API_ASSEMBLY_NAME).plus_file(EDITOR_API_ASSEMBLY_NAME ".csproj");
|
||||
proj_info.configs.push_back("Debug");
|
||||
proj_info.configs.push_back("Release");
|
||||
|
||||
r_solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, proj_info);
|
||||
|
||||
_log("The solution and C# project for the Editor API was generated successfully\n");
|
||||
r_compile_items.push_back(internal_methods_file);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error BindingsGenerator::generate_cs_api(const String &p_output_dir) {
|
||||
|
||||
String output_dir = DirAccess::get_full_path(p_output_dir, DirAccess::ACCESS_FILESYSTEM);
|
||||
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
|
||||
|
||||
if (!DirAccess::exists(p_output_dir)) {
|
||||
Error err = da->make_dir_recursive(p_output_dir);
|
||||
if (!DirAccess::exists(output_dir)) {
|
||||
Error err = da->make_dir_recursive(output_dir);
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
|
||||
}
|
||||
|
||||
DotNetSolution solution(API_SOLUTION_NAME);
|
||||
|
||||
if (!solution.set_path(p_output_dir))
|
||||
return ERR_FILE_NOT_FOUND;
|
||||
|
||||
Error proj_err;
|
||||
|
||||
proj_err = generate_cs_core_project(p_output_dir, solution);
|
||||
// Generate GodotSharp source files
|
||||
|
||||
String core_proj_dir = output_dir.plus_file(CORE_API_ASSEMBLY_NAME);
|
||||
Vector<String> core_compile_items;
|
||||
|
||||
proj_err = generate_cs_core_project(core_proj_dir, core_compile_items);
|
||||
if (proj_err != OK) {
|
||||
ERR_PRINT("Generation of the Core API C# project failed");
|
||||
return proj_err;
|
||||
}
|
||||
|
||||
proj_err = generate_cs_editor_project(p_output_dir, solution);
|
||||
// Generate GodotSharpEditor source files
|
||||
|
||||
String editor_proj_dir = output_dir.plus_file(EDITOR_API_ASSEMBLY_NAME);
|
||||
Vector<String> editor_compile_items;
|
||||
|
||||
proj_err = generate_cs_editor_project(editor_proj_dir, editor_compile_items);
|
||||
if (proj_err != OK) {
|
||||
ERR_PRINT("Generation of the Editor API C# project failed");
|
||||
return proj_err;
|
||||
}
|
||||
|
||||
Error sln_error = solution.save();
|
||||
if (sln_error != OK) {
|
||||
ERR_PRINT("Failed to save API solution");
|
||||
return sln_error;
|
||||
// Generate solution
|
||||
|
||||
if (!CSharpProject::generate_api_solution(output_dir,
|
||||
core_proj_dir, core_compile_items, editor_proj_dir, editor_compile_items)) {
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
_log("The solution for the Godot API was generated successfully\n");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -1311,8 +1289,9 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
|
|||
output.append(MEMBER_BEGIN "private static Godot.Object singleton;\n");
|
||||
output.append(MEMBER_BEGIN "public static Godot.Object Singleton\n" INDENT2 "{\n" INDENT3
|
||||
"get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" INDENT5
|
||||
"singleton = Engine.GetSingleton(" BINDINGS_NATIVE_NAME_FIELD ");\n" INDENT4
|
||||
"return singleton;\n" INDENT3 "}\n" INDENT2 "}\n");
|
||||
"singleton = Engine.GetSingleton(typeof(");
|
||||
output.append(itype.proxy_name);
|
||||
output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n");
|
||||
|
||||
output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
|
||||
output.append(itype.name);
|
||||
|
@ -2347,6 +2326,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
|
|||
imethod.return_type.is_enum = true;
|
||||
} else if (return_info.class_name != StringName()) {
|
||||
imethod.return_type.cname = return_info.class_name;
|
||||
if (!imethod.is_virtual && ClassDB::is_parent_class(return_info.class_name, name_cache.type_Reference) && return_info.hint != PROPERTY_HINT_RESOURCE_TYPE) {
|
||||
/* clang-format off */
|
||||
ERR_PRINTS("Return type is reference but hint is not " _STR(PROPERTY_HINT_RESOURCE_TYPE) "."
|
||||
" Are you returning a reference type by pointer? Method: " + itype.name + "." + imethod.name);
|
||||
/* clang-format on */
|
||||
ERR_FAIL();
|
||||
}
|
||||
} else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
|
||||
imethod.return_type.cname = return_info.hint_string;
|
||||
} else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
|
||||
|
@ -3018,36 +3004,49 @@ void BindingsGenerator::_initialize() {
|
|||
void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
|
||||
|
||||
const int NUM_OPTIONS = 2;
|
||||
String mono_glue_option = "--generate-mono-glue";
|
||||
String cs_api_option = "--generate-cs-api";
|
||||
String generate_all_glue_option = "--generate-mono-glue";
|
||||
String generate_cs_glue_option = "--generate-mono-cs-glue";
|
||||
String generate_cpp_glue_option = "--generate-mono-cpp-glue";
|
||||
|
||||
String mono_glue_path;
|
||||
String cs_api_path;
|
||||
String glue_dir_path;
|
||||
String cs_dir_path;
|
||||
String cpp_dir_path;
|
||||
|
||||
int options_left = NUM_OPTIONS;
|
||||
|
||||
const List<String>::Element *elem = p_cmdline_args.front();
|
||||
|
||||
while (elem && options_left) {
|
||||
if (elem->get() == mono_glue_option) {
|
||||
if (elem->get() == generate_all_glue_option) {
|
||||
const List<String>::Element *path_elem = elem->next();
|
||||
|
||||
if (path_elem) {
|
||||
mono_glue_path = path_elem->get();
|
||||
glue_dir_path = path_elem->get();
|
||||
elem = elem->next();
|
||||
} else {
|
||||
ERR_PRINTS(mono_glue_option + ": No output directory specified");
|
||||
ERR_PRINTS(generate_all_glue_option + ": No output directory specified (expected path to {GODOT_ROOT}/modules/mono/glue)");
|
||||
}
|
||||
|
||||
--options_left;
|
||||
} else if (elem->get() == cs_api_option) {
|
||||
} else if (elem->get() == generate_cs_glue_option) {
|
||||
const List<String>::Element *path_elem = elem->next();
|
||||
|
||||
if (path_elem) {
|
||||
cs_api_path = path_elem->get();
|
||||
cs_dir_path = path_elem->get();
|
||||
elem = elem->next();
|
||||
} else {
|
||||
ERR_PRINTS(cs_api_option + ": No output directory specified");
|
||||
ERR_PRINTS(generate_cs_glue_option + ": No output directory specified");
|
||||
}
|
||||
|
||||
--options_left;
|
||||
} else if (elem->get() == generate_cpp_glue_option) {
|
||||
const List<String>::Element *path_elem = elem->next();
|
||||
|
||||
if (path_elem) {
|
||||
cpp_dir_path = path_elem->get();
|
||||
elem = elem->next();
|
||||
} else {
|
||||
ERR_PRINTS(generate_cpp_glue_option + ": No output directory specified");
|
||||
}
|
||||
|
||||
--options_left;
|
||||
|
@ -3056,18 +3055,26 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
|
|||
elem = elem->next();
|
||||
}
|
||||
|
||||
if (mono_glue_path.length() || cs_api_path.length()) {
|
||||
if (glue_dir_path.length() || cs_dir_path.length() || cpp_dir_path.length()) {
|
||||
BindingsGenerator bindings_generator;
|
||||
bindings_generator.set_log_print_enabled(true);
|
||||
|
||||
if (mono_glue_path.length()) {
|
||||
if (bindings_generator.generate_glue(mono_glue_path) != OK)
|
||||
ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue");
|
||||
if (glue_dir_path.length()) {
|
||||
if (bindings_generator.generate_glue(glue_dir_path) != OK)
|
||||
ERR_PRINTS(generate_all_glue_option + ": Failed to generate the C++ glue");
|
||||
|
||||
if (bindings_generator.generate_cs_api(glue_dir_path.plus_file("Managed/Generated")) != OK)
|
||||
ERR_PRINTS(generate_all_glue_option + ": Failed to generate the C# API");
|
||||
}
|
||||
|
||||
if (cs_api_path.length()) {
|
||||
if (bindings_generator.generate_cs_api(cs_api_path) != OK)
|
||||
ERR_PRINTS(cs_api_option + ": Failed to generate the C# API");
|
||||
if (cs_dir_path.length()) {
|
||||
if (bindings_generator.generate_cs_api(cs_dir_path) != OK)
|
||||
ERR_PRINTS(generate_cs_glue_option + ": Failed to generate the C# API");
|
||||
}
|
||||
|
||||
if (cpp_dir_path.length()) {
|
||||
if (bindings_generator.generate_glue(cpp_dir_path) != OK)
|
||||
ERR_PRINTS(generate_cpp_glue_option + ": Failed to generate the C++ glue");
|
||||
}
|
||||
|
||||
// Exit once done
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
#include "core/class_db.h"
|
||||
#include "core/string_builder.h"
|
||||
#include "dotnet_solution.h"
|
||||
#include "editor/doc/doc_data.h"
|
||||
#include "editor/editor_help.h"
|
||||
|
||||
|
@ -614,12 +613,13 @@ class BindingsGenerator {
|
|||
void _initialize();
|
||||
|
||||
public:
|
||||
Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution);
|
||||
Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution);
|
||||
Error generate_cs_core_project(const String &p_proj_dir, Vector<String> &r_compile_files);
|
||||
Error generate_cs_editor_project(const String &p_proj_dir, Vector<String> &r_compile_items);
|
||||
Error generate_cs_api(const String &p_output_dir);
|
||||
Error generate_glue(const String &p_output_dir);
|
||||
|
||||
void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; }
|
||||
_FORCE_INLINE_ bool is_log_print_enabled() { return log_print_enabled; }
|
||||
_FORCE_INLINE_ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; }
|
||||
|
||||
static uint32_t get_version();
|
||||
|
||||
|
|
|
@ -44,66 +44,54 @@
|
|||
|
||||
namespace CSharpProject {
|
||||
|
||||
String generate_core_api_project(const String &p_dir, const Vector<String> &p_files) {
|
||||
bool generate_api_solution_impl(const String &p_solution_dir, const String &p_core_proj_dir, const Vector<String> &p_core_compile_items,
|
||||
const String &p_editor_proj_dir, const Vector<String> &p_editor_compile_items,
|
||||
GDMonoAssembly *p_tools_project_editor_assembly) {
|
||||
|
||||
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
|
||||
GDMonoClass *klass = p_tools_project_editor_assembly->get_class("GodotTools.ProjectEditor", "ApiSolutionGenerator");
|
||||
|
||||
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator");
|
||||
|
||||
Variant dir = p_dir;
|
||||
Variant compile_items = p_files;
|
||||
const Variant *args[2] = { &dir, &compile_items };
|
||||
Variant solution_dir = p_solution_dir;
|
||||
Variant core_proj_dir = p_core_proj_dir;
|
||||
Variant core_compile_items = p_core_compile_items;
|
||||
Variant editor_proj_dir = p_editor_proj_dir;
|
||||
Variant editor_compile_items = p_editor_compile_items;
|
||||
const Variant *args[5] = { &solution_dir, &core_proj_dir, &core_compile_items, &editor_proj_dir, &editor_compile_items };
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc);
|
||||
klass->get_method("GenerateApiSolution", 5)->invoke(NULL, args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL_V(String());
|
||||
ERR_FAIL_V(false);
|
||||
}
|
||||
|
||||
return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String();
|
||||
return true;
|
||||
}
|
||||
|
||||
String generate_editor_api_project(const String &p_dir, const String &p_core_proj_path, const Vector<String> &p_files) {
|
||||
bool generate_api_solution(const String &p_solution_dir, const String &p_core_proj_dir, const Vector<String> &p_core_compile_items,
|
||||
const String &p_editor_proj_dir, const Vector<String> &p_editor_compile_items) {
|
||||
|
||||
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
|
||||
if (GDMono::get_singleton()->get_tools_project_editor_assembly()) {
|
||||
return generate_api_solution_impl(p_solution_dir, p_core_proj_dir, p_core_compile_items,
|
||||
p_editor_proj_dir, p_editor_compile_items,
|
||||
GDMono::get_singleton()->get_tools_project_editor_assembly());
|
||||
} else {
|
||||
MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.ApiSolutionGenerationDomain");
|
||||
CRASH_COND(temp_domain == NULL);
|
||||
_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain);
|
||||
|
||||
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator");
|
||||
_GDMONO_SCOPE_DOMAIN_(temp_domain);
|
||||
|
||||
Variant dir = p_dir;
|
||||
Variant core_proj_path = p_core_proj_path;
|
||||
Variant compile_items = p_files;
|
||||
const Variant *args[3] = { &dir, &core_proj_path, &compile_items };
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc);
|
||||
GDMonoAssembly *tools_project_editor_assembly = NULL;
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL_V(String());
|
||||
if (!GDMono::get_singleton()->load_assembly("GodotTools.ProjectEditor", &tools_project_editor_assembly)) {
|
||||
ERR_EXPLAIN("Failed to load assembly: 'GodotTools.ProjectEditor'");
|
||||
ERR_FAIL_V(false);
|
||||
}
|
||||
|
||||
return generate_api_solution_impl(p_solution_dir, p_core_proj_dir, p_core_compile_items,
|
||||
p_editor_proj_dir, p_editor_compile_items,
|
||||
tools_project_editor_assembly);
|
||||
}
|
||||
|
||||
return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String();
|
||||
}
|
||||
|
||||
String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files) {
|
||||
|
||||
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
|
||||
|
||||
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator");
|
||||
|
||||
Variant dir = p_dir;
|
||||
Variant name = p_name;
|
||||
Variant compile_items = p_files;
|
||||
const Variant *args[3] = { &dir, &name, &compile_items };
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL_V(String());
|
||||
}
|
||||
|
||||
return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String();
|
||||
}
|
||||
|
||||
void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) {
|
||||
|
@ -111,9 +99,9 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
|
|||
if (!GLOBAL_DEF("mono/project/auto_update_project", true))
|
||||
return;
|
||||
|
||||
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
|
||||
GDMonoAssembly *tools_project_editor_assembly = GDMono::get_singleton()->get_tools_project_editor_assembly();
|
||||
|
||||
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils");
|
||||
GDMonoClass *klass = tools_project_editor_assembly->get_class("GodotTools.ProjectEditor", "ProjectUtils");
|
||||
|
||||
Variant project_path = p_project_path;
|
||||
Variant item_type = p_item_type;
|
||||
|
@ -128,126 +116,4 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
|
|||
}
|
||||
}
|
||||
|
||||
Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path) {
|
||||
|
||||
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
|
||||
|
||||
if (FileAccess::exists(p_output_path)) {
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
||||
Error rm_err = da->remove(p_output_path);
|
||||
|
||||
ERR_EXPLAIN("Failed to remove old scripts metadata file");
|
||||
ERR_FAIL_COND_V(rm_err != OK, rm_err);
|
||||
}
|
||||
|
||||
GDMonoClass *project_utils = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils");
|
||||
|
||||
void *args[2] = {
|
||||
GDMonoMarshal::mono_string_from_godot(p_project_path),
|
||||
GDMonoMarshal::mono_string_from_godot("Compile")
|
||||
};
|
||||
|
||||
MonoException *exc = NULL;
|
||||
MonoArray *ret = (MonoArray *)project_utils->get_method("GetIncludeFiles", 2)->invoke_raw(NULL, args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL_V(FAILED);
|
||||
}
|
||||
|
||||
PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret);
|
||||
PoolStringArray::Read r = project_files.read();
|
||||
|
||||
Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata_or_nothing();
|
||||
Dictionary new_dict;
|
||||
|
||||
for (int i = 0; i < project_files.size(); i++) {
|
||||
const String &project_file = ("res://" + r[i]).simplify_path();
|
||||
|
||||
uint64_t modified_time = FileAccess::get_modified_time(project_file);
|
||||
|
||||
const Variant *old_file_var = old_dict.getptr(project_file);
|
||||
if (old_file_var) {
|
||||
Dictionary old_file_dict = old_file_var->operator Dictionary();
|
||||
|
||||
if (old_file_dict["modified_time"].operator uint64_t() == modified_time) {
|
||||
// No changes so no need to parse again
|
||||
new_dict[project_file] = old_file_dict;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ScriptClassParser scp;
|
||||
Error err = scp.parse_file(project_file);
|
||||
if (err != OK) {
|
||||
ERR_PRINTS("Parse error: " + scp.get_error());
|
||||
ERR_EXPLAIN("Failed to determine namespace and class for script: " + project_file);
|
||||
ERR_FAIL_V(err);
|
||||
}
|
||||
|
||||
Vector<ScriptClassParser::ClassDecl> classes = scp.get_classes();
|
||||
|
||||
bool found = false;
|
||||
Dictionary class_dict;
|
||||
|
||||
String search_name = project_file.get_file().get_basename();
|
||||
|
||||
for (int j = 0; j < classes.size(); j++) {
|
||||
const ScriptClassParser::ClassDecl &class_decl = classes[j];
|
||||
|
||||
if (class_decl.base.size() == 0)
|
||||
continue; // Does not inherit nor implement anything, so it can't be a script class
|
||||
|
||||
String class_cmp;
|
||||
|
||||
if (class_decl.nested) {
|
||||
class_cmp = class_decl.name.get_slice(".", class_decl.name.get_slice_count(".") - 1);
|
||||
} else {
|
||||
class_cmp = class_decl.name;
|
||||
}
|
||||
|
||||
if (class_cmp != search_name)
|
||||
continue;
|
||||
|
||||
class_dict["namespace"] = class_decl.namespace_;
|
||||
class_dict["class_name"] = class_decl.name;
|
||||
class_dict["nested"] = class_decl.nested;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
Dictionary file_dict;
|
||||
file_dict["modified_time"] = modified_time;
|
||||
file_dict["class"] = class_dict;
|
||||
new_dict[project_file] = file_dict;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_dict.size()) {
|
||||
String json = JSON::print(new_dict, "", false);
|
||||
|
||||
String base_dir = p_output_path.get_base_dir();
|
||||
|
||||
if (!DirAccess::exists(base_dir)) {
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
||||
|
||||
Error err = da->make_dir_recursive(base_dir);
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
|
||||
}
|
||||
|
||||
Error ferr;
|
||||
FileAccess *f = FileAccess::open(p_output_path, FileAccess::WRITE, &ferr);
|
||||
ERR_EXPLAIN("Cannot open file for writing: " + p_output_path);
|
||||
ERR_FAIL_COND_V(ferr != OK, ferr);
|
||||
f->store_string(json);
|
||||
f->flush();
|
||||
f->close();
|
||||
memdelete(f);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace CSharpProject
|
||||
|
|
|
@ -35,14 +35,11 @@
|
|||
|
||||
namespace CSharpProject {
|
||||
|
||||
String generate_core_api_project(const String &p_dir, const Vector<String> &p_files = Vector<String>());
|
||||
String generate_editor_api_project(const String &p_dir, const String &p_core_proj_path, const Vector<String> &p_files = Vector<String>());
|
||||
String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files = Vector<String>());
|
||||
bool generate_api_solution(const String &p_solution_dir, const String &p_core_proj_dir, const Vector<String> &p_core_compile_items,
|
||||
const String &p_editor_proj_dir, const Vector<String> &p_editor_compile_items);
|
||||
|
||||
void add_item(const String &p_project_path, const String &p_item_type, const String &p_include);
|
||||
|
||||
Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path);
|
||||
|
||||
} // namespace CSharpProject
|
||||
|
||||
#endif // CSHARP_PROJECT_H
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* dotnet_solution.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 "dotnet_solution.h"
|
||||
|
||||
#include "core/os/dir_access.h"
|
||||
#include "core/os/file_access.h"
|
||||
|
||||
#include "../utils/path_utils.h"
|
||||
#include "../utils/string_utils.h"
|
||||
#include "csharp_project.h"
|
||||
|
||||
#define SOLUTION_TEMPLATE \
|
||||
"Microsoft Visual Studio Solution File, Format Version 12.00\n" \
|
||||
"# Visual Studio 2012\n" \
|
||||
"%0\n" \
|
||||
"Global\n" \
|
||||
"\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n" \
|
||||
"%1\n" \
|
||||
"\tEndGlobalSection\n" \
|
||||
"\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n" \
|
||||
"%2\n" \
|
||||
"\tEndGlobalSection\n" \
|
||||
"EndGlobal\n"
|
||||
|
||||
#define PROJECT_DECLARATION "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"%0\", \"%1\", \"{%2}\"\nEndProject"
|
||||
|
||||
#define SOLUTION_PLATFORMS_CONFIG "\t%0|Any CPU = %0|Any CPU"
|
||||
|
||||
#define PROJECT_PLATFORMS_CONFIG \
|
||||
"\t\t{%0}.%1|Any CPU.ActiveCfg = %1|Any CPU\n" \
|
||||
"\t\t{%0}.%1|Any CPU.Build.0 = %1|Any CPU"
|
||||
|
||||
void DotNetSolution::add_new_project(const String &p_name, const ProjectInfo &p_project_info) {
|
||||
projects[p_name] = p_project_info;
|
||||
}
|
||||
|
||||
bool DotNetSolution::has_project(const String &p_name) const {
|
||||
return projects.find(p_name) != NULL;
|
||||
}
|
||||
|
||||
const DotNetSolution::ProjectInfo &DotNetSolution::get_project_info(const String &p_name) const {
|
||||
return projects[p_name];
|
||||
}
|
||||
|
||||
bool DotNetSolution::remove_project(const String &p_name) {
|
||||
return projects.erase(p_name);
|
||||
}
|
||||
|
||||
Error DotNetSolution::save() {
|
||||
bool dir_exists = DirAccess::exists(path);
|
||||
ERR_EXPLAIN("The directory does not exist.");
|
||||
ERR_FAIL_COND_V(!dir_exists, ERR_FILE_NOT_FOUND);
|
||||
|
||||
String projs_decl;
|
||||
String sln_platform_cfg;
|
||||
String proj_platform_cfg;
|
||||
|
||||
for (Map<String, ProjectInfo>::Element *E = projects.front(); E; E = E->next()) {
|
||||
const String &name = E->key();
|
||||
const ProjectInfo &proj_info = E->value();
|
||||
|
||||
bool is_front = E == projects.front();
|
||||
|
||||
if (!is_front)
|
||||
projs_decl += "\n";
|
||||
|
||||
projs_decl += sformat(PROJECT_DECLARATION, name, proj_info.relpath.replace("/", "\\"), proj_info.guid);
|
||||
|
||||
for (int i = 0; i < proj_info.configs.size(); i++) {
|
||||
const String &config = proj_info.configs[i];
|
||||
|
||||
if (i != 0 || !is_front) {
|
||||
sln_platform_cfg += "\n";
|
||||
proj_platform_cfg += "\n";
|
||||
}
|
||||
|
||||
sln_platform_cfg += sformat(SOLUTION_PLATFORMS_CONFIG, config);
|
||||
proj_platform_cfg += sformat(PROJECT_PLATFORMS_CONFIG, proj_info.guid, config);
|
||||
}
|
||||
}
|
||||
|
||||
String content = sformat(SOLUTION_TEMPLATE, projs_decl, sln_platform_cfg, proj_platform_cfg);
|
||||
|
||||
FileAccess *file = FileAccess::open(path_join(path, name + ".sln"), FileAccess::WRITE);
|
||||
ERR_FAIL_NULL_V(file, ERR_FILE_CANT_WRITE);
|
||||
file->store_string(content);
|
||||
file->close();
|
||||
memdelete(file);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool DotNetSolution::set_path(const String &p_existing_path) {
|
||||
if (p_existing_path.is_abs_path()) {
|
||||
path = p_existing_path;
|
||||
} else {
|
||||
String abspath;
|
||||
if (!rel_path_to_abs(p_existing_path, abspath))
|
||||
return false;
|
||||
path = abspath;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String DotNetSolution::get_path() {
|
||||
return path;
|
||||
}
|
||||
|
||||
DotNetSolution::DotNetSolution(const String &p_name) {
|
||||
name = p_name;
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* dotnet_solution.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 NET_SOLUTION_H
|
||||
#define NET_SOLUTION_H
|
||||
|
||||
#include "core/map.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
struct DotNetSolution {
|
||||
String name;
|
||||
|
||||
struct ProjectInfo {
|
||||
String guid;
|
||||
String relpath; // Must be relative to the solution directory
|
||||
Vector<String> configs;
|
||||
};
|
||||
|
||||
void add_new_project(const String &p_name, const ProjectInfo &p_project_info);
|
||||
bool has_project(const String &p_name) const;
|
||||
const ProjectInfo &get_project_info(const String &p_name) const;
|
||||
bool remove_project(const String &p_name);
|
||||
|
||||
Error save();
|
||||
|
||||
bool set_path(const String &p_existing_path);
|
||||
String get_path();
|
||||
|
||||
DotNetSolution(const String &p_name);
|
||||
|
||||
private:
|
||||
String path;
|
||||
Map<String, ProjectInfo> projects;
|
||||
};
|
||||
|
||||
#endif // NET_SOLUTION_H
|
429
modules/mono/editor/editor_internal_calls.cpp
Normal file
429
modules/mono/editor/editor_internal_calls.cpp
Normal file
|
@ -0,0 +1,429 @@
|
|||
/*************************************************************************/
|
||||
/* editor_internal_calls.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 "editor_internal_calls.h"
|
||||
|
||||
#include "core/message_queue.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/version.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/script_editor_debugger.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#include "../csharp_script.h"
|
||||
#include "../glue/cs_glue_version.gen.h"
|
||||
#include "../godotsharp_dirs.h"
|
||||
#include "../mono_gd/gd_mono_marshal.h"
|
||||
#include "../utils/osx_utils.h"
|
||||
#include "bindings_generator.h"
|
||||
#include "godotsharp_export.h"
|
||||
#include "script_class_parser.h"
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_ResDataDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_data_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_ResMetadataDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_metadata_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_ResAssembliesBaseDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_base_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_ResAssembliesDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_ResConfigDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_config_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_ResTempDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_base_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_MonoUserDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_user_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_MonoLogsDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_logs_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_MonoSolutionsDir() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_solutions_dir());
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_BuildLogsDirs() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_build_logs_dir());
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_ProjectSlnPath() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_sln_path());
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_ProjectCsProjPath() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_csproj_path());
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_DataEditorToolsDir() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_tools_dir());
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_prebuilt_api_dir());
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_DataMonoEtcDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_etc_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_DataMonoLibDir() {
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_lib_dir());
|
||||
}
|
||||
|
||||
MonoString *godot_icall_GodotSharpDirs_DataMonoBinDir() {
|
||||
#ifdef WINDOWS_ENABLED
|
||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_bin_dir());
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void godot_icall_EditorProgress_Create(MonoString *p_task, MonoString *p_label, int32_t p_amount, MonoBoolean p_can_cancel) {
|
||||
String task = GDMonoMarshal::mono_string_to_godot(p_task);
|
||||
String label = GDMonoMarshal::mono_string_to_godot(p_label);
|
||||
EditorNode::progress_add_task(task, label, p_amount, (bool)p_can_cancel);
|
||||
}
|
||||
|
||||
void godot_icall_EditorProgress_Dispose(MonoString *p_task) {
|
||||
String task = GDMonoMarshal::mono_string_to_godot(p_task);
|
||||
EditorNode::progress_end_task(task);
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_EditorProgress_Step(MonoString *p_task, MonoString *p_state, int32_t p_step, MonoBoolean p_force_refresh) {
|
||||
String task = GDMonoMarshal::mono_string_to_godot(p_task);
|
||||
String state = GDMonoMarshal::mono_string_to_godot(p_state);
|
||||
return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh);
|
||||
}
|
||||
|
||||
BindingsGenerator *godot_icall_BindingsGenerator_Ctor() {
|
||||
return memnew(BindingsGenerator);
|
||||
}
|
||||
|
||||
void godot_icall_BindingsGenerator_Dtor(BindingsGenerator *p_handle) {
|
||||
memdelete(p_handle);
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_BindingsGenerator_LogPrintEnabled(BindingsGenerator *p_handle) {
|
||||
return p_handle->is_log_print_enabled();
|
||||
}
|
||||
|
||||
void godot_icall_BindingsGenerator_SetLogPrintEnabled(BindingsGenerator p_handle, MonoBoolean p_enabled) {
|
||||
p_handle.set_log_print_enabled(p_enabled);
|
||||
}
|
||||
|
||||
int32_t godot_icall_BindingsGenerator_GenerateCsApi(BindingsGenerator *p_handle, MonoString *p_output_dir) {
|
||||
String output_dir = GDMonoMarshal::mono_string_to_godot(p_output_dir);
|
||||
return p_handle->generate_cs_api(output_dir);
|
||||
}
|
||||
|
||||
uint32_t godot_icall_BindingsGenerator_Version() {
|
||||
return BindingsGenerator::get_version();
|
||||
}
|
||||
|
||||
uint32_t godot_icall_BindingsGenerator_CsGlueVersion() {
|
||||
return CS_GLUE_VERSION;
|
||||
}
|
||||
|
||||
int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObject *p_classes) {
|
||||
String filepath = GDMonoMarshal::mono_string_to_godot(p_filepath);
|
||||
|
||||
ScriptClassParser scp;
|
||||
Error err = scp.parse_file(filepath);
|
||||
if (err == OK) {
|
||||
Array classes = GDMonoMarshal::mono_object_to_variant(p_classes);
|
||||
const Vector<ScriptClassParser::ClassDecl> &class_decls = scp.get_classes();
|
||||
|
||||
for (int i = 0; i < class_decls.size(); i++) {
|
||||
const ScriptClassParser::ClassDecl &classDecl = class_decls[i];
|
||||
|
||||
Dictionary classDeclDict;
|
||||
classDeclDict["name"] = classDecl.name;
|
||||
classDeclDict["namespace"] = classDecl.namespace_;
|
||||
classDeclDict["nested"] = classDecl.nested;
|
||||
classDeclDict["base_count"] = classDecl.base.size();
|
||||
classes.push_back(classDeclDict);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
uint32_t godot_icall_GodotSharpExport_GetExportedAssemblyDependencies(MonoString *p_project_dll_name, MonoString *p_project_dll_src_path,
|
||||
MonoString *p_build_config, MonoString *p_custom_lib_dir, MonoObject *r_dependencies) {
|
||||
String project_dll_name = GDMonoMarshal::mono_string_to_godot(p_project_dll_name);
|
||||
String project_dll_src_path = GDMonoMarshal::mono_string_to_godot(p_project_dll_src_path);
|
||||
String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config);
|
||||
String custom_lib_dir = GDMonoMarshal::mono_string_to_godot(p_custom_lib_dir);
|
||||
Dictionary dependencies = GDMonoMarshal::mono_object_to_variant(r_dependencies);
|
||||
|
||||
return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_lib_dir, dependencies);
|
||||
}
|
||||
|
||||
float godot_icall_Internal_EditorScale() {
|
||||
return EDSCALE;
|
||||
}
|
||||
|
||||
MonoObject *godot_icall_Internal_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
|
||||
String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
|
||||
Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
|
||||
Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
|
||||
return GDMonoMarshal::variant_to_mono_object(result);
|
||||
}
|
||||
|
||||
MonoObject *godot_icall_Internal_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
|
||||
String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
|
||||
Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
|
||||
Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
|
||||
return GDMonoMarshal::variant_to_mono_object(result);
|
||||
}
|
||||
|
||||
MonoString *godot_icall_Internal_FullTemplatesDir() {
|
||||
String full_templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
|
||||
return GDMonoMarshal::mono_string_from_godot(full_templates_dir);
|
||||
}
|
||||
|
||||
MonoString *godot_icall_Internal_SimplifyGodotPath(MonoString *p_path) {
|
||||
String path = GDMonoMarshal::mono_string_to_godot(p_path);
|
||||
return GDMonoMarshal::mono_string_from_godot(path.simplify_path());
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_Internal_IsOsxAppBundleInstalled(MonoString *p_bundle_id) {
|
||||
#ifdef OSX_ENABLED
|
||||
String bundle_id = GDMonoMarshal::mono_string_to_godot(p_bundle_id);
|
||||
return (MonoBoolean)osx_is_app_bundle_installed;
|
||||
#else
|
||||
(void)p_bundle_id; // UNUSED
|
||||
return (MonoBoolean) false;
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_Internal_MetadataIsApiAssemblyInvalidated(int32_t p_api_type) {
|
||||
return GDMono::get_singleton()->metadata_is_api_assembly_invalidated((APIAssembly::Type)p_api_type);
|
||||
}
|
||||
|
||||
void godot_icall_Internal_MetadataSetApiAssemblyInvalidated(int32_t p_api_type, MonoBoolean p_invalidated) {
|
||||
GDMono::get_singleton()->metadata_set_api_assembly_invalidated((APIAssembly::Type)p_api_type, (bool)p_invalidated);
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_Internal_IsMessageQueueFlushing() {
|
||||
return (MonoBoolean)MessageQueue::get_singleton()->is_flushing();
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_Internal_GodotIs32Bits() {
|
||||
return sizeof(void *) == 4;
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_Internal_GodotIsRealTDouble() {
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
return (MonoBoolean) true;
|
||||
#else
|
||||
return (MonoBoolean) false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void godot_icall_Internal_GodotMainIteration() {
|
||||
Main::iteration();
|
||||
}
|
||||
|
||||
uint64_t godot_icall_Internal_GetCoreApiHash() {
|
||||
return ClassDB::get_api_hash(ClassDB::API_CORE);
|
||||
}
|
||||
|
||||
uint64_t godot_icall_Internal_GetEditorApiHash() {
|
||||
return ClassDB::get_api_hash(ClassDB::API_EDITOR);
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_Internal_IsAssembliesReloadingNeeded() {
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
return (MonoBoolean)CSharpLanguage::get_singleton()->is_assembly_reloading_needed();
|
||||
#else
|
||||
return (MonoBoolean) false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) {
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
_GodotSharp::get_singleton()->call_deferred("_reload_assemblies", (bool)p_soft_reload);
|
||||
#endif
|
||||
}
|
||||
|
||||
void godot_icall_Internal_ScriptEditorDebuggerReloadScripts() {
|
||||
ScriptEditor::get_singleton()->get_debugger()->reload_scripts();
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_Internal_ScriptEditorEdit(MonoObject *p_resource, int32_t p_line, int32_t p_col, MonoBoolean p_grab_focus) {
|
||||
Ref<Resource> resource = GDMonoMarshal::mono_object_to_variant(p_resource);
|
||||
return (MonoBoolean)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus);
|
||||
}
|
||||
|
||||
void godot_icall_Internal_EditorNodeShowScriptScreen() {
|
||||
EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT);
|
||||
}
|
||||
|
||||
MonoObject *godot_icall_Internal_GetScriptsMetadataOrNothing(MonoReflectionType *p_dict_reftype) {
|
||||
Dictionary maybe_metadata = CSharpLanguage::get_singleton()->get_scripts_metadata_or_nothing();
|
||||
|
||||
MonoType *dict_type = mono_reflection_type_get_type(p_dict_reftype);
|
||||
|
||||
uint32_t type_encoding = mono_type_get_type(dict_type);
|
||||
MonoClass *type_class_raw = mono_class_from_mono_type(dict_type);
|
||||
GDMonoClass *type_class = GDMono::get_singleton()->get_class(type_class_raw);
|
||||
|
||||
return GDMonoMarshal::variant_to_mono_object(maybe_metadata, ManagedType(type_encoding, type_class));
|
||||
}
|
||||
|
||||
MonoString *godot_icall_Internal_MonoWindowsInstallRoot() {
|
||||
#ifdef WINDOWS_ENABLED
|
||||
String install_root_dir = GDMono::get_singleton()->get_mono_reg_info().install_root_dir;
|
||||
return GDMonoMarshal::mono_string_from_godot(install_root_dir);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoString *godot_icall_Utils_OS_GetPlatformName() {
|
||||
String os_name = OS::get_singleton()->get_name();
|
||||
return GDMonoMarshal::mono_string_from_godot(os_name);
|
||||
}
|
||||
|
||||
void register_editor_internal_calls() {
|
||||
|
||||
// GodotSharpDirs
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", (void *)godot_icall_GodotSharpDirs_ResDataDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", (void *)godot_icall_GodotSharpDirs_ResMetadataDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesBaseDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", (void *)godot_icall_GodotSharpDirs_ResConfigDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", (void *)godot_icall_GodotSharpDirs_ResTempDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", (void *)godot_icall_GodotSharpDirs_MonoUserDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", (void *)godot_icall_GodotSharpDirs_MonoLogsDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", (void *)godot_icall_GodotSharpDirs_MonoSolutionsDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", (void *)godot_icall_GodotSharpDirs_BuildLogsDirs);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", (void *)godot_icall_GodotSharpDirs_ProjectSlnPath);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", (void *)godot_icall_GodotSharpDirs_ProjectCsProjPath);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", (void *)godot_icall_GodotSharpDirs_DataEditorToolsDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", (void *)godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", (void *)godot_icall_GodotSharpDirs_DataMonoEtcDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", (void *)godot_icall_GodotSharpDirs_DataMonoLibDir);
|
||||
mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", (void *)godot_icall_GodotSharpDirs_DataMonoBinDir);
|
||||
|
||||
// EditorProgress
|
||||
mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", (void *)godot_icall_EditorProgress_Create);
|
||||
mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", (void *)godot_icall_EditorProgress_Dispose);
|
||||
mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", (void *)godot_icall_EditorProgress_Step);
|
||||
|
||||
// BiningsGenerator
|
||||
mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Ctor", (void *)godot_icall_BindingsGenerator_Ctor);
|
||||
mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Dtor", (void *)godot_icall_BindingsGenerator_Dtor);
|
||||
mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_LogPrintEnabled", (void *)godot_icall_BindingsGenerator_LogPrintEnabled);
|
||||
mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_SetLogPrintEnabled", (void *)godot_icall_BindingsGenerator_SetLogPrintEnabled);
|
||||
mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_GenerateCsApi", (void *)godot_icall_BindingsGenerator_GenerateCsApi);
|
||||
mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Version", (void *)godot_icall_BindingsGenerator_Version);
|
||||
mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_CsGlueVersion", (void *)godot_icall_BindingsGenerator_CsGlueVersion);
|
||||
|
||||
// ScriptClassParser
|
||||
mono_add_internal_call("GodotTools.Internals.ScriptClassParser::internal_ParseFile", (void *)godot_icall_ScriptClassParser_ParseFile);
|
||||
|
||||
// GodotSharpExport
|
||||
mono_add_internal_call("GodotTools.GodotSharpExport::internal_GetExportedAssemblyDependencies", (void *)godot_icall_GodotSharpExport_GetExportedAssemblyDependencies);
|
||||
|
||||
// Internals
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorScale", (void *)godot_icall_Internal_EditorScale);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GlobalDef", (void *)godot_icall_Internal_GlobalDef);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorDef", (void *)godot_icall_Internal_EditorDef);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", (void *)godot_icall_Internal_FullTemplatesDir);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", (void *)godot_icall_Internal_SimplifyGodotPath);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", (void *)godot_icall_Internal_IsOsxAppBundleInstalled);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_MetadataIsApiAssemblyInvalidated", (void *)godot_icall_Internal_MetadataIsApiAssemblyInvalidated);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_MetadataSetApiAssemblyInvalidated", (void *)godot_icall_Internal_MetadataSetApiAssemblyInvalidated);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_IsMessageQueueFlushing", (void *)godot_icall_Internal_IsMessageQueueFlushing);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", (void *)godot_icall_Internal_GodotIs32Bits);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", (void *)godot_icall_Internal_GodotIsRealTDouble);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", (void *)godot_icall_Internal_GodotMainIteration);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", (void *)godot_icall_Internal_GetCoreApiHash);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", (void *)godot_icall_Internal_GetEditorApiHash);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", (void *)godot_icall_Internal_IsAssembliesReloadingNeeded);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", (void *)godot_icall_Internal_ReloadAssemblies);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebuggerReloadScripts", (void *)godot_icall_Internal_ScriptEditorDebuggerReloadScripts);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", (void *)godot_icall_Internal_ScriptEditorEdit);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", (void *)godot_icall_Internal_EditorNodeShowScriptScreen);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", (void *)godot_icall_Internal_GetScriptsMetadataOrNothing);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", (void *)godot_icall_Internal_MonoWindowsInstallRoot);
|
||||
|
||||
// Utils.OS
|
||||
mono_add_internal_call("GodotTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*************************************************************************/
|
||||
/* mono_build_info.h */
|
||||
/* editor_internal_calls.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
|
@ -28,28 +28,9 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef MONO_BUILD_INFO_H
|
||||
#define MONO_BUILD_INFO_H
|
||||
#ifndef EDITOR_INTERNAL_CALL_H
|
||||
#define EDITOR_INTERNAL_CALL_H
|
||||
|
||||
#include "core/ustring.h"
|
||||
#include "core/vector.h"
|
||||
void register_editor_internal_calls();
|
||||
|
||||
struct MonoBuildInfo {
|
||||
|
||||
struct Hasher {
|
||||
static uint32_t hash(const MonoBuildInfo &p_key);
|
||||
};
|
||||
|
||||
String solution;
|
||||
String configuration;
|
||||
Vector<String> custom_props;
|
||||
|
||||
bool operator==(const MonoBuildInfo &p_b) const;
|
||||
|
||||
String get_log_dirpath();
|
||||
|
||||
MonoBuildInfo();
|
||||
MonoBuildInfo(const String &p_solution, const String &p_config);
|
||||
};
|
||||
|
||||
#endif // MONO_BUILD_INFO_H
|
||||
#endif // EDITOR_INTERNAL_CALL_H
|
|
@ -1,632 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* godotsharp_builds.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 "godotsharp_builds.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/vector.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#include "../glue/cs_glue_version.gen.h"
|
||||
#include "../godotsharp_dirs.h"
|
||||
#include "../mono_gd/gd_mono_class.h"
|
||||
#include "../mono_gd/gd_mono_marshal.h"
|
||||
#include "../utils/path_utils.h"
|
||||
#include "bindings_generator.h"
|
||||
#include "csharp_project.h"
|
||||
#include "godotsharp_editor.h"
|
||||
|
||||
#define PROP_NAME_MSBUILD_MONO "MSBuild (Mono)"
|
||||
#define PROP_NAME_MSBUILD_VS "MSBuild (VS Build Tools)"
|
||||
#define PROP_NAME_XBUILD "xbuild (Deprecated)"
|
||||
|
||||
void godot_icall_BuildInstance_ExitCallback(MonoString *p_solution, MonoString *p_config, int p_exit_code) {
|
||||
|
||||
String solution = GDMonoMarshal::mono_string_to_godot(p_solution);
|
||||
String config = GDMonoMarshal::mono_string_to_godot(p_config);
|
||||
GodotSharpBuilds::get_singleton()->build_exit_callback(MonoBuildInfo(solution, config), p_exit_code);
|
||||
}
|
||||
|
||||
static Vector<const char *> _get_msbuild_hint_dirs() {
|
||||
Vector<const char *> ret;
|
||||
#ifdef OSX_ENABLED
|
||||
ret.push_back("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
|
||||
ret.push_back("/usr/local/var/homebrew/linked/mono/bin/");
|
||||
#endif
|
||||
ret.push_back("/opt/novell/mono/bin/");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef UNIX_ENABLED
|
||||
String _find_build_engine_on_unix(const String &p_name) {
|
||||
String ret = path_which(p_name);
|
||||
|
||||
if (ret.length())
|
||||
return ret;
|
||||
|
||||
String ret_fallback = path_which(p_name + ".exe");
|
||||
if (ret_fallback.length())
|
||||
return ret_fallback;
|
||||
|
||||
static Vector<const char *> locations = _get_msbuild_hint_dirs();
|
||||
|
||||
for (int i = 0; i < locations.size(); i++) {
|
||||
String hint_path = locations[i] + p_name;
|
||||
|
||||
if (FileAccess::exists(hint_path)) {
|
||||
return hint_path;
|
||||
}
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
#endif
|
||||
|
||||
MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
|
||||
|
||||
GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool")));
|
||||
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
switch (build_tool) {
|
||||
case GodotSharpBuilds::MSBUILD_VS: {
|
||||
static String msbuild_tools_path;
|
||||
|
||||
if (msbuild_tools_path.empty() || !FileAccess::exists(msbuild_tools_path)) {
|
||||
// Try to search it again if it wasn't found last time or if it was removed from its location
|
||||
msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path();
|
||||
|
||||
if (msbuild_tools_path.empty()) {
|
||||
ERR_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_VS "'. Tried with path: " + msbuild_tools_path);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!msbuild_tools_path.ends_with("\\"))
|
||||
msbuild_tools_path += "\\";
|
||||
|
||||
return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
|
||||
} break;
|
||||
case GodotSharpBuilds::MSBUILD_MONO: {
|
||||
String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat");
|
||||
|
||||
if (!FileAccess::exists(msbuild_path)) {
|
||||
ERR_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_MONO "'. Tried with path: " + msbuild_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return GDMonoMarshal::mono_string_from_godot(msbuild_path);
|
||||
} break;
|
||||
case GodotSharpBuilds::XBUILD: {
|
||||
String xbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("xbuild.bat");
|
||||
|
||||
if (!FileAccess::exists(xbuild_path)) {
|
||||
ERR_PRINTS("Cannot find executable for '" PROP_NAME_XBUILD "'. Tried with path: " + xbuild_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return GDMonoMarshal::mono_string_from_godot(xbuild_path);
|
||||
} break;
|
||||
default:
|
||||
ERR_EXPLAIN("You don't deserve to live");
|
||||
CRASH_NOW();
|
||||
}
|
||||
#elif defined(UNIX_ENABLED)
|
||||
static String msbuild_path;
|
||||
static String xbuild_path;
|
||||
|
||||
if (build_tool == GodotSharpBuilds::XBUILD) {
|
||||
if (xbuild_path.empty() || !FileAccess::exists(xbuild_path)) {
|
||||
// Try to search it again if it wasn't found last time or if it was removed from its location
|
||||
xbuild_path = _find_build_engine_on_unix("msbuild");
|
||||
}
|
||||
|
||||
if (xbuild_path.empty()) {
|
||||
ERR_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (msbuild_path.empty() || !FileAccess::exists(msbuild_path)) {
|
||||
// Try to search it again if it wasn't found last time or if it was removed from its location
|
||||
msbuild_path = _find_build_engine_on_unix("msbuild");
|
||||
}
|
||||
|
||||
if (msbuild_path.empty()) {
|
||||
ERR_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
|
||||
#else
|
||||
(void)build_tool; // UNUSED
|
||||
|
||||
ERR_EXPLAIN("Not implemented on this platform");
|
||||
ERR_FAIL_V(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoString *godot_icall_BuildInstance_get_MonoWindowsBinDir() {
|
||||
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
|
||||
if (mono_reg_info.bin_dir.length()) {
|
||||
return GDMonoMarshal::mono_string_from_godot(mono_reg_info.bin_dir);
|
||||
}
|
||||
|
||||
ERR_EXPLAIN("Cannot find Mono's binaries directory in the registry");
|
||||
ERR_FAIL_V(NULL);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows() {
|
||||
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
return GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool"))) == GodotSharpBuilds::MSBUILD_MONO;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_BuildInstance_get_PrintBuildOutput() {
|
||||
|
||||
return (bool)EDITOR_GET("mono/builds/print_build_output");
|
||||
}
|
||||
|
||||
void GodotSharpBuilds::register_internal_calls() {
|
||||
|
||||
static bool registered = false;
|
||||
ERR_FAIL_COND(registered);
|
||||
registered = true;
|
||||
|
||||
mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback);
|
||||
mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath);
|
||||
mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MonoWindowsBinDir", (void *)godot_icall_BuildInstance_get_MonoWindowsBinDir);
|
||||
mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows", (void *)godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows);
|
||||
mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_PrintBuildOutput", (void *)godot_icall_BuildInstance_get_PrintBuildOutput);
|
||||
}
|
||||
|
||||
void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
|
||||
|
||||
GodotSharpEditor::get_singleton()->show_error_dialog(p_message, "Build error");
|
||||
MonoBottomPanel::get_singleton()->show_build_tab();
|
||||
}
|
||||
|
||||
bool GodotSharpBuilds::build_api_sln(const String &p_api_sln_dir, const String &p_config) {
|
||||
|
||||
String api_sln_file = p_api_sln_dir.plus_file(API_SOLUTION_NAME ".sln");
|
||||
|
||||
String core_api_assembly_dir = p_api_sln_dir.plus_file(CORE_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config);
|
||||
String core_api_assembly_file = core_api_assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
|
||||
|
||||
String editor_api_assembly_dir = p_api_sln_dir.plus_file(EDITOR_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config);
|
||||
String editor_api_assembly_file = editor_api_assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
|
||||
if (!FileAccess::exists(core_api_assembly_file) || !FileAccess::exists(editor_api_assembly_file)) {
|
||||
MonoBuildInfo api_build_info(api_sln_file, p_config);
|
||||
// TODO Replace this global NoWarn with '#pragma warning' directives on generated files,
|
||||
// once we start to actively document manually maintained C# classes
|
||||
api_build_info.custom_props.push_back("NoWarn=1591"); // Ignore missing documentation warnings
|
||||
|
||||
if (!GodotSharpBuilds::get_singleton()->build(api_build_info)) {
|
||||
show_build_error_dialog("Failed to build " API_SOLUTION_NAME " solution.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!FileAccess::exists(assembly_dst) ||
|
||||
FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) ||
|
||||
GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) {
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
|
||||
String xml_file = p_assembly_name + ".xml";
|
||||
if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK)
|
||||
WARN_PRINTS("Failed to copy " + xml_file);
|
||||
|
||||
String pdb_file = p_assembly_name + ".pdb";
|
||||
if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK)
|
||||
WARN_PRINTS("Failed to copy " + pdb_file);
|
||||
|
||||
Error err = da->copy(assembly_src, assembly_dst);
|
||||
|
||||
if (err != OK) {
|
||||
show_build_error_dialog("Failed to copy " + assembly_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) {
|
||||
|
||||
uint64_t api_hash = p_api_type == APIAssembly::API_CORE ?
|
||||
GDMono::get_singleton()->get_api_core_hash() :
|
||||
GDMono::get_singleton()->get_api_editor_hash();
|
||||
return String::num_uint64(api_hash) +
|
||||
"_" + String::num_uint64(BindingsGenerator::get_version()) +
|
||||
"_" + String::num_uint64(CS_GLUE_VERSION);
|
||||
}
|
||||
|
||||
bool GodotSharpBuilds::make_api_assembly(APIAssembly::Type p_api_type) {
|
||||
|
||||
String api_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
|
||||
|
||||
String editor_prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir();
|
||||
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
|
||||
|
||||
if (FileAccess::exists(editor_prebuilt_api_dir.plus_file(api_name + ".dll"))) {
|
||||
EditorProgress pr("mono_copy_prebuilt_api_assembly", "Copying prebuilt " + api_name + " assembly...", 1);
|
||||
pr.step("Copying " + api_name + " assembly", 0);
|
||||
return GodotSharpBuilds::copy_api_assembly(editor_prebuilt_api_dir, res_assemblies_dir, api_name, p_api_type);
|
||||
}
|
||||
|
||||
String api_build_config = "Release";
|
||||
|
||||
EditorProgress pr("mono_build_release_" API_SOLUTION_NAME, "Building " API_SOLUTION_NAME " solution...", 3);
|
||||
|
||||
pr.step("Generating " API_SOLUTION_NAME " solution", 0);
|
||||
|
||||
String api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
|
||||
.plus_file(_api_folder_name(APIAssembly::API_CORE));
|
||||
|
||||
String api_sln_file = api_sln_dir.plus_file(API_SOLUTION_NAME ".sln");
|
||||
|
||||
if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
|
||||
BindingsGenerator bindings_generator;
|
||||
|
||||
if (!OS::get_singleton()->is_stdout_verbose()) {
|
||||
bindings_generator.set_log_print_enabled(false);
|
||||
}
|
||||
|
||||
Error err = bindings_generator.generate_cs_api(api_sln_dir);
|
||||
if (err != OK) {
|
||||
show_build_error_dialog("Failed to generate " API_SOLUTION_NAME " solution. Error: " + itos(err));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pr.step("Building " API_SOLUTION_NAME " solution", 1);
|
||||
|
||||
if (!GodotSharpBuilds::build_api_sln(api_sln_dir, api_build_config))
|
||||
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(api_name).plus_file("bin").plus_file(api_build_config);
|
||||
if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GodotSharpBuilds::build_project_blocking(const String &p_config, const Vector<String> &p_godot_defines) {
|
||||
|
||||
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
|
||||
return true; // No solution to build
|
||||
|
||||
if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
|
||||
return false;
|
||||
|
||||
if (!GodotSharpBuilds::make_api_assembly(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);
|
||||
|
||||
// Add Godot defines
|
||||
#ifdef WINDOWS_ENABLED
|
||||
String constants = "GodotDefineConstants=\"";
|
||||
#else
|
||||
String constants = "GodotDefineConstants=\\\"";
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < p_godot_defines.size(); i++) {
|
||||
constants += "GODOT_" + p_godot_defines[i].to_upper().replace("-", "_").replace(" ", "_").replace(";", "_") + ";";
|
||||
}
|
||||
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
constants += "GODOT_REAL_T_IS_DOUBLE;";
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
constants += "\"";
|
||||
#else
|
||||
constants += "\\\"";
|
||||
#endif
|
||||
build_info.custom_props.push_back(constants);
|
||||
|
||||
if (!GodotSharpBuilds::get_singleton()->build(build_info)) {
|
||||
GodotSharpBuilds::show_build_error_dialog("Failed to build project solution");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GodotSharpBuilds::editor_build_callback() {
|
||||
|
||||
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
|
||||
return true; // No solution to build
|
||||
|
||||
String scripts_metadata_path_editor = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor");
|
||||
String scripts_metadata_path_player = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor_player");
|
||||
|
||||
Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor);
|
||||
ERR_FAIL_COND_V(metadata_err != OK, false);
|
||||
|
||||
if (FileAccess::exists(scripts_metadata_path_editor)) {
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
||||
Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player);
|
||||
|
||||
ERR_EXPLAIN("Failed to copy scripts metadata file");
|
||||
ERR_FAIL_COND_V(copy_err != OK, false);
|
||||
}
|
||||
|
||||
Vector<String> godot_defines;
|
||||
godot_defines.push_back(OS::get_singleton()->get_name());
|
||||
godot_defines.push_back(sizeof(void *) == 4 ? "32" : "64");
|
||||
return build_project_blocking("Tools", godot_defines);
|
||||
}
|
||||
|
||||
GodotSharpBuilds *GodotSharpBuilds::singleton = NULL;
|
||||
|
||||
void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) {
|
||||
|
||||
BuildProcess *match = builds.getptr(p_build_info);
|
||||
ERR_FAIL_NULL(match);
|
||||
|
||||
BuildProcess &bp = *match;
|
||||
bp.on_exit(p_exit_code);
|
||||
}
|
||||
|
||||
void GodotSharpBuilds::restart_build(MonoBuildTab *p_build_tab) {
|
||||
}
|
||||
|
||||
void GodotSharpBuilds::stop_build(MonoBuildTab *p_build_tab) {
|
||||
}
|
||||
|
||||
bool GodotSharpBuilds::build(const MonoBuildInfo &p_build_info) {
|
||||
|
||||
BuildProcess *match = builds.getptr(p_build_info);
|
||||
|
||||
if (match) {
|
||||
BuildProcess &bp = *match;
|
||||
bp.start(true);
|
||||
return bp.exit_code == 0;
|
||||
} else {
|
||||
BuildProcess bp = BuildProcess(p_build_info);
|
||||
bp.start(true);
|
||||
builds.set(p_build_info, bp);
|
||||
return bp.exit_code == 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool GodotSharpBuilds::build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) {
|
||||
|
||||
BuildProcess *match = builds.getptr(p_build_info);
|
||||
|
||||
if (match) {
|
||||
BuildProcess &bp = *match;
|
||||
bp.start();
|
||||
return !bp.exited; // failed to start
|
||||
} else {
|
||||
BuildProcess bp = BuildProcess(p_build_info, p_callback);
|
||||
bp.start();
|
||||
builds.set(p_build_info, bp);
|
||||
return !bp.exited; // failed to start
|
||||
}
|
||||
}
|
||||
|
||||
GodotSharpBuilds::GodotSharpBuilds() {
|
||||
|
||||
singleton = this;
|
||||
|
||||
EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::editor_build_callback);
|
||||
|
||||
// Build tool settings
|
||||
EditorSettings *ed_settings = EditorSettings::get_singleton();
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
EDITOR_DEF("mono/builds/build_tool", MSBUILD_VS);
|
||||
#else
|
||||
EDITOR_DEF("mono/builds/build_tool", MSBUILD_MONO);
|
||||
#endif
|
||||
|
||||
ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM,
|
||||
PROP_NAME_MSBUILD_MONO
|
||||
#ifdef WINDOWS_ENABLED
|
||||
"," PROP_NAME_MSBUILD_VS
|
||||
#endif
|
||||
"," PROP_NAME_XBUILD));
|
||||
|
||||
EDITOR_DEF("mono/builds/print_build_output", false);
|
||||
}
|
||||
|
||||
GodotSharpBuilds::~GodotSharpBuilds() {
|
||||
|
||||
singleton = NULL;
|
||||
}
|
||||
|
||||
void GodotSharpBuilds::BuildProcess::on_exit(int p_exit_code) {
|
||||
|
||||
exited = true;
|
||||
exit_code = p_exit_code;
|
||||
build_tab->on_build_exit(p_exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR);
|
||||
build_instance.unref();
|
||||
|
||||
if (exit_callback)
|
||||
exit_callback(exit_code);
|
||||
}
|
||||
|
||||
void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
|
||||
|
||||
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
|
||||
|
||||
exit_code = -1;
|
||||
|
||||
String log_dirpath = build_info.get_log_dirpath();
|
||||
|
||||
if (build_tab) {
|
||||
build_tab->on_build_start();
|
||||
} else {
|
||||
build_tab = memnew(MonoBuildTab(build_info, log_dirpath));
|
||||
MonoBottomPanel::get_singleton()->add_build_tab(build_tab);
|
||||
}
|
||||
|
||||
if (p_blocking) {
|
||||
// Required in order to update the build tasks list
|
||||
Main::iteration();
|
||||
}
|
||||
|
||||
if (!exited) {
|
||||
exited = true;
|
||||
String message = "Tried to start build process, but it is already running";
|
||||
build_tab->on_build_exec_failed(message);
|
||||
ERR_EXPLAIN(message);
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
exited = false;
|
||||
|
||||
// Remove old issues file
|
||||
|
||||
String issues_file = get_msbuild_issues_filename();
|
||||
DirAccessRef d = DirAccess::create_for_path(log_dirpath);
|
||||
if (d->file_exists(issues_file)) {
|
||||
Error err = d->remove(issues_file);
|
||||
if (err != OK) {
|
||||
exited = true;
|
||||
String file_path = ProjectSettings::get_singleton()->localize_path(log_dirpath).plus_file(issues_file);
|
||||
String message = "Cannot remove issues file: " + file_path;
|
||||
build_tab->on_build_exec_failed(message);
|
||||
ERR_EXPLAIN(message);
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Build", "BuildInstance");
|
||||
|
||||
MonoObject *mono_object = mono_object_new(mono_domain_get(), klass->get_mono_ptr());
|
||||
|
||||
// Construct
|
||||
|
||||
Variant solution = build_info.solution;
|
||||
Variant config = build_info.configuration;
|
||||
|
||||
const Variant *ctor_args[2] = { &solution, &config };
|
||||
|
||||
MonoException *exc = NULL;
|
||||
GDMonoMethod *ctor = klass->get_method(".ctor", 2);
|
||||
ctor->invoke(mono_object, ctor_args, &exc);
|
||||
|
||||
if (exc) {
|
||||
exited = true;
|
||||
GDMonoUtils::debug_unhandled_exception(exc);
|
||||
String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
|
||||
build_tab->on_build_exec_failed(message);
|
||||
ERR_EXPLAIN(message);
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
// Call Build
|
||||
|
||||
String logger_assembly_path = GDMono::get_singleton()->get_editor_tools_assembly()->get_path();
|
||||
Variant logger_assembly = ProjectSettings::get_singleton()->globalize_path(logger_assembly_path);
|
||||
Variant logger_output_dir = log_dirpath;
|
||||
Variant custom_props = build_info.custom_props;
|
||||
|
||||
const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props };
|
||||
|
||||
exc = NULL;
|
||||
GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3);
|
||||
build_method->invoke(mono_object, args, &exc);
|
||||
|
||||
if (exc) {
|
||||
exited = true;
|
||||
GDMonoUtils::debug_unhandled_exception(exc);
|
||||
String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
|
||||
build_tab->on_build_exec_failed(message);
|
||||
ERR_EXPLAIN(message);
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
// Build returned
|
||||
|
||||
if (p_blocking) {
|
||||
exited = true;
|
||||
exit_code = klass->get_field("exitCode")->get_int_value(mono_object);
|
||||
|
||||
if (exit_code != 0) {
|
||||
String log_filepath = build_info.get_log_dirpath().plus_file(get_msbuild_log_filename());
|
||||
print_verbose("MSBuild exited with code: " + itos(exit_code) + ". Log file: " + log_filepath);
|
||||
}
|
||||
|
||||
build_tab->on_build_exit(exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR);
|
||||
} else {
|
||||
build_instance = MonoGCHandle::create_strong(mono_object);
|
||||
exited = false;
|
||||
}
|
||||
}
|
||||
|
||||
GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) :
|
||||
build_info(p_build_info),
|
||||
build_tab(NULL),
|
||||
exit_callback(p_callback),
|
||||
exited(true),
|
||||
exit_code(-1) {
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* godotsharp_builds.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 GODOTSHARP_BUILDS_H
|
||||
#define GODOTSHARP_BUILDS_H
|
||||
|
||||
#include "../mono_gd/gd_mono.h"
|
||||
#include "mono_bottom_panel.h"
|
||||
#include "mono_build_info.h"
|
||||
|
||||
typedef void (*GodotSharpBuild_ExitCallback)(int);
|
||||
|
||||
class GodotSharpBuilds {
|
||||
|
||||
private:
|
||||
struct BuildProcess {
|
||||
Ref<MonoGCHandle> build_instance;
|
||||
MonoBuildInfo build_info;
|
||||
MonoBuildTab *build_tab;
|
||||
GodotSharpBuild_ExitCallback exit_callback;
|
||||
bool exited;
|
||||
int exit_code;
|
||||
|
||||
void on_exit(int p_exit_code);
|
||||
void start(bool p_blocking = false);
|
||||
|
||||
BuildProcess() {}
|
||||
BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
|
||||
};
|
||||
|
||||
HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds;
|
||||
|
||||
static String _api_folder_name(APIAssembly::Type p_api_type);
|
||||
|
||||
static GodotSharpBuilds *singleton;
|
||||
|
||||
public:
|
||||
enum BuildTool {
|
||||
MSBUILD_MONO,
|
||||
#ifdef WINDOWS_ENABLED
|
||||
MSBUILD_VS,
|
||||
#endif
|
||||
XBUILD // Deprecated
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; }
|
||||
|
||||
static void register_internal_calls();
|
||||
|
||||
static void show_build_error_dialog(const String &p_message);
|
||||
|
||||
static const char *get_msbuild_issues_filename() { return "msbuild_issues.csv"; }
|
||||
static const char *get_msbuild_log_filename() { return "msbuild_log.txt"; }
|
||||
|
||||
void build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code);
|
||||
|
||||
void restart_build(MonoBuildTab *p_build_tab);
|
||||
void stop_build(MonoBuildTab *p_build_tab);
|
||||
|
||||
bool build(const MonoBuildInfo &p_build_info);
|
||||
bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
|
||||
|
||||
static bool build_api_sln(const String &p_api_sln_dir, const String &p_config);
|
||||
static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type);
|
||||
|
||||
static bool make_api_assembly(APIAssembly::Type p_api_type);
|
||||
|
||||
static bool build_project_blocking(const String &p_config, const Vector<String> &p_godot_defines);
|
||||
|
||||
static bool editor_build_callback();
|
||||
|
||||
GodotSharpBuilds();
|
||||
~GodotSharpBuilds();
|
||||
};
|
||||
|
||||
#endif // GODOTSHARP_BUILDS_H
|
|
@ -1,582 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* godotsharp_editor.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 "godotsharp_editor.h"
|
||||
|
||||
#include "core/message_queue.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
#include "../csharp_script.h"
|
||||
#include "../godotsharp_dirs.h"
|
||||
#include "../mono_gd/gd_mono.h"
|
||||
#include "../mono_gd/gd_mono_marshal.h"
|
||||
#include "../utils/path_utils.h"
|
||||
#include "bindings_generator.h"
|
||||
#include "csharp_project.h"
|
||||
#include "dotnet_solution.h"
|
||||
#include "godotsharp_export.h"
|
||||
|
||||
#ifdef OSX_ENABLED
|
||||
#include "../utils/osx_utils.h"
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
#include "../utils/mono_reg_utils.h"
|
||||
#endif
|
||||
|
||||
GodotSharpEditor *GodotSharpEditor::singleton = NULL;
|
||||
|
||||
bool GodotSharpEditor::_create_project_solution() {
|
||||
|
||||
EditorProgress pr("create_csharp_solution", TTR("Generating solution..."), 2);
|
||||
|
||||
pr.step(TTR("Generating C# project..."));
|
||||
|
||||
String path = OS::get_singleton()->get_resource_dir();
|
||||
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
||||
if (appname_safe.empty()) {
|
||||
appname_safe = "UnnamedProject";
|
||||
}
|
||||
|
||||
String guid = CSharpProject::generate_game_project(path, appname_safe);
|
||||
|
||||
if (guid.length()) {
|
||||
|
||||
DotNetSolution solution(appname_safe);
|
||||
|
||||
if (!solution.set_path(path)) {
|
||||
show_error_dialog(TTR("Failed to create solution."));
|
||||
return false;
|
||||
}
|
||||
|
||||
DotNetSolution::ProjectInfo proj_info;
|
||||
proj_info.guid = guid;
|
||||
proj_info.relpath = appname_safe + ".csproj";
|
||||
proj_info.configs.push_back("Debug");
|
||||
proj_info.configs.push_back("Release");
|
||||
proj_info.configs.push_back("Tools");
|
||||
|
||||
solution.add_new_project(appname_safe, proj_info);
|
||||
|
||||
Error sln_error = solution.save();
|
||||
|
||||
if (sln_error != OK) {
|
||||
show_error_dialog(TTR("Failed to save solution."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
|
||||
return false;
|
||||
|
||||
if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR))
|
||||
return false;
|
||||
|
||||
pr.step(TTR("Done"));
|
||||
|
||||
// Here, after all calls to progress_task_step
|
||||
call_deferred("_remove_create_sln_menu_option");
|
||||
|
||||
} else {
|
||||
show_error_dialog(TTR("Failed to create C# project."));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GodotSharpEditor::_make_api_solutions_if_needed() {
|
||||
// I'm sick entirely of ProgressDialog
|
||||
|
||||
static int attempts_left = 100;
|
||||
|
||||
if (MessageQueue::get_singleton()->is_flushing() || !SceneTree::get_singleton()) {
|
||||
ERR_FAIL_COND(attempts_left == 0); // You've got to be kidding
|
||||
|
||||
if (SceneTree::get_singleton()) {
|
||||
SceneTree::get_singleton()->connect("idle_frame", this, "_make_api_solutions_if_needed", Vector<Variant>());
|
||||
} else {
|
||||
call_deferred("_make_api_solutions_if_needed");
|
||||
}
|
||||
|
||||
attempts_left--;
|
||||
return;
|
||||
}
|
||||
|
||||
// Recursion guard needed because signals don't play well with ProgressDialog either, but unlike
|
||||
// the message queue, with signals the collateral damage should be minimal in the worst case.
|
||||
static bool recursion_guard = false;
|
||||
if (!recursion_guard) {
|
||||
recursion_guard = true;
|
||||
|
||||
// Oneshot signals don't play well with ProgressDialog either, so we do it this way instead
|
||||
SceneTree::get_singleton()->disconnect("idle_frame", this, "_make_api_solutions_if_needed");
|
||||
|
||||
_make_api_solutions_if_needed_impl();
|
||||
|
||||
recursion_guard = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GodotSharpEditor::_make_api_solutions_if_needed_impl() {
|
||||
// If the project has a solution and C# project make sure the API assemblies are present and up to date
|
||||
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
|
||||
|
||||
if (!FileAccess::exists(res_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll")) ||
|
||||
GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) {
|
||||
if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(res_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll")) ||
|
||||
GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
|
||||
if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR))
|
||||
return; // Redundant? I don't think so
|
||||
}
|
||||
}
|
||||
|
||||
void GodotSharpEditor::_remove_create_sln_menu_option() {
|
||||
|
||||
menu_popup->remove_item(menu_popup->get_item_index(MENU_CREATE_SLN));
|
||||
|
||||
bottom_panel_btn->show();
|
||||
}
|
||||
|
||||
void GodotSharpEditor::_show_about_dialog() {
|
||||
|
||||
bool show_on_start = EDITOR_GET("mono/editor/show_info_on_start");
|
||||
about_dialog_checkbox->set_pressed(show_on_start);
|
||||
about_dialog->popup_centered_minsize();
|
||||
}
|
||||
|
||||
void GodotSharpEditor::_toggle_about_dialog_on_start(bool p_enabled) {
|
||||
|
||||
bool show_on_start = EDITOR_GET("mono/editor/show_info_on_start");
|
||||
if (show_on_start != p_enabled) {
|
||||
EditorSettings::get_singleton()->set_setting("mono/editor/show_info_on_start", p_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotSharpEditor::_build_solution_pressed() {
|
||||
|
||||
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path())) {
|
||||
if (!_create_project_solution())
|
||||
return; // Failed to create solution
|
||||
}
|
||||
|
||||
MonoBottomPanel::get_singleton()->call("_build_project_pressed");
|
||||
}
|
||||
|
||||
void GodotSharpEditor::_menu_option_pressed(int p_id) {
|
||||
|
||||
switch (p_id) {
|
||||
case MENU_CREATE_SLN: {
|
||||
|
||||
_create_project_solution();
|
||||
} break;
|
||||
case MENU_ABOUT_CSHARP: {
|
||||
|
||||
_show_about_dialog();
|
||||
} break;
|
||||
default:
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
void GodotSharpEditor::_notification(int p_notification) {
|
||||
|
||||
switch (p_notification) {
|
||||
|
||||
case NOTIFICATION_READY: {
|
||||
|
||||
bool show_info_dialog = EDITOR_GET("mono/editor/show_info_on_start");
|
||||
if (show_info_dialog) {
|
||||
about_dialog->set_exclusive(true);
|
||||
_show_about_dialog();
|
||||
// Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive then.
|
||||
about_dialog->set_exclusive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GodotSharpEditor::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_build_solution_pressed"), &GodotSharpEditor::_build_solution_pressed);
|
||||
ClassDB::bind_method(D_METHOD("_create_project_solution"), &GodotSharpEditor::_create_project_solution);
|
||||
ClassDB::bind_method(D_METHOD("_make_api_solutions_if_needed"), &GodotSharpEditor::_make_api_solutions_if_needed);
|
||||
ClassDB::bind_method(D_METHOD("_remove_create_sln_menu_option"), &GodotSharpEditor::_remove_create_sln_menu_option);
|
||||
ClassDB::bind_method(D_METHOD("_toggle_about_dialog_on_start"), &GodotSharpEditor::_toggle_about_dialog_on_start);
|
||||
ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed);
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled(MonoString *p_bundle_id) {
|
||||
#ifdef OSX_ENABLED
|
||||
return (MonoBoolean)osx_is_app_bundle_installed(GDMonoMarshal::mono_string_to_godot(p_bundle_id));
|
||||
#else
|
||||
(void)p_bundle_id; // UNUSED
|
||||
ERR_FAIL_V(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoString *godot_icall_Utils_OS_GetPlatformName() {
|
||||
return GDMonoMarshal::mono_string_from_godot(OS::get_singleton()->get_name());
|
||||
}
|
||||
|
||||
void GodotSharpEditor::register_internal_calls() {
|
||||
|
||||
static bool registered = false;
|
||||
ERR_FAIL_COND(registered);
|
||||
registered = true;
|
||||
|
||||
mono_add_internal_call("GodotSharpTools.Editor.MonoDevelopInstance::IsApplicationBundleInstalled", (void *)godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled);
|
||||
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) {
|
||||
|
||||
error_dialog->set_title(p_title);
|
||||
error_dialog->set_text(p_message);
|
||||
error_dialog->popup_centered_minsize();
|
||||
}
|
||||
|
||||
Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) {
|
||||
|
||||
ExternalEditor editor = ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor")));
|
||||
|
||||
switch (editor) {
|
||||
case EDITOR_VSCODE: {
|
||||
static String vscode_path;
|
||||
|
||||
if (vscode_path.empty() || !FileAccess::exists(vscode_path)) {
|
||||
// Try to search it again if it wasn't found last time or if it was removed from its location
|
||||
bool found = false;
|
||||
|
||||
// TODO: Use initializer lists once C++11 is allowed
|
||||
|
||||
static Vector<String> vscode_names;
|
||||
if (vscode_names.empty()) {
|
||||
vscode_names.push_back("code");
|
||||
vscode_names.push_back("code-oss");
|
||||
vscode_names.push_back("vscode");
|
||||
vscode_names.push_back("vscode-oss");
|
||||
vscode_names.push_back("visual-studio-code");
|
||||
vscode_names.push_back("visual-studio-code-oss");
|
||||
}
|
||||
for (int i = 0; i < vscode_names.size(); i++) {
|
||||
vscode_path = path_which(vscode_names[i]);
|
||||
if (!vscode_path.empty()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
vscode_path.clear(); // Not found, clear so next time the empty() check is enough
|
||||
}
|
||||
|
||||
List<String> args;
|
||||
|
||||
#ifdef OSX_ENABLED
|
||||
// The package path is '/Applications/Visual Studio Code.app'
|
||||
static const String vscode_bundle_id = "com.microsoft.VSCode";
|
||||
static bool osx_app_bundle_installed = osx_is_app_bundle_installed(vscode_bundle_id);
|
||||
|
||||
if (osx_app_bundle_installed) {
|
||||
args.push_back("-b");
|
||||
args.push_back(vscode_bundle_id);
|
||||
|
||||
// The reusing of existing windows made by the 'open' command might not choose a wubdiw that is
|
||||
// editing our folder. It's better to ask for a new window and let VSCode do the window management.
|
||||
args.push_back("-n");
|
||||
|
||||
// The open process must wait until the application finishes (which is instant in VSCode's case)
|
||||
args.push_back("--wait-apps");
|
||||
|
||||
args.push_back("--args");
|
||||
}
|
||||
#endif
|
||||
|
||||
args.push_back(ProjectSettings::get_singleton()->get_resource_path());
|
||||
|
||||
String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
|
||||
|
||||
if (p_line >= 0) {
|
||||
args.push_back("-g");
|
||||
args.push_back(script_path + ":" + itos(p_line + 1) + ":" + itos(p_col));
|
||||
} else {
|
||||
args.push_back(script_path);
|
||||
}
|
||||
|
||||
#ifdef OSX_ENABLED
|
||||
ERR_EXPLAIN("Cannot find code editor: VSCode");
|
||||
ERR_FAIL_COND_V(!osx_app_bundle_installed && vscode_path.empty(), ERR_FILE_NOT_FOUND);
|
||||
|
||||
String command = osx_app_bundle_installed ? "/usr/bin/open" : vscode_path;
|
||||
#else
|
||||
ERR_EXPLAIN("Cannot find code editor: VSCode");
|
||||
ERR_FAIL_COND_V(vscode_path.empty(), ERR_FILE_NOT_FOUND);
|
||||
|
||||
String command = vscode_path;
|
||||
#endif
|
||||
|
||||
Error err = OS::get_singleton()->execute(command, args, false);
|
||||
|
||||
if (err != OK) {
|
||||
ERR_PRINT("Error when trying to execute code editor: VSCode");
|
||||
return err;
|
||||
}
|
||||
} break;
|
||||
#ifdef OSX_ENABLED
|
||||
case EDITOR_VISUALSTUDIO_MAC:
|
||||
// [[fallthrough]];
|
||||
#endif
|
||||
case EDITOR_MONODEVELOP: {
|
||||
#ifdef OSX_ENABLED
|
||||
bool is_visualstudio = editor == EDITOR_VISUALSTUDIO_MAC;
|
||||
|
||||
MonoDevelopInstance **instance = is_visualstudio ?
|
||||
&visualstudio_mac_instance :
|
||||
&monodevelop_instance;
|
||||
|
||||
MonoDevelopInstance::EditorId editor_id = is_visualstudio ?
|
||||
MonoDevelopInstance::VISUALSTUDIO_FOR_MAC :
|
||||
MonoDevelopInstance::MONODEVELOP;
|
||||
#else
|
||||
MonoDevelopInstance **instance = &monodevelop_instance;
|
||||
MonoDevelopInstance::EditorId editor_id = MonoDevelopInstance::MONODEVELOP;
|
||||
#endif
|
||||
|
||||
if (!*instance)
|
||||
*instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path(), editor_id));
|
||||
|
||||
String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
|
||||
|
||||
if (p_line >= 0) {
|
||||
script_path += ";" + itos(p_line + 1) + ";" + itos(p_col);
|
||||
}
|
||||
|
||||
(*instance)->execute(script_path);
|
||||
} break;
|
||||
default:
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool GodotSharpEditor::overrides_external_editor() {
|
||||
|
||||
return ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor"))) != EDITOR_NONE;
|
||||
}
|
||||
|
||||
GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
|
||||
|
||||
singleton = this;
|
||||
|
||||
monodevelop_instance = NULL;
|
||||
#ifdef OSX_ENABLED
|
||||
visualstudio_mac_instance = NULL;
|
||||
#endif
|
||||
|
||||
editor = p_editor;
|
||||
|
||||
error_dialog = memnew(AcceptDialog);
|
||||
editor->get_gui_base()->add_child(error_dialog);
|
||||
|
||||
bottom_panel_btn = editor->add_bottom_panel_item(TTR("Mono"), memnew(MonoBottomPanel(editor)));
|
||||
|
||||
godotsharp_builds = memnew(GodotSharpBuilds);
|
||||
|
||||
editor->add_child(memnew(MonoReloadNode));
|
||||
|
||||
menu_popup = memnew(PopupMenu);
|
||||
menu_popup->hide();
|
||||
menu_popup->set_as_toplevel(true);
|
||||
menu_popup->set_pass_on_modal_close_click(false);
|
||||
|
||||
editor->add_tool_submenu_item("Mono", menu_popup);
|
||||
|
||||
// TODO: Remove or edit this info dialog once Mono support is no longer in alpha
|
||||
{
|
||||
menu_popup->add_item(TTR("About C# support"), MENU_ABOUT_CSHARP);
|
||||
about_dialog = memnew(AcceptDialog);
|
||||
editor->get_gui_base()->add_child(about_dialog);
|
||||
about_dialog->set_title("Important: C# support is not feature-complete");
|
||||
|
||||
// We don't use set_text() as the default AcceptDialog Label doesn't play well with the TextureRect and CheckBox
|
||||
// we'll add. Instead we add containers and a new autowrapped Label inside.
|
||||
|
||||
// Main VBoxContainer (icon + label on top, checkbox at bottom)
|
||||
VBoxContainer *about_vbc = memnew(VBoxContainer);
|
||||
about_dialog->add_child(about_vbc);
|
||||
|
||||
// HBoxContainer for icon + label
|
||||
HBoxContainer *about_hbc = memnew(HBoxContainer);
|
||||
about_vbc->add_child(about_hbc);
|
||||
|
||||
TextureRect *about_icon = memnew(TextureRect);
|
||||
about_hbc->add_child(about_icon);
|
||||
Ref<Texture> about_icon_tex = about_icon->get_icon("NodeWarning", "EditorIcons");
|
||||
about_icon->set_texture(about_icon_tex);
|
||||
|
||||
Label *about_label = memnew(Label);
|
||||
about_hbc->add_child(about_label);
|
||||
about_label->set_custom_minimum_size(Size2(600, 150) * EDSCALE);
|
||||
about_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
about_label->set_autowrap(true);
|
||||
String about_text =
|
||||
String("C# support in Godot Engine is in late alpha stage and, while already usable, ") +
|
||||
"it is not meant for use in production.\n\n" +
|
||||
"Projects can be exported to Linux, macOS and Windows, but not yet to mobile or web platforms. " +
|
||||
"Bugs and usability issues will be addressed gradually over future releases, " +
|
||||
"potentially including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
|
||||
"If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, MSBuild version, IDE, etc.:\n\n" +
|
||||
" https://github.com/godotengine/godot/issues\n\n" +
|
||||
"Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
|
||||
about_label->set_text(about_text);
|
||||
|
||||
EDITOR_DEF("mono/editor/show_info_on_start", true);
|
||||
|
||||
// CheckBox in main container
|
||||
about_dialog_checkbox = memnew(CheckBox);
|
||||
about_vbc->add_child(about_dialog_checkbox);
|
||||
about_dialog_checkbox->set_text("Show this warning when starting the editor");
|
||||
about_dialog_checkbox->connect("toggled", this, "_toggle_about_dialog_on_start");
|
||||
}
|
||||
|
||||
String sln_path = GodotSharpDirs::get_project_sln_path();
|
||||
String csproj_path = GodotSharpDirs::get_project_csproj_path();
|
||||
|
||||
if (FileAccess::exists(sln_path) && FileAccess::exists(csproj_path)) {
|
||||
// Defer this task because EditorProgress calls Main::iterarion() and the main loop is not yet initialized.
|
||||
call_deferred("_make_api_solutions_if_needed");
|
||||
} else {
|
||||
bottom_panel_btn->hide();
|
||||
menu_popup->add_item(TTR("Create C# solution"), MENU_CREATE_SLN);
|
||||
}
|
||||
|
||||
menu_popup->connect("id_pressed", this, "_menu_option_pressed");
|
||||
|
||||
ToolButton *build_button = memnew(ToolButton);
|
||||
build_button->set_text("Build");
|
||||
build_button->set_tooltip("Build solution");
|
||||
build_button->set_focus_mode(Control::FOCUS_NONE);
|
||||
build_button->connect("pressed", this, "_build_solution_pressed");
|
||||
editor->get_menu_hb()->add_child(build_button);
|
||||
|
||||
// External editor settings
|
||||
EditorSettings *ed_settings = EditorSettings::get_singleton();
|
||||
EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE);
|
||||
|
||||
String settings_hint_str = "Disabled";
|
||||
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
settings_hint_str += ",MonoDevelop,Visual Studio Code";
|
||||
#elif defined(OSX_ENABLED)
|
||||
settings_hint_str += ",Visual Studio,MonoDevelop,Visual Studio Code";
|
||||
#elif defined(UNIX_ENABLED)
|
||||
settings_hint_str += ",MonoDevelop,Visual Studio Code";
|
||||
#endif
|
||||
|
||||
ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, settings_hint_str));
|
||||
|
||||
// Export plugin
|
||||
Ref<GodotSharpExport> godotsharp_export;
|
||||
godotsharp_export.instance();
|
||||
EditorExport::get_singleton()->add_export_plugin(godotsharp_export);
|
||||
}
|
||||
|
||||
GodotSharpEditor::~GodotSharpEditor() {
|
||||
|
||||
singleton = NULL;
|
||||
|
||||
memdelete(godotsharp_builds);
|
||||
|
||||
if (monodevelop_instance) {
|
||||
memdelete(monodevelop_instance);
|
||||
monodevelop_instance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MonoReloadNode *MonoReloadNode::singleton = NULL;
|
||||
|
||||
void MonoReloadNode::_reload_timer_timeout() {
|
||||
|
||||
if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) {
|
||||
CSharpLanguage::get_singleton()->reload_assemblies(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MonoReloadNode::restart_reload_timer() {
|
||||
|
||||
reload_timer->stop();
|
||||
reload_timer->start();
|
||||
}
|
||||
|
||||
void MonoReloadNode::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_reload_timer_timeout"), &MonoReloadNode::_reload_timer_timeout);
|
||||
}
|
||||
|
||||
void MonoReloadNode::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
|
||||
restart_reload_timer();
|
||||
if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) {
|
||||
CSharpLanguage::get_singleton()->reload_assemblies(false);
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
};
|
||||
}
|
||||
|
||||
MonoReloadNode::MonoReloadNode() {
|
||||
|
||||
singleton = this;
|
||||
|
||||
reload_timer = memnew(Timer);
|
||||
add_child(reload_timer);
|
||||
reload_timer->set_one_shot(false);
|
||||
reload_timer->set_wait_time(EDITOR_DEF("mono/assembly_watch_interval_sec", 0.5));
|
||||
reload_timer->connect("timeout", this, "_reload_timer_timeout");
|
||||
reload_timer->start();
|
||||
}
|
||||
|
||||
MonoReloadNode::~MonoReloadNode() {
|
||||
|
||||
singleton = NULL;
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* godotsharp_editor.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 GODOTSHARP_EDITOR_H
|
||||
#define GODOTSHARP_EDITOR_H
|
||||
|
||||
#include "godotsharp_builds.h"
|
||||
#include "monodevelop_instance.h"
|
||||
|
||||
class GodotSharpEditor : public Node {
|
||||
GDCLASS(GodotSharpEditor, Node);
|
||||
|
||||
EditorNode *editor;
|
||||
|
||||
MenuButton *menu_button;
|
||||
PopupMenu *menu_popup;
|
||||
|
||||
AcceptDialog *error_dialog;
|
||||
AcceptDialog *about_dialog;
|
||||
CheckBox *about_dialog_checkbox;
|
||||
|
||||
ToolButton *bottom_panel_btn;
|
||||
|
||||
GodotSharpBuilds *godotsharp_builds;
|
||||
|
||||
MonoDevelopInstance *monodevelop_instance;
|
||||
#ifdef OSX_ENABLED
|
||||
MonoDevelopInstance *visualstudio_mac_instance;
|
||||
#endif
|
||||
|
||||
bool _create_project_solution();
|
||||
void _make_api_solutions_if_needed();
|
||||
void _make_api_solutions_if_needed_impl();
|
||||
|
||||
void _remove_create_sln_menu_option();
|
||||
void _show_about_dialog();
|
||||
void _toggle_about_dialog_on_start(bool p_enabled);
|
||||
|
||||
void _menu_option_pressed(int p_id);
|
||||
|
||||
void _build_solution_pressed();
|
||||
|
||||
static GodotSharpEditor *singleton;
|
||||
|
||||
protected:
|
||||
void _notification(int p_notification);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
enum MenuOptions {
|
||||
MENU_CREATE_SLN,
|
||||
MENU_ABOUT_CSHARP,
|
||||
};
|
||||
|
||||
enum ExternalEditor {
|
||||
EDITOR_NONE,
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
//EDITOR_VISUALSTUDIO, // TODO
|
||||
EDITOR_MONODEVELOP,
|
||||
EDITOR_VSCODE
|
||||
#elif defined(OSX_ENABLED)
|
||||
EDITOR_VISUALSTUDIO_MAC,
|
||||
EDITOR_MONODEVELOP,
|
||||
EDITOR_VSCODE
|
||||
#elif defined(UNIX_ENABLED)
|
||||
EDITOR_MONODEVELOP,
|
||||
EDITOR_VSCODE
|
||||
#endif
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ static GodotSharpEditor *get_singleton() { return singleton; }
|
||||
|
||||
static void register_internal_calls();
|
||||
|
||||
void show_error_dialog(const String &p_message, const String &p_title = "Error");
|
||||
|
||||
Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col);
|
||||
bool overrides_external_editor();
|
||||
|
||||
GodotSharpEditor(EditorNode *p_editor);
|
||||
~GodotSharpEditor();
|
||||
};
|
||||
|
||||
class MonoReloadNode : public Node {
|
||||
GDCLASS(MonoReloadNode, Node);
|
||||
|
||||
Timer *reload_timer;
|
||||
|
||||
void _reload_timer_timeout();
|
||||
|
||||
static MonoReloadNode *singleton;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ static MonoReloadNode *get_singleton() { return singleton; }
|
||||
|
||||
void restart_reload_timer();
|
||||
|
||||
MonoReloadNode();
|
||||
~MonoReloadNode();
|
||||
};
|
||||
|
||||
#endif // GODOTSHARP_EDITOR_H
|
|
@ -30,180 +30,28 @@
|
|||
|
||||
#include "godotsharp_export.h"
|
||||
|
||||
#include "core/version.h"
|
||||
#include <mono/metadata/image.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 "csharp_project.h"
|
||||
#include "godotsharp_builds.h"
|
||||
#include "../mono_gd/gd_mono.h"
|
||||
#include "../mono_gd/gd_mono_assembly.h"
|
||||
|
||||
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));
|
||||
String get_assemblyref_name(MonoImage *p_image, int index) {
|
||||
const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF);
|
||||
|
||||
uint32_t cols[MONO_ASSEMBLYREF_SIZE];
|
||||
|
||||
mono_metadata_decode_row(table_info, index, cols, MONO_ASSEMBLYREF_SIZE);
|
||||
|
||||
return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME]));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ERR_FAIL_COND(p_path.get_extension() != CSharpLanguage::get_singleton()->get_extension());
|
||||
|
||||
// TODO what if the source file is not part of the game's C# project
|
||||
|
||||
if (!GLOBAL_GET("mono/export/include_scripts_content")) {
|
||||
// We don't want to include the source code on exported games
|
||||
add_file(p_path, Vector<uint8_t>(), false);
|
||||
skip();
|
||||
}
|
||||
}
|
||||
|
||||
void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags) {
|
||||
|
||||
// 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(TOOLS_DOMAIN);
|
||||
ERR_FAIL_NULL(GDMono::get_singleton()->get_editor_tools_assembly());
|
||||
|
||||
if (FileAccess::exists(GodotSharpDirs::get_project_sln_path())) {
|
||||
String build_config = p_debug ? "Debug" : "Release";
|
||||
|
||||
String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata." + String(p_debug ? "debug" : "release"));
|
||||
Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path);
|
||||
ERR_FAIL_COND(metadata_err != OK);
|
||||
|
||||
ERR_FAIL_COND(!_add_file(scripts_metadata_path, scripts_metadata_path));
|
||||
|
||||
// Turn export features into defines
|
||||
Vector<String> godot_defines;
|
||||
for (Set<String>::Element *E = p_features.front(); E; E = E->next()) {
|
||||
godot_defines.push_back(E->get());
|
||||
}
|
||||
ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config, godot_defines));
|
||||
|
||||
// Add dependency assemblies
|
||||
|
||||
Map<String, String> dependencies;
|
||||
|
||||
String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
String project_dll_name_safe = OS::get_singleton()->get_safe_dir_name(project_dll_name);
|
||||
if (project_dll_name_safe.empty()) {
|
||||
project_dll_name_safe = "UnnamedProject";
|
||||
}
|
||||
|
||||
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_safe + ".dll");
|
||||
dependencies.insert(project_dll_name_safe, project_dll_src_path);
|
||||
|
||||
{
|
||||
MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
|
||||
ERR_FAIL_NULL(export_domain);
|
||||
_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
|
||||
|
||||
_GDMONO_SCOPE_DOMAIN_(export_domain);
|
||||
|
||||
GDMonoAssembly *scripts_assembly = NULL;
|
||||
bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name_safe,
|
||||
project_dll_src_path, &scripts_assembly, /* refonly: */ true);
|
||||
|
||||
ERR_EXPLAIN("Cannot load assembly (refonly): " + project_dll_name_safe);
|
||||
ERR_FAIL_COND(!load_success);
|
||||
|
||||
Vector<String> search_dirs;
|
||||
String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
|
||||
String android_bcl_dir = templates_dir.plus_file("android-bcl");
|
||||
|
||||
String custom_lib_dir;
|
||||
|
||||
if (p_features.find("Android") && DirAccess::exists(android_bcl_dir)) {
|
||||
custom_lib_dir = android_bcl_dir;
|
||||
}
|
||||
|
||||
GDMonoAssembly::fill_search_dirs(search_dirs, build_config, custom_lib_dir);
|
||||
|
||||
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_file(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_setref(features, 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_file(const String &p_src_path, const String &p_dst_path, bool p_remap) {
|
||||
|
||||
FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V(!f, false);
|
||||
|
||||
Vector<uint8_t> data;
|
||||
data.resize(f->get_len());
|
||||
f->get_buffer(data.ptrw(), data.size());
|
||||
|
||||
add_file(p_dst_path, data, p_remap);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies) {
|
||||
|
||||
Error GodotSharpExport::get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) {
|
||||
MonoImage *image = p_assembly->get_image();
|
||||
|
||||
for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
|
||||
MonoAssemblyName *ref_aname = aname_prealloc;
|
||||
mono_assembly_get_assemblyref(image, i, ref_aname);
|
||||
String ref_name = mono_assembly_name_get_name(ref_aname);
|
||||
String ref_name = get_assemblyref_name(image, i);
|
||||
|
||||
if (r_dependencies.find(ref_name))
|
||||
if (r_dependencies.has(ref_name))
|
||||
continue;
|
||||
|
||||
GDMonoAssembly *ref_assembly = NULL;
|
||||
|
@ -242,9 +90,9 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c
|
|||
ERR_FAIL_V(ERR_CANT_RESOLVE);
|
||||
}
|
||||
|
||||
r_dependencies.insert(ref_name, ref_assembly->get_path());
|
||||
r_dependencies[ref_name] = ref_assembly->get_path();
|
||||
|
||||
Error err = _get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies);
|
||||
Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies);
|
||||
if (err != OK) {
|
||||
ERR_EXPLAIN("Cannot load one of the dependencies for the assembly: " + ref_name);
|
||||
ERR_FAIL_V(err);
|
||||
|
@ -254,14 +102,22 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c
|
|||
return OK;
|
||||
}
|
||||
|
||||
GodotSharpExport::GodotSharpExport() {
|
||||
// MonoAssemblyName is an incomplete type (internal to mono), so we can't allocate it ourselves.
|
||||
// There isn't any api to allocate an empty one either, so we need to do it this way.
|
||||
aname_prealloc = mono_assembly_name_new("whatever");
|
||||
mono_assembly_name_free(aname_prealloc); // "it does not frees the object itself, only the name members" (typo included)
|
||||
}
|
||||
Error GodotSharpExport::get_exported_assembly_dependencies(const String &p_project_dll_name, const String &p_project_dll_src_path, const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_dependencies) {
|
||||
MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
|
||||
ERR_FAIL_NULL_V(export_domain, FAILED);
|
||||
_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
|
||||
|
||||
GodotSharpExport::~GodotSharpExport() {
|
||||
if (aname_prealloc)
|
||||
mono_free(aname_prealloc);
|
||||
_GDMONO_SCOPE_DOMAIN_(export_domain);
|
||||
|
||||
GDMonoAssembly *scripts_assembly = NULL;
|
||||
bool load_success = GDMono::get_singleton()->load_assembly_from(p_project_dll_name,
|
||||
p_project_dll_src_path, &scripts_assembly, /* refonly: */ true);
|
||||
|
||||
ERR_EXPLAIN("Cannot load assembly (refonly): " + p_project_dll_name);
|
||||
ERR_FAIL_COND_V(!load_success, ERR_CANT_RESOLVE);
|
||||
|
||||
Vector<String> search_dirs;
|
||||
GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_lib_dir);
|
||||
|
||||
return get_assembly_dependencies(scripts_assembly, search_dirs, r_dependencies);
|
||||
}
|
||||
|
|
|
@ -31,29 +31,19 @@
|
|||
#ifndef GODOTSHARP_EXPORT_H
|
||||
#define GODOTSHARP_EXPORT_H
|
||||
|
||||
#include <mono/metadata/image.h>
|
||||
|
||||
#include "editor/editor_export.h"
|
||||
#include "core/dictionary.h"
|
||||
#include "core/error_list.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
#include "../mono_gd/gd_mono_header.h"
|
||||
|
||||
class GodotSharpExport : public EditorExportPlugin {
|
||||
namespace GodotSharpExport {
|
||||
|
||||
MonoAssemblyName *aname_prealloc;
|
||||
Error get_exported_assembly_dependencies(const String &p_project_dll_name,
|
||||
const String &p_project_dll_src_path, const String &p_build_config,
|
||||
const String &p_custom_lib_dir, Dictionary &r_dependencies);
|
||||
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
|
||||
|
||||
bool _add_file(const String &p_src_path, const String &p_dst_path, bool p_remap = false);
|
||||
|
||||
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();
|
||||
};
|
||||
} // namespace GodotSharpExport
|
||||
|
||||
#endif // GODOTSHARP_EXPORT_H
|
||||
|
|
|
@ -1,528 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* mono_bottom_panel.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 "mono_bottom_panel.h"
|
||||
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/script_editor_debugger.h"
|
||||
|
||||
#include "../csharp_script.h"
|
||||
#include "../godotsharp_dirs.h"
|
||||
#include "csharp_project.h"
|
||||
#include "godotsharp_editor.h"
|
||||
|
||||
MonoBottomPanel *MonoBottomPanel::singleton = NULL;
|
||||
|
||||
void MonoBottomPanel::_update_build_tabs_list() {
|
||||
|
||||
build_tabs_list->clear();
|
||||
|
||||
int current_tab = build_tabs->get_current_tab();
|
||||
|
||||
bool no_current_tab = current_tab < 0 || current_tab >= build_tabs->get_tab_count();
|
||||
|
||||
for (int i = 0; i < build_tabs->get_child_count(); i++) {
|
||||
|
||||
MonoBuildTab *tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(i));
|
||||
|
||||
if (tab) {
|
||||
String item_name = tab->build_info.solution.get_file().get_basename();
|
||||
item_name += " [" + tab->build_info.configuration + "]";
|
||||
|
||||
build_tabs_list->add_item(item_name, tab->get_icon_texture());
|
||||
|
||||
String item_tooltip = "Solution: " + tab->build_info.solution;
|
||||
item_tooltip += "\nConfiguration: " + tab->build_info.configuration;
|
||||
item_tooltip += "\nStatus: ";
|
||||
|
||||
if (tab->build_exited) {
|
||||
item_tooltip += tab->build_result == MonoBuildTab::RESULT_SUCCESS ? "Succeeded" : "Errored";
|
||||
} else {
|
||||
item_tooltip += "Running";
|
||||
}
|
||||
|
||||
if (!tab->build_exited || tab->build_result == MonoBuildTab::RESULT_ERROR) {
|
||||
item_tooltip += "\nErrors: " + itos(tab->error_count);
|
||||
}
|
||||
|
||||
item_tooltip += "\nWarnings: " + itos(tab->warning_count);
|
||||
|
||||
build_tabs_list->set_item_tooltip(i, item_tooltip);
|
||||
|
||||
if (no_current_tab || current_tab == i) {
|
||||
build_tabs_list->select(i);
|
||||
_build_tabs_item_selected(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MonoBottomPanel::add_build_tab(MonoBuildTab *p_build_tab) {
|
||||
|
||||
build_tabs->add_child(p_build_tab);
|
||||
raise_build_tab(p_build_tab);
|
||||
}
|
||||
|
||||
void MonoBottomPanel::raise_build_tab(MonoBuildTab *p_build_tab) {
|
||||
|
||||
ERR_FAIL_COND(p_build_tab->get_parent() != build_tabs);
|
||||
build_tabs->move_child(p_build_tab, 0);
|
||||
_update_build_tabs_list();
|
||||
}
|
||||
|
||||
void MonoBottomPanel::show_build_tab() {
|
||||
|
||||
for (int i = 0; i < panel_tabs->get_tab_count(); i++) {
|
||||
if (panel_tabs->get_tab_control(i) == panel_builds_tab) {
|
||||
panel_tabs->set_current_tab(i);
|
||||
editor->make_bottom_panel_item_visible(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_PRINT("Builds tab not found");
|
||||
}
|
||||
|
||||
void MonoBottomPanel::_build_tabs_item_selected(int p_idx) {
|
||||
|
||||
ERR_FAIL_INDEX(p_idx, build_tabs->get_tab_count());
|
||||
|
||||
build_tabs->set_current_tab(p_idx);
|
||||
if (!build_tabs->is_visible())
|
||||
build_tabs->set_visible(true);
|
||||
|
||||
warnings_btn->set_visible(true);
|
||||
errors_btn->set_visible(true);
|
||||
view_log_btn->set_visible(true);
|
||||
}
|
||||
|
||||
void MonoBottomPanel::_build_tabs_nothing_selected() {
|
||||
|
||||
if (build_tabs->get_tab_count() != 0) { // just in case
|
||||
build_tabs->set_visible(false);
|
||||
|
||||
// This callback is called when clicking on the empty space of the list.
|
||||
// ItemList won't deselect the items automatically, so we must do it ourselves.
|
||||
build_tabs_list->unselect_all();
|
||||
}
|
||||
|
||||
warnings_btn->set_visible(false);
|
||||
errors_btn->set_visible(false);
|
||||
view_log_btn->set_visible(false);
|
||||
}
|
||||
|
||||
void MonoBottomPanel::_warnings_toggled(bool p_pressed) {
|
||||
|
||||
int current_tab = build_tabs->get_current_tab();
|
||||
ERR_FAIL_INDEX(current_tab, build_tabs->get_tab_count());
|
||||
MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(current_tab));
|
||||
build_tab->warnings_visible = p_pressed;
|
||||
build_tab->_update_issues_list();
|
||||
}
|
||||
|
||||
void MonoBottomPanel::_errors_toggled(bool p_pressed) {
|
||||
|
||||
int current_tab = build_tabs->get_current_tab();
|
||||
ERR_FAIL_INDEX(current_tab, build_tabs->get_tab_count());
|
||||
MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(current_tab));
|
||||
build_tab->errors_visible = p_pressed;
|
||||
build_tab->_update_issues_list();
|
||||
}
|
||||
|
||||
void MonoBottomPanel::_build_project_pressed() {
|
||||
|
||||
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
|
||||
return; // No solution to build
|
||||
|
||||
String scripts_metadata_path_editor = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor");
|
||||
String scripts_metadata_path_player = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor_player");
|
||||
|
||||
Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor);
|
||||
ERR_FAIL_COND(metadata_err != OK);
|
||||
|
||||
if (FileAccess::exists(scripts_metadata_path_editor)) {
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
||||
Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player);
|
||||
|
||||
ERR_EXPLAIN("Failed to copy scripts metadata file");
|
||||
ERR_FAIL_COND(copy_err != OK);
|
||||
}
|
||||
|
||||
Vector<String> godot_defines;
|
||||
godot_defines.push_back(OS::get_singleton()->get_name());
|
||||
godot_defines.push_back((sizeof(void *) == 4 ? "32" : "64"));
|
||||
bool build_success = GodotSharpBuilds::get_singleton()->build_project_blocking("Tools", godot_defines);
|
||||
|
||||
if (build_success) {
|
||||
// Notify running game for hot-reload
|
||||
ScriptEditor::get_singleton()->get_debugger()->reload_scripts();
|
||||
|
||||
// Hot-reload in the editor
|
||||
MonoReloadNode::get_singleton()->restart_reload_timer();
|
||||
|
||||
if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) {
|
||||
CSharpLanguage::get_singleton()->reload_assemblies(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MonoBottomPanel::_view_log_pressed() {
|
||||
|
||||
if (build_tabs_list->is_anything_selected()) {
|
||||
Vector<int> selected_items = build_tabs_list->get_selected_items();
|
||||
CRASH_COND(selected_items.size() != 1);
|
||||
int selected_item = selected_items[0];
|
||||
|
||||
MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_tab_control(selected_item));
|
||||
ERR_FAIL_NULL(build_tab);
|
||||
|
||||
String log_dirpath = build_tab->get_build_info().get_log_dirpath();
|
||||
|
||||
OS::get_singleton()->shell_open(log_dirpath.plus_file(GodotSharpBuilds::get_msbuild_log_filename()));
|
||||
}
|
||||
}
|
||||
|
||||
void MonoBottomPanel::_notification(int p_what) {
|
||||
|
||||
switch (p_what) {
|
||||
|
||||
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
|
||||
panel_tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles"));
|
||||
panel_tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles"));
|
||||
panel_tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles"));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void MonoBottomPanel::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_build_project_pressed"), &MonoBottomPanel::_build_project_pressed);
|
||||
ClassDB::bind_method(D_METHOD("_view_log_pressed"), &MonoBottomPanel::_view_log_pressed);
|
||||
ClassDB::bind_method(D_METHOD("_warnings_toggled", "pressed"), &MonoBottomPanel::_warnings_toggled);
|
||||
ClassDB::bind_method(D_METHOD("_errors_toggled", "pressed"), &MonoBottomPanel::_errors_toggled);
|
||||
ClassDB::bind_method(D_METHOD("_build_tabs_item_selected", "idx"), &MonoBottomPanel::_build_tabs_item_selected);
|
||||
ClassDB::bind_method(D_METHOD("_build_tabs_nothing_selected"), &MonoBottomPanel::_build_tabs_nothing_selected);
|
||||
}
|
||||
|
||||
MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) {
|
||||
|
||||
singleton = this;
|
||||
|
||||
editor = p_editor;
|
||||
|
||||
set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
set_anchors_and_margins_preset(Control::PRESET_WIDE);
|
||||
|
||||
panel_tabs = memnew(TabContainer);
|
||||
panel_tabs->set_tab_align(TabContainer::ALIGN_LEFT);
|
||||
panel_tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles"));
|
||||
panel_tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles"));
|
||||
panel_tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles"));
|
||||
panel_tabs->set_custom_minimum_size(Size2(0, 228) * EDSCALE);
|
||||
panel_tabs->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
add_child(panel_tabs);
|
||||
|
||||
{ // Builds
|
||||
panel_builds_tab = memnew(VBoxContainer);
|
||||
panel_builds_tab->set_name(TTR("Builds"));
|
||||
panel_builds_tab->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
panel_tabs->add_child(panel_builds_tab);
|
||||
|
||||
HBoxContainer *toolbar_hbc = memnew(HBoxContainer);
|
||||
toolbar_hbc->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
panel_builds_tab->add_child(toolbar_hbc);
|
||||
|
||||
Button *build_project_btn = memnew(Button);
|
||||
build_project_btn->set_text(TTR("Build Project"));
|
||||
build_project_btn->set_focus_mode(FOCUS_NONE);
|
||||
build_project_btn->connect("pressed", this, "_build_project_pressed");
|
||||
toolbar_hbc->add_child(build_project_btn);
|
||||
|
||||
toolbar_hbc->add_spacer();
|
||||
|
||||
warnings_btn = memnew(ToolButton);
|
||||
warnings_btn->set_text(TTR("Warnings"));
|
||||
warnings_btn->set_toggle_mode(true);
|
||||
warnings_btn->set_pressed(true);
|
||||
warnings_btn->set_visible(false);
|
||||
warnings_btn->set_focus_mode(FOCUS_NONE);
|
||||
warnings_btn->connect("toggled", this, "_warnings_toggled");
|
||||
toolbar_hbc->add_child(warnings_btn);
|
||||
|
||||
errors_btn = memnew(ToolButton);
|
||||
errors_btn->set_text(TTR("Errors"));
|
||||
errors_btn->set_toggle_mode(true);
|
||||
errors_btn->set_pressed(true);
|
||||
errors_btn->set_visible(false);
|
||||
errors_btn->set_focus_mode(FOCUS_NONE);
|
||||
errors_btn->connect("toggled", this, "_errors_toggled");
|
||||
toolbar_hbc->add_child(errors_btn);
|
||||
|
||||
toolbar_hbc->add_spacer();
|
||||
|
||||
view_log_btn = memnew(Button);
|
||||
view_log_btn->set_text(TTR("View log"));
|
||||
view_log_btn->set_focus_mode(FOCUS_NONE);
|
||||
view_log_btn->set_visible(false);
|
||||
view_log_btn->connect("pressed", this, "_view_log_pressed");
|
||||
toolbar_hbc->add_child(view_log_btn);
|
||||
|
||||
HSplitContainer *hsc = memnew(HSplitContainer);
|
||||
hsc->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
hsc->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
panel_builds_tab->add_child(hsc);
|
||||
|
||||
build_tabs_list = memnew(ItemList);
|
||||
build_tabs_list->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
build_tabs_list->connect("item_selected", this, "_build_tabs_item_selected");
|
||||
build_tabs_list->connect("nothing_selected", this, "_build_tabs_nothing_selected");
|
||||
hsc->add_child(build_tabs_list);
|
||||
|
||||
build_tabs = memnew(TabContainer);
|
||||
build_tabs->set_tab_align(TabContainer::ALIGN_LEFT);
|
||||
build_tabs->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
build_tabs->set_tabs_visible(false);
|
||||
hsc->add_child(build_tabs);
|
||||
}
|
||||
}
|
||||
|
||||
MonoBottomPanel::~MonoBottomPanel() {
|
||||
|
||||
singleton = NULL;
|
||||
}
|
||||
|
||||
void MonoBuildTab::_load_issues_from_file(const String &p_csv_file) {
|
||||
|
||||
FileAccessRef f = FileAccess::open(p_csv_file, FileAccess::READ);
|
||||
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
while (!f->eof_reached()) {
|
||||
Vector<String> csv_line = f->get_csv_line();
|
||||
|
||||
if (csv_line.size() == 1 && csv_line[0].empty())
|
||||
return;
|
||||
|
||||
ERR_CONTINUE(csv_line.size() != 7);
|
||||
|
||||
BuildIssue issue;
|
||||
issue.warning = csv_line[0] == "warning";
|
||||
issue.file = csv_line[1];
|
||||
issue.line = csv_line[2].to_int();
|
||||
issue.column = csv_line[3].to_int();
|
||||
issue.code = csv_line[4];
|
||||
issue.message = csv_line[5];
|
||||
issue.project_file = csv_line[6];
|
||||
|
||||
if (issue.warning)
|
||||
warning_count += 1;
|
||||
else
|
||||
error_count += 1;
|
||||
|
||||
issues.push_back(issue);
|
||||
}
|
||||
}
|
||||
|
||||
void MonoBuildTab::_update_issues_list() {
|
||||
|
||||
issues_list->clear();
|
||||
|
||||
Ref<Texture> warning_icon = get_icon("Warning", "EditorIcons");
|
||||
Ref<Texture> error_icon = get_icon("Error", "EditorIcons");
|
||||
|
||||
for (int i = 0; i < issues.size(); i++) {
|
||||
|
||||
const BuildIssue &issue = issues[i];
|
||||
|
||||
if (!(issue.warning ? warnings_visible : errors_visible))
|
||||
continue;
|
||||
|
||||
String tooltip;
|
||||
tooltip += String("Message: ") + issue.message;
|
||||
|
||||
if (issue.code.length()) {
|
||||
tooltip += String("\nCode: ") + issue.code;
|
||||
}
|
||||
|
||||
tooltip += String("\nType: ") + (issue.warning ? "warning" : "error");
|
||||
|
||||
String text;
|
||||
|
||||
if (issue.file.length()) {
|
||||
String sline = String::num_int64(issue.line);
|
||||
String scolumn = String::num_int64(issue.column);
|
||||
|
||||
text += issue.file + "(";
|
||||
text += sline + ",";
|
||||
text += scolumn + "): ";
|
||||
|
||||
tooltip += "\nFile: " + issue.file;
|
||||
tooltip += "\nLine: " + sline;
|
||||
tooltip += "\nColumn: " + scolumn;
|
||||
}
|
||||
|
||||
if (issue.project_file.length()) {
|
||||
tooltip += "\nProject: " + issue.project_file;
|
||||
}
|
||||
|
||||
text += issue.message;
|
||||
|
||||
int line_break_idx = text.find("\n");
|
||||
issues_list->add_item(line_break_idx == -1 ? text : text.substr(0, line_break_idx),
|
||||
issue.warning ? warning_icon : error_icon);
|
||||
int index = issues_list->get_item_count() - 1;
|
||||
issues_list->set_item_tooltip(index, tooltip);
|
||||
issues_list->set_item_metadata(index, i);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Texture> MonoBuildTab::get_icon_texture() const {
|
||||
|
||||
if (build_exited) {
|
||||
if (build_result == RESULT_ERROR) {
|
||||
return get_icon("StatusError", "EditorIcons");
|
||||
} else {
|
||||
return get_icon("StatusSuccess", "EditorIcons");
|
||||
}
|
||||
} else {
|
||||
return get_icon("Stop", "EditorIcons");
|
||||
}
|
||||
}
|
||||
|
||||
MonoBuildInfo MonoBuildTab::get_build_info() {
|
||||
|
||||
return build_info;
|
||||
}
|
||||
|
||||
void MonoBuildTab::on_build_start() {
|
||||
|
||||
build_exited = false;
|
||||
|
||||
issues.clear();
|
||||
warning_count = 0;
|
||||
error_count = 0;
|
||||
_update_issues_list();
|
||||
|
||||
MonoBottomPanel::get_singleton()->raise_build_tab(this);
|
||||
}
|
||||
|
||||
void MonoBuildTab::on_build_exit(BuildResult result) {
|
||||
|
||||
build_exited = true;
|
||||
build_result = result;
|
||||
|
||||
_load_issues_from_file(logs_dir.plus_file(GodotSharpBuilds::get_msbuild_issues_filename()));
|
||||
_update_issues_list();
|
||||
|
||||
MonoBottomPanel::get_singleton()->raise_build_tab(this);
|
||||
}
|
||||
|
||||
void MonoBuildTab::on_build_exec_failed(const String &p_cause) {
|
||||
|
||||
build_exited = true;
|
||||
build_result = RESULT_ERROR;
|
||||
|
||||
issues_list->clear();
|
||||
|
||||
BuildIssue issue;
|
||||
issue.message = p_cause;
|
||||
issue.warning = false;
|
||||
|
||||
error_count += 1;
|
||||
issues.push_back(issue);
|
||||
|
||||
_update_issues_list();
|
||||
|
||||
MonoBottomPanel::get_singleton()->raise_build_tab(this);
|
||||
}
|
||||
|
||||
void MonoBuildTab::restart_build() {
|
||||
|
||||
ERR_FAIL_COND(!build_exited);
|
||||
GodotSharpBuilds::get_singleton()->restart_build(this);
|
||||
}
|
||||
|
||||
void MonoBuildTab::stop_build() {
|
||||
|
||||
ERR_FAIL_COND(build_exited);
|
||||
GodotSharpBuilds::get_singleton()->stop_build(this);
|
||||
}
|
||||
|
||||
void MonoBuildTab::_issue_activated(int p_idx) {
|
||||
|
||||
ERR_FAIL_INDEX(p_idx, issues_list->get_item_count());
|
||||
|
||||
// Get correct issue idx from issue list
|
||||
int issue_idx = this->issues_list->get_item_metadata(p_idx);
|
||||
|
||||
ERR_FAIL_INDEX(issue_idx, issues.size());
|
||||
|
||||
const BuildIssue &issue = issues[issue_idx];
|
||||
|
||||
if (issue.project_file.empty() && issue.file.empty())
|
||||
return;
|
||||
|
||||
String project_dir = issue.project_file.length() ? issue.project_file.get_base_dir() : build_info.solution.get_base_dir();
|
||||
|
||||
String file = project_dir.simplify_path().plus_file(issue.file.simplify_path());
|
||||
|
||||
if (!FileAccess::exists(file))
|
||||
return;
|
||||
|
||||
file = ProjectSettings::get_singleton()->localize_path(file);
|
||||
|
||||
if (file.begins_with("res://")) {
|
||||
Ref<Script> script = ResourceLoader::load(file, CSharpLanguage::get_singleton()->get_type());
|
||||
|
||||
if (script.is_valid() && ScriptEditor::get_singleton()->edit(script, issue.line, issue.column)) {
|
||||
EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MonoBuildTab::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method("_issue_activated", &MonoBuildTab::_issue_activated);
|
||||
}
|
||||
|
||||
MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) :
|
||||
build_exited(false),
|
||||
issues_list(memnew(ItemList)),
|
||||
error_count(0),
|
||||
warning_count(0),
|
||||
errors_visible(true),
|
||||
warnings_visible(true),
|
||||
logs_dir(p_logs_dir),
|
||||
build_info(p_build_info) {
|
||||
issues_list->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
issues_list->connect("item_activated", this, "_issue_activated");
|
||||
add_child(issues_list);
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* mono_bottom_panel.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 MONO_BOTTOM_PANEL_H
|
||||
#define MONO_BOTTOM_PANEL_H
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
#include "mono_build_info.h"
|
||||
|
||||
class MonoBuildTab;
|
||||
|
||||
class MonoBottomPanel : public VBoxContainer {
|
||||
|
||||
GDCLASS(MonoBottomPanel, VBoxContainer);
|
||||
|
||||
EditorNode *editor;
|
||||
|
||||
TabContainer *panel_tabs;
|
||||
|
||||
VBoxContainer *panel_builds_tab;
|
||||
|
||||
ItemList *build_tabs_list;
|
||||
TabContainer *build_tabs;
|
||||
|
||||
ToolButton *warnings_btn;
|
||||
ToolButton *errors_btn;
|
||||
Button *view_log_btn;
|
||||
|
||||
void _update_build_tabs_list();
|
||||
|
||||
void _build_tabs_item_selected(int p_idx);
|
||||
void _build_tabs_nothing_selected();
|
||||
|
||||
void _warnings_toggled(bool p_pressed);
|
||||
void _errors_toggled(bool p_pressed);
|
||||
|
||||
void _build_project_pressed();
|
||||
void _view_log_pressed();
|
||||
|
||||
static MonoBottomPanel *singleton;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ static MonoBottomPanel *get_singleton() { return singleton; }
|
||||
|
||||
void add_build_tab(MonoBuildTab *p_build_tab);
|
||||
void raise_build_tab(MonoBuildTab *p_build_tab);
|
||||
|
||||
void show_build_tab();
|
||||
|
||||
MonoBottomPanel(EditorNode *p_editor = NULL);
|
||||
~MonoBottomPanel();
|
||||
};
|
||||
|
||||
class MonoBuildTab : public VBoxContainer {
|
||||
|
||||
GDCLASS(MonoBuildTab, VBoxContainer);
|
||||
|
||||
public:
|
||||
enum BuildResult {
|
||||
RESULT_ERROR,
|
||||
RESULT_SUCCESS
|
||||
};
|
||||
|
||||
struct BuildIssue {
|
||||
bool warning;
|
||||
String file;
|
||||
int line;
|
||||
int column;
|
||||
String code;
|
||||
String message;
|
||||
String project_file;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class MonoBottomPanel;
|
||||
|
||||
bool build_exited;
|
||||
BuildResult build_result;
|
||||
|
||||
Vector<BuildIssue> issues;
|
||||
ItemList *issues_list;
|
||||
|
||||
int error_count;
|
||||
int warning_count;
|
||||
|
||||
bool errors_visible;
|
||||
bool warnings_visible;
|
||||
|
||||
String logs_dir;
|
||||
|
||||
MonoBuildInfo build_info;
|
||||
|
||||
void _load_issues_from_file(const String &p_csv_file);
|
||||
void _update_issues_list();
|
||||
|
||||
void _issue_activated(int p_idx);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
Ref<Texture> get_icon_texture() const;
|
||||
|
||||
MonoBuildInfo get_build_info();
|
||||
|
||||
void on_build_start();
|
||||
void on_build_exit(BuildResult result);
|
||||
void on_build_exec_failed(const String &p_cause);
|
||||
|
||||
void restart_build();
|
||||
void stop_build();
|
||||
|
||||
MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir);
|
||||
};
|
||||
|
||||
#endif // MONO_BOTTOM_PANEL_H
|
|
@ -1,62 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* mono_build_info.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 "mono_build_info.h"
|
||||
|
||||
#include "../godotsharp_dirs.h"
|
||||
#include "../mono_gd/gd_mono_utils.h"
|
||||
|
||||
uint32_t MonoBuildInfo::Hasher::hash(const MonoBuildInfo &p_key) {
|
||||
|
||||
uint32_t hash = 0;
|
||||
|
||||
GDMonoUtils::hash_combine(hash, p_key.solution.hash());
|
||||
GDMonoUtils::hash_combine(hash, p_key.configuration.hash());
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool MonoBuildInfo::operator==(const MonoBuildInfo &p_b) const {
|
||||
|
||||
return p_b.solution == solution && p_b.configuration == configuration;
|
||||
}
|
||||
|
||||
String MonoBuildInfo::get_log_dirpath() {
|
||||
|
||||
return GodotSharpDirs::get_build_logs_dir().plus_file(solution.md5_text() + "_" + configuration);
|
||||
}
|
||||
|
||||
MonoBuildInfo::MonoBuildInfo() {}
|
||||
|
||||
MonoBuildInfo::MonoBuildInfo(const String &p_solution, const String &p_config) {
|
||||
|
||||
solution = p_solution;
|
||||
configuration = p_config;
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* monodevelop_instance.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 "monodevelop_instance.h"
|
||||
|
||||
#include "../mono_gd/gd_mono.h"
|
||||
#include "../mono_gd/gd_mono_class.h"
|
||||
|
||||
void MonoDevelopInstance::execute(const Vector<String> &p_files) {
|
||||
|
||||
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
|
||||
|
||||
ERR_FAIL_NULL(execute_method);
|
||||
ERR_FAIL_COND(gc_handle.is_null());
|
||||
|
||||
MonoException *exc = NULL;
|
||||
|
||||
Variant files = p_files;
|
||||
const Variant *args[1] = { &files };
|
||||
execute_method->invoke(gc_handle->get_target(), args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
void MonoDevelopInstance::execute(const String &p_file) {
|
||||
|
||||
Vector<String> files;
|
||||
files.push_back(p_file);
|
||||
execute(files);
|
||||
}
|
||||
|
||||
MonoDevelopInstance::MonoDevelopInstance(const String &p_solution, EditorId p_editor_id) {
|
||||
|
||||
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
|
||||
|
||||
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Editor", "MonoDevelopInstance");
|
||||
|
||||
MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr());
|
||||
|
||||
GDMonoMethod *ctor = klass->get_method(".ctor", 2);
|
||||
MonoException *exc = NULL;
|
||||
|
||||
Variant solution = p_solution;
|
||||
Variant editor_id = p_editor_id;
|
||||
const Variant *args[2] = { &solution, &editor_id };
|
||||
ctor->invoke(obj, args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
gc_handle = MonoGCHandle::create_strong(obj);
|
||||
execute_method = klass->get_method("Execute", 1);
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* monodevelop_instance.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 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 MONODEVELOP_INSTANCE_H
|
||||
#define MONODEVELOP_INSTANCE_H
|
||||
|
||||
#include "core/reference.h"
|
||||
|
||||
#include "../mono_gc_handle.h"
|
||||
#include "../mono_gd/gd_mono_method.h"
|
||||
|
||||
class MonoDevelopInstance {
|
||||
|
||||
Ref<MonoGCHandle> gc_handle;
|
||||
GDMonoMethod *execute_method;
|
||||
|
||||
public:
|
||||
enum EditorId {
|
||||
MONODEVELOP = 0,
|
||||
VISUALSTUDIO_FOR_MAC = 1
|
||||
};
|
||||
|
||||
void execute(const Vector<String> &p_files);
|
||||
void execute(const String &p_file);
|
||||
|
||||
MonoDevelopInstance(const String &p_solution, EditorId p_editor_id);
|
||||
};
|
||||
|
||||
#endif // MONODEVELOP_INSTANCE_H
|
2
modules/mono/glue/Managed/.gitignore
vendored
Normal file
2
modules/mono/glue/Managed/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Generated Godot API solution folder
|
||||
Generated
|
|
@ -2,27 +2,27 @@ using System;
|
|||
|
||||
namespace Godot
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class RemoteAttribute : Attribute {}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class SyncAttribute : Attribute {}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class MasterAttribute : Attribute {}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class PuppetAttribute : Attribute {}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class SlaveAttribute : Attribute {}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class RemoteSyncAttribute : Attribute {}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class MasterSyncAttribute : Attribute {}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class PuppetSyncAttribute : Attribute {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
namespace Godot
|
||||
{
|
||||
public interface ISerializationListener
|
||||
{
|
||||
void OnBeforeSerialize();
|
||||
void OnAfterDeserialize();
|
||||
}
|
||||
}
|
|
@ -299,14 +299,14 @@ namespace Godot
|
|||
if (basepos != -1)
|
||||
{
|
||||
var end = basepos + 3;
|
||||
rs = instance.Substring(end, instance.Length);
|
||||
rs = instance.Substring(end);
|
||||
@base = instance.Substring(0, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (instance.BeginsWith("/"))
|
||||
{
|
||||
rs = instance.Substring(1, instance.Length);
|
||||
rs = instance.Substring(1);
|
||||
@base = "/";
|
||||
}
|
||||
else
|
||||
|
@ -333,7 +333,7 @@ namespace Godot
|
|||
if (sep == -1)
|
||||
return instance;
|
||||
|
||||
return instance.Substring(sep + 1, instance.Length);
|
||||
return instance.Substring(sep + 1);
|
||||
}
|
||||
|
||||
// <summary>
|
||||
|
@ -911,7 +911,8 @@ namespace Godot
|
|||
// </summary>
|
||||
public static string Substr(this string instance, int from, int len)
|
||||
{
|
||||
return instance.Substring(from, len);
|
||||
int max = instance.Length - from;
|
||||
return instance.Substring(from, len > max ? max : len);
|
||||
}
|
||||
|
||||
// <summary>
|
||||
|
|
|
@ -68,12 +68,12 @@ MonoString *godot_icall_String_sha256_text(MonoString *p_str) {
|
|||
}
|
||||
|
||||
void godot_register_string_icalls() {
|
||||
mono_add_internal_call("Godot.String::godot_icall_String_md5_buffer", (void *)godot_icall_String_md5_buffer);
|
||||
mono_add_internal_call("Godot.String::godot_icall_String_md5_text", (void *)godot_icall_String_md5_text);
|
||||
mono_add_internal_call("Godot.String::godot_icall_String_rfind", (void *)godot_icall_String_rfind);
|
||||
mono_add_internal_call("Godot.String::godot_icall_String_rfindn", (void *)godot_icall_String_rfindn);
|
||||
mono_add_internal_call("Godot.String::godot_icall_String_sha256_buffer", (void *)godot_icall_String_sha256_buffer);
|
||||
mono_add_internal_call("Godot.String::godot_icall_String_sha256_text", (void *)godot_icall_String_sha256_text);
|
||||
mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", (void *)godot_icall_String_md5_buffer);
|
||||
mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", (void *)godot_icall_String_md5_text);
|
||||
mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", (void *)godot_icall_String_rfind);
|
||||
mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", (void *)godot_icall_String_rfindn);
|
||||
mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", (void *)godot_icall_String_sha256_buffer);
|
||||
mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", (void *)godot_icall_String_sha256_text);
|
||||
}
|
||||
|
||||
#endif // MONO_GLUE_ENABLED
|
||||
|
|
|
@ -39,7 +39,8 @@
|
|||
#define API_SOLUTION_NAME "GodotSharp"
|
||||
#define CORE_API_ASSEMBLY_NAME "GodotSharp"
|
||||
#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
|
||||
#define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools"
|
||||
#define TOOLS_ASSEMBLY_NAME "GodotTools"
|
||||
#define TOOLS_PROJECT_EDITOR_ASSEMBLY_NAME "GodotTools.ProjectEditor"
|
||||
|
||||
#define BINDINGS_CLASS_NATIVECALLS "NativeCalls"
|
||||
#define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
|
||||
|
|
|
@ -59,6 +59,20 @@ String _get_expected_build_config() {
|
|||
#endif
|
||||
}
|
||||
|
||||
String _get_expected_api_build_config() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return "Debug";
|
||||
#else
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
return "Debug";
|
||||
#else
|
||||
return "Release";
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
String _get_mono_user_dir() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (EditorSettings::get_singleton()) {
|
||||
|
@ -88,6 +102,7 @@ class _GodotSharpDirs {
|
|||
public:
|
||||
String res_data_dir;
|
||||
String res_metadata_dir;
|
||||
String res_assemblies_base_dir;
|
||||
String res_assemblies_dir;
|
||||
String res_config_dir;
|
||||
String res_temp_dir;
|
||||
|
@ -118,7 +133,8 @@ private:
|
|||
_GodotSharpDirs() {
|
||||
res_data_dir = "res://.mono";
|
||||
res_metadata_dir = res_data_dir.plus_file("metadata");
|
||||
res_assemblies_dir = res_data_dir.plus_file("assemblies");
|
||||
res_assemblies_base_dir = res_data_dir.plus_file("assemblies");
|
||||
res_assemblies_dir = res_assemblies_base_dir.plus_file(_get_expected_api_build_config());
|
||||
res_config_dir = res_data_dir.plus_file("etc").plus_file("mono");
|
||||
|
||||
// TODO use paths from csproj
|
||||
|
@ -231,6 +247,10 @@ String get_res_metadata_dir() {
|
|||
return _GodotSharpDirs::get_singleton().res_metadata_dir;
|
||||
}
|
||||
|
||||
String get_res_assemblies_base_dir() {
|
||||
return _GodotSharpDirs::get_singleton().res_assemblies_base_dir;
|
||||
}
|
||||
|
||||
String get_res_assemblies_dir() {
|
||||
return _GodotSharpDirs::get_singleton().res_assemblies_dir;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ namespace GodotSharpDirs {
|
|||
|
||||
String get_res_data_dir();
|
||||
String get_res_metadata_dir();
|
||||
String get_res_assemblies_base_dir();
|
||||
String get_res_assemblies_dir();
|
||||
String get_res_config_dir();
|
||||
String get_res_temp_dir();
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#include "gd_mono_utils.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "../editor/godotsharp_editor.h"
|
||||
#include "main/main.h"
|
||||
#endif
|
||||
|
||||
|
@ -99,7 +98,7 @@ void gdmono_profiler_init() {
|
|||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
|
||||
bool _wait_for_debugger_msecs(uint32_t p_msecs) {
|
||||
|
||||
do {
|
||||
if (mono_is_debugger_attached())
|
||||
|
@ -129,16 +128,17 @@ void gdmono_debug_init() {
|
|||
bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
|
||||
int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
|
||||
|
||||
CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint() ||
|
||||
ProjectSettings::get_singleton()->get_resource_path().empty() ||
|
||||
Main::is_project_manager()) {
|
||||
return;
|
||||
if (da_args.size() == 0)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
|
||||
|
||||
if (da_args.length() == 0) {
|
||||
da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
|
||||
",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
|
||||
|
@ -207,6 +207,10 @@ void GDMono::initialize() {
|
|||
|
||||
print_verbose("Mono: Initializing module...");
|
||||
|
||||
char *runtime_build_info = mono_get_runtime_build_info();
|
||||
print_verbose("Mono JIT compiler version " + String(runtime_build_info));
|
||||
mono_free(runtime_build_info);
|
||||
|
||||
#ifdef DEBUG_METHODS_ENABLED
|
||||
_initialize_and_check_api_hashes();
|
||||
#endif
|
||||
|
@ -339,18 +343,6 @@ void GDMono::initialize() {
|
|||
ERR_EXPLAIN("Mono: Failed to load mscorlib assembly");
|
||||
ERR_FAIL_COND(!_load_corlib_assembly());
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// The tools domain must be loaded here, before the scripts domain.
|
||||
// Otherwise domain unload on the scripts domain will hang indefinitely.
|
||||
|
||||
ERR_EXPLAIN("Mono: Failed to load tools domain");
|
||||
ERR_FAIL_COND(_load_tools_domain() != OK);
|
||||
|
||||
// TODO move to editor init callback, and do it lazily when required before editor init (e.g.: bindings generation)
|
||||
ERR_EXPLAIN("Mono: Failed to load Editor Tools assembly");
|
||||
ERR_FAIL_COND(!_load_editor_tools_assembly());
|
||||
#endif
|
||||
|
||||
ERR_EXPLAIN("Mono: Failed to load scripts domain");
|
||||
ERR_FAIL_COND(_load_scripts_domain() != OK);
|
||||
|
||||
|
@ -365,8 +357,15 @@ void GDMono::initialize() {
|
|||
// The following assemblies are not required at initialization
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
if (_load_api_assemblies()) {
|
||||
// Everything is fine with the api assemblies, load the project assembly
|
||||
// Everything is fine with the api assemblies, load the tools and project assemblies
|
||||
|
||||
#if defined(TOOLS_ENABLED)
|
||||
ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
|
||||
ERR_FAIL_COND(!_load_tools_assemblies());
|
||||
#endif
|
||||
|
||||
_load_project_assembly();
|
||||
|
||||
} else {
|
||||
if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated))
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
@ -427,10 +426,6 @@ void GDMono::_register_internal_calls() {
|
|||
#ifdef MONO_GLUE_ENABLED
|
||||
GodotSharpBindings::register_generated_icalls();
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
GodotSharpEditor::register_internal_calls();
|
||||
#endif
|
||||
}
|
||||
|
||||
void GDMono::_initialize_and_check_api_hashes() {
|
||||
|
@ -569,6 +564,50 @@ bool GDMono::_load_corlib_assembly() {
|
|||
return success;
|
||||
}
|
||||
|
||||
static bool 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) {
|
||||
ERR_PRINTS("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);
|
||||
|
||||
if (!FileAccess::exists(assembly_dst) ||
|
||||
FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) ||
|
||||
GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) {
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
|
||||
String xml_file = p_assembly_name + ".xml";
|
||||
if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK)
|
||||
WARN_PRINTS("Failed to copy " + xml_file);
|
||||
|
||||
String pdb_file = p_assembly_name + ".pdb";
|
||||
if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK)
|
||||
WARN_PRINTS("Failed to copy " + pdb_file);
|
||||
|
||||
Error err = da->copy(assembly_src, assembly_dst);
|
||||
|
||||
if (err != OK) {
|
||||
ERR_PRINTS("Failed to copy " + assembly_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GDMono::_load_core_api_assembly() {
|
||||
|
||||
if (core_api_assembly)
|
||||
|
@ -576,19 +615,31 @@ bool GDMono::_load_core_api_assembly() {
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) {
|
||||
print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated");
|
||||
return false;
|
||||
String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
|
||||
String prebuilt_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
|
||||
String invalidated_dll_path = get_invalidated_api_assembly_path(APIAssembly::API_CORE);
|
||||
|
||||
if (!FileAccess::exists(prebuilt_dll_path) ||
|
||||
FileAccess::get_modified_time(invalidated_dll_path) == FileAccess::get_modified_time(prebuilt_dll_path)) {
|
||||
print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated");
|
||||
return false;
|
||||
} else {
|
||||
// Copy the prebuilt Api
|
||||
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
|
||||
if (!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, CORE_API_ASSEMBLY_NAME, APIAssembly::API_CORE) ||
|
||||
!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) {
|
||||
print_verbose("Mono: Failed to copy prebuilt API. Skipping loading of Core API assembly because it was invalidated");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
|
||||
|
||||
if (!FileAccess::exists(assembly_path))
|
||||
return false;
|
||||
|
||||
bool success = load_assembly_from(CORE_API_ASSEMBLY_NAME,
|
||||
assembly_path,
|
||||
&core_api_assembly);
|
||||
bool success = (FileAccess::exists(assembly_path) &&
|
||||
load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly)) ||
|
||||
load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly);
|
||||
|
||||
if (success) {
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
|
@ -616,18 +667,29 @@ bool GDMono::_load_editor_api_assembly() {
|
|||
return true;
|
||||
|
||||
if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
|
||||
print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated");
|
||||
return false;
|
||||
String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
|
||||
String prebuilt_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
String invalidated_dll_path = get_invalidated_api_assembly_path(APIAssembly::API_EDITOR);
|
||||
|
||||
if (!FileAccess::exists(prebuilt_dll_path) ||
|
||||
FileAccess::get_modified_time(invalidated_dll_path) == FileAccess::get_modified_time(prebuilt_dll_path)) {
|
||||
print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated");
|
||||
return false;
|
||||
} else {
|
||||
// Copy the prebuilt editor Api (no need to copy the core api if we got to this point)
|
||||
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
|
||||
if (!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) {
|
||||
print_verbose("Mono: Failed to copy prebuilt API. Skipping loading of Editor API assembly because it was invalidated");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
|
||||
if (!FileAccess::exists(assembly_path))
|
||||
return false;
|
||||
|
||||
bool success = load_assembly_from(EDITOR_API_ASSEMBLY_NAME,
|
||||
assembly_path,
|
||||
&editor_api_assembly);
|
||||
bool success = (FileAccess::exists(assembly_path) &&
|
||||
load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly)) ||
|
||||
load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
|
||||
|
||||
if (success) {
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
|
@ -643,14 +705,15 @@ bool GDMono::_load_editor_api_assembly() {
|
|||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool GDMono::_load_editor_tools_assembly() {
|
||||
bool GDMono::_load_tools_assemblies() {
|
||||
|
||||
if (editor_tools_assembly)
|
||||
if (tools_assembly && tools_project_editor_assembly)
|
||||
return true;
|
||||
|
||||
_GDMONO_SCOPE_DOMAIN_(tools_domain)
|
||||
bool success = load_assembly(TOOLS_ASSEMBLY_NAME, &tools_assembly) &&
|
||||
load_assembly(TOOLS_PROJECT_EDITOR_ASSEMBLY_NAME, &tools_project_editor_assembly);
|
||||
|
||||
return load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly);
|
||||
return success;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -781,6 +844,14 @@ bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type)
|
|||
|
||||
return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time;
|
||||
}
|
||||
|
||||
String GDMono::get_invalidated_api_assembly_path(APIAssembly::Type p_api_type) {
|
||||
|
||||
return GodotSharpDirs::get_res_assemblies_dir()
|
||||
.plus_file(p_api_type == APIAssembly::API_CORE ?
|
||||
CORE_API_ASSEMBLY_NAME ".dll" :
|
||||
EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
}
|
||||
#endif
|
||||
|
||||
Error GDMono::_load_scripts_domain() {
|
||||
|
@ -826,6 +897,8 @@ Error GDMono::_unload_scripts_domain() {
|
|||
project_assembly = NULL;
|
||||
#ifdef TOOLS_ENABLED
|
||||
editor_api_assembly = NULL;
|
||||
tools_assembly = NULL;
|
||||
tools_project_editor_assembly = NULL;
|
||||
#endif
|
||||
|
||||
core_api_assembly_out_of_sync = false;
|
||||
|
@ -848,22 +921,6 @@ Error GDMono::_unload_scripts_domain() {
|
|||
return OK;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Error GDMono::_load_tools_domain() {
|
||||
|
||||
ERR_FAIL_COND_V(tools_domain != NULL, ERR_BUG);
|
||||
|
||||
print_verbose("Mono: Loading tools domain...");
|
||||
|
||||
tools_domain = GDMonoUtils::create_domain("GodotEngine.ToolsDomain");
|
||||
|
||||
ERR_EXPLAIN("Mono: Could not create tools app domain");
|
||||
ERR_FAIL_NULL_V(tools_domain, ERR_CANT_CREATE);
|
||||
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
Error GDMono::reload_scripts_domain() {
|
||||
|
||||
|
@ -925,6 +982,11 @@ Error GDMono::reload_scripts_domain() {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
|
||||
ERR_FAIL_COND_V(!_load_tools_assemblies(), ERR_CANT_OPEN);
|
||||
#endif
|
||||
|
||||
if (!_load_project_assembly()) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
@ -939,7 +1001,7 @@ Error GDMono::reload_scripts_domain() {
|
|||
Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
|
||||
|
||||
CRASH_COND(p_domain == NULL);
|
||||
CRASH_COND(p_domain == SCRIPTS_DOMAIN); // Should use _unload_scripts_domain() instead
|
||||
CRASH_COND(p_domain == GDMono::get_singleton()->get_scripts_domain()); // Should use _unload_scripts_domain() instead
|
||||
|
||||
String domain_name = mono_domain_get_friendly_name(p_domain);
|
||||
|
||||
|
@ -956,18 +1018,12 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
|
|||
|
||||
_domain_assemblies_cleanup(mono_domain_get_id(p_domain));
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (p_domain == tools_domain) {
|
||||
editor_tools_assembly = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
MonoException *exc = NULL;
|
||||
mono_domain_try_unload(p_domain, (MonoObject **)&exc);
|
||||
|
||||
if (exc) {
|
||||
ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`");
|
||||
GDMonoUtils::debug_unhandled_exception(exc);
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
|
@ -998,6 +1054,22 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &p_name) {
|
||||
|
||||
uint32_t domain_id = mono_domain_get_id(mono_domain_get());
|
||||
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
|
||||
|
||||
const String *k = NULL;
|
||||
while ((k = domain_assemblies.next(k))) {
|
||||
GDMonoAssembly *assembly = domain_assemblies.get(*k);
|
||||
GDMonoClass *klass = assembly->get_class(p_namespace, p_name);
|
||||
if (klass)
|
||||
return klass;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
|
||||
|
||||
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
|
||||
|
@ -1038,9 +1110,6 @@ GDMono::GDMono() {
|
|||
|
||||
root_domain = NULL;
|
||||
scripts_domain = NULL;
|
||||
#ifdef TOOLS_ENABLED
|
||||
tools_domain = NULL;
|
||||
#endif
|
||||
|
||||
core_api_assembly_out_of_sync = false;
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
@ -1052,7 +1121,8 @@ GDMono::GDMono() {
|
|||
project_assembly = NULL;
|
||||
#ifdef TOOLS_ENABLED
|
||||
editor_api_assembly = NULL;
|
||||
editor_tools_assembly = NULL;
|
||||
tools_assembly = NULL;
|
||||
tools_project_editor_assembly = NULL;
|
||||
#endif
|
||||
|
||||
api_core_hash = 0;
|
||||
|
@ -1064,16 +1134,6 @@ GDMono::GDMono() {
|
|||
GDMono::~GDMono() {
|
||||
|
||||
if (is_runtime_initialized()) {
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (tools_domain) {
|
||||
Error err = finalize_and_unload_domain(tools_domain);
|
||||
if (err != OK) {
|
||||
ERR_PRINT("Mono: Failed to unload tools domain");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (scripts_domain) {
|
||||
Error err = _unload_scripts_domain();
|
||||
if (err != OK) {
|
||||
|
@ -1128,14 +1188,14 @@ int32_t _GodotSharp::get_domain_id() {
|
|||
|
||||
int32_t _GodotSharp::get_scripts_domain_id() {
|
||||
|
||||
MonoDomain *domain = SCRIPTS_DOMAIN;
|
||||
MonoDomain *domain = GDMono::get_singleton()->get_scripts_domain();
|
||||
CRASH_COND(!domain); // User must check if scripts domain is loaded before calling this method
|
||||
return mono_domain_get_id(domain);
|
||||
}
|
||||
|
||||
bool _GodotSharp::is_scripts_domain_loaded() {
|
||||
|
||||
return GDMono::get_singleton()->is_runtime_initialized() && SCRIPTS_DOMAIN != NULL;
|
||||
return GDMono::get_singleton()->is_runtime_initialized() && GDMono::get_singleton()->get_scripts_domain() != NULL;
|
||||
}
|
||||
|
||||
bool _GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) {
|
||||
|
@ -1157,7 +1217,7 @@ bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) {
|
|||
|
||||
if (!p_domain)
|
||||
return true;
|
||||
if (p_domain == SCRIPTS_DOMAIN && GDMono::get_singleton()->is_finalizing_scripts_domain())
|
||||
if (p_domain == GDMono::get_singleton()->get_scripts_domain() && GDMono::get_singleton()->is_finalizing_scripts_domain())
|
||||
return true;
|
||||
return mono_domain_is_unloading(p_domain);
|
||||
}
|
||||
|
@ -1172,6 +1232,12 @@ bool _GodotSharp::is_runtime_initialized() {
|
|||
return GDMono::get_singleton()->is_runtime_initialized();
|
||||
}
|
||||
|
||||
void _GodotSharp::_reload_assemblies(bool p_soft_reload) {
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload);
|
||||
#endif
|
||||
}
|
||||
|
||||
void _GodotSharp::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread);
|
||||
|
@ -1184,6 +1250,7 @@ void _GodotSharp::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &_GodotSharp::is_runtime_shutting_down);
|
||||
ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &_GodotSharp::is_runtime_initialized);
|
||||
ClassDB::bind_method(D_METHOD("_reload_assemblies"), &_GodotSharp::_reload_assemblies);
|
||||
}
|
||||
|
||||
_GodotSharp::_GodotSharp() {
|
||||
|
|
|
@ -78,11 +78,6 @@ struct Version {
|
|||
String to_string(Type p_type);
|
||||
} // namespace APIAssembly
|
||||
|
||||
#define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain()
|
||||
#ifdef TOOLS_ENABLED
|
||||
#define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain()
|
||||
#endif
|
||||
|
||||
class GDMono {
|
||||
|
||||
bool runtime_initialized;
|
||||
|
@ -90,9 +85,6 @@ class GDMono {
|
|||
|
||||
MonoDomain *root_domain;
|
||||
MonoDomain *scripts_domain;
|
||||
#ifdef TOOLS_ENABLED
|
||||
MonoDomain *tools_domain;
|
||||
#endif
|
||||
|
||||
bool core_api_assembly_out_of_sync;
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
@ -104,7 +96,8 @@ class GDMono {
|
|||
GDMonoAssembly *project_assembly;
|
||||
#ifdef TOOLS_ENABLED
|
||||
GDMonoAssembly *editor_api_assembly;
|
||||
GDMonoAssembly *editor_tools_assembly;
|
||||
GDMonoAssembly *tools_assembly;
|
||||
GDMonoAssembly *tools_project_editor_assembly;
|
||||
#endif
|
||||
|
||||
HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
|
||||
|
@ -115,7 +108,7 @@ class GDMono {
|
|||
bool _load_core_api_assembly();
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool _load_editor_api_assembly();
|
||||
bool _load_editor_tools_assembly();
|
||||
bool _load_tools_assemblies();
|
||||
#endif
|
||||
bool _load_project_assembly();
|
||||
|
||||
|
@ -132,10 +125,6 @@ class GDMono {
|
|||
Error _load_scripts_domain();
|
||||
Error _unload_scripts_domain();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Error _load_tools_domain();
|
||||
#endif
|
||||
|
||||
uint64_t api_core_hash;
|
||||
#ifdef TOOLS_ENABLED
|
||||
uint64_t api_editor_hash;
|
||||
|
@ -170,6 +159,7 @@ public:
|
|||
#ifdef TOOLS_ENABLED
|
||||
void metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated);
|
||||
bool metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type);
|
||||
String get_invalidated_api_assembly_path(APIAssembly::Type p_api_type);
|
||||
#endif
|
||||
|
||||
static GDMono *get_singleton() { return singleton; }
|
||||
|
@ -185,16 +175,14 @@ public:
|
|||
_FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; }
|
||||
|
||||
_FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }
|
||||
#ifdef TOOLS_ENABLED
|
||||
_FORCE_INLINE_ MonoDomain *get_tools_domain() { return tools_domain; }
|
||||
#endif
|
||||
|
||||
_FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
|
||||
_FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; }
|
||||
_FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
|
||||
#ifdef TOOLS_ENABLED
|
||||
_FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; }
|
||||
_FORCE_INLINE_ GDMonoAssembly *get_editor_tools_assembly() const { return editor_tools_assembly; }
|
||||
_FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; }
|
||||
_FORCE_INLINE_ GDMonoAssembly *get_tools_project_editor_assembly() const { return tools_project_editor_assembly; }
|
||||
#endif
|
||||
|
||||
#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
|
||||
|
@ -202,6 +190,7 @@ public:
|
|||
#endif
|
||||
|
||||
GDMonoClass *get_class(MonoClass *p_raw_class);
|
||||
GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
|
||||
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
Error reload_scripts_domain();
|
||||
|
@ -276,6 +265,8 @@ class _GodotSharp : public Object {
|
|||
List<NodePath *> np_delete_queue;
|
||||
List<RID *> rid_delete_queue;
|
||||
|
||||
void _reload_assemblies(bool p_soft_reload);
|
||||
|
||||
protected:
|
||||
static _GodotSharp *singleton;
|
||||
static void _bind_methods();
|
||||
|
|
|
@ -46,6 +46,20 @@ bool GDMonoAssembly::in_preload = false;
|
|||
|
||||
Vector<String> GDMonoAssembly::search_dirs;
|
||||
|
||||
static String _get_expected_api_build_config() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return "Debug";
|
||||
#else
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
return "Debug";
|
||||
#else
|
||||
return "Release";
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) {
|
||||
|
||||
String framework_dir;
|
||||
|
@ -67,11 +81,19 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin
|
|||
r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
|
||||
}
|
||||
|
||||
String api_config = p_custom_config.empty() ? _get_expected_api_build_config() :
|
||||
(p_custom_config == "Release" ? "Release" : "Debug");
|
||||
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config));
|
||||
|
||||
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());
|
||||
|
||||
// For GodotTools to find the api assemblies
|
||||
r_search_dirs.push_back(GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
|
|||
|
||||
MonoException *exc = NULL;
|
||||
MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
return GDMonoMarshal::mono_string_to_godot(str);
|
||||
}
|
||||
|
@ -74,16 +74,13 @@ bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
|
|||
}
|
||||
|
||||
GDMonoClass *GDMonoClass::get_parent_class() {
|
||||
MonoClass *parent_mono_class = mono_class_get_parent(mono_class);
|
||||
return parent_mono_class ? GDMono::get_singleton()->get_class(parent_mono_class) : NULL;
|
||||
}
|
||||
|
||||
if (assembly) {
|
||||
MonoClass *parent_mono_class = mono_class_get_parent(mono_class);
|
||||
|
||||
if (parent_mono_class) {
|
||||
return GDMono::get_singleton()->get_class(parent_mono_class);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
GDMonoClass *GDMonoClass::get_nesting_class() {
|
||||
MonoClass *nesting_type = mono_class_get_nesting_type(mono_class);
|
||||
return nesting_type ? GDMono::get_singleton()->get_class(nesting_type) : NULL;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
|
|
@ -121,6 +121,7 @@ public:
|
|||
_FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
|
||||
|
||||
GDMonoClass *get_parent_class();
|
||||
GDMonoClass *get_nesting_class();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Vector<MonoClassField *> get_enum_fields();
|
||||
|
|
|
@ -315,7 +315,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
|||
|
||||
// The order in which we check the following interfaces is very important (dictionaries and generics first)
|
||||
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
|
||||
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type());
|
||||
|
||||
MonoReflectionType *key_reftype, *value_reftype;
|
||||
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
|
||||
|
@ -340,9 +340,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
|||
}
|
||||
|
||||
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
if (GDMonoUtils::tools_godot_api_check()) {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
} else {
|
||||
MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array());
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
|
||||
|
@ -450,7 +456,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
|||
} break;
|
||||
|
||||
case MONO_TYPE_GENERICINST: {
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
|
||||
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type());
|
||||
|
||||
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
|
||||
|
@ -489,9 +495,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
|||
}
|
||||
|
||||
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
if (GDMonoUtils::tools_godot_api_check()) {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
} else {
|
||||
MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array());
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
|
|
|
@ -74,15 +74,14 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
|
|||
script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass);
|
||||
script_binding.wrapper_class = klass;
|
||||
script_binding.gchandle = MonoGCHandle::create_strong(managed);
|
||||
script_binding.owner = unmanaged;
|
||||
|
||||
Reference *kref = Object::cast_to<Reference>(unmanaged);
|
||||
if (kref) {
|
||||
if (ref) {
|
||||
// Unsafe refcount increment. The managed instance also counts as a reference.
|
||||
// This way if the unmanaged world has no references to our owner
|
||||
// but the managed instance is alive, the refcount will be 1 instead of 0.
|
||||
// See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)
|
||||
|
||||
kref->reference();
|
||||
ref->reference();
|
||||
}
|
||||
|
||||
// The object was just created, no script instance binding should have been attached
|
||||
|
|
|
@ -159,7 +159,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
|
|||
|
||||
// The order in which we check the following interfaces is very important (dictionaries and generics first)
|
||||
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
|
||||
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type());
|
||||
|
||||
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
|
||||
return Variant::DICTIONARY;
|
||||
|
@ -179,7 +179,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
|
|||
} break;
|
||||
|
||||
case MONO_TYPE_GENERICINST: {
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
|
||||
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
|
||||
|
||||
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
|
||||
return Variant::DICTIONARY;
|
||||
|
@ -217,7 +217,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
|
|||
bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) {
|
||||
switch (p_array_type.type_encoding) {
|
||||
case MONO_TYPE_GENERICINST: {
|
||||
MonoReflectionType *array_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_array_type.type_class->get_mono_type());
|
||||
MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type());
|
||||
|
||||
if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) {
|
||||
MonoReflectionType *elem_reftype;
|
||||
|
@ -244,7 +244,7 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_
|
|||
bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type) {
|
||||
switch (p_dictionary_type.type_encoding) {
|
||||
case MONO_TYPE_GENERICINST: {
|
||||
MonoReflectionType *dict_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_dictionary_type.type_class->get_mono_type());
|
||||
MonoReflectionType *dict_reftype = mono_type_get_object(mono_domain_get(), p_dictionary_type.type_class->get_mono_type());
|
||||
|
||||
if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) {
|
||||
MonoReflectionType *key_reftype;
|
||||
|
@ -539,7 +539,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
|||
|
||||
// The order in which we check the following interfaces is very important (dictionaries and generics first)
|
||||
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
|
||||
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type());
|
||||
|
||||
MonoReflectionType *key_reftype, *value_reftype;
|
||||
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
|
||||
|
@ -558,7 +558,11 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
|||
}
|
||||
|
||||
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
|
||||
if (GDMonoUtils::tools_godot_api_check()) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
|
||||
} else {
|
||||
return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array());
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case MONO_TYPE_OBJECT: {
|
||||
|
@ -652,7 +656,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
|||
}
|
||||
break;
|
||||
case MONO_TYPE_GENERICINST: {
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
|
||||
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
|
||||
|
||||
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
|
||||
|
@ -681,7 +685,11 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
|||
}
|
||||
|
||||
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
|
||||
if (GDMonoUtils::tools_godot_api_check()) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
|
||||
} else {
|
||||
return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array());
|
||||
}
|
||||
}
|
||||
} break;
|
||||
} break;
|
||||
|
@ -831,20 +839,20 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
|
|||
if (CACHED_CLASS(Array) == type_class) {
|
||||
MonoException *exc = NULL;
|
||||
Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return ptr ? Variant(*ptr) : Variant();
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(Dictionary) == type_class) {
|
||||
MonoException *exc = NULL;
|
||||
Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return ptr ? Variant(*ptr) : Variant();
|
||||
}
|
||||
|
||||
// The order in which we check the following interfaces is very important (dictionaries and generics first)
|
||||
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
|
||||
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type());
|
||||
|
||||
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
|
||||
return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
|
||||
|
@ -864,19 +872,19 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
|
|||
} break;
|
||||
|
||||
case MONO_TYPE_GENERICINST: {
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
|
||||
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type());
|
||||
|
||||
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return *unbox<Dictionary *>(ret);
|
||||
}
|
||||
|
||||
if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return *unbox<Array *>(ret);
|
||||
}
|
||||
|
||||
|
|
|
@ -125,6 +125,7 @@ void MonoCache::clear_godot_api_cache() {
|
|||
class_Array = NULL;
|
||||
class_Dictionary = NULL;
|
||||
class_MarshalUtils = NULL;
|
||||
class_ISerializationListener = NULL;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
class_DebuggingUtils = NULL;
|
||||
|
@ -242,6 +243,7 @@ void update_godot_api_cache() {
|
|||
CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array));
|
||||
CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary));
|
||||
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
|
||||
CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener));
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils));
|
||||
|
@ -302,7 +304,7 @@ void update_godot_api_cache() {
|
|||
#endif
|
||||
|
||||
// TODO Move to CSharpLanguage::init() and do handle disposal
|
||||
MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
|
||||
MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
|
||||
GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler));
|
||||
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
|
||||
|
||||
|
@ -371,7 +373,6 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
|
|||
// This way if the unmanaged world has no references to our owner
|
||||
// but the managed instance is alive, the refcount will be 1 instead of 0.
|
||||
// See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)
|
||||
|
||||
ref->reference();
|
||||
}
|
||||
|
||||
|
@ -384,7 +385,7 @@ void set_main_thread(MonoThread *p_thread) {
|
|||
|
||||
void attach_current_thread() {
|
||||
ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
|
||||
MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN);
|
||||
MonoThread *mono_thread = mono_thread_attach(mono_domain_get());
|
||||
ERR_FAIL_NULL(mono_thread);
|
||||
}
|
||||
|
||||
|
@ -448,17 +449,12 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
|
|||
}
|
||||
|
||||
MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) {
|
||||
String object_type = p_object->get_class_name();
|
||||
|
||||
if (object_type[0] == '_')
|
||||
object_type = object_type.substr(1, object_type.length());
|
||||
|
||||
if (!ClassDB::is_parent_class(object_type, p_native)) {
|
||||
if (!ClassDB::is_parent_class(p_object->get_class_name(), p_native)) {
|
||||
ERR_EXPLAIN("Type inherits from native type '" + p_native + "', so it can't be instanced in object of type: '" + p_object->get_class() + "'");
|
||||
ERR_FAIL_V(NULL);
|
||||
}
|
||||
|
||||
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
|
||||
MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
|
||||
|
@ -470,7 +466,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
|
|||
}
|
||||
|
||||
MonoObject *create_managed_from(const NodePath &p_from) {
|
||||
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(NodePath));
|
||||
MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(NodePath));
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
// Construct
|
||||
|
@ -482,7 +478,7 @@ MonoObject *create_managed_from(const NodePath &p_from) {
|
|||
}
|
||||
|
||||
MonoObject *create_managed_from(const RID &p_from) {
|
||||
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(RID));
|
||||
MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(RID));
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
// Construct
|
||||
|
@ -494,7 +490,7 @@ MonoObject *create_managed_from(const RID &p_from) {
|
|||
}
|
||||
|
||||
MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
|
||||
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
|
||||
MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
// Search constructor that takes a pointer as parameter
|
||||
|
@ -518,13 +514,13 @@ MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
|
|||
|
||||
MonoException *exc = NULL;
|
||||
GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
return mono_object;
|
||||
}
|
||||
|
||||
MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) {
|
||||
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
|
||||
MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
// Search constructor that takes a pointer as parameter
|
||||
|
@ -548,7 +544,7 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class)
|
|||
|
||||
MonoException *exc = NULL;
|
||||
GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
return mono_object;
|
||||
}
|
||||
|
@ -667,7 +663,10 @@ void print_unhandled_exception(MonoException *p_exc) {
|
|||
}
|
||||
|
||||
void set_pending_exception(MonoException *p_exc) {
|
||||
#ifdef HAS_PENDING_EXCEPTIONS
|
||||
#ifdef NO_PENDING_EXCEPTIONS
|
||||
debug_unhandled_exception(p_exc);
|
||||
GD_UNREACHABLE();
|
||||
#else
|
||||
if (get_runtime_invoke_count() == 0) {
|
||||
debug_unhandled_exception(p_exc);
|
||||
GD_UNREACHABLE();
|
||||
|
@ -677,9 +676,6 @@ void set_pending_exception(MonoException *p_exc) {
|
|||
ERR_PRINTS("Exception thrown from managed code, but it could not be set as pending:");
|
||||
GDMonoUtils::debug_print_unhandled_exception(p_exc);
|
||||
}
|
||||
#else
|
||||
debug_unhandled_exception(p_exc);
|
||||
GD_UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -755,113 +751,137 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
|
|||
|
||||
namespace Marshal {
|
||||
|
||||
MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype) {
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
#ifdef TOOLS_ENABLED
|
||||
#define NO_GLUE_RET(m_ret) \
|
||||
{ \
|
||||
if (!mono_cache.godot_api_cache_updated) return m_ret; \
|
||||
}
|
||||
#else
|
||||
#define NO_GLUE_RET(m_ret) \
|
||||
{}
|
||||
#endif
|
||||
#else
|
||||
#define NO_GLUE_RET(m_ret) \
|
||||
{ return m_ret; }
|
||||
#endif
|
||||
|
||||
bool type_is_generic_array(MonoReflectionType *p_reftype) {
|
||||
NO_GLUE_RET(false);
|
||||
TypeIsGenericArray thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
|
||||
MonoException *exc = NULL;
|
||||
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
return res;
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return (bool)res;
|
||||
}
|
||||
|
||||
MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype) {
|
||||
bool type_is_generic_dictionary(MonoReflectionType *p_reftype) {
|
||||
NO_GLUE_RET(false);
|
||||
TypeIsGenericDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
|
||||
MonoException *exc = NULL;
|
||||
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
return res;
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return (bool)res;
|
||||
}
|
||||
|
||||
void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) {
|
||||
ArrayGetElementType thunk = CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType);
|
||||
MonoException *exc = NULL;
|
||||
invoke_method_thunk(thunk, p_array_reftype, r_elem_reftype, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
}
|
||||
|
||||
void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
|
||||
DictionaryGetKeyValueTypes thunk = CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes);
|
||||
MonoException *exc = NULL;
|
||||
invoke_method_thunk(thunk, p_dict_reftype, r_key_reftype, r_value_reftype, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
}
|
||||
|
||||
MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) {
|
||||
bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) {
|
||||
NO_GLUE_RET(false);
|
||||
GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType);
|
||||
MonoException *exc = NULL;
|
||||
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
return res;
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return (bool)res;
|
||||
}
|
||||
|
||||
MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) {
|
||||
bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) {
|
||||
NO_GLUE_RET(false);
|
||||
GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType);
|
||||
MonoException *exc = NULL;
|
||||
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
return res;
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return (bool)res;
|
||||
}
|
||||
|
||||
MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
|
||||
bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
|
||||
NO_GLUE_RET(false);
|
||||
GenericIEnumerableIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info);
|
||||
MonoException *exc = NULL;
|
||||
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
return res;
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return (bool)res;
|
||||
}
|
||||
|
||||
MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
|
||||
bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
|
||||
NO_GLUE_RET(false);
|
||||
GenericIDictionaryIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info);
|
||||
MonoException *exc = NULL;
|
||||
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
return res;
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return (bool)res;
|
||||
}
|
||||
|
||||
Array enumerable_to_array(MonoObject *p_enumerable) {
|
||||
NO_GLUE_RET(Array());
|
||||
Array result;
|
||||
EnumerableToArray thunk = CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray);
|
||||
MonoException *exc = NULL;
|
||||
invoke_method_thunk(thunk, p_enumerable, &result, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return result;
|
||||
}
|
||||
|
||||
Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) {
|
||||
NO_GLUE_RET(Dictionary());
|
||||
Dictionary result;
|
||||
IDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary);
|
||||
MonoException *exc = NULL;
|
||||
invoke_method_thunk(thunk, p_idictionary, &result, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return result;
|
||||
}
|
||||
|
||||
Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) {
|
||||
NO_GLUE_RET(Dictionary());
|
||||
Dictionary result;
|
||||
GenericIDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary);
|
||||
MonoException *exc = NULL;
|
||||
invoke_method_thunk(thunk, p_generic_idictionary, &result, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return result;
|
||||
}
|
||||
|
||||
GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) {
|
||||
NO_GLUE_RET(NULL);
|
||||
MakeGenericArrayType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType);
|
||||
MonoException *exc = NULL;
|
||||
MonoReflectionType *reftype = invoke_method_thunk(thunk, p_elem_reftype, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
|
||||
}
|
||||
|
||||
GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
|
||||
NO_GLUE_RET(NULL);
|
||||
MakeGenericDictionaryType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType);
|
||||
MonoException *exc = NULL;
|
||||
MonoReflectionType *reftype = invoke_method_thunk(thunk, p_key_reftype, p_value_reftype, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
|
||||
}
|
||||
|
||||
} // namespace Marshal
|
||||
|
||||
// namespace Marshal
|
||||
|
||||
} // namespace GDMonoUtils
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
#include "core/object.h"
|
||||
#include "core/reference.h"
|
||||
|
||||
#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \
|
||||
#define UNHANDLED_EXCEPTION(m_exc) \
|
||||
if (unlikely(m_exc != NULL)) { \
|
||||
GDMonoUtils::debug_unhandled_exception(m_exc); \
|
||||
GD_UNREACHABLE(); \
|
||||
|
@ -78,16 +78,16 @@ typedef void (*GenericIDictionaryToDictionary)(MonoObject *, Dictionary *, MonoE
|
|||
|
||||
namespace Marshal {
|
||||
|
||||
MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype);
|
||||
MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype);
|
||||
bool type_is_generic_array(MonoReflectionType *p_reftype);
|
||||
bool type_is_generic_dictionary(MonoReflectionType *p_reftype);
|
||||
|
||||
void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype);
|
||||
void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
|
||||
|
||||
MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype);
|
||||
MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype);
|
||||
MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype);
|
||||
MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
|
||||
bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype);
|
||||
bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype);
|
||||
bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype);
|
||||
bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
|
||||
|
||||
GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype);
|
||||
GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
|
||||
|
@ -157,6 +157,7 @@ struct MonoCache {
|
|||
GDMonoClass *class_Array;
|
||||
GDMonoClass *class_Dictionary;
|
||||
GDMonoClass *class_MarshalUtils;
|
||||
GDMonoClass *class_ISerializationListener;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
GDMonoClass *class_DebuggingUtils;
|
||||
|
@ -235,10 +236,19 @@ void update_godot_api_cache();
|
|||
inline void clear_corlib_cache() {
|
||||
mono_cache.clear_corlib_cache();
|
||||
}
|
||||
|
||||
inline void clear_godot_api_cache() {
|
||||
mono_cache.clear_godot_api_cache();
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool tools_godot_api_check() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return mono_cache.godot_api_cache_updated;
|
||||
#else
|
||||
return true; // Assume it's updated if this was called, otherwise it's a bug
|
||||
#endif
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) {
|
||||
p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2);
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc
|
|||
set_completed(true);
|
||||
|
||||
int signal_argc = p_argcount - 1;
|
||||
MonoArray *signal_args = mono_array_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(MonoObject), signal_argc);
|
||||
MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), signal_argc);
|
||||
|
||||
for (int i = 0; i < signal_argc; i++) {
|
||||
MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]);
|
||||
|
|
Loading…
Reference in a new issue