-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Use Source Code Generator for PSVersionInfo class #15603
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0f34632
94b1adc
3f49bb7
18dd324
0150339
c08c0f1
c6b34bb
cf51855
f09bd4d
7a89b2e
44a795e
77ad890
6998df1
1e26d0c
9cba5cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
|
daxian-dbw marked this conversation as resolved.
|
||
| } | ||
|
|
||
| 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); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,29 @@ | ||
| <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> | ||
| <NoWarn>$(NoWarn);CS1570;CS1734;CA1416</NoWarn> | ||
| <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> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generate the file, so VSCode and Visual Studio can always resolve the generated members.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You didn't see my reply to your comment? It's here: #15603 (comment), and quoted below:
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, too many comments already.... |
||
|
|
||
| <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" /> | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The path
gen\SourceGeneratedis duplicated in here andSystem.Management.Automation.csproj. I tried creating a property holding the path in thePropertyGroupright above, and use it inRemoveDirandCompilerGeneratedFilesOutputPath. However, it works inRemoveDir, but not inCompilerGeneratedFilesOutputPath, and I cannot figure out why. I'd love if someone can make it work.