// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using EpicGames.Core; namespace UnrealBuildTool.XcodeProjectXcconfig { static class XcodeSchemeFile { private static FileReference GetUserSchemeManagementFilePath(DirectoryReference ProjectFile, UnrealTargetPlatform? Platform) { return FileReference.Combine(XcodeUtils.ProjectDirPathForPlatform(ProjectFile, Platform), $"xcuserdata/{Environment.UserName}.xcuserdatad/xcschemes/xcschememanagement.plist"); } private static DirectoryReference GetProjectSchemeDirectory(DirectoryReference ProjectFile, UnrealTargetPlatform? Platform) { return DirectoryReference.Combine(XcodeUtils.ProjectDirPathForPlatform(ProjectFile, Platform), "xcshareddata/xcschemes"); } private static FileReference GetProjectSchemeFilePathForTarget(DirectoryReference ProjectFile, UnrealTargetPlatform? Platform, string TargetName) { return FileReference.Combine(GetProjectSchemeDirectory(ProjectFile, Platform), TargetName + ".xcscheme"); } public static void WriteSchemeFile(UnrealData UnrealData, UnrealTargetPlatform? Platform, string ProjectName, List RunTargets, string? BuildTargetGuid, string? IndexTargetGuid) { StringBuilder Content = new StringBuilder(); string ProductName = UnrealData.ProductName; string GameProjectPath = UnrealData.UProjectFileLocation != null ? UnrealData.UProjectFileLocation.FullName : ""; foreach (XcodeRunTarget RunTarget in RunTargets) { string TargetName = RunTarget.Name; string TargetGuid = RunTarget.Guid; bool bHasEditorConfiguration = RunTarget.BuildConfigList!.BuildConfigs.Any(x => x.Info.ProjectTarget!.TargetRules!.Type == TargetType.Editor); bool bUseEditorConfiguration = bHasEditorConfiguration && !UnrealData.bMakeProjectPerTarget && !XcodeProjectFileGenerator.bGenerateRunOnlyProject; string DefaultConfiguration = bUseEditorConfiguration ? "Development Editor" : "Development"; FileReference SchemeFilePath = GetProjectSchemeFilePathForTarget(UnrealData.XcodeProjectFileLocation, Platform, TargetName); DirectoryReference.CreateDirectory(SchemeFilePath.Directory); string? OldCommandLineArguments = null; if (FileReference.Exists(SchemeFilePath)) { string OldContents = File.ReadAllText(SchemeFilePath.FullName); int OldCommandLineArgumentsStart = OldContents.IndexOf("") + "".Length; int OldCommandLineArgumentsEnd = OldContents.IndexOf(""); if (OldCommandLineArgumentsStart != -1 && OldCommandLineArgumentsEnd != -1) { OldCommandLineArguments = OldContents.Substring(OldCommandLineArgumentsStart, OldCommandLineArgumentsEnd - OldCommandLineArgumentsStart); } } IEnumerable ScriptLines = new string[] { "# if a UBTGenerated plist doesn't exist, create it now for Xcode's dependency needs", "if [ ! -f "${PROJECT_DIR}/${INFOPLIST_FILE}" ]", "then", " echo Creating ${PROJECT_DIR}/${INFOPLIST_FILE}...", " touch "${PROJECT_DIR}/${INFOPLIST_FILE}"", "fi", "", "# if we had previously archived the build, then Xcode would have made a soft-link, but it will fail when building normally, so delete a soft-link", "if [ -L "${CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}.app" ]", "then", " rm "${CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}.app"", "fi", "", "# make sure we have a cookeddata directory if needed", "if [[ "${PLATFORM_NAME}" == "iphoneos" || "${PLATFORM_NAME}" == "appletvos" ]]", "then", " mkdir -p "${CONFIGURATION_BUILD_DIR}/../../../Saved/StagedBuilds/${UE_TARGET_PLATFORM_NAME}/cookeddata"", "fi", "", }; if (!XcodeProjectFileGenerator.bGenerateRunOnlyProject) { ScriptLines = ScriptLines.Concat(new string[] { "# Sometimes Xcode won't redo code sign iOS.app, leads to deployment fails", "# Force Xcode to redo code sign during each build", "if [ "${UE_PLATFORM_NAME}" != "Mac" ]", "then", " rm -f "${UE_BINARIES_DIR}/${PRODUCT_NAME}.app/Info.plist"", "fi", "", }); } // convert special characters to be xml happy IEnumerable Lines = ScriptLines.Concat(UnrealData.ExtraPreBuildScriptLines); Lines = Lines.Select(x => x.Replace("\"", """).Replace("&&", "&&")); Content.WriteLine(""); Content.WriteLine(""); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); if (String.IsNullOrEmpty(OldCommandLineArguments)) { if (bHasEditorConfiguration && !String.IsNullOrEmpty(GameProjectPath)) { Content.WriteLine(" "); if (UnrealData.bIsForeignProject) { Content.WriteLine(" "); Content.WriteLine(" "); } else { Content.WriteLine(" "); Content.WriteLine(" "); } Content.WriteLine(" "); } else if (UnrealData.TargetRules.Type == TargetType.Editor && UnrealData.UProjectFileLocation == null) { Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); } else if (UnrealData.TargetRules.Type == TargetType.Editor && UnrealData.TargetRules.Type != TargetType.Program && UnrealData.UProjectFileLocation == null) { Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); } } else { Content.WriteLine(" " + OldCommandLineArguments + ""); } Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(" "); Content.WriteLine(""); File.WriteAllText(SchemeFilePath.FullName, Content.ToString(), new UTF8Encoding()); Content.Clear(); } Content.WriteLine(""); Content.WriteLine(""); Content.WriteLine(""); Content.WriteLine(""); Content.WriteLine("\tSchemeUserState"); Content.WriteLine("\t"); foreach (XcodeRunTarget RunTarget in RunTargets) { Content.WriteLine("\t\t" + RunTarget.Name + ".xcscheme_^#shared#^_"); Content.WriteLine("\t\t"); Content.WriteLine("\t\t\torderHint"); Content.WriteLine("\t\t\t1"); Content.WriteLine("\t\t"); } Content.WriteLine("\t"); Content.WriteLine("\tSuppressBuildableAutocreation"); Content.WriteLine("\t"); foreach (XcodeRunTarget RunTarget in RunTargets) { Content.WriteLine("\t\t" + RunTarget.Guid + ""); Content.WriteLine("\t\t"); Content.WriteLine("\t\t\tprimary"); Content.WriteLine("\t\t\t"); Content.WriteLine("\t\t"); } if (BuildTargetGuid != null) { Content.WriteLine("\t\t" + BuildTargetGuid + ""); Content.WriteLine("\t\t"); Content.WriteLine("\t\t\tprimary"); Content.WriteLine("\t\t\t"); Content.WriteLine("\t\t"); } if (IndexTargetGuid != null) { Content.WriteLine("\t\t" + IndexTargetGuid + ""); Content.WriteLine("\t\t"); Content.WriteLine("\t\t\tprimary"); Content.WriteLine("\t\t\t"); Content.WriteLine("\t\t"); } Content.WriteLine("\t"); Content.WriteLine(""); Content.WriteLine(""); FileReference ManagementFile = GetUserSchemeManagementFilePath(UnrealData.XcodeProjectFileLocation, Platform); if (!DirectoryReference.Exists(ManagementFile.Directory)) { DirectoryReference.CreateDirectory(ManagementFile.Directory); } File.WriteAllText(ManagementFile.FullName, Content.ToString(), new UTF8Encoding()); } public static void CleanSchemeFile(UnrealData UnrealData, UnrealTargetPlatform? Platform) { // clean this up because we don't want it persisting if we narrow our project list DirectoryReference SchemeDir = GetProjectSchemeDirectory(UnrealData.XcodeProjectFileLocation, Platform); if (DirectoryReference.Exists(SchemeDir)) { DirectoryReference.Delete(SchemeDir, true); } } } }