diff --git a/src/NerdBank.GitVersioning.Tests/AssemblyInfoTest.cs b/src/NerdBank.GitVersioning.Tests/AssemblyInfoTest.cs new file mode 100644 index 00000000..1f0a0758 --- /dev/null +++ b/src/NerdBank.GitVersioning.Tests/AssemblyInfoTest.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Nerdbank.GitVersioning.Tasks; +using Xunit; + +namespace NerdBank.GitVersioning.Tests +{ + public class AssemblyInfoTest + { + [Fact] + public void FSharpGenerator() + { + var info = new AssemblyVersionInfo(); + info.AssemblyCompany = "company"; + info.AssemblyFileVersion = "1.3.1.0"; + info.AssemblyVersion = "1.3.0.0"; + info.CodeLanguage = "f#"; + + var built = info.BuildCode(); + + var expected = @"//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace AssemblyInfo +[] +[] +[] +do() +type internal ThisAssembly() = + static member internal AssemblyVersion = ""1.3.0.0"" + static member internal AssemblyFileVersion = ""1.3.1.0"" + static member internal AssemblyCompany = ""company"" + static member internal RootNamespace = """" +do() +"; + Assert.Equal(expected, built); + } + } +} diff --git a/src/NerdBank.GitVersioning.Tests/NerdBank.GitVersioning.Tests.csproj b/src/NerdBank.GitVersioning.Tests/NerdBank.GitVersioning.Tests.csproj index be755f37..36be883c 100644 --- a/src/NerdBank.GitVersioning.Tests/NerdBank.GitVersioning.Tests.csproj +++ b/src/NerdBank.GitVersioning.Tests/NerdBank.GitVersioning.Tests.csproj @@ -20,6 +20,7 @@ + diff --git a/src/Nerdbank.GitVersioning.Tasks/AssemblyVersionInfo.cs b/src/Nerdbank.GitVersioning.Tasks/AssemblyVersionInfo.cs index 86587db2..b928cd10 100644 --- a/src/Nerdbank.GitVersioning.Tasks/AssemblyVersionInfo.cs +++ b/src/Nerdbank.GitVersioning.Tasks/AssemblyVersionInfo.cs @@ -4,7 +4,6 @@ using System.CodeDom; using System.CodeDom.Compiler; using System.Collections.Generic; - using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -22,7 +21,7 @@ public class AssemblyVersionInfo : Task }; private CodeCompileUnit generatedFile; -#else +#endif private const string FileHeaderComment = @"------------------------------------------------------------------------------ This code was generated by a tool. @@ -36,8 +35,6 @@ the code is regenerated. private CodeGenerator generator; -#endif - [Required] public string CodeLanguage { get; set; } @@ -101,7 +98,17 @@ public override bool Execute() } else { - this.Log.LogError("CodeDomProvider not available for language: {0}. No version info will be embedded into assembly.", this.CodeLanguage); + // attempt to use local codegen + string fileContent = this.BuildCode(); + if (fileContent != null) + { + Directory.CreateDirectory(Path.GetDirectoryName(this.OutputFile)); + Utilities.FileOperationWithRetry(() => File.WriteAllText(this.OutputFile, fileContent)); + } + else + { + this.Log.LogError("CodeDomProvider not available for language: {0}. No version info will be embedded into assembly.", this.CodeLanguage); + } } return !this.Log.HasLoggedErrors; @@ -205,24 +212,36 @@ private static CodeAttributeDeclaration DeclareAttribute(Type attributeType, par #else public override bool Execute() + { + string fileContent = this.BuildCode(); + if (fileContent != null) + { + Directory.CreateDirectory(Path.GetDirectoryName(this.OutputFile)); + Utilities.FileOperationWithRetry(() => File.WriteAllText(this.OutputFile, fileContent)); + } + + return !this.Log.HasLoggedErrors; + } + +#endif + + public string BuildCode() { this.generator = this.CreateGenerator(); if (this.generator != null) { this.generator.AddComment(FileHeaderComment); this.generator.AddBlankLine(); - this.CreateAssemblyAttributes(); - this.CreateThisAssemblyClass(); - - Directory.CreateDirectory(Path.GetDirectoryName(this.OutputFile)); - string fileContent = this.generator.GetCode(); - Utilities.FileOperationWithRetry(() => File.WriteAllText(this.OutputFile, fileContent)); + this.generator.EmitNamespaceIfRequired(this.RootNamespace ?? "AssemblyInfo"); + this.GenerateAssemblyAttributes(); + this.GenerateThisAssemblyClass(); + return this.generator.GetCode(); } - return !this.Log.HasLoggedErrors; + return null; } - private void CreateAssemblyAttributes() + private void GenerateAssemblyAttributes() { this.generator.DeclareAttribute(typeof(AssemblyVersionAttribute), this.AssemblyVersion); this.generator.DeclareAttribute(typeof(AssemblyFileVersionAttribute), this.AssemblyFileVersion); @@ -240,7 +259,7 @@ private void CreateAssemblyAttributes() } } - private void CreateThisAssemblyClass() + private void GenerateThisAssemblyClass() { this.generator.StartThisAssemblyClass(); @@ -280,6 +299,7 @@ private void CreateThisAssemblyClass() this.generator.EndThisAssemblyClass(); } + private CodeGenerator CreateGenerator() { switch (this.CodeLanguage.ToLowerInvariant()) @@ -290,8 +310,9 @@ private CodeGenerator CreateGenerator() case "visualbasic": case "vb": return new VisualBasicCodeGenerator(); + case "f#": + return new FSharpCodeGenerator(); default: - this.Log.LogError("Code provider not available for language: {0}. No version info will be embedded into assembly.", this.CodeLanguage); return null; } } @@ -315,6 +336,12 @@ internal CodeGenerator() internal abstract void EndThisAssemblyClass(); + /// + /// Gives languages that *require* a namespace a chance to emit such. + /// + /// The RootNamespace of the project. + internal virtual void EmitNamespaceIfRequired(string ns) { } + internal string GetCode() => this.codeBuilder.ToString(); internal void AddBlankLine() @@ -334,6 +361,39 @@ protected void AddCodeComment(string comment, string token) } } + private class FSharpCodeGenerator : CodeGenerator + { + internal override void AddComment(string comment) + { + this.AddCodeComment(comment, "//"); + } + + internal override void AddThisAssemblyMember(string name, string value) + { + this.codeBuilder.AppendLine($" static member internal {name} = \"{value}\""); + } + + internal override void EmitNamespaceIfRequired(string ns) + { + this.codeBuilder.AppendLine($"namespace {ns}"); + } + + internal override void DeclareAttribute(Type type, string arg) + { + this.codeBuilder.AppendLine($"[]"); + } + + internal override void EndThisAssemblyClass() + { + this.codeBuilder.AppendLine("do()"); + } + + internal override void StartThisAssemblyClass() + { + this.codeBuilder.AppendLine("do()\r\ntype internal ThisAssembly() ="); + } + } + private class CSharpCodeGenerator : CodeGenerator { internal override void AddComment(string comment) @@ -389,7 +449,6 @@ internal override void EndThisAssemblyClass() this.codeBuilder.AppendLine("End Class"); } } -#endif private static string ToHex(byte[] data) {