commit 2e2dcfb9406fe9a5d9da4500a0e0d18a20918a86 Author: Blitz <> Date: Sun Aug 11 18:55:31 2024 +0200 Initial commit diff --git a/.gitea/workflows/linux.yml b/.gitea/workflows/linux.yml new file mode 100644 index 0000000..643d2c5 --- /dev/null +++ b/.gitea/workflows/linux.yml @@ -0,0 +1,32 @@ +name: Linux arm64 +run-name: Build And Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + Build: + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Packages cache + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/build/.packages + key: ${{ runner.os }}-xmake_cache + + - name: XMake config + run: xmake f -p linux -y + + - name: Build + run: xmake + + - name: Test + run: xmake test \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2797204 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Xmake cache +.xmake/ +build/ + +# MacOS Cache +.DS_Store + +.vscode/ + +*.dll +*.exp +*.pdb +*.ilk +*.obj +*.lib +*.TMP + +demo/.godot + +publish diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..436dd8e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Simon Pribylski + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..eabe994 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ + +This project is used to compile a GDExtension (godot engine cpp extension), and try to use xmake to manage the project. + +Forked from [Github](https://github.com/chaosddp/gdextension-cpp-xmake-template) + +## Install xmake + +Install xmake from [here](https://xmake.io/#/guide/installation). + +## How to use + +You must have godot in your path + +First modify the *xmake.lua* to change the project name, and other configurations as needed. + +```lua + +-- project name, this will be the output shared library name +PROJECT_NAME = "gdexample" + +-- project version +VERSION = "0.0.1" + +-- where is the godot project saved, default is "demo" +GODOT_PROJECT_FOLDER = "demo" + +-- where to save the export project, default is "publish" +PUBLISH_FOLDER = "publish" + +``` + +After change the configurations, you can run the following commands to build, run and export. + +```sh + +# 1. gen gdextension file (only once) +xmake gengdextension + +# 2. build the project +xmake + +# 3. open the editor at least once (works only in debug mode) +xmake run Editor + +# 4. run the demo (works only in debug mode) +xmake run + +# 5. export the project, the executable will be under "publish" folder by default +xmake p + +# 6. clean the build and publish +xmake clean + +# 7. generate a class that inherits from godot class, like Sprite2D +# by default the generated files will save under "src" folder, you can change it by -d folder/under/src +xmake ext-class -n MySprite2D -b Sprite2D -s myexample1 -d sample + +``` + +For better support in vscode, you can use command "Xmake: UpdateIntellisense" to generate *compile_commands.json*, then update "compileCommands" field in *c_cpp_properties.json* to point to the file. + +NOTE: not tested on other platforms except windows and linux. \ No newline at end of file diff --git a/demo/export_presets.cfg b/demo/export_presets.cfg new file mode 100644 index 0000000..85165a6 --- /dev/null +++ b/demo/export_presets.cfg @@ -0,0 +1,410 @@ +[preset.0] + +name="Windows Desktop" +platform="Windows Desktop" +runnable=true +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=false +texture_format/bptc=true +texture_format/s3tc=true +texture_format/etc=false +texture_format/etc2=false +binary_format/architecture="x86_64" +codesign/enable=false +codesign/timestamp=true +codesign/timestamp_server_url="" +codesign/digest_algorithm=1 +codesign/description="" +codesign/custom_options=PackedStringArray() +application/modify_resources=true +application/icon="" +application/console_wrapper_icon="" +application/icon_interpolation=4 +application/file_version="" +application/product_version="" +application/company_name="" +application/product_name="" +application/file_description="" +application/copyright="" +application/trademarks="" +application/export_angle=0 +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}' +$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}' +$trigger = New-ScheduledTaskTrigger -Once -At 00:00 +$settings = New-ScheduledTaskSettingsSet +$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings +Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true +Start-ScheduledTask -TaskName godot_remote_debug +while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 } +Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue" +ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue +Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue +Remove-Item -Recurse -Force '{temp_dir}'" + +[preset.1] + +name="Linux/X11" +platform="Linux/X11" +runnable=true +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false + +[preset.1.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=false +texture_format/bptc=true +texture_format/s3tc=true +texture_format/etc=false +texture_format/etc2=false +binary_format/architecture="x86_64" +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +export DISPLAY=:0 +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +\"{temp_dir}/{exe_name}\" {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" + +[preset.2] + +name="macOS" +platform="macOS" +runnable=true +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false + +[preset.2.options] + +export/distribution_type=1 +binary_format/architecture="universal" +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +application/icon="" +application/icon_interpolation=4 +application/bundle_identifier="" +application/signature="" +application/app_category="Games" +application/short_version="" +application/version="" +application/copyright="" +application/copyright_localized={} +application/min_macos_version="10.12" +application/export_angle=0 +display/high_res=true +xcode/platform_build="14C18" +xcode/sdk_version="13.1" +xcode/sdk_build="22C55" +xcode/sdk_name="macosx13.1" +xcode/xcode_version="1420" +xcode/xcode_build="14C18" +codesign/codesign=1 +codesign/installer_identity="" +codesign/apple_team_id="" +codesign/identity="" +codesign/entitlements/custom_file="" +codesign/entitlements/allow_jit_code_execution=false +codesign/entitlements/allow_unsigned_executable_memory=false +codesign/entitlements/allow_dyld_environment_variables=false +codesign/entitlements/disable_library_validation=false +codesign/entitlements/audio_input=false +codesign/entitlements/camera=false +codesign/entitlements/location=false +codesign/entitlements/address_book=false +codesign/entitlements/calendars=false +codesign/entitlements/photos_library=false +codesign/entitlements/apple_events=false +codesign/entitlements/debugging=false +codesign/entitlements/app_sandbox/enabled=false +codesign/entitlements/app_sandbox/network_server=false +codesign/entitlements/app_sandbox/network_client=false +codesign/entitlements/app_sandbox/device_usb=false +codesign/entitlements/app_sandbox/device_bluetooth=false +codesign/entitlements/app_sandbox/files_downloads=0 +codesign/entitlements/app_sandbox/files_pictures=0 +codesign/entitlements/app_sandbox/files_music=0 +codesign/entitlements/app_sandbox/files_movies=0 +codesign/entitlements/app_sandbox/files_user_selected=0 +codesign/entitlements/app_sandbox/helper_executables=[] +codesign/custom_options=PackedStringArray() +notarization/notarization=0 +privacy/microphone_usage_description="" +privacy/microphone_usage_description_localized={} +privacy/camera_usage_description="" +privacy/camera_usage_description_localized={} +privacy/location_usage_description="" +privacy/location_usage_description_localized={} +privacy/address_book_usage_description="" +privacy/address_book_usage_description_localized={} +privacy/calendar_usage_description="" +privacy/calendar_usage_description_localized={} +privacy/photos_library_usage_description="" +privacy/photos_library_usage_description_localized={} +privacy/desktop_folder_usage_description="" +privacy/desktop_folder_usage_description_localized={} +privacy/documents_folder_usage_description="" +privacy/documents_folder_usage_description_localized={} +privacy/downloads_folder_usage_description="" +privacy/downloads_folder_usage_description_localized={} +privacy/network_volumes_usage_description="" +privacy/network_volumes_usage_description_localized={} +privacy/removable_volumes_usage_description="" +privacy/removable_volumes_usage_description_localized={} +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +open \"{temp_dir}/{exe_name}.app\" --args {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name}.app/Contents/MacOS/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" + +[preset.3] + +name="Android" +platform="Android" +runnable=true +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false + +[preset.3.options] + +custom_template/debug="" +custom_template/release="" +gradle_build/use_gradle_build=false +gradle_build/export_format=0 +gradle_build/min_sdk="" +gradle_build/target_sdk="" +architectures/armeabi-v7a=false +architectures/arm64-v8a=true +architectures/x86=false +architectures/x86_64=false +version/code=1 +version/name="" +package/unique_name="com.example.$genname" +package/name="" +package/signed=true +package/app_category=2 +package/retain_data_on_uninstall=false +package/exclude_from_recents=false +package/show_in_android_tv=false +package/show_in_app_library=true +package/show_as_launcher_app=false +launcher_icons/main_192x192="" +launcher_icons/adaptive_foreground_432x432="" +launcher_icons/adaptive_background_432x432="" +graphics/opengl_debug=false +xr_features/xr_mode=0 +screen/immersive_mode=true +screen/support_small=true +screen/support_normal=true +screen/support_large=true +screen/support_xlarge=true +user_data_backup/allow=false +command_line/extra_args="" +apk_expansion/enable=false +apk_expansion/SALT="" +apk_expansion/public_key="" +permissions/custom_permissions=PackedStringArray() +permissions/access_checkin_properties=false +permissions/access_coarse_location=false +permissions/access_fine_location=false +permissions/access_location_extra_commands=false +permissions/access_mock_location=false +permissions/access_network_state=false +permissions/access_surface_flinger=false +permissions/access_wifi_state=false +permissions/account_manager=false +permissions/add_voicemail=false +permissions/authenticate_accounts=false +permissions/battery_stats=false +permissions/bind_accessibility_service=false +permissions/bind_appwidget=false +permissions/bind_device_admin=false +permissions/bind_input_method=false +permissions/bind_nfc_service=false +permissions/bind_notification_listener_service=false +permissions/bind_print_service=false +permissions/bind_remoteviews=false +permissions/bind_text_service=false +permissions/bind_vpn_service=false +permissions/bind_wallpaper=false +permissions/bluetooth=false +permissions/bluetooth_admin=false +permissions/bluetooth_privileged=false +permissions/brick=false +permissions/broadcast_package_removed=false +permissions/broadcast_sms=false +permissions/broadcast_sticky=false +permissions/broadcast_wap_push=false +permissions/call_phone=false +permissions/call_privileged=false +permissions/camera=false +permissions/capture_audio_output=false +permissions/capture_secure_video_output=false +permissions/capture_video_output=false +permissions/change_component_enabled_state=false +permissions/change_configuration=false +permissions/change_network_state=false +permissions/change_wifi_multicast_state=false +permissions/change_wifi_state=false +permissions/clear_app_cache=false +permissions/clear_app_user_data=false +permissions/control_location_updates=false +permissions/delete_cache_files=false +permissions/delete_packages=false +permissions/device_power=false +permissions/diagnostic=false +permissions/disable_keyguard=false +permissions/dump=false +permissions/expand_status_bar=false +permissions/factory_test=false +permissions/flashlight=false +permissions/force_back=false +permissions/get_accounts=false +permissions/get_package_size=false +permissions/get_tasks=false +permissions/get_top_activity_info=false +permissions/global_search=false +permissions/hardware_test=false +permissions/inject_events=false +permissions/install_location_provider=false +permissions/install_packages=false +permissions/install_shortcut=false +permissions/internal_system_window=false +permissions/internet=false +permissions/kill_background_processes=false +permissions/location_hardware=false +permissions/manage_accounts=false +permissions/manage_app_tokens=false +permissions/manage_documents=false +permissions/manage_external_storage=false +permissions/master_clear=false +permissions/media_content_control=false +permissions/modify_audio_settings=false +permissions/modify_phone_state=false +permissions/mount_format_filesystems=false +permissions/mount_unmount_filesystems=false +permissions/nfc=false +permissions/persistent_activity=false +permissions/post_notifications=false +permissions/process_outgoing_calls=false +permissions/read_calendar=false +permissions/read_call_log=false +permissions/read_contacts=false +permissions/read_external_storage=false +permissions/read_frame_buffer=false +permissions/read_history_bookmarks=false +permissions/read_input_state=false +permissions/read_logs=false +permissions/read_phone_state=false +permissions/read_profile=false +permissions/read_sms=false +permissions/read_social_stream=false +permissions/read_sync_settings=false +permissions/read_sync_stats=false +permissions/read_user_dictionary=false +permissions/reboot=false +permissions/receive_boot_completed=false +permissions/receive_mms=false +permissions/receive_sms=false +permissions/receive_wap_push=false +permissions/record_audio=false +permissions/reorder_tasks=false +permissions/restart_packages=false +permissions/send_respond_via_message=false +permissions/send_sms=false +permissions/set_activity_watcher=false +permissions/set_alarm=false +permissions/set_always_finish=false +permissions/set_animation_scale=false +permissions/set_debug_app=false +permissions/set_orientation=false +permissions/set_pointer_speed=false +permissions/set_preferred_applications=false +permissions/set_process_limit=false +permissions/set_time=false +permissions/set_time_zone=false +permissions/set_wallpaper=false +permissions/set_wallpaper_hints=false +permissions/signal_persistent_processes=false +permissions/status_bar=false +permissions/subscribed_feeds_read=false +permissions/subscribed_feeds_write=false +permissions/system_alert_window=false +permissions/transmit_ir=false +permissions/uninstall_shortcut=false +permissions/update_device_stats=false +permissions/use_credentials=false +permissions/use_sip=false +permissions/vibrate=false +permissions/wake_lock=false +permissions/write_apn_settings=false +permissions/write_calendar=false +permissions/write_call_log=false +permissions/write_contacts=false +permissions/write_external_storage=false +permissions/write_gservices=false +permissions/write_history_bookmarks=false +permissions/write_profile=false +permissions/write_secure_settings=false +permissions/write_settings=false +permissions/write_sms=false +permissions/write_social_stream=false +permissions/write_sync_settings=false +permissions/write_user_dictionary=false diff --git a/demo/icon.svg b/demo/icon.svg new file mode 100644 index 0000000..3fe4f4a --- /dev/null +++ b/demo/icon.svg @@ -0,0 +1 @@ + diff --git a/demo/icon.svg.import b/demo/icon.svg.import new file mode 100644 index 0000000..383529b --- /dev/null +++ b/demo/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cuend5rtkhbnp" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/demo/main.tscn b/demo/main.tscn new file mode 100644 index 0000000..04cbbac --- /dev/null +++ b/demo/main.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=2 format=3 uid="uid://uiwigxsdnci1"] + +[ext_resource type="Texture2D" uid="uid://cuend5rtkhbnp" path="res://icon.svg" id="1_q81dj"] + +[node name="Node2D" type="Node2D"] + +[node name="GDExample" type="GDExample" parent="."] +position = Vector2(5.20978, 6.34048) +texture = ExtResource("1_q81dj") +centered = false diff --git a/demo/project.godot b/demo/project.godot new file mode 100644 index 0000000..9d0b642 --- /dev/null +++ b/demo/project.godot @@ -0,0 +1,16 @@ +; 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=5 + +[application] + +config/name="demo" +run/main_scene="res://main.tscn" +config/features=PackedStringArray("4.2", "Forward Plus") +config/icon="res://icon.svg" diff --git a/src/gdexample.cpp b/src/gdexample.cpp new file mode 100644 index 0000000..2f6042e --- /dev/null +++ b/src/gdexample.cpp @@ -0,0 +1,28 @@ + +#include "gdexample.h" +#include + +using namespace godot; + +void GDExample::_bind_methods() { + // ClassDB::bind_method(D_METHOD("_process", "delta"), &GDExample::_process); +} + +GDExample::GDExample() +{ + time_passed = 0.0; + +} + +GDExample::~GDExample() {} + +void GDExample::_process(float delta) +{ + time_passed += delta; + + auto new_position = Vector2( + 10.0 + (10.0 * sin(time_passed * 2.0)), + 10.0 + (10.0 * cos(time_passed * 1.5))); + + set_position(new_position); +} \ No newline at end of file diff --git a/src/gdexample.h b/src/gdexample.h new file mode 100644 index 0000000..63d3c50 --- /dev/null +++ b/src/gdexample.h @@ -0,0 +1,24 @@ +#ifndef GDEXAMPLE_H +#define GDEXAMPLE_H + +#include + +namespace godot +{ + class GDExample : public Sprite2D + { + GDCLASS(GDExample, Sprite2D) + private: + double time_passed; + + protected: + static void _bind_methods(); + + public: + GDExample(); + ~GDExample(); + + void _process(float delta); + }; +} +#endif \ No newline at end of file diff --git a/src/register_types.cpp b/src/register_types.cpp new file mode 100644 index 0000000..d36ecdd --- /dev/null +++ b/src/register_types.cpp @@ -0,0 +1,44 @@ +#include "register_types.h" + +#include "gdexample.h" + +#include +#include +#include + +using namespace godot; + +void initialize_example_module(ModuleInitializationLevel p_level) +{ + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) + { + return; + } + + ClassDB::register_class(); +} + +void uninitialize_example_module(ModuleInitializationLevel p_level) +{ + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) + { + return; + } +} + +extern "C" +{ + GDExtensionBool GDE_EXPORT library_init( + GDExtensionInterfaceGetProcAddress p_get_proc, + const GDExtensionClassLibraryPtr p_library, + GDExtensionInitialization *r_initialization) + { + godot::GDExtensionBinding::InitObject init_obj(p_get_proc, p_library, r_initialization); + + init_obj.register_initializer(initialize_example_module); + init_obj.register_terminator(uninitialize_example_module); + init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); + + return init_obj.init(); + } +} \ No newline at end of file diff --git a/src/register_types.h b/src/register_types.h new file mode 100644 index 0000000..938f7a1 --- /dev/null +++ b/src/register_types.h @@ -0,0 +1,11 @@ +#ifndef GDEXAMPLE_REGISTER_TYPES_H +#define GDEXAMPLE_REGISTER_TYPES_H + +#include + +using namespace godot; + +void initialize_example_module(ModuleInitializationLevel p_level); +void uninitialize_example_module(ModuleInitializationLevel p_level); + +#endif \ No newline at end of file diff --git a/templates/class_tpl.lua b/templates/class_tpl.lua new file mode 100644 index 0000000..aae42f7 --- /dev/null +++ b/templates/class_tpl.lua @@ -0,0 +1,103 @@ +--[[ + #ifndef $(HEADER_GUARD) + #define $(HEADER_GUARD) + + #include + + namespace $(NAMESPACE) + { + class $(CLASS_NAME) : public godot::$(BASE_CLASS) + { + GDCLASS($(CLASS_NAME), godot::$(BASE_CLASS) + + protected: + static void _bind_methods(); + + public: + $(CLASS_NAME)(); + ~$(CLASS_NAME)(); + + void _process(float delta); + }; + } + #endif + +]] + +function render_header(header_guard, namespace, class_name, base_class) + local template = { + string.format("#ifndef %s", header_guard), + string.format("#define %s", header_guard), + string.format("#include ", string.lower(base_class)), + string.format("namespace %s", namespace), + "{", + string.format(" class %s : public godot::%s", class_name, base_class), + " {", + string.format(" GDCLASS(%s, godot::%s)", class_name, base_class), + " protected:", + string.format(" static void _bind_methods();"), + string.format(" public:"), + string.format(" %s();", class_name), + string.format(" ~%s();", class_name), + string.format(" void _process(float delta);"), + " };", + "}", + "#endif", + } + + return table.concat(template, "\n") +end + +--[[ + +#include "{% echo(" " .. string.lower(ClassName)) %}.h" + +using namespace {*Namespace*}; + +void {*ClassName*}::_bind_methods() { + // bind your own methods here, like + // ClassDB::bind_method(D_METHOD("my_foo", "delta"), &{ClassName}::my_foo); +} + +{*ClassName*}::{*ClassName*}() +{ + // initialize here +} + +{*ClassName*}::~{*ClassName*}() {} + +void {*ClassName*}::_process(float delta) +{ + // do update here +} +]] + +function render_impl(namespace, classname) + local template = { + string.format("#include \"%s.h\"", string.lower(classname)), + string.format("using namespace %s;", namespace), + "", + string.format("void %s::_bind_methods()", classname), + "{", + " // bind your own methods here, like", + string.format(" // ClassDB::bind_method(D_METHOD(\"my_foo\", \"delta\"), &%s::my_foo);", classname), + "}", + "", + string.format("%s::%s()", classname, classname), + "{", + " // initialize here", + "}", + "", + string.format("%s::~%s()", classname, classname), + "{", + " // do clean up here", + "}", + "", + string.format("void %s::_process(float delta)", classname), + "{", + " // do update here", + "}" + } + + return table.concat(template, "\n") +end \ No newline at end of file diff --git a/xmake.lua b/xmake.lua new file mode 100644 index 0000000..fbec89d --- /dev/null +++ b/xmake.lua @@ -0,0 +1,21 @@ +-- TODO: +-- 1. support other platforms +-- 2. switch optimization level +-- 3. refine the run command, as it currently run the project by default, maybe start a demo scene or a specified scene? + +------- basic custom config part ------- + +-- project name +PROJECT_NAME = "gdexample" + +-- project version +VERSION = "0.0.1" + +-- godot project folder, this will be used as export name, so be careful to make sure it is a valid name +GODOT_PROJECT_FOLDER = "demo" + +-- publish folder, the exported files will be put here +PUBLISH_FOLDER = "publish" + + +includes("xmake/godot.lua") \ No newline at end of file diff --git a/xmake/editor.lua b/xmake/editor.lua new file mode 100644 index 0000000..1e505a9 --- /dev/null +++ b/xmake/editor.lua @@ -0,0 +1,15 @@ +target("Editor") + set_default(false) + + -- Disable building + on_build((function() end)) + + on_run( + (function(godot_project_folder) + return function(target) + local project_file = path.join(godot_project_folder, "project.godot") + os.execv("echo", {"godot", project_file}) + os.exec("godot " .. project_file) + end + end)(GODOT_PROJECT_FOLDER) + ) \ No newline at end of file diff --git a/xmake/godot.lua b/xmake/godot.lua new file mode 100644 index 0000000..f520ac5 --- /dev/null +++ b/xmake/godot.lua @@ -0,0 +1,26 @@ +------- project settings ------- + +-- project name +set_project(PROJECT_NAME) + +-- project version +set_version(VERSION) + +-- min version of xmake we need to run +-- NOTE: this is the version i used to develop this template, may not 100% correct +set_xmakever("2.9.0") + +add_rules("mode.debug", "mode.release") + +-- c++17 is required for godot 4.x, we use c++20 here +set_languages("c++20") + +-- use latest 4.x version by default +add_requires("godotcpp4") + + +includes("tasks.lua") + +includes("target.lua") + +includes("editor.lua") \ No newline at end of file diff --git a/xmake/target.lua b/xmake/target.lua new file mode 100644 index 0000000..5d245eb --- /dev/null +++ b/xmake/target.lua @@ -0,0 +1,65 @@ +-- more on https://xmake.io/#/manual/project_target +target(PROJECT_NAME) + set_kind("shared") + set_default(true) + + add_packages("godotcpp4") + + -- more on https://xmake.io/#/manual/project_target?id=targetadd_files + add_files("../src/**.cpp") + + -- change the output name + set_basename(PROJECT_NAME .. ".$(os)_$(mode)_$(arch)") + + -- libraries will saved in os level folder + set_targetdir("../" .. GODOT_PROJECT_FOLDER .. "/lib") + + -- where to save .obj files + -- we use a seprate folder, so that it will not populate the targer folder + set_objectdir("../build/.objs") + + -- where .d files are saved + set_dependir("../build/.deps") + + -- set_optimize("smallest") + + -- handlers + + -- before_run(function (target) end) + -- after_run(function (target) end) + -- before_build(function (target) end) + -- after_build(function (target) end) + -- before_clean(function (target) end) + -- after_clean(function (target) end) + + on_run( + (function(godot_project_folder) + return function(target) + os.execv("echo", {"godot --path", godot_project_folder}) + os.exec("godot --path " .. godot_project_folder) + end + end)(GODOT_PROJECT_FOLDER) + ) + + on_package(function(target) + import("core.base.task") + + target_platform = "Unknown" + + if is_plat("windows") then + target_platform = "Windows Desktop" + elseif is_plat("macosx") then + target_platform = "macOS" + elseif is_plat("linux") then + target_platform = "Linux/X11" + elseif is_plat("android") then + target_platform = "Android" + end + + task.run("export", {}, target_platform) + end) + + after_clean(function (target) + import("core.base.task") + task.run("clean-publish") + end) \ No newline at end of file diff --git a/xmake/tasks.lua b/xmake/tasks.lua new file mode 100644 index 0000000..b937bd5 --- /dev/null +++ b/xmake/tasks.lua @@ -0,0 +1,204 @@ +-- NOTE: +-- xmake cannot accept the global variables in on_xxx functions, so we use a wrapper function to bind it + +-- export specified godot project, with the export execution name +-- args: +-- target_platform: the target platform, like "Windows Desktop" +-- name: the export execute name, it will be endswith ".exe" on windows +task("export") + on_run((function(godot_project_folder, publish_folder) + return function(target_platform) + local name = godot_project_folder + + -- different platform may have different execute name + -- xmake supported platforms: + -- windows + -- cross + -- linux + -- macosx + -- android + -- iphoneos + -- watchos + if is_plat("windows") then + name = name .. ".exe" + end + + local export_mode = "export-debug" + + if is_mode("release") then + export_mode = "export-release" + end + + if not os.isdir(publish_folder) then + os.mkdir(publish_folder) + end + + local godot_project_file = path.join(godot_project_folder, "project.godot") + + local export_path = path.absolute(path.join(publish_folder, name)) + + os.execv("echo", {"godot", godot_project_file, export_mode, target_platform, export_path}) + os.exec("godot %s --headless --%s %s %s", godot_project_file, export_mode, "\"" .. target_platform .. "\"", export_path) + end + + end)(GODOT_PROJECT_FOLDER, PUBLISH_FOLDER)) +task_end() + + + + + +-- clean the publish folder and build folder +task("clean-publish") + on_run((function(godot_project_folder, publish_folder) + return function () + -- remove publish folder + if os.isdir(publish_folder) then + os.tryrm(publish_folder) + end + + -- remove build target folder + local bin_dir = path.join(godot_project_folder, "bin") + if os.isdir(bin_dir) then + os.tryrm(path.join(bin_dir, "$(os)")) + end + end + end)(GODOT_PROJECT_FOLDER, PUBLISH_FOLDER)) +task_end() + + +-- tasks that exposed to cli +-- NOTE: for complex tasks, we can use a seprate file to define it + +-- generate a class that inherit from godot class +-- args: +-- name: the new class name +-- basename: the base class name to inherit (must under godot_cpp/classes) +-- dir: the directory to save the class (must under src) +-- namespace: the namespace of the new class + +task("ext-class") + on_run(function () + -- more on: https://xmake.io/#/manual/plugin_task + -- we need this module to load options from menu + import "core.base.option" + + local namespace = option.get("namespace") + local name = option.get("name") + local base = option.get("base") + local dir = option.get("dir") + + -- we calculate these here, in case there is any special case to handle + local header_guard = string.upper(name) .. "_H" + + import("class_tpl", {rootdir="templates", alias="class_render"}) + + -- header and impl text + local header_text = class_render.render_header(header_guard, namespace, name, base) + local impl_text = class_render.render_impl(namespace, name) + + -- save + local output_dir = "src" + + if dir ~= nil then + output_dir = path.join("src", dir) + end + + if not os.isdir(output_dir) then + os.mkdir(output_dir) + end + + local header_file = path.join(output_dir, string.lower(name) .. ".h") + local impl_file = path.join(output_dir, string.lower(name) .. ".cpp") + + io.writefile(header_file, header_text) + io.writefile(impl_file, impl_text) + + end) + + -- options + set_menu { + usage = "xmake ext-class [options]", + + description = "Generate godot class that inherits from a base class under godot_cpp/classes", + + options = { + + -- kv options + -- (short, long), (kv, default_value), description, [values]) + {"s", "namespace", "kv", "godot", "Set the namespace of the new class"}, + {"d", "dir", "kv", nil, "Set the directory to save the class"}, + + {}, + + -- single value options + {"n", "name", "v", nil, "Set the new class name"}, + {"b", "base", "v", nil, "Set the base class name to inherit"}, + } + } +task_end() + + + + + +task("gengdextension") + on_run((function(godot_project_folder, project_name) + return function () + + local bin_dir = path.join(godot_project_folder, "bin") + if not os.isdir(bin_dir) then + os.mkdir(bin_dir) + end + + local godot_gdextension_file = path.join(bin_dir, project_name .. ".gdextension") + + local fileContent = +[[ +[icons] +ProjectName = "res://icon.svg" + +[configuration] + +entry_symbol = "library_init" +compatibility_minimum = "4.2" + +[libraries] + +macos.debug = "res://lib/libProjectName.macos_framework" +macos.release = "res://lib/libProjectName.macos_framework" + +window.debug.x86_32 = "res://lib/libProjectName.windows_debug_x86.dll" +window.release.x86_32 = "res://lib/libProjectName.windows_release_x86.dll" +window.debug.x86_64 = "res://lib/libProjectName.windows_debug_x64.dll" +window.release.x86_64 = "res://lib/libProjectName.windows_release_x64.dll" + +linux.debug.x86_64 = "res://lib/libProjectName.linux_debug_x86_64.so" +linux.release.x86_64 = "res://lib/libProjectName.linux_release_x86_64.so" +linux.debug.arm64 = "res://lib/libProjectName.linux_debug_arm64.so" +linux.release.arm64 = "res://lib/libProjectName.linux_release_arm64.so" +linux.debug.rv64 = "res://lib/libProjectName.linux_debug_rv64.so" +linux.release.rv64 = "res://lib/libProjectName.linux_release_rv64.so" + +android.debug.x86_64 = "res://lib/libProjectName.android_debug_x86_64.so" +android.release.x86_64 = "res://lib/libProjectName.android_release_x86_64.so" +android.debug.arm64 = "res://lib/libProjectName.android_debug_arm64.so" +android.release.arm64 = "res://lib/libProjectName.android_release_arm64.so" + +]] + + -- needs testing with macos + + fileContent = string.gsub(fileContent, "ProjectName", project_name) + + io.writefile(godot_gdextension_file, fileContent) + end + end)(GODOT_PROJECT_FOLDER, PROJECT_NAME)) + + -- options + set_menu { + usage = "xmake gengdextension", + + description = "Generate gdextension file", + } +task_end()