Przeglądaj źródła

Merge pull request #80239 from bruvzg/macos_export_backports

[3.x] [macOS] Backport notarytool, provisioning profile and PKG export options.
Rémi Verschelde 2 lat temu
rodzic
commit
da5c95c5d8

+ 16 - 4
misc/dist/osx_template.app/Contents/Info.plist

@@ -10,8 +10,6 @@
 	<string>$name</string>
 	<string>$name</string>
 	<key>CFBundleDisplayName</key>
 	<key>CFBundleDisplayName</key>
 	<string>$name</string>
 	<string>$name</string>
-	<key>CFBundleGetInfoString</key>
-	<string>$info</string>
 	<key>CFBundleIconFile</key>
 	<key>CFBundleIconFile</key>
 	<string>icon.icns</string>
 	<string>icon.icns</string>
 	<key>CFBundleIdentifier</key>
 	<key>CFBundleIdentifier</key>
@@ -26,6 +24,20 @@
 	<string>$signature</string>
 	<string>$signature</string>
 	<key>CFBundleVersion</key>
 	<key>CFBundleVersion</key>
 	<string>$version</string>
 	<string>$version</string>
+	<key>DTPlatformBuild</key>
+	<string>$platfbuild</string>
+	<key>DTPlatformName</key>
+	<string>macosx</string>
+	<key>DTPlatformVersion</key>
+	<string>$sdkver</string>
+	<key>DTSDKBuild</key>
+	<string>$sdkbuild</string>
+	<key>DTSDKName</key>
+	<string>$sdkname</string>
+	<key>DTXcode</key>
+	<string>$xcodever</string>
+	<key>DTXcodeBuild</key>
+	<string>$xcodebuild</string>
 $usage_descriptions
 $usage_descriptions
 	<key>NSHumanReadableCopyright</key>
 	<key>NSHumanReadableCopyright</key>
 	<string>$copyright</string>
 	<string>$copyright</string>
@@ -38,11 +50,11 @@ $usage_descriptions
 	<key>LSApplicationCategoryType</key>
 	<key>LSApplicationCategoryType</key>
 	<string>public.app-category.$app_category</string>
 	<string>public.app-category.$app_category</string>
 	<key>LSMinimumSystemVersion</key>
 	<key>LSMinimumSystemVersion</key>
-	<string>10.12</string>
+	<string>$min_version</string>
 	<key>LSMinimumSystemVersionByArchitecture</key>
 	<key>LSMinimumSystemVersionByArchitecture</key>
 	<dict>
 	<dict>
 		<key>x86_64</key>
 		<key>x86_64</key>
-		<string>10.12</string>
+		<string>$min_version</string>
 	</dict>
 	</dict>
 	<key>NSHighResolutionCapable</key>
 	<key>NSHighResolutionCapable</key>
 $highres
 $highres

+ 338 - 34
platform/osx/export/export.cpp

@@ -68,6 +68,7 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
 	Error _export_osx_plugins_for(Ref<EditorExportPlugin> p_editor_export_plugin, const String &p_app_path_name,
 	Error _export_osx_plugins_for(Ref<EditorExportPlugin> p_editor_export_plugin, const String &p_app_path_name,
 			DirAccessRef &dir_access, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset,
 			DirAccessRef &dir_access, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset,
 			const String &p_ent_path);
 			const String &p_ent_path);
+	Error _create_pkg(const Ref<EditorExportPreset> &p_preset, const String &p_pkg_path, const String &p_app_path_name);
 	Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
 	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);
 	void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
 
 
@@ -112,11 +113,29 @@ public:
 
 
 	virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
 	virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
 		List<String> list;
 		List<String> list;
-		if (use_dmg()) {
-			list.push_back("dmg");
+
+		if (p_preset.is_valid()) {
+			int dist_type = p_preset->get("export/distribution_type");
+			if (dist_type == 0) {
+#ifdef OSX_ENABLED
+				list.push_back("dmg");
+#endif
+				list.push_back("zip");
+#ifndef WINDOWS_ENABLED
+				list.push_back("app");
+#endif
+			} else if (dist_type == 1) {
+#ifdef OSX_ENABLED
+				list.push_back("dmg");
+#endif
+				list.push_back("zip");
+			} else if (dist_type == 2) {
+#ifdef OSX_ENABLED
+				list.push_back("pkg");
+#endif
+			}
 		}
 		}
-		list.push_back("zip");
-		list.push_back("app");
+
 		return list;
 		return list;
 	}
 	}
 	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
 	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
@@ -157,7 +176,7 @@ bool EditorExportPlatformOSX::get_option_visibility(const EditorExportPreset *p_
 		int codesign_tool = p_preset->get("codesign/codesign");
 		int codesign_tool = p_preset->get("codesign/codesign");
 		switch (codesign_tool) {
 		switch (codesign_tool) {
 			case 1: { // built-in ad-hoc
 			case 1: { // built-in ad-hoc
-				if (p_option == "codesign/identity" || p_option == "codesign/certificate_file" || p_option == "codesign/certificate_password" || p_option == "codesign/custom_options") {
+				if (p_option == "codesign/identity" || p_option == "codesign/certificate_file" || p_option == "codesign/certificate_password" || p_option == "codesign/custom_options" || p_option == "codesign/team_id") {
 					return false;
 					return false;
 				}
 				}
 			} break;
 			} break;
@@ -174,25 +193,53 @@ bool EditorExportPlatformOSX::get_option_visibility(const EditorExportPreset *p_
 			} break;
 			} break;
 #endif
 #endif
 			default: { // disabled
 			default: { // disabled
-				if (p_option == "codesign/identity" || p_option == "codesign/certificate_file" || p_option == "codesign/certificate_password" || p_option == "codesign/custom_options" || p_option.begins_with("codesign/entitlements")) {
+				if (p_option == "codesign/identity" || p_option == "codesign/certificate_file" || p_option == "codesign/certificate_password" || p_option == "codesign/custom_options" || p_option.begins_with("codesign/entitlements") || p_option == "codesign/team_id") {
 					return false;
 					return false;
 				}
 				}
 			} break;
 			} break;
 		}
 		}
 
 
+		// Distribution type.
+		int dist_type = p_preset->get("export/distribution_type");
+		if (dist_type != 2 && p_option == "codesign/installer_identity") {
+			return false;
+		}
+
+		if (dist_type == 2 && p_option.begins_with("notarization/")) {
+			return false;
+		}
+
+		if (dist_type != 2 && p_option == "codesign/provisioning_profile") {
+			return false;
+		}
+
+		String custom_prof = p_preset->get("codesign/entitlements/custom_file");
+		if (custom_prof != "" && p_option != "codesign/entitlements/custom_file" && p_option.begins_with("codesign/entitlements/")) {
+			return false;
+		}
+
+		// Hide sandbox entitlements.
+		bool sandbox = p_preset->get("codesign/entitlements/app_sandbox/enabled");
+		if (!sandbox && p_option != "codesign/entitlements/app_sandbox/enabled" && p_option.begins_with("codesign/entitlements/app_sandbox/")) {
+			return false;
+		}
+
 		// Hide irrelevant notarization options.
 		// Hide irrelevant notarization options.
 		int notary_tool = p_preset->get("notarization/notarization");
 		int notary_tool = p_preset->get("notarization/notarization");
 		switch (notary_tool) {
 		switch (notary_tool) {
 			case 1: { // "rcodesign"
 			case 1: { // "rcodesign"
-				if (p_option == "notarization/apple_id_name" || p_option == "notarization/apple_id_password" || p_option == "notarization/apple_team_id") {
+				if (p_option == "notarization/apple_id_name" || p_option == "notarization/apple_id_password") {
 					return false;
 					return false;
 				}
 				}
 			} break;
 			} break;
-			case 2: { // "altool"
+			case 2: { // "notarytool"
+				// All options are visible.
+			} break;
+			case 3: { // "altool"
 				// All options are visible.
 				// All options are visible.
 			} break;
 			} break;
 			default: { // disabled
 			default: { // disabled
-				if (p_option == "notarization/apple_id_name" || p_option == "notarization/apple_id_password" || p_option == "notarization/apple_team_id" || p_option == "notarization/api_uuid" || p_option == "notarization/api_key") {
+				if (p_option == "notarization/apple_id_name" || p_option == "notarization/apple_id_password" || p_option == "notarization/api_uuid" || p_option == "notarization/api_key" || p_option == "notarization/api_key_id") {
 					return false;
 					return false;
 				}
 				}
 			} break;
 			} break;
@@ -209,11 +256,16 @@ bool EditorExportPlatformOSX::get_option_visibility(const EditorExportPreset *p_
 }
 }
 
 
 void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) {
 void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) {
+#ifdef OSX_ENABLED
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "export/distribution_type", PROPERTY_HINT_ENUM, "Testing,Distribution,App Store"), 1, true));
+#else
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "export/distribution_type", PROPERTY_HINT_ENUM, "Testing,Distribution"), 1, true));
+#endif
+
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
 
 
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.icns,*.png,*.webp,*.svg"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.icns,*.png,*.webp,*.svg"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
@@ -222,19 +274,31 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_macos_version"), "10.12"));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false));
 
 
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/platform_build"), "14C18"));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_version"), "13.1"));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_build"), "22C55"));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_name"), "macosx13.1"));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/xcode_version"), "1420"));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/xcode_build"), "14C18"));
+
 #ifdef OSX_ENABLED
 #ifdef OSX_ENABLED
-	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/codesign", PROPERTY_HINT_ENUM, "Disabled,Built-in (ad-hoc only),PyOxidizer rcodesign,Xcode codesign"), 3, true));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/codesign", PROPERTY_HINT_ENUM, "Disabled,Built-in (ad-hoc only),rcodesign,Xcode codesign"), 3, true));
 #else
 #else
-	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/codesign", PROPERTY_HINT_ENUM, "Disabled,Built-in (ad-hoc only),PyOxidizer rcodesign"), 1, true));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/codesign", PROPERTY_HINT_ENUM, "Disabled,Built-in (ad-hoc only),rcodesign"), 1, true));
 #endif
 #endif
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/installer_identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "3rd Party Mac Developer Installer: (ID)"), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "ID"), ""));
 	// "codesign" only options:
 	// "codesign" only options:
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
 	// "rcodesign" only options:
 	// "rcodesign" only options:
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_file", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_file", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_password"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_password"), ""));
 	// "codesign" and "rcodesign" only options:
 	// "codesign" and "rcodesign" only options:
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/provisioning_profile", PROPERTY_HINT_GLOBAL_FILE, "*.provisionprofile"), ""));
+
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false));
@@ -248,7 +312,7 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/debugging"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/debugging"), false));
-	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false, true));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false));
@@ -257,20 +321,21 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
 	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_user_selected", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray()));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray()));
 
 
 #ifdef OSX_ENABLED
 #ifdef OSX_ENABLED
-	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "notarization/notarization", PROPERTY_HINT_ENUM, "Disabled,PyOxidizer rcodesign,Xcode altool"), 0, true));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "notarization/notarization", PROPERTY_HINT_ENUM, "Disabled,rcodesign,Xcode notarytool,Xcode altool (deprecated)"), 0, true));
 #else
 #else
-	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "notarization/notarization", PROPERTY_HINT_ENUM, "Disabled,PyOxidizer rcodesign"), 0, true));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "notarization/notarization", PROPERTY_HINT_ENUM, "Disabled,rcodesign"), 0, true));
 #endif
 #endif
-	// "altool" only options:
+	// "altool" and "notarytool" only options:
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), ""));
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), ""));
 	// "altool" and "rcodesign" only options:
 	// "altool" and "rcodesign" only options:
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_uuid", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect issuer ID"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_uuid", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect issuer ID"), ""));
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect API key ID"), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key", PROPERTY_HINT_GLOBAL_FILE, "*.p8"), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect API key ID"), ""));
 
 
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), ""));
@@ -469,8 +534,6 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
 			strnew += lines[i].replace("$binary", p_binary) + "\n";
 			strnew += lines[i].replace("$binary", p_binary) + "\n";
 		} else if (lines[i].find("$name") != -1) {
 		} else if (lines[i].find("$name") != -1) {
 			strnew += lines[i].replace("$name", p_binary) + "\n";
 			strnew += lines[i].replace("$name", p_binary) + "\n";
-		} else if (lines[i].find("$info") != -1) {
-			strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n";
 		} else if (lines[i].find("$identifier") != -1) {
 		} else if (lines[i].find("$identifier") != -1) {
 			strnew += lines[i].replace("$identifier", p_preset->get("application/identifier")) + "\n";
 			strnew += lines[i].replace("$identifier", p_preset->get("application/identifier")) + "\n";
 		} else if (lines[i].find("$short_version") != -1) {
 		} else if (lines[i].find("$short_version") != -1) {
@@ -484,8 +547,22 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
 			strnew += lines[i].replace("$app_category", cat.to_lower()) + "\n";
 			strnew += lines[i].replace("$app_category", cat.to_lower()) + "\n";
 		} else if (lines[i].find("$copyright") != -1) {
 		} else if (lines[i].find("$copyright") != -1) {
 			strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
 			strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
+		} else if (lines[i].find("$min_version") != -1) {
+			strnew += lines[i].replace("$min_version", p_preset->get("application/min_macos_version")) + "\n";
 		} else if (lines[i].find("$highres") != -1) {
 		} else if (lines[i].find("$highres") != -1) {
 			strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "\t<true/>" : "\t<false/>") + "\n";
 			strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "\t<true/>" : "\t<false/>") + "\n";
+		} else if (lines[i].find("$platfbuild") != -1) {
+			strnew += lines[i].replace("$platfbuild", p_preset->get("xcode/platform_build")) + "\n";
+		} else if (lines[i].find("$sdkver") != -1) {
+			strnew += lines[i].replace("$sdkver", p_preset->get("xcode/sdk_version")) + "\n";
+		} else if (lines[i].find("$sdkname") != -1) {
+			strnew += lines[i].replace("$sdkname", p_preset->get("xcode/sdk_name")) + "\n";
+		} else if (lines[i].find("$sdkbuild") != -1) {
+			strnew += lines[i].replace("$sdkbuild", p_preset->get("xcode/sdk_build")) + "\n";
+		} else if (lines[i].find("$xcodever") != -1) {
+			strnew += lines[i].replace("$xcodever", p_preset->get("xcode/xcode_version")) + "\n";
+		} else if (lines[i].find("$xcodebuild") != -1) {
+			strnew += lines[i].replace("$xcodebuild", p_preset->get("xcode/xcode_build")) + "\n";
 		} else if (lines[i].find("$usage_descriptions") != -1) {
 		} else if (lines[i].find("$usage_descriptions") != -1) {
 			String descriptions;
 			String descriptions;
 			if (!((String)p_preset->get("privacy/microphone_usage_description")).empty()) {
 			if (!((String)p_preset->get("privacy/microphone_usage_description")).empty()) {
@@ -583,7 +660,12 @@ Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset
 			args.push_back(p_preset->get("notarization/api_uuid"));
 			args.push_back(p_preset->get("notarization/api_uuid"));
 
 
 			args.push_back("--api-key");
 			args.push_back("--api-key");
-			args.push_back(p_preset->get("notarization/api_key"));
+			args.push_back(p_preset->get("notarization/api_key_id"));
+
+			if (p_preset->get("notarization/api_key") != "") {
+				args.push_back("--api-key-path");
+				args.push_back(p_preset->get("notarization/api_key"));
+			}
 
 
 			args.push_back(p_path);
 			args.push_back(p_path);
 
 
@@ -614,7 +696,91 @@ Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset
 			}
 			}
 		} break;
 		} break;
 #ifdef OSX_ENABLED
 #ifdef OSX_ENABLED
-		case 2: { // "altool"
+		case 2: { // "notarytool"
+			print_verbose("using notarytool notarization...");
+
+			if (!FileAccess::exists("/usr/bin/xcrun") && !FileAccess::exists("/bin/xcrun")) {
+				add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Xcode command line tools are not installed."));
+				return Error::FAILED;
+			}
+
+			List<String> args;
+
+			args.push_back("notarytool");
+			args.push_back("submit");
+
+			args.push_back(p_path);
+
+			if (p_preset->get("notarization/apple_id_name") == "" && p_preset->get("notarization/api_uuid") == "") {
+				add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Neither Apple ID name nor App Store Connect issuer ID name not specified."));
+				return Error::FAILED;
+			}
+			if (p_preset->get("notarization/apple_id_name") != "" && p_preset->get("notarization/api_uuid") != "") {
+				add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Both Apple ID name and App Store Connect issuer ID name are specified, only one should be set at the same time."));
+				return Error::FAILED;
+			}
+
+			if (p_preset->get("notarization/apple_id_name") != "") {
+				if (p_preset->get("notarization/apple_id_password") == "") {
+					add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Apple ID password not specified."));
+					return Error::FAILED;
+				}
+				args.push_back("--apple-id");
+				args.push_back(p_preset->get("notarization/apple_id_name"));
+
+				args.push_back("--password");
+				args.push_back(p_preset->get("notarization/apple_id_password"));
+			} else {
+				if (p_preset->get("notarization/api_key_id") == "") {
+					add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified."));
+					return Error::FAILED;
+				}
+				args.push_back("--issuer");
+				args.push_back(p_preset->get("notarization/api_uuid"));
+
+				if (p_preset->get("notarization/api_key") != "") {
+					args.push_back("--key");
+					args.push_back(p_preset->get("notarization/api_key"));
+				}
+
+				args.push_back("--key-id");
+				args.push_back(p_preset->get("notarization/api_key_id"));
+			}
+
+			args.push_back("--no-progress");
+
+			if (p_preset->get("codesign/apple_team_id")) {
+				args.push_back("--team-id");
+				args.push_back(p_preset->get("codesign/apple_team_id"));
+			}
+
+			String str;
+			int exitcode = 0;
+			Error err = OS::get_singleton()->execute("xcrun", args, true, NULL, &str, &exitcode, true);
+			if (err != OK) {
+				add_message(EXPORT_MESSAGE_WARNING, TTR("Notarization"), TTR("Could not start xcrun executable."));
+				return err;
+			}
+
+			int rq_offset = str.find("id:");
+			if (exitcode != 0 || rq_offset == -1) {
+				print_line("notarytool (" + p_path + "):\n" + str);
+				add_message(EXPORT_MESSAGE_WARNING, TTR("Notarization"), TTR("Notarization failed, see editor log for details."));
+				return Error::FAILED;
+			} else {
+				print_verbose("notarytool (" + p_path + "):\n" + str);
+				int next_nl = str.find("\n", rq_offset);
+				String request_uuid = (next_nl == -1) ? str.substr(rq_offset + 4, -1) : str.substr(rq_offset + 4, next_nl - rq_offset - 4);
+				add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), vformat(TTR("Notarization request UUID: \"%s\""), request_uuid));
+				add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("The notarization process generally takes less than an hour."));
+				add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t" + TTR("You can check progress manually by opening a Terminal and running the following command:"));
+				add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t\t\"xcrun notarytool log <request uuid> --issuer <api uuid> --key-id <api key id> --key <api key path>\" or");
+				add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t\t\"xcrun notarytool log <request uuid> --apple-id <your email> --password <app-specific pwd>>\"");
+				add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t" + TTR("Run the following command to staple the notarization ticket to the exported application (optional):"));
+				add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t\t\"xcrun stapler staple <app path>\"");
+			}
+		} break;
+		case 3: { // "altool"
 			print_verbose("using altool notarization...");
 			print_verbose("using altool notarization...");
 
 
 			if (!FileAccess::exists("/usr/bin/xcrun") && !FileAccess::exists("/bin/xcrun")) {
 			if (!FileAccess::exists("/usr/bin/xcrun") && !FileAccess::exists("/bin/xcrun")) {
@@ -658,15 +824,15 @@ Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset
 				args.push_back(p_preset->get("notarization/api_uuid"));
 				args.push_back(p_preset->get("notarization/api_uuid"));
 
 
 				args.push_back("--apiKey");
 				args.push_back("--apiKey");
-				args.push_back(p_preset->get("notarization/api_key"));
+				args.push_back(p_preset->get("notarization/api_key_id"));
 			}
 			}
 
 
 			args.push_back("--type");
 			args.push_back("--type");
 			args.push_back("osx");
 			args.push_back("osx");
 
 
-			if (p_preset->get("notarization/apple_team_id")) {
+			if (p_preset->get("codesign/apple_team_id")) {
 				args.push_back("--asc-provider");
 				args.push_back("--asc-provider");
-				args.push_back(p_preset->get("notarization/apple_team_id"));
+				args.push_back(p_preset->get("codesign/apple_team_id"));
 			}
 			}
 
 
 			args.push_back("--file");
 			args.push_back("--file");
@@ -844,6 +1010,7 @@ Error EditorExportPlatformOSX::_code_sign_directory(const Ref<EditorExportPreset
 	if (extensions_to_sign.empty()) {
 	if (extensions_to_sign.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;
@@ -889,6 +1056,14 @@ Error EditorExportPlatformOSX::_copy_and_sign_files(DirAccessRef &dir_access, co
 		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.size() == 0) {
+		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
@@ -908,7 +1083,9 @@ Error EditorExportPlatformOSX::_copy_and_sign_files(DirAccessRef &dir_access, co
 			// 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);
+			if (extensions_to_sign.find(p_in_app_path.get_extension()) > -1) {
+				err = _code_sign(p_preset, p_in_app_path, p_ent_path);
+			}
 		}
 		}
 	}
 	}
 	return err;
 	return err;
@@ -931,6 +1108,42 @@ Error EditorExportPlatformOSX::_export_osx_plugins_for(Ref<EditorExportPlugin> p
 	return error;
 	return error;
 }
 }
 
 
+Error EditorExportPlatformOSX::_create_pkg(const Ref<EditorExportPreset> &p_preset, const String &p_pkg_path, const String &p_app_path_name) {
+	List<String> args;
+
+	if (FileAccess::exists(p_pkg_path)) {
+		OS::get_singleton()->move_to_trash(p_pkg_path);
+	}
+
+	args.push_back("productbuild");
+	args.push_back("--component");
+	args.push_back(p_app_path_name);
+	args.push_back("/Applications");
+	String ident = p_preset->get("codesign/installer_identity");
+	if (ident != "") {
+		args.push_back("--timestamp");
+		args.push_back("--sign");
+		args.push_back(ident);
+	}
+	args.push_back("--quiet");
+	args.push_back(p_pkg_path);
+
+	String str;
+	Error err = OS::get_singleton()->execute("xcrun", args, true, nullptr, &str, nullptr, true);
+	if (err != OK) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("PKG Creation"), TTR("Could not start productbuild executable."));
+		return err;
+	}
+
+	print_verbose("productbuild returned: " + str);
+	if (str.find("productbuild: error:") != -1) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("PKG Creation"), TTR("`productbuild` failed."));
+		return FAILED;
+	}
+
+	return OK;
+}
+
 Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) {
 Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) {
 	List<String> args;
 	List<String> args;
 
 
@@ -1023,12 +1236,16 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 	pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
 	pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
 
 
 	String export_format;
 	String export_format;
-	if (use_dmg() && p_path.ends_with("dmg")) {
-		export_format = "dmg";
-	} else if (p_path.ends_with("zip")) {
+	if (p_path.ends_with("zip")) {
 		export_format = "zip";
 		export_format = "zip";
 	} else if (p_path.ends_with("app")) {
 	} else if (p_path.ends_with("app")) {
 		export_format = "app";
 		export_format = "app";
+#ifdef OSX_ENABLED
+	} else if (p_path.ends_with("dmg")) {
+		export_format = "dmg";
+	} else if (p_path.ends_with("pkg")) {
+		export_format = "pkg";
+#endif
 	} else {
 	} else {
 		add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Invalid export format."));
 		add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Invalid export format."));
 		return ERR_CANT_CREATE;
 		return ERR_CANT_CREATE;
@@ -1063,16 +1280,25 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 	if (err == OK) {
 	if (err == OK) {
 		print_verbose("Creating " + tmp_app_path_name + "/Contents/MacOS");
 		print_verbose("Creating " + tmp_app_path_name + "/Contents/MacOS");
 		err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS");
 		err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS");
+		if (err != OK) {
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory \"%s\"."), tmp_app_path_name + "/Contents/MacOS"));
+		}
 	}
 	}
 
 
 	if (err == OK) {
 	if (err == OK) {
 		print_verbose("Creating " + tmp_app_path_name + "/Contents/Frameworks");
 		print_verbose("Creating " + tmp_app_path_name + "/Contents/Frameworks");
 		err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks");
 		err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks");
+		if (err != OK) {
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory \"%s\"."), tmp_app_path_name + "/Contents/Frameworks"));
+		}
 	}
 	}
 
 
 	if (err == OK) {
 	if (err == OK) {
 		print_verbose("Creating " + tmp_app_path_name + "/Contents/Resources");
 		print_verbose("Creating " + tmp_app_path_name + "/Contents/Resources");
 		err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources");
 		err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources");
+		if (err != OK) {
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory \"%s\"."), tmp_app_path_name + "/Contents/Resources"));
+		}
 	}
 	}
 
 
 	Vector<String> translations = ProjectSettings::get_singleton()->get("locale/translations");
 	Vector<String> translations = ProjectSettings::get_singleton()->get("locale/translations");
@@ -1113,6 +1339,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 		unz_file_info info;
 		unz_file_info info;
 		char fname[16384];
 		char fname[16384];
 		ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
 		ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
+		if (ret != UNZ_OK) {
+			break;
+		}
 
 
 		String file = String::utf8(fname);
 		String file = String::utf8(fname);
 
 
@@ -1135,10 +1364,16 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 			file = tmp_app_path_name.plus_file(file);
 			file = tmp_app_path_name.plus_file(file);
 			if (err == OK) {
 			if (err == OK) {
 				err = tmp_app_dir->make_dir_recursive(file.get_base_dir());
 				err = tmp_app_dir->make_dir_recursive(file.get_base_dir());
+				if (err != OK) {
+					add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory \"%s\"."), file.get_base_dir()));
+				}
 			}
 			}
 			if (err == OK) {
 			if (err == OK) {
 				String lnk_data = String::utf8((const char *)data.ptr(), data.size());
 				String lnk_data = String::utf8((const char *)data.ptr(), data.size());
 				err = tmp_app_dir->create_link(lnk_data, file);
 				err = tmp_app_dir->create_link(lnk_data, file);
+				if (err != OK) {
+					add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not created symlink \"%s\" -> \"%s\"."), lnk_data, file));
+				}
 				print_verbose(vformat("ADDING SYMLINK %s => %s\n", file, lnk_data));
 				print_verbose(vformat("ADDING SYMLINK %s => %s\n", file, lnk_data));
 			}
 			}
 
 
@@ -1215,6 +1450,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 			file = tmp_app_path_name.plus_file(file);
 			file = tmp_app_path_name.plus_file(file);
 			if (err == OK) {
 			if (err == OK) {
 				err = tmp_app_dir->make_dir_recursive(file.get_base_dir());
 				err = tmp_app_dir->make_dir_recursive(file.get_base_dir());
+				if (err != OK) {
+					add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory \"%s\"."), file.get_base_dir()));
+				}
 			}
 			}
 			if (err == OK) {
 			if (err == OK) {
 				FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
 				FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
@@ -1227,6 +1465,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 					}
 					}
 					memdelete(f);
 					memdelete(f);
 				} else {
 				} else {
+					add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not open \"%s\"."), file));
 					err = ERR_CANT_CREATE;
 					err = ERR_CANT_CREATE;
 				}
 				}
 			}
 			}
@@ -1254,6 +1493,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 
 
 		// See if we can code sign our new package.
 		// See if we can code sign our new package.
 		bool sign_enabled = (p_preset->get("codesign/codesign").operator int() > 0);
 		bool sign_enabled = (p_preset->get("codesign/codesign").operator int() > 0);
+		bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation");
 
 
 		String ent_path = p_preset->get("codesign/entitlements/custom_file");
 		String ent_path = p_preset->get("codesign/entitlements/custom_file");
 		if (sign_enabled && (ent_path == "")) {
 		if (sign_enabled && (ent_path == "")) {
@@ -1288,7 +1528,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_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/>");
 				}
 				}
@@ -1325,6 +1565,19 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 					ent_f->store_line("<true/>");
 					ent_f->store_line("<true/>");
 				}
 				}
 
 
+				int dist_type = p_preset->get("export/distribution_type");
+				if (dist_type == 2) {
+					String pprof = p_preset->get("codesign/provisioning_profile");
+					String teamid = p_preset->get("codesign/apple_team_id");
+					String bid = p_preset->get("application/bundle_identifier");
+					if (pprof != "" && teamid != "") {
+						ent_f->store_line("<key>com.apple.developer.team-identifier</key>");
+						ent_f->store_line("<string>" + teamid + "</string>");
+						ent_f->store_line("<key>com.apple.application-identifier</key>");
+						ent_f->store_line("<string>" + teamid + "." + bid + "</string>");
+					}
+				}
+
 				if ((bool)p_preset->get("codesign/entitlements/app_sandbox/enabled")) {
 				if ((bool)p_preset->get("codesign/entitlements/app_sandbox/enabled")) {
 					ent_f->store_line("<key>com.apple.security.app-sandbox</key>");
 					ent_f->store_line("<key>com.apple.security.app-sandbox</key>");
 					ent_f->store_line("<true/>");
 					ent_f->store_line("<true/>");
@@ -1377,6 +1630,14 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 						ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>");
 						ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>");
 						ent_f->store_line("<true/>");
 						ent_f->store_line("<true/>");
 					}
 					}
+					if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_user_selected") == 1) {
+						ent_f->store_line("<key>com.apple.security.files.user-selected.read-only</key>");
+						ent_f->store_line("<true/>");
+					}
+					if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_user_selected") == 2) {
+						ent_f->store_line("<key>com.apple.security.files.user-selected.read-write</key>");
+						ent_f->store_line("<true/>");
+					}
 				}
 				}
 
 
 				ent_f->store_line("</dict>");
 				ent_f->store_line("</dict>");
@@ -1408,7 +1669,6 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 		}
 		}
 
 
 		if (err == OK) {
 		if (err == OK) {
-			bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation");
 			if ((!dylibs_found.empty() || !shared_objects.empty()) && sign_enabled && ad_hoc && !lib_validation) {
 			if ((!dylibs_found.empty() || !shared_objects.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."));
 				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;
 				err = ERR_CANT_CREATE;
@@ -1444,6 +1704,15 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 		}
 		}
 
 
 		if (err == OK && sign_enabled) {
 		if (err == OK && sign_enabled) {
+			int dist_type = p_preset->get("export/distribution_type");
+			if (dist_type == 2) {
+				String pprof = p_preset->get("codesign/provisioning_profile").operator String();
+				if (pprof != "") {
+					DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+					err = da->copy(pprof, tmp_app_path_name + "/Contents/embedded.provisionprofile");
+				}
+			}
+
 			if (ep.step(TTR("Code signing bundle"), 2)) {
 			if (ep.step(TTR("Code signing bundle"), 2)) {
 				return ERR_SKIP;
 				return ERR_SKIP;
 			}
 			}
@@ -1465,6 +1734,14 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 				}
 				}
 				err = _code_sign(p_preset, p_path, ent_path);
 				err = _code_sign(p_preset, p_path, ent_path);
 			}
 			}
+		} else if (export_format == "pkg") {
+			// Create a Installer.
+			if (err == OK) {
+				if (ep.step(TTR("Making PKG installer"), 3)) {
+					return ERR_SKIP;
+				}
+				err = _create_pkg(p_preset, p_path, tmp_app_path_name);
+			}
 		} else if (export_format == "zip") {
 		} else if (export_format == "zip") {
 			// Create ZIP.
 			// Create ZIP.
 			if (err == OK) {
 			if (err == OK) {
@@ -1487,7 +1764,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 
 
 		bool noto_enabled = (p_preset->get("notarization/notarization").operator int() > 0);
 		bool noto_enabled = (p_preset->get("notarization/notarization").operator int() > 0);
 		if (err == OK && noto_enabled) {
 		if (err == OK && noto_enabled) {
-			if (export_format == "app") {
+			if (export_format == "app" || export_format == "pkg") {
 				add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("Notarization requires the app to be archived first, select the DMG or ZIP export format instead."));
 				add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("Notarization requires the app to be archived first, select the DMG or ZIP export format instead."));
 			} else {
 			} else {
 				if (ep.step(TTR("Sending archive for notarization"), 4)) {
 				if (ep.step(TTR("Sending archive for notarization"), 4)) {
@@ -1695,6 +1972,33 @@ bool EditorExportPlatformOSX::has_valid_project_configuration(const Ref<EditorEx
 		};
 		};
 	}
 	}
 	int notary_tool = p_preset->get("notarization/notarization");
 	int notary_tool = p_preset->get("notarization/notarization");
+	int dist_type = p_preset->get("export/distribution_type");
+
+	if (dist_type == 2) {
+		String pprof = p_preset->get("codesign/provisioning_profile");
+		if (pprof == "") {
+			err += TTR("Provisioning profile is required for App Store distribution.") + "\n";
+			valid = false;
+		}
+		String ident = p_preset->get("codesign/installer_identity");
+		if (ident == "") {
+			err += TTR("Installer signing identity is required for App Store distribution.") + "\n";
+			valid = false;
+		}
+		bool sandbox = p_preset->get("codesign/entitlements/app_sandbox/enabled");
+		if (!sandbox) {
+			err += TTR("App sandbox is required for App Store distribution.") + "\n";
+			valid = false;
+		}
+		if (codesign_tool == 0) {
+			err += TTR("Code signing is required for App Store distribution.") + "\n";
+			valid = false;
+		}
+		if (codesign_tool == 1) {
+			err += TTR("App Store distribution with ad-hoc code signing is not supported.") + "\n";
+			valid = false;
+		}
+	}
 
 
 	if (notary_tool > 0) {
 	if (notary_tool > 0) {
 		if (ad_hoc) {
 		if (ad_hoc) {
@@ -1705,7 +2009,7 @@ bool EditorExportPlatformOSX::has_valid_project_configuration(const Ref<EditorEx
 			err += TTR("Notarization: Code signing is required for notarization.") + "\n";
 			err += TTR("Notarization: Code signing is required for notarization.") + "\n";
 			valid = false;
 			valid = false;
 		}
 		}
-		if (notary_tool == 2) {
+		if (notary_tool == 2 || notary_tool == 3) {
 			if (!FileAccess::exists("/usr/bin/xcrun") && !FileAccess::exists("/bin/xcrun")) {
 			if (!FileAccess::exists("/usr/bin/xcrun") && !FileAccess::exists("/bin/xcrun")) {
 				err += TTR("Notarization: Xcode command line tools are not installed.") + "\n";
 				err += TTR("Notarization: Xcode command line tools are not installed.") + "\n";
 				valid = false;
 				valid = false;
@@ -1746,7 +2050,7 @@ bool EditorExportPlatformOSX::has_valid_project_configuration(const Ref<EditorEx
 				valid = false;
 				valid = false;
 			}
 			}
 		}
 		}
-	} else {
+	} else if (dist_type != 2) {
 		err += TTR("Warning: Notarization is disabled. The exported project will be blocked by Gatekeeper if it's downloaded from an unknown source.") + "\n";
 		err += TTR("Warning: Notarization is disabled. The exported project will be blocked by Gatekeeper if it's downloaded from an unknown source.") + "\n";
 		if (codesign_tool == 0) {
 		if (codesign_tool == 0) {
 			err += TTR("Code signing is disabled. The exported project will not run on Macs with enabled Gatekeeper and Apple Silicon powered Macs.") + "\n";
 			err += TTR("Code signing is disabled. The exported project will not run on Macs with enabled Gatekeeper and Apple Silicon powered Macs.") + "\n";