diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6652556..d2005568 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,8 @@ jobs: sudo apt-get update -qq sudo apt-get install -qqq build-essential pkg-config python -m pip install scons + curl -LO https://downloads.tuxfamily.org/godotengine/3.2.3/Godot_v3.2.3-stable_linux_server.64.zip + unzip Godot_v3.2.3-stable_linux_server.64.zip - name: Build godot-cpp run: | @@ -33,6 +35,14 @@ jobs: path: bin/libgodot-cpp.linux.release.64.a if-no-files-found: error + - name: Build test GDNative library + run: | + scons target=release platform=linux bits=64 -j $(nproc) -C test + + - name: Run test GDNative library + run: | + ./Godot_v3.2.3-stable_linux_server.64 --path test -s script.gd + windows-msvc: name: Build (Windows, MSVC) runs-on: windows-2019 @@ -113,6 +123,8 @@ jobs: - name: Install dependencies run: | python -m pip install scons + curl -LO https://downloads.tuxfamily.org/godotengine/3.2.3/Godot_v3.2.3-stable_osx.64.zip + unzip Godot_v3.2.3-stable_osx.64.zip - name: Build godot-cpp run: | @@ -125,6 +137,14 @@ jobs: path: bin/libgodot-cpp.osx.release.64.a if-no-files-found: error + - name: Build test GDNative library + run: | + scons target=release platform=osx bits=64 -j $(sysctl -n hw.logicalcpu) -C test + + - name: Run test GDNative library + run: | + ./Godot.app/Contents/MacOS/Godot --path test -s script.gd + static-checks: name: Static Checks (clang-format) runs-on: ubuntu-16.04 diff --git a/test/SConstruct b/test/SConstruct new file mode 100644 index 00000000..2dd85d5a --- /dev/null +++ b/test/SConstruct @@ -0,0 +1,134 @@ +#!/usr/bin/env python +import os +import sys + +# Try to detect the host platform automatically. +# This is used if no `platform` argument is passed +if sys.platform.startswith('linux'): + host_platform = 'linux' +elif sys.platform == 'darwin': + host_platform = 'osx' +elif sys.platform == 'win32' or sys.platform == 'msys': + host_platform = 'windows' +else: + raise ValueError( + 'Could not detect platform automatically, please specify with ' + 'platform=' + ) + +env = Environment(ENV = os.environ) + +opts = Variables([], ARGUMENTS) + +# Define our options +opts.Add(EnumVariable('target', "Compilation target", 'debug', ['d', 'debug', 'r', 'release'])) +opts.Add(EnumVariable('platform', "Compilation platform", host_platform, ['', 'windows', 'x11', 'linux', 'osx'])) +opts.Add(EnumVariable('p', "Compilation target, alias for 'platform'", host_platform, ['', 'windows', 'x11', 'linux', 'osx'])) +opts.Add(EnumVariable('bits', 'Target platform bits', '64', ('32', '64'))) +opts.Add(BoolVariable('use_llvm', "Use the LLVM / Clang compiler", 'no')) +opts.Add(PathVariable('target_path', 'The path where the lib is installed.', 'bin/', PathVariable.PathAccept)) +opts.Add(PathVariable('target_name', 'The library name.', 'libgdexample', PathVariable.PathAccept)) + +# Local dependency paths, adapt them to your setup +godot_headers_path = "../godot_headers/" +cpp_bindings_path = "../" +cpp_library = "libgodot-cpp" + +# only support 64 at this time.. +bits = 64 + +# Updates the environment with the option variables. +opts.Update(env) +# Generates help for the -h scons option. +Help(opts.GenerateHelpText(env)) + +# This makes sure to keep the session environment variables on Windows. +# This way, you can run SCons in a Visual Studio 2017 prompt and it will find +# all the required tools +if host_platform == 'windows' and env['platform'] != 'android': + if env['bits'] == '64': + env = Environment(TARGET_ARCH='amd64') + elif env['bits'] == '32': + env = Environment(TARGET_ARCH='x86') + + opts.Update(env) + +# Process some arguments +if env['use_llvm']: + env['CC'] = 'clang' + env['CXX'] = 'clang++' + +if env['p'] != '': + env['platform'] = env['p'] + +if env['platform'] == '': + print("No valid target platform selected.") + quit(); + +# For the reference: +# - CCFLAGS are compilation flags shared between C and C++ +# - CFLAGS are for C-specific compilation flags +# - CXXFLAGS are for C++-specific compilation flags +# - CPPFLAGS are for pre-processor flags +# - CPPDEFINES are for pre-processor defines +# - LINKFLAGS are for linking flags + +# Check our platform specifics +if env['platform'] == "osx": + env['target_path'] += 'osx/' + cpp_library += '.osx' + env.Append(CCFLAGS=['-arch', 'x86_64']) + env.Append(CXXFLAGS=['-std=c++17']) + env.Append(LINKFLAGS=['-arch', 'x86_64']) + if env['target'] in ('debug', 'd'): + env.Append(CCFLAGS=['-g', '-O2']) + else: + env.Append(CCFLAGS=['-g', '-O3']) + +elif env['platform'] in ('x11', 'linux'): + env['target_path'] += 'x11/' + cpp_library += '.linux' + env.Append(CCFLAGS=['-fPIC']) + env.Append(CXXFLAGS=['-std=c++17']) + if env['target'] in ('debug', 'd'): + env.Append(CCFLAGS=['-g3', '-Og']) + else: + env.Append(CCFLAGS=['-g', '-O3']) + +elif env['platform'] == "windows": + env['target_path'] += 'win64/' + cpp_library += '.windows' + # This makes sure to keep the session environment variables on windows, + # that way you can run scons in a vs 2017 prompt and it will find all the required tools + env.Append(ENV=os.environ) + + env.Append(CPPDEFINES=['WIN32', '_WIN32', '_WINDOWS', '_CRT_SECURE_NO_WARNINGS']) + env.Append(CCFLAGS=['-W3', '-GR']) + if env['target'] in ('debug', 'd'): + env.Append(CPPDEFINES=['_DEBUG']) + env.Append(CCFLAGS=['-EHsc', '-MDd', '-ZI']) + env.Append(LINKFLAGS=['-DEBUG']) + else: + env.Append(CPPDEFINES=['NDEBUG']) + env.Append(CCFLAGS=['-O2', '-EHsc', '-MD']) + +if env['target'] in ('debug', 'd'): + cpp_library += '.debug' +else: + cpp_library += '.release' + +cpp_library += '.' + str(bits) + +# make sure our binding library is properly includes +env.Append(CPPPATH=['.', godot_headers_path, cpp_bindings_path + 'include/', cpp_bindings_path + 'include/core/', cpp_bindings_path + 'include/gen/']) +env.Append(LIBPATH=[cpp_bindings_path + 'bin/']) +env.Append(LIBS=[cpp_library]) + +# tweak this if you want to use different folders, or more folders, to store your source code in. +env.Append(CPPPATH=['src/']) +sources = Glob('src/*.cpp') + +library = env.SharedLibrary(target=env['target_path'] + env['target_name'] , source=sources) + +Default(library) + diff --git a/test/gdexample.gdnlib b/test/gdexample.gdnlib new file mode 100644 index 00000000..260b4eab --- /dev/null +++ b/test/gdexample.gdnlib @@ -0,0 +1,20 @@ +[general] + +singleton=false +load_once=true +symbol_prefix="godot_" +reloadable=false + +[entry] + +X11.64="res://bin/x11/libgdexample.so" +Server.64="res://bin/x11/libgdexample.so" +Windows.64="res://bin/win64/libgdexample.dll" +OSX.64="res://bin/osx/libgdexample.dylib" + +[dependencies] + +X11.64=[] +Server.64=[] +Windows.64=[] +OSX.64=[] diff --git a/test/gdexample.gdns b/test/gdexample.gdns new file mode 100644 index 00000000..a9d02d35 --- /dev/null +++ b/test/gdexample.gdns @@ -0,0 +1,9 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://gdexample.gdnlib" type="GDNativeLibrary" id=1] + +[resource] + +resource_name = "gdexample" +class_name = "SimpleClass" +library = ExtResource( 1 ) diff --git a/test/project.godot b/test/project.godot new file mode 100644 index 00000000..c9e426ae --- /dev/null +++ b/test/project.godot @@ -0,0 +1,19 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +_global_script_classes=[ ] +_global_script_class_icons={ + +} + +[application] + +config/name="Test CI project" + diff --git a/test/script.gd b/test/script.gd new file mode 100644 index 00000000..d9b0fe10 --- /dev/null +++ b/test/script.gd @@ -0,0 +1,30 @@ + +extends MainLoop + +func _initialize(): + OS.exit_code = 1 + var native_script = load("res://gdexample.gdns") + print("Native Script ", native_script) + if native_script == null || !is_instance_valid(native_script): + return + print("Library ", native_script.library) + if native_script.library == null || !is_instance_valid(native_script.library): + return + var ref = native_script.new() + print("Reference ", ref) + if ref == null || !is_instance_valid(ref): + return + print("Reference name ", ref.name) + if ref.name != "SimpleClass": + return + print("Reference value ", ref.value) + if ref.value != 0: + return + print("Call method ", ref.method(1)) + if ref.method(1) != 1: + return + OS.exit_code = 0 + +func _idle(_delta): + return true + diff --git a/test/src/init.cpp b/test/src/init.cpp new file mode 100644 index 00000000..3eedb056 --- /dev/null +++ b/test/src/init.cpp @@ -0,0 +1,73 @@ +#include +#include + +using namespace godot; + +class SimpleClass : public Reference { + GODOT_CLASS(SimpleClass, Reference); + +public: + SimpleClass() {} + + /** `_init` must exist as it is called by Godot. */ + void _init() { + _name = String("SimpleClass"); + _value = 0; + } + + void test_void_method() { + Godot::print("This is test"); + } + + Variant method(Variant arg) { + Variant ret; + ret = arg; + + return ret; + } + + static void _register_methods() { + register_method("method", &SimpleClass::method); + + /** + * The line below is equivalent to the following GDScript export: + * export var _name = "SimpleClass" + **/ + register_property("name", &SimpleClass::_name, String("SimpleClass")); + + /** Alternatively, with getter and setter methods: */ + register_property("value", &SimpleClass::set_value, &SimpleClass::get_value, 0); + + /** Registering a signal: **/ + register_signal("signal_name0"); // windows: error C2668: 'godot::register_signal': ambiguous call to overloaded function + register_signal("signal_name1", "string_argument", GODOT_VARIANT_TYPE_STRING); + } + + String _name; + int _value; + + void set_value(int p_value) { + _value = p_value; + } + + int get_value() const { + return _value; + } +}; + +/** GDNative Initialize **/ +extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) { + godot::Godot::gdnative_init(o); +} + +/** GDNative Terminate **/ +extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) { + godot::Godot::gdnative_terminate(o); +} + +/** NativeScript Initialize **/ +extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) { + godot::Godot::nativescript_init(handle); + + godot::register_class(); +}