Browse Source

Allow specifying a prefix to automatically detect library files for gdextension exports

Gilles Roudière 2 years ago
parent
commit
fa4143cdeb

+ 107 - 23
core/extension/native_extension.cpp

@@ -30,7 +30,7 @@
 
 
 #include "native_extension.h"
 #include "native_extension.h"
 #include "core/config/project_settings.h"
 #include "core/config/project_settings.h"
-#include "core/io/config_file.h"
+#include "core/io/dir_access.h"
 #include "core/object/class_db.h"
 #include "core/object/class_db.h"
 #include "core/object/method_bind.h"
 #include "core/object/method_bind.h"
 #include "core/os/os.h"
 #include "core/os/os.h"
@@ -39,6 +39,111 @@ String NativeExtension::get_extension_list_config_file() {
 	return ProjectSettings::get_singleton()->get_project_data_path().path_join("extension_list.cfg");
 	return ProjectSettings::get_singleton()->get_project_data_path().path_join("extension_list.cfg");
 }
 }
 
 
+String NativeExtension::find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags) {
+	// First, check the explicit libraries.
+	if (p_config->has_section("libraries")) {
+		List<String> libraries;
+		p_config->get_section_keys("libraries", &libraries);
+
+		// Iterate the libraries, finding the best matching tags.
+		String best_library_path;
+		Vector<String> best_library_tags;
+		for (const String &E : libraries) {
+			Vector<String> tags = E.split(".");
+			bool all_tags_met = true;
+			for (int i = 0; i < tags.size(); i++) {
+				String tag = tags[i].strip_edges();
+				if (!p_has_feature(tag)) {
+					all_tags_met = false;
+					break;
+				}
+			}
+
+			if (all_tags_met && tags.size() > best_library_tags.size()) {
+				best_library_path = p_config->get_value("libraries", E);
+				best_library_tags = tags;
+			}
+		}
+
+		if (!best_library_path.is_empty()) {
+			if (best_library_path.is_relative_path()) {
+				best_library_path = p_path.get_base_dir().path_join(best_library_path);
+			}
+			if (r_tags != nullptr) {
+				r_tags->append_array(best_library_tags);
+			}
+			return best_library_path;
+		}
+	}
+
+	// Second, try to autodetect
+	String autodetect_library_prefix;
+	if (p_config->has_section_key("configuration", "autodetect_library_prefix")) {
+		autodetect_library_prefix = p_config->get_value("configuration", "autodetect_library_prefix");
+	}
+	if (!autodetect_library_prefix.is_empty()) {
+		String autodetect_path = autodetect_library_prefix;
+		if (autodetect_path.is_relative_path()) {
+			autodetect_path = p_path.get_base_dir().path_join(autodetect_path);
+		}
+
+		// Find the folder and file parts of the prefix.
+		String folder;
+		String file_prefix;
+		if (DirAccess::dir_exists_absolute(autodetect_path)) {
+			folder = autodetect_path;
+		} else if (DirAccess::dir_exists_absolute(autodetect_path.get_base_dir())) {
+			folder = autodetect_path.get_base_dir();
+			file_prefix = autodetect_path.get_file();
+		} else {
+			ERR_FAIL_V_MSG(String(), vformat("Error in extension: %s. Could not find folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
+		}
+
+		// Open the folder.
+		Ref<DirAccess> dir = DirAccess::open(folder);
+		ERR_FAIL_COND_V_MSG(!dir.is_valid(), String(), vformat("Error in extension: %s. Could not open folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
+
+		// Iterate the files and check the prefixes, finding the best matching file.
+		String best_file;
+		Vector<String> best_file_tags;
+		dir->list_dir_begin();
+		String file_name = dir->_get_next();
+		while (file_name != "") {
+			if (!dir->current_is_dir() && file_name.begins_with(file_prefix)) {
+				// Check if the files matches all requested feature tags.
+				String tags_str = file_name.trim_prefix(file_prefix);
+				tags_str = tags_str.trim_suffix(tags_str.get_extension());
+
+				Vector<String> tags = tags_str.split(".", false);
+				bool all_tags_met = true;
+				for (int i = 0; i < tags.size(); i++) {
+					String tag = tags[i].strip_edges();
+					if (!p_has_feature(tag)) {
+						all_tags_met = false;
+						break;
+					}
+				}
+
+				// If all tags are found in the feature list, and we found more tags than before, use this file.
+				if (all_tags_met && tags.size() > best_file_tags.size()) {
+					best_file_tags = tags;
+					best_file = file_name;
+				}
+			}
+			file_name = dir->_get_next();
+		}
+
+		if (!best_file.is_empty()) {
+			String library_path = folder.path_join(best_file);
+			if (r_tags != nullptr) {
+				r_tags->append_array(best_file_tags);
+			}
+			return library_path;
+		}
+	}
+	return String();
+}
+
 class NativeExtensionMethodBind : public MethodBind {
 class NativeExtensionMethodBind : public MethodBind {
 	GDNativeExtensionClassMethodCall call_func;
 	GDNativeExtensionClassMethodCall call_func;
 	GDNativeExtensionClassMethodPtrCall ptrcall_func;
 	GDNativeExtensionClassMethodPtrCall ptrcall_func;
@@ -415,28 +520,7 @@ Ref<Resource> NativeExtensionResourceLoader::load(const String &p_path, const St
 
 
 	String entry_symbol = config->get_value("configuration", "entry_symbol");
 	String entry_symbol = config->get_value("configuration", "entry_symbol");
 
 
-	List<String> libraries;
-
-	config->get_section_keys("libraries", &libraries);
-
-	String library_path;
-
-	for (const String &E : libraries) {
-		Vector<String> tags = E.split(".");
-		bool all_tags_met = true;
-		for (int i = 0; i < tags.size(); i++) {
-			String tag = tags[i].strip_edges();
-			if (!OS::get_singleton()->has_feature(tag)) {
-				all_tags_met = false;
-				break;
-			}
-		}
-
-		if (all_tags_met) {
-			library_path = config->get_value("libraries", E);
-			break;
-		}
-	}
+	String library_path = NativeExtension::find_extension_library(p_path, config, [](String p_feature) { return OS::get_singleton()->has_feature(p_feature); });
 
 
 	if (library_path.is_empty()) {
 	if (library_path.is_empty()) {
 		if (r_error) {
 		if (r_error) {

+ 4 - 0
core/extension/native_extension.h

@@ -31,7 +31,10 @@
 #ifndef NATIVE_EXTENSION_H
 #ifndef NATIVE_EXTENSION_H
 #define NATIVE_EXTENSION_H
 #define NATIVE_EXTENSION_H
 
 
+#include <functional>
+
 #include "core/extension/gdnative_interface.h"
 #include "core/extension/gdnative_interface.h"
+#include "core/io/config_file.h"
 #include "core/io/resource_loader.h"
 #include "core/io/resource_loader.h"
 #include "core/object/ref_counted.h"
 #include "core/object/ref_counted.h"
 
 
@@ -65,6 +68,7 @@ protected:
 
 
 public:
 public:
 	static String get_extension_list_config_file();
 	static String get_extension_list_config_file();
+	static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);
 
 
 	Error open_library(const String &p_path, const String &p_entry_symbol);
 	Error open_library(const String &p_path, const String &p_entry_symbol);
 	void close_library();
 	void close_library();

+ 12 - 6
core/os/os.cpp

@@ -353,20 +353,26 @@ bool OS::has_feature(const String &p_feature) {
 	if (p_feature == "debug") {
 	if (p_feature == "debug") {
 		return true;
 		return true;
 	}
 	}
+#endif // DEBUG_ENABLED
+
+#ifdef TOOLS_ENABLED
+	if (p_feature == "editor") {
+		return true;
+	}
 #else
 #else
-	if (p_feature == "release") {
+	if (p_feature == "template") {
 		return true;
 		return true;
 	}
 	}
-#endif
-#ifdef TOOLS_ENABLED
-	if (p_feature == "editor") {
+#ifdef DEBUG_ENABLED
+	if (p_feature == "template_debug") {
 		return true;
 		return true;
 	}
 	}
 #else
 #else
-	if (p_feature == "standalone") {
+	if (p_feature == "template_release" || p_feature == "release") {
 		return true;
 		return true;
 	}
 	}
-#endif
+#endif // DEBUG_ENABLED
+#endif // TOOLS_ENABLED
 
 
 	if (sizeof(void *) == 8 && p_feature == "64") {
 	if (sizeof(void *) == 8 && p_feature == "64") {
 		return true;
 		return true;

+ 3 - 2
editor/export/editor_export_platform.cpp

@@ -442,10 +442,11 @@ HashSet<String> EditorExportPlatform::get_features(const Ref<EditorExportPreset>
 		result.insert(E);
 		result.insert(E);
 	}
 	}
 
 
+	result.insert("template");
 	if (p_debug) {
 	if (p_debug) {
-		result.insert("debug");
+		result.insert("template_debug");
 	} else {
 	} else {
-		result.insert("release");
+		result.insert("template_release");
 	}
 	}
 
 
 	if (!p_preset->get_custom_features().is_empty()) {
 	if (!p_preset->get_custom_features().is_empty()) {

+ 2 - 2
editor/export/editor_export_platform_pc.cpp

@@ -81,8 +81,8 @@ bool EditorExportPlatformPC::has_valid_export_configuration(const Ref<EditorExpo
 
 
 	// Look for export templates (first official, and if defined custom templates).
 	// Look for export templates (first official, and if defined custom templates).
 	String arch = p_preset->get("binary_format/architecture");
 	String arch = p_preset->get("binary_format/architecture");
-	bool dvalid = exists_export_template(get_template_file_name("debug", arch), &err);
-	bool rvalid = exists_export_template(get_template_file_name("release", arch), &err);
+	bool dvalid = exists_export_template(get_template_file_name("template_debug", arch), &err);
+	bool rvalid = exists_export_template(get_template_file_name("template_release", arch), &err);
 
 
 	if (p_preset->get("custom_template/debug") != "") {
 	if (p_preset->get("custom_template/debug") != "") {
 		dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
 		dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));

+ 36 - 57
editor/plugins/gdextension_export_plugin.h

@@ -54,57 +54,36 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p
 
 
 	String entry_symbol = config->get_value("configuration", "entry_symbol");
 	String entry_symbol = config->get_value("configuration", "entry_symbol");
 
 
-	List<String> libraries;
-
-	config->get_section_keys("libraries", &libraries);
-
-	bool could_export = false;
-	for (const String &E : libraries) {
-		Vector<String> tags = E.split(".");
-		bool all_tags_met = true;
-		for (int i = 0; i < tags.size(); i++) {
-			String tag = tags[i].strip_edges();
-			if (!p_features.has(tag)) {
-				all_tags_met = false;
-				break;
-			}
-		}
-
-		if (all_tags_met) {
-			String library_path = config->get_value("libraries", E);
-			if (library_path.is_relative_path()) {
-				library_path = p_path.get_base_dir().path_join(library_path);
-			}
-			add_shared_object(library_path, tags);
-
-			if (p_features.has("iOS") && (library_path.ends_with(".a") || library_path.ends_with(".xcframework"))) {
-				String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n"
-										 "extern void add_ios_init_callback(void (*cb)());\n"
-										 "\n"
-										 "extern \"C\" void $ENTRY();\n"
-										 "void $ENTRY_init() {\n"
-										 "  if (&$ENTRY) register_dynamic_symbol((char *)\"$ENTRY\", (void *)$ENTRY);\n"
-										 "}\n"
-										 "struct $ENTRY_struct {\n"
-										 "  $ENTRY_struct() {\n"
-										 "    add_ios_init_callback($ENTRY_init);\n"
-										 "  }\n"
-										 "};\n"
-										 "$ENTRY_struct $ENTRY_struct_instance;\n\n";
-				additional_code = additional_code.replace("$ENTRY", entry_symbol);
-				add_ios_cpp_code(additional_code);
-
-				String linker_flags = "-Wl,-U,_" + entry_symbol;
-				add_ios_linker_flags(linker_flags);
-			}
-			could_export = true;
-			break;
+	PackedStringArray tags;
+	String library_path = NativeExtension::find_extension_library(
+			p_path, config, [p_features](String p_feature) { return p_features.has(p_feature); }, &tags);
+	if (!library_path.is_empty()) {
+		add_shared_object(library_path, tags);
+
+		if (p_features.has("iOS") && (library_path.ends_with(".a") || library_path.ends_with(".xcframework"))) {
+			String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n"
+									 "extern void add_ios_init_callback(void (*cb)());\n"
+									 "\n"
+									 "extern \"C\" void $ENTRY();\n"
+									 "void $ENTRY_init() {\n"
+									 "  if (&$ENTRY) register_dynamic_symbol((char *)\"$ENTRY\", (void *)$ENTRY);\n"
+									 "}\n"
+									 "struct $ENTRY_struct {\n"
+									 "  $ENTRY_struct() {\n"
+									 "    add_ios_init_callback($ENTRY_init);\n"
+									 "  }\n"
+									 "};\n"
+									 "$ENTRY_struct $ENTRY_struct_instance;\n\n";
+			additional_code = additional_code.replace("$ENTRY", entry_symbol);
+			add_ios_cpp_code(additional_code);
+
+			String linker_flags = "-Wl,-U,_" + entry_symbol;
+			add_ios_linker_flags(linker_flags);
 		}
 		}
-	}
-	if (!could_export) {
-		Vector<String> tags;
+	} else {
+		Vector<String> features_vector;
 		for (const String &E : p_features) {
 		for (const String &E : p_features) {
-			tags.append(E);
+			features_vector.append(E);
 		}
 		}
 		ERR_FAIL_MSG(vformat("No suitable library found. The libraries' tags referred to an invalid feature flag. Possible feature flags for your platform: %s", p_path, String(", ").join(tags)));
 		ERR_FAIL_MSG(vformat("No suitable library found. The libraries' tags referred to an invalid feature flag. Possible feature flags for your platform: %s", p_path, String(", ").join(tags)));
 	}
 	}
@@ -114,11 +93,11 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p
 		config->get_section_keys("dependencies", &dependencies);
 		config->get_section_keys("dependencies", &dependencies);
 	}
 	}
 
 
-	for (const String &E : libraries) {
-		Vector<String> tags = E.split(".");
+	for (const String &E : dependencies) {
+		Vector<String> dependency_tags = E.split(".");
 		bool all_tags_met = true;
 		bool all_tags_met = true;
-		for (int i = 0; i < tags.size(); i++) {
-			String tag = tags[i].strip_edges();
+		for (int i = 0; i < dependency_tags.size(); i++) {
+			String tag = dependency_tags[i].strip_edges();
 			if (!p_features.has(tag)) {
 			if (!p_features.has(tag)) {
 				all_tags_met = false;
 				all_tags_met = false;
 				break;
 				break;
@@ -128,12 +107,12 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p
 		if (all_tags_met) {
 		if (all_tags_met) {
 			Dictionary dependency = config->get_value("dependencies", E);
 			Dictionary dependency = config->get_value("dependencies", E);
 			for (const Variant *key = dependency.next(nullptr); key; key = dependency.next(key)) {
 			for (const Variant *key = dependency.next(nullptr); key; key = dependency.next(key)) {
-				String library_path = *key;
+				String dependency_path = *key;
 				String target_path = dependency[*key];
 				String target_path = dependency[*key];
-				if (library_path.is_relative_path()) {
-					library_path = p_path.get_base_dir().path_join(library_path);
+				if (dependency_path.is_relative_path()) {
+					dependency_path = p_path.get_base_dir().path_join(dependency_path);
 				}
 				}
-				add_shared_object(library_path, tags, target_path);
+				add_shared_object(dependency_path, dependency_tags, target_path);
 			}
 			}
 			break;
 			break;
 		}
 		}

+ 4 - 2
editor/project_settings_editor.cpp

@@ -274,10 +274,12 @@ void ProjectSettingsEditor::_add_feature_overrides() {
 	presets.insert("s3tc");
 	presets.insert("s3tc");
 	presets.insert("etc");
 	presets.insert("etc");
 	presets.insert("etc2");
 	presets.insert("etc2");
+	presets.insert("editor");
+	presets.insert("template_debug");
+	presets.insert("template_release");
 	presets.insert("debug");
 	presets.insert("debug");
 	presets.insert("release");
 	presets.insert("release");
-	presets.insert("editor");
-	presets.insert("standalone");
+	presets.insert("template");
 	presets.insert("32");
 	presets.insert("32");
 	presets.insert("64");
 	presets.insert("64");
 	presets.insert("movie");
 	presets.insert("movie");

+ 6 - 6
tests/core/os/test_os.h

@@ -97,14 +97,14 @@ TEST_CASE("[OS] Feature tags") {
 			OS::get_singleton()->has_feature("editor"),
 			OS::get_singleton()->has_feature("editor"),
 			"The binary has the \"editor\" feature tag.");
 			"The binary has the \"editor\" feature tag.");
 	CHECK_MESSAGE(
 	CHECK_MESSAGE(
-			!OS::get_singleton()->has_feature("standalone"),
-			"The binary does not have the \"standalone\" feature tag.");
+			!OS::get_singleton()->has_feature("template"),
+			"The binary does not have the \"template\" feature tag.");
 	CHECK_MESSAGE(
 	CHECK_MESSAGE(
-			OS::get_singleton()->has_feature("debug"),
-			"The binary has the \"debug\" feature tag.");
+			!OS::get_singleton()->has_feature("template_debug"),
+			"The binary does not have the \"template_debug\" feature tag.");
 	CHECK_MESSAGE(
 	CHECK_MESSAGE(
-			!OS::get_singleton()->has_feature("release"),
-			"The binary does not have the \"release\" feature tag.");
+			!OS::get_singleton()->has_feature("template_release"),
+			"The binary does not have the \"template_release\" feature tag.");
 }
 }
 
 
 TEST_CASE("[OS] Process ID") {
 TEST_CASE("[OS] Process ID") {