/// <summary> /// Reads an exported XGE task list /// </summary> /// <param name="TaskListFile">File to read from</param> /// <param name="FileToEnvironment">Mapping from source file to compile environment</param> public static void ReadTaskList(FileReference TaskListFile, DirectoryReference BaseDir, out Dictionary <FileReference, CompileEnvironment> FileToEnvironment) { XmlDocument Document = new XmlDocument(); Document.Load(TaskListFile.FullName); // Read the standard include paths from the INCLUDE environment variable in the script List <DirectoryReference> StandardIncludePaths = new List <DirectoryReference>(); foreach (XmlNode Node in Document.SelectNodes("BuildSet/Environments/Environment/Variables/Variable")) { XmlAttribute NameAttr = Node.Attributes["Name"]; if (NameAttr != null && String.Compare(NameAttr.InnerText, "INCLUDE") == 0) { foreach (string IncludePath in Node.Attributes["Value"].InnerText.Split(';')) { StandardIncludePaths.Add(new DirectoryReference(IncludePath)); } } } // Read all the individual compiles Dictionary <FileReference, CompileEnvironment> NewFileToEnvironment = new Dictionary <FileReference, CompileEnvironment>(); foreach (XmlNode Node in Document.SelectNodes("BuildSet/Environments/Environment/Tools/Tool")) { XmlAttribute ToolPathAttr = Node.Attributes["Path"]; if (ToolPathAttr != null) { // Get the full path to the tool FileReference ToolLocation = new FileReference(ToolPathAttr.InnerText); // Get the compiler type CompilerType CompilerType; if (GetCompilerType(ToolLocation, out CompilerType)) { string Name = Node.Attributes["Name"].InnerText; string Params = Node.Attributes["Params"].InnerText; // Construct the compile environment CompileEnvironment Environment = new CompileEnvironment(ToolLocation, CompilerType); // Parse a list of tokens List <string> Tokens = new List <string>(); ParseArgumentTokens(CompilerType, Params, Tokens); // Parse it into a list of options, removing any that don't apply List <FileReference> SourceFiles = new List <FileReference>(); List <FileReference> OutputFiles = new List <FileReference>(); for (int Idx = 0; Idx < Tokens.Count; Idx++) { if (Tokens[Idx] == "/Fo" || Tokens[Idx] == "/Fp" || Tokens[Idx] == "-o") { OutputFiles.Add(new FileReference(Tokens[++Idx])); } else if (Tokens[Idx].StartsWith("/Fo") || Tokens[Idx].StartsWith("/Fp")) { OutputFiles.Add(new FileReference(Tokens[Idx].Substring(3))); } else if (Tokens[Idx] == "/D" || Tokens[Idx] == "-D") { Environment.Definitions.Add(Tokens[++Idx]); } else if (Tokens[Idx].StartsWith("/D")) { Environment.Definitions.Add(Tokens[Idx].Substring(2)); } else if (Tokens[Idx] == "/I" || Tokens[Idx] == "-I") { Environment.IncludePaths.Add(new DirectoryReference(DirectoryReference.Combine(BaseDir, Tokens[++Idx].Replace("//", "/")).FullName.ToLowerInvariant())); } else if (Tokens[Idx].StartsWith("-I")) { Environment.IncludePaths.Add(new DirectoryReference(DirectoryReference.Combine(BaseDir, Tokens[Idx].Substring(2).Replace("//", "/")).FullName.ToLowerInvariant())); } else if (Tokens[Idx] == "-Xclang" || Tokens[Idx] == "-target" || Tokens[Idx] == "--target" || Tokens[Idx] == "-x" || Tokens[Idx] == "-o") { Environment.Options.Add(new CompileOption(Tokens[Idx], Tokens[++Idx])); } else if (Tokens[Idx].StartsWith("/") || Tokens[Idx].StartsWith("-")) { Environment.Options.Add(new CompileOption(Tokens[Idx], null)); } else { SourceFiles.Add(FileReference.Combine(BaseDir, Tokens[Idx])); } } // Make sure we're not compiling a precompiled header if (!OutputFiles.Any(x => x.HasExtension(".gch")) && !Environment.Options.Any(x => x.Name.StartsWith("/Yc"))) { // Add all the standard include paths Environment.IncludePaths.AddRange(StandardIncludePaths); // Add to the output map if there are any source files. Use the extension to distinguish between a source file and linker invocation on Clang. foreach (FileReference SourceFile in SourceFiles) { if (!SourceFile.HasExtension(".a")) { NewFileToEnvironment.Add(SourceFile, Environment); } } } } } } FileToEnvironment = NewFileToEnvironment; }
/// <summary> /// Generates a report listing the most frequently referenced opaque symbols in each module /// </summary> /// <param name="Files">The files to include in the report</param> /// <param name="ReportFileLocation">Output file for the report</param> /// <param name="Log">Writer for log output</param> public static void Generate(FileReference ReportFileLocation, DirectoryReference InputDir, HashSet <SourceFile> PreprocessedFiles, TextWriter Log) { Log.WriteLine("Writing {0}...", ReportFileLocation.FullName); // Get a count of files referencing each symbol Dictionary <Symbol, int> SymbolToRefCount = new Dictionary <Symbol, int>(); Dictionary <Symbol, int> SymbolToOpaqueRefCount = new Dictionary <Symbol, int>(); foreach (SourceFile PreprocessedFile in PreprocessedFiles) { if (PreprocessedFile.Fragments != null) { HashSet <Symbol> Symbols = new HashSet <Symbol>(); HashSet <Symbol> NonOpaqueSymbols = new HashSet <Symbol>(); foreach (SourceFragment Fragment in PreprocessedFile.Fragments) { foreach (KeyValuePair <Symbol, SymbolReferenceType> Pair in Fragment.ReferencedSymbols) { Symbols.Add(Pair.Key); if (Pair.Value != SymbolReferenceType.Opaque) { NonOpaqueSymbols.Add(Pair.Key); } } } foreach (Symbol Symbol in Symbols) { int Count; SymbolToRefCount.TryGetValue(Symbol, out Count); SymbolToRefCount[Symbol] = Count + 1; int OpaqueCount; SymbolToOpaqueRefCount.TryGetValue(Symbol, out OpaqueCount); SymbolToOpaqueRefCount[Symbol] = NonOpaqueSymbols.Contains(Symbol) ? OpaqueCount : OpaqueCount + 1; } } } // Build a map of module to symbols MultiValueDictionary <BuildModule, Symbol> ModuleToSymbols = new MultiValueDictionary <BuildModule, Symbol>(); foreach (Symbol Symbol in SymbolToRefCount.Keys) { SourceFile File = Symbol.Fragment.File; if (File.Module != null) { ModuleToSymbols.Add(File.Module, Symbol); } } // Write out a CSV report containing the list of symbols and number of files referencing them using (StreamWriter Writer = new StreamWriter(ReportFileLocation.FullName)) { Writer.WriteLine("Module,Symbol,Fwd,RefCount,OpaqueRefCount"); foreach (BuildModule Module in ModuleToSymbols.Keys) { foreach (Symbol Symbol in ModuleToSymbols[Module].OrderByDescending(x => SymbolToOpaqueRefCount[x])) { Writer.WriteLine("{0},{1},{2},{3},{4}", Module.Name, Symbol.Name, Symbol.ForwardDeclaration, SymbolToRefCount[Symbol], SymbolToOpaqueRefCount[Symbol]); } } } }
/// <summary> /// Constructor /// </summary> /// <param name="InFile">The source file that this fragment is part of</param> /// <param name="InMarkupMin">Index into the file's markup array of the start of this fragment (inclusive)</param> /// <param name="InMarkupMax">Index into the file's markup array of the end of this fragment (exclusive)</param> public SourceFragment(SourceFile File, int MarkupMin, int MarkupMax) { this.File = File; this.MarkupMin = MarkupMin; this.MarkupMax = MarkupMax; }
/// <summary> /// Creates an optimized output file /// </summary> /// <param name="InputFile">The input file that this output file corresponds to</param> /// <param name="HeaderFile">The corresponding header file</param> /// <param name="PreviousFiles">List of files parsed before this one</param> /// <param name="Includes">The active set of includes parsed for this file</param> /// <param name="InputFileStack">The active include stack</param> /// <param name="FwdSymbolToHeader"></param> /// <param name="bMakeStandalone">Whether to make this output file standalone</param> /// <param name="Log">Writer for log messages</param> public static OutputFile CreateOptimizedOutputFile(SourceFile InputFile, OutputFile HeaderFile, List <OutputFile> PreviousFiles, List <OutputFileInclude> Includes, List <SourceFile> InputFileStack, Dictionary <Symbol, OutputFile> FwdSymbolToHeader, bool bMakeStandalone, TextWriter Log) { Debug.Assert(HeaderFile == null || (InputFile.Flags & SourceFileFlags.TranslationUnit) != 0); // Write the state InputFile.LogVerbose("InputFile={0}", InputFile.Location.FullName); InputFile.LogVerbose("InputFile.Flags={0}", InputFile.Flags.ToString()); if (HeaderFile != null) { InputFile.LogVerbose("HeaderFile={0}", HeaderFile.InputFile.Location.FullName); InputFile.LogVerbose("HeaderFile.Flags={0}", HeaderFile.InputFile.Flags.ToString()); } InputFile.LogVerbose(""); for (int Idx = 0; Idx < InputFileStack.Count; Idx++) { InputFile.LogVerbose("InputFileStack[{0}]={1}", Idx, InputFileStack[Idx].Location.FullName); } InputFile.LogVerbose(""); for (int Idx = 0; Idx < PreviousFiles.Count; Idx++) { InputFile.LogVerbose("PreviousFiles[{0}]={1}", Idx, PreviousFiles[Idx].InputFile.Location.FullName); } InputFile.LogVerbose(""); for (int Idx = 0; Idx < Includes.Count; Idx++) { } InputFile.LogVerbose(""); for (int Idx = 0; Idx < Includes.Count; Idx++) { OutputFileInclude Include = Includes[Idx]; InputFile.LogVerbose("Includes[{0}]={1}", Idx, Includes[Idx].TargetFile.InputFile.Location.FullName); foreach (SourceFragment Fragment in Include.FinalFiles.SelectMany(x => x.IncludedFragments)) { InputFile.LogVerbose("Includes[{0}].FinalFiles.IncludedFragments={1}", Idx, Fragment); } } // 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 ((InputFile.Flags & SourceFileFlags.Standalone) != 0 && (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. OutputFileReference ForcedHeaderFileReference = null; foreach (OutputFileInclude Include in Includes) { if (Include.ExpandedReferences == null) { Include.ExpandedReferences = new List <OutputFileReference>(); if (Include == Includes[0] && HeaderFile != null) { ForcedHeaderFileReference = new OutputFileReference(HeaderFile, HeaderFile.IncludedFragments); Include.ExpandedReferences.Add(ForcedHeaderFileReference); 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 HashSet <SourceFragment> Dependencies = new HashSet <SourceFragment>(); List <Symbol> ForwardDeclarations = new List <Symbol>(); 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 == ForcedHeaderFileReference || 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}{2}; may have missing dependencies.", InputFile, String.Join(", ", InvalidDependencies.Select(x => x.Location.FullName).Take(3)), (InvalidDependencies.Count > 3)? String.Format(" and {0} others", InvalidDependencies.Count - 3) : ""); } 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--; } } } // Create the optimized file OutputFile OptimizedFile = new OutputFile(InputFile, Includes, Dependencies, ForwardDeclarations); // Write the verbose log InputFile.LogVerbose(""); foreach (OutputFile IncludedFile in OptimizedFile.IncludedFiles) { InputFile.LogVerbose("Output: {0}", IncludedFile.InputFile.Location.FullName); } // Return the optimized file return(OptimizedFile); }
/// <summary> /// Find or create an output file for a corresponding input file /// </summary> /// <param name="InputFile">The input file</param> /// <param name="IncludeStack">The current include stack</param> /// <param name="OutputFiles">List of output files</param> /// <param name="OutputFileLookup">Mapping from source file to output file</param> /// <returns>The new or existing output file</returns> static OutputFile FindOrCreateOutputFile(List <SourceFile> InputFileStack, Dictionary <SourceFile, SourceFile> CppFileToHeaderFile, HashList <OutputFile> PreviousFiles, Dictionary <SourceFile, OutputFile> OutputFileLookup, Dictionary <Symbol, OutputFile> FwdSymbolToHeader, bool bMakeStandalone, bool bUseOriginalIncludes, TextWriter Log) { // Get the file at the top of the stack SourceFile InputFile = InputFileStack[InputFileStack.Count - 1]; // Try to find an existing file OutputFile OutputFile; if (OutputFileLookup.TryGetValue(InputFile, out OutputFile)) { if (OutputFile == null) { throw new Exception("Circular include dependencies are not allowed."); } foreach (OutputFile IncludedFile in OutputFile.OriginalIncludedFiles.Where(x => !PreviousFiles.Contains(x))) { PreviousFiles.Add(IncludedFile); } } else { // Add a placeholder entry in the output file lookup, so we can detect circular include dependencies OutputFileLookup[InputFile] = null; // Duplicate the list of previously included files, so we can construct the List <OutputFile> PreviousFilesCopy = new List <OutputFile>(PreviousFiles); // Build a list of includes for this file. First include is a placeholder for any missing includes that need to be inserted. List <OutputFileInclude> Includes = new List <OutputFileInclude>(); if ((InputFile.Flags & SourceFileFlags.External) == 0) { for (int MarkupIdx = 0; MarkupIdx < InputFile.Markup.Length; MarkupIdx++) { PreprocessorMarkup Markup = InputFile.Markup[MarkupIdx]; if (Markup.IsActive && Markup.IncludedFile != null && (Markup.IncludedFile.Flags & SourceFileFlags.Inline) == 0 && Markup.IncludedFile.Counterpart == null) { InputFileStack.Add(Markup.IncludedFile); OutputFile IncludeFile = FindOrCreateOutputFile(InputFileStack, CppFileToHeaderFile, PreviousFiles, OutputFileLookup, FwdSymbolToHeader, bMakeStandalone, bUseOriginalIncludes, Log); InputFileStack.RemoveAt(InputFileStack.Count - 1); Includes.Add(new OutputFileInclude(MarkupIdx, IncludeFile)); } } } // Find the matching header file OutputFile HeaderFile = null; if ((InputFile.Flags & SourceFileFlags.TranslationUnit) != 0) { SourceFile CandidateHeaderFile; if (CppFileToHeaderFile.TryGetValue(InputFile, out CandidateHeaderFile) && (CandidateHeaderFile.Flags & SourceFileFlags.Standalone) != 0) { OutputFileLookup.TryGetValue(CandidateHeaderFile, out HeaderFile); } } // Create the output file. if ((InputFile.Flags & SourceFileFlags.Output) != 0 && !bUseOriginalIncludes) { OutputFile = CreateOptimizedOutputFile(InputFile, HeaderFile, PreviousFilesCopy, Includes, InputFileStack, FwdSymbolToHeader, bMakeStandalone, Log); } else { OutputFile = CreatePassthroughOutputFile(InputFile, Includes, Log); } // Replace the null entry in the output file lookup that we added earlier OutputFileLookup[InputFile] = OutputFile; // Add this file to the list of included files PreviousFiles.Add(OutputFile); // If the output file dependends on something on the stack, make sure it's marked as pinned if ((InputFile.Flags & SourceFileFlags.Pinned) == 0) { SourceFragment Dependency = OutputFile.Dependencies.FirstOrDefault(x => InputFileStack.Contains(x.File) && x.File != InputFile); if (Dependency != null) { throw new Exception(String.Format("'{0}' is not marked as pinned, but depends on '{1}' which includes it", InputFile.Location.GetFileName(), Dependency.UniqueName)); } } } return(OutputFile); }
/// <summary> /// Read all the exported symbol definitions from a file /// </summary> /// <param name="File">The file to parse</param> /// <param name="MarkupIdx">The current index into the markup array of this file</param> /// <param name="Scope">The scoping depth, ie. number of unmatched '{' tokens encountered in source code</param> void ReadExportedSymbols(SourceFile File, ref int MarkupIdx, ref int Scope) { // Ignore files which only have a header guard; there are no fragments that we can link to if (File.BodyMinIdx == File.BodyMaxIdx) { return; } // Read markup from the file for (; MarkupIdx < File.Markup.Length; MarkupIdx++) { PreprocessorMarkup Markup = File.Markup[MarkupIdx]; if (Markup.Type == PreprocessorMarkupType.Define) { // If the macro is not already defined, add it if (Markup.Tokens[0].Text != "LOCTEXT_NAMESPACE") { SourceFragment Fragment = FindFragment(File, MarkupIdx); AddSymbol(Markup.Tokens[0].Text, SymbolType.Macro, null, Fragment, Markup.Location); } } else if (Markup.Type == PreprocessorMarkupType.If && Markup.Tokens.Count == 2 && Markup.Tokens[0].Text == "!" && Markup.Tokens[1].Text == "CPP") { // Completely ignore these blocks; they sometimes differ in class/struct usage, and exist solely for noexport types to be parsed by UHT int Depth = 1; while (Depth > 0) { Markup = File.Markup[++MarkupIdx]; if (Markup.Type == PreprocessorMarkupType.If || Markup.Type == PreprocessorMarkupType.Ifdef || Markup.Type == PreprocessorMarkupType.Ifndef) { Depth++; } else if (Markup.Type == PreprocessorMarkupType.Endif) { Depth--; } } } else if (Markup.Type == PreprocessorMarkupType.If || Markup.Type == PreprocessorMarkupType.Ifdef || Markup.Type == PreprocessorMarkupType.Ifndef) { // Loop through all the branches of this conditional block, saving the resulting scope of each derivation List <int> NewScopes = new List <int>(); while (Markup.Type != PreprocessorMarkupType.Endif) { // Skip over the conditional directive MarkupIdx++; // Read any exported symbols from this conditional block int NewScope = Scope; ReadExportedSymbols(File, ref MarkupIdx, ref NewScope); NewScopes.Add(NewScope); // Get the new preprocessor markup Markup = File.Markup[MarkupIdx]; } // Make sure they were all consistent if (NewScopes.Any(x => x != NewScopes[0])) { throw new UnbalancedScopeException(String.Format("Unbalanced scope depth between conditional block derivations while parsing {0}", File.Location)); } // Update the scope Scope = NewScopes[0]; } else if (Markup.Type == PreprocessorMarkupType.Else || Markup.Type == PreprocessorMarkupType.Elif || Markup.Type == PreprocessorMarkupType.Endif) { // We've reached the end of this block; return success break; } else if (Markup.Type == PreprocessorMarkupType.Text) { // Read the declarations in this block SourceFragment Fragment = FindFragment(File, MarkupIdx); ParseDeclarations(File, Fragment, File.Markup[MarkupIdx], ref Scope); } } }
/// <summary> /// Constructor /// </summary> /// <param name="Type">The type of probe to create</param> /// <param name="Node">The node to optimize</param> /// <param name="IntermediateDir">Directory for intermediate files</param> /// <param name="UniqueName">Unique name prefix for all temporary files</param> public SequenceProbe(SequenceProbeType Type, SourceFragment[] Fragments, Tuple <int, SourceFile>[] IncludeHistory, CompileEnvironment CompileEnvironment, DirectoryReference IntermediateDir, IEnumerable <DirectoryReference> ExtraSystemIncludePaths, string UniqueName) { this.Type = Type; this.IntermediateDir = IntermediateDir; this.Fragments = Fragments; this.LastFragment = Fragments[Fragments.Length - 1]; this.UniqueName = UniqueName; this.TaskStateFile = FileReference.Combine(IntermediateDir, UniqueName + ((Type == SequenceProbeType.Verify)? ".verify.state" : ".state")); this.SummaryLogFile = FileReference.Combine(IntermediateDir, UniqueName + ".summary.txt"); this.CompileLogFile = FileReference.Combine(IntermediateDir, UniqueName + ".compile.txt"); // Get the file to use for trying to compile different permutations FileReference PermutationFile = FileReference.Combine(IntermediateDir, UniqueName + ".permutation"); // Create the response file FileReference ResponseFile = FileReference.Combine(IntermediateDir, UniqueName + ".response"); CompileEnvironment WorkerCompileEnvironment = new CompileEnvironment(CompileEnvironment); if (WorkerCompileEnvironment.CompilerType == CompilerType.Clang) { WorkerCompileEnvironment.Options.Add(new CompileOption("-o", FileReference.Combine(IntermediateDir, UniqueName + ".o").FullName.Replace('\\', '/'))); } else { WorkerCompileEnvironment.Options.RemoveAll(x => x.Name == "/Z7" || x.Name == "/Zi" || x.Name == "/ZI"); WorkerCompileEnvironment.Options.Add(new CompileOption("/Fo", FileReference.Combine(IntermediateDir, UniqueName + ".obj").FullName)); WorkerCompileEnvironment.Options.Add(new CompileOption("/WX", null)); } WorkerCompileEnvironment.WriteResponseFile(ResponseFile, PermutationFile); string ResponseFileDigest = Utility.ComputeDigest(ResponseFile); // Keep track of the include stack, so we can format the flat fragment list with context int IncludeHistoryIdx = 0; List <SourceFile> IncludeStack = new List <SourceFile>(); // Create the script for the probe List <Tuple <int, string> > Lines = new List <Tuple <int, string> >(); for (int Idx = 0; Idx < Fragments.Length; Idx++) { SourceFragment Fragment = Fragments[Idx]; // Figure out which tag it's bound to int Tag = (Fragment.File.Counterpart == null)? Idx : -1; // Update the stack for new includes while (IncludeHistoryIdx < IncludeHistory.Length && IncludeHistory[IncludeHistoryIdx].Item1 == Idx) { if (IncludeHistory[IncludeHistoryIdx].Item2 == null) { SourceFile IncludeFile = IncludeStack[IncludeStack.Count - 1]; IncludeStack.RemoveAt(IncludeStack.Count - 1); Lines.Add(new Tuple <int, string>(Tag, String.Format("{0}// END INCLUDE {1}", new string(' ', IncludeStack.Count * 4), IncludeFile.Location.FullName))); IncludeHistoryIdx++; } else if (IncludeHistoryIdx + 1 < IncludeHistory.Length && IncludeHistory[IncludeHistoryIdx + 1].Item2 == null) { IncludeHistoryIdx += 2; } else { SourceFile IncludeFile = IncludeHistory[IncludeHistoryIdx].Item2; Lines.Add(new Tuple <int, string>(Tag, String.Format("{0}// INCLUDE {1}", new string(' ', IncludeStack.Count * 4), IncludeFile.Location.FullName))); IncludeStack.Add(IncludeFile); IncludeHistoryIdx++; } } // Get the indent at this point string Indent = new string(' ', (IncludeStack.Count - 0) * 4); // Write out the forward declarations for every symbol referenced in this fragment. We don't want false dependencies caused by forward declarations in other fragments // if the heuristic for detecting when to use them doesn't work. if ((Fragment.File.Flags & SourceFileFlags.TranslationUnit) == 0) { foreach (KeyValuePair <Symbol, SymbolReferenceType> ReferencedSymbol in Fragment.ReferencedSymbols) { if (!String.IsNullOrEmpty(ReferencedSymbol.Key.ForwardDeclaration)) { Lines.Add(Tuple.Create(Tag, Indent + ReferencedSymbol.Key.ForwardDeclaration)); } } } // Some Clang/GCC system header wrappers require including as system includes in order to make the #include_next directive work DirectoryReference BaseSystemIncludePath = ExtraSystemIncludePaths.FirstOrDefault(x => Fragment.Location.IsUnderDirectory(x)); if (BaseSystemIncludePath != null) { Lines.Add(Tuple.Create(Tag, String.Format("{0}#include <{1}>", Indent, Fragment.Location.MakeRelativeTo(BaseSystemIncludePath)))); } else { Lines.Add(Tuple.Create(Tag, String.Format("{0}#include \"{1}\"", Indent, Fragment.Location.FullName))); } } // Create the new task string[] FragmentFileNames = Fragments.Select(Fragment => Fragment.Location.FullName).ToArray(); string[] FragmentDigests = Fragments.Select(Fragment => Fragment.Digest ?? "").ToArray(); Worker = new SequenceWorker(PermutationFile.FullName, ResponseFile.FullName, ResponseFileDigest, FragmentFileNames, FragmentDigests, Lines.ToArray(), CompileEnvironment.Compiler.FullName); AddDependency(Worker, Fragments, Fragments.Length - 1); // Log the referenced symbols if (LastFragment.ReferencedSymbols.Count > 0) { List <string> ReferenceLog = new List <string>(); ReferenceLog.Add(String.Format("Referenced symbols for {0}:", LastFragment.Location)); foreach (KeyValuePair <Symbol, SymbolReferenceType> Pair in LastFragment.ReferencedSymbols.OrderBy(x => x.Key.Type).ThenBy(x => x.Key.Name)) { Symbol ReferencedSymbol = Pair.Key; ReferenceLog.Add(String.Format(" {0}: {1} ({2}; {3})", ReferencedSymbol.Type.ToString(), ReferencedSymbol.Name, Pair.Value.ToString(), ReferencedSymbol.Fragment.Location)); } ReferenceLog.Add(""); Worker.SummaryLog.InsertRange(0, ReferenceLog); } // Check to see if an existing version of the task exists which we can continue if (Type == SequenceProbeType.Optimize && TaskStateFile.Exists()) { // Try to read the old task SequenceWorker OldWorker; try { OldWorker = SequenceWorker.Deserialize(TaskStateFile.FullName); } catch (Exception) { OldWorker = null; } // If it succeeded, compare it to the new task if (OldWorker != null) { SequenceWorker NewWorker = Worker; // Create a list of fragments which can be ignored, because they're already determined to be not part of the result for the old task HashSet <string> IgnoreFragments = new HashSet <string>(); for (int Idx = 0; Idx < OldWorker.FragmentCount; Idx++) { if (!OldWorker.KnownDependencies.Contains(Idx) && Idx >= OldWorker.RemainingFragmentCount) { IgnoreFragments.Add(OldWorker.FragmentFileNames[Idx]); } } IgnoreFragments.ExceptWith(NewWorker.KnownDependencies.Select(x => NewWorker.FragmentFileNames[x])); // Compute digests for the old and new tasks string OldDigest = CreateDigest(OldWorker, IgnoreFragments); string NewDigest = CreateDigest(NewWorker, IgnoreFragments); if (OldDigest == NewDigest) { // Build a map of fragment file names to their new index Dictionary <string, int> FragmentFileNameToNewIndex = new Dictionary <string, int>(); for (int Idx = 0; Idx < FragmentFileNames.Length; Idx++) { string FragmentFileName = FragmentFileNames[Idx]; FragmentFileNameToNewIndex[FragmentFileName] = Idx; } // Add known dependencies to the new worker foreach (int OldKnownDependency in OldWorker.KnownDependencies) { string OldFragmentFileName = OldWorker.FragmentFileNames[OldKnownDependency]; int NewKnownDependency = FragmentFileNameToNewIndex[OldFragmentFileName]; NewWorker.KnownDependencies.Add(NewKnownDependency); } // Update the remaining count. All these fragments must match, because they're not part of the ignore list. NewWorker.RemainingFragmentCount = OldWorker.RemainingFragmentCount; } } } // If this is a cpp file, make sure we have a dependency on the header file with the same name. It may specify linkage for the functions we declare. FileReference MainFileLocation = Fragments[Fragments.Length - 1].File.Location; if (MainFileLocation.HasExtension(".cpp")) { string HeaderFileName = Path.ChangeExtension(MainFileLocation.GetFileName(), ".h"); for (int FragmentIdx = 0; FragmentIdx < Fragments.Length; FragmentIdx++) { if (String.Compare(Fragments[FragmentIdx].File.Location.GetFileName(), HeaderFileName, true) == 0) { AddDependency(Worker, Fragments, FragmentIdx); } } } // Update the finished fragment if we're done, otherwise clear out all the intermediate files if (Worker.RemainingFragmentCount == 0) { SetCompletedDependencies(); } else { Worker.Serialize(TaskStateFile.FullName); PermutationFile.Delete(); SummaryLogFile.Delete(); CompileLogFile.Delete(); } }