Ejemplo n.º 1
0
        /// <summary>
        /// Write out an optimized file to the given location
        /// </summary>
        /// <param name="IncludePaths">Base directories for relative include paths</param>
        /// <param name="SystemIncludePaths">Base directories for system include paths</param>
        /// <param name="Writer">Writer for the output text</param>
        public void Write(IEnumerable <DirectoryReference> IncludePaths, IEnumerable <DirectoryReference> SystemIncludePaths, TextWriter Writer, TextWriter Log)
        {
            // Write the file header
            TextLocation LastLocation = Text.Start;

            // Write the standalone includes
            if (MissingIncludes.Count > 0)
            {
                TextLocation BoilerplateLocation = (BodyMinIdx == Markup.Length || (Markup[BodyMinIdx].Type != PreprocessorMarkupType.Include && BodyMinIdx > 0))? Markup[BodyMinIdx - 1].EndLocation : Markup[BodyMinIdx].Location;
                WriteLines(Text, LastLocation, BoilerplateLocation, Writer);
                LastLocation = BoilerplateLocation;

                if (LastLocation.LineIdx > 0 && Text.Lines[LastLocation.LineIdx - 1].TrimEnd().Length > 0)
                {
                    if (LastLocation.LineIdx + 1 < Text.Lines.Length && Text.Lines[LastLocation.LineIdx].TrimEnd().Length == 0 && Text.Lines[LastLocation.LineIdx + 1].TrimEnd().Length == 0)
                    {
                        LastLocation.LineIdx++;
                    }
                    Writer.WriteLine();
                }
                foreach (SourceFile MissingInclude in MissingIncludes)
                {
                    string IncludeText = FormatInclude(Location.Directory, MissingInclude.Location, IncludePaths, SystemIncludePaths, Log);
                    Writer.WriteLine("#include {0}", IncludeText);
                }
            }

            // Figure out before which markup object to write forward declarations, skipping over all the includes at the start of the file
            int ForwardDeclarationsBeforeMarkupIdx = -1;

            if ((Flags & SourceFileFlags.TranslationUnit) == 0)
            {
                int ConditionDepth = 0;
                for (int MarkupIdx = BodyMinIdx; MarkupIdx < Markup.Length; MarkupIdx++)
                {
                    if (ConditionDepth == 0)
                    {
                        ForwardDeclarationsBeforeMarkupIdx = MarkupIdx;
                    }
                    if (Markup[MarkupIdx].Type == PreprocessorMarkupType.Text)
                    {
                        break;
                    }
                    ConditionDepth += Markup[MarkupIdx].GetConditionDepthDelta();
                }
            }

            // Write all the other markup
            for (int MarkupIdx = BodyMinIdx; MarkupIdx < Markup.Length; MarkupIdx++)
            {
                PreprocessorMarkup ThisMarkup = Markup[MarkupIdx];

                // Write the forward declarations
                if (MarkupIdx == ForwardDeclarationsBeforeMarkupIdx)
                {
                    // Write out at least up to the end of the last markup
                    if (MarkupIdx > 0 && LastLocation <= Markup[MarkupIdx - 1].EndLocation)
                    {
                        WriteLines(Text, LastLocation, Markup[MarkupIdx - 1].EndLocation, Writer);
                        LastLocation = Markup[MarkupIdx - 1].EndLocation;
                    }

                    // Skip a blank line in the existing text.
                    TextLocation NewLastLocation = LastLocation;
                    if (LastLocation.LineIdx < Text.Lines.Length && String.IsNullOrWhiteSpace(Text.Lines[LastLocation.LineIdx]))
                    {
                        NewLastLocation = new TextLocation(LastLocation.LineIdx + 1, 0);
                    }

                    // Merge all the existing forward declarations with the new set.
                    bool bSkippedForwardDeclarations = false;
                    while (NewLastLocation.LineIdx < Text.Lines.Length)
                    {
                        string TrimLine = Text.Lines[NewLastLocation.LineIdx].Trim();
                        if (TrimLine.Length > 0 && !TrimLine.Equals("// Forward declarations", StringComparison.InvariantCultureIgnoreCase) && !TrimLine.Equals("// Forward declarations.", StringComparison.InvariantCultureIgnoreCase))
                        {
                            // Create a token reader for the current line
                            TokenReader Reader = new TokenReader(Text, new TextLocation(NewLastLocation.LineIdx, 0), new TextLocation(NewLastLocation.LineIdx, Text.Lines[NewLastLocation.LineIdx].Length));

                            // Read it into a buffer
                            List <Token> Tokens = new List <Token>();
                            while (Reader.MoveNext())
                            {
                                Tokens.Add(Reader.Current);
                            }

                            // Check it matches the syntax for a forward declaration, and add it to the list if it does
                            if (Tokens.Count == 3 && (Tokens[0].Text == "struct" || Tokens[0].Text == "class") && Tokens[1].Type == TokenType.Identifier && Tokens[2].Text == ";")
                            {
                                bSkippedForwardDeclarations = true;
                            }
                            else if (Tokens.Count == 4 && Tokens[0].Text == "enum" && Tokens[1].Text == "class" && Tokens[2].Type == TokenType.Identifier && Tokens[3].Text == ";")
                            {
                                bSkippedForwardDeclarations = true;
                            }
                            else if (Tokens.Count == 6 && Tokens[0].Text == "enum" && Tokens[1].Text == "class" && Tokens[2].Type == TokenType.Identifier && Tokens[3].Text == ":" && Tokens[4].Type == TokenType.Identifier && Tokens[5].Text == ";")
                            {
                                bSkippedForwardDeclarations = true;
                            }
                            else if (ForwardDeclarations.Contains(Text.Lines[NewLastLocation.LineIdx]))
                            {
                                bSkippedForwardDeclarations = true;
                            }
                            else
                            {
                                break;
                            }
                        }
                        NewLastLocation = new TextLocation(NewLastLocation.LineIdx + 1, 0);
                    }

                    // Write them out
                    if (ForwardDeclarations.Count > 0)
                    {
                        Writer.WriteLine();
                        //Writer.WriteLine("// Forward declarations");
                        foreach (string ForwardDeclaration in ForwardDeclarations.Distinct().OrderBy(x => GetForwardDeclarationSortKey(x)).ThenBy(x => x))
                        {
                            Writer.WriteLine("{0}{1}", GetIndent(MarkupIdx), ForwardDeclaration);
                        }
                        Writer.WriteLine();
                        LastLocation = NewLastLocation;
                    }
                    else if (bSkippedForwardDeclarations)
                    {
                        Writer.WriteLine();
                        LastLocation = NewLastLocation;
                    }
                }

                // Write the includes
                if (ThisMarkup.Type == PreprocessorMarkupType.Include && ThisMarkup.IsActive && !ThisMarkup.IsInlineInclude())
                {
                    // Write up to the start of this include
                    WriteLines(Text, LastLocation, ThisMarkup.Location, Writer);

                    // Get the original include text. Some modules - particularly editor modules - include headers from other modules based from Engine/Source which are not listed as dependencies. If
                    // the original include is based from a shallower directory than the one we would include otherwise, we'll use that instead.
                    string OriginalIncludeText = null;
                    if (ThisMarkup.Tokens.Count == 1)
                    {
                        OriginalIncludeText = ThisMarkup.Tokens[0].Text.Replace('\\', '/');
                    }

                    // Write the replacement includes
                    foreach (SourceFile OutputIncludedFile in ThisMarkup.OutputIncludedFiles)
                    {
                        string IncludeText = FormatInclude(Location.Directory, OutputIncludedFile.Location, IncludePaths, SystemIncludePaths, Log);
                        if (OutputIncludedFile == ThisMarkup.IncludedFile && Rules.IsExternalIncludeMacro(ThisMarkup.Tokens))
                        {
                            IncludeText = Token.Format(ThisMarkup.Tokens);
                        }
                        else if (OutputIncludedFile == ThisMarkup.IncludedFile && (OutputIncludedFile.Flags & SourceFileFlags.External) != 0)
                        {
                            IncludeText = OriginalIncludeText;
                        }
                        else if (OriginalIncludeText != null && (Flags & SourceFileFlags.TranslationUnit) == 0 && OriginalIncludeText.EndsWith(IncludeText.TrimStart('\"'), StringComparison.InvariantCultureIgnoreCase) && (OriginalIncludeText.StartsWith("\"Runtime/", StringComparison.InvariantCultureIgnoreCase) || OriginalIncludeText.StartsWith("\"Developer/", StringComparison.InvariantCultureIgnoreCase) || OriginalIncludeText.StartsWith("\"Editor/", StringComparison.InvariantCultureIgnoreCase)))
                        {
                            IncludeText = OriginalIncludeText;
                        }
                        Writer.WriteLine("{0}#include {1}", GetIndent(MarkupIdx), IncludeText);
                    }

                    // Copy any formatting
                    if (ThisMarkup.EndLocation.LineIdx > ThisMarkup.Location.LineIdx + 1)
                    {
                        WriteLines(Text, new TextLocation(ThisMarkup.Location.LineIdx + 1, 0), ThisMarkup.EndLocation, Writer);
                    }

                    // Update the location to the start of the next line
                    LastLocation = new TextLocation(ThisMarkup.Location.LineIdx + 1, 0);
                }
            }

            // Write to the end of the file
            WriteLines(Text, LastLocation, Text.End, Writer);
        }