Files
PowerShell/src/System.Management.Automation/SourceGenerators/PSVersionInfoGenerator/PSVersionInfoGenerator.cs
T

130 lines
5.6 KiB
C#
Raw Normal View History

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Globalization;
using Microsoft.CodeAnalysis;
namespace SMA
{
/// <summary>
/// Source Code Generator to create partial PSVersionInfo class.
/// </summary>
[Generator]
public class PSVersionInfoGenerator : ISourceGenerator
{
/// <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)
{
// No initialization required for this one.
}
/// <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)
{
// We must put "<auto-generated" on first line so that Roslyng analyzers skip the file.
const string SourceTemplate = @"// <auto-generated>
// This file is auto-generated by PSVersionInfoGenerator.
// </auto-generated>
namespace System.Management.Automation
{{
public static partial class PSVersionInfo
{{
// Defined in 'PowerShell.Common.props' as 'ProductVersion'
// Example:
// - when built from a commit: ProductVersion = '7.3.0-preview.8 Commits: 29 SHA: 52c6b...'
// - when built from a preview release tag: ProductVersion = '7.3.0-preview.8 SHA: f1ec9...'
// - when built from a stable release tag: ProductVersion = '7.3.0 SHA: f1ec9...'
internal const string ProductVersion = ""{0}"";
// The git commit id that the build is based off.
// Defined in 'PowerShell.Common.props' as 'PowerShellVersion' or 'ReleaseTag',
// depending on whether the '-ReleaseTag' is specified when building.
// Example:
// - when built from a commit: GitCommitId = '7.3.0-preview.8-29-g52c6b...'
// - when built from a preview release tag: GitCommitId = '7.3.0-preview.8'
// - when built from a stable release tag: GitCommitId = '7.3.0'
internal const string GitCommitId = ""{1}"";
// The PowerShell version components.
// The version string is defined in 'PowerShell.Common.props' as 'PSCoreBuildVersion',
// but we break it into components to save the overhead of parsing at runtime.
// Example:
// - '7.3.0-preview.8' for preview release or private build
// - '7.3.0' for stable release
private const int Version_Major = {2};
private const int Version_Minor = {3};
private const int Version_Patch = {4};
private const string Version_Label = ""{5}"";
}}
}}";
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
int dashIndex = mainVersion.IndexOf('-');
bool hasLabel = dashIndex != -1;
string preReleaseLabel = hasLabel ? mainVersion.Substring(dashIndex + 1) : string.Empty;
if (hasLabel)
{
mainVersion = mainVersion.Substring(0, dashIndex);
}
int majorEnd = mainVersion.IndexOf('.');
int minorEnd = mainVersion.LastIndexOf('.');
int major = int.Parse(mainVersion.Substring(0, majorEnd), NumberStyles.Integer, CultureInfo.InvariantCulture);
int minor = int.Parse(mainVersion.Substring(majorEnd + 1, minorEnd - majorEnd - 1), NumberStyles.Integer, CultureInfo.InvariantCulture);
int patch = int.Parse(mainVersion.Substring(minorEnd + 1), NumberStyles.Integer, CultureInfo.InvariantCulture);
return (major, minor, patch, preReleaseLabel);
}
}
}