Skip to content

Commit 5d48b1d

Browse files
author
Robert Holt
committed
Fix v3 compat, add union profile name, begin version work
1 parent 45dff90 commit 5d48b1d

File tree

6 files changed

+157
-29
lines changed

6 files changed

+157
-29
lines changed

CrossCompatibility/CrossCompatibility.psm1

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,23 @@ $commonParams = @(
4646

4747
foreach ($p in $commonParams)
4848
{
49-
$null = $script:CommonParameters.Add($p)
49+
[void]$script:CommonParameters.Add($p)
5050
}
5151

5252
# The file name for the any-platform reference generated from the union of all other platforms
53-
[string]$script:AnyPlatformReferenceProfileFilePath = [System.IO.Path]::Combine($script:CompatibilityProfileDir, 'anyplatform_union.json')
53+
[string]$script:AnyPlatformUnionPlatformName = [Microsoft.PowerShell.CrossCompatibility.Utility.PlatformNaming]::AnyPlatformUnionName
54+
[string]$script:AnyPlatformReferenceProfileFilePath = [System.IO.Path]::Combine($script:CompatibilityProfileDir, "$script:AnyPlatformUnionPlatformName.json")
5455

55-
# User and Shared module path locations
56+
# Module path locations
5657
if ($PSVersionTable.PSVersion.Major -ge 6)
5758
{
58-
[string]$script:UserModulePath = [System.Management.Automation.ModuleIntrinsics].GetMethod('GetPersonalModulePath', [System.Reflection.BindingFlags]'static,nonpublic').Invoke($null, @())
59-
[string]$script:SharedModulePath = [System.Management.Automation.ModuleIntrinsics].GetMethod('GetSharedModulePath', [System.Reflection.BindingFlags]'static,nonpublic').Invoke($null, @())
59+
[string]$script:PSHomeModulePath = [System.Management.Automation.ModuleIntrinsics].GetMethod('GetPSHomeModulePath', [System.Reflection.BindingFlags]'static,nonpublic').Invoke($null, @())
60+
[string]$script:WinPSHomeModulePath = "$env:windir\System32\WindowsPowerShell\v1.0\Modules"
6061
}
6162
else
6263
{
63-
$documentsFolder = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Personal)
64-
[string]$script:UserModulePath = "$documentsFolder\PowerShell\Modules"
65-
[string]$script:SharedModulePath = "$env:ProgramFiles\WindowsPowerShell\Modules"
64+
[string]$script:PSHomeModulePath = "$PSHOME\Modules"
65+
[string]$script:WinPSHomeModulePath = $script:PSHomeModulePath
6666
}
6767

6868
<#
@@ -219,7 +219,7 @@ function New-AllPlatformReferenceProfile
219219
Remove-Item -Path $Path -Force
220220
}
221221

222-
$tmpPath = Join-Path ([System.IO.Path]::GetTempPath()) 'anyprofile.json'
222+
$tmpPath = Join-Path ([System.IO.Path]::GetTempPath()) "$script:AnyPlatformReferenceProfileFilePath.json"
223223

224224
Join-CompatibilityProfile -InputFile $ProfileDir -Union | ConvertTo-CompatibilityJson -NoWhitespace | Out-File -Encoding UTF8 -FilePath $tmpPath
225225

@@ -530,12 +530,12 @@ function Get-PowerShellCompatibilityData
530530
param(
531531
[Parameter()]
532532
[switch]
533-
$IncludeUserModules
533+
$IncludeAllModules
534534
)
535535

536-
$modules = Get-AvailableModules -IncludeUserModules:$IncludeUserModules
536+
$modules = Get-AvailableModules -IncludeAll:$IncludeAllModules
537537
$typeAccelerators = Get-TypeAccelerators
538-
$asms = Get-AvailableTypes -IncludeUserModules:$IncludeUserModules
538+
$asms = Get-AvailableTypes -All:$IncludeAllModules
539539

540540
$coreModule = Get-CoreModuleData
541541

@@ -557,15 +557,15 @@ Gets all assemblies publicly available in
557557
the current PowerShell session.
558558
Skips assemblies from user modules by default.
559559
560-
.PARAMETER IncludeUserModules
561-
Include loaded assemblies located on the module installation path.
560+
.PARAMETER All
561+
Include
562562
#>
563563
function Get-AvailableTypes
564564
{
565565
param(
566566
[Parameter()]
567567
[switch]
568-
$IncludeUserModules
568+
$All
569569
)
570570

571571
$asms = New-Object 'System.Collections.Generic.List[System.Reflection.Assembly]'
@@ -577,13 +577,10 @@ function Get-AvailableTypes
577577
continue
578578
}
579579

580-
if (-not $IncludeUserModules -and
581-
(Test-HasAnyPrefix $asm.Location -Prefix $script:UserModulePath,$script:SharedModulePath -IgnoreCase:$script:IsWindows))
580+
if ($All -or (Test-HasAnyPrefix $asm.Location -Prefix $script:PSHomeModulePath,$script:WinPSHomeModulePath -IgnoreCase:$script:IsWindows))
582581
{
583-
continue
582+
$asms.Add($asm)
584583
}
585-
586-
$asms.Add($asm)
587584
}
588585

589586
return $asms
@@ -657,17 +654,17 @@ function Get-AvailableModules
657654
param(
658655
[Parameter()]
659656
[switch]
660-
$IncludeUserModules
657+
$IncludeAll
661658
)
662659

663-
if ($IncludeUserModules)
660+
if ($IncludeAll)
664661
{
665662
$modsToLoad = Get-Module -ListAvailable
666663
}
667664
else
668665
{
669666
$modsToLoad = Get-Module -ListAvailable `
670-
| Where-Object { -not (Test-HasAnyPrefix $_.Path $script:UserModulePath,$script:SharedModulePath -IgnoreCase:$script:IsWindows) }
667+
| Where-Object { Test-HasAnyPrefix $_.Path $script:PSHomeModulePath,$script:WinPSHomeModulePath -IgnoreCase:$script:IsWindows }
671668
}
672669

673670
# Filter out this module
@@ -1132,6 +1129,11 @@ function Test-HasAnyPrefix
11321129

11331130
foreach ($p in $Prefix)
11341131
{
1132+
if ($null -eq $p)
1133+
{
1134+
continue
1135+
}
1136+
11351137
if ($String.StartsWith($p, $strcmp))
11361138
{
11371139
return $true

CrossCompatibility/CrossCompatibility/Utility/PlatformNaming.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace Microsoft.PowerShell.CrossCompatibility.Utility
55
{
66
public static class PlatformNaming
77
{
8+
public static string AnyPlatformUnionName => "anyplatform_union";
9+
810
public static string PlatformNameJoiner
911
{
1012
get
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Text;
3+
4+
namespace Microsoft.PowerShell.CrossCompatibility.Utility
5+
{
6+
public class PowerShellVersion
7+
{
8+
public static PowerShellVersion FromSemVer(dynamic semver)
9+
{
10+
if (semver.GetType().FullName != "System.Management.Automation.SemanticVersion")
11+
{
12+
throw new ArgumentException($"{nameof(semver)} must be of type 'System.Management.Automation.SemanticVersion'");
13+
}
14+
15+
return new PowerShellVersion(semver.Major, semver.Minor, semver.Patch, semver.PreReleaseLabel);
16+
}
17+
18+
public static PowerShellVersion Parse(string versionStr)
19+
{
20+
}
21+
22+
public PowerShellVersion(Version version)
23+
: this(version.Major, version.Minor, version.Build, version.Revision)
24+
{
25+
}
26+
27+
public PowerShellVersion(int major, int minor, int build, int revision)
28+
{
29+
Major = major;
30+
Minor = minor;
31+
Build = build;
32+
Revision = revision;
33+
}
34+
35+
public PowerShellVersion(int major, int minor, int patch, string preReleaseLabel)
36+
{
37+
Major = major;
38+
Minor = minor;
39+
Build = patch;
40+
PreReleaseLabel = preReleaseLabel;
41+
}
42+
43+
public int Major { get; }
44+
45+
public int Minor { get; }
46+
47+
public int Build { get; }
48+
49+
public int Patch => Build;
50+
51+
public int? Revision { get; }
52+
53+
public string PreReleaseLabel { get; }
54+
55+
public bool IsSemVer => Revision == null;
56+
57+
public override string ToString()
58+
{
59+
if (!IsSemVer)
60+
{
61+
return $"{Major}.{Minor}.{Build}.{Revision}";
62+
}
63+
64+
var sb = new StringBuilder()
65+
.Append(Major).Append('.')
66+
.Append(Minor).Append('.')
67+
.Append(Patch);
68+
69+
if (!string.IsNullOrEmpty(PreReleaseLabel))
70+
{
71+
sb.Append('-').Append(PreReleaseLabel);
72+
}
73+
74+
return sb.ToString();
75+
}
76+
}
77+
}

Engine/Generic/ConfigurableRulePropertyAttribute.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic
1111
[AttributeUsage(AttributeTargets.Property)]
1212
public class ConfigurableRulePropertyAttribute : Attribute
1313
{
14+
// TODO: Remove this parameter or make it optional.
15+
// Properties already have a way to specify default values in C#.
16+
// Having this prevents using null (which may be legitimate)
17+
// or values from other assemblies, and overrides the constructor,
18+
// which just makes life harder.
19+
1420
/// <summary>
1521
/// Default value of the property that the attribute decorates.
1622
/// </summary>
@@ -22,6 +28,7 @@ public class ConfigurableRulePropertyAttribute : Attribute
2228
/// <param name="defaultValue"></param>
2329
public ConfigurableRulePropertyAttribute(object defaultValue)
2430
{
31+
// TODO: null is a legitimate value and should be allowed.
2532
if (defaultValue == null)
2633
{
2734
throw new ArgumentNullException(nameof(defaultValue), Strings.ConfigurableScriptRuleNRE);

Rules/UseCompatibleCmdlets2.cs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ public class UseCompatibleCmdlets2 : ConfigurableRule
1616
{
1717
private const string PROFILE_DIR_NAME = "compatibility_profiles";
1818

19-
private const string ANY_PLATFORM_UNION_PROFILE_NAME = "anyplatform_union";
20-
2119
private static readonly Regex s_falseProfileExtensionPattern = new Regex(
2220
"\\d+_(x64|x86|arm32|arm64)",
2321
RegexOptions.IgnoreCase | RegexOptions.Compiled);
@@ -32,12 +30,26 @@ public UseCompatibleCmdlets2()
3230
_profileLoader = CompatibilityProfileLoader.StaticInstance;
3331
}
3432

35-
[ConfigurableRuleProperty(defaultValue: ANY_PLATFORM_UNION_PROFILE_NAME)]
33+
/// <summary>
34+
/// The path to the "anyprofile union" profile.
35+
/// If given as a filename, this is presumed to be under the profiles directory.
36+
/// If no file extension is given on the filename, ".json" is assumed.
37+
/// </summary>
38+
/// <remarks>
39+
/// The default value for this should be PlatformNaming.AnyPlatformUnionName,
40+
/// but a non-constant expression cannot be used as an attribute parameter.
41+
/// This is done in the ConfigureRule() override below.
42+
/// The ConfigurableRuleProperty should just remove this parameter and use the
43+
/// property default value.
44+
/// </remarks>
45+
[ConfigurableRuleProperty(defaultValue: "")]
3646
public string AnyProfilePath { get; set; }
3747

3848
[ConfigurableRuleProperty(defaultValue: new string[] {})]
3949
public string[] TargetProfilePaths { get; set; }
4050

51+
public DiagnosticSeverity DiagnosticSeverity => DiagnosticSeverity.Warning;
52+
4153
public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
4254
{
4355
CmdletCompatibilityVisitor compatibilityVisitor = CreateVisitorFromConfiguration(fileName);
@@ -74,8 +86,17 @@ public override SourceType GetSourceType()
7486
{
7587
return SourceType.Builtin;
7688
}
89+
90+
public override void ConfigureRule(IDictionary<string, object> paramValueMap)
91+
{
92+
base.ConfigureRule(paramValueMap);
7793

78-
public DiagnosticSeverity DiagnosticSeverity => DiagnosticSeverity.Warning;
94+
// Default anyprofile path is the one specified in the CrossCompatibility library
95+
if (string.IsNullOrEmpty(AnyProfilePath))
96+
{
97+
AnyProfilePath = PlatformNaming.AnyPlatformUnionName;
98+
}
99+
}
79100

80101
private CmdletCompatibilityVisitor CreateVisitorFromConfiguration(string analyzedFileName)
81102
{

Tests/Rules/UseCompatibleCmdlets2.Tests.ps1

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ $script:AssetDirPath = Join-Path $PSScriptRoot 'UseCompatibleCmdlets2'
44
$script:ProfileDirPath = [System.IO.Path]::Combine($PSScriptRoot, '..', '..', 'CrossCompatibility', 'profiles')
55
$script:AnyProfilePath = Join-Path $script:ProfileDirPath 'anyplatforms_union.json'
66

7+
try
8+
{
9+
$null = [semver]
10+
$script:HasSemVer = $true
11+
}
12+
catch
13+
{
14+
$script:HasSemVer = $false
15+
}
16+
717
$script:CompatibilityTestCases = @(
818
@{ Target = 'win-4_x64_10.0.18312.0_5.1.18312.1000_x64'; Script = "Remove-Alias gcm"; Commands = @("Remove-Alias"); Version = "5.1"; OS = "Windows"; ProblemCount = 1 }
919
@{ Target = 'win-4_x64_10.0.18312.0_5.1.18312.1000_x64'; Script = "Get-Uptime"; Commands = @("Get-Uptime"); Version = "5.1"; OS = "Windows"; ProblemCount = 1 }
@@ -64,10 +74,19 @@ Describe 'UseCompatibleCmdlets2' {
6474

6575
for ($i = 0; $i -lt $diagnostics.Count; $i++)
6676
{
77+
try
78+
{
79+
$psVersion = [version]$diagnostics[$i].TargetPlatform.PowerShell.Version
80+
}
81+
catch
82+
{
83+
$psVersion = [semver]$diagnostics[$i].TargetPlatform.PowerShell.Version
84+
}
85+
6786
$diagnostics[$i].Command | Should -BeExactly $Commands[$i]
6887
$diagnostics[$i].TargetPlatform.OperatingSystem.Family | Should -Be $OS
69-
$diagnostics[$i].TargetPlatform.PowerShell.Version.Major | Should -Be $Version.Major
70-
$diagnostics[$i].TargetPlatform.PowerShell.Version.Minor | Should -Be $Version.Minor
88+
$psVersion.Major | Should -Be $Version.Major
89+
$psVersion.Minor | Should -Be $Version.Minor
7190
}
7291
}
7392
}

0 commit comments

Comments
 (0)