From 7f8eecc3833e0e40ceb938f47a47453d04c19e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Fri, 28 May 2021 08:58:49 +0200 Subject: [PATCH 1/5] Add the actual command that generated the .cs file in the comments Your colleagues and your future self will be happy to know exactly all the parameters that were used during generation when the C# source code must be regenerated for whatever reason. Before: ```csharp // This code was generated by XmlSchemaClassGenerator version 1.0.0.0. ``` After: ```csharp // This code was generated by XmlSchemaClassGenerator version 1.0.0.0 using the following command: // xscgen --namespace http://microsoft.com/schemas/VisualStudio/TeamTest/2010=vstst --collectionSettersMode=Public --collectionType=System.Array --nullable --pcl --verbose vstst.xsd ``` --- .../ExtensionsTests.cs | 17 ++++++++++++++ XmlSchemaClassGenerator.Tests/XmlTests.cs | 23 ++++++++++--------- XmlSchemaClassGenerator/Extensions.cs | 22 ++++++++++++++++++ XmlSchemaClassGenerator/Generator.cs | 11 ++++++--- 4 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 XmlSchemaClassGenerator.Tests/ExtensionsTests.cs diff --git a/XmlSchemaClassGenerator.Tests/ExtensionsTests.cs b/XmlSchemaClassGenerator.Tests/ExtensionsTests.cs new file mode 100644 index 00000000..64c11d1e --- /dev/null +++ b/XmlSchemaClassGenerator.Tests/ExtensionsTests.cs @@ -0,0 +1,17 @@ +using Xunit; + +namespace XmlSchemaClassGenerator.Tests +{ + public class ExtensionsTests + { + [Theory] + [InlineData(null, null)] + [InlineData("", "")] + [InlineData("MyText", "MyText")] + [InlineData("My Text", "\"My Text\"")] + public void QuoteEmptyOrNull(string input, string expected) + { + Assert.Equal(expected, input.QuoteIfNeeded()); + } + } +} \ No newline at end of file diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs index 486fddbe..fc2c6d2f 100644 --- a/XmlSchemaClassGenerator.Tests/XmlTests.cs +++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs @@ -1049,7 +1049,7 @@ public void ComplexTypeWithAttributeGroupExtension() var csharp = Assert.Single(contents); CompareOutput( - @"//------------------------------------------------------------------------------ + $@"//------------------------------------------------------------------------------ // // This code was generated by a tool. // @@ -1058,9 +1058,10 @@ public void ComplexTypeWithAttributeGroupExtension() // //------------------------------------------------------------------------------ -// This code was generated by Tests. +// This code was generated by Tests using the following command: +// {Extensions.GetFormattedCommandLineArgs()} namespace Test -{ +{{ [System.CodeDom.Compiler.GeneratedCodeAttribute(""Tests"", """")] @@ -1068,36 +1069,36 @@ namespace Test [System.Xml.Serialization.XmlTypeAttribute(""group-name"", Namespace="""")] [System.ComponentModel.DesignerCategoryAttribute(""code"")] public partial class Group_Name - { + {{ /// /// Ruft den Text ab oder legt diesen fest. /// Gets or sets the text value. /// [System.Xml.Serialization.XmlTextAttribute()] - public string Value { get; set; } + public string Value {{ get; set; }} [System.Xml.Serialization.XmlAttributeAttribute(""justify"")] - public SimpleType Justify { get; set; } + public SimpleType Justify {{ get; set; }} /// /// Ruft einen Wert ab, der angibt, ob die Justify-Eigenschaft spezifiziert ist, oder legt diesen fest. /// Gets or sets a value indicating whether the Justify property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool JustifySpecified { get; set; } - } + public bool JustifySpecified {{ get; set; }} + }} [System.CodeDom.Compiler.GeneratedCodeAttribute(""Tests"", """")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(""simpleType"", Namespace="""")] public enum SimpleType - { + {{ [System.Xml.Serialization.XmlEnumAttribute(""foo"")] Foo, - } -} + }} +}} ", csharp); } diff --git a/XmlSchemaClassGenerator/Extensions.cs b/XmlSchemaClassGenerator/Extensions.cs index be10fcfc..b8844365 100644 --- a/XmlSchemaClassGenerator/Extensions.cs +++ b/XmlSchemaClassGenerator/Extensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -41,5 +42,26 @@ public static IEnumerable DistinctBy(this IEnumerable x.First()); } + + public static string GetFormattedCommandLineArgs() + { + var args = Environment.GetCommandLineArgs(); + return string.Join(" ", args.Take(1).Select(Path.GetFileNameWithoutExtension).Concat(args.Skip(1)).Select(QuoteIfNeeded)); + } + + public static string QuoteIfNeeded(this string text) + { + if (string.IsNullOrEmpty(text)) + { + return text; + } + + if (text.Contains(" ")) + { + return "\"" + text + "\""; + } + + return text; + } } } diff --git a/XmlSchemaClassGenerator/Generator.cs b/XmlSchemaClassGenerator/Generator.cs index 8bb4690e..461f452d 100644 --- a/XmlSchemaClassGenerator/Generator.cs +++ b/XmlSchemaClassGenerator/Generator.cs @@ -329,9 +329,14 @@ public void Generate(XmlSchemaSet set) { if (Version != null) { - var comment = $"This code was generated by {Version.Title}" - + (CreateGeneratedCodeAttributeVersion ? $" version {Version.Version}." : "."); - ns.Comments.Add(new CodeCommentStatement(comment)); + var comment = new StringBuilder($"This code was generated by {Version.Title}"); + if (CreateGeneratedCodeAttributeVersion) + { + comment.Append($" version {Version.Version}"); + } + comment.Append(" using the following command:"); + ns.Comments.Add(new CodeCommentStatement(comment.ToString())); + ns.Comments.Add(new CodeCommentStatement(Extensions.GetFormattedCommandLineArgs())); } writer.Write(ns); From fe3b230d0b0590f5eac83994c325170e4c389a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Fri, 28 May 2021 18:33:25 +0200 Subject: [PATCH 2/5] Add a flag (ca|commandArgs) to disable the command line args comment By default, a comment with the exact command line arguments that were used to generate the source code is generated, only when run from `xcsgen` or `XmlSchemaClassGenerator.Console`. --- XmlSchemaClassGenerator.Console/Program.cs | 5 +++- XmlSchemaClassGenerator.Tests/XmlTests.cs | 23 +++++++++---------- .../CommandLineArgumentsProvider.cs | 18 +++++++++++++++ XmlSchemaClassGenerator/Extensions.cs | 6 ----- XmlSchemaClassGenerator/Generator.cs | 22 ++++++++++++++++-- .../GeneratorConfiguration.cs | 12 ++++++++++ 6 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs diff --git a/XmlSchemaClassGenerator.Console/Program.cs b/XmlSchemaClassGenerator.Console/Program.cs index 416f2ace..15374ebf 100644 --- a/XmlSchemaClassGenerator.Console/Program.cs +++ b/XmlSchemaClassGenerator.Console/Program.cs @@ -54,6 +54,7 @@ static void Main(string[] args) var uniqueTypeNamesAcrossNamespaces = false; var createGeneratedCodeAttributeVersion = true; var netCoreSpecificCode = false; + var generateCommandLineArgs = true; var options = new OptionSet { { "h|help", "show this message and exit", v => showHelp = v != null }, @@ -126,6 +127,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l { "un|uniqueTypeNames", "generate type names that are unique across namespaces (default is false)", v => uniqueTypeNamesAcrossNamespaces = v != null }, { "gc|generatedCodeAttribute", "add version information to GeneratedCodeAttribute (default is true)", v => createGeneratedCodeAttributeVersion = v != null }, { "nc|netCore", "generate .NET Core specific code that might not work with .NET Framework (default is false)", v => netCoreSpecificCode = v != null }, + { "ca|commandArgs", "generate a comment with the exact command line arguments that were used to generate the source code (default is true)", v => generateCommandLineArgs = v != null }, }; var globsAndUris = options.Parse(args); @@ -200,7 +202,8 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l CompactTypeNames = compactTypeNames, UniqueTypeNamesAcrossNamespaces = uniqueTypeNamesAcrossNamespaces, CreateGeneratedCodeAttributeVersion = createGeneratedCodeAttributeVersion, - NetCoreSpecificCode = netCoreSpecificCode + NetCoreSpecificCode = netCoreSpecificCode, + GenerateCommandLineArgumentsComment = generateCommandLineArgs, }; generator.CommentLanguages.AddRange(commentLanguages); diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs index fc2c6d2f..e1a0b59f 100644 --- a/XmlSchemaClassGenerator.Tests/XmlTests.cs +++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs @@ -1049,7 +1049,7 @@ public void ComplexTypeWithAttributeGroupExtension() var csharp = Assert.Single(contents); CompareOutput( - $@"//------------------------------------------------------------------------------ + @"//------------------------------------------------------------------------------ // // This code was generated by a tool. // @@ -1058,10 +1058,9 @@ public void ComplexTypeWithAttributeGroupExtension() // //------------------------------------------------------------------------------ -// This code was generated by Tests using the following command: -// {Extensions.GetFormattedCommandLineArgs()} +// This code was generated by Tests namespace Test -{{ +{ [System.CodeDom.Compiler.GeneratedCodeAttribute(""Tests"", """")] @@ -1069,36 +1068,36 @@ namespace Test [System.Xml.Serialization.XmlTypeAttribute(""group-name"", Namespace="""")] [System.ComponentModel.DesignerCategoryAttribute(""code"")] public partial class Group_Name - {{ + { /// /// Ruft den Text ab oder legt diesen fest. /// Gets or sets the text value. /// [System.Xml.Serialization.XmlTextAttribute()] - public string Value {{ get; set; }} + public string Value { get; set; } [System.Xml.Serialization.XmlAttributeAttribute(""justify"")] - public SimpleType Justify {{ get; set; }} + public SimpleType Justify { get; set; } /// /// Ruft einen Wert ab, der angibt, ob die Justify-Eigenschaft spezifiziert ist, oder legt diesen fest. /// Gets or sets a value indicating whether the Justify property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool JustifySpecified {{ get; set; }} - }} + public bool JustifySpecified { get; set; } + } [System.CodeDom.Compiler.GeneratedCodeAttribute(""Tests"", """")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(""simpleType"", Namespace="""")] public enum SimpleType - {{ + { [System.Xml.Serialization.XmlEnumAttribute(""foo"")] Foo, - }} -}} + } +} ", csharp); } diff --git a/XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs b/XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs new file mode 100644 index 00000000..b3e02910 --- /dev/null +++ b/XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs @@ -0,0 +1,18 @@ +using System; +using System.IO; +using System.Linq; + +namespace XmlSchemaClassGenerator +{ + public class CommandLineArgumentsProvider + { + public virtual string CommandLineArguments + { + get + { + var args = Environment.GetCommandLineArgs(); + return string.Join(" ", args.Take(1).Select(Path.GetFileNameWithoutExtension).Concat(args.Skip(1)).Select(Extensions.QuoteIfNeeded)); + } + } + } +} \ No newline at end of file diff --git a/XmlSchemaClassGenerator/Extensions.cs b/XmlSchemaClassGenerator/Extensions.cs index b8844365..b97d30bc 100644 --- a/XmlSchemaClassGenerator/Extensions.cs +++ b/XmlSchemaClassGenerator/Extensions.cs @@ -43,12 +43,6 @@ public static IEnumerable DistinctBy(this IEnumerable x.First()); } - public static string GetFormattedCommandLineArgs() - { - var args = Environment.GetCommandLineArgs(); - return string.Join(" ", args.Take(1).Select(Path.GetFileNameWithoutExtension).Concat(args.Skip(1)).Select(QuoteIfNeeded)); - } - public static string QuoteIfNeeded(this string text) { if (string.IsNullOrEmpty(text)) diff --git a/XmlSchemaClassGenerator/Generator.cs b/XmlSchemaClassGenerator/Generator.cs index 461f452d..c2dc59f6 100644 --- a/XmlSchemaClassGenerator/Generator.cs +++ b/XmlSchemaClassGenerator/Generator.cs @@ -288,6 +288,18 @@ public bool NetCoreSpecificCode set { _configuration.NetCoreSpecificCode = value; } } + public bool GenerateCommandLineArgumentsComment + { + get { return _configuration.GenerateCommandLineArgumentsComment; } + set { _configuration.GenerateCommandLineArgumentsComment = value; } + } + + public CommandLineArgumentsProvider CommandLineArgumentsProvider + { + get { return _configuration.CommandLineArgumentsProvider; } + set { _configuration.CommandLineArgumentsProvider = value; } + } + static Generator() { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); @@ -334,9 +346,15 @@ public void Generate(XmlSchemaSet set) { comment.Append($" version {Version.Version}"); } - comment.Append(" using the following command:"); + if (GenerateCommandLineArgumentsComment) + { + comment.Append(" using the following command:"); + } ns.Comments.Add(new CodeCommentStatement(comment.ToString())); - ns.Comments.Add(new CodeCommentStatement(Extensions.GetFormattedCommandLineArgs())); + if (GenerateCommandLineArgumentsComment) + { + ns.Comments.Add(new CodeCommentStatement(CommandLineArgumentsProvider?.CommandLineArguments ?? "N/A")); + } } writer.Write(ns); diff --git a/XmlSchemaClassGenerator/GeneratorConfiguration.cs b/XmlSchemaClassGenerator/GeneratorConfiguration.cs index 80e79d7b..c29b10bb 100644 --- a/XmlSchemaClassGenerator/GeneratorConfiguration.cs +++ b/XmlSchemaClassGenerator/GeneratorConfiguration.cs @@ -38,6 +38,7 @@ public GeneratorConfiguration() NamingProvider = new NamingProvider(NamingScheme); Version = VersionProvider.CreateFromAssembly(); EnableUpaCheck = true; + CommandLineArgumentsProvider = new CommandLineArgumentsProvider(); } /// @@ -304,5 +305,16 @@ public void WriteLog(string message) /// /// public bool NetCoreSpecificCode { get; set; } + + /// + /// Adds a comment with the exact command line arguments that were used to generate the + /// source code using the . Default is false. + /// + public bool GenerateCommandLineArgumentsComment { get; set; } + + /// + /// A provider to obtain the command line arguments of the tool. + /// + public CommandLineArgumentsProvider CommandLineArgumentsProvider { get; set; } } } From 9fbbe8e98cb035e06f8a45c623c236dc9336c3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Fri, 28 May 2021 18:49:04 +0200 Subject: [PATCH 3/5] Update the README with the new --commandArgs option --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 56b209d1..74e37a21 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,9 @@ Options: (default is true) --nc, --netCore generate .NET Core specific code that might not work with .NET Framework (default is false) + --ca, --commandArgs generate a comment with the exact command line + arguments that were used to generate the source + code (default is true) ``` For use from code use the [library NuGet package](https://www.nuget.org/packages/XmlSchemaClassGenerator-beta/): @@ -362,4 +365,4 @@ Contrbutions are welcome. Here are some guidelines: - If it's not a trivial fix, please submit an issue first - Try and blend new code with the existing code's style -- Add unit tests +- Add unit tests \ No newline at end of file From 98579514441cd7398d20699431d3917a2e0c33a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sun, 30 May 2021 12:07:47 +0200 Subject: [PATCH 4/5] Rewrite CommandLineArgumentsProvider to be usable without subclassing --- .../CommandLineArgumentsProvider.cs | 18 ++++++++++++------ .../GeneratorConfiguration.cs | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs b/XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs index b3e02910..144e14c2 100644 --- a/XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs +++ b/XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -6,13 +7,18 @@ namespace XmlSchemaClassGenerator { public class CommandLineArgumentsProvider { - public virtual string CommandLineArguments + public CommandLineArgumentsProvider(string commandLineArguments) { - get - { - var args = Environment.GetCommandLineArgs(); - return string.Join(" ", args.Take(1).Select(Path.GetFileNameWithoutExtension).Concat(args.Skip(1)).Select(Extensions.QuoteIfNeeded)); - } + CommandLineArguments = commandLineArguments ?? throw new ArgumentNullException(nameof(commandLineArguments)); + } + + public string CommandLineArguments { get; } + + public static CommandLineArgumentsProvider CreateFromEnvironment() + { + var args = Environment.GetCommandLineArgs(); + var commandLineArguments = string.Join(" ", args.Take(1).Select(Path.GetFileNameWithoutExtension).Concat(args.Skip(1)).Select(Extensions.QuoteIfNeeded)); + return new CommandLineArgumentsProvider(commandLineArguments); } } } \ No newline at end of file diff --git a/XmlSchemaClassGenerator/GeneratorConfiguration.cs b/XmlSchemaClassGenerator/GeneratorConfiguration.cs index c29b10bb..fa71cfd0 100644 --- a/XmlSchemaClassGenerator/GeneratorConfiguration.cs +++ b/XmlSchemaClassGenerator/GeneratorConfiguration.cs @@ -38,7 +38,7 @@ public GeneratorConfiguration() NamingProvider = new NamingProvider(NamingScheme); Version = VersionProvider.CreateFromAssembly(); EnableUpaCheck = true; - CommandLineArgumentsProvider = new CommandLineArgumentsProvider(); + CommandLineArgumentsProvider = CommandLineArgumentsProvider.CreateFromEnvironment(); } /// From 62681c044aa9824fcceef8e698bcae7da1c1b095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sun, 30 May 2021 23:42:04 +0200 Subject: [PATCH 5/5] Add test that covers including command line arguments in the comments --- XmlSchemaClassGenerator.Tests/XmlTests.cs | 63 ++++++++++++++++++- .../CommandLineArgumentsProvider.cs | 2 +- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs index e1a0b59f..1b66de13 100644 --- a/XmlSchemaClassGenerator.Tests/XmlTests.cs +++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs @@ -56,7 +56,9 @@ private static IEnumerable ConvertXml(string name, IEnumerable x CodeTypeReferenceOptions = generatorPrototype.CodeTypeReferenceOptions, DoNotForceIsNullable = generatorPrototype.DoNotForceIsNullable, CreateGeneratedCodeAttributeVersion = generatorPrototype.CreateGeneratedCodeAttributeVersion, - NetCoreSpecificCode = generatorPrototype.NetCoreSpecificCode + NetCoreSpecificCode = generatorPrototype.NetCoreSpecificCode, + GenerateCommandLineArgumentsComment = generatorPrototype.GenerateCommandLineArgumentsComment, + CommandLineArgumentsProvider = generatorPrototype.CommandLineArgumentsProvider, }; gen.CommentLanguages.Clear(); @@ -2388,5 +2390,64 @@ void UnknownAttributeHandler(object sender, XmlAttributeEventArgs e) AssertEx.Equal(deserializedObject, deserializedXml); } } + + [Theory] + [InlineData("fake command line arguments", "fake command line arguments")] + [InlineData(null, "N/A")] + public void IncludeCommandLineArguments(string commandLineArguments, string expectedCommandLineArguments) + { + const string xsd = @" + + + + + +"; + + var generator = new Generator + { + GenerateInterfaces = false, + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => "Test" + }, + GenerateCommandLineArgumentsComment = true, + CommandLineArgumentsProvider = new CommandLineArgumentsProvider(commandLineArguments) + }; + + var contents = ConvertXml(nameof(IncludeCommandLineArguments), xsd, generator); + + var csharp = Assert.Single(contents); + + CompareOutput( + $@"//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// This code was generated by Tests version 1.0.0.1 using the following command: +// {expectedCommandLineArguments} +namespace Test +{{ + + + [System.CodeDom.Compiler.GeneratedCodeAttribute(""Tests"", ""1.0.0.1"")] + [System.SerializableAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(""elem"", Namespace=""http://local.none"")] + [System.ComponentModel.DesignerCategoryAttribute(""code"")] + [System.Xml.Serialization.XmlRootAttribute(""document"", Namespace=""http://local.none"")] + public partial class Elem + {{ + + [System.Xml.Serialization.XmlAttributeAttribute(""Text"")] + public string Text {{ get; set; }} + }} +}} +", csharp); + } } } diff --git a/XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs b/XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs index 144e14c2..e71d0c4f 100644 --- a/XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs +++ b/XmlSchemaClassGenerator/CommandLineArgumentsProvider.cs @@ -9,7 +9,7 @@ public class CommandLineArgumentsProvider { public CommandLineArgumentsProvider(string commandLineArguments) { - CommandLineArguments = commandLineArguments ?? throw new ArgumentNullException(nameof(commandLineArguments)); + CommandLineArguments = commandLineArguments; } public string CommandLineArguments { get; }