diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml
index 6f1c96ee536..b53fadb8c04 100644
--- a/doc/classes/EditorExportPlugin.xml
+++ b/doc/classes/EditorExportPlugin.xml
@@ -96,12 +96,22 @@
Adds a static lib from the given [code]path[/code] to the iOS project.
+
+
+
+
+ Adds file or directory matching [code]path[/code] to [code]PlugIns[/code] directory of macOS app bundle.
+ [b]Note:[/b] This is useful only for macOS exports.
+
+
- Adds a shared object with the given [code]tags[/code] and destination [code]path[/code].
+ Adds a shared object or a directory containing only shared objects with the given [code]tags[/code] and destination [code]path[/code].
+ [b]Note:[/b] In case of macOS exports, those shared objects will be added to [code]Frameworks[/code] directory of app bundle.
+ In case of a directory code-sign will error if you place non code object in directory.
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index bc8a9bff251..6957e2762b8 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -550,6 +550,14 @@ String EditorExportPlugin::get_ios_cpp_code() const {
return ios_cpp_code;
}
+void EditorExportPlugin::add_osx_plugin_file(const String &p_path) {
+ osx_plugin_files.push_back(p_path);
+}
+
+const Vector &EditorExportPlugin::get_osx_plugin_files() const {
+ return osx_plugin_files;
+}
+
void EditorExportPlugin::add_ios_project_static_lib(const String &p_path) {
ios_project_static_libs.push_back(p_path);
}
@@ -596,6 +604,7 @@ void EditorExportPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_ios_linker_flags", "flags"), &EditorExportPlugin::add_ios_linker_flags);
ClassDB::bind_method(D_METHOD("add_ios_bundle_file", "path"), &EditorExportPlugin::add_ios_bundle_file);
ClassDB::bind_method(D_METHOD("add_ios_cpp_code", "code"), &EditorExportPlugin::add_ios_cpp_code);
+ ClassDB::bind_method(D_METHOD("add_osx_plugin_file", "path"), &EditorExportPlugin::add_osx_plugin_file);
ClassDB::bind_method(D_METHOD("skip"), &EditorExportPlugin::skip);
BIND_VMETHOD(MethodInfo("_export_file", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::POOL_STRING_ARRAY, "features")));
diff --git a/editor/editor_export.h b/editor/editor_export.h
index 2662e29d085..026b906078a 100644
--- a/editor/editor_export.h
+++ b/editor/editor_export.h
@@ -290,6 +290,8 @@ class EditorExportPlugin : public Reference {
Vector ios_bundle_files;
String ios_cpp_code;
+ Vector osx_plugin_files;
+
_FORCE_INLINE_ void _clear() {
shared_objects.clear();
extra_files.clear();
@@ -303,6 +305,7 @@ class EditorExportPlugin : public Reference {
ios_plist_content = "";
ios_linker_flags = "";
ios_cpp_code = "";
+ osx_plugin_files.clear();
}
void _export_file_script(const String &p_path, const String &p_type, const PoolVector &p_features);
@@ -324,6 +327,8 @@ protected:
void add_ios_bundle_file(const String &p_path);
void add_ios_cpp_code(const String &p_code);
+ void add_osx_plugin_file(const String &p_path);
+
void skip();
virtual void _export_file(const String &p_path, const String &p_type, const Set &p_features);
@@ -340,6 +345,8 @@ public:
Vector get_ios_bundle_files() const;
String get_ios_cpp_code() const;
+ const Vector &get_osx_plugin_files() const;
+
EditorExportPlugin();
};
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 2ee2ecd995d..9ae208b88e0 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -57,6 +57,14 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
Error _notarize(const Ref &p_preset, const String &p_path);
Error _code_sign(const Ref &p_preset, const String &p_path, const String &p_ent_path);
+ Error _code_sign_directory(const Ref &p_preset, const String &p_path, const String &p_ent_path,
+ bool p_should_error_on_non_code = true);
+ Error _copy_and_sign_files(DirAccessRef &dir_access, const String &p_src_path, const String &p_in_app_path,
+ bool p_sign_enabled, const Ref &p_preset, const String &p_ent_path,
+ bool p_should_error_on_non_code_sign);
+ Error _export_osx_plugins_for(Ref p_editor_export_plugin, const String &p_app_path_name,
+ DirAccessRef &dir_access, bool p_sign_enabled, const Ref &p_preset,
+ const String &p_ent_path);
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
@@ -528,6 +536,101 @@ Error EditorExportPlatformOSX::_code_sign(const Ref &p_prese
return OK;
}
+Error EditorExportPlatformOSX::_code_sign_directory(const Ref &p_preset, const String &p_path,
+ const String &p_ent_path, bool p_should_error_on_non_code) {
+#ifdef OSX_ENABLED
+ static Vector extensions_to_sign;
+
+ if (extensions_to_sign.empty()) {
+ extensions_to_sign.push_back("dylib");
+ extensions_to_sign.push_back("framework");
+ }
+
+ Error dir_access_error;
+ DirAccessRef dir_access{ DirAccess::open(p_path, &dir_access_error) };
+
+ if (dir_access_error != OK) {
+ return dir_access_error;
+ }
+
+ dir_access->list_dir_begin();
+ String current_file{ dir_access->get_next() };
+ while (!current_file.empty()) {
+ String current_file_path{ p_path.plus_file(current_file) };
+
+ if (current_file == ".." || current_file == ".") {
+ current_file = dir_access->get_next();
+ continue;
+ }
+
+ if (extensions_to_sign.find(current_file.get_extension()) > -1) {
+ Error code_sign_error{ _code_sign(p_preset, current_file_path, p_ent_path) };
+ if (code_sign_error != OK) {
+ return code_sign_error;
+ }
+ } else if (dir_access->current_is_dir()) {
+ Error code_sign_error{ _code_sign_directory(p_preset, current_file_path, p_ent_path, p_should_error_on_non_code) };
+ if (code_sign_error != OK) {
+ return code_sign_error;
+ }
+ } else if (p_should_error_on_non_code) {
+ ERR_PRINT(vformat("Cannot sign file %s.", current_file));
+ return Error::FAILED;
+ }
+
+ current_file = dir_access->get_next();
+ }
+#endif
+
+ return OK;
+}
+
+Error EditorExportPlatformOSX::_copy_and_sign_files(DirAccessRef &dir_access, const String &p_src_path,
+ const String &p_in_app_path, bool p_sign_enabled,
+ const Ref &p_preset, const String &p_ent_path,
+ bool p_should_error_on_non_code_sign) {
+ Error err{ OK };
+ if (dir_access->dir_exists(p_src_path)) {
+#ifndef UNIX_ENABLED
+ WARN_PRINT("Relative symlinks are not supported, exported " + p_src_path.get_file() + " might be broken!");
+#endif
+ print_verbose("export framework: " + p_src_path + " -> " + p_in_app_path);
+ err = dir_access->make_dir_recursive(p_in_app_path);
+ if (err == OK) {
+ err = dir_access->copy_dir(p_src_path, p_in_app_path, -1, true);
+ }
+ } else {
+ print_verbose("export dylib: " + p_src_path + " -> " + p_in_app_path);
+ err = dir_access->copy(p_src_path, p_in_app_path);
+ }
+ if (err == OK && p_sign_enabled) {
+ if (dir_access->dir_exists(p_src_path) && p_src_path.get_extension().empty()) {
+ // If it is a directory, find and sign all dynamic libraries.
+ err = _code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_should_error_on_non_code_sign);
+ } else {
+ err = _code_sign(p_preset, p_in_app_path, p_ent_path);
+ }
+ }
+ return err;
+}
+
+Error EditorExportPlatformOSX::_export_osx_plugins_for(Ref p_editor_export_plugin,
+ const String &p_app_path_name, DirAccessRef &dir_access,
+ bool p_sign_enabled, const Ref &p_preset,
+ const String &p_ent_path) {
+ Error error{ OK };
+ const Vector &osx_plugins{ p_editor_export_plugin->get_osx_plugin_files() };
+ for (int i = 0; i < osx_plugins.size(); ++i) {
+ String src_path{ ProjectSettings::get_singleton()->globalize_path(osx_plugins[i]) };
+ String path_in_app{ p_app_path_name + "/Contents/PlugIns/" + src_path.get_file() };
+ error = _copy_and_sign_files(dir_access, src_path, path_in_app, p_sign_enabled, p_preset, p_ent_path, false);
+ if (error != OK) {
+ break;
+ }
+ }
+ return error;
+}
+
Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) {
List args;
@@ -914,27 +1017,23 @@ Error EditorExportPlatformOSX::export_project(const Ref &p_p
}
if (err == OK) {
- DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < shared_objects.size(); i++) {
String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path);
- if (da->dir_exists(src_path)) {
-#ifndef UNIX_ENABLED
- WARN_PRINT("Relative symlinks are not supported, exported " + src_path.get_file() + " might be broken!");
-#endif
- print_verbose("export framework: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
- err = da->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
- if (err == OK) {
- err = da->copy_dir(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), -1, true);
- }
- } else {
- print_verbose("export dylib: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
- err = da->copy(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
- }
- if (err == OK && sign_enabled) {
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), ent_path);
+ String path_in_app{ tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file() };
+ err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, true);
+ if (err != OK) {
+ break;
+ }
+ }
+
+ Vector[> export_plugins{ EditorExport::get_singleton()->get_export_plugins() };
+ for (int i = 0; i < export_plugins.size(); ++i) {
+ err = _export_osx_plugins_for(export_plugins[i], tmp_app_path_name, da, sign_enabled, p_preset, ent_path);
+ if (err != OK) {
+ break;
}
}
- memdelete(da);
}
if (sign_enabled) {
]