diff options
Diffstat (limited to 'platform/ios/export')
-rw-r--r-- | platform/ios/export/export_plugin.cpp | 257 | ||||
-rw-r--r-- | platform/ios/export/export_plugin.h | 16 | ||||
-rw-r--r-- | platform/ios/export/godot_plugin_config.cpp | 15 | ||||
-rw-r--r-- | platform/ios/export/godot_plugin_config.h | 3 |
4 files changed, 183 insertions, 108 deletions
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 2ac44ccf8b..8e4d91ac50 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -34,7 +34,6 @@ #include "editor/editor_node.h" void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { - String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); // Vulkan and OpenGL ES 3.0 both mandate ETC2 support. r_features->push_back("etc2"); @@ -140,27 +139,27 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/photolibrary_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with Retina display - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with Retina HD display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPhone/iPod Touch with Retina display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPhone with Retina HD display - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with Retina display - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPad + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPad with Retina display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPad Pro - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // App Store - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with Retina display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Spotlight + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Spotlight on devices with Retina display r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color())); for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); } } @@ -359,8 +358,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ switch (image_scale_mode) { case 0: { - String logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - bool is_on = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + String logo_path = GLOBAL_GET("application/boot_splash/image"); + bool is_on = GLOBAL_GET("application/boot_splash/fullsize"); // If custom logo is not specified, Godot does not scale default one, so we should do the same. value = (is_on && logo_path.length() > 0) ? "scaleAspectFit" : "center"; } break; @@ -372,7 +371,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ strnew += lines[i].replace("$launch_screen_image_mode", value) + "\n"; } else if (lines[i].find("$launch_screen_background_color") != -1) { bool use_custom = p_preset->get("storyboard/use_custom_bg_color"); - Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); + Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : GLOBAL_GET("application/boot_splash/bg_color"); const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\""; Dictionary value_dictionary; @@ -385,34 +384,71 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ strnew += lines[i].replace("$launch_screen_background_color", value) + "\n"; } else if (lines[i].find("$pbx_locale_file_reference") != -1) { String locale_files; - Vector<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); + Vector<String> translations = GLOBAL_GET("internationalization/locale/translations"); if (translations.size() > 0) { - int index = 0; + HashSet<String> languages; for (const String &E : translations) { Ref<Translation> tr = ResourceLoader::load(E); - if (tr.is_valid()) { - String lang = tr->get_locale(); - locale_files += "D0BCFE4518AEBDA2004A" + itos(index).pad_zeros(4) + " /* " + lang + " */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = " + lang + "; path = " + lang + ".lproj/InfoPlist.strings; sourceTree = \"<group>\"; };"; + if (tr.is_valid() && tr->get_locale() != "en") { + languages.insert(tr->get_locale()); } + } + + int index = 0; + for (const String &lang : languages) { + locale_files += "D0BCFE4518AEBDA2004A" + itos(index).pad_zeros(4) + " /* " + lang + " */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = " + lang + "; path = " + lang + ".lproj/InfoPlist.strings; sourceTree = \"<group>\"; };\n"; index++; } } strnew += lines[i].replace("$pbx_locale_file_reference", locale_files); } else if (lines[i].find("$pbx_locale_build_reference") != -1) { String locale_files; - Vector<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); + Vector<String> translations = GLOBAL_GET("internationalization/locale/translations"); if (translations.size() > 0) { - int index = 0; + HashSet<String> languages; for (const String &E : translations) { Ref<Translation> tr = ResourceLoader::load(E); - if (tr.is_valid()) { - String lang = tr->get_locale(); - locale_files += "D0BCFE4518AEBDA2004A" + itos(index).pad_zeros(4) + " /* " + lang + " */,"; + if (tr.is_valid() && tr->get_locale() != "en") { + languages.insert(tr->get_locale()); } + } + + int index = 0; + for (const String &lang : languages) { + locale_files += "D0BCFE4518AEBDA2004A" + itos(index).pad_zeros(4) + " /* " + lang + " */,\n"; index++; } } strnew += lines[i].replace("$pbx_locale_build_reference", locale_files); + } else if (lines[i].find("$swift_runtime_migration") != -1) { + String value = !p_config.use_swift_runtime ? "" : "LastSwiftMigration = 1250;"; + strnew += lines[i].replace("$swift_runtime_migration", value) + "\n"; + } else if (lines[i].find("$swift_runtime_build_settings") != -1) { + String value = !p_config.use_swift_runtime ? "" : R"( + CLANG_ENABLE_MODULES = YES; + SWIFT_OBJC_BRIDGING_HEADER = "$binary/dummy.h"; + SWIFT_VERSION = 5.0; + )"; + value = value.replace("$binary", p_config.binary_name); + strnew += lines[i].replace("$swift_runtime_build_settings", value) + "\n"; + } else if (lines[i].find("$swift_runtime_fileref") != -1) { + String value = !p_config.use_swift_runtime ? "" : R"( + 90B4C2AA2680BC560039117A /* dummy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "dummy.h"; sourceTree = "<group>"; }; + 90B4C2B52680C7E90039117A /* dummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "dummy.swift"; sourceTree = "<group>"; }; + )"; + strnew += lines[i].replace("$swift_runtime_fileref", value) + "\n"; + } else if (lines[i].find("$swift_runtime_binary_files") != -1) { + String value = !p_config.use_swift_runtime ? "" : R"( + 90B4C2AA2680BC560039117A /* dummy.h */, + 90B4C2B52680C7E90039117A /* dummy.swift */, + )"; + strnew += lines[i].replace("$swift_runtime_binary_files", value) + "\n"; + } else if (lines[i].find("$swift_runtime_buildfile") != -1) { + String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B4C2B52680C7E90039117A /* dummy.swift */; };"; + strnew += lines[i].replace("$swift_runtime_buildfile", value) + "\n"; + } else if (lines[i].find("$swift_runtime_build_phase") != -1) { + String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift */,"; + strnew += lines[i].replace("$swift_runtime_build_phase", value) + "\n"; } else { strnew += lines[i] + "\n"; } @@ -502,26 +538,27 @@ struct IconInfo { const char *actual_size_side; const char *scale; const char *unscaled_size; + const bool force_opaque; }; static const IconInfo icon_infos[] = { // Home screen on iPhone - { "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60" }, - { "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40" }, - { "icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60" }, + { "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", false }, + { "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", false }, + { "icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false }, // Home screen on iPad - { "icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76" }, - { "icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76" }, - { "icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5" }, + { "icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", false }, + { "icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76", false }, + { "icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false }, // App Store - { "icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024" }, + { "icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true }, // Spotlight - { "icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40" }, - { "icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40" }, - { "icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40" } + { "icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40", false }, + { "icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false }, + { "icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false } }; Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) { @@ -537,18 +574,21 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr String icon_path = p_preset->get(info.preset_key); if (icon_path.length() == 0) { // Resize main app icon - icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); + icon_path = GLOBAL_GET("application/config/icon"); Ref<Image> img = memnew(Image); Error err = ImageLoader::load_image(icon_path, img); if (err != OK) { - ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); + return ERR_UNCONFIGURED; + } + if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); return ERR_UNCONFIGURED; } img->resize(side_size, side_size); err = img->save_png(p_iconset_dir + info.export_name); if (err) { - String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); - ERR_PRINT(err_str.utf8().get_data()); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); return err; } } else { @@ -556,11 +596,15 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr Ref<Image> img = memnew(Image); Error err = ImageLoader::load_image(icon_path, img); if (err != OK) { - ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); + return ERR_UNCONFIGURED; + } + if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); return ERR_UNCONFIGURED; } if (img->get_width() != side_size || img->get_height() != side_size) { - WARN_PRINT("Icon (" + String(info.preset_key) + "): '" + icon_path + "' has incorrect size (" + String::num_int64(img->get_width()) + "x" + String::num_int64(img->get_height()) + ") and was automatically resized to " + String::num_int64(side_size) + "x" + String::num_int64(side_size) + "."); + add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s): '%s' has incorrect size %s and was automatically resized to %s.", info.preset_key, icon_path, img->get_size(), Vector2i(side_size, side_size))); img->resize(side_size, side_size); err = img->save_png(p_iconset_dir + info.export_name); } else { @@ -568,8 +612,7 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr } if (err) { - String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); - ERR_PRINT(err_str.utf8().get_data()); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); return err; } } @@ -605,7 +648,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor if (custom_launch_image_2x.length() > 0 && custom_launch_image_3x.length() > 0) { Ref<Image> image; - String image_path = p_dest_dir.plus_file("splash@2x.png"); + String image_path = p_dest_dir.path_join("splash@2x.png"); image.instantiate(); Error err = image->load(custom_launch_image_2x); @@ -619,7 +662,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor } image.unref(); - image_path = p_dest_dir.plus_file("splash@3x.png"); + image_path = p_dest_dir.path_join("splash@3x.png"); image.instantiate(); err = image->load(custom_launch_image_3x); @@ -634,7 +677,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor } else { Ref<Image> splash; - const String splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + const String splash_path = GLOBAL_GET("application/boot_splash/image"); if (!splash_path.is_empty()) { splash.instantiate(); @@ -652,8 +695,8 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor // because Godot's own boot logo uses single image for all resolutions. // Also not using @1x image, because devices using this image variant // are not supported by iOS 9, which is minimal target. - const String splash_png_path_2x = p_dest_dir.plus_file("splash@2x.png"); - const String splash_png_path_3x = p_dest_dir.plus_file("splash@3x.png"); + const String splash_png_path_2x = p_dest_dir.path_join("splash@2x.png"); + const String splash_png_path_3x = p_dest_dir.path_join("splash@3x.png"); if (splash->save_png(splash_png_path_2x) != OK) { return ERR_FILE_CANT_WRITE; @@ -675,9 +718,9 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp LoadingScreenInfo info = loading_screen_infos[i]; String loading_screen_file = p_preset->get(info.preset_key); - Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); - String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color"); + String boot_logo_path = GLOBAL_GET("application/boot_splash/image"); + bool boot_logo_scale = GLOBAL_GET("application/boot_splash/fullsize"); if (loading_screen_file.size() > 0) { // Load custom loading screens, and resize if required. @@ -697,8 +740,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp img->resize(info.width, info.width / aspect_ratio); } } - Ref<Image> new_img = memnew(Image); - new_img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + Ref<Image> new_img = Image::create_empty(info.width, info.height, false, Image::FORMAT_RGBA8); new_img->fill(boot_bg_color); _blend_and_rotate(new_img, img, false); err = new_img->save_png(p_dest_dir + info.export_name); @@ -712,8 +754,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp } } else { // Generate loading screen from the splash screen - Ref<Image> img = memnew(Image); - img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + Ref<Image> img = Image::create_empty(info.width, info.height, false, Image::FORMAT_RGBA8); img->fill(boot_bg_color); Ref<Image> img_bs; @@ -768,7 +809,7 @@ Error EditorExportPlatformIOS::_walk_dir_recursive(Ref<DirAccess> &p_da, FileHan dirs.push_back(path); } } else { - Error err = p_handler(current_dir.plus_file(path), p_userdata); + Error err = p_handler(current_dir.path_join(path), p_userdata); if (err) { p_da->list_dir_end(); return err; @@ -984,7 +1025,7 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String if (p_is_framework && p_asset.ends_with(".dylib")) { // For iOS we need to turn .dylib into .framework // to be able to send application to AppStore - asset_path = String("dylibs").plus_file(base_dir); + asset_path = String("dylibs").path_join(base_dir); String file_name; @@ -996,12 +1037,12 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String String framework_name = file_name + ".framework"; - asset_path = asset_path.plus_file(framework_name); - destination_dir = p_out_dir.plus_file(asset_path); - destination = destination_dir.plus_file(file_name); + asset_path = asset_path.path_join(framework_name); + destination_dir = p_out_dir.path_join(asset_path); + destination = destination_dir.path_join(file_name); create_framework = true; } else if (p_is_framework && (p_asset.ends_with(".framework") || p_asset.ends_with(".xcframework"))) { - asset_path = String("dylibs").plus_file(base_dir); + asset_path = String("dylibs").path_join(base_dir); String file_name; @@ -1011,8 +1052,8 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String file_name = *p_custom_file_name; } - asset_path = asset_path.plus_file(file_name); - destination_dir = p_out_dir.plus_file(asset_path); + asset_path = asset_path.path_join(file_name); + destination_dir = p_out_dir.path_join(asset_path); destination = destination_dir; } else { asset_path = base_dir; @@ -1025,9 +1066,9 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String file_name = *p_custom_file_name; } - destination_dir = p_out_dir.plus_file(asset_path); - asset_path = asset_path.plus_file(file_name); - destination = p_out_dir.plus_file(asset_path); + destination_dir = p_out_dir.path_join(asset_path); + asset_path = asset_path.path_join(file_name); + destination = p_out_dir.path_join(asset_path); } Ref<DirAccess> filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); @@ -1044,7 +1085,7 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String if (err) { return err; } - IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed }; + IOSExportAsset exported_asset = { binary_name.path_join(asset_path), p_is_framework, p_should_embed }; r_exported_assets.push_back(exported_asset); if (create_framework) { @@ -1062,7 +1103,7 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String { List<String> install_name_args; install_name_args.push_back("-id"); - install_name_args.push_back(String("@rpath").plus_file(framework_name).plus_file(file_name)); + install_name_args.push_back(String("@rpath").path_join(framework_name).path_join(file_name)); install_name_args.push_back(destination); OS::get_singleton()->execute("install_name_tool", install_name_args); @@ -1097,7 +1138,7 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String String info_plist = info_plist_format.replace("$name", file_name); - Ref<FileAccess> f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE); + Ref<FileAccess> f = FileAccess::open(destination_dir.path_join("Info.plist"), FileAccess::WRITE); if (f.is_valid()) { f->store_string(info_plist); } @@ -1298,6 +1339,10 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> plugin_initialization_cpp_code += "\t" + initialization_method; plugin_deinitialization_cpp_code += "\t" + deinitialization_method; + + if (plugin.use_swift_runtime) { + p_config_data.use_swift_runtime = true; + } } // Updating `Info.plist` @@ -1449,8 +1494,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p print_line("Static framework: " + library_to_use); String pkg_name; - if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + if (String(GLOBAL_GET("application/config/name")) != "") { + pkg_name = String(GLOBAL_GET("application/config/name")); } else { pkg_name = "Unnamed"; } @@ -1479,7 +1524,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p "", "", "", - Vector<String>() + Vector<String>(), + false }; Vector<IOSExportAsset> assets; @@ -1598,12 +1644,12 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p return ERR_FILE_NOT_FOUND; } - Dictionary appnames = ProjectSettings::get_singleton()->get("application/config/name_localized"); + Dictionary appnames = GLOBAL_GET("application/config/name_localized"); Dictionary camera_usage_descriptions = p_preset->get("privacy/camera_usage_description_localized"); Dictionary microphone_usage_descriptions = p_preset->get("privacy/microphone_usage_description_localized"); Dictionary photolibrary_usage_descriptions = p_preset->get("privacy/photolibrary_usage_description_localized"); - Vector<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); + Vector<String> translations = GLOBAL_GET("internationalization/locale/translations"); if (translations.size() > 0) { { String fname = dest_dir + binary_name + "/en.lproj"; @@ -1611,33 +1657,37 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE); f->store_line("/* Localized versions of Info.plist keys */"); f->store_line(""); - f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";"); + f->store_line("CFBundleDisplayName = \"" + GLOBAL_GET("application/config/name").operator String() + "\";"); f->store_line("NSCameraUsageDescription = \"" + p_preset->get("privacy/camera_usage_description").operator String() + "\";"); f->store_line("NSMicrophoneUsageDescription = \"" + p_preset->get("privacy/microphone_usage_description").operator String() + "\";"); f->store_line("NSPhotoLibraryUsageDescription = \"" + p_preset->get("privacy/photolibrary_usage_description").operator String() + "\";"); } + HashSet<String> languages; for (const String &E : translations) { Ref<Translation> tr = ResourceLoader::load(E); - if (tr.is_valid()) { - String lang = tr->get_locale(); - String fname = dest_dir + binary_name + "/" + 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 */"); - f->store_line(""); - if (appnames.has(lang)) { - f->store_line("CFBundleDisplayName = \"" + appnames[lang].operator String() + "\";"); - } - if (camera_usage_descriptions.has(lang)) { - f->store_line("NSCameraUsageDescription = \"" + camera_usage_descriptions[lang].operator String() + "\";"); - } - if (microphone_usage_descriptions.has(lang)) { - f->store_line("NSMicrophoneUsageDescription = \"" + microphone_usage_descriptions[lang].operator String() + "\";"); - } - if (photolibrary_usage_descriptions.has(lang)) { - f->store_line("NSPhotoLibraryUsageDescription = \"" + photolibrary_usage_descriptions[lang].operator String() + "\";"); - } + if (tr.is_valid() && tr->get_locale() != "en") { + languages.insert(tr->get_locale()); + } + } + + for (const String &lang : languages) { + String fname = dest_dir + binary_name + "/" + 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 */"); + f->store_line(""); + if (appnames.has(lang)) { + f->store_line("CFBundleDisplayName = \"" + appnames[lang].operator String() + "\";"); + } + if (camera_usage_descriptions.has(lang)) { + f->store_line("NSCameraUsageDescription = \"" + camera_usage_descriptions[lang].operator String() + "\";"); + } + if (microphone_usage_descriptions.has(lang)) { + f->store_line("NSMicrophoneUsageDescription = \"" + microphone_usage_descriptions[lang].operator String() + "\";"); + } + if (photolibrary_usage_descriptions.has(lang)) { + f->store_line("NSPhotoLibraryUsageDescription = \"" + photolibrary_usage_descriptions[lang].operator String() + "\";"); } } } @@ -1783,7 +1833,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p return OK; } -bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { +bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { String err; bool valid = false; @@ -1808,7 +1858,18 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset valid = dvalid || rvalid; r_missing_templates = !valid; - // Validate the rest of the configuration. + if (!err.is_empty()) { + r_error = err; + } + + return valid; +} + +bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const { + String err; + bool valid = true; + + // Validate the project configuration. String team_id = p_preset->get("application/app_store_team_id"); if (team_id.length() == 0) { @@ -1839,10 +1900,14 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset EditorExportPlatformIOS::EditorExportPlatformIOS() { logo = ImageTexture::create_from_image(memnew(Image(_ios_logo))); plugins_changed.set(); +#ifndef ANDROID_ENABLED check_for_changes_thread.start(_check_for_changes_poll_thread, this); +#endif } EditorExportPlatformIOS::~EditorExportPlatformIOS() { +#ifndef ANDROID_ENABLED quit_request.set(); check_for_changes_thread.wait_to_finish(); +#endif } diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h index 07e30c1d00..639f2416a5 100644 --- a/platform/ios/export/export_plugin.h +++ b/platform/ios/export/export_plugin.h @@ -57,8 +57,10 @@ class EditorExportPlatformIOS : public EditorExportPlatform { // Plugins SafeFlag plugins_changed; +#ifndef ANDROID_ENABLED Thread check_for_changes_thread; SafeFlag quit_request; +#endif Mutex plugins_lock; Vector<PluginConfigIOS> plugins; @@ -79,6 +81,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { String modules_buildphase; String modules_buildgrp; Vector<String> capabilities; + bool use_swift_runtime; }; struct ExportArchitecture { String name; @@ -138,6 +141,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { return true; } +#ifndef ANDROID_ENABLED static void _check_for_changes_poll_thread(void *ud) { EditorExportPlatformIOS *ea = static_cast<EditorExportPlatformIOS *>(ud); @@ -171,6 +175,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { } } } +#endif protected: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override; @@ -197,7 +202,8 @@ public: } virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override; virtual void get_platform_features(List<String> *r_features) const override { r_features->push_back("mobile"); @@ -232,9 +238,9 @@ public: if (da->current_is_dir()) { if (p_check_directories) { - Vector<String> directory_files = list_plugin_config_files(p_path.plus_file(file), false); + Vector<String> directory_files = list_plugin_config_files(p_path.path_join(file), false); for (int i = 0; i < directory_files.size(); ++i) { - dir_files.push_back(file.plus_file(directory_files[i])); + dir_files.push_back(file.path_join(directory_files[i])); } } @@ -254,7 +260,7 @@ public: static Vector<PluginConfigIOS> get_plugins() { Vector<PluginConfigIOS> loaded_plugins; - String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("ios/plugins"); + String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().path_join("ios/plugins"); if (DirAccess::exists(plugins_dir)) { Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true); @@ -262,7 +268,7 @@ public: if (!plugins_filenames.is_empty()) { Ref<ConfigFile> config_file = memnew(ConfigFile); for (int i = 0; i < plugins_filenames.size(); i++) { - PluginConfigIOS config = PluginConfigIOS::load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i])); + PluginConfigIOS config = PluginConfigIOS::load_plugin_config(config_file, plugins_dir.path_join(plugins_filenames[i])); if (config.valid_config) { loaded_plugins.push_back(config); } else { diff --git a/platform/ios/export/godot_plugin_config.cpp b/platform/ios/export/godot_plugin_config.cpp index 9118b95337..42797d449b 100644 --- a/platform/ios/export/godot_plugin_config.cpp +++ b/platform/ios/export/godot_plugin_config.cpp @@ -46,7 +46,7 @@ String PluginConfigIOS::resolve_local_dependency_path(String plugin_config_dir, } String res_path = ProjectSettings::get_singleton()->globalize_path("res://"); - absolute_path = plugin_config_dir.plus_file(dependency_path); + absolute_path = plugin_config_dir.path_join(dependency_path); return absolute_path.replace(res_path, "res://"); } @@ -64,7 +64,7 @@ String PluginConfigIOS::resolve_system_dependency_path(String dependency_path) { String system_path = "/System/Library/Frameworks"; - return system_path.plus_file(dependency_path); + return system_path.path_join(dependency_path); } Vector<String> PluginConfigIOS::resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths) { @@ -121,8 +121,8 @@ bool PluginConfigIOS::validate_plugin(PluginConfigIOS &plugin_config) { String file_path = plugin_config.binary.get_base_dir(); String file_name = plugin_config.binary.get_basename().get_file(); String file_extension = plugin_config.binary.get_extension(); - String release_file_name = file_path.plus_file(file_name + ".release." + file_extension); - String debug_file_name = file_path.plus_file(file_name + ".debug." + file_extension); + String release_file_name = file_path.path_join(file_name + ".release." + file_extension); + String debug_file_name = file_path.path_join(file_name + ".debug." + file_extension); if ((plugin_extension == "a" && FileAccess::exists(release_file_name) && FileAccess::exists(debug_file_name)) || (plugin_extension == "xcframework" && DirAccess::exists(release_file_name) && DirAccess::exists(debug_file_name))) { @@ -144,7 +144,7 @@ String PluginConfigIOS::get_plugin_main_binary(PluginConfigIOS &plugin_config, b String plugin_extension = plugin_config.binary.get_extension(); String plugin_file = plugin_name_prefix + "." + (p_debug ? "debug" : "release") + "." + plugin_extension; - return plugin_binary_dir.plus_file(plugin_file); + return plugin_binary_dir.path_join(plugin_file); } uint64_t PluginConfigIOS::get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path) { @@ -156,8 +156,8 @@ uint64_t PluginConfigIOS::get_plugin_modification_time(const PluginConfigIOS &pl String file_path = plugin_config.binary.get_base_dir(); String file_name = plugin_config.binary.get_basename().get_file(); String plugin_extension = plugin_config.binary.get_extension(); - String release_file_name = file_path.plus_file(file_name + ".release." + plugin_extension); - String debug_file_name = file_path.plus_file(file_name + ".debug." + plugin_extension); + String release_file_name = file_path.path_join(file_name + ".release." + plugin_extension); + String debug_file_name = file_path.path_join(file_name + ".debug." + plugin_extension); last_updated = MAX(last_updated, FileAccess::get_modified_time(release_file_name)); last_updated = MAX(last_updated, FileAccess::get_modified_time(debug_file_name)); @@ -184,6 +184,7 @@ PluginConfigIOS PluginConfigIOS::load_plugin_config(Ref<ConfigFile> config_file, String config_base_dir = path.get_base_dir(); plugin_config.name = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_NAME_KEY, String()); + plugin_config.use_swift_runtime = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_USE_SWIFT_KEY, false); plugin_config.initialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_INITIALIZE_KEY, String()); plugin_config.deinitialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_DEINITIALIZE_KEY, String()); diff --git a/platform/ios/export/godot_plugin_config.h b/platform/ios/export/godot_plugin_config.h index 5ca8b05b42..98e8456ed5 100644 --- a/platform/ios/export/godot_plugin_config.h +++ b/platform/ios/export/godot_plugin_config.h @@ -39,6 +39,7 @@ The `config` section and fields are required and defined as follow: - **name**: name of the plugin - **binary**: path to static `.a` library +- **use_swift_runtime**: optional boolean field used to determine if Swift runtime is used The `dependencies` and fields are optional. - **linked**: dependencies that should only be linked. @@ -57,6 +58,7 @@ struct PluginConfigIOS { inline static const char *CONFIG_SECTION = "config"; inline static const char *CONFIG_NAME_KEY = "name"; inline static const char *CONFIG_BINARY_KEY = "binary"; + inline static const char *CONFIG_USE_SWIFT_KEY = "use_swift_runtime"; inline static const char *CONFIG_INITIALIZE_KEY = "initialization"; inline static const char *CONFIG_DEINITIALIZE_KEY = "deinitialization"; @@ -93,6 +95,7 @@ struct PluginConfigIOS { // Required config section String name; String binary; + bool use_swift_runtime; String initialization_method; String deinitialization_method; |