// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using EpicGames.Core; using UnrealBuildTool; namespace AutomationTool { /// /// Commandlet to finalize the creation of an installed build - creating an InstalledBuild.txt file and writing /// out installed platform entries for all platforms/configurations where a UnrealGame .target file can be found /// [Help("Command to perform additional steps to prepare an installed build.")] [Help("OutputDir=", "Root Directory of the installed build data (required)")] [Help("ContentOnlyPlatforms=", "List of platforms that should be marked as only supporting content projects (optional)")] [Help("Platforms=)", "List of platforms to add")] [Help("Architectures=)", "List of architectures that are used for a given platform (optional)")] [Help("GPUArchitectures=)", "List of GPU architectures that are used for a given platform (optional)")] [Help("AnalyticsTypeOverride=", "Name to give this build for analytics purposes (optional)")] [Help("BuildId=", "Unique identifier for this build (optional)")] class FinalizeInstalledBuild : BuildCommand { /// /// Entry point for the commandlet /// public override void ExecuteBuild() { string OutputDir = ParseRequiredStringParam("OutputDir"); List Platforms = ParsePlatformsParamValue("Platforms"); List ContentOnlyPlatforms = ParsePlatformsParamValue("ContentOnlyPlatforms"); string AnalyticsTypeOverride = ParseParamValue("AnalyticsTypeOverride"); string BuildId = ParseOptionalStringParam("BuildId"); // Write InstalledBuild.txt to indicate Engine is installed string InstalledBuildFile = CommandUtils.CombinePaths(OutputDir, "Engine/Build/InstalledBuild.txt"); CommandUtils.CreateDirectory(CommandUtils.GetDirectoryName(InstalledBuildFile)); // Write build Identifier into InstalledBuild.txt if(String.IsNullOrWhiteSpace(BuildId)) { BuildId = Guid.NewGuid().ToString().ToUpper(); } CommandUtils.WriteAllText(InstalledBuildFile, BuildId); // Write InstalledBuild.txt to indicate Engine is installed string Project = ParseParamValue("Project"); if(Project != null) { string InstalledProjectBuildFile = CommandUtils.CombinePaths(OutputDir, "Engine/Build/InstalledProjectBuild.txt"); CommandUtils.CreateDirectory(CommandUtils.GetDirectoryName(InstalledProjectBuildFile)); CommandUtils.WriteAllText(InstalledProjectBuildFile, new FileReference(Project).MakeRelativeTo(new DirectoryReference(OutputDir))); } string OutputEnginePath = Path.Combine(OutputDir, "Engine"); string OutputBaseEnginePath = Path.Combine(OutputEnginePath, "Config", "BaseEngine.ini"); FileAttributes OutputAttributes = FileAttributes.ReadOnly; List IniLines = new List(); // Should always exist but if not, we don't need extra line if (File.Exists(OutputBaseEnginePath)) { OutputAttributes = File.GetAttributes(OutputBaseEnginePath); IniLines.Add(""); } else { CommandUtils.CreateDirectory(CommandUtils.GetDirectoryName(OutputBaseEnginePath)); CommandUtils.WriteAllText(OutputBaseEnginePath, ""); OutputAttributes = File.GetAttributes(OutputBaseEnginePath) | OutputAttributes; } // Create list of platform configurations installed in a Rocket build List InstalledConfigs = new List(); // Add the editor platform/architecture(s) for unspecified target, otherwise we'll never be able to run UAT UnrealArchitectures EditorArchitectures = GetArchitecturesForPlatform(HostPlatform.Current.HostEditorPlatform); foreach (UnrealArch EditorArchitecture in EditorArchitectures.Architectures) { InstalledConfigs.Add(new InstalledPlatformInfo.InstalledPlatformConfiguration(UnrealTargetConfiguration.Development, HostPlatform.Current.HostEditorPlatform, TargetRules.TargetType.Editor, EditorArchitecture, "", EProjectType.Unknown, false)); InstalledConfigs.Add(new InstalledPlatformInfo.InstalledPlatformConfiguration(UnrealTargetConfiguration.DebugGame, HostPlatform.Current.HostEditorPlatform, TargetRules.TargetType.Editor, EditorArchitecture, "", EProjectType.Unknown, false)); } foreach (UnrealTargetPlatform CodeTargetPlatform in Platforms) { // Build a list of pre-compiled architecture combinations for this platform if any UnrealArchitectures AllArchitectures = GetArchitecturesForPlatform(CodeTargetPlatform); // Check whether this platform should only be used for content based projects EProjectType ProjectType = ContentOnlyPlatforms.Contains(CodeTargetPlatform) ? EProjectType.Content : EProjectType.Any; // Allow Content only platforms to be shown as options in all projects bool bCanBeDisplayed = ProjectType == EProjectType.Content; foreach (UnrealTargetConfiguration CodeTargetConfiguration in Enum.GetValues(typeof(UnrealTargetConfiguration))) { Dictionary Targets = new Dictionary() { { "UnrealGame", TargetType.Game }, { "UnrealClient", TargetType.Client }, { "UnrealServer", TargetType.Server } }; foreach (KeyValuePair Target in Targets) { string CurrentTargetName = Target.Key; TargetType CurrentTargetType = Target.Value; // Need to check for development receipt as we use that for the Engine code in DebugGame UnrealTargetConfiguration EngineConfiguration = (CodeTargetConfiguration == UnrealTargetConfiguration.DebugGame) ? UnrealTargetConfiguration.Development : CodeTargetConfiguration; // if the platform doesn't split up multi-arch into multiple targets (which equate to receipt files) then make multiple configs from one receipt if (UnrealArchitectureConfig.ForPlatform(CodeTargetPlatform).Mode != UnrealArchitectureMode.OneTargetPerArchitecture) { FileReference ReceiptFileName = TargetReceipt.GetDefaultPath(new DirectoryReference(OutputEnginePath), CurrentTargetName, CodeTargetPlatform, EngineConfiguration, AllArchitectures); if (FileReference.Exists(ReceiptFileName)) { // Strip the output folder so that this can be used on any machine string RelativeReceiptFileName = ReceiptFileName.MakeRelativeTo(new DirectoryReference(OutputDir)); // Blindly append all of the architecture names foreach (UnrealArch Arch in AllArchitectures.Architectures) { InstalledConfigs.Add(new InstalledPlatformInfo.InstalledPlatformConfiguration(CodeTargetConfiguration, CodeTargetPlatform, CurrentTargetType, Arch, RelativeReceiptFileName, ProjectType, bCanBeDisplayed)); } } } // otherwise, look for receipts for each specified architecture else { foreach (UnrealArch Arch in AllArchitectures.Architectures) { FileReference ReceiptFileName = TargetReceipt.GetDefaultPath(new DirectoryReference(OutputEnginePath), CurrentTargetName, CodeTargetPlatform, EngineConfiguration, AllArchitectures); if (FileReference.Exists(ReceiptFileName)) { string RelativeReceiptFileName = ReceiptFileName.MakeRelativeTo(new DirectoryReference(OutputDir)); InstalledConfigs.Add(new InstalledPlatformInfo.InstalledPlatformConfiguration(CodeTargetConfiguration, CodeTargetPlatform, CurrentTargetType, Arch, RelativeReceiptFileName, ProjectType, bCanBeDisplayed)); } } } } } } UnrealBuildTool.InstalledPlatformInfo.WriteConfigFileEntries(InstalledConfigs, ref IniLines); if (!String.IsNullOrEmpty(AnalyticsTypeOverride)) { // Write Custom Analytics type setting IniLines.Add(""); IniLines.Add("[Analytics]"); IniLines.Add(String.Format("UE4TypeOverride=\"{0}\"", AnalyticsTypeOverride)); } // Make sure we can write to the the config file File.SetAttributes(OutputBaseEnginePath, OutputAttributes & ~FileAttributes.ReadOnly); File.AppendAllLines(OutputBaseEnginePath, IniLines); File.SetAttributes(OutputBaseEnginePath, OutputAttributes); } /// /// Parse an argument containing a list of platforms /// /// Name of the argument /// List of platforms List ParsePlatformsParamValue(string Name) { string PlatformsString = ParseParamValue(Name); if (String.IsNullOrWhiteSpace(PlatformsString)) { return new List(); } else { return PlatformsString.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(x => UnrealTargetPlatform.Parse(x)).ToList(); } } /// /// Look for a platform's architecture(s) on the commandline, and if not, then ask the platform's config for active architectures /// /// Platform to look for architectures /// One or more architectures private UnrealArchitectures GetArchitecturesForPlatform(UnrealTargetPlatform Platform) { // Try to parse additional Architectures from the command line string Architectures = ParseParamValue(Platform.ToString() + "Architectures"); // Wwas a platform specified on commandline? if (!string.IsNullOrWhiteSpace(Architectures)) { return UnrealArchitectures.FromString(Architectures, Platform); } // if there aren't any, use the default else { // get the architectures for the target platform, for unspecified target UnrealArchitectureConfig PlatformArchConfig = UnrealArchitectureConfig.ForPlatform(Platform); return PlatformArchConfig.ActiveArchitectures(null, null); } } } }