Skip to content

Commit b6e0736

Browse files
authored
Update compatibility profiles for PowerShell 7 (#1429)
* Update compatibility profiles for PS7 * Update tests * Fix alias collection * Update profiles * Update documentation * Make queryable profile initialization eager * Add supression IDs for compatibility rules * Fix NREs and remove Console.WriteLine * Add supression documentation * Activate tests in CI * Remove 6.1 profiles * Fix type loading * Update profiles collected with fixed module * Remove Wait-Debugger * Fix other tests * Update tests for PS 3 * Fix PS 4 tests * Fix types tests * Fix syntax * Fix the way parse errors are filtered out in tests * Correct docs * Add more specific Win10 name to docs
1 parent 2514855 commit b6e0736

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+542
-334
lines changed

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Collection/CompatibilityProfileCollector.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Linq;
7-
using System.Linq.Expressions;
87
using System.Management.Automation;
98
using System.Reflection;
10-
using Microsoft.PowerShell.Commands;
119
using Microsoft.PowerShell.CrossCompatibility.Data;
1210
using Microsoft.PowerShell.CrossCompatibility.Utility;
1311
using SMA = System.Management.Automation;
12+
using System.IO;
1413

1514
#if CoreCLR
1615
using System.Runtime.InteropServices;
@@ -78,7 +77,7 @@ public CompatibilityProfileCollector Build(SMA.PowerShell pwsh)
7877
pwsh,
7978
platformInfoCollector,
8079
_pwshDataCollectorBuilder.Build(pwsh, platformInfoCollector.PSVersion),
81-
_typeDataColletorBuilder.Build());
80+
_typeDataColletorBuilder.Build(Path.GetDirectoryName(typeof(SMA.PowerShell).Assembly.Location)));
8281
}
8382
}
8483

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Collection/PowerShellDataCollector.cs

+41-8
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,14 @@ public Tuple<string, Version, ModuleData> GetCoreModuleData()
283283
var moduleData = new ModuleData();
284284

285285
IEnumerable<CommandInfo> coreCommands = _pwsh.AddCommand(GcmInfo)
286-
.AddParameter("Module", CORE_MODULE_NAME)
287-
.InvokeAndClear<CommandInfo>();
286+
.AddParameter("Type", CommandTypes.Alias | CommandTypes.Cmdlet | CommandTypes.Function)
287+
.InvokeAndClear<CommandInfo>()
288+
.Where(commandInfo => string.IsNullOrEmpty(commandInfo.ModuleName) || CORE_MODULE_NAME.Equals(commandInfo.ModuleName, StringComparison.OrdinalIgnoreCase));
288289

289290
var cmdletData = new JsonCaseInsensitiveStringDictionary<CmdletData>();
290291
var functionData = new JsonCaseInsensitiveStringDictionary<FunctionData>();
292+
var aliases = new JsonCaseInsensitiveStringDictionary<string>();
293+
var aliasesToRequest = new List<string>();
291294
foreach (CommandInfo command in coreCommands)
292295
{
293296
switch (command)
@@ -315,6 +318,24 @@ public Tuple<string, Version, ModuleData> GetCoreModuleData()
315318
}
316319
continue;
317320

321+
case AliasInfo alias:
322+
try
323+
{
324+
// Some aliases won't resolve unless specified specifically
325+
if (alias.Definition == null)
326+
{
327+
aliasesToRequest.Add(alias.Name);
328+
continue;
329+
}
330+
331+
aliases.Add(alias.Name, alias.Definition);
332+
}
333+
catch (RuntimeException)
334+
{
335+
// Ignore aliases that have trouble loading
336+
}
337+
continue;
338+
318339
default:
319340
throw new CompatibilityAnalysisException($"Command {command.Name} in core module is of unsupported type {command.CommandType}");
320341
}
@@ -323,15 +344,31 @@ public Tuple<string, Version, ModuleData> GetCoreModuleData()
323344
moduleData.Cmdlets = cmdletData;
324345
moduleData.Functions = functionData;
325346

347+
if (aliasesToRequest != null && aliasesToRequest.Count > 0)
348+
{
349+
IEnumerable<AliasInfo> resolvedAliases = _pwsh.AddCommand(GcmInfo)
350+
.AddParameter("Name", aliasesToRequest)
351+
.InvokeAndClear<AliasInfo>();
352+
353+
foreach (AliasInfo resolvedAlias in resolvedAliases)
354+
{
355+
if (resolvedAlias?.Definition == null)
356+
{
357+
continue;
358+
}
359+
360+
aliases[resolvedAlias.Name] = resolvedAlias.Definition;
361+
}
362+
}
363+
326364
// Get default variables and core aliases out of a fresh runspace
327365
using (SMA.PowerShell freshPwsh = SMA.PowerShell.Create(RunspaceMode.NewRunspace))
328366
{
329367
Collection<PSObject> varsAndAliases = freshPwsh.AddCommand("Get-ChildItem")
330-
.AddParameter("Path", "variable:,alias:")
368+
.AddParameter("Path", "variable:")
331369
.InvokeAndClear();
332370

333371
var variables = new List<string>();
334-
var aliases = new JsonCaseInsensitiveStringDictionary<string>();
335372

336373
foreach (PSObject returnedObject in varsAndAliases)
337374
{
@@ -341,10 +378,6 @@ public Tuple<string, Version, ModuleData> GetCoreModuleData()
341378
variables.Add(variable.Name);
342379
continue;
343380

344-
case AliasInfo alias:
345-
aliases.Add(alias.Name, GetSingleAliasData(alias));
346-
continue;
347-
348381
// Skip over other objects we get back, since there's no reason to throw
349382
}
350383
}

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Collection/TypeDataCollector.cs

+48-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using SMA = System.Management.Automation;
1515
using System.Runtime.InteropServices;
1616
using System.IO;
17+
using Newtonsoft.Json.Serialization;
1718

1819
namespace Microsoft.PowerShell.CrossCompatibility.Collection
1920
{
@@ -37,9 +38,9 @@ public class Builder
3738
/// Build the configured TypeDataCollector object.
3839
/// </summary>
3940
/// <returns>The constructed TypeDataCollector object.</returns>
40-
public TypeDataCollector Build()
41+
public TypeDataCollector Build(string psHomePath)
4142
{
42-
return new TypeDataCollector(ExcludedAssemblyPathPrefixes);
43+
return new TypeDataCollector(psHomePath, ExcludedAssemblyPathPrefixes);
4344
}
4445
}
4546

@@ -53,8 +54,13 @@ public TypeDataCollector Build()
5354

5455
private readonly IReadOnlyCollection<string> _excludedAssemblyPathPrefixes;
5556

56-
private TypeDataCollector(IReadOnlyCollection<string> excludedAssemblyPathPrefixes)
57+
private readonly string _psHomePath;
58+
59+
private TypeDataCollector(
60+
string psHomePath,
61+
IReadOnlyCollection<string> excludedAssemblyPathPrefixes)
5762
{
63+
_psHomePath = psHomePath;
5864
_excludedAssemblyPathPrefixes = excludedAssemblyPathPrefixes;
5965
}
6066

@@ -65,10 +71,49 @@ private TypeDataCollector(IReadOnlyCollection<string> excludedAssemblyPathPrefix
6571
/// <returns>A data object describing the assemblies and PowerShell type accelerators available.</returns>
6672
public AvailableTypeData GetAvailableTypeData(out IEnumerable<CompatibilityAnalysisException> errors)
6773
{
74+
// PS 6+ behaves as if assemblies in PSHOME are already loaded when they aren't
75+
// so we must load them pre-emptively to capture the correct behavior
76+
#if CoreCLR
77+
List<CompatibilityAnalysisException> psHomeLoadErrors = new List<CompatibilityAnalysisException>();
78+
foreach (string dllPath in Directory.GetFiles(_psHomePath))
79+
{
80+
if (!string.Equals(Path.GetExtension(dllPath), ".dll"))
81+
{
82+
continue;
83+
}
84+
85+
try
86+
{
87+
Assembly.LoadFrom(dllPath);
88+
}
89+
catch (Exception e)
90+
{
91+
psHomeLoadErrors.Add(new CompatibilityAnalysisException($"Unable to load PSHOME DLL at path '{dllPath}'", e));
92+
}
93+
}
94+
#endif
95+
6896
IReadOnlyDictionary<string, Type> typeAccelerators = GetTypeAccelerators();
6997
IEnumerable<Assembly> loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
7098

99+
#if !CoreCLR
71100
return AssembleAvailableTypes(loadedAssemblies, typeAccelerators, out errors);
101+
#else
102+
103+
AvailableTypeData typeData = AssembleAvailableTypes(loadedAssemblies, typeAccelerators, out IEnumerable<CompatibilityAnalysisException> typeCollectionErrors);
104+
105+
if (psHomeLoadErrors.Count > 0)
106+
{
107+
psHomeLoadErrors.AddRange(typeCollectionErrors);
108+
errors = psHomeLoadErrors;
109+
}
110+
else
111+
{
112+
errors = typeCollectionErrors;
113+
}
114+
115+
return typeData;
116+
#endif
72117
}
73118

74119
/// <summary>

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Commands/NewPSCompatibilityProfileCommand.cs

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using Microsoft.PowerShell.CrossCompatibility.Collection;
99
using Microsoft.PowerShell.CrossCompatibility.Data;
1010
using Microsoft.PowerShell.CrossCompatibility.Retrieval;
11-
using Microsoft.PowerShell.CrossCompatibility.Utility;
1211
using Newtonsoft.Json;
1312
using SMA = System.Management.Automation;
1413

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Query/CommonPowerShellData.cs

+9-10
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,30 @@ namespace Microsoft.PowerShell.CrossCompatibility.Query
1212
/// </summary>
1313
public class CommonPowerShellData
1414
{
15-
private readonly Lazy<Tuple<IReadOnlyDictionary<string, ParameterData>, IReadOnlyDictionary<string, ParameterData>>> _parameters;
16-
1715
/// <summary>
1816
/// Create a new query object for common PowerShell data.
1917
/// </summary>
2018
/// <param name="commonPowerShellData">The mutable data object holding common data information.</param>
2119
public CommonPowerShellData(Data.CommonPowerShellData commonPowerShellData)
2220
{
23-
_parameters = new Lazy<Tuple<IReadOnlyDictionary<string, ParameterData>, IReadOnlyDictionary<string, ParameterData>>>(() => CreateParameterTable(commonPowerShellData.Parameters, commonPowerShellData.ParameterAliases));
21+
Parameters = CreateParameterTable(commonPowerShellData.Parameters, commonPowerShellData.ParameterAliases, out IReadOnlyDictionary<string, ParameterData> parameterAliases);
22+
ParameterAliases = parameterAliases;
2423
}
2524

2625
/// <summary>
2726
/// Common parameters, present on all commands bound with the cmdlet binding.
2827
/// </summary>
29-
public IReadOnlyDictionary<string, ParameterData> Parameters => _parameters.Value.Item1;
28+
public IReadOnlyDictionary<string, ParameterData> Parameters { get; }
3029

3130
/// <summary>
3231
/// Aliases for common parameters.
3332
/// </summary>
34-
public IReadOnlyDictionary<string, ParameterData> ParameterAliases => _parameters.Value.Item2;
33+
public IReadOnlyDictionary<string, ParameterData> ParameterAliases { get; }
3534

36-
private Tuple<IReadOnlyDictionary<string, ParameterData>, IReadOnlyDictionary<string, ParameterData>> CreateParameterTable(
35+
private IReadOnlyDictionary<string, ParameterData> CreateParameterTable(
3736
IReadOnlyDictionary<string, Data.ParameterData> parameters,
38-
IReadOnlyDictionary<string, string> parameterAliases)
37+
IReadOnlyDictionary<string, string> parameterAliases,
38+
out IReadOnlyDictionary<string, ParameterData> queryAliases)
3939
{
4040
var parameterDict = new Dictionary<string, ParameterData>(parameters.Count + parameterAliases.Count, StringComparer.OrdinalIgnoreCase);
4141
var parameterAliasDict = new Dictionary<string, ParameterData>(parameterAliases.Count, StringComparer.OrdinalIgnoreCase);
@@ -52,9 +52,8 @@ private Tuple<IReadOnlyDictionary<string, ParameterData>, IReadOnlyDictionary<st
5252
parameterDict[parameterAlias.Key] = aliasedParameter;
5353
}
5454

55-
return new Tuple<IReadOnlyDictionary<string, ParameterData>, IReadOnlyDictionary<string, ParameterData>>(
56-
parameterDict,
57-
parameterAliasDict);
55+
queryAliases = parameterAliasDict;
56+
return parameterDict;
5857
}
5958
}
6059
}

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Query/Modules/CommandData.cs

+15-7
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,32 @@ namespace Microsoft.PowerShell.CrossCompatibility.Query
1313
/// </summary>
1414
public abstract class CommandData
1515
{
16-
protected readonly Data.CommandData _commandData;
17-
1816
/// <summary>
1917
/// Create a new command data query object from the data object.
2018
/// </summary>
2119
/// <param name="name">The name of the command.</param>
2220
/// <param name="commandData">The command data object describing the command.</param>
2321
protected CommandData(string name, Data.CommandData commandData)
2422
{
25-
_commandData = commandData;
2623
Name = name;
24+
DefaultParameterSet = commandData.DefaultParameterSet;
25+
26+
if (commandData.OutputType != null)
27+
{
28+
OutputType = new List<string>(commandData.OutputType);
29+
}
30+
31+
if (commandData.ParameterSets != null)
32+
{
33+
ParameterSets = new List<string>(commandData.ParameterSets);
34+
}
2735

2836
var parameters = new Dictionary<string, ParameterData>(StringComparer.OrdinalIgnoreCase);
2937
var paramAliases = new Dictionary<string, ParameterData>(StringComparer.OrdinalIgnoreCase);
3038

3139
if (commandData.Parameters != null)
3240
{
33-
foreach (KeyValuePair<string, Microsoft.PowerShell.CrossCompatibility.Data.ParameterData> parameter in commandData.Parameters)
41+
foreach (KeyValuePair<string, Data.ParameterData> parameter in commandData.Parameters)
3442
{
3543
parameters.Add(parameter.Key, new ParameterData(parameter.Key, parameter.Value));
3644
}
@@ -59,17 +67,17 @@ protected CommandData(string name, Data.CommandData commandData)
5967
/// <summary>
6068
/// The output types of the command, if any.
6169
/// </summary>
62-
public IReadOnlyList<string> OutputType => _commandData.OutputType;
70+
public IReadOnlyList<string> OutputType { get; }
6371

6472
/// <summary>
6573
/// The parameter sets of the command, if any.
6674
/// </summary>
67-
public IReadOnlyList<string> ParameterSets => _commandData.ParameterSets;
75+
public IReadOnlyList<string> ParameterSets { get; }
6876

6977
/// <summary>
7078
/// The default parameter set of the command, if any.
7179
/// </summary>
72-
public string DefaultParameterSet => _commandData.DefaultParameterSet;
80+
public string DefaultParameterSet { get; }
7381

7482
/// <summary>
7583
/// Parameter aliases of the command.

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Query/Modules/FunctionData.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ public class FunctionData : CommandData
1818
public FunctionData(string name, Data.FunctionData functionData)
1919
: base(name, functionData)
2020
{
21+
IsCmdletBinding = functionData.CmdletBinding;
2122
}
2223

2324
/// <summary>
2425
/// True if this is an advanced function (has a cmdlet binding), false otherwise.
2526
/// </summary>
26-
public override bool IsCmdletBinding => ((Data.FunctionData)_commandData).CmdletBinding;
27+
public override bool IsCmdletBinding { get; }
2728
}
2829
}

0 commit comments

Comments
 (0)