131 lines
4.7 KiB
C#
131 lines
4.7 KiB
C#
#pragma warning disable RS1035 // Do not use APIs banned for analyzers - This is an MSBuild task, not an analyzer
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using Microsoft.Build.Framework;
|
|
using Microsoft.Build.Utilities;
|
|
|
|
namespace Svrnty.CQRS.Grpc.Generators;
|
|
|
|
/// <summary>
|
|
/// MSBuild task that extracts the auto-generated proto file content from the source generator
|
|
/// output and writes it to disk so Grpc.Tools can process it
|
|
/// </summary>
|
|
public class WriteProtoFileTask : Task
|
|
{
|
|
/// <summary>
|
|
/// The project directory where we should look for generated files
|
|
/// </summary>
|
|
[Required]
|
|
public string ProjectDirectory { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// The intermediate output path (typically obj/Debug/net10.0)
|
|
/// </summary>
|
|
[Required]
|
|
public string IntermediateOutputPath { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// The output directory where the proto file should be written (typically Protos/)
|
|
/// </summary>
|
|
[Required]
|
|
public string OutputDirectory { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// The name of the proto file to generate (typically cqrs_services.proto)
|
|
/// </summary>
|
|
[Required]
|
|
public string ProtoFileName { get; set; } = string.Empty;
|
|
|
|
public override bool Execute()
|
|
{
|
|
try
|
|
{
|
|
Log.LogMessage(MessageImportance.High,
|
|
"Svrnty.CQRS.Grpc: Extracting auto-generated proto file...");
|
|
|
|
// Look for the generated C# file containing the proto content
|
|
// Source generators output to obj/Generated, not IntermediateOutputPath/Generated
|
|
var generatedFilePath = Path.Combine(
|
|
ProjectDirectory,
|
|
"obj",
|
|
"Generated",
|
|
"Svrnty.CQRS.Grpc.Generators",
|
|
"Svrnty.CQRS.Grpc.Generators.ProtoFileSourceGenerator",
|
|
"GeneratedProtoFile.g.cs"
|
|
);
|
|
|
|
if (!File.Exists(generatedFilePath))
|
|
{
|
|
Log.LogWarning(
|
|
$"Generated proto file not found at {generatedFilePath}. " +
|
|
"The proto file may not have been generated yet. This is normal on first build.");
|
|
return true; // Don't fail the build, just skip
|
|
}
|
|
|
|
// Read the generated C# file
|
|
var csContent = File.ReadAllText(generatedFilePath);
|
|
|
|
// Extract the proto content using a more robust approach
|
|
// Looking for: public const string Content = @"...";
|
|
var startMarker = "public const string Content = @\"";
|
|
var startIndex = csContent.IndexOf(startMarker);
|
|
|
|
if (startIndex < 0)
|
|
{
|
|
Log.LogError($"Could not find Content property in {generatedFilePath}");
|
|
return false;
|
|
}
|
|
|
|
startIndex += startMarker.Length;
|
|
|
|
// Find the closing "; - We need the LAST occurrence because the content contains escaped quotes
|
|
// The pattern is: Content = @"...content...";
|
|
// where content has "" for literal quotes
|
|
var endMarker = "\";";
|
|
|
|
// Find where the next field starts or class ends to limit our search
|
|
var nextFieldOrEnd = csContent.IndexOf("\n }", startIndex); // End of class
|
|
if (nextFieldOrEnd < 0)
|
|
{
|
|
nextFieldOrEnd = csContent.Length;
|
|
}
|
|
|
|
var endIndex = csContent.LastIndexOf(endMarker, nextFieldOrEnd, nextFieldOrEnd - startIndex);
|
|
|
|
if (endIndex < 0 || endIndex < startIndex)
|
|
{
|
|
Log.LogError($"Could not find end of Content property in {generatedFilePath}");
|
|
return false;
|
|
}
|
|
|
|
// Extract and unescape doubled quotes
|
|
var protoContent = csContent.Substring(startIndex, endIndex - startIndex);
|
|
protoContent = protoContent.Replace("\"\"", "\"");
|
|
|
|
Log.LogMessage(MessageImportance.High,
|
|
$"Extracted proto content length: {protoContent.Length} characters");
|
|
|
|
// Ensure output directory exists
|
|
var fullOutputPath = Path.Combine(ProjectDirectory, OutputDirectory);
|
|
Directory.CreateDirectory(fullOutputPath);
|
|
|
|
// Write the proto file
|
|
var protoFilePath = Path.Combine(fullOutputPath, ProtoFileName);
|
|
File.WriteAllText(protoFilePath, protoContent);
|
|
|
|
Log.LogMessage(MessageImportance.High,
|
|
$"Svrnty.CQRS.Grpc: Successfully generated proto file at {protoFilePath}");
|
|
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.LogErrorFromException(ex, true);
|
|
return false;
|
|
}
|
|
}
|
|
}
|