Skip to content

Commit

Permalink
Replace PSVersion source generator with incremental one (PowerShell…
Browse files Browse the repository at this point in the history
  • Loading branch information
iSazonov authored Aug 20, 2024
1 parent be59e5e commit 5153f9b
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 50 deletions.
2 changes: 2 additions & 0 deletions PowerShell.sln
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "powershell-unix", "src\powe
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xUnit.tests", "test\xUnit\xUnit.tests.csproj", "{08704934-9764-48CE-86DB-BCF0A1CF7899}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSVersionInfoGenerator", "src\System.Management.Automation\SourceGenerators\PSVersionInfoGenerator\PSVersionInfoGenerator.csproj", "{B22424E8-0516-4FC3-A9CB-D84D15EF0589}"
EndProject
# Configuration mapping comment
# All global configurations must be mapped to project configurations
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,70 @@ namespace SMA
/// Source Code Generator to create partial PSVersionInfo class.
/// </summary>
[Generator]
public class PSVersionInfoGenerator : ISourceGenerator
public class PSVersionInfoGenerator : IIncrementalGenerator
{
/// <summary>
/// Generate output PSVersionInfo.g.cs file.
/// This allows to directly get ProductVersion and others without reflection.
/// </summary>
/// <param name="context">Generator execution context.</param>
public void Execute(GeneratorExecutionContext context)
{
var result = CreatePSVersionInfoPartialClass(context);

// We must use specific file name suffix (*.g.cs,*.g, *.i.cs, *.generated.cs, *.designer.cs)
// so that Roslyn analyzers skip the file.
context.AddSource("PSVersionInfo.g.cs", result);
}

/// <summary>
/// Not used.
/// </summary>
/// <param name="context">Generator initialization context.</param>
public void Initialize(GeneratorInitializationContext context)
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// No initialization required for this one.
IncrementalValueProvider<BuildOptions> buildOptionsProvider = context.AnalyzerConfigOptionsProvider
.Select(static (provider, _) =>
{
provider.GlobalOptions.TryGetValue("build_property.ProductVersion", out var productVersion);
provider.GlobalOptions.TryGetValue("build_property.PSCoreBuildVersion", out var mainVersion);
provider.GlobalOptions.TryGetValue("build_property.PowerShellVersion", out var gitDescribe);
provider.GlobalOptions.TryGetValue("build_property.ReleaseTag", out var releaseTag);

BuildOptions options = new()
{
ProductVersion = productVersion ?? string.Empty,
MainVersion = mainVersion ?? string.Empty,
GitDescribe = gitDescribe ?? string.Empty,
ReleaseTag = releaseTag ?? string.Empty
};

return options;
});

context.RegisterSourceOutput(
buildOptionsProvider,
static (context, buildOptions) =>
{
string gitCommitId = string.IsNullOrEmpty(buildOptions.ReleaseTag) ? buildOptions.GitDescribe : buildOptions.ReleaseTag;
if (gitCommitId.StartsWith("v"))
{
gitCommitId = gitCommitId.Substring(1);
}

var versions = ParsePSVersion(buildOptions.MainVersion);
string result = string.Format(
CultureInfo.InvariantCulture,
SourceTemplate,
buildOptions.ProductVersion,
gitCommitId,
versions.major,
versions.minor,
versions.patch,
versions.preReleaseLabel);

// We must use specific file name suffix (*.g.cs,*.g, *.i.cs, *.generated.cs, *.designer.cs)
// so that Roslyn analyzers skip the file.
context.AddSource("PSVersionInfo.g.cs", result);
});
}

/// <summary>
/// Generate source code for the partial PSVersionInfo class.
/// </summary>
/// <param name="context">Generator execution context.</param>
/// <returns>A string with partial PSVersionInfo class.</returns>
private static string CreatePSVersionInfoPartialClass(GeneratorExecutionContext context)
private struct BuildOptions
{
// We must put "<auto-generated" on first line so that Roslyng analyzers skip the file.
const string SourceTemplate = @"// <auto-generated>
public string ProductVersion;
public string MainVersion;
public string GitDescribe;
public string ReleaseTag;
}

// We must put "<auto-generated" on first line so that Roslyng analyzers skip the file.
private const string SourceTemplate = @"// <auto-generated>
// This file is auto-generated by PSVersionInfoGenerator.
// </auto-generated>
Expand Down Expand Up @@ -81,30 +111,6 @@ public static partial class PSVersionInfo
}}
}}";

context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.ProductVersion", out var productVersion);
context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.PSCoreBuildVersion", out var mainVersion);
context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.PowerShellVersion", out var gitDescribe);
context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.ReleaseTag", out var releaseTag);

string gitCommitId = string.IsNullOrEmpty(releaseTag) ? gitDescribe : releaseTag;
if (gitCommitId.StartsWith("v"))
{
gitCommitId = gitCommitId.Substring(1);
}

var result = ParsePSVersion(mainVersion);

return string.Format(
CultureInfo.InvariantCulture,
SourceTemplate,
productVersion,
gitCommitId,
result.major,
result.minor,
result.patch,
result.preReleaseLabel);
}

private static (int major, int minor, int patch, string preReleaseLabel) ParsePSVersion(string mainVersion)
{
// We only handle the pre-defined PSVersion format here, e.g. 7.x.x or 7.x.x-preview.x
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<LangVersion>11.0</LangVersion>
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<NoWarn>RS1035</NoWarn>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit 5153f9b

Please sign in to comment.