Browse Source

Merge pull request #66952 from bruvzg/macos_net_export

Fix macOS .NET export.
Rémi Verschelde 2 years ago
parent
commit
f2248969db

+ 1 - 0
editor/export/editor_export_platform.h

@@ -144,6 +144,7 @@ public:
 	};
 	};
 
 
 	virtual Ref<EditorExportPreset> create_preset();
 	virtual Ref<EditorExportPreset> create_preset();
+	virtual bool is_executable(const String &p_path) const { return false; }
 
 
 	virtual void clear_messages() { messages.clear(); }
 	virtual void clear_messages() { messages.clear(); }
 	virtual void add_message(ExportMessageType p_type, const String &p_category, const String &p_message) {
 	virtual void add_message(ExportMessageType p_type, const String &p_category, const String &p_message) {

+ 4 - 2
editor/export/editor_export_platform_pc.cpp

@@ -185,10 +185,12 @@ Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset>
 			String src_path = ProjectSettings::get_singleton()->globalize_path(so_files[i].path);
 			String src_path = ProjectSettings::get_singleton()->globalize_path(so_files[i].path);
 			String target_path;
 			String target_path;
 			if (so_files[i].target.is_empty()) {
 			if (so_files[i].target.is_empty()) {
-				target_path = p_path.get_base_dir().path_join(src_path.get_file());
+				target_path = p_path.get_base_dir();
 			} else {
 			} else {
-				target_path = p_path.get_base_dir().path_join(so_files[i].target).path_join(src_path.get_file());
+				target_path = p_path.get_base_dir().path_join(so_files[i].target);
+				da->make_dir_recursive(target_path);
 			}
 			}
+			target_path = target_path.path_join(src_path.get_file());
 
 
 			if (da->dir_exists(src_path)) {
 			if (da->dir_exists(src_path)) {
 				err = da->make_dir_recursive(target_path);
 				err = da->make_dir_recursive(target_path);

+ 71 - 47
modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs

@@ -17,6 +17,8 @@ namespace GodotTools.Export
 {
 {
     public partial class ExportPlugin : EditorExportPlugin
     public partial class ExportPlugin : EditorExportPlugin
     {
     {
+        private List<string> _tempFolders = new List<string>();
+
         public void RegisterExportSettings()
         public void RegisterExportSettings()
         {
         {
             // TODO: These would be better as export preset options, but that doesn't seem to be supported yet
             // TODO: These would be better as export preset options, but that doesn't seem to be supported yet
@@ -111,62 +113,78 @@ namespace GodotTools.Export
 
 
             string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
             string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
 
 
-            // TODO: This works for now, as we only implemented support for x86 family desktop so far, but it needs to be fixed
-            string arch = features.Contains("x86_64") ? "x86_64" : "x86";
-
-            string ridOS = DetermineRuntimeIdentifierOS(platform);
-            string ridArch = DetermineRuntimeIdentifierArch(arch);
-            string runtimeIdentifier = $"{ridOS}-{ridArch}";
-
-            // Create temporary publish output directory
-
-            string publishOutputTempDir = Path.Combine(Path.GetTempPath(), "godot-publish-dotnet",
-                $"{Process.GetCurrentProcess().Id}-{buildConfig}-{runtimeIdentifier}");
-
-            if (!Directory.Exists(publishOutputTempDir))
-                Directory.CreateDirectory(publishOutputTempDir);
-
-            // Execute dotnet publish
-
-            if (!BuildManager.PublishProjectBlocking(buildConfig, platform,
-                    runtimeIdentifier, publishOutputTempDir))
+            var archs = new List<string>();
+            if (features.Contains("x86_64"))
             {
             {
-                throw new InvalidOperationException("Failed to build project.");
+                archs.Add("x86_64");
             }
             }
-
-            string soExt = ridOS switch
+            else if (features.Contains("x86_32"))
             {
             {
-                OS.DotNetOS.Win or OS.DotNetOS.Win10 => "dll",
-                OS.DotNetOS.OSX or OS.DotNetOS.iOS => "dylib",
-                _ => "so"
-            };
-
-            if (!File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.dll"))
-                // NativeAOT shared library output
-                && !File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.{soExt}")))
+                archs.Add("x86_32");
+            }
+            else if (features.Contains("arm64"))
             {
             {
-                throw new NotSupportedException(
-                    "Publish succeeded but project assembly not found in the output directory");
+                archs.Add("arm64");
             }
             }
-
-            // Copy all files from the dotnet publish output directory to
-            // a data directory next to the Godot output executable.
-
-            string outputDataDir = Path.Combine(outputDir, DetermineDataDirNameForProject());
-
-            if (Directory.Exists(outputDataDir))
-                Directory.Delete(outputDataDir, recursive: true); // Clean first
-
-            Directory.CreateDirectory(outputDataDir);
-
-            foreach (string dir in Directory.GetDirectories(publishOutputTempDir, "*", SearchOption.AllDirectories))
+            else if (features.Contains("universal"))
             {
             {
-                Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(publishOutputTempDir.Length + 1)));
+                if (platform == OS.Platforms.MacOS)
+                {
+                    archs.Add("x86_64");
+                    archs.Add("arm64");
+                }
             }
             }
 
 
-            foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories))
+            foreach (var arch in archs)
             {
             {
-                File.Copy(file, Path.Combine(outputDataDir, file.Substring(publishOutputTempDir.Length + 1)));
+                string ridOS = DetermineRuntimeIdentifierOS(platform);
+                string ridArch = DetermineRuntimeIdentifierArch(arch);
+                string runtimeIdentifier = $"{ridOS}-{ridArch}";
+                string projectDataDirName = $"{DetermineDataDirNameForProject()}_{arch}";
+                if (platform == OS.Platforms.MacOS)
+                {
+                    projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName);
+                }
+
+                // Create temporary publish output directory
+
+                string publishOutputTempDir = Path.Combine(Path.GetTempPath(), "godot-publish-dotnet",
+                    $"{Process.GetCurrentProcess().Id}-{buildConfig}-{runtimeIdentifier}");
+
+                _tempFolders.Add(publishOutputTempDir);
+
+                if (!Directory.Exists(publishOutputTempDir))
+                    Directory.CreateDirectory(publishOutputTempDir);
+
+                // Execute dotnet publish
+
+                if (!BuildManager.PublishProjectBlocking(buildConfig, platform,
+                        runtimeIdentifier, publishOutputTempDir))
+                {
+                    throw new InvalidOperationException("Failed to build project.");
+                }
+
+                string soExt = ridOS switch
+                {
+                    OS.DotNetOS.Win or OS.DotNetOS.Win10 => "dll",
+                    OS.DotNetOS.OSX or OS.DotNetOS.iOS => "dylib",
+                    _ => "so"
+                };
+
+                if (!File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.dll"))
+                    // NativeAOT shared library output
+                    && !File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.{soExt}")))
+                {
+                    throw new NotSupportedException(
+                        "Publish succeeded but project assembly not found in the output directory");
+                }
+
+                // Add to the exported project shared object list.
+
+                foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories))
+                {
+                    AddSharedObject(file, tags: null, projectDataDirName);
+                }
             }
             }
         }
         }
 
 
@@ -198,6 +216,12 @@ namespace GodotTools.Export
             if (Directory.Exists(aotTempDir))
             if (Directory.Exists(aotTempDir))
                 Directory.Delete(aotTempDir, recursive: true);
                 Directory.Delete(aotTempDir, recursive: true);
 
 
+            foreach (string folder in _tempFolders)
+            {
+                Directory.Delete(folder, recursive: true);
+            }
+            _tempFolders.Clear();
+
             // TODO: The following is just a workaround until the export plugins can be made to abort with errors
             // TODO: The following is just a workaround until the export plugins can be made to abort with errors
 
 
             // We check for empty as well, because it's set to empty after hot-reloading
             // We check for empty as well, because it's set to empty after hot-reloading

+ 16 - 136
modules/mono/godotsharp_dirs.cpp

@@ -94,138 +94,63 @@ String _get_mono_user_dir() {
 
 
 class _GodotSharpDirs {
 class _GodotSharpDirs {
 public:
 public:
-	String res_data_dir;
 	String res_metadata_dir;
 	String res_metadata_dir;
-	String res_config_dir;
-	String res_temp_dir;
-	String res_temp_assemblies_base_dir;
 	String res_temp_assemblies_dir;
 	String res_temp_assemblies_dir;
 	String mono_user_dir;
 	String mono_user_dir;
-	String mono_logs_dir;
-
-	String api_assemblies_base_dir;
 	String api_assemblies_dir;
 	String api_assemblies_dir;
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-	String mono_solutions_dir;
 	String build_logs_dir;
 	String build_logs_dir;
-
 	String data_editor_tools_dir;
 	String data_editor_tools_dir;
-#else
-	// Equivalent of res_assemblies_dir, but in the data directory rather than in 'res://'.
-	// Only defined on export templates. Used when exporting assemblies outside of PCKs.
-	String data_game_assemblies_dir;
-#endif
-
-	String data_mono_etc_dir;
-	String data_mono_lib_dir;
-
-#ifdef WINDOWS_ENABLED
-	String data_mono_bin_dir;
 #endif
 #endif
 
 
 private:
 private:
 	_GodotSharpDirs() {
 	_GodotSharpDirs() {
-		res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().path_join("mono");
+		String res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().path_join("mono");
 		res_metadata_dir = res_data_dir.path_join("metadata");
 		res_metadata_dir = res_data_dir.path_join("metadata");
-		res_config_dir = res_data_dir.path_join("etc").path_join("mono");
 
 
 		// TODO use paths from csproj
 		// TODO use paths from csproj
-		res_temp_dir = res_data_dir.path_join("temp");
-		res_temp_assemblies_base_dir = res_temp_dir.path_join("bin");
-		res_temp_assemblies_dir = res_temp_assemblies_base_dir.path_join(_get_expected_build_config());
-
-		api_assemblies_base_dir = res_data_dir.path_join("assemblies");
+		res_temp_assemblies_dir = res_data_dir.path_join("temp").path_join("bin").path_join(_get_expected_build_config());
 
 
 #ifdef WEB_ENABLED
 #ifdef WEB_ENABLED
 		mono_user_dir = "user://";
 		mono_user_dir = "user://";
 #else
 #else
 		mono_user_dir = _get_mono_user_dir();
 		mono_user_dir = _get_mono_user_dir();
 #endif
 #endif
-		mono_logs_dir = mono_user_dir.path_join("mono_logs");
-
-#ifdef TOOLS_ENABLED
-		mono_solutions_dir = mono_user_dir.path_join("solutions");
-		build_logs_dir = mono_user_dir.path_join("build_logs");
-
-		String base_path = ProjectSettings::get_singleton()->globalize_path("res://");
-#endif
 
 
 		String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
 		String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
+		String res_dir = OS::get_singleton()->get_bundle_resource_dir();
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-
 		String data_dir_root = exe_dir.path_join("GodotSharp");
 		String data_dir_root = exe_dir.path_join("GodotSharp");
 		data_editor_tools_dir = data_dir_root.path_join("Tools");
 		data_editor_tools_dir = data_dir_root.path_join("Tools");
-		api_assemblies_base_dir = data_dir_root.path_join("Api");
-
-		String data_mono_root_dir = data_dir_root.path_join("Mono");
-		data_mono_etc_dir = data_mono_root_dir.path_join("etc");
-
-#ifdef ANDROID_ENABLED
-		data_mono_lib_dir = gdmono::android::support::get_app_native_lib_dir();
-#else
-		data_mono_lib_dir = data_mono_root_dir.path_join("lib");
-#endif
-
-#ifdef WINDOWS_ENABLED
-		data_mono_bin_dir = data_mono_root_dir.path_join("bin");
-#endif
-
+		String api_assemblies_base_dir = data_dir_root.path_join("Api");
+		build_logs_dir = mono_user_dir.path_join("build_logs");
 #ifdef MACOS_ENABLED
 #ifdef MACOS_ENABLED
 		if (!DirAccess::exists(data_editor_tools_dir)) {
 		if (!DirAccess::exists(data_editor_tools_dir)) {
-			data_editor_tools_dir = exe_dir.path_join("../Resources/GodotSharp/Tools");
+			data_editor_tools_dir = res_dir.path_join("GodotSharp").path_join("Tools");
 		}
 		}
-
 		if (!DirAccess::exists(api_assemblies_base_dir)) {
 		if (!DirAccess::exists(api_assemblies_base_dir)) {
-			api_assemblies_base_dir = exe_dir.path_join("../Resources/GodotSharp/Api");
-		}
-
-		if (!DirAccess::exists(data_mono_root_dir)) {
-			data_mono_etc_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/etc");
-			data_mono_lib_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/lib");
+			api_assemblies_base_dir = res_dir.path_join("GodotSharp").path_join("Api");
 		}
 		}
 #endif
 #endif
-
-#else
-
+		api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config());
+#else // TOOLS_ENABLED
+		String arch = Engine::get_singleton()->get_architecture_name();
 		String appname = ProjectSettings::get_singleton()->get("application/config/name");
 		String appname = ProjectSettings::get_singleton()->get("application/config/name");
 		String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
 		String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
-		String data_dir_root = exe_dir.path_join("data_" + appname_safe);
+		String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
 		if (!DirAccess::exists(data_dir_root)) {
 		if (!DirAccess::exists(data_dir_root)) {
-			data_dir_root = exe_dir.path_join("data_Godot");
+			data_dir_root = exe_dir.path_join("data_Godot_" + arch);
 		}
 		}
-
-		String data_mono_root_dir = data_dir_root.path_join("Mono");
-		data_mono_etc_dir = data_mono_root_dir.path_join("etc");
-
-#ifdef ANDROID_ENABLED
-		data_mono_lib_dir = gdmono::android::support::get_app_native_lib_dir();
-#else
-		data_mono_lib_dir = data_mono_root_dir.path_join("lib");
-		data_game_assemblies_dir = data_dir_root.path_join("Assemblies");
-#endif
-
-#ifdef WINDOWS_ENABLED
-		data_mono_bin_dir = data_mono_root_dir.path_join("bin");
-#endif
-
 #ifdef MACOS_ENABLED
 #ifdef MACOS_ENABLED
-		if (!DirAccess::exists(data_mono_root_dir)) {
-			data_mono_etc_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/etc");
-			data_mono_lib_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/lib");
+		if (!DirAccess::exists(data_dir_root)) {
+			data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch);
 		}
 		}
-
-		if (!DirAccess::exists(data_game_assemblies_dir)) {
-			data_game_assemblies_dir = exe_dir.path_join("../Resources/GodotSharp/Assemblies");
+		if (!DirAccess::exists(data_dir_root)) {
+			data_dir_root = res_dir.path_join("data_Godot_" + arch);
 		}
 		}
 #endif
 #endif
-
-#endif
-
-#ifdef TOOLS_ENABLED
-		api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config());
-#else
 		api_assemblies_dir = data_dir_root;
 		api_assemblies_dir = data_dir_root;
 #endif
 #endif
 	}
 	}
@@ -237,26 +162,10 @@ public:
 	}
 	}
 };
 };
 
 
-String get_res_data_dir() {
-	return _GodotSharpDirs::get_singleton().res_data_dir;
-}
-
 String get_res_metadata_dir() {
 String get_res_metadata_dir() {
 	return _GodotSharpDirs::get_singleton().res_metadata_dir;
 	return _GodotSharpDirs::get_singleton().res_metadata_dir;
 }
 }
 
 
-String get_res_config_dir() {
-	return _GodotSharpDirs::get_singleton().res_config_dir;
-}
-
-String get_res_temp_dir() {
-	return _GodotSharpDirs::get_singleton().res_temp_dir;
-}
-
-String get_res_temp_assemblies_base_dir() {
-	return _GodotSharpDirs::get_singleton().res_temp_assemblies_base_dir;
-}
-
 String get_res_temp_assemblies_dir() {
 String get_res_temp_assemblies_dir() {
 	return _GodotSharpDirs::get_singleton().res_temp_assemblies_dir;
 	return _GodotSharpDirs::get_singleton().res_temp_assemblies_dir;
 }
 }
@@ -265,23 +174,11 @@ String get_api_assemblies_dir() {
 	return _GodotSharpDirs::get_singleton().api_assemblies_dir;
 	return _GodotSharpDirs::get_singleton().api_assemblies_dir;
 }
 }
 
 
-String get_api_assemblies_base_dir() {
-	return _GodotSharpDirs::get_singleton().api_assemblies_base_dir;
-}
-
 String get_mono_user_dir() {
 String get_mono_user_dir() {
 	return _GodotSharpDirs::get_singleton().mono_user_dir;
 	return _GodotSharpDirs::get_singleton().mono_user_dir;
 }
 }
 
 
-String get_mono_logs_dir() {
-	return _GodotSharpDirs::get_singleton().mono_logs_dir;
-}
-
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-String get_mono_solutions_dir() {
-	return _GodotSharpDirs::get_singleton().mono_solutions_dir;
-}
-
 String get_build_logs_dir() {
 String get_build_logs_dir() {
 	return _GodotSharpDirs::get_singleton().build_logs_dir;
 	return _GodotSharpDirs::get_singleton().build_logs_dir;
 }
 }
@@ -289,23 +186,6 @@ String get_build_logs_dir() {
 String get_data_editor_tools_dir() {
 String get_data_editor_tools_dir() {
 	return _GodotSharpDirs::get_singleton().data_editor_tools_dir;
 	return _GodotSharpDirs::get_singleton().data_editor_tools_dir;
 }
 }
-#else
-String get_data_game_assemblies_dir() {
-	return _GodotSharpDirs::get_singleton().data_game_assemblies_dir;
-}
 #endif
 #endif
 
 
-String get_data_mono_etc_dir() {
-	return _GodotSharpDirs::get_singleton().data_mono_etc_dir;
-}
-
-String get_data_mono_lib_dir() {
-	return _GodotSharpDirs::get_singleton().data_mono_lib_dir;
-}
-
-#ifdef WINDOWS_ENABLED
-String get_data_mono_bin_dir() {
-	return _GodotSharpDirs::get_singleton().data_mono_bin_dir;
-}
-#endif
 } // namespace GodotSharpDirs
 } // namespace GodotSharpDirs

+ 0 - 16
modules/mono/godotsharp_dirs.h

@@ -35,34 +35,18 @@
 
 
 namespace GodotSharpDirs {
 namespace GodotSharpDirs {
 
 
-String get_res_data_dir();
 String get_res_metadata_dir();
 String get_res_metadata_dir();
-String get_res_config_dir();
-String get_res_temp_dir();
-String get_res_temp_assemblies_base_dir();
 String get_res_temp_assemblies_dir();
 String get_res_temp_assemblies_dir();
 
 
 String get_api_assemblies_dir();
 String get_api_assemblies_dir();
-String get_api_assemblies_base_dir();
 
 
 String get_mono_user_dir();
 String get_mono_user_dir();
-String get_mono_logs_dir();
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-String get_mono_solutions_dir();
 String get_build_logs_dir();
 String get_build_logs_dir();
-
 String get_data_editor_tools_dir();
 String get_data_editor_tools_dir();
-#else
-String get_data_game_assemblies_dir();
 #endif
 #endif
 
 
-String get_data_mono_etc_dir();
-String get_data_mono_lib_dir();
-
-#ifdef WINDOWS_ENABLED
-String get_data_mono_bin_dir();
-#endif
 } // namespace GodotSharpDirs
 } // namespace GodotSharpDirs
 
 
 #endif // GODOTSHARP_DIRS_H
 #endif // GODOTSHARP_DIRS_H

+ 64 - 67
platform/macos/export/export_plugin.cpp

@@ -31,6 +31,8 @@
 #include "export_plugin.h"
 #include "export_plugin.h"
 
 
 #include "codesign.h"
 #include "codesign.h"
+#include "lipo.h"
+#include "macho.h"
 
 
 #include "core/string/translation.h"
 #include "core/string/translation.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
@@ -754,6 +756,7 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres
 	if (extensions_to_sign.is_empty()) {
 	if (extensions_to_sign.is_empty()) {
 		extensions_to_sign.push_back("dylib");
 		extensions_to_sign.push_back("dylib");
 		extensions_to_sign.push_back("framework");
 		extensions_to_sign.push_back("framework");
+		extensions_to_sign.push_back("");
 	}
 	}
 
 
 	Error dir_access_error;
 	Error dir_access_error;
@@ -778,6 +781,10 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres
 			if (code_sign_error != OK) {
 			if (code_sign_error != OK) {
 				return code_sign_error;
 				return code_sign_error;
 			}
 			}
+			if (is_executable(current_file_path)) {
+				// chmod with 0755 if the file is executable.
+				FileAccess::set_unix_permissions(current_file_path, 0755);
+			}
 		} else if (dir_access->current_is_dir()) {
 		} 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) };
 			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) {
 			if (code_sign_error != OK) {
@@ -799,6 +806,14 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
 		const String &p_in_app_path, bool p_sign_enabled,
 		const String &p_in_app_path, bool p_sign_enabled,
 		const Ref<EditorExportPreset> &p_preset, const String &p_ent_path,
 		const Ref<EditorExportPreset> &p_preset, const String &p_ent_path,
 		bool p_should_error_on_non_code_sign) {
 		bool p_should_error_on_non_code_sign) {
+	static Vector<String> extensions_to_sign;
+
+	if (extensions_to_sign.is_empty()) {
+		extensions_to_sign.push_back("dylib");
+		extensions_to_sign.push_back("framework");
+		extensions_to_sign.push_back("");
+	}
+
 	Error err{ OK };
 	Error err{ OK };
 	if (dir_access->dir_exists(p_src_path)) {
 	if (dir_access->dir_exists(p_src_path)) {
 #ifndef UNIX_ENABLED
 #ifndef UNIX_ENABLED
@@ -818,7 +833,13 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
 			// If it is a directory, find and sign all dynamic libraries.
 			// 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);
 			err = _code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_should_error_on_non_code_sign);
 		} else {
 		} else {
-			err = _code_sign(p_preset, p_in_app_path, p_ent_path, false);
+			if (extensions_to_sign.find(p_in_app_path.get_extension()) > -1) {
+				err = _code_sign(p_preset, p_in_app_path, p_ent_path, false);
+			}
+			if (is_executable(p_in_app_path)) {
+				// chmod with 0755 if the file is executable.
+				FileAccess::set_unix_permissions(p_in_app_path, 0755);
+			}
 		}
 		}
 	}
 	}
 	return err;
 	return err;
@@ -877,6 +898,17 @@ Error EditorExportPlatformMacOS::_create_dmg(const String &p_dmg_path, const Str
 	return OK;
 	return OK;
 }
 }
 
 
+bool EditorExportPlatformMacOS::is_shbang(const String &p_path) const {
+	Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
+	ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path));
+	uint16_t magic = fb->get_16();
+	return (magic == 0x2123);
+}
+
+bool EditorExportPlatformMacOS::is_executable(const String &p_path) const {
+	return MachO::is_macho(p_path) || LipO::is_lipo(p_path) || is_shbang(p_path);
+}
+
 Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
 Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
 	Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
 	Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
 	if (f.is_null()) {
 	if (f.is_null()) {
@@ -1158,11 +1190,8 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
 
 
 	// Now process our template.
 	// Now process our template.
 	bool found_binary = false;
 	bool found_binary = false;
-	Vector<String> dylibs_found;
 
 
 	while (ret == UNZ_OK && err == OK) {
 	while (ret == UNZ_OK && err == OK) {
-		bool is_execute = false;
-
 		// Get filename.
 		// Get filename.
 		unz_file_info info;
 		unz_file_info info;
 		char fname[16384];
 		char fname[16384];
@@ -1219,7 +1248,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
 				continue; // skip
 				continue; // skip
 			}
 			}
 			found_binary = true;
 			found_binary = true;
-			is_execute = true;
 			file = "Contents/MacOS/" + pkg_name;
 			file = "Contents/MacOS/" + pkg_name;
 		}
 		}
 
 
@@ -1251,25 +1279,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
 		}
 		}
 
 
 		if (data.size() > 0) {
 		if (data.size() > 0) {
-			if (file.find("/data.mono.macos.release_debug." + architecture + "/") != -1) {
-				if (!p_debug) {
-					ret = unzGoToNextFile(src_pkg_zip);
-					continue; // skip
-				}
-				file = file.replace("/data.mono.macos.release_debug." + architecture + "/", "/GodotSharp/");
-			}
-			if (file.find("/data.mono.macos.release." + architecture + "/") != -1) {
-				if (p_debug) {
-					ret = unzGoToNextFile(src_pkg_zip);
-					continue; // skip
-				}
-				file = file.replace("/data.mono.macos.release." + architecture + "/", "/GodotSharp/");
-			}
-
-			if (file.ends_with(".dylib")) {
-				dylibs_found.push_back(file);
-			}
-
 			print_verbose("ADDING: " + file + " size: " + itos(data.size()));
 			print_verbose("ADDING: " + file + " size: " + itos(data.size()));
 
 
 			// Write it into our application bundle.
 			// Write it into our application bundle.
@@ -1285,7 +1294,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
 				if (f.is_valid()) {
 				if (f.is_valid()) {
 					f->store_buffer(data.ptr(), data.size());
 					f->store_buffer(data.ptr(), data.size());
 					f.unref();
 					f.unref();
-					if (is_execute) {
+					if (is_executable(file)) {
 						// chmod with 0755 if the file is executable.
 						// chmod with 0755 if the file is executable.
 						FileAccess::set_unix_permissions(file, 0755);
 						FileAccess::set_unix_permissions(file, 0755);
 					}
 					}
@@ -1324,12 +1333,35 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
 			return ERR_SKIP;
 			return ERR_SKIP;
 		}
 		}
 
 
+		// See if we can code sign our new package.
+		bool sign_enabled = (p_preset->get("codesign/codesign").operator int() > 0);
+		bool ad_hoc = false;
+		int codesign_tool = p_preset->get("codesign/codesign");
+		switch (codesign_tool) {
+			case 1: { // built-in ad-hoc
+				ad_hoc = true;
+			} break;
+			case 2: { // "rcodesign"
+				ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
+			} break;
+#ifdef MACOS_ENABLED
+			case 3: { // "codesign"
+				ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-");
+			} break;
+#endif
+			default: {
+			};
+		}
+
 		String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck";
 		String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck";
 		Vector<SharedObject> shared_objects;
 		Vector<SharedObject> shared_objects;
 		err = save_pack(p_preset, p_debug, pack_path, &shared_objects);
 		err = save_pack(p_preset, p_debug, pack_path, &shared_objects);
 
 
-		// See if we can code sign our new package.
-		bool sign_enabled = (p_preset->get("codesign/codesign").operator int() > 0);
+		bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation");
+		if (!shared_objects.is_empty() && sign_enabled && ad_hoc && !lib_validation) {
+			add_message(EXPORT_MESSAGE_INFO, TTR("Entitlements Modified"), TTR("Ad-hoc signed applications require the 'Disable Library Validation' entitlement to load dynamic libraries."));
+			lib_validation = true;
+		}
 
 
 		String ent_path = p_preset->get("codesign/entitlements/custom_file");
 		String ent_path = p_preset->get("codesign/entitlements/custom_file");
 		String hlp_ent_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + "_helper.entitlements");
 		String hlp_ent_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + "_helper.entitlements");
@@ -1365,7 +1397,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
 					}
 					}
 				}
 				}
 
 
-				if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) {
+				if (lib_validation) {
 					ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>");
 					ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>");
 					ent_f->store_line("<true/>");
 					ent_f->store_line("<true/>");
 				}
 				}
@@ -1495,32 +1527,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
 			}
 			}
 		}
 		}
 
 
-		bool ad_hoc = false;
-		int codesign_tool = p_preset->get("codesign/codesign");
-		switch (codesign_tool) {
-			case 1: { // built-in ad-hoc
-				ad_hoc = true;
-			} break;
-			case 2: { // "rcodesign"
-				ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
-			} break;
-#ifdef MACOS_ENABLED
-			case 3: { // "codesign"
-				ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-");
-			} break;
-#endif
-			default: {
-			};
-		}
-
-		if (err == OK) {
-			bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation");
-			if ((!dylibs_found.is_empty() || !shared_objects.is_empty()) && sign_enabled && ad_hoc && !lib_validation) {
-				add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Ad-hoc signed applications require the 'Disable Library Validation' entitlement to load dynamic libraries."));
-				err = ERR_CANT_CREATE;
-			}
-		}
-
 		if (err == OK) {
 		if (err == OK) {
 			Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 			Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 			for (int i = 0; i < shared_objects.size(); i++) {
 			for (int i = 0; i < shared_objects.size(); i++) {
@@ -1529,8 +1535,9 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
 					String path_in_app = tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file();
 					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);
 					err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, true);
 				} else {
 				} else {
-					String path_in_app = tmp_app_path_name.path_join(shared_objects[i].target).path_join(src_path.get_file());
-					err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, false);
+					String path_in_app = tmp_app_path_name.path_join(shared_objects[i].target);
+					tmp_app_dir->make_dir_recursive(path_in_app);
+					err = _copy_and_sign_files(da, src_path, path_in_app.path_join(src_path.get_file()), sign_enabled, p_preset, ent_path, false);
 				}
 				}
 				if (err != OK) {
 				if (err != OK) {
 					break;
 					break;
@@ -1546,14 +1553,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
 			}
 			}
 		}
 		}
 
 
-		if (sign_enabled) {
-			for (int i = 0; i < dylibs_found.size(); i++) {
-				if (err == OK) {
-					err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path, false);
-				}
-			}
-		}
-
 		if (err == OK && sign_enabled) {
 		if (err == OK && sign_enabled) {
 			if (ep.step(TTR("Code signing bundle"), 2)) {
 			if (ep.step(TTR("Code signing bundle"), 2)) {
 				return ERR_SKIP;
 				return ERR_SKIP;
@@ -1683,8 +1682,6 @@ void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const Stri
 		} else if (da->current_is_dir()) {
 		} else if (da->current_is_dir()) {
 			_zip_folder_recursive(p_zip, p_root_path, p_folder.path_join(f), p_pkg_name);
 			_zip_folder_recursive(p_zip, p_root_path, p_folder.path_join(f), p_pkg_name);
 		} else {
 		} else {
-			bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)) || p_folder.ends_with("Helpers") || f.ends_with(".command");
-
 			OS::DateTime dt = OS::get_singleton()->get_datetime();
 			OS::DateTime dt = OS::get_singleton()->get_datetime();
 
 
 			zip_fileinfo zipfi;
 			zip_fileinfo zipfi;
@@ -1698,7 +1695,7 @@ void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const Stri
 			// 0100000: regular file type
 			// 0100000: regular file type
 			// 0000755: permissions rwxr-xr-x
 			// 0000755: permissions rwxr-xr-x
 			// 0000644: permissions rw-r--r--
 			// 0000644: permissions rw-r--r--
-			uint32_t _mode = (is_executable ? 0100755 : 0100644);
+			uint32_t _mode = (is_executable(dir.path_join(f)) ? 0100755 : 0100644);
 			zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
 			zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
 			zipfi.internal_fa = 0;
 			zipfi.internal_fa = 0;
 
 

+ 2 - 0
platform/macos/export/export_plugin.h

@@ -97,6 +97,7 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
 
 
 		return true;
 		return true;
 	}
 	}
+	bool is_shbang(const String &p_path) const;
 
 
 protected:
 protected:
 	virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
 	virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
@@ -108,6 +109,7 @@ public:
 	virtual String get_os_name() const override { return "macOS"; }
 	virtual String get_os_name() const override { return "macOS"; }
 	virtual Ref<Texture2D> get_logo() const override { return logo; }
 	virtual Ref<Texture2D> get_logo() const override { return logo; }
 
 
+	virtual bool is_executable(const String &p_path) const override;
 	virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
 	virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
 		List<String> list;
 		List<String> list;
 		if (use_dmg()) {
 		if (use_dmg()) {