Skip to content

Commit 955bcbc

Browse files
author
Robert Holt
committed
Fix v3 compat, add union profile name, begin version work
1 parent 10d0244 commit 955bcbc

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
@@ -42,23 +42,23 @@ $commonParams = @(
4242

4343
foreach ($p in $commonParams)
4444
{
45-
$null = $script:CommonParameters.Add($p)
45+
[void]$script:CommonParameters.Add($p)
4646
}
4747

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

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

6464
<#
@@ -215,7 +215,7 @@ function New-AllPlatformReferenceProfile
215215
Remove-Item -Path $Path -Force
216216
}
217217

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

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

@@ -526,12 +526,12 @@ function Get-PowerShellCompatibilityData
526526
param(
527527
[Parameter()]
528528
[switch]
529-
$IncludeUserModules
529+
$IncludeAllModules
530530
)
531531

532-
$modules = Get-AvailableModules -IncludeUserModules:$IncludeUserModules
532+
$modules = Get-AvailableModules -IncludeAll:$IncludeAllModules
533533
$typeAccelerators = Get-TypeAccelerators
534-
$asms = Get-AvailableTypes -IncludeUserModules:$IncludeUserModules
534+
$asms = Get-AvailableTypes -All:$IncludeAllModules
535535

536536
$coreModule = Get-CoreModuleData
537537

@@ -553,15 +553,15 @@ Gets all assemblies publicly available in
553553
the current PowerShell session.
554554
Skips assemblies from user modules by default.
555555
556-
.PARAMETER IncludeUserModules
557-
Include loaded assemblies located on the module installation path.
556+
.PARAMETER All
557+
Include
558558
#>
559559
function Get-AvailableTypes
560560
{
561561
param(
562562
[Parameter()]
563563
[switch]
564-
$IncludeUserModules
564+
$All
565565
)
566566

567567
$asms = New-Object 'System.Collections.Generic.List[System.Reflection.Assembly]'
@@ -573,13 +573,10 @@ function Get-AvailableTypes
573573
continue
574574
}
575575

576-
if (-not $IncludeUserModules -and
577-
(Test-HasAnyPrefix $asm.Location -Prefix $script:UserModulePath,$script:SharedModulePath -IgnoreCase:$script:IsWindows))
576+
if ($All -or (Test-HasAnyPrefix $asm.Location -Prefix $script:PSHomeModulePath,$script:WinPSHomeModulePath -IgnoreCase:$script:IsWindows))
578577
{
579-
continue
578+
$asms.Add($asm)
580579
}
581-
582-
$asms.Add($asm)
583580
}
584581

585582
return $asms
@@ -653,17 +650,17 @@ function Get-AvailableModules
653650
param(
654651
[Parameter()]
655652
[switch]
656-
$IncludeUserModules
653+
$IncludeAll
657654
)
658655

659-
if ($IncludeUserModules)
656+
if ($IncludeAll)
660657
{
661658
$modsToLoad = Get-Module -ListAvailable
662659
}
663660
else
664661
{
665662
$modsToLoad = Get-Module -ListAvailable `
666-
| Where-Object { -not (Test-HasAnyPrefix $_.Path $script:UserModulePath,$script:SharedModulePath -IgnoreCase:$script:IsWindows) }
663+
| Where-Object { Test-HasAnyPrefix $_.Path $script:PSHomeModulePath,$script:WinPSHomeModulePath -IgnoreCase:$script:IsWindows }
667664
}
668665

669666
# Filter out this module
@@ -1128,6 +1125,11 @@ function Test-HasAnyPrefix
11281125

11291126
foreach ($p in $Prefix)
11301127
{
1128+
if ($null -eq $p)
1129+
{
1130+
continue
1131+
}
1132+
11311133
if ($String.StartsWith($p, $strcmp))
11321134
{
11331135
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)