public FileItem CachePCHUsageForCPPFile(FileItem CPPFile, CppIncludePaths IncludePaths, CppPlatform Platform) { // @todo ubtmake: We don't really need to scan every file looking for PCH headers, just need one. The rest is just for error checking. // @todo ubtmake: We don't need all of the direct includes either. We just need the first, unless we want to check for errors. List <DependencyInclude> DirectIncludeFilenames = GetDirectIncludeDependencies(CPPFile, bOnlyCachedDependencies: false); if (UnrealBuildTool.bPrintDebugInfo) { Log.TraceVerbose("Found direct includes for {0}: {1}", Path.GetFileName(CPPFile.AbsolutePath), string.Join(", ", DirectIncludeFilenames.Select(F => F.IncludeName))); } if (DirectIncludeFilenames.Count == 0) { return(null); } DependencyInclude FirstInclude = DirectIncludeFilenames[0]; // Resolve the PCH header to an absolute path. // Check NullOrEmpty here because if the file could not be resolved we need to throw an exception if (FirstInclude.IncludeResolvedNameIfSuccessful != null && // ignore any preexisting resolve cache if we are not configured to use it. bUseIncludeDependencyResolveCache && // if we are testing the resolve cache, we force UBT to resolve every time to look for conflicts !bTestIncludeDependencyResolveCache) { CPPFile.PrecompiledHeaderIncludeFilename = FirstInclude.IncludeResolvedNameIfSuccessful; return(FileItem.GetItemByFileReference(CPPFile.PrecompiledHeaderIncludeFilename)); } // search the include paths to resolve the file. string FirstIncludeName = FirstInclude.IncludeName; UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(Platform); // convert back from relative to host path if needed if (!BuildPlatform.UseAbsolutePathsInUnityFiles()) { FirstIncludeName = RemoteExports.UnconvertPath(FirstIncludeName); } FileItem PrecompiledHeaderIncludeFile = CPPHeaders.FindIncludedFile(CPPFile.Reference, FirstIncludeName, IncludePaths); if (PrecompiledHeaderIncludeFile == null) { FirstIncludeName = RemoteExports.UnconvertPath(FirstInclude.IncludeName); throw new BuildException("The first include statement in source file '{0}' is trying to include the file '{1}' as the precompiled header, but that file could not be located in any of the module's include search paths.", CPPFile.AbsolutePath, FirstIncludeName); } IncludeDependencyCache.CacheResolvedIncludeFullPath(CPPFile, 0, PrecompiledHeaderIncludeFile.Reference, bUseIncludeDependencyResolveCache, bTestIncludeDependencyResolveCache); CPPFile.PrecompiledHeaderIncludeFilename = PrecompiledHeaderIncludeFile.Reference; return(PrecompiledHeaderIncludeFile); }
/// <summary> /// Get a set of directly included files from the given source file, resolving their include paths to FileItem instances. /// </summary> /// <param name="SourceFile">The file to check.</param> /// <param name="IncludePaths">Include paths to search.</param> /// <param name="bOnlyCachedDependencies">Whether to just return cached dependencies, or update the cache with new results.</param> /// <returns>Set of files that are included</returns> private HashSet <FileItem> GetDirectlyIncludedFiles(FileItem SourceFile, CppIncludePaths IncludePaths, bool bOnlyCachedDependencies) { // Gather a list of names of files directly included by this C++ file. List <DependencyInclude> DirectIncludes = GetDirectIncludeDependencies(SourceFile, bOnlyCachedDependencies: bOnlyCachedDependencies); // Build a list of the unique set of files that are included by this file. HashSet <FileItem> DirectlyIncludedFiles = new HashSet <FileItem>(); // require a for loop here because we need to keep track of the index in the list. for (int DirectlyIncludedFileNameIndex = 0; DirectlyIncludedFileNameIndex < DirectIncludes.Count; ++DirectlyIncludedFileNameIndex) { // Resolve the included file name to an actual file. DependencyInclude DirectInclude = DirectIncludes[DirectlyIncludedFileNameIndex]; if (!DirectInclude.HasAttemptedResolve || // ignore any preexisting resolve cache if we are not configured to use it. !bUseIncludeDependencyResolveCache || // if we are testing the resolve cache, we force UBT to resolve every time to look for conflicts bTestIncludeDependencyResolveCache ) { ++TotalDirectIncludeResolveCacheMisses; // search the include paths to resolve the file FileItem DirectIncludeResolvedFile = CPPHeaders.FindIncludedFile(SourceFile.Location, DirectInclude.IncludeName, IncludePaths); if (DirectIncludeResolvedFile != null) { DirectlyIncludedFiles.Add(DirectIncludeResolvedFile); } IncludeDependencyCache.CacheResolvedIncludeFullPath(SourceFile, DirectlyIncludedFileNameIndex, DirectIncludeResolvedFile != null ? DirectIncludeResolvedFile.Location : null, bUseIncludeDependencyResolveCache, bTestIncludeDependencyResolveCache); } else { // we might have cached an attempt to resolve the file, but couldn't actually find the file (system headers, etc). if (DirectInclude.IncludeResolvedNameIfSuccessful != null) { DirectlyIncludedFiles.Add(FileItem.GetItemByFileReference(DirectInclude.IncludeResolvedNameIfSuccessful)); } } } TotalDirectIncludeResolves += DirectIncludes.Count; return(DirectlyIncludedFiles); }
/// <summary> /// Finds the files directly or indirectly included by the given C++ file. /// </summary> /// <param name="CPPFile">C++ file to get the dependencies for.</param> /// <param name="Result">List of CPPFile dependencies.</param> /// <returns>false if CPPFile is still being processed further down the callstack, true otherwise.</returns> public bool FindAndCacheAllIncludedFiles(FileItem CPPFile, UEBuildPlatform BuildPlatform, CPPIncludeInfo CPPIncludeInfo, ref IncludedFilesSet Result, bool bOnlyCachedDependencies) { IncludedFilesSet IncludedFileList; Dictionary <FileItem, IncludedFilesSet> IncludedFilesMap = bOnlyCachedDependencies ? OnlyCachedIncludedFilesMap : ExhaustiveIncludedFilesMap; if (!IncludedFilesMap.TryGetValue(CPPFile, out IncludedFileList)) { DateTime TimerStartTime = DateTime.UtcNow; IncludedFileList = new IncludedFilesSet(); // Add an uninitialized entry for the include file to avoid infinitely recursing on include file loops. IncludedFilesMap.Add(CPPFile, IncludedFileList); // Gather a list of names of files directly included by this C++ file. List <DependencyInclude> DirectIncludes = GetDirectIncludeDependencies(CPPFile, BuildPlatform, bOnlyCachedDependencies: bOnlyCachedDependencies); // Build a list of the unique set of files that are included by this file. HashSet <FileItem> DirectlyIncludedFiles = new HashSet <FileItem>(); // require a for loop here because we need to keep track of the index in the list. for (int DirectlyIncludedFileNameIndex = 0; DirectlyIncludedFileNameIndex < DirectIncludes.Count; ++DirectlyIncludedFileNameIndex) { // Resolve the included file name to an actual file. DependencyInclude DirectInclude = DirectIncludes[DirectlyIncludedFileNameIndex]; if (!DirectInclude.HasAttemptedResolve || // ignore any preexisting resolve cache if we are not configured to use it. !BuildConfiguration.bUseIncludeDependencyResolveCache || // if we are testing the resolve cache, we force UBT to resolve every time to look for conflicts BuildConfiguration.bTestIncludeDependencyResolveCache ) { ++TotalDirectIncludeResolveCacheMisses; // search the include paths to resolve the file FileItem DirectIncludeResolvedFile = CPPHeaders.FindIncludedFile(DirectInclude.IncludeName, !BuildConfiguration.bCheckExternalHeadersForModification, CPPIncludeInfo.GetIncludesPathsToSearch(CPPFile), CPPIncludeInfo.IncludeFileSearchDictionary); if (DirectIncludeResolvedFile != null) { DirectlyIncludedFiles.Add(DirectIncludeResolvedFile); } IncludeDependencyCache.CacheResolvedIncludeFullPath(CPPFile, DirectlyIncludedFileNameIndex, DirectIncludeResolvedFile != null ? DirectIncludeResolvedFile.Reference : null); } else { // we might have cached an attempt to resolve the file, but couldn't actually find the file (system headers, etc). if (DirectInclude.IncludeResolvedNameIfSuccessful != null) { DirectlyIncludedFiles.Add(FileItem.GetItemByFileReference(DirectInclude.IncludeResolvedNameIfSuccessful)); } } } TotalDirectIncludeResolves += DirectIncludes.Count; // Convert the dictionary of files included by this file into a list. foreach (FileItem DirectlyIncludedFile in DirectlyIncludedFiles) { // Add the file we're directly including IncludedFileList.Add(DirectlyIncludedFile); // Also add all of the indirectly included files! if (FindAndCacheAllIncludedFiles(DirectlyIncludedFile, BuildPlatform, CPPIncludeInfo, ref IncludedFileList, bOnlyCachedDependencies: bOnlyCachedDependencies) == false) { // DirectlyIncludedFile is a circular dependency which is still being processed // further down the callstack. Add this file to its circular dependencies list // so that it can update its dependencies later. IncludedFilesSet DirectlyIncludedFileIncludedFileList; if (IncludedFilesMap.TryGetValue(DirectlyIncludedFile, out DirectlyIncludedFileIncludedFileList)) { DirectlyIncludedFileIncludedFileList.CircularDependencies.Add(CPPFile); } } } // All dependencies have been processed by now so update all circular dependencies // with the full list. foreach (FileItem CircularDependency in IncludedFileList.CircularDependencies) { IncludedFilesSet CircularDependencyIncludedFiles = IncludedFilesMap[CircularDependency]; foreach (FileItem IncludedFile in IncludedFileList) { CircularDependencyIncludedFiles.Add(IncludedFile); } } // No need to keep this around anymore. IncludedFileList.CircularDependencies.Clear(); // Done collecting files. IncludedFileList.bIsInitialized = true; TimeSpan TimerDuration = DateTime.UtcNow - TimerStartTime; TotalTimeSpentGettingIncludes += TimerDuration.TotalSeconds; } if (IncludedFileList.bIsInitialized) { // Copy the list of files included by this file into the result list. foreach (FileItem IncludedFile in IncludedFileList) { // If the result list doesn't contain this file yet, add the file and the files it includes. // NOTE: For some reason in .NET 4, Add() is over twice as fast as calling UnionWith() on the set Result.Add(IncludedFile); } return(true); } else { // The IncludedFileList.bIsInitialized was false because we added a dummy entry further down the call stack. We're already processing // the include list for this header elsewhere in the stack frame, so we don't need to add anything here. return(false); } }