Source Generators are an excellent way to reduce boilerplate and repetitive code that we have to write. This post will look at the project properties that we need to set on both the source generator project and consumer project.
A source generator is a .Net class.
The empty project is targeting Net5.0. I have also enabled nullables.
<PropertyGroup> <TargetFramework>net5.0</TargetFramework> <Nullable>enable</Nullable> <WarningsAsErrors>true</WarningsAsErrors> </PropertyGroup>
Next, we will add the CodeAnalysis libraries from Nuget
<ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.8.0" /> </ItemGroup>
In the consumer project, we need to add a reference to the source generator. This can be done as either a project reference or a NuGet package.
In Debug mode, we reference the project directly so that its easier for development.
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ProjectReference Include="..\\SourceGenerator\\SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> </ItemGroup>
- OutputItemType : Source generators as packaged and deployed the same way as Roslyn Analysers so we need to tell the compiler that it is not a normal project reference.
- ReferenceOutputAssembly : Tells the compiler not to include the source generator project in the published output i.e the bin folder.
In Release mode, we reference the nuget package so that we can test the package.
<ItemGroup Condition="'$(Configuration)'=='Release'"> <PackageReference Include="SourceGenerator" Version="x.y.z" /> </ItemGroup>
By default, the generated code is not written to disk. However, when writing a source generator, looking at the generated code can be helpful.
<PropertyGroup> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> <CompilerGeneratedFilesOutputPath>GeneratedFiles</CompilerGeneratedFilesOutputPath> </PropertyGroup>
- EmitCompilerGeneratedFiles : This flag tells the compiler to write the generated files to disk
- CompilerGeneratedFilesOutputPath : By default, the generated code is written to the obj/generated folder. This property allows us to override the folder. In this case, I am writing them to a GeneratedFiles folder
We will need to exclude the generated folder or the compiler will try to add the same file twice - one from disk and one from code generator
<ItemGroup> <Compile Remove="$(CompilerGeneratedFilesOutputPath)/*/**/*.cs" /> </ItemGroup>
Now, we have our projects setup so that we can start creating source generators and consuming the generated code
If you want to look at the code, you can find it here