From d328f424af7ce02959f498c5be023f177bf0efdc Mon Sep 17 00:00:00 2001 From: Stephan Probst Date: Sun, 11 Jan 2026 16:34:16 +0100 Subject: [PATCH 1/2] Fix MSBuild task to avoid assembly loading Replace Assembly.Load() with text-based parsing of generated source files. This prevents issues with frameworks like TUnit that use module initializers. - Enable EmitCompilerGeneratedFiles to write generated sources to disk - Parse AppVersion.g.cs with regex instead of loading the assembly - No external dependencies required (removed bundled DLLs) --- .../build/SimpleBranchVersioning.props | 3 + .../build/SimpleBranchVersioning.targets | 100 +++++++++++++++--- 2 files changed, 90 insertions(+), 13 deletions(-) diff --git a/src/SimpleBranchVersioning/build/SimpleBranchVersioning.props b/src/SimpleBranchVersioning/build/SimpleBranchVersioning.props index 15467f5..9ad972f 100644 --- a/src/SimpleBranchVersioning/build/SimpleBranchVersioning.props +++ b/src/SimpleBranchVersioning/build/SimpleBranchVersioning.props @@ -10,6 +10,9 @@ false false false + + + true diff --git a/src/SimpleBranchVersioning/build/SimpleBranchVersioning.targets b/src/SimpleBranchVersioning/build/SimpleBranchVersioning.targets index 107a478..a1e38eb 100644 --- a/src/SimpleBranchVersioning/build/SimpleBranchVersioning.targets +++ b/src/SimpleBranchVersioning/build/SimpleBranchVersioning.targets @@ -5,27 +5,99 @@ - + + - + + (); + +try { - var method = writerType.GetMethod("WriteJson", BindingFlags.Public | BindingFlags.Static); - method.Invoke(null, new object[] { OutputPath }); + // Find AppVersion.g.cs in generated files directory + // Path pattern: generated/SimpleBranchVersioning/SimpleBranchVersioning.AppVersionGenerator/AppVersion.g.cs + string sourceFile = null; + if (Directory.Exists(GeneratedFilesDir)) + { + // Search for any .g.cs file containing "public const string Version" + foreach (string file in Directory.GetFiles(GeneratedFilesDir, "*.g.cs", SearchOption.AllDirectories)) + { + string content = File.ReadAllText(file); + if (content.Contains("public const string Version") && content.Contains("public const string PackageVersion")) + { + sourceFile = file; + break; + } + } + } + + if (sourceFile == null) + { + Log.LogWarning("SimpleBranchVersioning: Generated AppVersion source file not found in " + GeneratedFilesDir + ". Ensure EmitCompilerGeneratedFiles is enabled."); + return true; + } + + Log.LogMessage(MessageImportance.Low, "SimpleBranchVersioning: Reading version info from " + sourceFile); + string sourceContent = File.ReadAllText(sourceFile); + + // Extract const string values using regex + // Pattern matches: public const string FieldName = "value"; + var pattern = new Regex(@"public const string (\w+) = ""([^""]*)"";"); + foreach (Match match in pattern.Matches(sourceContent)) + { + string fieldName = match.Groups[1].Value; + string fieldValue = match.Groups[2].Value; + values[fieldName] = fieldValue; + } + + if (values.Count == 0) + { + Log.LogWarning("SimpleBranchVersioning: No version constants found in generated source file."); + return true; + } + + // Build JSON manually (avoid System.Text.Json dependency for .NET Framework MSBuild compatibility) + var sb = new StringBuilder(); + sb.AppendLine("{"); + + string GetValue(string key) { string v; return values.TryGetValue(key, out v) ? v : ""; } + + sb.AppendLine(" \"Version\": \"" + EscapeJson(GetValue("Version")) + "\","); + sb.AppendLine(" \"Branch\": \"" + EscapeJson(GetValue("Branch")) + "\","); + sb.AppendLine(" \"CommitId\": \"" + EscapeJson(GetValue("CommitId")) + "\","); + sb.AppendLine(" \"PackageVersion\": \"" + EscapeJson(GetValue("PackageVersion")) + "\","); + sb.AppendLine(" \"AssemblyVersion\": \"" + EscapeJson(GetValue("AssemblyVersion")) + "\","); + sb.AppendLine(" \"FileVersion\": \"" + EscapeJson(GetValue("FileVersion")) + "\","); + sb.AppendLine(" \"InformationalVersion\": \"" + EscapeJson(GetValue("InformationalVersion")) + "\""); + sb.AppendLine("}"); + + // Ensure directory exists + string dir = Path.GetDirectoryName(OutputPath); + if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + + File.WriteAllText(OutputPath, sb.ToString()); Log.LogMessage(MessageImportance.Normal, "Generated version file: " + OutputPath); } -else +catch (Exception ex) +{ + Log.LogWarning("SimpleBranchVersioning: Failed to generate version file: " + ex.Message); +} + +// Local function to escape JSON strings +string EscapeJson(string s) { - Log.LogWarning("SimpleBranchVersioning: VersionFileWriter type not found. Ensure the source generator is running correctly."); + if (string.IsNullOrEmpty(s)) return s; + return s.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n").Replace("\r", "\\r").Replace("\t", "\\t"); } ]]> @@ -35,8 +107,9 @@ else $([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(OutputPath)', 'version.json')) + $(IntermediateOutputPath)generated\ - + @@ -89,7 +162,8 @@ else Condition="'$(SetPackageVersionFromBranch)' == 'true' and '$(TargetFramework)' != ''"> $([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(OutputPath)', 'version.json')) + $(IntermediateOutputPath)generated\ - + From b99703d8a22b8a37f9ccca5d76ebf1486d4f0a85 Mon Sep 17 00:00:00 2001 From: Stephan Probst Date: Sun, 11 Jan 2026 16:39:43 +0100 Subject: [PATCH 2/2] Remove unused VersionFileWriter generation No longer needed since MSBuild task reads AppVersion.g.cs directly. --- .../AppVersionGenerator.cs | 36 ------------------ .../AppVersionGeneratorTests.cs | 38 ------------------- 2 files changed, 74 deletions(-) diff --git a/src/SimpleBranchVersioning/AppVersionGenerator.cs b/src/SimpleBranchVersioning/AppVersionGenerator.cs index 5cc483b..fe12b1d 100644 --- a/src/SimpleBranchVersioning/AppVersionGenerator.cs +++ b/src/SimpleBranchVersioning/AppVersionGenerator.cs @@ -218,13 +218,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // Generate assembly version attributes string assemblyAttributesSource = GenerateAssemblyAttributes(versionInfo); ctx.AddSource("AssemblyVersionInfo.g.cs", SourceText.From(assemblyAttributesSource, Encoding.UTF8)); - - // Generate version file writer helper only when needed (GenerateVersionFile or SetPackageVersionFromBranch) - if (buildProps.GenerateVersionFile || buildProps.SetPackageVersionFromBranch) - { - string writerSource = GenerateVersionFileWriter(versionInfo, branch, commitId); - ctx.AddSource("VersionFileWriter.g.cs", SourceText.From(writerSource, Encoding.UTF8)); - } }); } @@ -265,35 +258,6 @@ private static string GenerateAssemblyAttributes(VersionInfo versionInfo) """; } - private static string GenerateVersionFileWriter(VersionInfo versionInfo, string branch, string commitId) - { - return $$""" - // - namespace SimpleBranchVersioning.Generated - { - [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - internal static class VersionFileWriter - { - public static void WriteJson(string path) - { - string json = "{\n" + - " \"Version\": \"{{EscapeJson(versionInfo.Version)}}\",\n" + - " \"Branch\": \"{{EscapeJson(branch)}}\",\n" + - " \"CommitId\": \"{{EscapeJson(commitId)}}\",\n" + - " \"PackageVersion\": \"{{EscapeJson(versionInfo.PackageVersion)}}\",\n" + - " \"AssemblyVersion\": \"{{EscapeJson(versionInfo.AssemblyVersion)}}\",\n" + - " \"FileVersion\": \"{{EscapeJson(versionInfo.FileVersion)}}\",\n" + - " \"InformationalVersion\": \"{{EscapeJson(versionInfo.InformationalVersion)}}\"\n" + - "}"; - global::System.IO.File.WriteAllText(path, json); - } - } - } - """; - } - - private static string EscapeJson(string value) => value.Replace("\\", @"\\").Replace("\"", "\\\""); - private static string GenerateSource(string? namespaceName, string className, VersionInfo versionInfo, string branch, string commitId) { string classDefinition = $$""" diff --git a/tests/SimpleBranchVersioning.Tests/AppVersionGeneratorTests.cs b/tests/SimpleBranchVersioning.Tests/AppVersionGeneratorTests.cs index de1b7f0..61f9a36 100644 --- a/tests/SimpleBranchVersioning.Tests/AppVersionGeneratorTests.cs +++ b/tests/SimpleBranchVersioning.Tests/AppVersionGeneratorTests.cs @@ -307,44 +307,6 @@ public async Task Generator_WithTopLevelStatements_UsesGlobalNamespace() #endregion - #region VersionFileWriter Generation Tests - - [Test] - public async Task Generator_WithGenerateVersionFile_GeneratesVersionFileWriter() - { - var result = GeneratorTestHelper.RunGenerator( - MinimalSource, - branchOverride: "main", - generateVersionFile: true); - - await Assert.That(result.GeneratedFileNames).Contains("VersionFileWriter.g.cs"); - } - - [Test] - public async Task Generator_WithSetPackageVersionFromBranch_GeneratesVersionFileWriter() - { - var result = GeneratorTestHelper.RunGenerator( - MinimalSource, - branchOverride: "main", - setPackageVersionFromBranch: true); - - await Assert.That(result.GeneratedFileNames).Contains("VersionFileWriter.g.cs"); - } - - [Test] - public async Task Generator_WithBothDisabled_DoesNotGenerateVersionFileWriter() - { - var result = GeneratorTestHelper.RunGenerator( - MinimalSource, - branchOverride: "main", - generateVersionFile: false, - setPackageVersionFromBranch: false); - - await Assert.That(result.GeneratedFileNames).DoesNotContain("VersionFileWriter.g.cs"); - } - - #endregion - #region Branch Name Slash Replacement Tests [Test]