Browse Source

Ensure more export errors are reported to users

Also fixes the timing issue when exporting all
presets at the same time, where the error report
would try to appear while the progress dialog
was still visible.
Yuri Sizov 1 year ago
parent
commit
773b4d7764

+ 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();
 	p_log->add_newline();
 
 
-	if (msg_count) {
+	if (msg_count > 0) {
 		p_log->push_table(2);
 		p_log->push_table(2);
 		p_log->set_table_column_expand(0, false);
 		p_log->set_table_column_expand(0, false);
 		p_log->set_table_column_expand(1, true);
 		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->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->pop();
 		p_log->add_newline();
 		p_log->add_newline();
 	}
 	}
+
 	p_log->add_newline();
 	p_log->add_newline();
+
 	return has_messages;
 	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) {
 	if (err != OK) {
 		add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("Failed to copy export template."));
 		add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("Failed to copy export template."));
+		return err;
 	}
 	}
 
 
 	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) {
 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;
 	exporting = true;
-
 	bool show_dialog = false;
 	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) {
 	if (show_dialog) {
 		result_dialog->popup_centered_ratio(0.5);
 		result_dialog->popup_centered_ratio(0.5);
 	}
 	}
@@ -1148,7 +1151,6 @@ void ProjectExportDialog::_export_all(bool p_debug) {
 }
 }
 
 
 void ProjectExportDialog::_bind_methods() {
 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("set_export_path", &ProjectExportDialog::set_export_path);
 	ClassDB::bind_method("get_export_path", &ProjectExportDialog::get_export_path);
 	ClassDB::bind_method("get_export_path", &ProjectExportDialog::get_export_path);
 	ClassDB::bind_method("get_current_preset", &ProjectExportDialog::get_current_preset);
 	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) {
 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);
 	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;
 	String src_apk;
 	Error err;
 	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);
 		const String assets_directory = get_assets_directory(p_preset, export_format);
 		String sdk_path = EDITOR_GET("export/android/android_sdk_path");
 		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);
 		print_verbose("Android sdk path: " + sdk_path);
 
 
 		// TODO: should we use "package/name" or "application/config/name"?
 		// 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;
 	Ref<FileAccess> io_fa;
 	zlib_filefunc_def io = zipio_create_io(&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);
 	zipClose(unaligned_apk, nullptr);
 	unzClose(pkg);
 	unzClose(pkg);
 
 
-	if (err != OK) {
-		CLEANUP_AND_RETURN(err);
-	}
-
 	// Let's zip-align (must be done before signing)
 	// Let's zip-align (must be done before signing)
 
 
 	static const int ZIP_ALIGNMENT = 4;
 	static const int ZIP_ALIGNMENT = 4;
@@ -3392,6 +3393,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 		// file will invalidate the signature.
 		// file will invalidate the signature.
 		err = sign_apk(p_preset, p_debug, p_path, ep);
 		err = sign_apk(p_preset, p_debug, p_path, ep);
 		if (err != OK) {
 		if (err != OK) {
+			// Message is supplied by the subroutine method.
 			CLEANUP_AND_RETURN(err);
 			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;
 	String sizes;
 
 
 	Ref<DirAccess> da = DirAccess::open(p_iconset_dir);
 	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");
 	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 += "]}";
 	json_description += "]}";
 
 
 	Ref<FileAccess> json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE);
 	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();
 	CharString json_utf8 = json_description.utf8();
 	json_file->store_buffer((const uint8_t *)json_utf8.get_data(), json_utf8.length());
 	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);
 	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();
 	CharString sizes_utf8 = sizes.utf8();
 	sizes_file->store_buffer((const uint8_t *)sizes_utf8.get_data(), sizes_utf8.length());
 	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];
 		String asset = p_assets[f_idx];
 		if (asset.begins_with("res://")) {
 		if (asset.begins_with("res://")) {
 			Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets);
 			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://")) {
 		} 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);
 			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 {
 		} else {
 			// either SDK-builtin or already a part of the export template
 			// either SDK-builtin or already a part of the export template
 			IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed };
 			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.
 		// We shouldn't embed .xcframework that contains static libraries.
 		// Static libraries are not embedded anyway.
 		// Static libraries are not embedded anyway.
 		err = _copy_asset(dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets);
 		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.
 		// Adding dependencies.
 		// Use separate container for names to check for duplicates.
 		// 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
 		// Export linked plugin dependency
 		err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
 		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
 		// Export embedded plugin dependency
 		err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
 		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
 		// Export plugin files
 		err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets);
 		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
 	// 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) {
 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);
 	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");
 	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");
 	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.");
 	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) {
 	if (p_debug) {
 		src_pkg_name = p_preset->get("custom_template/debug");
 		src_pkg_name = p_preset->get("custom_template/debug");
 	} else {
 	} 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);
 		Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 		if (da.is_valid()) {
 		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
 			// remove leftovers from last export so they don't interfere
 			// in case some files are no longer needed
 			// 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();
 				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->erase_contents_recursive();
 			}
 			}
 
 
 			da->change_dir(current_dir);
 			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;
 					return err;
 				}
 				}
 			}
 			}
@@ -1554,10 +1567,11 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 	if (ep.step("Making .pck", 0)) {
 	if (ep.step("Making .pck", 0)) {
 		return ERR_SKIP;
 		return ERR_SKIP;
 	}
 	}
-	String pack_path = dest_dir + binary_name + ".pck";
+	String pack_path = binary_dir + ".pck";
 	Vector<SharedObject> libraries;
 	Vector<SharedObject> libraries;
 	Error err = save_pack(p_preset, p_debug, pack_path, &libraries);
 	Error err = save_pack(p_preset, p_debug, pack_path, &libraries);
 	if (err) {
 	if (err) {
+		// Message is supplied by the subroutine method.
 		return err;
 		return err;
 	}
 	}
 
 
@@ -1606,7 +1620,10 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 	Vector<IOSExportAsset> assets;
 	Vector<IOSExportAsset> assets;
 
 
 	Ref<DirAccess> tmp_app_path = DirAccess::create_for_path(dest_dir);
 	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...");
 	print_line("Unzipping...");
 	Ref<FileAccess> io_fa;
 	Ref<FileAccess> io_fa;
@@ -1617,8 +1634,14 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 		return ERR_CANT_OPEN;
 		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
 	//export rest of the files
 	int ret = unzGoToFirstFile(src_pkg_zip);
 	int ret = unzGoToFirstFile(src_pkg_zip);
@@ -1683,8 +1706,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 				print_line("Creating " + dir_name);
 				print_line("Creating " + dir_name);
 				Error dir_err = tmp_app_path->make_dir_recursive(dir_name);
 				Error dir_err = tmp_app_path->make_dir_recursive(dir_name);
 				if (dir_err) {
 				if (dir_err) {
-					ERR_PRINT("Can't create '" + dir_name + "'.");
 					unzClose(src_pkg_zip);
 					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;
 					return ERR_CANT_CREATE;
 				}
 				}
 			}
 			}
@@ -1693,8 +1716,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 			{
 			{
 				Ref<FileAccess> f = FileAccess::open(file, FileAccess::WRITE);
 				Ref<FileAccess> f = FileAccess::open(file, FileAccess::WRITE);
 				if (f.is_null()) {
 				if (f.is_null()) {
-					ERR_PRINT("Can't write '" + file + "'.");
 					unzClose(src_pkg_zip);
 					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;
 					return ERR_CANT_CREATE;
 				};
 				};
 				f->store_buffer(data.ptr(), data.size());
 				f->store_buffer(data.ptr(), data.size());
@@ -1715,7 +1738,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
 	unzClose(src_pkg_zip);
 	unzClose(src_pkg_zip);
 
 
 	if (!found_library) {
 	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;
 		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");
 	Vector<String> translations = GLOBAL_GET("internationalization/locale/translations");
 	if (translations.size() > 0) {
 	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);
 			tmp_app_path->make_dir_recursive(fname);
 			Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
 			Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
 			f->store_line("/* Localized versions of Info.plist keys */");
 			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) {
 		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);
 			tmp_app_path->make_dir_recursive(fname);
 			Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
 			Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
 			f->store_line("/* Localized versions of Info.plist keys */");
 			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();
 			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);
 			Error lib_copy_err = tmp_app_path->copy(static_lib_path, dest_lib_file_path);
 			if (lib_copy_err != OK) {
 			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;
 				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;
 	err = OK;
 	if (!tmp_app_path->dir_exists(iconset_dir)) {
 	if (!tmp_app_path->dir_exists(iconset_dir)) {
 		err = tmp_app_path->make_dir_recursive(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;
 		return err;
 	}
 	}
 
 
 	err = _export_icons(p_preset, iconset_dir);
 	err = _export_icons(p_preset, iconset_dir);
-	if (err) {
+	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 		return err;
 	}
 	}
 
 
 	{
 	{
 		bool use_storyboard = p_preset->get("storyboard/use_launch_screen_storyboard");
 		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);
 		Ref<DirAccess> launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 		if (launch_screen_da.is_null()) {
 		if (launch_screen_da.is_null()) {
+			add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not access the filesystem."));
 			return ERR_CANT_CREATE;
 			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);
 			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 {
 		} else {
 			print_line("Using Launch Images");
 			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);
 			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);
 			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;
 		return err;
 	}
 	}
 
 
 	print_line("Exporting additional assets");
 	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);
 	_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);
 		Ref<FileAccess> f = FileAccess::open(project_file_name, FileAccess::WRITE);
 		if (f.is_null()) {
 		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;
 			return ERR_CANT_CREATE;
 		};
 		};
 		f->store_buffer(project_file_data.ptr(), project_file_data.size());
 		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)) {
 		if (ep.step("Code-signing dylibs", 2)) {
 			return ERR_SKIP;
 			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);
 		ERR_FAIL_COND_V(dylibs_dir.is_null(), ERR_CANT_OPEN);
 		CodesignData codesign_data(p_preset, p_debug);
 		CodesignData codesign_data(p_preset, p_debug);
 		err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data);
 		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)) {
 	if (ep.step("Making .xcarchive", 3)) {
 		return ERR_SKIP;
 		return ERR_SKIP;
 	}
 	}
+
 	String archive_path = p_path.get_basename() + ".xcarchive";
 	String archive_path = p_path.get_basename() + ".xcarchive";
 	List<String> archive_args;
 	List<String> archive_args;
 	archive_args.push_back("-project");
 	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("-scheme");
 	archive_args.push_back(binary_name);
 	archive_args.push_back(binary_name);
 	archive_args.push_back("-sdk");
 	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("-allowProvisioningUpdates");
 	archive_args.push_back("-archivePath");
 	archive_args.push_back("-archivePath");
 	archive_args.push_back(archive_path);
 	archive_args.push_back(archive_path);
+
 	String archive_str;
 	String archive_str;
 	err = OS::get_singleton()->execute("xcodebuild", archive_args, &archive_str, nullptr, true);
 	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);
 	print_line("xcodebuild (.xcarchive):\n" + archive_str);
 	if (!archive_str.contains("** ARCHIVE SUCCEEDED **")) {
 	if (!archive_str.contains("** ARCHIVE SUCCEEDED **")) {
 		add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR("Xcode project build failed, see editor log for details."));
 		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)) {
 		if (ep.step("Making .ipa", 4)) {
 			return ERR_SKIP;
 			return ERR_SKIP;
 		}
 		}
+
 		List<String> export_args;
 		List<String> export_args;
 		export_args.push_back("-exportArchive");
 		export_args.push_back("-exportArchive");
 		export_args.push_back("-archivePath");
 		export_args.push_back("-archivePath");
 		export_args.push_back(archive_path);
 		export_args.push_back(archive_path);
 		export_args.push_back("-exportOptionsPlist");
 		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("-allowProvisioningUpdates");
 		export_args.push_back("-exportPath");
 		export_args.push_back("-exportPath");
 		export_args.push_back(dest_dir);
 		export_args.push_back(dest_dir);
+
 		String export_str;
 		String export_str;
 		err = OS::get_singleton()->execute("xcodebuild", export_args, &export_str, nullptr, true);
 		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);
 		print_line("xcodebuild (.ipa):\n" + export_str);
 		if (!export_str.contains("** EXPORT SUCCEEDED **")) {
 		if (!export_str.contains("** EXPORT SUCCEEDED **")) {
 			add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR(".ipa export failed, see editor log for details."));
 			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);
 	Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
 	if (export_as_zip) {
 	if (export_as_zip) {
 		if (tmp_app_dir.is_null()) {
 		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;
 			return ERR_CANT_CREATE;
 		}
 		}
 		if (DirAccess::exists(tmp_dir_path)) {
 		if (DirAccess::exists(tmp_dir_path)) {
@@ -93,19 +94,18 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset>
 	// Export project.
 	// Export project.
 	Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, path, p_flags);
 	Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, path, p_flags);
 	if (err != OK) {
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 		return err;
 	}
 	}
 
 
 	// Save console wrapper.
 	// 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) {
 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);
 	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);
 	EditorProgress ep("export", TTR("Exporting for macOS"), 3, true);
 
 
+	String src_pkg_name;
 	if (p_debug) {
 	if (p_debug) {
 		src_pkg_name = p_preset->get("custom_template/debug");
 		src_pkg_name = p_preset->get("custom_template/debug");
 	} else {
 	} else {
@@ -1280,16 +1286,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
 		String err;
 		String err;
 		src_pkg_name = find_export_template("macos.zip", &err);
 		src_pkg_name = find_export_template("macos.zip", &err);
 		if (src_pkg_name.is_empty()) {
 		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;
 			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;
 	Ref<FileAccess> io_fa;
 	zlib_filefunc_def io = zipio_create_io(&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);
 	_replace_strings(replaces, sw);
 	Error err = _write_or_error(sw.ptr(), sw.size(), dir.path_join(name + ".service.worker.js"));
 	Error err = _write_or_error(sw.ptr(), sw.size(), dir.path_join(name + ".service.worker.js"));
 	if (err != OK) {
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 		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");
 	const String icon144_path = p_preset->get("progressive_web_app/icon_144x144");
 	err = _add_manifest_icon(p_path, icon144_path, 144, icons_arr);
 	err = _add_manifest_icon(p_path, icon144_path, 144, icons_arr);
 	if (err != OK) {
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 		return err;
 	}
 	}
 	const String icon180_path = p_preset->get("progressive_web_app/icon_180x180");
 	const String icon180_path = p_preset->get("progressive_web_app/icon_180x180");
 	err = _add_manifest_icon(p_path, icon180_path, 180, icons_arr);
 	err = _add_manifest_icon(p_path, icon180_path, 180, icons_arr);
 	if (err != OK) {
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 		return err;
 	}
 	}
 	const String icon512_path = p_preset->get("progressive_web_app/icon_512x512");
 	const String icon512_path = p_preset->get("progressive_web_app/icon_512x512");
 	err = _add_manifest_icon(p_path, icon512_path, 512, icons_arr);
 	err = _add_manifest_icon(p_path, icon512_path, 512, icons_arr);
 	if (err != OK) {
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 		return err;
 	}
 	}
 	manifest["icons"] = icons_arr;
 	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();
 	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"));
 	err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.path_join(name + ".manifest.json"));
 	if (err != OK) {
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 		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_path = p_path.get_basename();
 	const String base_name = p_path.get_file().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
 	// Find the correct template
 	String template_path = p_debug ? custom_debug : custom_release;
 	String template_path = p_debug ? custom_debug : custom_release;
 	template_path = template_path.strip_edges();
 	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));
 		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)) {
 	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));
 		add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Template file not found: \"%s\"."), template_path));
 		return ERR_FILE_NOT_FOUND;
 		return ERR_FILE_NOT_FOUND;
@@ -480,6 +486,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
 	// Extract templates.
 	// Extract templates.
 	error = _extract_template(template_path, base_dir, base_name, pwa);
 	error = _extract_template(template_path, base_dir, base_name, pwa);
 	if (error) {
 	if (error) {
+		// Message is supplied by the subroutine method.
 		return error;
 		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);
 	_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);
 	Error err = _write_or_error(html.ptr(), html.size(), p_path);
 	if (err != OK) {
 	if (err != OK) {
+		// Message is supplied by the subroutine method.
 		return err;
 		return err;
 	}
 	}
 	html.resize(0);
 	html.resize(0);
@@ -543,6 +551,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
 	if (pwa) {
 	if (pwa) {
 		err = _build_pwa(p_preset, p_path, shared_objects);
 		err = _build_pwa(p_preset, p_path, shared_objects);
 		if (err != OK) {
 		if (err != OK) {
+			// Message is supplied by the subroutine method.
 			return err;
 			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);
 	Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
 	if (export_as_zip) {
 	if (export_as_zip) {
 		if (tmp_app_dir.is_null()) {
 		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;
 			return ERR_CANT_CREATE;
 		}
 		}
 		if (DirAccess::exists(tmp_dir_path)) {
 		if (DirAccess::exists(tmp_dir_path)) {
@@ -242,8 +243,14 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
 	if (embedded) {
 	if (embedded) {
 		pck_path = pck_path.get_basename() + ".tmp";
 		pck_path = pck_path.get_basename() + ".tmp";
 	}
 	}
+
 	Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags);
 	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);
 		_code_sign(p_preset, pck_path);
 		String wrapper_path = p_path.get_basename() + ".console.exe";
 		String wrapper_path = p_path.get_basename() + ".console.exe";
 		if (FileAccess::exists(wrapper_path)) {
 		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());
 		Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
 		err = tmp_dir->rename(pck_path, p_path);
 		err = tmp_dir->rename(pck_path, p_path);
 		if (err != OK) {
 		if (err != OK) {