From 33ce138b9b243c23bd9e418aab68672ea0502674 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Thu, 30 Nov 2023 16:57:38 +0200 Subject: [PATCH] [iOS one-click] Add support for Xcode 15 devicectl. --- platform/ios/export/export_plugin.cpp | 167 ++++++++++++++++++++------ platform/ios/export/export_plugin.h | 3 +- 2 files changed, 134 insertions(+), 36 deletions(-) diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index c0e052865fce..91174fa4339a 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -1493,7 +1493,7 @@ Error EditorExportPlatformIOS::export_project(const Ref &p_p return _export_project_helper(p_preset, p_debug, p_path, p_flags, false, false); } -Error EditorExportPlatformIOS::_export_project_helper(const Ref &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 &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_oneclick) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); String src_pkg_name; @@ -1501,6 +1501,9 @@ Error EditorExportPlatformIOS::_export_project_helper(const Refget("application/export_project_only"); + if (p_oneclick) { + export_project_only = false; // Skip for one-click deploy. + } EditorProgress ep("export", export_project_only ? TTR("Exporting for iOS (Project Files Only)") : TTR("Exporting for iOS"), export_project_only ? 2 : 5, true); @@ -1904,7 +1907,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref ldevices; - // Enum real devices. + // Enum real devices (via ios_deploy, pre Xcode 15). String idepl = EDITOR_GET("export/ios/ios_deploy"); - if (idepl.is_empty()) { - idepl = "ios-deploy"; - } - { + if (!idepl.is_empty()) { String devices; List args; args.push_back("-c"); @@ -2143,8 +2143,9 @@ void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) { Dictionary device_info = device_event["Device"]; Device nd; nd.id = device_info["DeviceIdentifier"]; - nd.name = device_info["DeviceName"].operator String() + " (connected through " + device_event["Interface"].operator String() + ")"; + nd.name = device_info["DeviceName"].operator String() + " (ios_deploy, " + ((device_event["Interface"] == "WIFI") ? "network" : "wired") + ")"; nd.wifi = device_event["Interface"] == "WIFI"; + nd.use_ios_deploy = true; nd.simulator = false; ldevices.push_back(nd); } @@ -2153,39 +2154,78 @@ void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) { } } - // Enum simulators + // Enum simulators. if (_check_xcode_install() && (FileAccess::exists("/usr/bin/xcrun") || FileAccess::exists("/bin/xcrun"))) { - String devices; - List args; - args.push_back("simctl"); - args.push_back("list"); - args.push_back("devices"); - args.push_back("-j"); - - int ec = 0; - Error err = OS::get_singleton()->execute("xcrun", args, &devices, &ec, true); - if (err == OK && ec == 0) { - Ref json; - json.instantiate(); - err = json->parse(devices); - if (err == OK) { - Dictionary data = json->get_data(); - Dictionary devices = data["devices"]; - for (const Variant *key = devices.next(nullptr); key; key = devices.next(key)) { - Array os_devices = devices[*key]; - for (int i = 0; i < os_devices.size(); i++) { - Dictionary device_info = os_devices[i]; - if (device_info["isAvailable"].operator bool() && device_info["state"] == "Booted") { + { + String devices; + List args; + args.push_back("devicectl"); + args.push_back("list"); + args.push_back("devices"); + args.push_back("-j"); + args.push_back("-"); + args.push_back("-q"); + int ec = 0; + Error err = OS::get_singleton()->execute("xcrun", args, &devices, &ec, true); + if (err == OK && ec == 0) { + Ref json; + json.instantiate(); + err = json->parse(devices); + if (err == OK) { + const Dictionary &data = json->get_data(); + const Dictionary &result = data["result"]; + const Array &devices = result["devices"]; + for (int i = 0; i < devices.size(); i++) { + const Dictionary &device_info = devices[i]; + const Dictionary &conn_props = device_info["connectionProperties"]; + const Dictionary &dev_props = device_info["deviceProperties"]; + if (conn_props["pairingState"] == "paired" && dev_props["developerModeStatus"] == "enabled") { Device nd; - nd.id = device_info["udid"]; - nd.name = device_info["name"].operator String() + " (simulator)"; - nd.simulator = true; + nd.id = device_info["identifier"]; + nd.name = dev_props["name"].operator String() + " (devicectl, " + ((conn_props["transportType"] == "localNetwork") ? "network" : "wired") + ")"; + nd.wifi = conn_props["transportType"] == "localNetwork"; + nd.simulator = false; ldevices.push_back(nd); } } } } } + + // Enum simulators. + { + String devices; + List args; + args.push_back("simctl"); + args.push_back("list"); + args.push_back("devices"); + args.push_back("-j"); + + int ec = 0; + Error err = OS::get_singleton()->execute("xcrun", args, &devices, &ec, true); + if (err == OK && ec == 0) { + Ref json; + json.instantiate(); + err = json->parse(devices); + if (err == OK) { + const Dictionary &data = json->get_data(); + const Dictionary &devices = data["devices"]; + for (const Variant *key = devices.next(nullptr); key; key = devices.next(key)) { + const Array &os_devices = devices[*key]; + for (int i = 0; i < os_devices.size(); i++) { + const Dictionary &device_info = os_devices[i]; + if (device_info["isAvailable"].operator bool() && device_info["state"] == "Booted") { + Device nd; + nd.id = device_info["udid"]; + nd.name = device_info["name"].operator String() + " (simulator)"; + nd.simulator = true; + ldevices.push_back(nd); + } + } + } + } + } + } } // Update device list. @@ -2346,6 +2386,7 @@ Error EditorExportPlatformIOS::run(const Ref &p_preset, int List args; args.push_back("simctl"); args.push_back("launch"); + args.push_back("--terminate-running-process"); args.push_back(dev.id); args.push_back(p_preset->get("application/bundle_identifier")); for (const String &E : cmd_args_list) { @@ -2364,8 +2405,8 @@ Error EditorExportPlatformIOS::run(const Ref &p_preset, int add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), TTR("Running failed, see editor log for details.")); } } - } else { - // Deploy and run on real device. + } else if (dev.use_ios_deploy) { + // Deploy and run on real device (via ios-deploy). if (ep.step("Installing and running on device...", 4)) { CLEANUP_AND_RETURN(ERR_SKIP); } else { @@ -2403,6 +2444,62 @@ Error EditorExportPlatformIOS::run(const Ref &p_preset, int CLEANUP_AND_RETURN(ERR_UNCONFIGURED); } } + } else { + // Deploy and run on real device. + if (ep.step("Installing to device...", 3)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } else { + List args; + args.push_back("devicectl"); + args.push_back("device"); + args.push_back("install"); + args.push_back("app"); + args.push_back("-d"); + args.push_back(dev.id); + args.push_back(EditorPaths::get_singleton()->get_cache_dir().path_join(id).path_join("export.xcarchive/Products/Applications/export.app")); + + String log; + int ec; + err = OS::get_singleton()->execute("xcrun", args, &log, &ec, true); + if (err != OK) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Run"), TTR("Could not start device executable.")); + CLEANUP_AND_RETURN(err); + } + if (ec != 0) { + print_line("device install:\n" + log); + add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), TTR("Installation failed, see editor log for details.")); + CLEANUP_AND_RETURN(ERR_UNCONFIGURED); + } + } + + if (ep.step("Running on device...", 4)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } else { + List args; + args.push_back("devicectl"); + args.push_back("device"); + args.push_back("process"); + args.push_back("launch"); + args.push_back("--terminate-existing"); + args.push_back("-d"); + args.push_back(dev.id); + args.push_back(p_preset->get("application/bundle_identifier")); + for (const String &E : cmd_args_list) { + args.push_back(E); + } + + String log; + int ec; + err = OS::get_singleton()->execute("xcrun", args, &log, &ec, true); + if (err != OK) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Run"), TTR("Could not start devicectl executable.")); + CLEANUP_AND_RETURN(err); + } + if (ec != 0) { + print_line("devicectl launch:\n" + log); + add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), TTR("Running failed, see editor log for details.")); + } + } } CLEANUP_AND_RETURN(OK); diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h index 951017ddae64..fd2243bcdaa8 100644 --- a/platform/ios/export/export_plugin.h +++ b/platform/ios/export/export_plugin.h @@ -70,6 +70,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { String name; bool simulator = false; bool wifi = false; + bool use_ios_deploy = false; }; Vector devices; @@ -139,7 +140,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { Error _export_additional_assets(const String &p_out_dir, const Vector &p_libraries, Vector &r_exported_assets); Error _export_ios_plugins(const Ref &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector &r_exported_assets, bool p_debug); - Error _export_project_helper(const Ref &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_skip_ipa); + Error _export_project_helper(const Ref &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_oneclick); bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const;