From ede88cf59d20da54042f16df3fe6ab1aca760d64 Mon Sep 17 00:00:00 2001 From: Fredia Huya-Kouadio Date: Tue, 13 Feb 2024 14:26:05 -0800 Subject: [PATCH] Fix loading GDExtension dependencies on Android --- core/extension/gdextension.cpp | 50 +++++++++++++- core/extension/gdextension.h | 4 +- core/os/os.h | 2 +- .../os/shared_object.h | 8 +-- drivers/unix/os_unix.cpp | 2 +- drivers/unix/os_unix.h | 2 +- editor/export/editor_export_platform.h | 2 +- editor/export/editor_export_plugin.cpp | 4 ++ editor/export/editor_export_plugin.h | 3 +- editor/plugins/gdextension_export_plugin.h | 31 +-------- platform/android/os_android.cpp | 66 +++++++++++++++---- platform/android/os_android.h | 4 +- platform/ios/os_ios.h | 2 +- platform/ios/os_ios.mm | 2 +- platform/macos/os_macos.h | 2 +- platform/macos/os_macos.mm | 2 +- platform/web/os_web.cpp | 2 +- platform/web/os_web.h | 2 +- platform/windows/os_windows.cpp | 2 +- platform/windows/os_windows.h | 2 +- 20 files changed, 131 insertions(+), 63 deletions(-) rename editor/export/editor_export_shared_object.h => core/os/shared_object.h (93%) diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 546c40e9cbf..1735ee51a48 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -46,6 +46,41 @@ String GDExtension::get_extension_list_config_file() { return ProjectSettings::get_singleton()->get_project_data_path().path_join("extension_list.cfg"); } +Vector GDExtension::find_extension_dependencies(const String &p_path, Ref p_config, std::function p_has_feature) { + Vector dependencies_shared_objects; + if (p_config->has_section("dependencies")) { + List config_dependencies; + p_config->get_section_keys("dependencies", &config_dependencies); + + for (const String &dependency : config_dependencies) { + Vector dependency_tags = dependency.split("."); + bool all_tags_met = true; + for (int i = 0; i < dependency_tags.size(); i++) { + String tag = dependency_tags[i].strip_edges(); + if (!p_has_feature(tag)) { + all_tags_met = false; + break; + } + } + + if (all_tags_met) { + Dictionary dependency_value = p_config->get_value("dependencies", dependency); + for (const Variant *key = dependency_value.next(nullptr); key; key = dependency_value.next(key)) { + String dependency_path = *key; + String target_path = dependency_value[*key]; + if (dependency_path.is_relative_path()) { + dependency_path = p_path.get_base_dir().path_join(dependency_path); + } + dependencies_shared_objects.push_back(SharedObject(dependency_path, dependency_tags, target_path)); + } + break; + } + } + } + + return dependencies_shared_objects; +} + String GDExtension::find_extension_library(const String &p_path, Ref p_config, std::function p_has_feature, PackedStringArray *r_tags) { // First, check the explicit libraries. if (p_config->has_section("libraries")) { @@ -727,10 +762,18 @@ GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const String return *function; } -Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol) { +Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol, Vector *p_dependencies) { String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path); + + Vector abs_dependencies_paths; + if (p_dependencies != nullptr && !p_dependencies->is_empty()) { + for (const SharedObject &dependency : *p_dependencies) { + abs_dependencies_paths.push_back(ProjectSettings::get_singleton()->globalize_path(dependency.path)); + } + } + String actual_lib_path; - Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, true, &actual_lib_path, Engine::get_singleton()->is_editor_hint()); + Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, true, &actual_lib_path, Engine::get_singleton()->is_editor_hint(), &abs_dependencies_paths); if (actual_lib_path.get_file() != abs_path.get_file()) { // If temporary files are generated, let's change the library path to point at the original, @@ -970,7 +1013,8 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, FileAccess::get_modified_time(library_path)); #endif - err = p_extension->open_library(is_static_library ? String() : library_path, entry_symbol); + Vector library_dependencies = GDExtension::find_extension_dependencies(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); }); + err = p_extension->open_library(is_static_library ? String() : library_path, entry_symbol, &library_dependencies); if (err != OK) { // Unreference the extension so that this loading can be considered a failure. p_extension.unref(); diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 1f48edecf76..23b1f512082 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -37,6 +37,7 @@ #include "core/io/config_file.h" #include "core/io/resource_loader.h" #include "core/object/ref_counted.h" +#include "core/os/shared_object.h" class GDExtensionMethodBind; @@ -123,8 +124,9 @@ public: static String get_extension_list_config_file(); static String find_extension_library(const String &p_path, Ref p_config, std::function p_has_feature, PackedStringArray *r_tags = nullptr); + static Vector find_extension_dependencies(const String &p_path, Ref p_config, std::function p_has_feature); - Error open_library(const String &p_path, const String &p_entry_symbol); + Error open_library(const String &p_path, const String &p_entry_symbol, Vector *p_dependencies = nullptr); void close_library(); enum InitializationLevel { diff --git a/core/os/os.h b/core/os/os.h index 06be0e2b419..cafd5cf6c4e 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -153,7 +153,7 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); - virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false) { return ERR_UNAVAILABLE; } + virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false, PackedStringArray *p_library_dependencies = nullptr) { return ERR_UNAVAILABLE; } virtual Error close_dynamic_library(void *p_library_handle) { return ERR_UNAVAILABLE; } virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional = false) { return ERR_UNAVAILABLE; } diff --git a/editor/export/editor_export_shared_object.h b/core/os/shared_object.h similarity index 93% rename from editor/export/editor_export_shared_object.h rename to core/os/shared_object.h index 54e1314a42d..88423bed13c 100644 --- a/editor/export/editor_export_shared_object.h +++ b/core/os/shared_object.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* editor_export_shared_object.h */ +/* shared_object.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef EDITOR_EXPORT_SHARED_OBJECT_H -#define EDITOR_EXPORT_SHARED_OBJECT_H +#ifndef SHARED_OBJECT_H +#define SHARED_OBJECT_H #include "core/string/ustring.h" #include "core/templates/vector.h" @@ -48,4 +48,4 @@ struct SharedObject { SharedObject() {} }; -#endif // EDITOR_EXPORT_SHARED_OBJECT_H +#endif // SHARED_OBJECT_H diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 1a8cd534867..70668a26b0b 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -784,7 +784,7 @@ String OS_Unix::get_locale() const { return locale; } -Error OS_Unix::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files) { +Error OS_Unix::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files, PackedStringArray *p_library_dependencies) { String path = p_path; if (FileAccess::exists(path) && path.is_relative_path()) { diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index a107e7a0e39..d028d7bb0b4 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -62,7 +62,7 @@ public: virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override; - virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false) override; + virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false, PackedStringArray *p_library_dependencies = nullptr) override; virtual Error close_dynamic_library(void *p_library_handle) override; virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional = false) override; diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index 26e1f86c8b8..3fd75ff67f2 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -36,8 +36,8 @@ struct EditorProgress; #include "core/io/dir_access.h" #include "core/io/zip_io.h" +#include "core/os/shared_object.h" #include "editor_export_preset.h" -#include "editor_export_shared_object.h" #include "scene/gui/rich_text_label.h" #include "scene/main/node.h" #include "scene/resources/image_texture.h" diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp index 5353eae6545..28d0750d5a9 100644 --- a/editor/export/editor_export_plugin.cpp +++ b/editor/export/editor_export_plugin.cpp @@ -60,6 +60,10 @@ void EditorExportPlugin::add_shared_object(const String &p_path, const Vector &p_file, bool p_remap); void add_shared_object(const String &p_path, const Vector &tags, const String &p_target = String()); + void _add_shared_object(const SharedObject &p_shared_object); void add_ios_framework(const String &p_path); void add_ios_embedded_framework(const String &p_path); diff --git a/editor/plugins/gdextension_export_plugin.h b/editor/plugins/gdextension_export_plugin.h index 28080ed5596..da136b70ae2 100644 --- a/editor/plugins/gdextension_export_plugin.h +++ b/editor/plugins/gdextension_export_plugin.h @@ -129,34 +129,9 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p ERR_FAIL_MSG(vformat("No suitable library found for GDExtension: %s. Possible feature flags for your platform: %s", p_path, String(", ").join(features_vector))); } - List dependencies; - if (config->has_section("dependencies")) { - config->get_section_keys("dependencies", &dependencies); - } - - for (const String &E : dependencies) { - Vector dependency_tags = E.split("."); - bool all_tags_met = true; - for (int i = 0; i < dependency_tags.size(); i++) { - String tag = dependency_tags[i].strip_edges(); - if (!p_features.has(tag)) { - all_tags_met = false; - break; - } - } - - if (all_tags_met) { - Dictionary dependency = config->get_value("dependencies", E); - for (const Variant *key = dependency.next(nullptr); key; key = dependency.next(key)) { - String dependency_path = *key; - String target_path = dependency[*key]; - if (dependency_path.is_relative_path()) { - dependency_path = p_path.get_base_dir().path_join(dependency_path); - } - add_shared_object(dependency_path, dependency_tags, target_path); - } - break; - } + Vector dependencies_shared_objects = GDExtension::find_extension_dependencies(p_path, config, [p_features](String p_feature) { return p_features.has(p_feature); }); + for (const SharedObject &shared_object : dependencies_shared_objects) { + _add_shared_object(shared_object); } } } diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index bf6b7a73724..93a17aea7b5 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -162,7 +162,39 @@ Vector OS_Android::get_granted_permissions() const { return godot_java->get_granted_permissions(); } -Error OS_Android::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files) { +bool OS_Android::copy_dynamic_library(const String &p_library_path, const String &p_target_dir, String *r_copy_path) { + if (!FileAccess::exists(p_library_path)) { + return false; + } + + Ref da_ref = DirAccess::create_for_path(p_library_path); + if (!da_ref.is_valid()) { + return false; + } + + String copy_path = p_target_dir.path_join(p_library_path.get_file()); + bool copy_exists = FileAccess::exists(copy_path); + if (copy_exists) { + print_verbose("Deleting existing library copy " + copy_path); + if (da_ref->remove(copy_path) != OK) { + print_verbose("Unable to delete " + copy_path); + } + } + + print_verbose("Copying " + p_library_path + " to " + p_target_dir); + Error create_dir_result = da_ref->make_dir_recursive(p_target_dir); + if (create_dir_result == OK || create_dir_result == ERR_ALREADY_EXISTS) { + copy_exists = da_ref->copy(p_library_path, copy_path) == OK; + } + + if (copy_exists && r_copy_path != nullptr) { + *r_copy_path = copy_path; + } + + return copy_exists; +} + +Error OS_Android::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files, PackedStringArray *p_library_dependencies) { String path = p_path; bool so_file_exists = true; if (!FileAccess::exists(path)) { @@ -172,24 +204,32 @@ Error OS_Android::open_dynamic_library(const String &p_path, void *&p_library_ha p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); if (!p_library_handle && so_file_exists) { - // The library may be on the sdcard and thus inaccessible. Try to copy it to the internal - // directory. - uint64_t so_modified_time = FileAccess::get_modified_time(p_path); - String dynamic_library_path = get_dynamic_libraries_path().path_join(String::num_uint64(so_modified_time)); - String internal_path = dynamic_library_path.path_join(p_path.get_file()); + // The library (and its dependencies) may be on the sdcard and thus inaccessible. + // Try to copy to the internal directory for access. + const String dynamic_library_path = get_dynamic_libraries_path(); - bool internal_so_file_exists = FileAccess::exists(internal_path); - if (!internal_so_file_exists) { - Ref da_ref = DirAccess::create_for_path(p_path); - if (da_ref.is_valid()) { - Error create_dir_result = da_ref->make_dir_recursive(dynamic_library_path); - if (create_dir_result == OK || create_dir_result == ERR_ALREADY_EXISTS) { - internal_so_file_exists = da_ref->copy(path, internal_path) == OK; + if (p_library_dependencies != nullptr && !p_library_dependencies->is_empty()) { + // Copy the library dependencies + print_verbose("Copying library dependencies.."); + for (const String &library_dependency_path : *p_data->library_dependencies) { + String internal_library_dependency_path; + if (!copy_dynamic_library(library_dependency_path, dynamic_library_path.path_join(library_dependency_path.get_base_dir()), &internal_library_dependency_path)) { + ERR_PRINT(vformat("Unable to copy library dependency %s", library_dependency_path)); + } else { + void *lib_dependency_handle = dlopen(internal_library_dependency_path.utf8().get_data(), RTLD_NOW); + if (!lib_dependency_handle) { + ERR_PRINT(vformat("Can't open dynamic library dependency: %s. Error: %s.", internal_library_dependency_path, dlerror())); + } } } } + String internal_path; + print_verbose("Copying library " + p_path); + const bool internal_so_file_exists = copy_dynamic_library(p_path, dynamic_library_path.path_join(p_path.get_base_dir()), &internal_path); + if (internal_so_file_exists) { + print_verbose("Opening library " + internal_path); p_library_handle = dlopen(internal_path.utf8().get_data(), RTLD_NOW); if (p_library_handle) { path = internal_path; diff --git a/platform/android/os_android.h b/platform/android/os_android.h index e01d7594943..b6be5d8ba4f 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -113,7 +113,7 @@ public: virtual void alert(const String &p_alert, const String &p_title) override; - virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false) override; + virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false, PackedStringArray *p_library_dependencies = nullptr) override; virtual String get_name() const override; virtual String get_distribution_name() const override; @@ -178,6 +178,8 @@ public: private: // Location where we relocate external dynamic libraries to make them accessible. String get_dynamic_libraries_path() const; + // Copy a dynamic library to the given location to make it accessible for loading. + bool copy_dynamic_library(const String &p_library_path, const String &p_target_dir, String *r_copy_path = nullptr); }; #endif // OS_ANDROID_H diff --git a/platform/ios/os_ios.h b/platform/ios/os_ios.h index 6950720b385..5a775c6f121 100644 --- a/platform/ios/os_ios.h +++ b/platform/ios/os_ios.h @@ -103,7 +103,7 @@ public: virtual Vector get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override; virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override; - virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false) override; + virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false, PackedStringArray *p_library_dependencies = nullptr) override; virtual Error close_dynamic_library(void *p_library_handle) override; virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional = false) override; diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm index bcf21bc802b..18e6087467f 100644 --- a/platform/ios/os_ios.mm +++ b/platform/ios/os_ios.mm @@ -217,7 +217,7 @@ _FORCE_INLINE_ String OS_IOS::get_framework_executable(const String &p_path) { return p_path; } -Error OS_IOS::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files) { +Error OS_IOS::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files, PackedStringArray *p_library_dependencies) { if (p_path.length() == 0) { // Static xcframework. p_library_handle = RTLD_SELF; diff --git a/platform/macos/os_macos.h b/platform/macos/os_macos.h index 439e7d2fff6..0e42978ee35 100644 --- a/platform/macos/os_macos.h +++ b/platform/macos/os_macos.h @@ -85,7 +85,7 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override; - virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false) override; + virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false, PackedStringArray *p_library_dependencies = nullptr) override; virtual MainLoop *get_main_loop() const override; diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index 261777a4e83..4730694eed8 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -217,7 +217,7 @@ _FORCE_INLINE_ String OS_MacOS::get_framework_executable(const String &p_path) { return p_path; } -Error OS_MacOS::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files) { +Error OS_MacOS::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files, PackedStringArray *p_library_dependencies) { String path = get_framework_executable(p_path); if (!FileAccess::exists(path)) { diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp index 41582955207..40723d7646a 100644 --- a/platform/web/os_web.cpp +++ b/platform/web/os_web.cpp @@ -247,7 +247,7 @@ bool OS_Web::is_userfs_persistent() const { return idb_available; } -Error OS_Web::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files) { +Error OS_Web::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files, PackedStringArray *p_library_dependencies) { String path = p_path.get_file(); p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); diff --git a/platform/web/os_web.h b/platform/web/os_web.h index eeeafdac345..466499d8380 100644 --- a/platform/web/os_web.h +++ b/platform/web/os_web.h @@ -109,7 +109,7 @@ public: void alert(const String &p_alert, const String &p_title = "ALERT!") override; - Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false) override; + Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false, PackedStringArray *p_library_dependencies = nullptr) override; void resume_audio(); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index f2a99896068..3ae7a5c620f 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -359,7 +359,7 @@ void debug_dynamic_library_check_dependencies(const String &p_root_path, const S } #endif -Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files) { +Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path, bool p_generate_temp_files, PackedStringArray *p_library_dependencies) { String path = p_path.replace("/", "\\"); if (!FileAccess::exists(path)) { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 288154745f0..b44e4c66e93 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -161,7 +161,7 @@ public: virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override; - virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false) override; + virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = false, PackedStringArray *p_library_dependencies = nullptr) override; virtual Error close_dynamic_library(void *p_library_handle) override; virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional = false) override;