From 0f3badd62b24da46e5a041e6d56ac33075815723 Mon Sep 17 00:00:00 2001 From: Jelle Date: Tue, 25 Feb 2025 16:42:20 +0000 Subject: [PATCH] Allow user to set generator newline output type (#1915) * Allow user defined `NewLine` sequence * Auto detect line endings * Handle exception if `git` is not found --- src/Generator/Driver.cs | 12 +++ src/Generator/Generators/CodeGenerator.cs | 8 +- src/Generator/Options.cs | 11 +++ .../Passes/GetterSetterToPropertyPass.cs | 4 +- src/Generator/Utils/BlockGenerator.cs | 6 +- src/Generator/Utils/ProcessHelper.cs | 76 ++++++++++--------- src/Generator/Utils/TextGenerator.cs | 25 ++++-- src/Generator/Utils/Utils.cs | 58 +++++++++++++- 8 files changed, 149 insertions(+), 51 deletions(-) diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 856398e9..b8e15ecd 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -45,8 +45,20 @@ namespace CppSharp } if (Options.NoGenIncludeDirs != null) + { foreach (var incDir in Options.NoGenIncludeDirs) ParserOptions.AddIncludeDirs(incDir); + } + + NewLineType newLineType = Options.OutputNewLineType; + if (Options.OutputNewLineType == NewLineType.Auto) + { + var sourceNewLineType = GeneratorHelpers.GetNewLineTypeFromGitConfig(Options.OutputDir); + + newLineType = sourceNewLineType ?? NewLineType.Host; + } + + TextGenerator.SetLineEndingType(newLineType); } public void Setup() diff --git a/src/Generator/Generators/CodeGenerator.cs b/src/Generator/Generators/CodeGenerator.cs index e8b8a0b3..b3d74a31 100644 --- a/src/Generator/Generators/CodeGenerator.cs +++ b/src/Generator/Generators/CodeGenerator.cs @@ -140,11 +140,13 @@ namespace CppSharp.Generators var lines = new List(); - if (comment.BriefText.Contains("\n")) + if (comment.BriefText.Contains('\n')) { + var commentLines = HtmlEncoder.HtmlEncode(comment.BriefText) + .Split('\n', '\r'); + lines.Add(""); - foreach (string line in HtmlEncoder.HtmlEncode(comment.BriefText).Split( - Environment.NewLine.ToCharArray())) + foreach (string line in commentLines) { if (string.IsNullOrWhiteSpace(line)) continue; diff --git a/src/Generator/Options.cs b/src/Generator/Options.cs index fe90828f..0d9a0008 100644 --- a/src/Generator/Options.cs +++ b/src/Generator/Options.cs @@ -15,6 +15,15 @@ namespace CppSharp FilePerUnit } + public enum NewLineType + { + Auto, // Attempt to auto-detect the new line type using git repository settings of the output directory + Host, // Same as Environment.NewLine on the source generator host + LF, + CR, + CRLF, + } + public class DriverOptions { public DriverOptions() @@ -84,6 +93,8 @@ namespace CppSharp public string OutputDir; + public NewLineType OutputNewLineType = NewLineType.Auto; + public bool OutputInteropIncludes; public bool GenerateFunctionTemplates; /// diff --git a/src/Generator/Passes/GetterSetterToPropertyPass.cs b/src/Generator/Passes/GetterSetterToPropertyPass.cs index a5d0be1a..0653ab3a 100644 --- a/src/Generator/Passes/GetterSetterToPropertyPass.cs +++ b/src/Generator/Passes/GetterSetterToPropertyPass.cs @@ -334,8 +334,8 @@ namespace CppSharp.Passes Method setter = property.SetMethod; if (getter != setter && setter?.Comment != null) { - comment.BriefText += Environment.NewLine + setter.Comment.BriefText; - comment.Text += Environment.NewLine + setter.Comment.Text; + comment.BriefText += TextGenerator.NewLineChar + setter.Comment.BriefText; + comment.Text += TextGenerator.NewLineChar + setter.Comment.Text; comment.FullComment.Blocks.AddRange(setter.Comment.FullComment.Blocks); } } diff --git a/src/Generator/Utils/BlockGenerator.cs b/src/Generator/Utils/BlockGenerator.cs index 1dff9927..56a2b193 100644 --- a/src/Generator/Utils/BlockGenerator.cs +++ b/src/Generator/Utils/BlockGenerator.cs @@ -135,12 +135,12 @@ namespace CppSharp if (previousBlock != null && (previousBlock.NewLineKind == NewLineKind.BeforeNextBlock || (previousBlock.NewLineKind == NewLineKind.IfNotEmpty && !previousBlockEmpty))) - builder.AppendLine(); + builder.Append(TextGenerator.NewLineChar); builder.Append(childText); if (childBlock.NewLineKind == NewLineKind.Always) - builder.AppendLine(); + builder.Append(TextGenerator.NewLineChar); previousBlock = childBlock; previousBlockEmpty = childText.Length == 0; @@ -318,7 +318,7 @@ namespace CppSharp private readonly BlockGenerator generator; private readonly NewLineKind next; - public PushedBlock(BlockGenerator generator, NewLineKind next) + public PushedBlock(BlockGenerator generator, NewLineKind next) { this.generator = generator; this.next = next; diff --git a/src/Generator/Utils/ProcessHelper.cs b/src/Generator/Utils/ProcessHelper.cs index d482b89f..96a562bf 100644 --- a/src/Generator/Utils/ProcessHelper.cs +++ b/src/Generator/Utils/ProcessHelper.cs @@ -7,46 +7,50 @@ namespace CppSharp.Utils { public static string Run(string path, string args, out int error, out string errorMessage) { - using (var process = new Process()) + return RunFrom(null, path, args, out error, out errorMessage); + } + + public static string RunFrom(string workingDir, string path, string args, out int error, out string errorMessage) + { + using var process = new Process(); + process.StartInfo.WorkingDirectory = workingDir; + process.StartInfo.FileName = path; + process.StartInfo.Arguments = args; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + + var reterror = new StringBuilder(); + var retout = new StringBuilder(); + process.OutputDataReceived += (sender, outargs) => + { + if (string.IsNullOrEmpty(outargs.Data)) + return; + + if (retout.Length > 0) + retout.AppendLine(); + retout.Append(outargs.Data); + }; + process.ErrorDataReceived += (sender, errargs) => { - process.StartInfo.FileName = path; - process.StartInfo.Arguments = args; - process.StartInfo.UseShellExecute = false; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; + if (string.IsNullOrEmpty(errargs.Data)) + return; - var reterror = new StringBuilder(); - var retout = new StringBuilder(); - process.OutputDataReceived += (sender, outargs) => - { - if (!string.IsNullOrEmpty(outargs.Data)) - { - if (retout.Length > 0) - retout.AppendLine(); - retout.Append(outargs.Data); - } - }; - process.ErrorDataReceived += (sender, errargs) => - { - if (!string.IsNullOrEmpty(errargs.Data)) - { - if (reterror.Length > 0) - reterror.AppendLine(); - reterror.Append(errargs.Data); - } - }; + if (reterror.Length > 0) + reterror.AppendLine(); + reterror.Append(errargs.Data); + }; - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - process.WaitForExit(); - process.CancelOutputRead(); - process.CancelErrorRead(); + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.WaitForExit(); + process.CancelOutputRead(); + process.CancelErrorRead(); - error = process.ExitCode; - errorMessage = reterror.ToString(); - return retout.ToString(); - } + error = process.ExitCode; + errorMessage = reterror.ToString(); + return retout.ToString(); } } } diff --git a/src/Generator/Utils/TextGenerator.cs b/src/Generator/Utils/TextGenerator.cs index a57ae0c6..9dbbedff 100644 --- a/src/Generator/Utils/TextGenerator.cs +++ b/src/Generator/Utils/TextGenerator.cs @@ -21,9 +21,11 @@ namespace CppSharp public class TextGenerator : ITextGenerator { + public static string NewLineChar; + public const uint DefaultIndentation = 4; - public StringBuilder StringBuilder = new StringBuilder(); + public StringBuilder StringBuilder = new(); public bool IsStartOfLine { get; set; } public bool NeedsNewLine { get; set; } public uint CurrentIndentation { get; set; } @@ -45,6 +47,19 @@ namespace CppSharp return new TextGenerator(this); } + public static void SetLineEndingType(NewLineType newLineType) + { + NewLineChar = newLineType switch + { + NewLineType.Host => Environment.NewLine, + NewLineType.CR => "\r", + NewLineType.LF => "\n", + NewLineType.CRLF => "\r\n", + NewLineType.Auto => throw new ArgumentException("Don't use Auto here.", nameof(newLineType)), + _ => throw new ArgumentOutOfRangeException(nameof(newLineType)) + }; + } + public void Write(string msg, params object[] args) { if (string.IsNullOrEmpty(msg)) @@ -54,11 +69,9 @@ namespace CppSharp msg = string.Format(msg, args); if (IsStartOfLine && !string.IsNullOrWhiteSpace(msg)) - StringBuilder.Append(new string(' ', - (int)(CurrentIndentation * DefaultIndentation))); + StringBuilder.Append(new string(' ', (int)(CurrentIndentation * DefaultIndentation))); - if (msg.Length > 0) - IsStartOfLine = msg.EndsWith(Environment.NewLine); + IsStartOfLine = msg.Length > 0 && msg.EndsWith(NewLineChar); StringBuilder.Append(msg); } @@ -90,7 +103,7 @@ namespace CppSharp public void NewLine() { - StringBuilder.AppendLine(string.Empty); + StringBuilder.Append(NewLineChar); IsStartOfLine = true; } diff --git a/src/Generator/Utils/Utils.cs b/src/Generator/Utils/Utils.cs index 3a6a1c9b..44e777cf 100644 --- a/src/Generator/Utils/Utils.cs +++ b/src/Generator/Utils/Utils.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; +using CppSharp.Utils; namespace CppSharp { @@ -83,7 +84,7 @@ namespace CppSharp { while (stringBuilder.Length > 0 && stringBuilder[0] == '_') stringBuilder.Remove(0, 1); - while (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] == '_') + while (stringBuilder.Length > 0 && stringBuilder[^1] == '_') stringBuilder.Remove(stringBuilder.Length - 1, 1); } } @@ -130,4 +131,59 @@ namespace CppSharp return uri1.MakeRelativeUri(uri2).ToString(); } } + + public static class GeneratorHelpers + { + public static NewLineType? GetNewLineTypeFromGitConfig(string outputDir) + { + try + { + if (!bool.TryParse( + ProcessHelper.RunFrom(outputDir, + "git", "rev-parse --is-inside-work-tree", + out _, out _ + ), + out bool isInsideWorkTree)) + { + return null; + } + + if (!isInsideWorkTree) + return null; + + // Check git config core.eol setting + var eolConfig = ProcessHelper.RunFrom(outputDir, + "git", "config --get core.eol", + out _, out _) + .Trim().ToLowerInvariant(); + + switch (eolConfig) + { + case "lf": + return NewLineType.LF; + case "crlf": + return NewLineType.CRLF; + } + + // Otherwise check git config core.autocrlf setting + var autoCrLf = ProcessHelper.RunFrom(outputDir, + "git", "config --get core.autocrlf", + out _, out _) + .Trim().ToLowerInvariant(); + + return autoCrLf switch + { + "input" => NewLineType.LF, + "true" => NewLineType.CRLF, + "false" => NewLineType.Host, + + _ => null + }; + } + catch (Exception) + { + return null; + } + } + } }