From 621d408078362060edab9583df41501c5693161f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 10 Jun 2023 23:05:52 +0200 Subject: [PATCH] Add iOS app exporting --- .../Cooker/Platform/iOS/iOSPlatformTools.cpp | 50 ++++++++++++------- .../Cooker/Steps/PrecompileAssembliesStep.cpp | 1 + .../Engine/Platform/iOS/iOSPlatformSettings.h | 22 ++++++++ .../iOS/Binaries/Project/ExportOptions.plist | 14 ++++++ .../Flax.Build/Platforms/iOS/iOSToolchain.cs | 8 +-- 5 files changed, 74 insertions(+), 21 deletions(-) create mode 100644 Source/Platforms/iOS/Binaries/Project/ExportOptions.plist diff --git a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp index 09695840e..2fa621390 100644 --- a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp @@ -34,6 +34,18 @@ namespace return productName; } + const Char* GetExportMethod(iOSPlatformSettings::ExportMethods method) + { + switch (method) + { + case iOSPlatformSettings::ExportMethods::AppStore: return TEXT("app-store"); + case iOSPlatformSettings::ExportMethods::Development: return TEXT("development"); + case iOSPlatformSettings::ExportMethods::AdHoc: return TEXT("ad-hoc"); + case iOSPlatformSettings::ExportMethods::Enterprise: return TEXT("enterprise"); + default: return TEXT(""); + } + } + String GetUIInterfaceOrientation(iOSPlatformSettings::UIInterfaceOrientations orientations) { String result; @@ -196,6 +208,7 @@ bool iOSPlatformTools::OnPostProcess(CookingData& data) configReplaceMap[TEXT("${ProjectName}")] = gameSettings->ProductName; configReplaceMap[TEXT("${ProjectVersion}")] = projectVersion; configReplaceMap[TEXT("${HeaderSearchPaths}")] = Globals::StartupFolder; + configReplaceMap[TEXT("${ExportMethod}")] = GetExportMethod(platformSettings->ExportMethod); configReplaceMap[TEXT("${UISupportedInterfaceOrientations_iPhone}")] = GetUIInterfaceOrientation(platformSettings->SupportedInterfaceOrientationsiPhone); configReplaceMap[TEXT("${UISupportedInterfaceOrientations_iPad}")] = GetUIInterfaceOrientation(platformSettings->SupportedInterfaceOrientationsiPad); { @@ -232,13 +245,6 @@ bool iOSPlatformTools::OnPostProcess(CookingData& data) configReplaceMap[TEXT("${PBXFileReference}")] += String::Format(TEXT("\t\t{0} /* {1} */ = {{isa = PBXFileReference; lastKnownFileType = \"compiled.mach-o.dylib\"; name = \"{1}\"; path = \"FlaxGame/Data/{2}\"; sourceTree = \"\"; }};\n"), fileId, name, projectPath); configReplaceMap[TEXT("${PBXFrameworksBuildPhase}")] += String::Format(TEXT("\t\t\t\t{0} /* {1} in Frameworks */,\n"), frameworkId, name); configReplaceMap[TEXT("${PBXFrameworksGroup}")] += String::Format(TEXT("\t\t\t\t{0} /* {1} */,\n"), fileId, name); - - // Fix rpath id - // TODO: run this only for dylibs during AOT process (other libs are fine) - CreateProcessSettings proc; - proc.FileName = TEXT("install_name_tool"); - proc.Arguments = String::Format(TEXT("-id \"@rpath/{0}\" \"{1}\""), name, file); - Platform::CreateProcess(proc); } else { @@ -251,6 +257,7 @@ bool iOSPlatformTools::OnPostProcess(CookingData& data) } bool failed = false; failed |= EditorUtilities::ReplaceInFile(data.OriginalOutputPath / TEXT("FlaxGame.xcodeproj/project.pbxproj"), configReplaceMap); + failed |= EditorUtilities::ReplaceInFile(data.OriginalOutputPath / TEXT("ExportOptions.plist"), configReplaceMap); if (failed) { LOG(Error, "Failed to format XCode project"); @@ -275,19 +282,26 @@ bool iOSPlatformTools::OnPostProcess(CookingData& data) if (buildSettings->SkipPackaging) return false; GameCooker::PackageFiles(); - LOG(Info, "Building app package..."); - // TODO: run XCode archive and export -#if 0 - const String ipaPath = data.OriginalOutputPath / appName + TEXT(".ipa"); - const String ipaCommand = String::Format(TEXT("zip -r -X {0}.ipa Payload iTunesArtwork"), appName); - const int32 result = Platform::RunProcess(ipaCommand, data.OriginalOutputPath); - if (result != 0) { - data.Error(String::Format(TEXT("Failed to package app (result code: {0}). See log for more info."), result)); - return true; + LOG(Info, "Building app package..."); + const Char* configuration = data.Configuration == BuildConfiguration::Release ? TEXT("Release") : TEXT("Debug"); + String command = String::Format(TEXT("xcodebuild -project FlaxGame.xcodeproj -configuration {} -scheme FlaxGame -archivePath FlaxGame.xcarchive archive"), configuration); + int32 result = Platform::RunProcess(command, data.OriginalOutputPath); + if (result != 0) + { + data.Error(String::Format(TEXT("Failed to package app (result code: {0}). See log for more info."), result)); + return true; + } + command = TEXT("xcodebuild -exportArchive -archivePath FlaxGame.xcarchive -allowProvisioningUpdates -exportPath . -exportOptionsPlist ExportOptions.plist"); + result = Platform::RunProcess(command, data.OriginalOutputPath); + if (result != 0) + { + data.Error(String::Format(TEXT("Failed to package app (result code: {0}). See log for more info."), result)); + return true; + } + const String ipaPath = data.OriginalOutputPath / TEXT("FlaxGame.ipa"); + LOG(Info, "Output application package: {0} (size: {1} MB)", ipaPath, FileSystem::GetFileSize(ipaPath) / 1024 / 1024); } - LOG(Info, "Output application package: {0} (size: {1} MB)", ipaPath, FileSystem::GetFileSize(ipaPath) / 1024 / 1024); -#endif return false; } diff --git a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp index b28516630..deb8f5022 100644 --- a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp +++ b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp @@ -34,6 +34,7 @@ void PrecompileAssembliesStep::OnBuildStarted(CookingData& data) File::ReadAllText(aotModeCacheFilePath, cachedData); if (cachedData != aotModeCacheValue) { + LOG(Info, "AOT cache invalidation"); FileSystem::DeleteDirectory(data.ManagedCodeOutputPath); } } diff --git a/Source/Engine/Platform/iOS/iOSPlatformSettings.h b/Source/Engine/Platform/iOS/iOSPlatformSettings.h index 5794c18f5..2f0f13c3b 100644 --- a/Source/Engine/Platform/iOS/iOSPlatformSettings.h +++ b/Source/Engine/Platform/iOS/iOSPlatformSettings.h @@ -13,6 +13,21 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API { DECLARE_SCRIPTING_TYPE_MINIMAL(ApplePlatformSettings); + /// + /// The app export destination methods. + /// + API_ENUM() enum class ExportMethods + { + // Distribute using TestFlight or through the App Store. + AppStore, + // Distribute to a limited number of devices you register in App Store Connect. + Development, + // Distribute to a limited number of devices you register in App Store Connect. + AdHoc, + // Distribute to members of your organization if you’re a part of the Apple Developer Enterprise Program and are ready to release your app to users in your organization. + Enterprise, + }; + /// /// The display orientation modes. Can be combined as flags. /// @@ -42,6 +57,12 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API API_FIELD(Attributes="EditorOrder(20), EditorDisplay(\"General\")") String AppVersion = TEXT("1"); + /// + /// The app export mode (if automatic packaging is not disabled via Build Settings, otherwise export app manually via XCode project). + /// + API_FIELD(Attributes="EditorOrder(50), EditorDisplay(\"General\")") + ExportMethods ExportMethod = ExportMethods::Development; + /// /// The UI interface orientation modes supported on iPhone devices. /// @@ -65,6 +86,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API ApplePlatformSettings::Deserialize(stream, modifier); DESERIALIZE(AppTeamId); DESERIALIZE(AppVersion); + DESERIALIZE(ExportMethod); DESERIALIZE(SupportedInterfaceOrientationsiPhone); DESERIALIZE(SupportedInterfaceOrientationsiPad); } diff --git a/Source/Platforms/iOS/Binaries/Project/ExportOptions.plist b/Source/Platforms/iOS/Binaries/Project/ExportOptions.plist new file mode 100644 index 000000000..9c8037d53 --- /dev/null +++ b/Source/Platforms/iOS/Binaries/Project/ExportOptions.plist @@ -0,0 +1,14 @@ + + + + + method + ${ExportMethod} + + teamID + ${AppTeamId} + + compileBitcode + + + diff --git a/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs b/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs index 040a2e84a..fd4370e05 100644 --- a/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs @@ -51,9 +51,6 @@ namespace Flax.Build.Platforms options.LinkEnv.InputLibraries.Add("IOKit.framework"); options.LinkEnv.InputLibraries.Add("UIKit.framework"); options.LinkEnv.InputLibraries.Add("QuartzCore.framework"); - //options.LinkEnv.InputLibraries.Add("QuartzCore.framework"); - //options.LinkEnv.InputLibraries.Add("AudioToolbox.framework"); - //options.LinkEnv.InputLibraries.Add("AudioUnit.framework"); } protected override void AddArgsCommon(BuildOptions options, List args) @@ -124,6 +121,11 @@ namespace Flax.Build.Platforms File.Delete(inputFileAsm); File.Delete(inputFileObj); + // Fix rpath id + result = Utilities.Run("install_name_tool", $"-id \"@rpath/{Path.GetFileName(outputFileDylib)}\" \"{outputFileDylib}\"", null, inputFileFolder, Utilities.RunOptions.ConsoleLogOutput, envVars); + if (result != 0) + return true; + return false; } }