Files
UnrealEngine/Engine/Source/Programs/AutomationTool/Turnkey/TurnkeyManifest.cs
2025-05-18 13:04:45 +08:00

266 lines
9.1 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using EpicGames.Core;
using UnrealBuildTool;
using AutomationTool;
namespace Turnkey
{
public class TurnkeyManifest
{
static private string StandardManifestName = "TurnkeyManifest.xml";
[XmlElement("FileSource")]
public FileSource[] FileSources = null;
[XmlArrayItem(ElementName = "Manifest")]
public string[] AdditionalManifests = null;
public SavedSetting[] SavedSettings = null;
internal void PostDeserialize()
{
// load any settings set in the xml
if (SavedSettings != null)
{
Array.ForEach(SavedSettings, x => TurnkeyUtils.SetVariable(x.Variable, TurnkeyUtils.ExpandVariables(x.Value)));
}
if (FileSources != null)
{
Array.ForEach(FileSources, x => x.PostDeserialize());
// look for list expansion and fixup file sources (filexpansion will come later, on demand, to improve speed)
List<FileSource> ExpandedSources = new List<FileSource>();
List<FileSource> NonExpandedSources = new List<FileSource>();
foreach (FileSource Source in FileSources)
{
List<FileSource> Expansions = Source.ConditionalExpandLists();
if (Expansions != null)
{
ExpandedSources.AddRange(Expansions);
}
else
{
// add to new list, instead of removing from FileSources since we are iterating
NonExpandedSources.Add(Source);
}
}
// now combine them and replace FileSources
NonExpandedSources.AddRange(ExpandedSources);
FileSources = NonExpandedSources.ToArray();
}
}
static List<FileSource> DiscoveredFileSources = null;
public static List<UnrealTargetPlatform> GetPlatformsWithSdks()
{
DiscoverManifests();
// this can handle FileSources with pending Expansions, so get the unique set of Platforms represented by pre or post expansion sources
// skip over non-Sdk types, since this wants just Sdks
List<UnrealTargetPlatform> Platforms = new List<UnrealTargetPlatform>();
DiscoveredFileSources.FindAll(x => x.IsSdkType()).ForEach(x => Platforms.AddRange(x.GetPlatforms()));
return Platforms.Distinct().ToList();
}
public static List<string> GetProjectsWithBuilds()
{
DiscoverManifests();
// this can handle FileSources with pending Expansions, so get the unique set of Platforms represented by pre or post expansion sources
// skip over non-Sdk types, since this wants just Sdks
List<string> Projects = new List<string>();
DiscoveredFileSources.FindAll(x => x.Type == FileSource.SourceType.Build).ForEach(x => Projects.Add(x.Project));
return Projects.Distinct(StringComparer.InvariantCultureIgnoreCase).ToList();
}
public static List<FileSource> GetAllDiscoveredFileSources()
{
return FilterDiscoveredFileSources(null, null);
}
private static List<FileSource> ExpandFilteredSources(List<FileSource> Sources)
{
// get the set that needs to expand
List<FileSource> NeedsExpansion = Sources.FindAll(x => x.NeedsFileExpansion());
List<FileSource> NotNeedsExpansion = Sources.FindAll(x => !x.NeedsFileExpansion());
// remove the ones that we are going to expand below
DiscoveredFileSources = DiscoveredFileSources.FindAll(x => !NeedsExpansion.Contains(x));
foreach (FileSource Source in NeedsExpansion)
{
List<FileSource> ExpansionResult = Source.ExpandCopySource();
// add them to the full set of sources, and our filtered sources
NotNeedsExpansion.AddRange(ExpansionResult);
DiscoveredFileSources.AddRange(ExpansionResult);
}
// NotNeedsExpansion now contains the expanded sources, and the sources that didn't need to be expanded
// now filter out platforms that are disabled on this host
NotNeedsExpansion = NotNeedsExpansion.FindAll(x => x.GetPlatforms().Any(y => UEBuildPlatformSDK.GetSDKForPlatform(y.ToString()).bIsSdkAllowedOnHost));
return NotNeedsExpansion;
}
public static List<FileSource> FilterDiscoveredFileSources(UnrealTargetPlatform? Platform, FileSource.SourceType? Type)
{
// hunt down manifests if needed
DiscoverManifests();
List<FileSource> Matching;
if (Platform == null && Type == null)
{
Matching = DiscoveredFileSources;
}
else
{
// if the platform is from expansion (possible with AutoSDKs in particular), then we need to expand it in case this platform will be matched
Matching = DiscoveredFileSources.FindAll(x => (Platform == null || (x.PlatformString?.StartsWith("$(")).GetValueOrDefault() || x.SupportsPlatform(Platform.Value)) && (Type == null || x.Type == Type.Value));
}
return ExpandFilteredSources(Matching);
}
public static List<FileSource> FilterDiscoveredBuilds(string Project)
{
// hunt down manifests if needed
DiscoverManifests();
List<FileSource> Filtered = DiscoveredFileSources.FindAll(x => x.Type == FileSource.SourceType.Build && x.Project.Equals(Project, StringComparison.InvariantCultureIgnoreCase));
return ExpandFilteredSources(Filtered);
}
public static void DiscoverManifests()
{
// this is the indicator that we haven't run yet
if (DiscoveredFileSources == null)
{
DiscoveredFileSources = new List<FileSource>();
// known location to branch from, this will include a few other locations
string RootOperation = "file:$(EngineDir)/Build/Turnkey/TurnkeyManifest.xml";
LoadManifestsFromProvider(RootOperation).ForEach(x =>
{
if (x.FileSources != null)
{
DiscoveredFileSources.AddRange(x.FileSources);
}
});
// also manually create local "FileSource" objects specified via code
foreach (UnrealTargetPlatform Platform in UnrealTargetPlatform.GetValidPlatforms())
{
DataDrivenPlatformInfo.ConfigDataDrivenPlatformInfo Info = DataDrivenPlatformInfo.GetDataDrivenInfoForPlatform(Platform.ToString());
if (Info != null && Info.bIsEnabled)
{
// these are usually going to be empty
foreach (string SpecifiedVersion in AutomationTool.Platform.GetPlatform(Platform).GetCodeSpecifiedSdkVersions())
{
FileSource CodeSource = FileSource.CreateCodeSpecifiedSource($"{Platform} SDK {SpecifiedVersion}", SpecifiedVersion, Platform);
DiscoveredFileSources.Add(CodeSource);
}
foreach (string SpecifiedVersion in AutomationTool.Platform.GetPlatform(Platform).GetCodeSpecifiedDeviceSoftwareUpdateVersions())
{
FileSource CodeSource = FileSource.CreateCodeSpecifiedSource($"{Platform} Device Software/Flash {SpecifiedVersion}", SpecifiedVersion, Platform);
CodeSource.Type = FileSource.SourceType.Flash;
DiscoveredFileSources.Add(CodeSource);
}
}
}
}
}
public static List<TurnkeyManifest> LoadManifestsFromProvider(string EnumerationOperation)
{
List<TurnkeyManifest> Manifests = new List<TurnkeyManifest>();
string[] EnumeratedSources = CopyProvider.ExecuteEnumerate(EnumerationOperation);
if (EnumeratedSources != null)
{
foreach (string Source in EnumeratedSources)
{
// retrieve the actual file locally
string LocalManifestPath = CopyProvider.ExecuteCopy(Source);
// if it doesn't exist, that's okay
if (LocalManifestPath == null)
{
continue;
}
// chop off the last path component to get the dir of the ManifestFile source
int LastSlash = Source.LastIndexOf('/');
int LastBackSlash = Source.LastIndexOf('\\');
string ThisManifestDir = Source.Substring(0, Math.Max(LastSlash, LastBackSlash));
// if a directory is returned, look for the standardized manifest name, as we have not much else we can do with a directory
if (LocalManifestPath.EndsWith("/") || LocalManifestPath.EndsWith("\\"))
{
LocalManifestPath = LocalManifestPath + StandardManifestName;
if (!File.Exists(LocalManifestPath))
{
continue;
}
// if we had a directory, then ThisManifestDir above would have the parent of this directory, not this directory itself, fix it
ThisManifestDir = Source;
}
// Console.WriteLine("Info: Setting ManifestDir to {0}", ThisManifestDir);
// allow this manifest's directory to be used in further copy, but needs to be in a stack
string PrevManifestDir = TurnkeyUtils.SetVariable("ThisManifestDir", ThisManifestDir);
// TurnkeyUtils.Log("Info: Processing manifest: {0}", LocalManifestPath);
// read in the .xml
TurnkeyManifest Manifest = Utils.ReadClass<TurnkeyManifest>(LocalManifestPath, Log.Logger);
Manifest.PostDeserialize();
Manifests.Add(Manifest);
// now process any more referenced Sdks
if (Manifest.AdditionalManifests != null)
{
foreach (string ManifestPath in Manifest.AdditionalManifests)
{
// now recurse on the extra manifests
Manifests.AddRange(LoadManifestsFromProvider(ManifestPath));
}
}
TurnkeyUtils.SetVariable("ThisManifestDir", PrevManifestDir);
// TurnkeyUtils.Log("Info: Resetting ManifestDir to {0}", PrevManifestDir);
}
}
return Manifests;
}
public void Write(string Path)
{
Utils.WriteClass<TurnkeyManifest>(this, Path, "", Log.Logger);
}
}
}