Support experimental feature in modules

This commit is contained in:
Dongbo Wang
2018-07-06 13:28:19 -07:00
parent 930350e2ee
commit 8a5ddbb231
3 changed files with 112 additions and 43 deletions
@@ -2027,12 +2027,75 @@ namespace Microsoft.PowerShell.Commands
Array.Clear(tmpNestedModules, 0, tmpNestedModules.Length);
}
// Set the private data member for the module if the manifest contains
// this member
object privateData = null;
if (data.Contains("PrivateData"))
// Set the private data member for the module if the manifest contains this member
object privateData = data["PrivateData"];
// Validate the 'ExperimentalFeatures' member of the manifest
List<ExperimentalFeature> expFeatureList = null;
if (privateData is Hashtable hashData && hashData["PSData"] is Hashtable psData)
{
privateData = data["PrivateData"];
if (!GetScalarFromData(psData, moduleManifestPath, "ExperimentalFeatures", manifestProcessingFlags, out Hashtable[] features))
{
containedErrors = true;
if (bailOnFirstError) return null;
}
if (features != null && features.Length > 0)
{
bool nameMissingOrEmpty = false;
var invalidNames = new List<string>();
string moduleName = ModuleIntrinsics.GetModuleName(moduleManifestPath);
expFeatureList = new List<ExperimentalFeature>(features.Length);
foreach (var feature in features)
{
string featureName = feature["Name"] as string;
if (String.IsNullOrEmpty(featureName))
{
nameMissingOrEmpty = true;
continue;
}
if (ExperimentalFeature.IsModuleFeatureName(featureName, moduleName))
{
string featureDescription = feature["Description"] as string;
expFeatureList.Add(new ExperimentalFeature(featureName, featureDescription, moduleName,
ExperimentalFeature.HasEnabled(featureName)));
}
else
{
invalidNames.Add(featureName);
}
}
if (nameMissingOrEmpty)
{
if (writingErrors)
{
WriteError(new ErrorRecord(new ArgumentException(Modules.ExperimentalFeatureNameMissingOrEmpty),
"Modules_ExperimentalFeatureNameMissingOrEmpty",
ErrorCategory.InvalidData, null));
}
containedErrors = true;
if (bailOnFirstError) { return null; }
}
if (invalidNames.Count > 0)
{
if (writingErrors)
{
string invalidNameStr = String.Join(',', invalidNames);
string errorMsg = StringUtil.Format(Modules.InvalidExperimentalFeatureName, invalidNameStr);
WriteError(new ErrorRecord(new ArgumentException(errorMsg),
"Modules_InvalidExperimentalFeatureName",
ErrorCategory.InvalidData, null));
}
containedErrors = true;
if (bailOnFirstError) { return null; }
}
}
}
// Process all of the exports...
@@ -2413,6 +2476,12 @@ namespace Microsoft.PowerShell.Commands
manifestInfo.PowerShellVersion = powerShellVersion;
manifestInfo.ProcessorArchitecture = requiredProcessorArchitecture;
manifestInfo.Prefix = resolvedCommandPrefix;
if (expFeatureList != null)
{
manifestInfo.ExperimentalFeatures = new ReadOnlyCollection<ExperimentalFeature>(expFeatureList);
}
if (assemblyList != null)
{
foreach (var a in assemblyList)
@@ -3009,6 +3078,7 @@ namespace Microsoft.PowerShell.Commands
newManifestInfo.LicenseUri = manifestInfo.LicenseUri;
newManifestInfo.IconUri = manifestInfo.IconUri;
newManifestInfo.RepositorySourceLocation = manifestInfo.RepositorySourceLocation;
newManifestInfo.ExperimentalFeatures = manifestInfo.ExperimentalFeatures;
// If we are in module discovery, then fix the path.
if (ss == null)
@@ -301,49 +301,37 @@ namespace System.Management.Automation
ProjectUri = null;
IconUri = null;
var privateDataHashTable = _privateData as Hashtable;
if (privateDataHashTable != null)
if (_privateData is Hashtable hashData && hashData["PSData"] is Hashtable psData)
{
var psData = privateDataHashTable["PSData"] as Hashtable;
if (psData != null)
var tagsValue = psData["Tags"];
if (tagsValue is object[] tags && tags.Length > 0)
{
object tagsValue = psData["Tags"];
if (tagsValue != null)
foreach (var tagString in tags.OfType<string>())
{
var tags = tagsValue as object[];
if (tags != null && tags.Any())
{
foreach (var tagString in tags.OfType<string>())
{
AddToTags(tagString);
}
}
else
{
AddToTags(tagsValue.ToString());
}
AddToTags(tagString);
}
var licenseUri = psData["LicenseUri"] as string;
if (licenseUri != null)
{
LicenseUri = GetUriFromString(licenseUri);
}
var projectUri = psData["ProjectUri"] as string;
if (projectUri != null)
{
ProjectUri = GetUriFromString(projectUri);
}
var iconUri = psData["IconUri"] as string;
if (iconUri != null)
{
IconUri = GetUriFromString(iconUri);
}
ReleaseNotes = psData["ReleaseNotes"] as string;
}
else if (tagsValue is string tag)
{
AddToTags(tag);
}
if (psData["LicenseUri"] is string licenseUri)
{
LicenseUri = GetUriFromString(licenseUri);
}
if (psData["ProjectUri"] is string projectUri)
{
ProjectUri = GetUriFromString(projectUri);
}
if (psData["IconUri"] is string iconUri)
{
IconUri = GetUriFromString(iconUri);
}
ReleaseNotes = psData["ReleaseNotes"] as string;
}
}
@@ -360,6 +348,11 @@ namespace System.Management.Automation
return uri;
}
/// <summary>
/// Get the experimental features declared in this module.
/// </summary>
public IEnumerable<ExperimentalFeature> ExperimentalFeatures { get; internal set; } = Utils.EmptyReadOnlyCollection<ExperimentalFeature>();
/// <summary>
/// Tags of this module.
/// </summary>
@@ -606,4 +606,10 @@
<data name="PrerequisiteForDesktopEditionOnly" xml:space="preserve">
<value>This prerequisite is valid for the PowerShell Desktop edition only.</value>
</data>
<data name="ExperimentalFeatureNameMissingOrEmpty" xml:space="preserve">
<value>A non-empty string value should be specified for an experimental feature declared in the module manifest.</value>
</data>
<data name="InvalidExperimentalFeatureName" xml:space="preserve">
<value>One or more invalid experimental feature names found: {0}. A module experimental feature name should follow this convention: 'ModuleName.FeatureName'.</value>
</data>
</root>