// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Diagnostics; using System.Threading; using System.Reflection; using System.Linq; namespace UnrealBuildTool { public class UProjectInfo { public string GameName; public string FileName; public string FilePath; public string Folder; public bool bIsCodeProject; UProjectInfo(string InFilePath, bool bInIsCodeProject) { GameName = Path.GetFileNameWithoutExtension(InFilePath); FileName = Path.GetFileName(InFilePath); FilePath = InFilePath; Folder = Path.GetDirectoryName(InFilePath).TrimEnd('\\', '/'); bIsCodeProject = bInIsCodeProject; } /** Map of relative or complete project file names to the project info */ static Dictionary ProjectInfoDictionary = new Dictionary( StringComparer.InvariantCultureIgnoreCase ); /** Map of short project file names to the relative or complete project file name */ static Dictionary ShortProjectNameDictionary = new Dictionary( StringComparer.InvariantCultureIgnoreCase ); /** Map of targetnames to the relative or complete project file name */ static Dictionary TargetToProjectDictionary = new Dictionary( StringComparer.InvariantCultureIgnoreCase ); public static bool FindTargetFilesInFolder(string InTargetFolder) { bool bFoundTargetFiles = false; IEnumerable Files; if (!Utils.IsRunningOnMono) { Files = Directory.EnumerateFiles (InTargetFolder, "*.target.cs", SearchOption.TopDirectoryOnly); } else { Files = Directory.GetFiles (InTargetFolder, "*.Target.cs", SearchOption.TopDirectoryOnly).AsEnumerable(); } foreach (var TargetFilename in Files) { bFoundTargetFiles = true; foreach (KeyValuePair Entry in ProjectInfoDictionary) { FileInfo ProjectFileInfo = new FileInfo(Entry.Key); string ProjectDir = ProjectFileInfo.DirectoryName; if (TargetFilename.StartsWith(ProjectDir, StringComparison.InvariantCultureIgnoreCase)) { FileInfo TargetInfo = new FileInfo(TargetFilename); // Strip off the target.cs string TargetName = Utils.GetFilenameWithoutAnyExtensions( TargetInfo.Name ); if (TargetToProjectDictionary.ContainsKey(TargetName) == false) { TargetToProjectDictionary.Add(TargetName, Entry.Key); } } } } return bFoundTargetFiles; } public static bool FindTargetFiles(string InCurrentTopDirectory, ref bool bOutFoundTargetFiles) { // We will only search as deep as the first target file found string CurrentTopDirectory = InCurrentTopDirectory; List SubFolderList = new List(); // Check the root directory bOutFoundTargetFiles |= FindTargetFilesInFolder(CurrentTopDirectory); if (bOutFoundTargetFiles == false) { foreach (var TargetFolder in Directory.EnumerateDirectories(CurrentTopDirectory, "*", SearchOption.TopDirectoryOnly)) { SubFolderList.Add(TargetFolder); bOutFoundTargetFiles |= FindTargetFilesInFolder(TargetFolder); } } if (bOutFoundTargetFiles == false) { // Recurse each folders folders foreach (var SubFolder in SubFolderList) { FindTargetFiles(SubFolder, ref bOutFoundTargetFiles); } } return bOutFoundTargetFiles; } /// /// Add a single project to the project info dictionary /// public static void AddProject(string ProjectFile, bool bIsCodeProject) { UProjectInfo NewProjectInfo = new UProjectInfo(ProjectFile, bIsCodeProject); if (!ShortProjectNameDictionary.ContainsKey(NewProjectInfo.GameName)) { ProjectInfoDictionary.Add(ProjectFile, NewProjectInfo); ShortProjectNameDictionary.Add(NewProjectInfo.GameName, ProjectFile); } else { var FirstProject = ProjectInfoDictionary[ShortProjectNameDictionary[NewProjectInfo.GameName]]; Log.TraceWarning("There are multiple projects with name {0}\n\t* {1}\n\t* {2}\nThis is not currently supported and only the first one will be maintained by UBT. Please rename.", NewProjectInfo.GameName, Path.GetFullPath(FirstProject.FilePath), Path.GetFullPath(NewProjectInfo.FilePath)); } } /// /// Discover and fill in the project info /// public static void FillProjectInfo() { DateTime StartTime = DateTime.Now; List DirectoriesToSearch = new List(); // Find all the .uprojectdirs files contained in the root folder and add their entries to the search array string RootDirectory = Path.Combine(Utils.GetExecutingAssemblyDirectory(), "..", "..", ".."); string EngineSourceDirectory = Path.GetFullPath( Path.Combine(RootDirectory, "Engine", "Source") ); foreach (var File in Directory.EnumerateFiles(RootDirectory, "*.uprojectdirs", SearchOption.TopDirectoryOnly)) { string FilePath = Path.GetFullPath (File); Log.TraceVerbose("\tFound uprojectdirs file {0}", FilePath); using (StreamReader Reader = new StreamReader(FilePath)) { string LineRead; while ((LineRead = Reader.ReadLine()) != null) { string ProjDirEntry = LineRead.Trim(); if (String.IsNullOrEmpty(ProjDirEntry) == false) { if (ProjDirEntry.StartsWith(";")) { // Commented out line... skip it continue; } else { string DirPath = Path.GetFullPath (Path.Combine (RootDirectory, ProjDirEntry)); DirectoriesToSearch.Add(DirPath); } } } } } Log.TraceVerbose("\tFound {0} directories to search", DirectoriesToSearch.Count); // Initialize the target finding time to 0 TimeSpan TotalTargetTime = DateTime.Now - DateTime.Now; foreach (string DirToSearch in DirectoriesToSearch) { Log.TraceVerbose("\t\tSearching {0}", DirToSearch); if (Directory.Exists(DirToSearch)) { foreach (string SubDir in Directory.EnumerateDirectories(DirToSearch, "*", SearchOption.TopDirectoryOnly)) { Log.TraceVerbose("\t\t\tFound subdir {0}", SubDir); string[] SubDirFiles = Directory.GetFiles(SubDir, "*.uproject", SearchOption.TopDirectoryOnly); foreach (string UProjFile in SubDirFiles) { string RelativePath = Utils.MakePathRelativeTo(UProjFile, EngineSourceDirectory); Log.TraceVerbose("\t\t\t\t{0}", RelativePath); if( !ProjectInfoDictionary.ContainsKey(RelativePath) ) { DateTime TargetStartTime = DateTime.Now; string SourceFolder = Path.Combine(Path.GetDirectoryName(UProjFile), "Source"); bool bIsCodeProject = Directory.Exists(SourceFolder); AddProject(RelativePath, bIsCodeProject); if(bIsCodeProject) { // Find all Target.cs files bool bFoundTargetFiles = false; if (!FindTargetFiles(SourceFolder, ref bFoundTargetFiles)) { Log.TraceVerbose("No target files found under " + SourceFolder); } } DateTime TargetStopTime = DateTime.Now; TotalTargetTime += TargetStopTime - TargetStartTime; } } } } else { Log.TraceVerbose("ProjectInfo: Skipping directory {0} from .uprojectdirs file as it doesn't exist.", DirToSearch); } } DateTime StopTime = DateTime.Now; if( BuildConfiguration.bPrintPerformanceInfo ) { TimeSpan TotalProjectInfoTime = StopTime - StartTime; Log.TraceInformation("FillProjectInfo took {0} milliseconds (AddTargetInfo {1} ms)", TotalProjectInfoTime.Milliseconds, TotalTargetTime.Milliseconds); } if (UnrealBuildTool.CommandLineContains("-dumpprojectinfo")) { UProjectInfo.DumpProjectInfo(); } } public static void DumpProjectInfo() { Log.TraceInformation("Dumping project info..."); Log.TraceInformation("\tProjectInfo"); foreach (KeyValuePair InfoEntry in ProjectInfoDictionary) { Log.TraceInformation("\t\t" + InfoEntry.Key); Log.TraceInformation("\t\t\tName : " + InfoEntry.Value.FileName); Log.TraceInformation("\t\t\tFile Folder : " + InfoEntry.Value.Folder); Log.TraceInformation("\t\t\tCode Project : " + (InfoEntry.Value.bIsCodeProject ? "YES" : "NO")); } Log.TraceInformation("\tShortName to Project"); foreach (KeyValuePair ShortEntry in ShortProjectNameDictionary) { Log.TraceInformation("\t\tShort Name : " + ShortEntry.Key); Log.TraceInformation("\t\tProject : " + ShortEntry.Value); } Log.TraceInformation("\tTarget to Project"); foreach (KeyValuePair TargetEntry in TargetToProjectDictionary) { Log.TraceInformation("\t\tTarget : " + TargetEntry.Key); Log.TraceInformation("\t\tProject : " + TargetEntry.Value); } } /// /// Filter the list of game projects /// /// If true, only return code projects /// Game name to filter against. May be null. public static List FilterGameProjects(bool bOnlyCodeProjects, string GameNameFilter) { List Projects = new List(); foreach (KeyValuePair Entry in ProjectInfoDictionary) { if (!bOnlyCodeProjects || Entry.Value.bIsCodeProject) { if (string.IsNullOrEmpty(GameNameFilter) || Entry.Value.GameName == GameNameFilter) { Projects.Add(Entry.Value); } } } return Projects; } /// /// Get the project folder for the given target name /// /// Name of the target of interest /// The project filename, empty string if not found public static string GetProjectForTarget(string InTargetName) { string ProjectName; if (TargetToProjectDictionary.TryGetValue(InTargetName, out ProjectName) == true) { return ProjectName; } return ""; } /// /// Get the project folder for the given project name /// /// Name of the project of interest /// The project filename, empty string if not found public static string GetProjectFilePath(string InProjectName) { string ProjectFilePath; if (ShortProjectNameDictionary.TryGetValue(InProjectName, out ProjectFilePath)) { return ProjectFilePath; } return ""; } /// /// Determine if a plugin is enabled for a given project /// /// The project to check /// Information about the plugin /// The target platform /// True if the plugin should be enabled for this project public static bool IsPluginEnabledForProject(PluginInfo Plugin, ProjectDescriptor Project, UnrealTargetPlatform Platform) { bool bEnabled = Plugin.Descriptor.bEnabledByDefault || Plugin.LoadedFrom == PluginLoadedFrom.GameProject; if(Project != null && Project.Plugins != null) { foreach(PluginReferenceDescriptor PluginReference in Project.Plugins) { if(String.Compare(PluginReference.Name, Plugin.Name, true) == 0) { bEnabled = PluginReference.IsEnabledForPlatform(Platform); } } } return bEnabled; } } }