---
title: "C# Source Generators: How to get build information?"
description: "Source generators are a powerful feature introduced to C#, allowing developers to generate additional code during the compilation process automatically. They can help reduce boilerplate, improve performance, and simplify your codebase. \n\nThis blog post will introduce source generators, discuss how they work, and walk through an example of a source generator for generating build information."
updated: 2023-03-27
tags: [".NET", "Generator", "C#"]
canonical: "https://steven-giesel.com/blogPost/cec8df6e-b271-4b4c-8ff6-e9f3aa5e26a1/c-source-generators-how-to-get-build-information"
---

# C# Source Generators: How to get build information?

Source generators are a powerful feature introduced to C#, allowing developers to generate additional code during the compilation process automatically. They can help reduce boilerplate, improve performance, and simplify your codebase. 

This blog post will introduce source generators, discuss how they work, and walk through an example of a source generator for generating build information.

## What do we want to do here?
Simply having something like this:
```csharp
var buildAt = BuildInformation.BuildAt; // In UTC when the Build was generated
var configuration = BuildInformation.Configuration; // Release or Debug 
var platform = BuildInformation.Platform; // Something like AnyCPU or arm64
```

## What are Source Generators?
Source generators are a compile-time feature in C# that enables you to generate additional C# source code to be included in the compilation process. They are essentially custom Roslyn analyzers that can produce new code files, which are then compiled together with the rest of your project. Source generators can help you automate code generation for repetitive tasks, enforce coding standards, and even optimize your code at compile time.

## How to Use Source Generators?
First, we have to create a new class library project that targets `netstandard2.0`. It has to target `netstandard2.0` and not something else. The reason is [here](https://github.com/dotnet/roslyn/issues/45162#issuecomment-645052356):
> because the compiler must run on both the old .NET Framework and the new .NET all generators must target .NET Standard 2.0

Afterward add the following packages:
```xml
<ItemGroup>
  <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
    <PrivateAssets>all</PrivateAssets>
    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
  </PackageReference>
  <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" />
</ItemGroup>
```

With these two packages, we can generate the generator. `PrivateAssets` means that the package is more or less a developer dependency and will not be shipped with the linked assembly. And that makes sense. The generator will create a new C# file that will be part of the linked assembly and not the generator itself, so why ship the generator? 

Now we can implement the generator like this:
```csharp
[Generator]
public sealed class IncrementalBuildInformationGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
```

We need the `GeneratorAttribute` and implement the `IIncrementalGenerator` interface. Now we can implement the generator that creates the source code that will enable us to get the build information:

```csharp
using Microsoft.CodeAnalysis;

[Generator]
public sealed class IncrementalBuildInformationGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var compilerOptions = context.CompilationProvider.Select((s, _)  => s.Options);

        context.RegisterSourceOutput(compilerOptions, static (productionContext, options) =>
        {
            var buildInformation = new BuildInformationInfo
            {
                BuildAt = DateTime.UtcNow.ToString("O"),
                Platform = options.Platform.ToString(),
                WarningLevel = options.WarningLevel,
                Configuration = options.OptimizationLevel.ToString(),
            };

            productionContext.AddSource("LinkDotNet.BuildInformation.g", GenerateBuildInformationClass(buildInformation));
        });
    }

    private static string GenerateBuildInformationClass(BuildInformationInfo buildInformation)
    {
        return $@"
using System;
using System.Globalization;
public static class BuildInformation
{{
    /// <summary>
    /// Returns the build date (UTC).
    /// </summary>
    /// <remarks>Value is: {buildInformation.BuildAt}</remarks>
    public static readonly DateTime BuildAt = DateTime.ParseExact(""{buildInformation.BuildAt}"", ""O"", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
    /// <summary>
    /// Returns the platform.
    /// </summary>
    /// <remarks>Value is: {buildInformation.Platform}</remarks>
    public const string Platform = ""{buildInformation.Platform}"";
    /// <summary>
    /// Returns the warning level.
    /// </summary>
    /// <remarks>Value is: {buildInformation.WarningLevel}</remarks>
    public const int WarningLevel = {buildInformation.WarningLevel};
    /// <summary>
    /// Returns the configuration.
    /// </summary>
    /// <remarks>Value is: {buildInformation.Configuration}</remarks>
    public const string Configuration = ""{buildInformation.Configuration}"";
}}
";
    }

    private sealed class BuildInformationInfo
    {
        public string BuildAt { get; set; } = string.Empty;
        public string Platform { get; set; } = string.Empty;
        public int WarningLevel { get; set; }
        public string Configuration { get; set; } = string.Empty;
    }
}
```

The provided code is an example of an incremental source generator that generates a static `BuildInformation` class containing build-related information, such as build date, platform, warning level, and configuration. Let's break it down step by step:
1. The `IncrementalBuildInformationGenerator` class is marked with the `[Generator]` attribute and implements the `IIncrementalGenerator` interface. This informs the compiler that this class should be treated as an incremental source generator.
2. The `Initialize` method is responsible for setting up the generator's dependencies and registering the output. In this example, the generator depends on the compiler options. The `context.CompilationProvider` is used to obtain the compilation, and the `Select` method is used to extract the `Options` property from it.
3. The `context.RegisterSourceOutput` method is called to register the generator's output based on its dependencies (the `compilerOptions`). It accepts a delegate with two parameters: the `productionContext`, which allows you to add generated source code to the compilation, and the `options`, which contains the compiler options.
4. The `productionContext.AddSource` method is called, passing a unique name for the generated file ("LinkDotNet.BuildInformation.g") and the generated source code. This adds the generated `BuildInformation` class to the compilation.

## Usage
Now let's create a new console project to use the generator. To include the generator, you can do this:
```xml
<ItemGroup>
  <ProjectReference Include="..\LinkDotNet.BuildInformation\LinkDotNet.BuildInformation.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
</ItemGroup>
```

As we can see, the generator is considered an analyzer and we don't reference the generator in our output - as we described a bit above! Depending on your IDE, you can directly use our class or hit the "Build" button:
```csharp
Console.WriteLine($"Build at: {BuildInformation.BuildAt}");
Console.WriteLine($"Platform: {BuildInformation.Platform}");
Console.WriteLine($"Warning level: {BuildInformation.WarningLevel}");
Console.WriteLine($"Configuration: {BuildInformation.Configuration}");
```

Which can lead to the following output:
```csharp
Build at: 2023-03-23T12:34:56.7890123Z
Platform: AnyCPU
Warning level: 4
Configuration: Debug
```

Pretty easy. Of course, this was an easy entrance to the generators, but it can get complicated very quickly, especially if you have to handle the whole compilation object.

## Conclusion
Source generators in C# provide an innovative way to automate code generation and improve your codebase. They can be used to simplify repetitive tasks, enforce coding standards, and optimize your code at compile time. The IncrementalBuildInformationGenerator example demonstrates how to create a source generator that generates build information, making it easy to track and access important details about your project's build configuration.

## Resources
 * If you want to use the `BuildInformation` in your project, you can use the NuGet: https://www.nuget.org/packages/LinkDotNet.BuildInformation
 * Source code to this blog post: [here](https://github.com/linkdotnet/BlogExamples/tree/main/BuildInformation)
 * All my sample code is hosted in this repository: [here](https://github.com/linkdotnet/BlogExamples)