godot/platform/ios/export/export_plugin.cpp

/**************************************************************************/
/*  export_plugin.cpp                                                     */
/**************************************************************************/
/*                         This file is part of:                          */
/*                             GODOT ENGINE                               */
/*                        https://godotengine.org                         */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
/*                                                                        */
/* Permission is hereby granted, free of charge, to any person obtaining  */
/* a copy of this software and associated documentation files (the        */
/* "Software"), to deal in the Software without restriction, including    */
/* without limitation the rights to use, copy, modify, merge, publish,    */
/* distribute, sublicense, and/or sell copies of the Software, and to     */
/* permit persons to whom the Software is furnished to do so, subject to  */
/* the following conditions:                                              */
/*                                                                        */
/* The above copyright notice and this permission notice shall be         */
/* included in all copies or substantial portions of the Software.        */
/*                                                                        */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
/**************************************************************************/

#include "export_plugin.h"

#include "logo_svg.gen.h"
#include "run_icon_svg.gen.h"

#include "core/io/json.h"
#include "core/io/plist.h"
#include "core/string/translation.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_string_names.h"
#include "editor/export/editor_export.h"
#include "editor/export/lipo.h"
#include "editor/export/macho.h"
#include "editor/import/resource_importer_texture_settings.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/themes/editor_scale.h"

#include "modules/modules_enabled.gen.h" // For mono and svg.
#ifdef MODULE_SVG_ENABLED
#include "modules/svg/image_loader_svg.h"
#endif

void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {}

Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_get_supported_architectures() const {}

struct IconInfo {};

static const IconInfo icon_infos[] =;

struct APIAccessInfo {};

static const APIAccessInfo api_info[] =;

struct DataCollectionInfo {};

static const DataCollectionInfo data_collect_type_info[] =;

static const DataCollectionInfo data_collect_purpose_info[] =;

String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {}

void EditorExportPlatformIOS::_notification(int p_what) {}

bool EditorExportPlatformIOS::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const {}

void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) const {}

void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) {}

String EditorExportPlatformIOS::_get_additional_plist_content() {}

String EditorExportPlatformIOS::_get_linker_flags() {}

String EditorExportPlatformIOS::_get_cpp_code() {}

void EditorExportPlatformIOS::_blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot) {}

Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) {}

Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {}

Error EditorExportPlatformIOS::_walk_dir_recursive(Ref<DirAccess> &p_da, FileHandler p_handler, void *p_userdata) {}

struct CodesignData {};

Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) {}

struct PbxId {};

struct ExportLibsData {};

bool EditorExportPlatformIOS::_archive_has_arm64(const String &p_path, uint32_t *r_cputype, uint32_t *r_cpusubtype) const {}

int EditorExportPlatformIOS::_archive_convert_to_simulator(const String &p_path) const {}

void EditorExportPlatformIOS::_check_xcframework_content(const String &p_path, int &r_total_libs, int &r_static_libs, int &r_dylibs, int &r_frameworks) const {}

Error EditorExportPlatformIOS::_convert_to_framework(const String &p_source, const String &p_destination, const String &p_id) const {}

void EditorExportPlatformIOS::_add_assets_to_project(const String &p_out_dir, const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) {}

Error EditorExportPlatformIOS::_copy_asset(const Ref<EditorExportPreset> &p_preset, 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 EditorExportPlatformIOS::_export_additional_assets(const Ref<EditorExportPreset> &p_preset, 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::_export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) {}

Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<EditorExportPreset> &p_preset) const {}

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) {}

Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {}

Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, bool p_simulator, bool p_oneclick) {}

bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {}

bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {}

int EditorExportPlatformIOS::get_options_count() const {}

String EditorExportPlatformIOS::get_options_tooltip() const {}

Ref<ImageTexture> EditorExportPlatformIOS::get_option_icon(int p_index) const {}

String EditorExportPlatformIOS::get_option_label(int p_index) const {}

String EditorExportPlatformIOS::get_option_tooltip(int p_index) const {}

bool EditorExportPlatformIOS::is_package_name_valid(const String &p_package, String *r_error) const {}

#ifdef MACOS_ENABLED
bool EditorExportPlatformIOS::_check_xcode_install() {
	static bool xcode_found = false;
	if (!xcode_found) {
		Vector<String> mdfind_paths;
		List<String> mdfind_args;
		mdfind_args.push_back("kMDItemCFBundleIdentifier=com.apple.dt.Xcode");

		String output;
		Error err = OS::get_singleton()->execute("mdfind", mdfind_args, &output);
		if (err == OK) {
			mdfind_paths = output.split("\n");
		}
		for (const String &found_path : mdfind_paths) {
			xcode_found = !found_path.is_empty() && DirAccess::dir_exists_absolute(found_path.strip_edges());
			if (xcode_found) {
				break;
			}
		}
	}
	return xcode_found;
}

void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) {
	EditorExportPlatformIOS *ea = static_cast<EditorExportPlatformIOS *>(ud);

	while (!ea->quit_request.is_set()) {
		// Nothing to do if we already know the plugins have changed.
		if (!ea->plugins_changed.is_set()) {
			MutexLock lock(ea->plugins_lock);

			Vector<PluginConfigIOS> loaded_plugins = get_plugins();

			if (ea->plugins.size() != loaded_plugins.size()) {
				ea->plugins_changed.set();
			} else {
				for (int i = 0; i < ea->plugins.size(); i++) {
					if (ea->plugins[i].name != loaded_plugins[i].name || ea->plugins[i].last_updated != loaded_plugins[i].last_updated) {
						ea->plugins_changed.set();
						break;
					}
				}
			}
		}

		// Check for devices updates.
		Vector<Device> ldevices;

		// Enum real devices (via ios_deploy, pre Xcode 15).
		String idepl = EDITOR_GET("export/ios/ios_deploy");
		if (ea->has_runnable_preset.is_set() && !idepl.is_empty()) {
			String devices;
			List<String> args;
			args.push_back("-c");
			args.push_back("-timeout");
			args.push_back("1");
			args.push_back("-j");
			args.push_back("-u");
			args.push_back("-I");

			int ec = 0;
			Error err = OS::get_singleton()->execute(idepl, args, &devices, &ec, true);
			if (err == OK && ec == 0) {
				Ref<JSON> json;
				json.instantiate();
				devices = "{ \"devices\":[" + devices.replace("}{", "},{") + "]}";
				err = json->parse(devices);
				if (err == OK) {
					Dictionary data = json->get_data();
					Array devices = data["devices"];
					for (int i = 0; i < devices.size(); i++) {
						Dictionary device_event = devices[i];
						if (device_event["Event"] == "DeviceDetected") {
							Dictionary device_info = device_event["Device"];
							Device nd;
							nd.id = device_info["DeviceIdentifier"];
							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);
						}
					}
				}
			}
		}

		// Enum simulators.
		if (ea->has_runnable_preset.is_set() && _check_xcode_install() && (FileAccess::exists("/usr/bin/xcrun") || FileAccess::exists("/bin/xcrun"))) {
			{
				String devices;
				List<String> 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;
					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["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.
			if (ea->has_runnable_preset.is_set()) {
				String devices;
				List<String> 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;
					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.
		{
			MutexLock lock(ea->device_lock);

			bool different = false;

			if (ea->devices.size() != ldevices.size()) {
				different = true;
			} else {
				for (int i = 0; i < ea->devices.size(); i++) {
					if (ea->devices[i].id != ldevices[i].id) {
						different = true;
						break;
					}
				}
			}

			if (different) {
				ea->devices = ldevices;
				ea->devices_changed.set();
			}
		}

		uint64_t sleep = 200;
		uint64_t wait = 3000000;
		uint64_t time = OS::get_singleton()->get_ticks_usec();
		while (OS::get_singleton()->get_ticks_usec() - time < wait) {
			OS::get_singleton()->delay_usec(1000 * sleep);
			if (ea->quit_request.is_set()) {
				break;
			}
		}
	}
}

void EditorExportPlatformIOS::_update_preset_status() {
	const int preset_count = EditorExport::get_singleton()->get_export_preset_count();
	bool has_runnable = false;

	for (int i = 0; i < preset_count; i++) {
		const Ref<EditorExportPreset> &preset = EditorExport::get_singleton()->get_export_preset(i);
		if (preset->get_platform() == this && preset->is_runnable()) {
			has_runnable = true;
			break;
		}
	}

	if (has_runnable) {
		has_runnable_preset.set();
	} else {
		has_runnable_preset.clear();
	}
	devices_changed.set();
}
#endif

Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) {}

EditorExportPlatformIOS::EditorExportPlatformIOS() {}

EditorExportPlatformIOS::~EditorExportPlatformIOS() {}