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 and consumer projects.
Source Generator Project Properties
A source generator is a .Net class.
Create a new solution and a library project
The empty project is targeting Net5.0. I have also enabled nullables.
<PropertyGroup> <TargetFramework>net5.0</TargetFramework> <Nullable>enable</Nullable> <WarningsAsErrors>true</WarningsAsErrors> </PropertyGroup>
Add the required packages
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>
Consumer Project Properties
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.
Reference the project in Debug mode
In Debug mode, we reference the project directly so that it’s 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 standard project reference.
- ReferenceOutputAssembly: Tells the compiler not to include the source generator project in the published output, i.e. the bin folder.
Reference the NuGet package in Release mode
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>
Writing the generated code to disk
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
Excluding the generated 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 the code generator
<ItemGroup> <Compile Remove="$(CompilerGeneratedFilesOutputPath)/*/**/*.cs" /> </ItemGroup>
Now, we have our projects set up to start creating source generators and consuming the generated code.
If you want to look at the code, you can find it here