diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 584b75b026e5..3597e2b818cf 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -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; }; diff --git a/core/reference.cpp b/core/reference.cpp index 7b5145184ad6..1984af9a340d 100644 --- a/core/reference.cpp +++ b/core/reference.cpp @@ -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); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 90d6c3a98343..c2a845653e02 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -220,7 +220,7 @@ EditorSelection *EditorInterface::get_selection() { return EditorNode::get_singleton()->get_editor_selection(); } -EditorSettings *EditorInterface::get_editor_settings() { +Ref EditorInterface::get_editor_settings() { return EditorSettings::get_singleton(); } diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index ec369bbdbb16..75c230adb74a 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -87,7 +87,7 @@ class EditorInterface : public Node { EditorSelection *get_selection(); //EditorImportExport *get_import_export(); - EditorSettings *get_editor_settings(); + Ref get_editor_settings(); EditorResourcePreview *get_resource_previewer(); EditorFileSystem *get_resource_file_system(); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index ecc63b1a8da1..8e8c12ba4400 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -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() { diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 3bddc6d6d426..04041152699c 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -119,7 +119,7 @@ class EditorSpatialGizmo : public SpatialGizmo { void set_spatial_node(Spatial *p_node); Spatial *get_spatial_node() const { return spatial_node; } - EditorSpatialGizmoPlugin *get_plugin() const { return gizmo_plugin; } + Ref get_plugin() const { return gizmo_plugin; } Vector3 get_handle_pos(int p_idx) const; bool intersect_frustum(const Camera *p_camera, const Vector &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); diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 6c3ecee27215..cc60e64a1194 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -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 + +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) -import build_scripts.godotsharptools_build as godotsharptools_build +# Build GodotTools -godotsharptools_build.build(env_mono) +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) diff --git a/modules/mono/__init__.py b/modules/mono/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/modules/mono/build_scripts/api_solution_build.py b/modules/mono/build_scripts/api_solution_build.py new file mode 100644 index 000000000000..1fe00a30289f --- /dev/null +++ b/modules/mono/build_scripts/api_solution_build.py @@ -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) diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py new file mode 100644 index 000000000000..f66ffdb5734b --- /dev/null +++ b/modules/mono/build_scripts/godot_tools_build.py @@ -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) diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index 3b15f722a3b9..9f0eb5889668 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -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_if_exists(src, dst): 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 diff --git a/modules/mono/build_scripts/godotsharptools_build.py b/modules/mono/build_scripts/solution_builder.py similarity index 76% rename from modules/mono/build_scripts/godotsharptools_build.py rename to modules/mono/build_scripts/solution_builder.py index 17f9a990aff6..9f549a10edcb 100644 --- a/modules/mono/build_scripts/godotsharptools_build.py +++ b/modules/mono/build_scripts/solution_builder.py @@ -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): +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]) + + 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 - from shutil import copyfile + 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) - sln_path = os.path.abspath(str(source[0])) - target_path = os.path.abspath(str(target[0])) + # 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 = [ - 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') + msbuild_args = [solution_path, '/p:Configuration=' + build_config] + msbuild_args += ['/p:FrameworkPathOverride=' + framework_path] if framework_path else [] + msbuild_args += extra_msbuild_args - 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') diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index cdec10eaba2a..b5c91a8585a3 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -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 cmdline_args = OS::get_singleton()->get_cmdline_args(); - BindingsGenerator::handle_cmdline_args(cmdline_args); - } + List 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