Location>code7788 >text

Using .NET Source Generator (SG) to generate version number information for a project

Popularity:434 ℃/2024-09-03 21:50:34

Having previously written aSource Generator generates code for automatic injection Mainly throughSyntaxProviderFind Annotation Feature Implementation

In fact, apart fromSyntaxProviderThere are several other important Providers, such as.MetadataReferencesProvider,AdditionalTextsProvider,AnalyzerConfigOptionsProvideretc.

Today.AnalyzerConfigOptionsProviderThis Provider, here, gets references to project folders and top-level namespaces through the AnalyzerConfigOptionsProvider.
With the following code we can print out the referenced project'sGlobalOptions:

var projectKeysProvider =
    .Select((options, _) =>
    {
        var keys = ;
        List<(string? Key, string? Value)> keyValues = [];
        foreach (var key in keys)
        {
            (key, out var value);
            ((key, value));
        }
        return keyValues;
    });
(projectKeysProvider, (ctx, projectKeys) =>
{
    // Generate Code
    StringBuilder stringBuilder = new();
    foreach (var (Key, Value) in projectKeys)
    {
        ($"// {Key} {Value}");
    }
});

image

It's not hard to see that the key for the project folder and the top-level namespace isbuild_property.projectdir,build_property.rootnamespace,

Once we have the project folder address we can read the corresponding*.csprojproject file. Here we read the file via IO and fetch the configuredAssemblyVersion,FileVersion,Versionitem, and then you can generate the version information.
The project file itself is an Xml file, so reading the configuration items can be done using theXPathorregular expression (math.)For the sake of simplicity and efficiency, here's the regular expression I'm using to get.


//Generate version number
var inc = ((pvd, _) =>
{
    //Access to project catalogs
    var flag = ("build_property.projectdir", out var root);
    if (!flag)
        return new VersionInfo(null, null);

    //Acquisition of namespace
    ("build_property.rootnamespace", out var @namespace);

    //var file = (root, $"*.csproj");
    //findcsprojfile
    var files = (root, "*.csproj", );

    return new VersionInfo(@namespace, == 0 ? null : files[0]);
});

//generating
(inc, (ctx, info) =>
{
    if ( == null || == null)
        return;

    string version = DefaultVersion;
    string fileVersion = DefaultVersion;
    string assemblyVersion = DefaultVersion;

    // 获取不含扩展名的file名
    //var @namespace = (info.Item2);

    // 读取file
    var text = ();

    // go down (in history)Import的file,for example : <Import Project="..\" />
    // Matching with regular expressionsProject:
    var importMatchs = (text, "<Import Project=\"(.*?)\"");

    foreach (Match importMatch in importMatchs)
    {
        var importFile = ((), [1].Value);
        if ((importFile))
        {
            text += (importFile);
        }
    }

    var match = (text, "<Version>(.*?)</Version>");
    var fileVersionMatch = (text, "<FileVersion>(.*?)</FileVersion>");
    var assemblyVersionMatch = (text, "<AssemblyVersion>(.*?)</AssemblyVersion>");
    if ()
    {
        version = [1].Value;
    }
    if ()
    {
        fileVersion = [1].Value;
    }
    if ()
    {
        assemblyVersion = [1].Value;
    }

    string source = $@"// <auto-generated/>
namespace {}.Generated
{{
/// <summary>
/// The version class
/// </summary>
public static class Version
{{
/// <summary>
/// The current version
/// </summary>
public static Current => (""{version}"");

/// <summary>
/// The file version
/// </summary>
public static FileVersion => (""{fileVersion}"");

/// <summary>
/// The assembly version
/// </summary>
public static AssemblyVersion => (""{assemblyVersion}"");

}}
}}
";
    // output code
    ("", (source, Encoding.UTF8));
});

Then the desired content was generated:

// <auto-generated/>
namespace 
{
    /// <summary>
    /// The version class
    /// </summary>
    public static class Version
    {
        /// <summary>
        /// The current version
        /// </summary>
        public static  Current => ("2.0.0");

        /// <summary>
        /// The file version
        /// </summary>
        public static  FileVersion => ("2.0.0");

        /// <summary>
        /// The assembly version
        /// </summary>
        public static  AssemblyVersion => ("2.0.0");

    }
}

lastly{namespace}..*You can get the version information

With the above code we can theoretically read the contents of all the files in the project folder, except of course theAnalyzerConfigOptionsProviderIn addition, we can also use theAdditionalTextsProviderRead the contents of the attached file, since the current article does not cover it I'll talk about it sometime!

The above code completes the entire source generation step, and finally you can use the nuget package I released to experience.

dotnet add package 

I posted the source code to GitHub, welcome to star!/vipwan/

/vipwan//blob/master//