mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Use source generator for PSVersionInfo to improve startup time (#15603)
This commit is contained in:
@@ -104,6 +104,9 @@
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Remove existing generated files by source generators because new source code will soon be generated when the build starts -->
|
||||
<RemoveDir Directories="gen\SourceGenerated" Condition="Exists('gen\SourceGenerated')" />
|
||||
|
||||
<!-- Output For Debugging
|
||||
<WriteLinesToFile File="targetfile1.txt"
|
||||
Lines="ReleaseTag=$(ReleaseTag);
|
||||
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
|
||||
<PropertyGroup>
|
||||
<Description>Generate code for SMA using source generator</Description>
|
||||
<AssemblyName>SMA.Generator</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- source generator project needs to target 'netstandard2.0' -->
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
|
||||
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
|
||||
<Import Project="..\..\PowerShell.Common.props" />
|
||||
<PropertyGroup>
|
||||
<Description>PowerShell's System.Management.Automation project</Description>
|
||||
@@ -6,6 +6,24 @@
|
||||
<AssemblyName>System.Management.Automation</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- we persist source generator files under 'gen' folder so that they are visible to IDEs -->
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<CompilerGeneratedFilesOutputPath>gen\SourceGenerated</CompilerGeneratedFilesOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- these properties are declared in 'PowerShell.Common.props' and used by source generator -->
|
||||
<CompilerVisibleProperty Include="ProductVersion" />
|
||||
<CompilerVisibleProperty Include="PSCoreBuildVersion" />
|
||||
<CompilerVisibleProperty Include="PowerShellVersion" />
|
||||
<CompilerVisibleProperty Include="ReleaseTag" />
|
||||
|
||||
<ProjectReference Include="SourceGenerators\PSVersionInfoGenerator\PSVersionInfoGenerator.csproj"
|
||||
OutputItemType="Analyzer"
|
||||
ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(IsWindows)' == 'true' ">
|
||||
<ProjectReference Include="..\Microsoft.PowerShell.CoreCLR.Eventing\Microsoft.PowerShell.CoreCLR.Eventing.csproj" />
|
||||
</ItemGroup>
|
||||
@@ -38,6 +56,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- exclude code of source generators from compilation and IDEs (e.g. vscode and visual studio) -->
|
||||
<Compile Remove="SourceGenerators\**\*.cs" />
|
||||
|
||||
<Compile Remove="cimSupport\cmdletization\xml\cmdlets-over-objects.objectModel.autogen.cs" />
|
||||
<Compile Remove="cimSupport\cmdletization\xml\cmdlets-over-objects.xmlSerializer.autogen.cs" />
|
||||
<Compile Remove="engine\TransactedString.cs" />
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@@ -27,7 +26,7 @@ namespace System.Management.Automation
|
||||
/// The above statement retrieves the PowerShell edition.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static class PSVersionInfo
|
||||
public static partial class PSVersionInfo
|
||||
{
|
||||
internal const string PSVersionTableName = "PSVersionTable";
|
||||
internal const string PSRemotingProtocolVersionName = "PSRemotingProtocolVersion";
|
||||
@@ -42,6 +41,18 @@ namespace System.Management.Automation
|
||||
|
||||
private static readonly PSVersionHashTable s_psVersionTable;
|
||||
|
||||
/*
|
||||
The following constants are generated by the source generator 'PSVersionInfoGenerator':
|
||||
|
||||
internal const string ProductVersion;
|
||||
internal const string GitCommitId;
|
||||
|
||||
private const int Version_Major
|
||||
private const int Version_Minor;
|
||||
private const int Version_Patch;
|
||||
private const string Version_Label;
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// A constant to track current PowerShell Version.
|
||||
/// </summary>
|
||||
@@ -78,39 +89,14 @@ namespace System.Management.Automation
|
||||
{
|
||||
s_psVersionTable = new PSVersionHashTable(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
Assembly currentAssembly = typeof(PSVersionInfo).Assembly;
|
||||
ProductVersion = currentAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
||||
|
||||
// Get 'GitCommitId' and 'PSVersion' from the 'productVersion' assembly attribute.
|
||||
//
|
||||
// The strings can be one of the following format examples:
|
||||
// when powershell is built from a commit:
|
||||
// productVersion = '6.0.0-beta.7 Commits: 29 SHA: 52c6b...' convert to GitCommitId = 'v6.0.0-beta.7-29-g52c6b...'
|
||||
// PSVersion = '6.0.0-beta.7'
|
||||
// when powershell is built from a release tag:
|
||||
// productVersion = '6.0.0-beta.7 SHA: f1ec9...' convert to GitCommitId = 'v6.0.0-beta.7'
|
||||
// PSVersion = '6.0.0-beta.7'
|
||||
// when powershell is built from a release tag for RTM:
|
||||
// productVersion = '6.0.0 SHA: f1ec9...' convert to GitCommitId = 'v6.0.0'
|
||||
// PSVersion = '6.0.0'
|
||||
string rawGitCommitId;
|
||||
string mainVersion = ProductVersion.Substring(0, ProductVersion.IndexOf(' '));
|
||||
|
||||
if (ProductVersion.Contains(" Commits: "))
|
||||
{
|
||||
rawGitCommitId = ProductVersion.Replace(" Commits: ", "-").Replace(" SHA: ", "-g");
|
||||
}
|
||||
else
|
||||
{
|
||||
rawGitCommitId = mainVersion;
|
||||
}
|
||||
|
||||
s_psSemVersion = new SemanticVersion(mainVersion);
|
||||
s_psSemVersion = Version_Label == string.Empty
|
||||
? new SemanticVersion(Version_Major, Version_Minor, Version_Patch)
|
||||
: new SemanticVersion(Version_Major, Version_Minor, Version_Patch, Version_Label, buildLabel: null);
|
||||
s_psVersion = (Version)s_psSemVersion;
|
||||
|
||||
s_psVersionTable[PSVersionName] = s_psSemVersion;
|
||||
s_psVersionTable[PSEditionName] = PSEditionValue;
|
||||
s_psVersionTable[PSGitCommitIdName] = rawGitCommitId;
|
||||
s_psVersionTable[PSGitCommitIdName] = GitCommitId;
|
||||
s_psVersionTable[PSCompatibleVersionsName] = new Version[] { s_psV1Version, s_psV2Version, s_psV3Version, s_psV4Version, s_psV5Version, s_psV51Version, s_psV6Version, s_psV61Version, s_psV62Version, s_psV7Version, s_psV71Version, s_psV72Version, s_psVersion };
|
||||
s_psVersionTable[SerializationVersionName] = new Version(InternalSerializer.DefaultVersion);
|
||||
s_psVersionTable[PSRemotingProtocolVersionName] = RemotingConstants.ProtocolVersion;
|
||||
@@ -183,16 +169,6 @@ namespace System.Management.Automation
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ProductVersion { get; }
|
||||
|
||||
internal static string GitCommitId
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)s_psVersionTable[PSGitCommitIdName];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the edition of PowerShell.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user