Hi! I’m new to source generator. I want to make a generator that read the item from a enum and create a struct public struct Tag_ ItemName : IComponentData {} and create a static class with a method that return the responding ComponentType of the the struct when entering a item of the enum.
the generator code is
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CavePeople_TagSourceGenerator
{
[Generator]
public class TagSourceGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var enumDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (s, _) => s is EnumDeclarationSyntax enumDecl && enumDecl.AttributeLists.Any(),
transform: (ctx, _) => (EnumDeclarationSyntax)ctx.Node)
.Where(enumDecl => enumDecl != null && HasGenerateTagAttribute(enumDecl));
context.RegisterSourceOutput(enumDeclarations, (spc, enumDeclaration) =>
{
var sourceBuilder = new StringBuilder();
sourceBuilder.AppendLine("using Unity.Entities;");
var B_nameSpace = new SyntaxBlock("namespace CavePeople", enumDeclaration.Members.Count + 5);
GenStructs(B_nameSpace, enumDeclaration);
GenBuildingUtil(B_nameSpace, enumDeclaration);
SyntaxBlock.BuildBlockAndClear(ref sourceBuilder, B_nameSpace);
spc.AddSource("GeneratedBuildingTypeTags", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
});
}
static bool HasGenerateTagAttribute(EnumDeclarationSyntax enumDecl)
{
return enumDecl.AttributeLists
.SelectMany(al => al.Attributes)
.Any(attr => attr.Name.ToString() == "GenerateTag");
}
void GenStructs(SyntaxBlock B_nameSpace, EnumDeclarationSyntax enumDeclaration)
{
foreach (var member in enumDeclaration.Members)
{
var enumName = member.Identifier.Text;
if (enumName == "None" || enumName == "Count") continue;
B_nameSpace.AddSubBlock(new SyntaxBlock($"public struct Tag_{enumName} : IComponentData"));
}
}
void GenBuildingUtil(SyntaxBlock B_nameSpace, EnumDeclarationSyntax enumDeclaration)
{
var B_buildingUtil = new SyntaxBlock("public static class TypeTagUtil", 5, parent: B_nameSpace);
var B_getBuildingTag = new SyntaxBlock("public static ComponentType GetBuildingTag(BuildingType buildingType)", 5, parent: B_buildingUtil);
var B_switch = new SyntaxBlock("return buildingType switch", enumDeclaration.Members.Count, parent: B_getBuildingTag);
foreach (var member in enumDeclaration.Members)
{
var enumName = member.Identifier.Text;
if (enumName == "None" || enumName == "Count") continue;
B_switch.AddSubBlock(new SyntaxBlock($"BuildingType.{enumName} => typeof(Tag_{enumName}),", addBracket: false));
}
B_switch.AddSubBlock(new SyntaxBlock("_ => throw new System.Exception(\"Invalid BuildingType\")", addBracket: false));
}
}
public class SyntaxBlock
{
public string head;
public bool addBracket;
public List<SyntaxBlock> subBlock;
public SyntaxBlock(string head, int capacity = 0, bool addBracket = true, SyntaxBlock parent = null)
{
this.head = head;
if (capacity != 0)
{
subBlock = new List<SyntaxBlock>(capacity);
}
parent?.AddSubBlock(this);
this.addBracket = addBracket;
}
public void AddSubBlock(SyntaxBlock block)
{
if (subBlock == null)
{
subBlock = new List<SyntaxBlock>();
}
subBlock.Add(block);
}
public static void BuildBlockAndClear(ref StringBuilder builder, SyntaxBlock block)
{
if (block == null) return;
builder.AppendLine(block.head);
if (block.addBracket) builder.AppendLine("{");
if (block.subBlock != null)
{
for (int i = 0; i < block.subBlock.Count; i++)
{
BuildBlockAndClear(ref builder, block.subBlock[i]);
}
block.subBlock.Clear();
block.subBlock = null;
}
if (block.addBracket) builder.AppendLine("}");
}
}
}
In unity, the BuildingType enum is decorated with the attribute [GenerateTag] to trigger.
[GenerateTag]
public enum BuildingType : byte
{
None,
House, Floor, Stair, StructuralFrame, LifeSource,
WaterPool, Warehouse, Bullpen, WoodFarm, RobberyStartPoint,
ShipPoint,
ArcherTower,
Kitchen, Temple,
Count
}
[AttributeUsage(AttributeTargets.Enum, Inherited = false, AllowMultiple = false)]
sealed class GenerateTagAttribute : Attribute
{
public GenerateTagAttribute() { }
}
And the generated code looks ok
using Unity.Entities;
namespace CavePeople
{
public struct Tag_House : IComponentData
{
}
public struct Tag_Floor : IComponentData
{
}
public struct Tag_Stair : IComponentData
{
}
public struct Tag_StructuralFrame : IComponentData
{
}
public struct Tag_LifeSource : IComponentData
{
}
public struct Tag_WaterPool : IComponentData
{
}
public struct Tag_Warehouse : IComponentData
{
}
public struct Tag_Bullpen : IComponentData
{
}
public struct Tag_WoodFarm : IComponentData
{
}
public struct Tag_RobberyStartPoint : IComponentData
{
}
public struct Tag_ShipPoint : IComponentData
{
}
public struct Tag_ArcherTower : IComponentData
{
}
public struct Tag_Kitchen : IComponentData
{
}
public struct Tag_Temple : IComponentData
{
}
public static class TypeTagUtil
{
public static ComponentType GetBuildingTag(BuildingType buildingType)
{
return buildingType switch
{
BuildingType.House => typeof(Tag_House),
BuildingType.Floor => typeof(Tag_Floor),
BuildingType.Stair => typeof(Tag_Stair),
BuildingType.StructuralFrame => typeof(Tag_StructuralFrame),
BuildingType.LifeSource => typeof(Tag_LifeSource),
BuildingType.WaterPool => typeof(Tag_WaterPool),
BuildingType.Warehouse => typeof(Tag_Warehouse),
BuildingType.Bullpen => typeof(Tag_Bullpen),
BuildingType.WoodFarm => typeof(Tag_WoodFarm),
BuildingType.RobberyStartPoint => typeof(Tag_RobberyStartPoint),
BuildingType.ShipPoint => typeof(Tag_ShipPoint),
BuildingType.ArcherTower => typeof(Tag_ArcherTower),
BuildingType.Kitchen => typeof(Tag_Kitchen),
BuildingType.Temple => typeof(Tag_Temple),
_ => throw new System.Exception("Invalid BuildingType")
}
}
}
}
But when I use the TypeTagUtil.GetBuildingTag(BuildingType buildingType), it logs the error
The name 'TypeTagUtil' does not exist in the current context