diff --git a/modules/mono/SCsub b/modules/mono/SCsub new file mode 100644 index 00000000000..0af2056c5cb --- /dev/null +++ b/modules/mono/SCsub @@ -0,0 +1,120 @@ +#!/usr/bin/env python + +Import('env') + + +def make_cs_files_header(src, dst): + with open(dst, 'wb') as header: + header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n') + header.write('#ifndef _CS_FILES_DATA_H\n') + header.write('#define _CS_FILES_DATA_H\n\n') + header.write('#include "map.h"\n') + header.write('#include "ustring.h"\n') + inserted_files = '' + import os + for file in os.listdir(src): + if file.endswith('.cs'): + with open(os.path.join(src, file), 'rb') as f: + buf = f.read() + decomp_size = len(buf) + import zlib + buf = zlib.compress(buf) + name = os.path.splitext(file)[0] + header.write('\nstatic const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n') + header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n') + header.write('static const unsigned char _cs_' + name + '_compressed[] = { ') + for i, buf_idx in enumerate(range(len(buf))): + if i > 0: + header.write(', ') + header.write(str(ord(buf[buf_idx]))) + inserted_files += '\tr_files.insert(\"' + file + '\", ' \ + 'CompressedFile(_cs_' + name + '_compressed_size, ' \ + '_cs_' + name + '_uncompressed_size, ' \ + '_cs_' + name + '_compressed));\n' + header.write(' };\n') + header.write('\nstruct CompressedFile\n' '{\n' + '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' + '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' + '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n' + '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n' + '\nvoid get_compressed_files(Map& r_files)\n' '{\n' + inserted_files + '}\n' + ) + header.write('#endif // _CS_FILES_DATA_H') + + +env.add_source_files(env.modules_sources, '*.cpp') +env.add_source_files(env.modules_sources, 'mono_gd/*.cpp') +env.add_source_files(env.modules_sources, 'utils/*.cpp') + +if env['tools']: + env.add_source_files(env.modules_sources, 'editor/*.cpp') + make_cs_files_header('glue/cs_files', 'glue/cs_compressed.gen.h') + +vars = Variables() +vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True)) +vars.Update(env) + +# Glue sources +if env['mono_glue']: + env.add_source_files(env.modules_sources, 'glue/*.cpp') +else: + env.Append(CPPDEFINES = [ 'MONO_GLUE_DISABLED' ]) + +if ARGUMENTS.get('yolo_copy', False): + env.Append(CPPDEFINES = [ 'YOLO_COPY' ]) + +# Build GodotSharpTools solution + +import os +import subprocess +import mono_reg_utils as monoreg + + +def mono_build_solution(source, target, env): + if os.name == 'nt': + msbuild_tools_path = monoreg.find_msbuild_tools_path_reg() + if not msbuild_tools_path: + raise RuntimeError('Cannot find MSBuild Tools Path in the registry') + msbuild_path = os.path.join(msbuild_tools_path, 'MSBuild.exe') + else: + msbuild_path = 'msbuild' + + output_path = os.path.abspath(os.path.join(str(target[0]), os.pardir)) + + msbuild_args = [ + msbuild_path, + os.path.abspath(str(source[0])), + '/p:Configuration=Release', + '/p:OutputPath=' + output_path + ] + + msbuild_env = os.environ.copy() + + # Needed when running from Developer Command Prompt for VS + if 'PLATFORM' in msbuild_env: + del msbuild_env['PLATFORM'] + + msbuild_alt_paths = [ 'xbuild' ] + + while True: + try: + subprocess.check_call(msbuild_args, env = msbuild_env) + break + except subprocess.CalledProcessError: + raise RuntimeError('GodotSharpTools build failed') + except OSError: + if os.name != 'nt': + if not msbuild_alt_paths: + raise RuntimeError('Could not find commands msbuild or xbuild') + # Try xbuild + msbuild_args[0] = msbuild_alt_paths.pop(0) + else: + raise RuntimeError('Could not find command MSBuild.exe') + + +mono_sln_builder = Builder(action = mono_build_solution) +env.Append(BUILDERS = { 'MonoBuildSolution' : mono_sln_builder }) +env.MonoBuildSolution( + os.path.join(Dir('#bin').abspath, 'GodotSharpTools.dll'), + 'editor/GodotSharpTools/GodotSharpTools.sln' +) diff --git a/modules/mono/config.py b/modules/mono/config.py new file mode 100644 index 00000000000..9de199bb5a5 --- /dev/null +++ b/modules/mono/config.py @@ -0,0 +1,143 @@ + +import imp +import os +import sys +from shutil import copyfile + +from SCons.Script import BoolVariable, Environment, Variables + + +monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') + + +def find_file_in_dir(directory, files, prefix='', extension=''): + if not extension.startswith('.'): + extension = '.' + extension + for curfile in files: + if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): + return curfile + + return None + + +def can_build(platform): + if platform in ["javascript"]: + return False # Not yet supported + return True + + +def is_enabled(): + # The module is disabled by default. Use module_mono_enabled=yes to enable it. + return False + + +def configure(env): + env.use_ptrcall = True + + envvars = Variables() + envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) + envvars.Update(env) + + mono_static = env['mono_static'] + + mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] + + if env['platform'] == 'windows': + if mono_static: + raise RuntimeError('mono-static: Not supported on Windows') + + if env['bits'] == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + elif os.name == 'nt': + mono_root = monoreg.find_mono_root_dir() + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + elif os.name == 'nt': + mono_root = monoreg.find_mono_root_dir() + + if mono_root is None: + raise RuntimeError('Mono installation directory not found') + + mono_lib_path = os.path.join(mono_root, 'lib') + + env.Append(LIBPATH=mono_lib_path) + env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + + mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') + + if mono_lib_name is None: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + if os.getenv('VCINSTALLDIR'): + env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + else: + env.Append(LIBS=mono_lib_name) + + mono_bin_path = os.path.join(mono_root, 'bin') + + mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') + + mono_dll_src = os.path.join(mono_bin_path, mono_dll_name + '.dll') + mono_dll_dst = os.path.join('bin', mono_dll_name + '.dll') + copy_mono_dll = True + + if not os.path.isdir('bin'): + os.mkdir('bin') + elif os.path.exists(mono_dll_dst): + copy_mono_dll = False + + if copy_mono_dll: + copyfile(mono_dll_src, mono_dll_dst) + else: + mono_root = None + + if env['bits'] == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + + if mono_root is not None: + mono_lib_path = os.path.join(mono_root, 'lib') + + env.Append(LIBPATH=mono_lib_path) + env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + + mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a') + + if mono_lib is None: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + env.Append(CPPFLAGS=['-D_REENTRANT']) + + if mono_static: + mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a') + + if sys.platform == "darwin": + env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) + elif sys.platform == "linux" or sys.platform == "linux2": + env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) + else: + raise RuntimeError('mono-static: Not supported on this platform') + else: + env.Append(LIBS=[mono_lib]) + + env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) + else: + if mono_static: + raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually') + + env.ParseConfig('pkg-config mono-2 --cflags --libs') + + env.Append(LINKFLAGS='-rdynamic') + + +def get_doc_classes(): + return ["@C#", "CSharpScript", "GodotSharp"] + + +def get_doc_path(): + return "doc_classes" diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp new file mode 100644 index 00000000000..67b4e67e2b9 --- /dev/null +++ b/modules/mono/csharp_script.cpp @@ -0,0 +1,1853 @@ +/*************************************************************************/ +/* csharp_script.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "csharp_script.h" + +#include + +#include "os/file_access.h" +#include "os/os.h" +#include "os/thread.h" +#include "project_settings.h" + +#ifdef TOOLS_ENABLED +#include "editor/bindings_generator.h" +#include "editor/csharp_project.h" +#include "editor/editor_node.h" +#include "editor/godotsharp_editor.h" +#endif + +#include "godotsharp_dirs.h" +#include "mono_gd/gd_mono_class.h" +#include "mono_gd/gd_mono_marshal.h" +#include "signal_awaiter_utils.h" + +#define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->string_names.m_var) + +CSharpLanguage *CSharpLanguage::singleton = NULL; + +String CSharpLanguage::get_name() const { + + return "C#"; +} + +String CSharpLanguage::get_type() const { + + return "CSharpScript"; +} + +String CSharpLanguage::get_extension() const { + + return "cs"; +} + +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); + gdmono->initialize(); + +#ifdef MONO_GLUE_DISABLED + WARN_PRINT("This binary is built with `mono_glue=no` and cannot be used for scripting"); +#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); + } +#endif + +#ifdef TOOLS_ENABLED + EditorNode::add_init_callback(&gdsharp_editor_init_callback); +#endif +} + +void CSharpLanguage::finish() { + + if (gdmono) { + memdelete(gdmono); + gdmono = NULL; + } +} + +void CSharpLanguage::get_reserved_words(List *p_words) const { + + static const char *_reserved_words[] = { + // Reserved keywords + "abstract", + "as", + "base", + "bool", + "break", + "byte", + "case", + "catch", + "char", + "checked", + "class", + "const", + "continue", + "decimal", + "default", + "delegate", + "do", + "double", + "else", + "enum", + "event", + "explicit", + "extern", + "false", + "finally", + "fixed", + "float", + "for", + "forech", + "goto", + "if", + "implicit", + "in", + "int", + "interface", + "internal", + "is", + "lock", + "long", + "namespace", + "new", + "null", + "object", + "operator", + "out", + "override", + "params", + "private", + "protected", + "public", + "readonly", + "ref", + "return", + "sbyte", + "sealed", + "short", + "sizeof", + "stackalloc", + "static", + "string", + "struct", + "switch", + "this", + "throw", + "true", + "try", + "typeof", + "uint", + "ulong", + "unchecked", + "unsafe", + "ushort", + "using", + "virtual", + "volatile", + "void", + "while", + + // Contextual keywords. Not reserved words, but I guess we should include + // them because this seems to be used only for syntax highlighting. + "add", + "ascending", + "by", + "descending", + "dynamic", + "equals", + "from", + "get", + "global", + "group", + "in", + "into", + "join", + "let", + "on", + "orderby", + "partial", + "remove", + "select", + "set", + "value", + "var", + "where", + "yield", + 0 + }; + + const char **w = _reserved_words; + + while (*w) { + p_words->push_back(*w); + w++; + } +} + +void CSharpLanguage::get_comment_delimiters(List *p_delimiters) const { + + p_delimiters->push_back("//"); // single-line comment + p_delimiters->push_back("/* */"); // delimited comment +} + +void CSharpLanguage::get_string_delimiters(List *p_delimiters) const { + + p_delimiters->push_back("' '"); // character literal + p_delimiters->push_back("\" \""); // regular string literal + p_delimiters->push_back("@\" \""); // verbatim string literal +} + +Ref