Refactor module version/GUID comparison logic (#7125)

This commit is contained in:
Robert Holt
2018-11-01 13:41:51 -07:00
committed by Aditya Patwardhan
parent e888e51097
commit 5d06fba366
4 changed files with 394 additions and 241 deletions
@@ -388,7 +388,7 @@ namespace Microsoft.PowerShell.Commands
{
PSModuleInfo alreadyLoadedModule = null;
Context.Modules.ModuleTable.TryGetValue(module.Path, out alreadyLoadedModule);
if (!BaseForce && IsModuleAlreadyLoaded(alreadyLoadedModule))
if (!BaseForce && DoesAlreadyLoadedModuleSatisfyConstraints(alreadyLoadedModule))
{
AddModuleToModuleTables(this.Context, this.TargetSessionState.Internal, alreadyLoadedModule);
@@ -586,13 +586,8 @@ namespace Microsoft.PowerShell.Commands
PSModuleInfo module;
if (!BaseForce && Context.Modules.ModuleTable.TryGetValue(rootedPath, out module))
{
if (RequiredVersion == null
|| module.Version.Equals(RequiredVersion)
|| (BaseMinimumVersion == null && BaseMaximumVersion == null)
|| module.ModuleType != ModuleType.Manifest
|| (BaseMinimumVersion == null && BaseMaximumVersion != null && module.Version <= BaseMaximumVersion)
|| (BaseMinimumVersion != null && BaseMaximumVersion == null && module.Version >= BaseMinimumVersion)
|| (BaseMinimumVersion != null && BaseMaximumVersion != null && module.Version >= BaseMinimumVersion && module.Version <= BaseMaximumVersion))
if (module.ModuleType != ModuleType.Manifest
|| ModuleIntrinsics.IsVersionMatchingConstraints(module.Version, RequiredVersion, BaseMinimumVersion, BaseMaximumVersion))
{
alreadyLoaded = true;
AddModuleToModuleTables(this.Context, this.TargetSessionState.Internal, module);
@@ -361,9 +361,7 @@ namespace Microsoft.PowerShell.Commands
foreach (var version in ModuleUtils.GetModuleVersionSubfolders(moduleBase))
{
// Skip the version folder if it is not equal to the required version or does not satisfy the minimum/maximum version criteria
if ((BaseRequiredVersion != null && !BaseRequiredVersion.Equals(version))
|| (BaseMinimumVersion != null && BaseRequiredVersion == null && version < BaseMinimumVersion)
|| (BaseMaximumVersion != null && BaseRequiredVersion == null && version > BaseMaximumVersion))
if (!ModuleIntrinsics.IsVersionMatchingConstraints(version, BaseRequiredVersion, BaseMinimumVersion, BaseMaximumVersion))
{
continue;
}
@@ -1585,22 +1583,7 @@ namespace Microsoft.PowerShell.Commands
}
}
// TODO/FIXME: use IsModuleAlreadyLoaded to get consistent behavior
// if (IsModuleAlreadyLoaded(loadedModule) &&
// ((manifestProcessingFlags & ManifestProcessingFlags.LoadElements) == ManifestProcessingFlags.LoadElements))
if (loadedModule != null &&
(BaseRequiredVersion == null || loadedModule.Version.Equals(BaseRequiredVersion)) &&
((BaseMinimumVersion == null && BaseMaximumVersion == null)
||
(BaseMaximumVersion != null && BaseMinimumVersion == null &&
loadedModule.Version <= BaseMaximumVersion)
||
(BaseMaximumVersion == null && BaseMinimumVersion != null &&
loadedModule.Version >= BaseMinimumVersion)
||
(BaseMaximumVersion != null && BaseMinimumVersion != null &&
loadedModule.Version >= BaseMinimumVersion && loadedModule.Version <= BaseMaximumVersion)) &&
(BaseGuid == null || loadedModule.Guid.Equals(BaseGuid)) && importingModule)
if (importingModule && DoesAlreadyLoadedModuleSatisfyConstraints(loadedModule))
{
if (!BaseForce)
{
@@ -1672,23 +1655,20 @@ namespace Microsoft.PowerShell.Commands
}
if (bailOnFirstError) return null;
}
else if (requiredVersion != null && !moduleVersion.Equals(requiredVersion))
else if (!ModuleIntrinsics.AreModuleFieldsMatchingConstraints(
moduleGuid: manifestGuid,
moduleVersion: moduleVersion,
requiredGuid: requiredModuleGuid,
requiredVersion: requiredVersion,
minimumRequiredVersion: minimumVersion,
maximumRequiredVersion: maximumVersion))
{
if (bailOnFirstError) return null;
}
else
{
if (moduleVersion < minimumVersion || (maximumVersion != null && moduleVersion > maximumVersion))
if (bailOnFirstError)
{
if (bailOnFirstError) return null;
return null;
}
}
if (requiredModuleGuid != null && !requiredModuleGuid.Equals(manifestGuid))
{
if (bailOnFirstError) return null;
}
// Verify that the module version from the module manifest is equal to module version folder.
DirectoryInfo parent = null;
try
@@ -3644,91 +3624,27 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
/// <param name="context">Execution Context.</param>
/// <param name="requiredModule">Either a string or a hash of ModuleName, optional Guid, and ModuleVersion.</param>
/// <param name="wrongVersion">Sets if the module cannot be found due to incorrect version.</param>
/// <param name="wrongGuid">Sets if the module cannot be found due to incorrect guid.</param>
/// <param name="matchFailureReason">The reason the module failed to load, or null on success.</param>
/// <param name="loaded">Sets if the module/snapin is already present.</param>
/// <returns>null if the module is not loaded or loadElements is false, the loaded module otherwise.</returns>
internal static object IsModuleLoaded(ExecutionContext context, ModuleSpecification requiredModule, out bool wrongVersion, out bool wrongGuid, out bool loaded)
internal static object IsModuleLoaded(ExecutionContext context, ModuleSpecification requiredModule, out ModuleMatchFailure matchFailureReason, out bool loaded)
{
loaded = false;
Dbg.Assert(requiredModule != null, "Caller should verify requiredModuleSpecification != null");
// Assume the module is not loaded.
object result = null;
wrongVersion = false;
wrongGuid = false;
string moduleName = requiredModule.Name;
Guid? moduleGuid = requiredModule.Guid;
Dbg.Assert(moduleName != null, "GetModuleSpecification should guarantee that moduleName != null");
Dbg.Assert(requiredModule.Name != null, "GetModuleSpecification should guarantee that moduleName != null");
ModuleMatchFailure matchFailure = ModuleMatchFailure.None;
foreach (PSModuleInfo module in context.Modules.GetModules(new string[] { "*" }, false))
{
if (moduleName.Equals(module.Name, StringComparison.OrdinalIgnoreCase))
// Check that the module meets the module constraints give
if (ModuleIntrinsics.IsModuleMatchingModuleSpec(out matchFailure, module, requiredModule))
{
if (!moduleGuid.HasValue || moduleGuid.Value.Equals(module.Guid))
{
if (requiredModule.RequiredVersion != null)
{
if (requiredModule.RequiredVersion.Equals(module.Version))
{
result = module;
loaded = true;
break;
}
wrongVersion = true;
}
else if (requiredModule.Version != null)
{
if (requiredModule.MaximumVersion != null)
{
if (GetMaximumVersion(requiredModule.MaximumVersion) >= module.Version && requiredModule.Version <= module.Version)
{
result = module;
loaded = true;
break;
}
else
{
wrongVersion = true;
}
}
else if (requiredModule.Version <= module.Version)
{
result = module;
loaded = true;
break;
}
else
{
wrongVersion = true;
}
}
else if (requiredModule.MaximumVersion != null)
{
if (GetMaximumVersion(requiredModule.MaximumVersion) >= module.Version)
{
result = module;
loaded = true;
break;
}
else
{
wrongVersion = true;
}
}
else
{
result = module;
loaded = true;
break;
}
}
else
{
wrongGuid = true;
}
result = module;
loaded = true;
break;
}
}
@@ -3742,6 +3658,7 @@ namespace Microsoft.PowerShell.Commands
}
}
matchFailureReason = matchFailure;
return result;
}
@@ -3795,10 +3712,9 @@ namespace Microsoft.PowerShell.Commands
Guid? moduleGuid = requiredModuleSpecification.Guid;
PSModuleInfo result = null;
bool wrongVersion = false;
bool wrongGuid = false;
ModuleMatchFailure loadFailureReason = ModuleMatchFailure.None;
bool loaded = false;
object loadedModule = IsModuleLoaded(context, requiredModuleSpecification, out wrongVersion, out wrongGuid, out loaded);
object loadedModule = IsModuleLoaded(context, requiredModuleSpecification, out loadFailureReason, out loaded);
if (loadedModule == null)
{
@@ -3855,33 +3771,73 @@ namespace Microsoft.PowerShell.Commands
{
if (0 != (manifestProcessingFlags & ManifestProcessingFlags.WriteErrors))
{
if (wrongVersion)
switch (loadFailureReason)
{
if (requiredModuleSpecification.RequiredVersion != null)
{
message = StringUtil.Format(Modules.RequiredModuleNotLoadedWrongVersion, moduleManifestPath, moduleName, requiredModuleSpecification.RequiredVersion);
}
else if (requiredModuleSpecification.Version != null && requiredModuleSpecification.MaximumVersion == null)
{
message = StringUtil.Format(Modules.RequiredModuleNotLoadedWrongVersion, moduleManifestPath, moduleName, requiredModuleSpecification.Version);
}
else if (requiredModuleSpecification.Version == null && requiredModuleSpecification.MaximumVersion != null)
{
message = StringUtil.Format(Modules.RequiredModuleNotLoadedWrongMaximumVersion, moduleManifestPath, moduleName, requiredModuleSpecification.MaximumVersion);
}
else
{
message = StringUtil.Format(Modules.RequiredModuleNotLoadedWrongMinimumVersionAndMaximumVersion, moduleManifestPath, moduleName, requiredModuleSpecification.Version, requiredModuleSpecification.MaximumVersion);
}
}
else if (wrongGuid)
{
message = StringUtil.Format(Modules.RequiredModuleNotLoadedWrongGuid, moduleManifestPath, moduleName, moduleGuid.Value);
}
else
{
message = StringUtil.Format(Modules.RequiredModuleNotLoaded, moduleManifestPath, moduleName);
case ModuleMatchFailure.RequiredVersion:
message = StringUtil.Format(
Modules.RequiredModuleNotLoadedWrongVersion,
moduleManifestPath,
moduleName,
requiredModuleSpecification.RequiredVersion);
break;
case ModuleMatchFailure.MinimumVersion:
// If both max and min versions were specified, use a different error message
if (requiredModuleSpecification.MaximumVersion == null)
{
message = StringUtil.Format(
Modules.RequiredModuleNotLoadedWrongVersion,
moduleManifestPath, moduleName,
requiredModuleSpecification.Version);
}
else
{
message = StringUtil.Format(
Modules.RequiredModuleNotLoadedWrongMinimumVersionAndMaximumVersion,
moduleManifestPath,
moduleName,
requiredModuleSpecification.Version,
requiredModuleSpecification.MaximumVersion);
}
break;
case ModuleMatchFailure.MaximumVersion:
// If both max and min versions were specified, use a different error message
if (requiredModuleSpecification.Version == null)
{
message = StringUtil.Format(
Modules.RequiredModuleNotLoadedWrongMaximumVersion,
moduleManifestPath,
moduleName,
requiredModuleSpecification.MaximumVersion);
}
else
{
message = StringUtil.Format(
Modules.RequiredModuleNotLoadedWrongMinimumVersionAndMaximumVersion,
moduleManifestPath,
moduleName,
requiredModuleSpecification.Version,
requiredModuleSpecification.MaximumVersion);
}
break;
case ModuleMatchFailure.Guid:
message = StringUtil.Format(
Modules.RequiredModuleNotLoadedWrongGuid,
moduleManifestPath,
moduleName,
moduleGuid.Value);
break;
default:
message = StringUtil.Format(
Modules.RequiredModuleNotLoaded,
moduleManifestPath,
moduleName);
break;
}
MissingMemberException mm = new MissingMemberException(message);
error = new ErrorRecord(mm, "Modules_InvalidManifest",
ErrorCategory.ResourceUnavailable, moduleManifestPath);
@@ -3943,8 +3899,6 @@ namespace Microsoft.PowerShell.Commands
}
else
{
bool wrongVersion = false;
bool wrongGuid = false;
// Check if the correct module is loaded using Version , Guid Information.
string moduleNameToCheckAgainst = requiredModule.Name;
string manifestPath = string.Empty;
@@ -3953,6 +3907,7 @@ namespace Microsoft.PowerShell.Commands
manifestPath = moduleNameToCheckAgainst;
moduleNameToCheckAgainst = Path.GetFileNameWithoutExtension(moduleNameToCheckAgainst);
}
ModuleSpecification ms = new ModuleSpecification(moduleNameToCheckAgainst);
if (requiredModule.Guid != null)
{
@@ -3970,9 +3925,13 @@ namespace Microsoft.PowerShell.Commands
{
ms.MaximumVersion = requiredModule.MaximumVersion;
}
ModuleMatchFailure loadFailureReason;
bool loaded = false;
object r = IsModuleLoaded(context, ms, out wrongVersion, out wrongGuid, out loaded);
object r = IsModuleLoaded(context, ms, out loadFailureReason, out loaded);
Dbg.Assert(r is PSModuleInfo, "The returned value should be PSModuleInfo");
result = r as PSModuleInfo;
if (result == null)
{
@@ -4099,43 +4058,13 @@ namespace Microsoft.PowerShell.Commands
tempResult = powerShell.Invoke<PSModuleInfo>();
}
// Check if the available module is of the correct version and GUID
foreach (var m in tempResult)
foreach (var module in tempResult)
{
if (!requiredModule.Guid.HasValue || requiredModule.Guid.Value.Equals(m.Guid))
if (ModuleIntrinsics.IsModuleMatchingModuleSpec(module, requiredModule))
{
if (requiredModule.RequiredVersion != null)
{
if (requiredModule.RequiredVersion.Equals(m.Version))
{
result.Add(m);
}
}
else if (requiredModule.Version != null)
{
if (requiredModule.MaximumVersion != null)
{
if (GetMaximumVersion(requiredModule.MaximumVersion) >= m.Version && requiredModule.Version <= m.Version)
{
result.Add(m);
}
}
else if (requiredModule.Version <= m.Version)
{
result.Add(m);
}
}
else if (requiredModule.MaximumVersion != null)
{
if (GetMaximumVersion(requiredModule.MaximumVersion) >= m.Version)
{
result.Add(m);
}
}
else
{
result.Add(m);
}
result.Add(module);
}
}
return result;
@@ -5074,34 +5003,19 @@ namespace Microsoft.PowerShell.Commands
return false;
}
internal bool IsModuleAlreadyLoaded(PSModuleInfo alreadyLoadedModule)
/// <summary>
/// Checks if an already loaded module meets the constraints passed to the module cmdlet by the user.
/// </summary>
/// <param name="alreadyLoadedModule">The already loaded module that matched the name of the module to load.</param>
/// <returns>True if the pre-loaded module matches all GUID and version constraints provided, false otherwise.</returns>
internal bool DoesAlreadyLoadedModuleSatisfyConstraints(PSModuleInfo alreadyLoadedModule)
{
if (alreadyLoadedModule == null)
{
return false;
}
if (this.BaseRequiredVersion != null && !alreadyLoadedModule.Version.Equals(this.BaseRequiredVersion))
{
// "alreadyLoadedModule" is a different module (than the one we want to load)
return false;
}
if (this.BaseMinimumVersion != null && alreadyLoadedModule.Version < this.BaseMinimumVersion)
{
// "alreadyLoadedModule" is a different module (than the one we want to load)
return false;
}
if (this.BaseMaximumVersion != null && alreadyLoadedModule.Version > this.BaseMaximumVersion)
{
// "alreadyLoadedModule" is a different module (than the one we want to load)
return false;
}
if (BaseGuid != null && !alreadyLoadedModule.Guid.Equals(this.BaseGuid))
{
// "alreadyLoadedModule" is a different module (than the one we want to load)
return false;
}
return true;
return ModuleIntrinsics.IsModuleMatchingConstraints(
alreadyLoadedModule,
guid: BaseGuid,
requiredVersion: BaseRequiredVersion,
minimumVersion: BaseMinimumVersion,
maximumVersion: BaseMaximumVersion);
}
/// <summary>
@@ -5118,7 +5032,7 @@ namespace Microsoft.PowerShell.Commands
PSModuleInfo alreadyLoadedModule;
if (this.Context.Modules.ModuleTable.TryGetValue(modulePath, out alreadyLoadedModule))
{
if (this.IsModuleAlreadyLoaded(alreadyLoadedModule))
if (this.DoesAlreadyLoadedModuleSatisfyConstraints(alreadyLoadedModule))
{
if (this.BaseForce) // remove the previously imported module + return null (null = please proceed with regular import)
{
@@ -5256,17 +5170,7 @@ namespace Microsoft.PowerShell.Commands
// If the module has already been loaded, just emit it and continue...
Context.Modules.ModuleTable.TryGetValue(fileName, out module);
// TODO/FIXME: use IsModuleAlreadyLoaded to get consistent behavior
// if (!BaseForce &&
// IsModuleAlreadyLoaded(module) &&
// ((manifestProcessingFlags & ManifestProcessingFlags.LoadElements) == ManifestProcessingFlags.LoadElements))
if (!BaseForce && module != null &&
(BaseRequiredVersion == null || module.Version.Equals(BaseRequiredVersion)) &&
((BaseMinimumVersion == null && BaseMaximumVersion == null)
|| (BaseMaximumVersion != null && BaseMinimumVersion == null && module.Version <= BaseMaximumVersion)
|| (BaseMaximumVersion == null && BaseMinimumVersion != null && module.Version >= BaseMinimumVersion)
|| (BaseMaximumVersion != null && BaseMinimumVersion != null && module.Version >= BaseMinimumVersion && module.Version <= BaseMaximumVersion)) &&
(BaseGuid == null || module.Guid.Equals(BaseGuid)) && importingModule)
if (!BaseForce && importingModule && DoesAlreadyLoadedModuleSatisfyConstraints(module))
{
moduleFileFound = true;
module = Context.Modules.ModuleTable[fileName];
@@ -5311,12 +5215,7 @@ namespace Microsoft.PowerShell.Commands
{
moduleFileFound = true;
// Win8: 325243 - Added the version check so that we do not unload modules with the same name but different version
if (BaseForce && module != null &&
(this.BaseRequiredVersion == null || module.Version.Equals(this.BaseRequiredVersion)) &&
(BaseGuid == null || module.Guid.Equals(BaseGuid)))
// TODO/FIXME: use IsModuleAlreadyLoaded to get consistent behavior
// TODO/FIXME: (for example the checks above are not lookint at this.BaseMinimumVersion)
// if (BaseForce && IsModuleAlreadyLoaded(module))
if (BaseForce && DoesAlreadyLoadedModuleSatisfyConstraints(module))
{
RemoveModule(module);
}
@@ -5493,14 +5392,11 @@ namespace Microsoft.PowerShell.Commands
// This will allow the search to continue and load a different version of the module.
if (Context.Modules.ModuleTable.TryGetValue(fileName, out module))
{
if (BaseMinimumVersion != null && module.Version >= BaseMinimumVersion)
if (!ModuleIntrinsics.IsVersionMatchingConstraints(module.Version, minimumVersion: BaseMinimumVersion, maximumVersion: BaseMaximumVersion))
{
if (BaseMaximumVersion == null || module.Version <= BaseMaximumVersion)
{
found = false;
moduleFileFound = false;
return null;
}
found = false;
moduleFileFound = false;
return null;
}
}
}
@@ -410,21 +410,253 @@ namespace System.Management.Automation
return modulesMatched.OrderBy(m => m.Name).ToList();
}
/// <summary>
/// Check if a given module info object matches a given module specification.
/// </summary>
/// <param name="moduleInfo">The module info object to check.</param>
/// <param name="moduleSpec">The module specification to match the module info object against.</param>
/// <returns>True if the module info object meets all the constraints on the module specification, false otherwise.</returns>
internal static bool IsModuleMatchingModuleSpec(PSModuleInfo moduleInfo, ModuleSpecification moduleSpec)
{
if (moduleInfo != null && moduleSpec != null &&
moduleInfo.Name.Equals(moduleSpec.Name, StringComparison.OrdinalIgnoreCase) &&
(!moduleSpec.Guid.HasValue || moduleSpec.Guid.Equals(moduleInfo.Guid)) &&
((moduleSpec.Version == null && moduleSpec.RequiredVersion == null && moduleSpec.MaximumVersion == null)
|| (moduleSpec.RequiredVersion != null && moduleSpec.RequiredVersion.Equals(moduleInfo.Version))
|| (moduleSpec.MaximumVersion == null && moduleSpec.Version != null && moduleSpec.RequiredVersion == null && moduleSpec.Version <= moduleInfo.Version)
|| (moduleSpec.MaximumVersion != null && moduleSpec.Version == null && moduleSpec.RequiredVersion == null && ModuleCmdletBase.GetMaximumVersion(moduleSpec.MaximumVersion) >= moduleInfo.Version)
|| (moduleSpec.MaximumVersion != null && moduleSpec.Version != null && moduleSpec.RequiredVersion == null && ModuleCmdletBase.GetMaximumVersion(moduleSpec.MaximumVersion) >= moduleInfo.Version && moduleSpec.Version <= moduleInfo.Version)))
return IsModuleMatchingModuleSpec(out ModuleMatchFailure matchFailureReason, moduleInfo, moduleSpec);
}
/// <summary>
/// Check if a given module info object matches a given module specification.
/// </summary>
/// <param name="matchFailureReason">The constraint that caused the match failure, if any.</param>
/// <param name="moduleInfo">The module info object to check.</param>
/// <param name="moduleSpec">The module specification to match the module info object against.</param>
/// <returns>True if the module info object meets all the constraints on the module specification, false otherwise.</returns>
internal static bool IsModuleMatchingModuleSpec(out ModuleMatchFailure matchFailureReason, PSModuleInfo moduleInfo, ModuleSpecification moduleSpec)
{
if (moduleSpec == null)
{
return true;
matchFailureReason = ModuleMatchFailure.NullModuleSpecification;
return false;
}
return false;
return IsModuleMatchingConstraints(
out matchFailureReason,
moduleInfo,
moduleSpec.Name,
moduleSpec.Guid,
moduleSpec.RequiredVersion,
moduleSpec.Version,
moduleSpec.MaximumVersion == null ? null : ModuleCmdletBase.GetMaximumVersion(moduleSpec.MaximumVersion));
}
/// <summary>
/// Check if a given module info object matches the given constraints.
/// Constraints given as null are ignored.
/// </summary>
/// <param name="moduleInfo">The module info object to check.</param>
/// <param name="name">The name of the expected module.</param>
/// <param name="guid">The guid of the expected module.</param>
/// <param name="requiredVersion">The required version of the expected module.</param>
/// <param name="minimumVersion">The minimum required version of the expected module.</param>
/// <param name="maximumVersion">The maximum required version of the expected module.</param>
/// <returns>True if the module info object matches all given constraints, false otherwise.</returns>
internal static bool IsModuleMatchingConstraints(
PSModuleInfo moduleInfo,
string name = null,
Guid? guid = null,
Version requiredVersion = null,
Version minimumVersion = null,
Version maximumVersion = null)
{
return IsModuleMatchingConstraints(
out ModuleMatchFailure matchFailureReason,
moduleInfo,
name,
guid,
requiredVersion,
minimumVersion,
maximumVersion);
}
/// <summary>
/// Check if a given module info object matches the given constraints.
/// Constraints given as null are ignored.
/// </summary>
/// <param name="matchFailureReason">The reason for the module constraint match failing.</param>
/// <param name="moduleInfo">The module info object to check.</param>
/// <param name="name">The name of the expected module.</param>
/// <param name="guid">The guid of the expected module.</param>
/// <param name="requiredVersion">The required version of the expected module.</param>
/// <param name="minimumVersion">The minimum required version of the expected module.</param>
/// <param name="maximumVersion">The maximum required version of the expected module.</param>
/// <returns>True if the module info object matches all given constraints, false otherwise.</returns>
internal static bool IsModuleMatchingConstraints(
out ModuleMatchFailure matchFailureReason,
PSModuleInfo moduleInfo,
string name,
Guid? guid,
Version requiredVersion,
Version minimumVersion,
Version maximumVersion)
{
// Define that a null module does not meet any constraints
if (moduleInfo == null)
{
matchFailureReason = ModuleMatchFailure.NullModule;
return false;
}
return AreModuleFieldsMatchingConstraints(
out matchFailureReason,
moduleInfo.Name,
moduleInfo.Guid,
moduleInfo.Version,
name,
guid,
requiredVersion,
minimumVersion,
maximumVersion
);
}
/// <summary>
/// Check that given module fields meet any given constraints.
/// </summary>
/// <param name="moduleName">The name of the module to check.</param>
/// <param name="moduleGuid">The GUID of the module to check.</param>
/// <param name="moduleVersion">The version of the module to check.</param>
/// <param name="requiredName">The name the module must have, if any.</param>
/// <param name="requiredGuid">The GUID the module must have, if any.</param>
/// <param name="requiredVersion">The exact version the module must have, if any.</param>
/// <param name="minimumRequiredVersion">The minimum version the module may have, if any.</param>
/// <param name="maximumRequiredVersion">The maximum version the module may have, if any.</param>
/// <returns>True if the module parameters match all given constraints, false otherwise.</returns>
internal static bool AreModuleFieldsMatchingConstraints(
string moduleName = null,
Guid? moduleGuid = null,
Version moduleVersion = null,
string requiredName = null,
Guid? requiredGuid = null,
Version requiredVersion = null,
Version minimumRequiredVersion = null,
Version maximumRequiredVersion = null)
{
return AreModuleFieldsMatchingConstraints(
out ModuleMatchFailure matchFailureReason,
moduleName,
moduleGuid,
moduleVersion,
requiredName,
requiredGuid,
requiredVersion,
minimumRequiredVersion,
maximumRequiredVersion);
}
/// <summary>
/// Check that given module fields meet any given constraints.
/// </summary>
/// <param name="matchFailureReason">The reason the match failed, if any.</param>
/// <param name="moduleName">The name of the module to check.</param>
/// <param name="moduleGuid">The GUID of the module to check.</param>
/// <param name="moduleVersion">The version of the module to check.</param>
/// <param name="requiredName">The name the module must have, if any.</param>
/// <param name="requiredGuid">The GUID the module must have, if any.</param>
/// <param name="requiredVersion">The exact version the module must have, if any.</param>
/// <param name="minimumRequiredVersion">The minimum version the module may have, if any.</param>
/// <param name="maximumRequiredVersion">The maximum version the module may have, if any.</param>
/// <returns>True if the module parameters match all given constraints, false otherwise.</returns>
internal static bool AreModuleFieldsMatchingConstraints(
out ModuleMatchFailure matchFailureReason,
string moduleName,
Guid? moduleGuid,
Version moduleVersion,
string requiredName,
Guid? requiredGuid,
Version requiredVersion,
Version minimumRequiredVersion,
Version maximumRequiredVersion)
{
// If a name is required, check it matches
if (requiredName != null && !requiredName.Equals(moduleName, StringComparison.OrdinalIgnoreCase))
{
matchFailureReason = ModuleMatchFailure.Name;
return false;
}
// If a GUID is required, check it matches
if (requiredGuid != null && !requiredGuid.Equals(moduleGuid))
{
matchFailureReason = ModuleMatchFailure.Guid;
return false;
}
// Check the versions
return IsVersionMatchingConstraints(out matchFailureReason, moduleVersion, requiredVersion, minimumRequiredVersion, maximumRequiredVersion);
}
/// <summary>
/// Check that a given module version matches the required or minimum/maximum version constraints.
/// Null constraints are not checked.
/// </summary>
/// <param name="version">The module version to check. Must not be null</param>
/// <param name="requiredVersion">The version that the given version must be, if not null.</param>
/// <param name="minimumVersion">The minimum version that the given version must be greater than or equal to, if not null.</param>
/// <param name="maximumVersion">The maximum version that the given version must be less then or equal to, if not null.</param>
/// <returns>
/// True if the version matches the required version, or if it is absent, is between the minimum and maximum versions, and false otherwise.
/// </returns>
internal static bool IsVersionMatchingConstraints(
Version version,
Version requiredVersion = null,
Version minimumVersion = null,
Version maximumVersion = null)
{
return IsVersionMatchingConstraints(out ModuleMatchFailure matchFailureReason, version, requiredVersion, minimumVersion, maximumVersion);
}
/// <summary>
/// Check that a given module version matches the required or minimum/maximum version constraints.
/// Null constraints are not checked.
/// </summary>
/// <param name="matchFailureReason">The reason why the match failed.</param>
/// <param name="version">The module version to check. Must not be null</param>
/// <param name="requiredVersion">The version that the given version must be, if not null.</param>
/// <param name="minimumVersion">The minimum version that the given version must be greater than or equal to, if not null.</param>
/// <param name="maximumVersion">The maximum version that the given version must be less then or equal to, if not null.</param>
/// <returns>
/// True if the version matches the required version, or if it is absent, is between the minimum and maximum versions, and false otherwise.
/// </returns>
internal static bool IsVersionMatchingConstraints(
out ModuleMatchFailure matchFailureReason,
Version version,
Version requiredVersion = null,
Version minimumVersion = null,
Version maximumVersion = null)
{
Dbg.Assert(version != null, $"Caller to verify that {nameof(version)} is not null");
// If a RequiredVersion is given it overrides other version settings
if (requiredVersion != null)
{
matchFailureReason = ModuleMatchFailure.RequiredVersion;
return requiredVersion.Equals(version);
}
// Check the version is at least the minimum version
if (minimumVersion != null && version < minimumVersion)
{
matchFailureReason = ModuleMatchFailure.MinimumVersion;
return false;
}
// Check the version is at most the maximum version
if (maximumVersion != null && version > maximumVersion)
{
matchFailureReason = ModuleMatchFailure.MaximumVersion;
return false;
}
matchFailureReason = ModuleMatchFailure.None;
return true;
}
internal static Version GetManifestModuleVersion(string manifestPath)
@@ -1306,6 +1538,36 @@ namespace System.Management.Automation
}
} // ModuleIntrinsics
/// <summary>
/// Enumeration of reasons for a failure to match a module by constraints.
/// </summary>
internal enum ModuleMatchFailure
{
/// <summary>Match did not fail.</summary>
None,
/// <summary>Match failed because the module was null.</summary>
NullModule,
/// <summary>Module name did not match.</summary>
Name,
/// <summary>Module GUID did not match.</summary>
Guid,
/// <summary>Module version did not match the required version.</summary>
RequiredVersion,
/// <summary>Module version was lower than the minimum version.</summary>
MinimumVersion,
/// <summary>Module version was greater than the maximum version.</summary>
MaximumVersion,
/// <summary>The module specifcation passed in was null.</summary>
NullModuleSpecification,
}
/// <summary>
/// Used by Modules/Snapins to provide a hook to the engine for startup initialization
/// w.r.t compiled assembly loading.
@@ -865,7 +865,7 @@ Describe "Preloaded module specification checking" -Tags "Feature" {
{ Import-Module -FullyQualifiedName $modSpec -ErrorAction Stop } | Should -Throw -ErrorId 'Modules_ModuleWithVersionNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand'
}
It "Does not load the module with FullyQualifiedName from the manifest when ModuleVersion=<ModuleVersion>, MaximumVersion=<MaximumVersion>, RequiredVersion=<RequiredVersion>, Guid=<Guid>" -TestCases $guidFailCases -Pending {
It "Does not load the module with FullyQualifiedName from the manifest when ModuleVersion=<ModuleVersion>, MaximumVersion=<MaximumVersion>, RequiredVersion=<RequiredVersion>, Guid=<Guid>" -TestCases $guidFailCases {
param($ModuleVersion, $MaximumVersion, $RequiredVersion, $Guid)
$modSpec = New-ModuleSpecification -ModuleName $manifestPath -ModuleVersion $ModuleVersion -MaximumVersion $MaximumVersion -RequiredVersion $RequiredVersion -Guid $Guid
@@ -1088,7 +1088,7 @@ Describe "Preloaded modules with versioned directory version checking" -Tag "Fea
{ Import-Module -FullyQualifiedName $modSpec -ErrorAction Stop } | Should -Throw -ErrorId 'Modules_ModuleWithVersionNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand'
}
It "Does not load the module with FullyQualifiedName from the manifest when ModuleVersion=<ModuleVersion>, MaximumVersion=<MaximumVersion>, RequiredVersion=<RequiredVersion>, Guid=<Guid>" -TestCases $guidFailCases -Pending {
It "Does not load the module with FullyQualifiedName from the manifest when ModuleVersion=<ModuleVersion>, MaximumVersion=<MaximumVersion>, RequiredVersion=<RequiredVersion>, Guid=<Guid>" -TestCases $guidFailCases {
param($ModuleVersion, $MaximumVersion, $RequiredVersion, $Guid)
$modSpec = New-ModuleSpecification -ModuleName $manifestPath -ModuleVersion $ModuleVersion -MaximumVersion $MaximumVersion -RequiredVersion $RequiredVersion -Guid $Guid