Parcourir la source

Merge pull request #85845 from YuriSizov/editor-export-hidden-errors

Ensure more export errors are reported to users
Yuri Sizov il y a 1 an
Parent
commit
26ba706831

+ 30 - 1
editor/export/editor_export_platform.cpp

@@ -93,7 +93,7 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err)
 	}
 	p_log->add_newline();
 
-	if (msg_count) {
+	if (msg_count > 0) {
 		p_log->push_table(2);
 		p_log->set_table_column_expand(0, false);
 		p_log->set_table_column_expand(1, true);
@@ -131,10 +131,39 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err)
 			p_log->pop();
 			p_log->pop();
 		}
+		p_log->pop();
+		p_log->add_newline();
+	} else if (p_err != OK) {
+		// We failed but don't show any user-facing messages. This is bad and should not
+		// be allowed, but just in case this happens, let's give the user something at least.
+		p_log->push_table(2);
+		p_log->set_table_column_expand(0, false);
+		p_log->set_table_column_expand(1, true);
+
+		{
+			Color color = p_log->get_theme_color(SNAME("error_color"), EditorStringName(Editor));
+			Ref<Texture> icon = p_log->get_editor_theme_icon(SNAME("Error"));
+
+			p_log->push_cell();
+			p_log->add_text("\t");
+			if (icon.is_valid()) {
+				p_log->add_image(icon);
+			}
+			p_log->pop();
+
+			p_log->push_cell();
+			p_log->push_color(color);
+			p_log->add_text(vformat("[%s]: %s", TTR("Unknown Error"), vformat(TTR("Export failed with error code %d."), p_err)));
+			p_log->pop();
+			p_log->pop();
+		}
+
 		p_log->pop();
 		p_log->add_newline();
 	}
+
 	p_log->add_newline();
+
 	return has_messages;
 }
 

+ 1 - 0
editor/export/editor_export_platform_pc.cpp

@@ -161,6 +161,7 @@ Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_
 	}
 	if (err != OK) {
 		add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("Failed to copy export template."));
+		return err;
 	}
 
 	return err;

+ 27 - 25
editor/export/project_export.cpp

@@ -1109,37 +1109,40 @@ void ProjectExportDialog::_export_all_dialog_action(const String &p_str) {
 }
 
 void ProjectExportDialog::_export_all(bool p_debug) {
-	String export_target = p_debug ? TTR("Debug") : TTR("Release");
-	EditorProgress ep("exportall", TTR("Exporting All") + " " + export_target, EditorExport::get_singleton()->get_export_preset_count(), true);
-
 	exporting = true;
-
 	bool show_dialog = false;
-	result_dialog_log->clear();
-	for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
-		Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
-		if (preset.is_null()) {
-			exporting = false;
-			ERR_FAIL_MSG("Failed to start the export: one of the presets is invalid.");
-		}
 
-		Ref<EditorExportPlatform> platform = preset->get_platform();
-		if (platform.is_null()) {
-			exporting = false;
-			ERR_FAIL_MSG("Failed to start the export: one of the presets has no valid platform.");
-		}
+	{ // Scope for the editor progress, we must free it before showing the dialog at the end.
+		String export_target = p_debug ? TTR("Debug") : TTR("Release");
+		EditorProgress ep("exportall", TTR("Exporting All") + " " + export_target, EditorExport::get_singleton()->get_export_preset_count(), true);
 
-		ep.step(preset->get_name(), i);
+		result_dialog_log->clear();
+		for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+			Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
+			if (preset.is_null()) {
+				exporting = false;
+				ERR_FAIL_MSG("Failed to start the export: one of the presets is invalid.");
+			}
 
-		platform->clear_messages();
-		Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0);
-		if (err == ERR_SKIP) {
-			exporting = false;
-			return;
+			Ref<EditorExportPlatform> platform = preset->get_platform();
+			if (platform.is_null()) {
+				exporting = false;
+				ERR_FAIL_MSG("Failed to start the export: one of the presets has no valid platform.");
+			}
+
+			ep.step(preset->get_name(), i);
+
+			platform->clear_messages();
+			Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0);
+			if (err == ERR_SKIP) {
+				exporting = false;
+				return;
+			}
+			bool has_messages = platform->fill_log_messages(result_dialog_log, err);
+			show_dialog = show_dialog || has_messages;
 		}
-		bool has_messages = platform->fill_log_messages(result_dialog_log, err);
-		show_dialog = show_dialog || has_messages;
 	}
+
 	if (show_dialog) {
 		result_dialog->popup_centered_ratio(0.5);
 	}
@@ -1148,7 +1151,6 @@ void ProjectExportDialog::_export_all(bool p_debug) {
 }
 
 void ProjectExportDialog::_bind_methods() {
-	ClassDB::bind_method("_export_all", &ProjectExportDialog::_export_all);
 	ClassDB::bind_method("set_export_path", &ProjectExportDialog::set_export_path);
 	ClassDB::bind_method("get_export_path", &ProjectExportDialog::get_export_path);
 	ClassDB::bind_method("get_current_preset", &ProjectExportDialog::get_current_preset);

+ 11 - 9
platform/android/export/export_plugin.cpp

@@ -2782,6 +2782,12 @@ Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset>
 Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) {
 	ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
 
+	const String base_dir = p_path.get_base_dir();
+	if (!DirAccess::exists(base_dir)) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), base_dir));
+		return ERR_FILE_BAD_PATH;
+	}
+
 	String src_apk;
 	Error err;
 
@@ -2856,7 +2862,10 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 		}
 		const String assets_directory = get_assets_directory(p_preset, export_format);
 		String sdk_path = EDITOR_GET("export/android/android_sdk_path");
-		ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'.");
+		if (sdk_path.is_empty()) {
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'."));
+			return ERR_UNCONFIGURED;
+		}
 		print_verbose("Android sdk path: " + sdk_path);
 
 		// TODO: should we use "package/name" or "application/config/name"?
@@ -3106,10 +3115,6 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 		}
 	}
 
-	if (!DirAccess::exists(p_path.get_base_dir())) {
-		return ERR_FILE_BAD_PATH;
-	}
-
 	Ref<FileAccess> io_fa;
 	zlib_filefunc_def io = zipio_create_io(&io_fa);
 
@@ -3302,10 +3307,6 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 	zipClose(unaligned_apk, nullptr);
 	unzClose(pkg);
 
-	if (err != OK) {
-		CLEANUP_AND_RETURN(err);
-	}
-
 	// Let's zip-align (must be done before signing)
 
 	static const int ZIP_ALIGNMENT = 4;
@@ -3392,6 +3393,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 		// file will invalidate the signature.
 		err = sign_apk(p_preset, p_debug, p_path, ep);
 		if (err != OK) {
+			// Message is supplied by the subroutine method.
 			CLEANUP_AND_RETURN(err);
 		}
 	}

+ 87 - 47
platform/ios/export/export_plugin.cpp

@@ -620,7 +620,10 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
 	String sizes;
 
 	Ref<DirAccess> da = DirAccess::open(p_iconset_dir);
-	ERR_FAIL_COND_V_MSG(da.is_null(), ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'.");
+	if (da.is_null()) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat(TTR("Could not open a directory at path \"%s\"."), p_iconset_dir));
+		return ERR_CANT_OPEN;
+	}
 
 	Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color");
 
@@ -692,12 +695,20 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
 	json_description += "]}";
 
 	Ref<FileAccess> json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE);
-	ERR_FAIL_COND_V(json_file.is_null(), ERR_CANT_CREATE);
+	if (json_file.is_null()) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat(TTR("Could not write to a file at path \"%s\"."), p_iconset_dir + "Contents.json"));
+		return ERR_CANT_CREATE;
+	}
+
 	CharString json_utf8 = json_description.utf8();
 	json_file->store_buffer((const uint8_t *)json_utf8.get_data(), json_utf8.length());
 
 	Ref<FileAccess> sizes_file = FileAccess::open(p_iconset_dir + "sizes", FileAccess::WRITE);
-	ERR_FAIL_COND_V(sizes_file.is_null(), ERR_CANT_CREATE);
+	if (sizes_file.is_null()) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat(TTR("Could not write to a file at path \"%s\"."), p_iconset_dir + "sizes"));
+		return ERR_CANT_CREATE;
+	}
+
 	CharString sizes_utf8 = sizes.utf8();
 	sizes_file->store_buffer((const uint8_t *)sizes_utf8.get_data(), sizes_utf8.length());
 
@@ -1219,10 +1230,10 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir
 		String asset = p_assets[f_idx];
 		if (asset.begins_with("res://")) {
 			Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets);
-			ERR_FAIL_COND_V(err, err);
+			ERR_FAIL_COND_V(err != OK, err);
 		} else if (ProjectSettings::get_singleton()->localize_path(asset).begins_with("res://")) {
 			Error err = _copy_asset(p_out_dir, ProjectSettings::get_singleton()->localize_path(asset), nullptr, p_is_framework, p_should_embed, r_exported_assets);
-			ERR_FAIL_COND_V(err, err);
+			ERR_FAIL_COND_V(err != OK, err);
 		} else {
 			// either SDK-builtin or already a part of the export template
 			IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed };
@@ -1306,8 +1317,7 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
 		// We shouldn't embed .xcframework that contains static libraries.
 		// Static libraries are not embedded anyway.
 		err = _copy_asset(dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets);
-
-		ERR_FAIL_COND_V(err, err);
+		ERR_FAIL_COND_V(err != OK, err);
 
 		// Adding dependencies.
 		// Use separate container for names to check for duplicates.
@@ -1432,15 +1442,15 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
 	{
 		// Export linked plugin dependency
 		err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
-		ERR_FAIL_COND_V(err, err);
+		ERR_FAIL_COND_V(err != OK, err);
 
 		// Export embedded plugin dependency
 		err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
-		ERR_FAIL_COND_V(err, err);
+		ERR_FAIL_COND_V(err != OK, err);
 
 		// Export plugin files
 		err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets);
-		ERR_FAIL_COND_V(err, err);
+		ERR_FAIL_COND_V(err != OK, err);
 	}
 
 	// Update CPP
@@ -1496,9 +1506,14 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
 Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_skip_ipa) {
 	ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
 
-	String src_pkg_name;
-	String dest_dir = p_path.get_base_dir() + "/";
-	String binary_name = p_path.get_file().get_basename();
+	const String dest_dir = p_path.get_base_dir() + "/";
+	const String binary_name = p_path.get_file().get_basename();
+	const String binary_dir = dest_dir + binary_name;
+
+	if (!DirAccess::exists(dest_dir)) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), dest_dir));
+		return ERR_FILE_BAD_PATH;
+	}
 
 	bool export_project_only = p_preset->get("application/export_project_only");
 
@@ -1507,6 +1522,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 	String team_id = p_preset->get("application/app_store_team_id");
 	ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project.");
 
+	String src_pkg_name;
 	if (p_debug) {
 		src_pkg_name = p_preset->get("custom_template/debug");
 	} else {
@@ -1522,10 +1538,6 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 		}
 	}
 
-	if (!DirAccess::exists(dest_dir)) {
-		return ERR_FILE_BAD_PATH;
-	}
-
 	{
 		Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 		if (da.is_valid()) {
@@ -1533,18 +1545,19 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 
 			// remove leftovers from last export so they don't interfere
 			// in case some files are no longer needed
-			if (da->change_dir(dest_dir + binary_name + ".xcodeproj") == OK) {
+			if (da->change_dir(binary_dir + ".xcodeproj") == OK) {
 				da->erase_contents_recursive();
 			}
-			if (da->change_dir(dest_dir + binary_name) == OK) {
+			if (da->change_dir(binary_dir) == OK) {
 				da->erase_contents_recursive();
 			}
 
 			da->change_dir(current_dir);
 
-			if (!da->dir_exists(dest_dir + binary_name)) {
-				Error err = da->make_dir(dest_dir + binary_name);
-				if (err) {
+			if (!da->dir_exists(binary_dir)) {
+				Error err = da->make_dir(binary_dir);
+				if (err != OK) {
+					add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create the directory: \"%s\""), binary_dir));
 					return err;
 				}
 			}
@@ -1554,10 +1567,11 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 	if (ep.step("Making .pck", 0)) {
 		return ERR_SKIP;
 	}
-	String pack_path = dest_dir + binary_name + ".pck";
+	String pack_path = binary_dir + ".pck";
 	Vector<SharedObject> libraries;
 	Error err = save_pack(p_preset, p_debug, pack_path, &libraries);
 	if (err) {
+		// Message is supplied by the subroutine method.
 		return err;
 	}
 
@@ -1606,7 +1620,10 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 	Vector<IOSExportAsset> assets;
 
 	Ref<DirAccess> tmp_app_path = DirAccess::create_for_path(dest_dir);
-	ERR_FAIL_COND_V(tmp_app_path.is_null(), ERR_CANT_CREATE);
+	if (tmp_app_path.is_null()) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not create and open the directory: \"%s\""), dest_dir));
+		return ERR_CANT_CREATE;
+	}
 
 	print_line("Unzipping...");
 	Ref<FileAccess> io_fa;
@@ -1617,8 +1634,14 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 		return ERR_CANT_OPEN;
 	}
 
-	err = _export_ios_plugins(p_preset, config_data, dest_dir + binary_name, assets, p_debug);
-	ERR_FAIL_COND_V(err, err);
+	err = _export_ios_plugins(p_preset, config_data, binary_dir, assets, p_debug);
+	if (err != OK) {
+		// TODO: Improve error reporting by using `add_message` throughout all methods called via `_export_ios_plugins`.
+		// For now a generic top level message would be fine, but we're ought to use proper reporting here instead of
+		// just fail macros and non-descriptive error return values.
+		add_message(EXPORT_MESSAGE_ERROR, TTR("iOS Plugins"), vformat(TTR("Failed to export iOS plugins with code %d. Please check the output log."), err));
+		return err;
+	}
 
 	//export rest of the files
 	int ret = unzGoToFirstFile(src_pkg_zip);
@@ -1683,8 +1706,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 				print_line("Creating " + dir_name);
 				Error dir_err = tmp_app_path->make_dir_recursive(dir_name);
 				if (dir_err) {
-					ERR_PRINT("Can't create '" + dir_name + "'.");
 					unzClose(src_pkg_zip);
+					add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create a directory at path \"%s\"."), dir_name));
 					return ERR_CANT_CREATE;
 				}
 			}
@@ -1693,8 +1716,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 			{
 				Ref<FileAccess> f = FileAccess::open(file, FileAccess::WRITE);
 				if (f.is_null()) {
-					ERR_PRINT("Can't write '" + file + "'.");
 					unzClose(src_pkg_zip);
+					add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not write to a file at path \"%s\"."), file));
 					return ERR_CANT_CREATE;
 				};
 				f->store_buffer(data.ptr(), data.size());
@@ -1715,7 +1738,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 	unzClose(src_pkg_zip);
 
 	if (!found_library) {
-		ERR_PRINT("Requested template library '" + library_to_use + "' not found. It might be missing from your template archive.");
+		add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Requested template library '%s' not found. It might be missing from your template archive."), library_to_use));
 		return ERR_FILE_NOT_FOUND;
 	}
 
@@ -1727,7 +1750,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 	Vector<String> translations = GLOBAL_GET("internationalization/locale/translations");
 	if (translations.size() > 0) {
 		{
-			String fname = dest_dir + binary_name + "/en.lproj";
+			String fname = binary_dir + "/en.lproj";
 			tmp_app_path->make_dir_recursive(fname);
 			Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
 			f->store_line("/* Localized versions of Info.plist keys */");
@@ -1747,7 +1770,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 		}
 
 		for (const String &lang : languages) {
-			String fname = dest_dir + binary_name + "/" + lang + ".lproj";
+			String fname = binary_dir + "/" + lang + ".lproj";
 			tmp_app_path->make_dir_recursive(fname);
 			Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
 			f->store_line("/* Localized versions of Info.plist keys */");
@@ -1776,34 +1799,37 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 			String dest_lib_file_path = dest_dir + static_lib_path.get_file();
 			Error lib_copy_err = tmp_app_path->copy(static_lib_path, dest_lib_file_path);
 			if (lib_copy_err != OK) {
-				ERR_PRINT("Can't copy '" + static_lib_path + "'.");
+				add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not copy a file at path \"%s\" to \"%s\"."), static_lib_path, dest_lib_file_path));
 				return lib_copy_err;
 			}
 		}
 	}
 
-	String iconset_dir = dest_dir + binary_name + "/Images.xcassets/AppIcon.appiconset/";
+	String iconset_dir = binary_dir + "/Images.xcassets/AppIcon.appiconset/";
 	err = OK;
 	if (!tmp_app_path->dir_exists(iconset_dir)) {
 		err = tmp_app_path->make_dir_recursive(iconset_dir);
 	}
-	if (err) {
+	if (err != OK) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create a directory at path \"%s\"."), iconset_dir));
 		return err;
 	}
 
 	err = _export_icons(p_preset, iconset_dir);
-	if (err) {
+	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 	}
 
 	{
 		bool use_storyboard = p_preset->get("storyboard/use_launch_screen_storyboard");
 
-		String launch_image_path = dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/";
-		String splash_image_path = dest_dir + binary_name + "/Images.xcassets/SplashImage.imageset/";
+		String launch_image_path = binary_dir + "/Images.xcassets/LaunchImage.launchimage/";
+		String splash_image_path = binary_dir + "/Images.xcassets/SplashImage.imageset/";
 
 		Ref<DirAccess> launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 		if (launch_screen_da.is_null()) {
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not access the filesystem."));
 			return ERR_CANT_CREATE;
 		}
 
@@ -1816,10 +1842,11 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 			}
 
 			err = _export_loading_screen_file(p_preset, splash_image_path);
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create a file at path \"%s\" with code %d."), splash_image_path, err));
 		} else {
 			print_line("Using Launch Images");
 
-			const String launch_screen_path = dest_dir + binary_name + "/Launch Screen.storyboard";
+			const String launch_screen_path = binary_dir + "/Launch Screen.storyboard";
 
 			launch_screen_da->remove(launch_screen_path);
 
@@ -1829,21 +1856,22 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 			}
 
 			err = _export_loading_screen_images(p_preset, launch_image_path);
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create a file at path \"%s\" with code %d."), launch_image_path, err));
 		}
 	}
 
-	if (err) {
+	if (err != OK) {
 		return err;
 	}
 
 	print_line("Exporting additional assets");
-	_export_additional_assets(dest_dir + binary_name, libraries, assets);
+	_export_additional_assets(binary_dir, libraries, assets);
 	_add_assets_to_project(p_preset, project_file_data, assets);
-	String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj";
+	String project_file_name = binary_dir + ".xcodeproj/project.pbxproj";
 	{
 		Ref<FileAccess> f = FileAccess::open(project_file_name, FileAccess::WRITE);
 		if (f.is_null()) {
-			ERR_PRINT("Can't write '" + project_file_name + "'.");
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not write to a file at path \"%s\"."), project_file_name));
 			return ERR_CANT_CREATE;
 		};
 		f->store_buffer(project_file_data.ptr(), project_file_data.size());
@@ -1854,7 +1882,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 		if (ep.step("Code-signing dylibs", 2)) {
 			return ERR_SKIP;
 		}
-		Ref<DirAccess> dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs");
+		Ref<DirAccess> dylibs_dir = DirAccess::open(binary_dir + "/dylibs");
 		ERR_FAIL_COND_V(dylibs_dir.is_null(), ERR_CANT_OPEN);
 		CodesignData codesign_data(p_preset, p_debug);
 		err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data);
@@ -1871,10 +1899,11 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 	if (ep.step("Making .xcarchive", 3)) {
 		return ERR_SKIP;
 	}
+
 	String archive_path = p_path.get_basename() + ".xcarchive";
 	List<String> archive_args;
 	archive_args.push_back("-project");
-	archive_args.push_back(dest_dir + binary_name + ".xcodeproj");
+	archive_args.push_back(binary_dir + ".xcodeproj");
 	archive_args.push_back("-scheme");
 	archive_args.push_back(binary_name);
 	archive_args.push_back("-sdk");
@@ -1895,9 +1924,14 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 	archive_args.push_back("-allowProvisioningUpdates");
 	archive_args.push_back("-archivePath");
 	archive_args.push_back(archive_path);
+
 	String archive_str;
 	err = OS::get_singleton()->execute("xcodebuild", archive_args, &archive_str, nullptr, true);
-	ERR_FAIL_COND_V(err, err);
+	if (err != OK) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), vformat(TTR("Failed to run xcodebuild with code %d"), err));
+		return err;
+	}
+
 	print_line("xcodebuild (.xcarchive):\n" + archive_str);
 	if (!archive_str.contains("** ARCHIVE SUCCEEDED **")) {
 		add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR("Xcode project build failed, see editor log for details."));
@@ -1908,18 +1942,24 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 		if (ep.step("Making .ipa", 4)) {
 			return ERR_SKIP;
 		}
+
 		List<String> export_args;
 		export_args.push_back("-exportArchive");
 		export_args.push_back("-archivePath");
 		export_args.push_back(archive_path);
 		export_args.push_back("-exportOptionsPlist");
-		export_args.push_back(dest_dir + binary_name + "/export_options.plist");
+		export_args.push_back(binary_dir + "/export_options.plist");
 		export_args.push_back("-allowProvisioningUpdates");
 		export_args.push_back("-exportPath");
 		export_args.push_back(dest_dir);
+
 		String export_str;
 		err = OS::get_singleton()->execute("xcodebuild", export_args, &export_str, nullptr, true);
-		ERR_FAIL_COND_V(err, err);
+		if (err != OK) {
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), vformat(TTR("Failed to run xcodebuild with code %d"), err));
+			return err;
+		}
+
 		print_line("xcodebuild (.ipa):\n" + export_str);
 		if (!export_str.contains("** EXPORT SUCCEEDED **")) {
 			add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR(".ipa export failed, see editor log for details."));

+ 9 - 9
platform/linuxbsd/export/export_plugin.cpp

@@ -79,6 +79,7 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset>
 	Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
 	if (export_as_zip) {
 		if (tmp_app_dir.is_null()) {
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not create and open the directory: \"%s\""), tmp_dir_path));
 			return ERR_CANT_CREATE;
 		}
 		if (DirAccess::exists(tmp_dir_path)) {
@@ -93,19 +94,18 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset>
 	// Export project.
 	Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, path, p_flags);
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 	}
 
 	// Save console wrapper.
-	if (err == OK) {
-		int con_scr = p_preset->get("debug/export_console_wrapper");
-		if ((con_scr == 1 && p_debug) || (con_scr == 2)) {
-			String scr_path = path.get_basename() + ".sh";
-			err = _export_debug_script(p_preset, pkg_name, path.get_file(), scr_path);
-			FileAccess::set_unix_permissions(scr_path, 0755);
-			if (err != OK) {
-				add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Console Export"), TTR("Could not create console wrapper."));
-			}
+	int con_scr = p_preset->get("debug/export_console_wrapper");
+	if ((con_scr == 1 && p_debug) || (con_scr == 2)) {
+		String scr_path = path.get_basename() + ".sh";
+		err = _export_debug_script(p_preset, pkg_name, path.get_file(), scr_path);
+		FileAccess::set_unix_permissions(scr_path, 0755);
+		if (err != OK) {
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Console Export"), TTR("Could not create console wrapper."));
 		}
 	}
 

+ 8 - 7
platform/macos/export/export_plugin.cpp

@@ -1266,10 +1266,16 @@ Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPres
 Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
 	ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
 
-	String src_pkg_name;
+	const String base_dir = p_path.get_base_dir();
+
+	if (!DirAccess::exists(base_dir)) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), base_dir));
+		return ERR_FILE_BAD_PATH;
+	}
 
 	EditorProgress ep("export", TTR("Exporting for macOS"), 3, true);
 
+	String src_pkg_name;
 	if (p_debug) {
 		src_pkg_name = p_preset->get("custom_template/debug");
 	} else {
@@ -1280,16 +1286,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
 		String err;
 		src_pkg_name = find_export_template("macos.zip", &err);
 		if (src_pkg_name.is_empty()) {
-			add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("Export template not found."));
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("Export template not found.") + "\n" + err);
 			return ERR_FILE_NOT_FOUND;
 		}
 	}
 
-	if (!DirAccess::exists(p_path.get_base_dir())) {
-		add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("The given export path doesn't exist."));
-		return ERR_FILE_BAD_PATH;
-	}
-
 	Ref<FileAccess> io_fa;
 	zlib_filefunc_def io = zipio_create_io(&io_fa);
 

+ 13 - 4
platform/web/export/export_plugin.cpp

@@ -259,6 +259,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
 	_replace_strings(replaces, sw);
 	Error err = _write_or_error(sw.ptr(), sw.size(), dir.path_join(name + ".service.worker.js"));
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 	}
 
@@ -291,16 +292,19 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
 	const String icon144_path = p_preset->get("progressive_web_app/icon_144x144");
 	err = _add_manifest_icon(p_path, icon144_path, 144, icons_arr);
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 	}
 	const String icon180_path = p_preset->get("progressive_web_app/icon_180x180");
 	err = _add_manifest_icon(p_path, icon180_path, 180, icons_arr);
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 	}
 	const String icon512_path = p_preset->get("progressive_web_app/icon_512x512");
 	err = _add_manifest_icon(p_path, icon512_path, 512, icons_arr);
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 	}
 	manifest["icons"] = icons_arr;
@@ -308,6 +312,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
 	CharString cs = Variant(manifest).to_json_string().utf8();
 	err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.path_join(name + ".manifest.json"));
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 	}
 
@@ -439,6 +444,11 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
 	const String base_path = p_path.get_basename();
 	const String base_name = p_path.get_file().get_basename();
 
+	if (!DirAccess::exists(base_dir)) {
+		add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), base_dir));
+		return ERR_FILE_BAD_PATH;
+	}
+
 	// Find the correct template
 	String template_path = p_debug ? custom_debug : custom_release;
 	template_path = template_path.strip_edges();
@@ -447,10 +457,6 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
 		template_path = find_export_template(_get_template_name(extensions, p_debug));
 	}
 
-	if (!DirAccess::exists(base_dir)) {
-		return ERR_FILE_BAD_PATH;
-	}
-
 	if (!template_path.is_empty() && !FileAccess::exists(template_path)) {
 		add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Template file not found: \"%s\"."), template_path));
 		return ERR_FILE_NOT_FOUND;
@@ -480,6 +486,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
 	// Extract templates.
 	error = _extract_template(template_path, base_dir, base_name, pwa);
 	if (error) {
+		// Message is supplied by the subroutine method.
 		return error;
 	}
 
@@ -510,6 +517,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
 	_fix_html(html, p_preset, base_name, p_debug, p_flags, shared_objects, file_sizes);
 	Error err = _write_or_error(html.ptr(), html.size(), p_path);
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 	}
 	html.resize(0);
@@ -543,6 +551,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
 	if (pwa) {
 		err = _build_pwa(p_preset, p_path, shared_objects);
 		if (err != OK) {
+			// Message is supplied by the subroutine method.
 			return err;
 		}
 	}

+ 9 - 2
platform/windows/export/export_plugin.cpp

@@ -226,6 +226,7 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
 	Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
 	if (export_as_zip) {
 		if (tmp_app_dir.is_null()) {
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not create and open the directory: \"%s\""), tmp_dir_path));
 			return ERR_CANT_CREATE;
 		}
 		if (DirAccess::exists(tmp_dir_path)) {
@@ -242,8 +243,14 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
 	if (embedded) {
 		pck_path = pck_path.get_basename() + ".tmp";
 	}
+
 	Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags);
-	if (p_preset->get("codesign/enable") && err == OK) {
+	if (err != OK) {
+		// Message is supplied by the subroutine method.
+		return err;
+	}
+
+	if (p_preset->get("codesign/enable")) {
 		_code_sign(p_preset, pck_path);
 		String wrapper_path = p_path.get_basename() + ".console.exe";
 		if (FileAccess::exists(wrapper_path)) {
@@ -251,7 +258,7 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
 		}
 	}
 
-	if (embedded && err == OK) {
+	if (embedded) {
 		Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
 		err = tmp_dir->rename(pck_path, p_path);
 		if (err != OK) {