mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Add 'Get-ExperimentalFeature' cmdlet
This commit is contained in:
@@ -286,7 +286,7 @@ namespace System.Management.Automation
|
||||
/// <summary>
|
||||
/// An instance that represents the none-value.
|
||||
/// </summary>
|
||||
internal readonly static ExperimentalAttribute None = new ExperimentalAttribute();
|
||||
internal static readonly ExperimentalAttribute None = new ExperimentalAttribute();
|
||||
|
||||
internal bool ToHide => EffectiveAction == ExperimentAction.Hide;
|
||||
internal bool ToShow => EffectiveAction == ExperimentAction.Show;
|
||||
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
using System.Management.Automation.Internal;
|
||||
|
||||
namespace Microsoft.PowerShell.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements Get-ExperimentalFeature cmdlet.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Get, "ExperimentalFeature", HelpUri = "")]
|
||||
public class GetExperimentalFeatureCommand : PSCmdlet
|
||||
{
|
||||
/// <summary>
|
||||
/// Specify the feature names.
|
||||
/// </summary>
|
||||
[Parameter(ValueFromPipeline = true, Position = 0)]
|
||||
[ValidateNotNullOrEmpty]
|
||||
public string[] Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Search module paths to find all available experimental features.
|
||||
/// </summary>
|
||||
[Parameter()]
|
||||
public SwitchParameter ListAvailable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ProcessRecord method of this cmdlet.
|
||||
/// </summary>
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant;
|
||||
IEnumerable<WildcardPattern> namePatterns = SessionStateUtilities.CreateWildcardsFromStrings(Name, wildcardOptions);
|
||||
|
||||
if (ListAvailable)
|
||||
{
|
||||
foreach (ExperimentalFeature feature in GetAvailableExperimentalFeatures(namePatterns).OrderBy(GetSortingString))
|
||||
{
|
||||
WriteObject(feature);
|
||||
}
|
||||
}
|
||||
else if (ExperimentalFeature.EnabledExperimentalFeatureNames.Count > 0)
|
||||
{
|
||||
foreach (ExperimentalFeature feature in GetEnabledExperimentalFeatures(namePatterns).OrderBy(GetSortingString))
|
||||
{
|
||||
WriteObject(feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct the string for sorting experimental feature records.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Engine features come before module features.
|
||||
/// Within engine features and more features, features are ordered by name.
|
||||
/// </remarks>
|
||||
private static string GetSortingString(ExperimentalFeature feature)
|
||||
{
|
||||
if (ExperimentalFeature.EngineSource.Equals(feature.Source))
|
||||
{
|
||||
return "0:" + feature.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "1:" + feature.Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get enabled experimental features based on the specified name patterns.
|
||||
/// </summary>
|
||||
private IEnumerable<ExperimentalFeature> GetEnabledExperimentalFeatures(IEnumerable<WildcardPattern> namePatterns)
|
||||
{
|
||||
var moduleFeatures = new List<string>();
|
||||
var moduleNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (string featureName in ExperimentalFeature.EnabledExperimentalFeatureNames)
|
||||
{
|
||||
// Only process the feature names that matches any name patterns.
|
||||
if (SessionStateUtilities.MatchesAnyWildcardPattern(featureName, namePatterns, defaultValue: true))
|
||||
{
|
||||
if (ExperimentalFeature.EngineExperimentalFeatureMap.TryGetValue(featureName, out ExperimentalFeature feature))
|
||||
{
|
||||
yield return feature;
|
||||
}
|
||||
else
|
||||
{
|
||||
moduleFeatures.Add(featureName);
|
||||
int lastDotIndex = featureName.LastIndexOf('.');
|
||||
moduleNames.Add(featureName.Substring(0, lastDotIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleFeatures.Count > 0)
|
||||
{
|
||||
var featuresFromGivenModules = new Dictionary<string, ExperimentalFeature>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (string moduleFile in GetValidModuleFiles(moduleNames))
|
||||
{
|
||||
ExperimentalFeature[] features = ModuleIntrinsics.GetExperimentalFeature(moduleFile);
|
||||
foreach (var feature in features)
|
||||
{
|
||||
featuresFromGivenModules.TryAdd(feature.Name, feature);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string featureName in moduleFeatures)
|
||||
{
|
||||
if (featuresFromGivenModules.TryGetValue(featureName, out ExperimentalFeature feature))
|
||||
{
|
||||
yield return feature;
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new ExperimentalFeature(featureName, description: null, source: null, isEnabled: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get available experimental features based on the specified name patterns.
|
||||
/// </summary>
|
||||
private IEnumerable<ExperimentalFeature> GetAvailableExperimentalFeatures(IEnumerable<WildcardPattern> namePatterns)
|
||||
{
|
||||
foreach (ExperimentalFeature feature in ExperimentalFeature.EngineExperimentalFeatures)
|
||||
{
|
||||
if (SessionStateUtilities.MatchesAnyWildcardPattern(feature.Name, namePatterns, defaultValue: true))
|
||||
{
|
||||
yield return feature;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string moduleFile in GetValidModuleFiles(moduleNamesToFind: null))
|
||||
{
|
||||
ExperimentalFeature[] features = ModuleIntrinsics.GetExperimentalFeature(moduleFile);
|
||||
foreach (var feature in features)
|
||||
{
|
||||
if (SessionStateUtilities.MatchesAnyWildcardPattern(feature.Name, namePatterns, defaultValue: true))
|
||||
{
|
||||
yield return feature;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get valid module files from module paths.
|
||||
/// </summary>
|
||||
private IEnumerable<string> GetValidModuleFiles(HashSet<string> moduleNamesToFind)
|
||||
{
|
||||
var modulePaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (string path in ModuleIntrinsics.GetModulePath(includeSystemModulePath: false, Context))
|
||||
{
|
||||
string uniquePath = path.TrimEnd(Utils.Separators.Directory);
|
||||
if (!modulePaths.Add(uniquePath)) { continue; }
|
||||
|
||||
foreach (string moduleFile in ModuleUtils.GetDefaultAvailableModuleFiles(uniquePath))
|
||||
{
|
||||
// We only care about module manifest files because that's where experimental features are declared.
|
||||
if (!moduleFile.EndsWith(StringLiterals.PowerShellDataFileExtension, StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
|
||||
if (moduleNamesToFind != null)
|
||||
{
|
||||
string currentModuleName = ModuleIntrinsics.GetModuleName(moduleFile);
|
||||
if (!moduleNamesToFind.Contains(currentModuleName)) { continue; }
|
||||
}
|
||||
|
||||
yield return moduleFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5277,6 +5277,7 @@ end
|
||||
{"Export-ModuleMember", new SessionStateCmdletEntry("Export-ModuleMember", typeof(ExportModuleMemberCommand), helpFile) },
|
||||
{"ForEach-Object", new SessionStateCmdletEntry("ForEach-Object", typeof(ForEachObjectCommand), helpFile) },
|
||||
{"Get-Command", new SessionStateCmdletEntry("Get-Command", typeof(GetCommandCommand), helpFile) },
|
||||
{"Get-ExperimentalFeature", new SessionStateCmdletEntry("Get-ExperimentalFeature", typeof(GetExperimentalFeatureCommand), helpFile) },
|
||||
{"Get-Help", new SessionStateCmdletEntry("Get-Help", typeof(GetHelpCommand), helpFile) },
|
||||
{"Get-History", new SessionStateCmdletEntry("Get-History", typeof(GetHistoryCommand), helpFile) },
|
||||
{"Get-Job", new SessionStateCmdletEntry("Get-Job", typeof(GetJobCommand), helpFile) },
|
||||
|
||||
@@ -2059,8 +2059,8 @@ namespace Microsoft.PowerShell.Commands
|
||||
if (ExperimentalFeature.IsModuleFeatureName(featureName, moduleName))
|
||||
{
|
||||
string featureDescription = feature["Description"] as string;
|
||||
expFeatureList.Add(new ExperimentalFeature(featureName, featureDescription, moduleName,
|
||||
ExperimentalFeature.HasEnabled(featureName)));
|
||||
expFeatureList.Add(new ExperimentalFeature(featureName, featureDescription, moduleManifestPath,
|
||||
ExperimentalFeature.HasEnabled(featureName)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Management.Automation.Configuration;
|
||||
@@ -414,64 +415,92 @@ namespace System.Management.Automation
|
||||
|
||||
internal static Version GetManifestModuleVersion(string manifestPath)
|
||||
{
|
||||
if (manifestPath != null &&
|
||||
manifestPath.EndsWith(StringLiterals.PowerShellDataFileExtension, StringComparison.OrdinalIgnoreCase))
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var dataFileSetting =
|
||||
PsUtils.GetModuleManifestProperties(
|
||||
manifestPath,
|
||||
PsUtils.ManifestModuleVersionPropertyName);
|
||||
Hashtable dataFileSetting =
|
||||
PsUtils.GetModuleManifestProperties(
|
||||
manifestPath,
|
||||
PsUtils.ManifestModuleVersionPropertyName);
|
||||
|
||||
var versionValue = dataFileSetting["ModuleVersion"];
|
||||
if (versionValue != null)
|
||||
object versionValue = dataFileSetting["ModuleVersion"];
|
||||
if (versionValue != null)
|
||||
{
|
||||
Version moduleVersion;
|
||||
if (LanguagePrimitives.TryConvertTo(versionValue, out moduleVersion))
|
||||
{
|
||||
Version moduleVersion;
|
||||
if (LanguagePrimitives.TryConvertTo(versionValue, out moduleVersion))
|
||||
{
|
||||
return moduleVersion;
|
||||
}
|
||||
return moduleVersion;
|
||||
}
|
||||
}
|
||||
catch (PSInvalidOperationException)
|
||||
{
|
||||
}
|
||||
}
|
||||
catch (PSInvalidOperationException) { }
|
||||
|
||||
return new Version(0, 0);
|
||||
}
|
||||
|
||||
internal static Guid GetManifestGuid(string manifestPath)
|
||||
{
|
||||
if (manifestPath != null &&
|
||||
manifestPath.EndsWith(StringLiterals.PowerShellDataFileExtension, StringComparison.OrdinalIgnoreCase))
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var dataFileSetting =
|
||||
PsUtils.GetModuleManifestProperties(
|
||||
manifestPath,
|
||||
PsUtils.ManifestGuidPropertyName);
|
||||
Hashtable dataFileSetting =
|
||||
PsUtils.GetModuleManifestProperties(
|
||||
manifestPath,
|
||||
PsUtils.ManifestGuidPropertyName);
|
||||
|
||||
var guidValue = dataFileSetting["GUID"];
|
||||
if (guidValue != null)
|
||||
object guidValue = dataFileSetting["GUID"];
|
||||
if (guidValue != null)
|
||||
{
|
||||
Guid guidID;
|
||||
if (LanguagePrimitives.TryConvertTo(guidValue, out guidID))
|
||||
{
|
||||
Guid guidID;
|
||||
if (LanguagePrimitives.TryConvertTo(guidValue, out guidID))
|
||||
{
|
||||
return guidID;
|
||||
}
|
||||
return guidID;
|
||||
}
|
||||
}
|
||||
catch (PSInvalidOperationException)
|
||||
{
|
||||
}
|
||||
}
|
||||
catch (PSInvalidOperationException) { }
|
||||
|
||||
return new Guid();
|
||||
}
|
||||
|
||||
internal static ExperimentalFeature[] GetExperimentalFeature(string manifestPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
Hashtable dataFileSetting =
|
||||
PsUtils.GetModuleManifestProperties(
|
||||
manifestPath,
|
||||
PsUtils.ManifestPrivateDataPropertyName);
|
||||
|
||||
object privateData = dataFileSetting["PrivateData"];
|
||||
if (privateData is Hashtable hashData && hashData["PSData"] is Hashtable psData)
|
||||
{
|
||||
object expFeatureValue = psData["ExperimentalFeatures"];
|
||||
if (expFeatureValue != null &&
|
||||
LanguagePrimitives.TryConvertTo(expFeatureValue, out Hashtable[] features) &&
|
||||
features.Length > 0)
|
||||
{
|
||||
string moduleName = ModuleIntrinsics.GetModuleName(manifestPath);
|
||||
var expFeatureList = new List<ExperimentalFeature>();
|
||||
foreach (Hashtable feature in features)
|
||||
{
|
||||
string featureName = feature["Name"] as string;
|
||||
if (String.IsNullOrEmpty(featureName)) { continue; }
|
||||
|
||||
if (ExperimentalFeature.IsModuleFeatureName(featureName, moduleName))
|
||||
{
|
||||
string featureDescription = feature["Description"] as string;
|
||||
expFeatureList.Add(new ExperimentalFeature(featureName, featureDescription, manifestPath,
|
||||
ExperimentalFeature.HasEnabled(featureName)));
|
||||
}
|
||||
}
|
||||
return expFeatureList.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (PSInvalidOperationException) { }
|
||||
|
||||
return Utils.EmptyArray<ExperimentalFeature>();
|
||||
}
|
||||
|
||||
// The extensions of all of the files that can be processed with Import-Module, put the ni.dll in front of .dll to have higher priority to be loaded.
|
||||
internal static string[] PSModuleProcessableExtensions = new string[] {
|
||||
StringLiterals.PowerShellDataFileExtension,
|
||||
|
||||
@@ -357,7 +357,6 @@ namespace System.Management.Automation
|
||||
|
||||
// $ShellId - if there is no runspace config, use the default string
|
||||
string shellId = ExecutionContext.ShellID;
|
||||
|
||||
v = new PSVariable(SpecialVariables.ShellId, shellId,
|
||||
ScopedItemOptions.Constant | ScopedItemOptions.AllScope,
|
||||
RunspaceInit.MshShellIdDescription);
|
||||
@@ -366,18 +365,10 @@ namespace System.Management.Automation
|
||||
// $PSHOME
|
||||
// This depends on the shellId. If we cannot read the application base
|
||||
// registry key, set the variable to empty string
|
||||
string applicationBase = string.Empty;
|
||||
try
|
||||
{
|
||||
applicationBase = Utils.GetApplicationBase(shellId);
|
||||
}
|
||||
catch (SecurityException)
|
||||
{
|
||||
}
|
||||
string applicationBase = Utils.DefaultPowerShellAppBase;
|
||||
v = new PSVariable(SpecialVariables.PSHome, applicationBase,
|
||||
ScopedItemOptions.Constant | ScopedItemOptions.AllScope,
|
||||
RunspaceInit.PSHOMEDescription);
|
||||
|
||||
this.GlobalScope.SetVariable(v.Name, v, false, true, this, CommandOrigin.Internal, fastPath: true);
|
||||
}
|
||||
|
||||
|
||||
@@ -477,6 +477,7 @@ namespace System.Management.Automation
|
||||
internal static readonly string[] ManifestModuleVersionPropertyName = new[] { "ModuleVersion" };
|
||||
internal static readonly string[] ManifestGuidPropertyName = new[] { "GUID" };
|
||||
internal static readonly string[] FastModuleManifestAnalysisPropertyNames = new[] { "AliasesToExport", "CmdletsToExport", "FunctionsToExport", "NestedModules", "RootModule", "ModuleToProcess", "ModuleVersion" };
|
||||
internal static readonly string[] ManifestPrivateDataPropertyName = new[] { "PrivateData" };
|
||||
|
||||
internal static Hashtable GetModuleManifestProperties(string psDataFilePath, string[] keys)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user