Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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