/// <summary> /// Caches the contents of this directory /// </summary> public void CacheFiles() { // Check if we need to update the file list if (NameToFile == null) { // Add all the files in this directory Dictionary <string, WorkspaceFile> NewNameToFile = new Dictionary <string, WorkspaceFile>(StringComparer.InvariantCultureIgnoreCase); foreach (FileInfo FileInfo in Info.EnumerateFiles()) { WorkspaceFile File = new WorkspaceFile(FileInfo.Name, this); NewNameToFile.Add(FileInfo.Name, File); } Interlocked.CompareExchange(ref NameToFile, NewNameToFile, null); // Update the project directory if we have one. For programs, we treat the directory containing .target.cs files as the project, because they // do not overlap with other programs or projects. if (NormalizedPathFromBranchRoot != null && ProjectDirectory == null) { if (NormalizedPathFromBranchRoot.StartsWith("/engine/")) { if (NormalizedPathFromBranchRoot.StartsWith("/engine/source/programs/") && Files.Any(x => x.NormalizedPathFromBranchRoot.EndsWith(".target.cs"))) { ProjectDirectory = this; } } else { if (Files.Any(x => x.NormalizedPathFromBranchRoot.EndsWith(".uproject"))) { ProjectDirectory = this; } } } } }
/// <summary> /// Gets the flags for a new source file /// </summary> /// <param name="File">The workspace file being read</param> /// <returns>Flags for the corresponding source file</returns> public static SourceFileFlags GetSourceFileFlags(WorkspaceFile File) { string NormalizedPath = File.NormalizedPathFromBranchRoot; if (NormalizedPath == null || IsExternalHeaderPath(NormalizedPath)) { return(SourceFileFlags.Pinned | SourceFileFlags.External); } SourceFileFlags Flags = SourceFileFlags.Standalone; if (NormalizedPath.EndsWith(".inl") || NormalizedPath.EndsWith(".inc") || NormalizedPath.EndsWith(".generated.h")) { Flags = (Flags | SourceFileFlags.Pinned) & ~SourceFileFlags.Standalone; } if (NormalizedPath.IndexOf("/public/") != -1 || NormalizedPath.IndexOf("/classes/") != -1) { Flags |= SourceFileFlags.Public; } if (NormalizedPath.IndexOf("/intermediate/") != -1) { if (NormalizedPath.EndsWith(".generated.h")) { Flags |= SourceFileFlags.GeneratedHeader | SourceFileFlags.Inline | SourceFileFlags.Public; } else if (NormalizedPath.EndsWith("classes.h")) { Flags |= SourceFileFlags.GeneratedClassesHeader | SourceFileFlags.Public; } } if (NormalizedPath.EndsWith(".cpp") || NormalizedPath.IndexOf("/windows/") != -1 || NormalizedPath.IndexOf("/linux/") != -1) { Flags |= SourceFileFlags.Pinned; } if (NormalizedPath.EndsWith("fwd.h") && !IgnoreFwdHeaders.Contains(NormalizedPath)) { Flags |= SourceFileFlags.FwdHeader; } if (InlineFileNames.Contains(NormalizedPath)) { Flags = (Flags | SourceFileFlags.Inline) & ~SourceFileFlags.Standalone; } if (PinnedFileNames.Contains(NormalizedPath)) { Flags = (Flags | SourceFileFlags.Pinned) & ~SourceFileFlags.Standalone; } if (NotStandaloneFileNames.Contains(NormalizedPath)) { Flags &= ~SourceFileFlags.Standalone; } if (AggregateFileNames.Contains(NormalizedPath)) { Flags |= SourceFileFlags.Aggregate; } if (AllowMultipleFragmentFileNames.Contains(NormalizedPath)) { Flags |= SourceFileFlags.AllowMultipleFragments; } if (ShouldIgnoreExports(NormalizedPath)) { Flags |= SourceFileFlags.IgnoreExportedSymbols; } return(Flags); }
/// <summary> /// Tries to get a file of the given name from this directory /// </summary> /// <param name="Name">The name to look for</param> /// <param name="Result">If successful, receives the file object</param> /// <returns>True if the file was found</returns> public bool TryGetFile(string Name, out WorkspaceFile Result) { CacheFiles(); return(NameToFile.TryGetValue(Name, out Result)); }
/// <summary> /// Tries to resolve an include path from a file, given a set of candidates /// </summary> /// <param name="FromFile">The file containing the #include directive</param> /// <param name="IncludePath">The path being included</param> /// <param name="InitialCandidateFiles">Set of candidate files to pick from</param> /// <param name="bIsSystemInclude">Whether the include path is for a system header. If these aren't found, we don't fail.</param> /// <param name="IncludedFile">On success, the matching file</param> /// <param name="Log">Log instance for output messages</param> /// <returns>True if the include path was resolved, false otherwise</returns> public static bool ResolveInclude(WorkspaceFile FromFile, string IncludePath, IEnumerable <WorkspaceFile> InitialCandidateFiles, bool bIsSystemInclude, out WorkspaceFile IncludedFile, TextWriter Log) { List <WorkspaceFile> CandidateFiles = new List <WorkspaceFile>(InitialCandidateFiles); // Check if it's a direct file reference WorkspaceFile DirectlyReferencedFile = CandidateFiles.FirstOrDefault(x => x.Location == FileReference.Combine(FromFile.Location.Directory, IncludePath)); if (DirectlyReferencedFile != null) { IncludedFile = DirectlyReferencedFile; return(true); } // Apply any of the ignore patterns for (int Idx = 0; Idx < IgnoreIncludePatterns.GetLength(0); Idx++) { if (FromFile.NormalizedPathFromBranchRoot.StartsWith(IgnoreIncludePatterns[Idx, 0])) { CandidateFiles.RemoveAll(x => x.NormalizedPathFromBranchRoot.Contains(IgnoreIncludePatterns[Idx, 1])); } } // Remove any candidate files that don't have the same suffix as the include path string IncludeSuffix = IncludePath.ToLowerInvariant().Replace('\\', '/'); while (IncludeSuffix.StartsWith("../")) { IncludeSuffix = IncludeSuffix.Substring(3); } CandidateFiles.RemoveAll(x => !x.NormalizedPathFromBranchRoot.EndsWith(IncludeSuffix)); // Remove any files from a different project. We do occasionally cross this boundary via explicitly referenced paths, so skip if there's already only one match. if (CandidateFiles.Count > 1) { if (FromFile.ProjectDirectory == null) { CandidateFiles.RemoveAll(x => x.ProjectDirectory != null); } else { CandidateFiles.RemoveAll(x => x.ProjectDirectory != FromFile.ProjectDirectory && x.ProjectDirectory != null); } } // If we're including Engine.h and haven't explicitly referenced the UEngine header, assume it's referencing the header for the engine module if (CandidateFiles.Count > 2 && IncludeSuffix == "engine.h") { if (CandidateFiles.Any(x => x.NormalizedPathFromBranchRoot == "/engine/source/runtime/engine/public/engine.h")) { CandidateFiles.RemoveAll(x => x.NormalizedPathFromBranchRoot == "/engine/source/runtime/engine/classes/engine/engine.h"); } } // Use case to disambiguate if we still have mismatches. This actually weeds out quite a lot of false positives, expecially with TPS // (the typical pattern is that Epic code uses TitleCase but OSS uses all lowercase). if (CandidateFiles.Count > 1) { List <WorkspaceFile> ExactCaseMatches = CandidateFiles.Where(x => x.Location.FullName.EndsWith(IncludePath.Replace('/', Path.DirectorySeparatorChar))).ToList(); if (ExactCaseMatches.Count == 1) { CandidateFiles.Clear(); CandidateFiles.AddRange(ExactCaseMatches); } } // Use case to disambiguate if we still have mismatches. This actually weeds out quite a lot of false positives, expecially with TPS // (the typical pattern is that Epic code uses TitleCase but OSS uses all lowercase). if (CandidateFiles.Count > 1) { List <WorkspaceFile> NonThirdPartyCandidates = CandidateFiles.Where(x => !x.NormalizedPathFromBranchRoot.Contains("/thirdparty/")).ToList(); if (NonThirdPartyCandidates.Count == 1) { CandidateFiles.Clear(); CandidateFiles.AddRange(NonThirdPartyCandidates); } } // Check if we've got an exact match if (CandidateFiles.Count == 1) { IncludedFile = CandidateFiles[0]; return(true); } // If it's a system header, it's ok if we didn't resolve it if (CandidateFiles.Count == 0 && bIsSystemInclude) { IncludedFile = null; return(true); } // If they're all third party files, just treat it as an external include if (CandidateFiles.Count > 1 && CandidateFiles.All(x => x.NormalizedPathFromBranchRoot.Contains("/thirdparty/"))) { IncludedFile = null; return(true); } // Allow generated headers to be excluded for now if (IncludePath.EndsWith("Classes.h") || IncludePath.EndsWith(".generated.h")) { IncludedFile = null; return(true); } // Otherwise print the remaining candidates Log.WriteLine("Failed to resolve include of \"{0}\" from \"{1}\". {2} candidate files.", IncludePath, FromFile.Location, CandidateFiles.Count); foreach (WorkspaceFile FinalCandidateFile in CandidateFiles) { Log.WriteLine(" Could be: {0}", FinalCandidateFile.Location); } Log.WriteLine(); // ...and fail IncludedFile = null; return(false); }