// Copyright Epic Games, Inc. All Rights Reserved. using System.Collections.Generic; using System.IO; using System.Linq; using EpicGames.Core; using UnrealBuildBase; namespace UnrealBuildTool { static class SourceFileSearch { // Certain file types should never be added to project files. These extensions must all be lowercase. static readonly string[] DefaultExcludedFileSuffixes = new string[] { ".vcxproj", // Visual Studio project files ".vcxproj.filters", // Visual Studio filter file ".vcxproj.user", // Visual Studio project user file ".vcxproj.vspscc", // Visual Studio source control state ".sln", // Visual Studio solution file ".sdf", // Visual Studio embedded symbol database file ".suo", // Visual Studio solution option files ".sln.docstates.suo", // Visual Studio temp file ".tmp", // Temp files used by P4 diffing (e.g. t6884t87698.tmp) ".csproj", // Visual Studio C# project files ".userprefs", // MonoDevelop project settings ".ds_store", // Mac Desktop Services Store hidden files ".bin", // Binary files Path.DirectorySeparatorChar + "do_not_delete.txt", // Perforce placeholder file }; // Default directory names to exclude. Must be lowercase. static readonly string[] DefaultExcludedDirectorySuffixes = new string[] { Path.DirectorySeparatorChar + "intermediate", Path.DirectorySeparatorChar + "source" + Path.DirectorySeparatorChar + "thirdparty", Path.DirectorySeparatorChar + "third_party", }; /// Finds mouse source files public static List FindModuleSourceFiles(FileReference ModuleRulesFile, bool SearchSubdirectories = true, HashSet? SearchedDirectories = null) { // The module's "base directory" is simply the directory where its xxx.Build.cs file is stored. We'll always // harvest source files for this module in this base directory directory and all of its sub-directories. return SourceFileSearch.FindFiles(ModuleRulesFile.Directory, SubdirectoryNamesToExclude: null, SearchSubdirectories: SearchSubdirectories, SearchedDirectories: SearchedDirectories); } /// /// Fills in a project file with source files discovered in the specified list of paths /// /// Directory to search /// Directory base names to ignore when searching subdirectories. Can be null. /// True to include subdirectories, otherwise we only search the list of base directories /// If non-null, is updated with a list of the directories searched public static List FindFiles(DirectoryReference DirectoryToSearch, List? SubdirectoryNamesToExclude = null, bool SearchSubdirectories = true, HashSet? SearchedDirectories = null) { // Build a list of directory names that we don't want to search under. We always ignore intermediate directories. string[] ExcludedDirectorySuffixes; if (SubdirectoryNamesToExclude == null) { ExcludedDirectorySuffixes = DefaultExcludedDirectorySuffixes; } else { ExcludedDirectorySuffixes = SubdirectoryNamesToExclude.Select(x => Path.DirectorySeparatorChar + x.ToLowerInvariant()).Union(DefaultExcludedDirectorySuffixes).ToArray(); } // Find all the files List FoundFiles = new List(); if (SearchSubdirectories) { FindFilesInternalRecursive(DirectoryToSearch, ExcludedDirectorySuffixes, FoundFiles, SearchedDirectories); } else { FindFilesInternal(DirectoryToSearch, ExcludedDirectorySuffixes, FoundFiles, SearchedDirectories); } return FoundFiles; } static void FindFilesInternal(DirectoryReference Directory, string[] ExcludedDirectorySuffixes, List FoundFiles, HashSet? SearchedDirectories) { SearchedDirectories?.Add(Directory); foreach (FileReference File in DirectoryLookupCache.EnumerateFiles(Directory)) { if (ShouldInclude(File, DefaultExcludedFileSuffixes)) { FoundFiles.Add(File); } } } static void FindFilesInternalRecursive(DirectoryReference Directory, string[] ExcludedDirectorySuffixes, List FoundFiles, HashSet? SearchedDirectories) { FindFilesInternal(Directory, ExcludedDirectorySuffixes, FoundFiles, SearchedDirectories); foreach (DirectoryReference SubDirectory in DirectoryLookupCache.EnumerateDirectories(Directory)) { if (ShouldInclude(SubDirectory, ExcludedDirectorySuffixes)) { FindFilesInternalRecursive(SubDirectory, ExcludedDirectorySuffixes, FoundFiles, SearchedDirectories); } } } static bool ShouldInclude(FileSystemReference Reference, string[] ExcludedSuffixes) { // Ignore Mac resource fork files on non-HFS partitions if (Path.GetFileName(Reference.FullName).StartsWith("._")) { return false; } foreach (string ExcludedSuffix in ExcludedSuffixes) { if (Reference.FullName.EndsWith(ExcludedSuffix, FileSystemReference.Comparison)) { return false; } } return true; } } }