/// <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;
        }
示例#2
0
        /// <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]);
                    }
                }
            }
        }
示例#3
0
 /// <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;
 }
示例#4
0
        /// <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);
        }
示例#5
0
        /// <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);
        }
示例#6
0
        /// <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);
                }
            }
        }
示例#7
0
        /// <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();
            }
        }