diff options
author | Sergey Minakov <naithar@icloud.com> | 2020-08-17 21:47:37 +0300 |
---|---|---|
committer | Sergey Minakov <naithar@icloud.com> | 2020-11-10 16:40:06 +0300 |
commit | 1f2f477e1ee28f9fac8b7fc17bbd6d4707c4e5e8 (patch) | |
tree | c570df03a94388ceb808320eae384fb1ade0fae6 | |
parent | 1f94a59859e3be74009aa12cf4dcdb25cb5a6c2f (diff) |
iOS Export: support multi-target plugin
Plugins can use 'binary_name.a' or 'binary_name.release.a' and 'binary_name.debug.a' for plugin library.
-rw-r--r-- | platform/iphone/export/export.cpp | 286 | ||||
-rw-r--r-- | platform/iphone/plugin/godot_plugin_config.h | 41 |
2 files changed, 200 insertions, 127 deletions
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index 7cca0f390d..cbef136247 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -111,8 +111,9 @@ class EditorExportPlatformIOS : public EditorExportPlatform { void _add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets); Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets); + Error _copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets); Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets); - Error _export_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets); + Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug); bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { String pname = p_package; @@ -1121,142 +1122,179 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese } } -Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) { +Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) { DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'."); + String binary_name = p_out_dir.get_file().get_basename(); - ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'."); - for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) { - String asset = p_assets[f_idx]; - if (!asset.begins_with("res://")) { - // either SDK-builtin or already a part of the export template - IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed }; - r_exported_assets.push_back(exported_asset); + DirAccess *da = DirAccess::create_for_path(p_asset); + if (!da) { + memdelete(filesystem_da); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + p_asset + "."); + } + bool file_exists = da->file_exists(p_asset); + bool dir_exists = da->dir_exists(p_asset); + if (!file_exists && !dir_exists) { + memdelete(da); + memdelete(filesystem_da); + return ERR_FILE_NOT_FOUND; + } + + String base_dir = p_asset.get_base_dir().replace("res://", ""); + String destination_dir; + String destination; + String asset_path; + + bool create_framework = false; + + 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); + + String file_name; + + if (!p_custom_file_name) { + file_name = p_asset.get_basename().get_file(); } else { - DirAccess *da = DirAccess::create_for_path(asset); - if (!da) { - memdelete(filesystem_da); - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + asset + "."); - } - bool file_exists = da->file_exists(asset); - bool dir_exists = da->dir_exists(asset); - if (!file_exists && !dir_exists) { - memdelete(da); - memdelete(filesystem_da); - return ERR_FILE_NOT_FOUND; - } + file_name = *p_custom_file_name; + } - String base_dir = asset.get_base_dir().replace("res://", ""); - String destination_dir; - String destination; - String asset_path; + String framework_name = file_name + ".framework"; - bool create_framework = false; + asset_path = asset_path.plus_file(framework_name); + destination_dir = p_out_dir.plus_file(asset_path); + destination = destination_dir.plus_file(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); - if (p_is_framework && 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); + String file_name; - String file_name = asset.get_basename().get_file(); - String framework_name = file_name + ".framework"; + if (!p_custom_file_name) { + file_name = p_asset.get_file(); + } else { + file_name = *p_custom_file_name; + } - asset_path = asset_path.plus_file(framework_name); - destination_dir = p_out_dir.plus_file(asset_path); - destination = destination_dir.plus_file(file_name); - create_framework = true; - } else if (p_is_framework && (asset.ends_with(".framework") || asset.ends_with(".xcframework"))) { - asset_path = String("dylibs").plus_file(base_dir); + asset_path = asset_path.plus_file(file_name); + destination_dir = p_out_dir.plus_file(asset_path); + destination = destination_dir; + } else { + asset_path = base_dir; - String file_name = asset.get_file(); - asset_path = asset_path.plus_file(file_name); - destination_dir = p_out_dir.plus_file(asset_path); - destination = destination_dir; - } else { - asset_path = base_dir; + String file_name; - String file_name = asset.get_file(); - 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); - } + if (!p_custom_file_name) { + file_name = p_asset.get_file(); + } else { + file_name = *p_custom_file_name; + } - if (!filesystem_da->dir_exists(destination_dir)) { - Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir); - if (make_dir_err) { - memdelete(da); - memdelete(filesystem_da); - return make_dir_err; - } - } + 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); + } - Error err = dir_exists ? da->copy_dir(asset, destination) : da->copy(asset, destination); + if (!filesystem_da->dir_exists(destination_dir)) { + Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir); + if (make_dir_err) { memdelete(da); - if (err) { - memdelete(filesystem_da); - return err; - } - IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed }; - r_exported_assets.push_back(exported_asset); + memdelete(filesystem_da); + return make_dir_err; + } + } - if (create_framework) { - String file_name = asset.get_basename().get_file(); - String framework_name = file_name + ".framework"; + Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination); + memdelete(da); + if (err) { + memdelete(filesystem_da); + return err; + } + IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed }; + r_exported_assets.push_back(exported_asset); - // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib - { - 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(destination); + if (create_framework) { + String file_name; - OS::get_singleton()->execute("install_name_tool", install_name_args, true); - } + if (!p_custom_file_name) { + file_name = p_asset.get_basename().get_file(); + } else { + file_name = *p_custom_file_name; + } - // Creating Info.plist - { - String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" - "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" - "<plist version=\"1.0\">\n" - "<dict>\n" - "<key>CFBundleShortVersionString</key>\n" - "<string>1.0</string>\n" - "<key>CFBundleIdentifier</key>\n" - "<string>com.gdnative.framework.$name</string>\n" - "<key>CFBundleName</key>\n" - "<string>$name</string>\n" - "<key>CFBundleExecutable</key>\n" - "<string>$name</string>\n" - "<key>DTPlatformName</key>\n" - "<string>iphoneos</string>\n" - "<key>CFBundleInfoDictionaryVersion</key>\n" - "<string>6.0</string>\n" - "<key>CFBundleVersion</key>\n" - "<string>1</string>\n" - "<key>CFBundlePackageType</key>\n" - "<string>FMWK</string>\n" - "<key>MinimumOSVersion</key>\n" - "<string>10.0</string>\n" - "</dict>\n" - "</plist>"; - - String info_plist = info_plist_format.replace("$name", file_name); - - FileAccess *f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE); - if (f) { - f->store_string(info_plist); - f->close(); - memdelete(f); - } - } + String framework_name = file_name + ".framework"; + + // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib + { + 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(destination); + + OS::get_singleton()->execute("install_name_tool", install_name_args, true); + } + + // Creating Info.plist + { + String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + "<plist version=\"1.0\">\n" + "<dict>\n" + "<key>CFBundleShortVersionString</key>\n" + "<string>1.0</string>\n" + "<key>CFBundleIdentifier</key>\n" + "<string>com.gdnative.framework.$name</string>\n" + "<key>CFBundleName</key>\n" + "<string>$name</string>\n" + "<key>CFBundleExecutable</key>\n" + "<string>$name</string>\n" + "<key>DTPlatformName</key>\n" + "<string>iphoneos</string>\n" + "<key>CFBundleInfoDictionaryVersion</key>\n" + "<string>6.0</string>\n" + "<key>CFBundleVersion</key>\n" + "<string>1</string>\n" + "<key>CFBundlePackageType</key>\n" + "<string>FMWK</string>\n" + "<key>MinimumOSVersion</key>\n" + "<string>10.0</string>\n" + "</dict>\n" + "</plist>"; + + String info_plist = info_plist_format.replace("$name", file_name); + + FileAccess *f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE); + if (f) { + f->store_string(info_plist); + f->close(); + memdelete(f); } } } + memdelete(filesystem_da); return OK; } +Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) { + for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) { + String asset = p_assets[f_idx]; + if (!asset.begins_with("res://")) { + // either SDK-builtin or already a part of the export template + IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed }; + r_exported_assets.push_back(exported_asset); + } else { + Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets); + ERR_FAIL_COND_V(err, err); + } + } + + return OK; +} + Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) { Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); for (int i = 0; i < export_plugins.size(); i++) { @@ -1302,12 +1340,11 @@ Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<Edit return enabled_archs; } -Error EditorExportPlatformIOS::_export_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets) { +Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug) { String plugin_definition_cpp_code; String plugin_initialization_cpp_code; String plugin_deinitialization_cpp_code; - Vector<String> plugin_libraries; Vector<String> plugin_linked_dependencies; Vector<String> plugin_embedded_dependencies; Vector<String> plugin_files; @@ -1318,11 +1355,24 @@ Error EditorExportPlatformIOS::_export_plugins(const Ref<EditorExportPreset> &p_ Vector<String> added_embedded_dependenciy_names; HashMap<String, String> plist_values; + Error err; + for (int i = 0; i < enabled_plugins.size(); i++) { PluginConfig plugin = enabled_plugins[i]; - // Adding plugin binary. - plugin_libraries.push_back(plugin.binary); + // Export plugin binary. + if (!plugin.supports_targets) { + err = _copy_asset(dest_dir, plugin.binary, nullptr, true, true, r_exported_assets); + } else { + String plugin_binary_dir = plugin.binary.get_base_dir(); + String plugin_name_prefix = plugin.binary.get_basename().get_file(); + String plugin_file = plugin_name_prefix + "." + (p_debug ? "debug" : "release") + ".a"; + String result_file_name = plugin.binary.get_file(); + + err = _copy_asset(dest_dir, plugin_binary_dir.plus_file(plugin_file), &result_file_name, true, true, r_exported_assets); + } + + ERR_FAIL_COND_V(err, err); // Adding dependencies. // Use separate container for names to check for duplicates. @@ -1421,10 +1471,6 @@ Error EditorExportPlatformIOS::_export_plugins(const Ref<EditorExportPreset> &p_ // Export files { - // Export plugin libraries - Error err = _export_additional_assets(dest_dir, plugin_libraries, true, true, r_exported_assets); - ERR_FAIL_COND_V(err, err); - // Export linked plugin dependency err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets); ERR_FAIL_COND_V(err, err); @@ -1587,7 +1633,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p return ERR_CANT_OPEN; } - err = _export_plugins(p_preset, config_data, dest_dir + binary_name, assets); + err = _export_ios_plugins(p_preset, config_data, dest_dir + binary_name, assets, p_debug); ERR_FAIL_COND_V(err, err); //export rest of the files diff --git a/platform/iphone/plugin/godot_plugin_config.h b/platform/iphone/plugin/godot_plugin_config.h index 4d7297d5b8..5323f94989 100644 --- a/platform/iphone/plugin/godot_plugin_config.h +++ b/platform/iphone/plugin/godot_plugin_config.h @@ -71,6 +71,7 @@ The `plist` section are optional. struct PluginConfig { // Set to true when the config file is properly loaded. bool valid_config = false; + bool supports_targets = false; // Unix timestamp of last change to this plugin. uint64_t last_updated = 0; @@ -158,18 +159,46 @@ static inline Vector<String> resolve_system_dependencies(Vector<String> p_paths) return paths; } -static inline bool is_plugin_config_valid(PluginConfig plugin_config) { +static inline bool validate_plugin(PluginConfig &plugin_config) { bool valid_name = !plugin_config.name.empty(); - bool valid_binary = !plugin_config.binary.empty() && FileAccess::exists(plugin_config.binary); + bool valid_binary_name = !plugin_config.binary.empty(); bool valid_initialize = !plugin_config.initialization_method.empty(); bool valid_deinitialize = !plugin_config.deinitialization_method.empty(); - return valid_name && valid_binary && valid_initialize && valid_deinitialize; + bool fields_value = valid_name && valid_binary_name && valid_initialize && valid_deinitialize; + + if (fields_value && FileAccess::exists(plugin_config.binary)) { + plugin_config.valid_config = true; + plugin_config.supports_targets = false; + } else if (fields_value) { + String file_path = plugin_config.binary.get_base_dir(); + String file_name = plugin_config.binary.get_basename().get_file(); + String release_file_name = file_path.plus_file(file_name + ".release.a"); + String debug_file_name = file_path.plus_file(file_name + ".debug.a"); + + if (FileAccess::exists(release_file_name) && FileAccess::exists(debug_file_name)) { + plugin_config.valid_config = true; + plugin_config.supports_targets = true; + } + } + + return plugin_config.valid_config; } static inline uint64_t get_plugin_modification_time(const PluginConfig &plugin_config, const String &config_path) { uint64_t last_updated = FileAccess::get_modified_time(config_path); - last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary)); + + if (!plugin_config.supports_targets) { + last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary)); + } else { + String file_path = plugin_config.binary.get_base_dir(); + String file_name = plugin_config.binary.get_basename().get_file(); + String release_file_name = file_path.plus_file(file_name + ".release.a"); + String debug_file_name = file_path.plus_file(file_name + ".debug.a"); + + last_updated = MAX(last_updated, FileAccess::get_modified_time(release_file_name)); + last_updated = MAX(last_updated, FileAccess::get_modified_time(debug_file_name)); + } return last_updated; } @@ -226,9 +255,7 @@ static inline PluginConfig load_plugin_config(Ref<ConfigFile> config_file, const } } - plugin_config.valid_config = is_plugin_config_valid(plugin_config); - - if (plugin_config.valid_config) { + if (validate_plugin(plugin_config)) { plugin_config.last_updated = get_plugin_modification_time(plugin_config, path); } |