/// <summary> /// Constructor /// </summary> public OutputFile(SourceFile InputFile, List <OutputFileInclude> Includes, HashSet <SourceFragment> Dependencies, List <Symbol> ForwardDeclarations) { this.InputFile = InputFile; this.Includes = Includes; this.Dependencies = Dependencies; this.ForwardDeclarations = ForwardDeclarations; // Build the list of satisfied dependencies IncludedFragments = new HashSet <SourceFragment>(); IncludedFragments.UnionWith(Includes.SelectMany(x => x.FinalFiles).SelectMany(x => x.IncludedFragments)); IncludedFragments.UnionWith(InputFile.Fragments); // Build the list of all the included files, so other output files that include it can expand it out. IncludedFiles = new HashList <OutputFile>(); IncludedFiles.UnionWith(Includes.SelectMany(x => x.FinalFiles).SelectMany(x => x.IncludedFiles)); IncludedFiles.Add(this); // Build the list of all the originally included files, so that we can make files that include it standalone OriginalIncludedFiles = new HashList <OutputFile>(); OriginalIncludedFiles.UnionWith(Includes.Select(x => x.TargetFile).Where(x => x != null).SelectMany(x => x.OriginalIncludedFiles)); OriginalIncludedFiles.Add(this); }
/// <summary> /// Constructor /// </summary> /// <param name="InputFile">The input file that this output file corresponds to</param> public OutputFile(SourceFile InputFile, OutputFile HeaderFile, IEnumerable <OutputFile> PreviousFiles, List <OutputFileInclude> Includes, List <SourceFile> InputFileStack, Dictionary <Symbol, OutputFile> FwdSymbolToHeader, bool bMakeStandalone, TextWriter Log) { this.InputFile = InputFile; this.Includes = Includes; Debug.Assert(HeaderFile == null || (InputFile.Flags & SourceFileFlags.TranslationUnit) != 0); // Traverse through all the included headers, figuring out the first unique include for each file and fragment HashSet <OutputFile> VisitedFiles = new HashSet <OutputFile>(); HashSet <SourceFragment> VisitedFragments = new HashSet <SourceFragment>(); // Go through the standalone headers first OutputFile MonolithicHeader = null; if (HeaderFile == null && (InputFile.Flags & SourceFileFlags.Standalone) != 0 && (InputFile.Flags & SourceFileFlags.External) == 0 && (InputFile.Flags & SourceFileFlags.Aggregate) == 0) { // Insert a dummy include to receive all the inserted headers OutputFileInclude ImplicitInclude = new OutputFileInclude(-1, null); ImplicitInclude.ExpandedReferences = new List <OutputFileReference>(); Includes.Insert(0, ImplicitInclude); // Determine which monolithic header to use IEnumerable <OutputFile> PotentialMonolithicHeaders = PreviousFiles.Union(Includes.Select(x => x.TargetFile).Where(x => x != null).SelectMany(x => x.IncludedFiles)); if (InputFile.Module != null && InputFile.Module.PublicDependencyModules.Union(InputFile.Module.PrivateDependencyModules).Any(x => x.Name == "Core")) { MonolithicHeader = PotentialMonolithicHeaders.FirstOrDefault(x => (x.InputFile.Flags & SourceFileFlags.IsCoreMinimal) != 0); } else { MonolithicHeader = PotentialMonolithicHeaders.FirstOrDefault(x => (x.InputFile.Flags & SourceFileFlags.IsCoreTypes) != 0); } // Update the dependencies to treat all the contents of a monolithic header as pinned if (MonolithicHeader != null) { SourceFragment[] UniqueFragments = MonolithicHeader.IncludedFragments.Except(VisitedFragments).ToArray(); ImplicitInclude.ExpandedReferences.Add(new OutputFileReference(MonolithicHeader, UniqueFragments)); VisitedFragments.UnionWith(UniqueFragments); VisitedFiles.Add(MonolithicHeader); } // Insert all the forward declaration headers, but only treat them as supplying the forward declarations themselves. They may happen to include // some utility classes (eg. TSharedPtr), and we don't want to include an unrelated header to satisfy that dependency. foreach (OutputFile FwdHeader in FwdSymbolToHeader.Values) { FindExpandedReferences(FwdHeader, ImplicitInclude.ExpandedReferences, VisitedFiles, VisitedFragments, true); } // Add all the other files if (bMakeStandalone) { foreach (OutputFile PreviousFile in PreviousFiles) { if ((PreviousFile.InputFile.Flags & SourceFileFlags.Inline) == 0 && (PreviousFile.InputFile.Flags & SourceFileFlags.Pinned) == 0 && VisitedFiles.Add(PreviousFile)) { SourceFragment[] UniqueFragments = PreviousFile.IncludedFragments.Except(VisitedFragments).ToArray(); ImplicitInclude.ExpandedReferences.Add(new OutputFileReference(PreviousFile, UniqueFragments)); VisitedFragments.UnionWith(UniqueFragments); } } } } // Figure out a list of files which are uniquely reachable through each include. Force an include of the matching header as the first thing. foreach (OutputFileInclude Include in Includes) { if (Include.ExpandedReferences == null) { Include.ExpandedReferences = new List <OutputFileReference>(); if (Include == Includes[0] && HeaderFile != null) { Include.ExpandedReferences.Add(new OutputFileReference(HeaderFile, HeaderFile.IncludedFragments)); VisitedFragments.UnionWith(HeaderFile.IncludedFragments); } FindExpandedReferences(Include.TargetFile, Include.ExpandedReferences, VisitedFiles, VisitedFragments, true); } } // Find all the symbols which are referenced by this file HashSet <SourceFragment> FragmentsWithReferencedSymbols = new HashSet <SourceFragment>(); foreach (SourceFragment Fragment in InputFile.Fragments) { foreach (KeyValuePair <Symbol, SymbolReferenceType> ReferencedSymbol in Fragment.ReferencedSymbols) { if (ReferencedSymbol.Value == SymbolReferenceType.RequiresDefinition) { FragmentsWithReferencedSymbols.Add(ReferencedSymbol.Key.Fragment); } } } // Aggregate headers are designed to explicitly include headers from the current module. Expand out a list of them, so they can be included when encountered. HashSet <OutputFile> ExplicitIncludes = new HashSet <OutputFile>(); if ((InputFile.Flags & SourceFileFlags.Aggregate) != 0) { foreach (OutputFileInclude Include in Includes) { ExplicitIncludes.UnionWith(Include.ExpandedReferences.Where(x => x.File.InputFile.Location.IsUnderDirectory(InputFile.Location.Directory)).Select(x => x.File)); } foreach (OutputFileInclude Include in Includes) { ExplicitIncludes.Remove(Include.TargetFile); } } // Create the list of remaining dependencies for this file, and add any forward declarations Dependencies = new HashSet <SourceFragment>(); AddForwardDeclarations(InputFile, ForwardDeclarations, Dependencies, FwdSymbolToHeader); // Reduce the list of includes to those that are required. for (int FragmentIdx = InputFile.Fragments.Length - 1, IncludeIdx = Includes.Count - 1; FragmentIdx >= 0; FragmentIdx--) { // Update the dependency lists for this fragment SourceFragment InputFragment = InputFile.Fragments[FragmentIdx]; if (InputFragment.Dependencies != null) { Dependencies.UnionWith(InputFragment.Dependencies); } Dependencies.Remove(InputFragment); // Scan backwards through the list of includes, expanding each include to those which are required int MarkupMin = (FragmentIdx == 0)? -1 : InputFragment.MarkupMin; for (; IncludeIdx >= 0 && Includes[IncludeIdx].MarkupIdx >= MarkupMin; IncludeIdx--) { OutputFileInclude Include = Includes[IncludeIdx]; // Always include the same header for aggregates if ((InputFile.Flags & SourceFileFlags.Aggregate) != 0) { Include.FinalFiles.Insert(0, Include.TargetFile); Dependencies.ExceptWith(Include.TargetFile.IncludedFragments); Dependencies.UnionWith(Include.TargetFile.Dependencies); } // Include any indirectly included files for (int Idx = Include.ExpandedReferences.Count - 1; Idx >= 0; Idx--) { // Make sure we haven't already added it above OutputFileReference Reference = Include.ExpandedReferences[Idx]; if (!Include.FinalFiles.Contains(Reference.File)) { if (Dependencies.Any(x => Reference.UniqueFragments.Contains(x)) || (Reference.File.InputFile.Flags & SourceFileFlags.Pinned) != 0 || Reference.File == HeaderFile || Reference.File == MonolithicHeader || ExplicitIncludes.Contains(Reference.File) || ((InputFile.Flags & SourceFileFlags.Aggregate) != 0 && Reference.File == Include.TargetFile) || // Always include the original header for aggregates. They are written explicitly to include certain files. Reference.UniqueFragments.Any(x => FragmentsWithReferencedSymbols.Contains(x))) { Include.FinalFiles.Insert(0, Reference.File); Dependencies.ExceptWith(Reference.File.IncludedFragments); Dependencies.UnionWith(Reference.File.Dependencies); } } } } } // Remove any includes that are already included by the matching header if (HeaderFile != null) { HashSet <OutputFile> HeaderIncludedFiles = new HashSet <OutputFile>(HeaderFile.Includes.SelectMany(x => x.FinalFiles)); foreach (OutputFileInclude Include in Includes) { Include.FinalFiles.RemoveAll(x => HeaderIncludedFiles.Contains(x)); } } // Check that all the dependencies have been satisfied if (Dependencies.Count > 0) { // Find those which are completely invalid List <SourceFragment> InvalidDependencies = Dependencies.Where(x => !InputFileStack.Contains(x.File)).ToList(); if (InvalidDependencies.Count > 0) { Log.WriteLine("warning: {0} does not include {1}; may have missing dependencies.", InputFile, String.Join(", ", InvalidDependencies.Select(x => x.Location.FullName))); } Dependencies.ExceptWith(InvalidDependencies); // Otherwise warn about those which were not pinned foreach (SourceFile DependencyFile in Dependencies.Select(x => x.File)) { Log.WriteLine("warning: {0} is included by {1} ({2}), but depends on it and should be marked as pinned.", InputFile, DependencyFile, String.Join(" -> ", InputFileStack.SkipWhile(x => x != DependencyFile).Select(x => x.Location.GetFileName()))); } // Mark it as non-standalone and pinned InputFile.Flags = (InputFile.Flags | SourceFileFlags.Pinned) & ~SourceFileFlags.Standalone; } // Do one more forward pass through all the headers, and remove anything that's included more than once. That can happen if we have a referenced symbol as well as // an explicit include, for example. HashSet <OutputFile> FinalIncludes = new HashSet <OutputFile>(); foreach (OutputFileInclude Include in Includes) { for (int Idx = 0; Idx < Include.FinalFiles.Count; Idx++) { if (!FinalIncludes.Add(Include.FinalFiles[Idx])) { Include.FinalFiles.RemoveAt(Idx); Idx--; } } } // Build the list of satisfied dependencies IncludedFragments = new HashSet <SourceFragment>(); IncludedFragments.UnionWith(Includes.SelectMany(x => x.FinalFiles).SelectMany(x => x.IncludedFragments)); IncludedFragments.UnionWith(InputFile.Fragments); // Build the list of all the included files, so other output files that include it can expand it out. IncludedFiles = new HashList <OutputFile>(); IncludedFiles.UnionWith(Includes.SelectMany(x => x.FinalFiles).SelectMany(x => x.IncludedFiles)); IncludedFiles.Add(this); }