PowerShell/src/Microsoft.PackageManagement/Implementation/PackageManagementService.cs
PowerShell Team c748652c34 Copy all mapped files from [SD:725290]
commit 8cec8f150da7583b7af5efbe2853efee0179750c
2016-07-28 23:23:03 -07:00

1614 lines
76 KiB
C#

//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
namespace Microsoft.PackageManagement.Internal.Implementation {
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.AccessControl;
using Api;
using PackageManagement.Implementation;
using PackageManagement.Packaging;
using Packaging;
using Providers;
using Utility.Collections;
using Utility.Extensions;
using Utility.Platform;
using Utility.Plugin;
using Utility.Versions;
using Win32;
using Directory = System.IO.Directory;
using File = System.IO.File;
#if CORECLR
using System.Management.Automation;
#endif
/// <summary>
/// The Client API is designed for use by installation hosts:
/// - PackageManagement Powershell Cmdlets
/// The Client API provides high-level consumer functions to support SDII functionality.
/// </summary>
internal class PackageManagementService : IPackageManagementService {
private static int _lastCallCount;
private static HashSet<string> _providersTriedThisCall;
private string[] _bootstrappableProviderNames;
private bool _initialized;
// well known, built in provider assemblies.
private readonly string[] _defaultProviders = {
Path.GetFullPath(CurrentAssemblyLocation), // load the providers from this assembly
"Microsoft.PackageManagement.MetaProvider.PowerShell.dll"
};
private readonly object _lockObject = new object();
private readonly IDictionary<string, IMetaProvider> _metaProviders = new Dictionary<string, IMetaProvider>(StringComparer.OrdinalIgnoreCase);
private readonly IDictionary<string, PackageProvider> _packageProviders = new Dictionary<string, PackageProvider>(StringComparer.OrdinalIgnoreCase);
internal readonly IDictionary<string, Archiver> Archivers = new Dictionary<string, Archiver>(StringComparer.OrdinalIgnoreCase);
internal readonly IDictionary<string, Downloader> Downloaders = new Dictionary<string, Downloader>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, List<PackageProvider>> _providerCacheTable = new Dictionary<string, List<PackageProvider>>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, byte[]> _providerFiles = new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase);
private string _baseDir;
internal bool InternalPackageManagementInstallOnly = false;
private readonly string _nuget ="NuGet";
internal enum ProviderOption
{
AllProvider = 0,
LatestVersion = 1,
}
internal Dictionary<string, List<PackageProvider>> ProviderCacheTable
{
get
{
return _providerCacheTable;
}
}
internal static string CurrentAssemblyLocation
{
get
{
#if !CORECLR
return Assembly.GetExecutingAssembly().Location;
#else
return typeof(PackageManagementService).GetTypeInfo().Assembly.ManifestModule.FullyQualifiedName;
#endif
}
}
internal string BaseDir {
get {
return _baseDir ?? (_baseDir = Path.GetDirectoryName(CurrentAssemblyLocation));
}
}
internal string[] BootstrappableProviderNames {
get {
return _bootstrappableProviderNames ?? new string[0];
}
set {
if (_bootstrappableProviderNames.IsNullOrEmpty()) {
_bootstrappableProviderNames = value;
}
}
}
internal IEnumerable<string> AutoLoadedAssemblyLocations {
get
{
var folder = Path.GetDirectoryName(CurrentAssemblyLocation);
if (!string.IsNullOrWhiteSpace(folder) && folder.DirectoryExists()) {
yield return folder;
}
}
}
internal IEnumerable<string> ProviderAssembliesLocation {
get {
var folder = SystemAssemblyLocation;
if (!string.IsNullOrWhiteSpace(folder) && folder.DirectoryExists()) {
yield return folder;
}
folder = UserAssemblyLocation;
if (!string.IsNullOrWhiteSpace(folder) && folder.DirectoryExists()) {
yield return folder;
}
}
}
#if CORECLR
private IEnumerable<string> PowerShellModulePath {
get {
var psModulePath = Environment.GetEnvironmentVariable("PSModulePath") ?? "";
var paths = psModulePath.Split(new char[] {';'}, StringSplitOptions.RemoveEmptyEntries).ToArray();
return paths;
}
}
#endif
internal string UserAssemblyLocation {
get {
try {
#if !CORECLR
var basepath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
#else
var basepath = System.Environment.GetEnvironmentVariable("LocalAppData");
#endif
if (string.IsNullOrWhiteSpace(basepath)) {
return null;
}
var path = Path.Combine(basepath, @"PackageManagement\ProviderAssemblies");
if (!Directory.Exists(path)) {
Directory.CreateDirectory(path);
}
return path;
} catch {
// if it can't be created, it's not the end of the world.
}
return null;
}
}
internal string SystemAssemblyLocation {
get {
try {
#if !CORECLR
var basepath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
#else
var basepath = System.Environment.GetEnvironmentVariable("ProgramFiles");
#endif
if (string.IsNullOrWhiteSpace(basepath)) {
return null;
}
var path = Path.Combine(basepath, @"PackageManagement\ProviderAssemblies");
if (!Directory.Exists(path)) {
Directory.CreateDirectory(path);
}
return path;
} catch {
// ignore non-existant directory for now.
}
return null;
}
}
public IEnumerable<PackageProvider> PackageProviders {
get {
return _packageProviders.Values;
}
}
public bool Initialize(IHostApi request) {
lock (_lockObject) {
if (!_initialized) {
LoadProviders(request);
_initialized = true;
}
}
return _initialized;
}
public int Version {
get {
return Constants.PackageManagementVersion;
}
}
public IEnumerable<string> ProviderNames {
get {
return _packageProviders.Keys;
}
}
public IEnumerable<string> AllProviderNames {
get {
if (BootstrappableProviderNames.IsNullOrEmpty()) {
return _packageProviders.Keys;
}
return _packageProviders.Where(p => p.Value != null && (p.Value.Features == null || !p.Value.Features.ContainsKey(Constants.Features.AutomationOnly)))
.Select(each => each.Key).Concat(BootstrappableProviderNames).Distinct(StringComparer.OrdinalIgnoreCase);
}
}
public IEnumerable<PackageProvider> SelectProvidersWithFeature(string featureName) {
return _packageProviders.Values.Where(each => each.Features.ContainsKey(featureName));
}
public IEnumerable<PackageProvider> SelectProvidersWithFeature(string featureName, string value) {
return _packageProviders.Values.Where(each => each.Features.ContainsKey(featureName) && each.Features[featureName].Contains(value));
}
public IEnumerable<PackageProvider> SelectProviders(string providerName, IHostApi hostApi) {
if (!string.IsNullOrWhiteSpace(providerName)) {
// match with wildcards
var results = _packageProviders.Values.Where(each => each.ProviderName.IsWildcardMatch(providerName)).ReEnumerable();
if (results.Any()) {
return results;
}
// If the provider is installed but not imported, let's import it
// we don't want import package provider via name to write errors, because in that case the subsequent call to bootstrapper provider will get cancelled.
var provider = ImportPackageProviderHelper(hostApi, providerName, null, null, null, false, false, false).ToArray();
if (provider.Any()) {
return provider;
}
if (hostApi != null && !providerName.ContainsWildcards()) {
// if the end user requested a provider that's not there. perhaps the bootstrap provider can find it.
if (RequirePackageProvider(null, providerName, Constants.MinVersion, hostApi)) {
// seems to think we found it.
if (_packageProviders.ContainsKey(providerName)) {
return _packageProviders[providerName].SingleItemAsEnumerable();
}
}
// SelectProviders() is iterating through the loaded provider list. As we still need to go through the
// unloaded provider list, we should not warn users yet at this point of time.
// If the provider is not found, eventually we will error out in SelectProviders()/cmdletbase.cs().
//hostApi.Warn(hostApi.FormatMessageString(Constants.Messages.UnknownProvider, providerName));
}
return Enumerable.Empty<PackageProvider>();
} else {
// If a user does not specify -provider or -provider name, we will bootstrap the nuget provider if it does not exist.
//Only find, install, uninstall, and save cmdlets requires the bootstrap.
var bootstrapNuGet = hostApi.GetOptionValues(Constants.BootstrapNuGet).FirstOrDefault();
if ((bootstrapNuGet != null) && bootstrapNuGet.EqualsIgnoreCase("true")) {
//check if the NuGet provider is already loaded
if (!_packageProviders.Keys.Any(each => each.EqualsIgnoreCase(_nuget))) {
//we'll bootstrap NuGet provider under the following cases:
//case 1: on a clean VM, type install-package foobar
//case 2: on a existing VM, if the nuget provider does not exist and type install-package foobar
//case 3: An existing VM has a old version of the NuGet installed, no bootstrap will occur. This means there is no changes
// to the user, unless he does 'install-packageprovider -name nuget -force'.
if (RequirePackageProvider(null, _nuget, Constants.MinVersion, hostApi)) {
// seems to think we found it.
if (_packageProviders.ContainsKey(_nuget)) {
return PackageProviders.Concat(_packageProviders[_nuget].SingleItemAsEnumerable());
}
}
}
}
}
return PackageProviders;
}
public IEnumerable<SoftwareIdentity> FindPackageByCanonicalId(string packageId, IHostApi hostApi) {
Uri pkgId;
if (Uri.TryCreate(packageId, UriKind.Absolute, out pkgId)) {
var segments = pkgId.Segments;
if (segments.Length > 0) {
var provider = SelectProviders(pkgId.Scheme, hostApi).FirstOrDefault();
if (provider != null) {
var name = Uri.UnescapeDataString(segments[0].Trim('/', '\\'));
var version = (segments.Length > 1) ? Uri.UnescapeDataString(segments[1]) : null;
var source = pkgId.Fragment.TrimStart('#');
var sources = (string.IsNullOrWhiteSpace(source) ? hostApi.Sources : Uri.UnescapeDataString(source).SingleItemAsEnumerable()).ToArray();
var host = new object[] {
new {
GetSources = new Func<IEnumerable<string>>(() => sources),
GetOptionValues = new Func<string, IEnumerable<string>>(key => key.EqualsIgnoreCase("FindByCanonicalId") ? new[] {"true"} : hostApi.GetOptionValues(key)),
GetOptionKeys = new Func<IEnumerable<string>>(() => hostApi.OptionKeys.ConcatSingleItem("FindByCanonicalId")),
},
hostApi,
}.As<IHostApi>();
return provider.FindPackage(name, version, null, null, host).Select(each => {
each.Status = Constants.PackageStatus.Dependency;
return each;
}).ReEnumerable();
}
}
}
return new SoftwareIdentity[0];
}
public bool RequirePackageProvider(string requestor, string packageProviderName, string minimumVersion, IHostApi hostApi) {
// check if the package provider is already installed
if (_packageProviders.ContainsKey(packageProviderName)) {
var current = _packageProviders[packageProviderName].Version;
if (current >= minimumVersion) {
return true;
}
}
var currentCallCount = hostApi.CallCount;
if (_lastCallCount >= currentCallCount) {
// we've already been here this call.
// are they asking for the same provider again?
if (_providersTriedThisCall.Contains(packageProviderName)) {
hostApi.Debug("Skipping RequirePackageProvider -- tried once this call previously.");
return false;
}
// remember this in case we come back again.
_providersTriedThisCall.Add(packageProviderName);
} else {
_lastCallCount = currentCallCount;
_providersTriedThisCall = new HashSet<string> {
packageProviderName
};
}
if (!hostApi.IsInteractive) {
hostApi.Debug("Skipping RequirePackageProvider due to not interactive");
// interactive indicates that the host can respond to queries -- this doesn't happen
// in powershell during tab-completion.
return false;
}
// no?
// ask the bootstrap provider if there is a package provider with that name available.
if (!_packageProviders.ContainsKey("Bootstrap")) {
return false;
}
var bootstrap = _packageProviders["Bootstrap"];
if (bootstrap == null) {
hostApi.Debug("Skipping RequirePackageProvider due to missing bootstrap provider");
return false;
}
var pkg = bootstrap.FindPackage(packageProviderName, null, minimumVersion, null, hostApi).OrderByDescending(p => p, SoftwareIdentityVersionComparer.Instance).GroupBy(package => package.Name).ToArray();
if (pkg.Length == 1) {
// Yeah? Install it.
var package = pkg[0].FirstOrDefault();
var metaWithProviderType = package.Meta.FirstOrDefault(each => each.ContainsKey("providerType"));
var providerType = metaWithProviderType == null ? "unknown" : metaWithProviderType.GetAttribute("providerType");
var destination = providerType == "assembly" ? (AdminPrivilege.IsElevated ? SystemAssemblyLocation : UserAssemblyLocation) : string.Empty;
var link = package.Links.FirstOrDefault(each => each.Relationship == "installationmedia");
var location = string.Empty;
if (link != null) {
location = link.HRef.ToString();
}
// what can't find an installationmedia link?
// todo: what should we say here?
if (hostApi.ShouldBootstrapProvider(requestor, package.Name, package.Version, providerType, location, destination)) {
var newRequest = hostApi.Extend<IHostApi>(new {
GetOptionValues = new Func<string, IEnumerable<string>>(key => {
if (key == "DestinationPath") {
return new[] {
destination
};
}
return new string[0];
})
});
var packagesInstalled = bootstrap.InstallPackage(package, newRequest).LastOrDefault();
if (packagesInstalled == null) {
// that's sad.
hostApi.Error(Constants.Messages.FailedProviderBootstrap, ErrorCategory.InvalidOperation.ToString(), package.Name, hostApi.FormatMessageString(Constants.Messages.FailedProviderBootstrap, package.Name));
return false;
}
// so it installed something
// we must tell the plugin loader to reload the plugins again.
LoadProviders(hostApi);
return true;
}
}
return false;
}
/// <summary>
/// Get all available providers.
/// </summary>
/// <param name="request"></param>
/// <param name="providerNames">providers to be loaded.</param>
public IEnumerable<PackageProvider> GetAvailableProviders(IHostApi request, string[] providerNames) {
//Handling two cases
//1. Both "-Name" and "-Listavailable" exist
//2. "-Listavailable" only.
return providerNames.IsNullOrEmpty() ?
GetAvailableProvider(request, String.Empty) :
providerNames.SelectMany(each => GetAvailableProvider(request, each));
}
/// <summary>
/// Get available provider. It handles "Get-Packageprovider -Name -ListAvailable" and "Get-Packageprovider -ListAvailable"
/// </summary>
/// <param name="request"></param>
/// <param name="providerName">Name of the provider to be loaded.</param>
private IEnumerable<PackageProvider> GetAvailableProvider(IHostApi request, string providerName) {
//This method is called when get-packageprovider -ListAvailable
//We will return whatever we can find
ScanForAvailableProviders(request, providerName, null, null, null);
//Check if the provider is in the cache
var packageProviders = GetPackageProviderFromCacheTable(providerName).ReEnumerable();
return packageProviders.Any() ? packageProviders.Where(p => p.Features == null || !p.Features.ContainsKey(Constants.Features.AutomationOnly))
: Enumerable.Empty<PackageProvider>();
}
private IEnumerable<PackageProvider> GetPackageProviderFromCacheTable(string providerName)
{
// latest version of the providers will be displayed first
var cacheList = (string.IsNullOrWhiteSpace(providerName)) ? _providerCacheTable.SelectMany(each => each.Value.OrderByDescending(provider => provider.Version)).WhereNotNull()
: _providerCacheTable.Where(each => each.Key.IsWildcardMatch(providerName)).SelectMany(each => each.Value.OrderByDescending(provider => provider.Version)).WhereNotNull();
return cacheList;
}
private void ScanForAvailableProviders(IHostApi request,
string providerName,
Version requiredVersion,
Version minimumVersion,
Version maximumVersion,
bool shouldRefreshCache = false,
bool logWarning = true) {
ResetProviderCachetable();
//search assemblies from the well-known locations and update the internal provider cache table
var providerAssemblies = ScanAllProvidersFromProviderAssembliesLocation(request, providerName, requiredVersion, minimumVersion, maximumVersion, ProviderOption.AllProvider).ToArray();
//find out which one are from root directory. Because we cannot tell its version and provider name
//we need to load it.
var files = providerAssemblies.Where(each => ProviderAssembliesLocation.ContainsIgnoreCase(Path.GetDirectoryName(each))).ReEnumerable();
//after the cache table gets cleaned, we need to load these assemblies sitting at the top level folder
files.ParallelForEach(providerAssemblyName => {
lock (_providerFiles) {
if (_providerFiles.ContainsKey(providerAssemblyName)) {
//remove the same file from the _providerFiles if any, so it gets reentered
//to the cache table.
_providerFiles.Remove(providerAssemblyName);
}
}
LoadProviderAssembly(request, providerAssemblyName, false);
});
var powerShellMetaProvider = GetMetaProviderObject(request);
if (powerShellMetaProvider == null) {
return;
}
//Get available powershell providers
powerShellMetaProvider.RefreshProviders(request.As<IRequest>(), providerName, requiredVersion, minimumVersion, maximumVersion, logWarning);
}
/// <summary>
/// Import a package provider.
/// </summary>
/// <param name="request"></param>
/// <param name="providerName">Provider name or file path</param>
/// <param name="requiredVersion">The provider version to be loaded</param>
/// <param name="minimumVersion">The minimum version of the provider to be loaded</param>
/// <param name="maximumVersion">The maximum version of the provider to be loaded</param>
/// <param name="isPathRooted">Whether the 'providerName' is path or name</param>
/// <param name="force">Whether -force is specified</param>
/// <returns></returns>
public IEnumerable<PackageProvider> ImportPackageProvider(IHostApi request,
string providerName,
Version requiredVersion,
Version minimumVersion,
Version maximumVersion,
bool isPathRooted,
bool force) {
return ImportPackageProviderHelper(request, providerName, requiredVersion, minimumVersion, maximumVersion, isPathRooted, force, true);
}
/// <summary>
/// Import a package provider.
/// </summary>
/// <param name="request"></param>
/// <param name="providerName">Provider name or file path</param>
/// <param name="requiredVersion">The provider version to be loaded</param>
/// <param name="minimumVersion">The minimum version of the provider to be loaded</param>
/// <param name="maximumVersion">The maximum version of the provider to be loaded</param>
/// <param name="isPathRooted">Whether the 'providerName' is path or name</param>
/// <param name="force">Whether -force is specified</param>
/// <param name="throwErrorWhenImportWithName">if true then we use write error when there is an
/// error when importing with name</param>
/// <returns></returns>
private IEnumerable<PackageProvider> ImportPackageProviderHelper(IHostApi request,
string providerName,
Version requiredVersion,
Version minimumVersion,
Version maximumVersion,
bool isPathRooted,
bool force,
bool throwErrorWhenImportWithName) {
request.Debug(string.Format(CultureInfo.CurrentCulture, "Calling ImportPackageProvider. providerName = '{0}', requiredVersion='{1}', minimumVersion = '{2}', maximumVersion='{3}'",
providerName, requiredVersion, minimumVersion, maximumVersion));
if (string.IsNullOrWhiteSpace(providerName)) {
return Enumerable.Empty<PackageProvider>();
}
if (providerName.ContainsWildcards()) {
request.Error(Constants.Messages.InvalidParameter, ErrorCategory.InvalidData.ToString(), providerName, string.Format(CultureInfo.CurrentCulture, Resources.Messages.InvalidParameter, "Import-PackageProvider"));
return Enumerable.Empty<PackageProvider>();
}
if (isPathRooted) {
if (!File.Exists(providerName)) {
request.Error(Constants.Messages.InvalidFilename, ErrorCategory.InvalidData.ToString(), providerName, string.Format(CultureInfo.CurrentCulture, Resources.Messages.FileNotFound, providerName));
return Enumerable.Empty<PackageProvider>();
}
//Check if the file type is supported: .dll, .exe, or .psm1
if (!Constants.SupportedAssemblyTypes.Any(each => each.EqualsIgnoreCase(Path.GetExtension(providerName)))) {
var fileTypes = Constants.SupportedAssemblyTypes.Aggregate(string.Empty, (current, each) => current + " " + each);
request.Error(Constants.Messages.InvalidFilename, ErrorCategory.InvalidData.ToString(), providerName, string.Format(CultureInfo.CurrentCulture, Resources.Messages.InvalidFileType, providerName, fileTypes));
return Enumerable.Empty<PackageProvider>();
}
}
var providers = isPathRooted ? ImportPackageProviderViaPath(request, providerName, requiredVersion, minimumVersion, maximumVersion, force)
: ImportPackageProviderViaName(request, providerName, requiredVersion, minimumVersion, maximumVersion, force, throwErrorWhenImportWithName);
return providers;
}
private IEnumerable<PackageProvider> ImportPackageProviderViaPath(IHostApi request,
string providerPath,
Version requiredVersion,
Version minimumVersion,
Version maximumVersion,
bool force) {
request.Debug(string.Format(CultureInfo.CurrentCulture, "Calling ImportPackageProviderViaPath. providerName = '{0}', requiredVersion='{1}', minimumVersion = '{2}', maximumVersion='{3}'",
providerPath, requiredVersion, minimumVersion, maximumVersion));
var extension = Path.GetExtension(providerPath);
if (extension != null && extension.EqualsIgnoreCase(".psm1")) {
//loading the PowerShell provider
request.Verbose(string.Format(CultureInfo.CurrentCulture, Resources.Messages.LoadingPowerShellModule, providerPath));
return ImportPowerShellProvider(request, providerPath, requiredVersion, force);
}
//loading non-PowerShell providers
request.Verbose(string.Format(CultureInfo.CurrentCulture, Resources.Messages.LoadingAssembly, providerPath));
var loaded = LoadProviderAssembly(request, providerPath, force);
if (loaded) {
return _packageProviders.Where(p => p.Value.ProviderPath.EqualsIgnoreCase(providerPath)).Select(each => each.Value);
}
return Enumerable.Empty<PackageProvider>();
}
private IEnumerable<PackageProvider> ImportPackageProviderViaName(IHostApi request,
string providerName,
Version requiredVersion,
Version minimumVersion,
Version maximumVersion,
bool force,
bool throwErrorWhenImportWithName) {
request.Debug(string.Format(CultureInfo.CurrentCulture, "Calling ImportPackageProviderViaName. providerName = '{0}', requiredVersion='{1}', minimumVersion = '{2}', maximumVersion='{3}'",
providerName, requiredVersion, minimumVersion, maximumVersion));
//Check if the module or assembly is already loaded
//key = path, value = version
HashSet<KeyValuePair<string, FourPartVersion>> refreshingProvidersPaths = new HashSet<KeyValuePair<string, FourPartVersion>>();
foreach (var provider in _packageProviders) {
if (provider.Key.IsWildcardMatch(providerName)) {
//found the provider with the same name is already loaded
if (force) {
// if -force is present and required version is specified, we will enforce that the loaded provider version must match the required version
if ((requiredVersion != null && provider.Value.Version == (FourPartVersion)requiredVersion)
// if -force is specified and no version information is provided, we will re-import directly from the path of the loaded provider
||(requiredVersion == null && maximumVersion == null && minimumVersion == null))
{
refreshingProvidersPaths.Add(new KeyValuePair<string, FourPartVersion>(_packageProviders[provider.Key].ProviderPath, _packageProviders[provider.Key].Version));
}
request.Verbose(string.Format(CultureInfo.CurrentCulture, Resources.Messages.ReImportProvider, provider.Key));
} else {
request.Verbose(string.Format(CultureInfo.CurrentCulture, Resources.Messages.ProviderImportedAlready, provider.Key));
return Enumerable.Empty<PackageProvider>();
}
}
}
//reload the assembly
foreach (var providerPath in refreshingProvidersPaths) {
var providers = ImportPackageProviderViaPath(request, providerPath.Key, providerPath.Value, minimumVersion, maximumVersion, force);
return providers;
}
IEnumerable<PackageProvider> results = null;
// if user doesn't have all the available providers in the cache,
// then there is a chance that we will miss the latest version of the provider
// so we will only try to search from the cache table without refreshing it
// if the user does not provide -force and either maximum or minimum version.
if (!force || (maximumVersion == null && minimumVersion == null))
{
//check if the provider is in the cache table
results = FindMatchedProvidersFromInternalCacheTable(request, providerName, requiredVersion, minimumVersion, maximumVersion, force).ToArray();
if (results.Any())
{
return results;
}
}
//If the provider is not in the cache list, rescan for providers
ScanForAvailableProviders(request, providerName, requiredVersion, minimumVersion, maximumVersion, true, false);
results = FindMatchedProvidersFromInternalCacheTable(request, providerName, requiredVersion, minimumVersion, maximumVersion, force).ToArray();
if (!results.Any()) {
if (throwErrorWhenImportWithName)
{
request.Error(Constants.Messages.NoMatchFoundForCriteria, ErrorCategory.InvalidData.ToString(),
providerName, string.Format(CultureInfo.CurrentCulture, Resources.Messages.NoMatchFoundForCriteria, providerName));
}
else
{
request.Verbose(string.Format(CultureInfo.CurrentCulture, Resources.Messages.NoMatchFoundForCriteria, providerName));
}
} else {
return results;
}
return Enumerable.Empty<PackageProvider>();
}
private IEnumerable<PackageProvider> FindMatchedProvidersFromInternalCacheTable(
IHostApi request,
string providerName,
Version requiredVersion,
Version minimumVersion,
Version maximumVersion,
bool force) {
//Search from the internal table to see if we can the matched provider
//check if the provider name matches
var providersTable = _providerCacheTable.Where(each => each.Key.IsWildcardMatch(providerName))
.Select(each => each.Value).ToArray();
//check if version matches
var providers = providersTable.Select(list => list.Where(each => {
bool foundMatch = true;
if (requiredVersion != null) {
return each.Version.Equals(requiredVersion);
}
if (minimumVersion != null) {
foundMatch = each.Version >= (FourPartVersion)minimumVersion;
}
if (maximumVersion != null) {
foundMatch &= each.Version <= (FourPartVersion)maximumVersion;
}
return foundMatch;
}).Select(each => each)).ToArray();
var selectedProviders = providers.Select(each => each.OrderByDescending(p => p.Version).FirstOrDefault()).WhereNotNull();
foreach (var instance in selectedProviders) {
if (instance.IsLoaded) {
//Initialize the provider
instance.Initialize(request);
//Add it to the provider list that all imported and in use
_packageProviders.AddOrSet(instance.ProviderName, instance);
request.Verbose(string.Format(Resources.Messages.ImportPackageProvider, instance.ProviderName));
yield return instance;
} else {
if (Path.GetExtension(instance.ProviderPath).EqualsIgnoreCase(".psm1")) {
//it's a powershell provider
var psProviders = ImportPowerShellProvider(request, instance.ProviderPath, instance.Version, shouldRefreshCache: force);
foreach (var p in psProviders) {
yield return p;
}
} else {
LoadProviderAssembly(request, instance.ProviderPath, shouldRefreshCache: force);
var foo = _packageProviders.Where(each => each.Key.IsWildcardMatch(providerName));
foreach (var p in foo) {
yield return p.Value;
}
}
}
}
}
private IEnumerable<PackageProvider> ImportPowerShellProvider(IHostApi request, string modulePath, Version requiredVersion, bool shouldRefreshCache)
{
request.Debug(string.Format(CultureInfo.CurrentCulture, "Calling ImportPowerShellProvider. providerName = '{0}', requiredVersion='{1}'",
modulePath, requiredVersion));
var powerShellMetaProvider = GetMetaProviderObject(request);
if (powerShellMetaProvider == null) {
yield break;
}
//providerName can be a file path or name.
var instances = powerShellMetaProvider.LoadAvailableProvider(request.As<IRequest>(), modulePath, requiredVersion, shouldRefreshCache).ReEnumerable();
if (!instances.Any()) {
//A provider is not found
request.Error(Constants.Messages.UnknownProvider, ErrorCategory.InvalidOperation.ToString(),
modulePath, string.Format(Resources.Messages.UnknownProvider, modulePath));
yield break;
}
foreach (var instance in instances) {
//Register the provider
var provider = instance.As<PackageProvider>();
if (provider != null) {
//initialize the actual powershell package provider
if (provider.Provider == null) {
continue;
}
provider.Provider.InitializeProvider(request.As<IRequest>());
AddToProviderCacheTable(provider.ProviderName, provider);
//initialize the warpper package provider
provider.Initialize(request);
// addOrSet locks the collection anyway.
_packageProviders.AddOrSet(provider.ProviderName, provider);
yield return provider;
}
}
}
private IMetaProvider GetMetaProviderObject(IHostApi request)
{
//retrieve the powershell metaprovider object
if (_metaProviders.ContainsKey("PowerShell")) {
var powerShellMetaProvider = _metaProviders["PowerShell"];
if (powerShellMetaProvider != null) {
return powerShellMetaProvider;
}
}
request.Verbose(string.Format(CultureInfo.CurrentCulture, Resources.Messages.FailedPowerShellMetaProvider));
return null;
}
private bool CompareProvider(PackageProvider p1, PackageProvider p2) {
if (p1 == null && p2 == null) {
return true;
}
if (p1 == null || p2 == null) {
return false;
}
if ((p1.Name != null) && (p1.Name.EqualsIgnoreCase(p2.Name)) && (p1.ProviderName != null && p1.ProviderName.EqualsIgnoreCase(p2.ProviderName) && p1.Version == p2.Version)) {
return true;
}
return false;
}
internal void AddToProviderCacheTable(string name, PackageProvider provider) {
lock (_providerCacheTable) {
if (_providerCacheTable.ContainsKey(name)) {
var list = _providerCacheTable[name];
var index = list.FindIndex(each => CompareProvider(each, provider));
if (index != -1) {
//overwrite the cache only if the provider is loaded but the existing one not loaded
if (!list[index].IsLoaded && provider.IsLoaded) {
list[index] = provider;
}
} else {
_providerCacheTable[name].Add(provider);
}
} else {
var entry = new List<PackageProvider> {
provider
};
_providerCacheTable.Add(name, entry);
}
}
}
private void ResetProviderCachetable() {
foreach (var list in _providerCacheTable.Values.WhereNotNull()) {
list.Clear();
}
_providerCacheTable.Clear();
_packageProviders.ParallelForEach(each => AddToProviderCacheTable(each.Key, each.Value));
}
//Scan through the well-known providerAssemblies folder to find the providers that met the condition.
internal IEnumerable<string> ScanAllProvidersFromProviderAssembliesLocation(
IHostApi request,
string providerName,
Version requiredVersion,
Version minimumVersion,
Version maximumVersion,
ProviderOption providerOption = ProviderOption.LatestVersion) {
//if provider is installed in providername\version format
var providerFolder = ProviderAssembliesLocation.Distinct(new PathEqualityComparer(PathCompareOption.Full)).SelectMany(Directory.EnumerateDirectories);
foreach (var providerNameFolder in providerFolder) {
var name = Path.GetFileName(providerNameFolder);
//check the providername folder
if (!string.IsNullOrWhiteSpace(providerName)) {
if (string.IsNullOrWhiteSpace(providerNameFolder)) {
continue;
}
if (string.IsNullOrWhiteSpace(name) || !name.IsWildcardMatch(providerName)) {
continue;
}
}
var selectedVersions = Directory.EnumerateDirectories(providerNameFolder).Select(versionFolder => {
//check if the version is in a valid format. Ver will be 0 if TryParse fails and it won't be selected
Version ver;
if (System.Version.TryParse(Path.GetFileName(versionFolder), out ver)) {
return new {
folder = versionFolder,
version = (FourPartVersion)ver
};
}
return null;
}).Where(each => each != null && each.version > 0L);
selectedVersions = selectedVersions.Where(eachVersion => {
if ((requiredVersion == null) || eachVersion.version == (FourPartVersion)requiredVersion) {
if ((minimumVersion == null) || eachVersion.version >= (FourPartVersion)minimumVersion) {
if ((maximumVersion == null) || eachVersion.version <= (FourPartVersion)maximumVersion) {
return true;
}
}
}
return false;
});
//Get the version folders
var versionFolders = (providerOption == ProviderOption.AllProvider) ?
selectedVersions.Select(each => each.folder).Where(Directory.Exists) :
new[] {selectedVersions.OrderByDescending(each => each.version).Select(each => each.folder).FirstOrDefault(Directory.Exists)};
foreach (var assemblyFolder in versionFolders.WhereNotNull()) {
//we reached the provider assembly file path now
var files = Directory.EnumerateFiles(assemblyFolder)
.Where(file => (file != null) && (Path.GetExtension(file).EqualsIgnoreCase(".dll") || Path.GetExtension(file).EqualsIgnoreCase(".exe"))
// we only check for dll that has manifest attached to it. (In case there are supporting assemblies in this folder)
&& Manifest.LoadFrom(file).Any(manifest => Swidtag.IsSwidtag(manifest) && new Swidtag(manifest).IsApplicable(new Hashtable())))
.ToArray();
//if found more than one dll with manifest is installed under a version folder, this is not allowed. warning here as enumerating for providers should continue
if (files.Any() && files.Count() > 1) {
request.Warning(string.Format(CultureInfo.CurrentCulture, Resources.Messages.SingleAssemblyAllowed, files.JoinWithComma()));
continue;
}
// find modules that have the provider manifests
var filelist = files.Where(each => Manifest.LoadFrom(each).Any(manifest => Swidtag.IsSwidtag(manifest) && new Swidtag(manifest).IsApplicable(new Hashtable())));
if (!filelist.Any()) {
continue;
}
var version = Path.GetFileName(assemblyFolder);
var defaultPkgProvider = new DefaultPackageProvider(name, version);
var providerPath = files.FirstOrDefault();
var pkgProvider = new PackageProvider(defaultPkgProvider)
{
ProviderPath = providerPath,
Version = version,
IsLoaded = false
};
pkgProvider.SetSwidTag(providerPath);
AddToProviderCacheTable(name, pkgProvider);
yield return providerPath;
}
}
//check if assembly is installed at the top leverl folder.
var providerFiles = ProviderAssembliesLocation.Distinct(new PathEqualityComparer(PathCompareOption.Full)).SelectMany(Directory.EnumerateFiles)
.Where(each => each.FileExists() && (Path.GetExtension(each).EqualsIgnoreCase(".dll") || Path.GetExtension(each).EqualsIgnoreCase(".exe"))).ReEnumerable();
// found the assemblies at the top level folder.
// if a user is looking for a specific version & provider name, we are not be able to know the provider name or version without loading it.
// Thus, for the top level providers, we just need to load them for the backward compatibility.
if (providerFiles.Any()) {
request.Verbose(string.Format(CultureInfo.CurrentCulture, Resources.Messages.ProviderNameAndVersionNotAvailableFromFilePath, providerFiles.JoinWithComma()));
foreach (var provider in providerFiles) {
//the provider is installed at the top level.
// find modules that have the provider manifests
if (Manifest.LoadFrom(provider).Any(manifest => Swidtag.IsSwidtag(manifest) && new Swidtag(manifest).IsApplicable(new Hashtable()))) {
yield return provider;
}
}
}
}
//Return all providers under the providerAssemblies folder
internal IEnumerable<string> AllProvidersFromProviderAssembliesLocation(IHostApi request) {
try {
return ScanAllProvidersFromProviderAssembliesLocation(request, null, null, null, null, ProviderOption.AllProvider).WhereNotNull().ToArray();
} catch (Exception ex) {
request.Debug(ex.Message);
}
return Enumerable.Empty<string>();
}
//return the providers with latest version under the providerAssemblies folder
//This method only gets called during the initialization, i.e. LoadProviders().
private IEnumerable<string> ProvidersWithLatestVersionFromProviderAssembliesLocation(IHostApi request) {
try {
var providerPaths = ScanAllProvidersFromProviderAssembliesLocation(request, null, null, null, null, ProviderOption.LatestVersion).WhereNotNull().ToArray();
var notRootAssemblies = providerPaths.Where(each => !ProviderAssembliesLocation.ContainsIgnoreCase(Path.GetDirectoryName(each))).ToArray();
var rootAssemblies = providerPaths.Where(each => ProviderAssembliesLocation.ContainsIgnoreCase(Path.GetDirectoryName(each))).ToArray();
var equalityComparer = new PathEqualityComparer(PathCompareOption.File);
//return the assemblies that are installed not directly under ProviderAssemblies root folder.
//For the assemblies under the root directory, we need to check further if the provider that has the later version
//installed under providername\version folder
//Convention: the providers are installed under providerassemblies\providername\version folder has the later version
//than those at the top root folder.
var assembliesUnderRootFolder = rootAssemblies.Where(rootPath => !notRootAssemblies.Any(element => equalityComparer.Equals(element, rootPath)));
//for these assemblies not under the providerassemblies root folders but they have the same provider names, we return the latest version
var assembliesUnderVersionFolder = notRootAssemblies.GroupBy(Path.GetFileName).Select(
each => each.OrderByDescending(file => {
var versionFolder = Path.GetDirectoryName(file);
Version ver;
return !System.Version.TryParse(Path.GetFileName(versionFolder), out ver) ? new Version("0.0") : ver;
}).FirstOrDefault()).WhereNotNull();
//filter out the old nuget-anycpu if exists
assembliesUnderRootFolder = assembliesUnderRootFolder.Where(rootPath => !assembliesUnderVersionFolder.Any(element => new PathEqualityComparer(PathCompareOption.Nuget).Equals(element, rootPath)));
return assembliesUnderVersionFolder.Concat(assembliesUnderRootFolder);
}
catch (Exception ex)
{
request.Debug(ex.Message);
}
return Enumerable.Empty<string>();
}
/// <summary>
/// This initializes the provider registry with the list of package providers.
/// (currently a hardcoded list, soon, registry driven)
/// </summary>
/// <param name="request"></param>
internal void LoadProviders(IHostApi request) {
if (request == null) {
throw new ArgumentNullException("request");
}
//looks like registry needs to be here for supporting .msi packages
var providerAssemblies = (_initialized ? Enumerable.Empty<string>() : _defaultProviders)
.Concat(GetProvidersFromRegistry(Registry.LocalMachine, "SOFTWARE\\MICROSOFT\\PACKAGEMANAGEMENT"))
.Concat(GetProvidersFromRegistry(Registry.CurrentUser, "SOFTWARE\\MICROSOFT\\PACKAGEMANAGEMENT"));
providerAssemblies = providerAssemblies.Concat(ProvidersWithLatestVersionFromProviderAssembliesLocation(request));
#if DEEP_DEBUG
providerAssemblies = providerAssemblies.ToArray();
foreach (var each in providerAssemblies) {
request.Debug("possible assembly: {0}".format(each));
}
#endif
// find modules that have manifests
// todo: expand this out to validate the assembly is ok for this instance of PackageManagement.
providerAssemblies = providerAssemblies.Where(each => Manifest.LoadFrom(each).Any(manifest => Swidtag.IsSwidtag(manifest) && new Swidtag(manifest).IsApplicable(new Hashtable())));
// add inbox assemblies (don't require manifests, because they are versioned with the core)
#if !COMMUNITY_BUILD
// todo: these should just be strong-named references. for now, just load them from the same directory.
providerAssemblies = providerAssemblies.Concat(new[] {
Path.Combine(BaseDir, "Microsoft.PackageManagement.MetaProvider.PowerShell.dll"),
Path.Combine(BaseDir, "Microsoft.PackageManagement.ArchiverProviders.dll"),
Path.Combine(BaseDir, "Microsoft.PackageManagement.CoreProviders.dll"),
#if !CORECLR
Path.Combine(BaseDir, "Microsoft.PackageManagement.MsuProvider.dll"),
Path.Combine(BaseDir, "Microsoft.PackageManagement.MsiProvider.dll")
#endif
});
#endif
#if DEEP_DEBUG
providerAssemblies = providerAssemblies.ToArray();
foreach (var each in providerAssemblies) {
request.Debug("possible assembly with manifest: {0}".format(each));
}
#endif
providerAssemblies = providerAssemblies.OrderByDescending(each => {
try {
// try to get a version from the file first
return (ulong)(FourPartVersion)FileVersionInfo.GetVersionInfo(each);
} catch {
// otherwise we can't make a distinction.
return (ulong)0;
}
});
providerAssemblies = providerAssemblies.Distinct(new PathEqualityComparer(PathCompareOption.FileWithoutExtension));
// there is no trouble with loading providers concurrently.
#if DEEP_DEBUG
providerAssemblies.SerialForEach(providerAssemblyName => {
#else
providerAssemblies.ParallelForEach(providerAssemblyName => {
#endif
LoadProviderAssembly(request, providerAssemblyName, false);
});
#if DEEP_DEBUG
WaitForDebugger();
#endif
}
#if DEEP_DEBUG
internal void WaitForDebugger() {
if (!System.Diagnostics.Debugger.IsAttached) {
Console.Beep(500, 2000);
while (!System.Diagnostics.Debugger.IsAttached) {
System.Threading.Thread.Sleep(1000);
Console.Beep(500, 200);
}
}
}
#endif
/// <summary>
/// Dynamic providers are the ones that are not installed with the core itself.
/// </summary>
internal IEnumerable<PackageProvider> DynamicProviders {
get {
return _packageProviders.Values.Where(each => !each.ProviderPath.StartsWith(BaseDir, StringComparison.OrdinalIgnoreCase));
}
}
private static IEnumerator<string> GetProvidersFromRegistry(RegistryKey registryKey, string p) {
RegistryKey key;
try {
#if !CORECLR
key = registryKey.OpenSubKey(p, RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey);
#else
key = registryKey.OpenSubKey(p);
#endif
} catch {
yield break;
}
if (key == null) {
yield break;
}
foreach (var name in key.GetValueNames()) {
yield return key.GetValue(name).ToString();
}
}
public IEnumerable<PackageSource> GetAllSourceNames(IHostApi request) {
return _packageProviders.Values.SelectMany(each => each.ResolvePackageSources(request));
}
/// <summary>
/// Searches for the assembly, interrogates it for it's providers and then proceeds to load
/// </summary>
/// <param name="request"></param>
/// <param name="providerAssemblyName"></param>
/// <param name="shouldRefreshCache"></param>
/// <returns></returns>
internal bool LoadProviderAssembly(IHostApi request, string providerAssemblyName, bool shouldRefreshCache) {
request.Debug(request.FormatMessageString("Trying provider assembly: {0}", providerAssemblyName));
var assemblyPath = FindAssembly(providerAssemblyName);
if (assemblyPath != null) {
try {
byte[] hash = null;
using (var stream = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read)) {
hash = System.Security.Cryptography.MD5.Create().ComputeHash(stream);
}
lock (_providerFiles) {
if (_providerFiles.ContainsKey(assemblyPath)) {
// have we tried this file before?
//if the exactly same provider is loaded already, skip the processed assembly.
if (_providerFiles[assemblyPath].SequenceEqual(hash) && !shouldRefreshCache) {
// and it's the exact same file?
request.Verbose(string.Format(CultureInfo.CurrentCulture, Resources.Messages.SkippingProcessedAssembly, assemblyPath));
return false;
}
// it's a different file in the same path? -force exists?
// we're gonna let it try the new file.
_providerFiles.Remove(assemblyPath);
}
request.Debug(request.FormatMessageString("Attempting loading of assembly: {0}", assemblyPath));
// record that this file is being loaded.
_providerFiles.Add(assemblyPath, hash);
}
if (AcquireProviders(assemblyPath, request, shouldRefreshCache)) {
request.Debug(request.FormatMessageString("SUCCESS provider assembly: {0}", providerAssemblyName));
return true;
}
} catch (Exception e) {
e.Dump();
lock (_providerFiles) {
// can't create hash from file?
// we're not going to try and load this.
// all we can do is record the name.
if (!_providerFiles.ContainsKey(assemblyPath)) {
_providerFiles.Add(assemblyPath, new byte[0]);
}
}
}
}
request.Debug(request.FormatMessageString("FAILED provider assembly: {0}", providerAssemblyName));
return false;
}
/// <summary>
/// PROTOTYPE -- extremely simplified assembly locator.
/// </summary>
/// <param name="assemblyName"></param>
/// <returns></returns>
private string FindAssembly(string assemblyName) {
try {
string fullPath;
// is the name given a strong name?
if (assemblyName.Contains(',')) {
// looks like a strong name
// todo: not there yet...
return null;
}
// is it a path?
if (assemblyName.Contains('\\') || assemblyName.Contains('/') || assemblyName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || assemblyName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) {
fullPath = Path.GetFullPath(assemblyName);
if (File.Exists(fullPath)) {
return fullPath;
}
if (File.Exists(fullPath + ".dll")) {
return fullPath;
}
// lets see if the assembly name is in the same directory as the current assembly...
try {
fullPath = Path.Combine(Path.GetDirectoryName(CurrentAssemblyLocation), assemblyName);
if (File.Exists(fullPath)) {
return fullPath;
}
if (File.Exists(fullPath + ".dll")) {
return fullPath;
}
} catch {
}
}
// must be just just a plain name.
// todo: search the GAC too?
// search the local folder.
fullPath = Path.GetFullPath(assemblyName + ".dll");
if (File.Exists(fullPath)) {
return fullPath;
}
// try next to where we are.
fullPath = Path.Combine(Path.GetDirectoryName(CurrentAssemblyLocation), assemblyName + ".dll");
if (File.Exists(fullPath)) {
return fullPath;
}
} catch (Exception e) {
e.Dump();
}
return null;
}
[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom", Justification = "This is a plugin loader. It *needs* to do that.")]
internal bool AcquireProviders(string assemblyPath, IHostApi request, bool shouldRefreshCache) {
var found = false;
try {
Assembly assembly = null;
#if !CORECLR
assembly = Assembly.LoadFrom(assemblyPath);
#else
assembly = Microsoft.PowerShell.CoreCLR.AssemblyExtensions.LoadFrom(assemblyPath);
#endif
if (assembly == null) {
return false;
}
var asmVersion = GetAssemblyVersion(assembly);
request.Debug("Acquiring providers for assembly" + assemblyPath);
assembly.FindCompatibleTypes<IMetaProvider>().AsyncForEach(metaProviderClass => {
request.Debug("Registering providers via metaproviders for assembly " + metaProviderClass);
found |= RegisterProvidersViaMetaProvider(metaProviderClass.Create<IMetaProvider>(), asmVersion, request);
})
.Concat(assembly.FindCompatibleTypes<IPackageProvider>().AsyncForEach(packageProviderClass => {
try {
//Handling C# based providers
var packageProvider = RegisterPackageProvider(packageProviderClass.Create<IPackageProvider>(), asmVersion, request, shouldRefreshCache);
if (packageProvider != null) {
found = true;
packageProvider.ProviderPath = assemblyPath;
packageProvider.SetSwidTag(assemblyPath);
}
} catch (Exception ex) {
request.Debug(ex.Message);
}
}))
.Concat(assembly.FindCompatibleTypes<IArchiver>().AsyncForEach(serviceProviderClass => {
var archiver = RegisterArchiver(serviceProviderClass.Create<IArchiver>(), asmVersion, request);
if (archiver != null) {
found = true;
archiver.ProviderPath = assemblyPath;
archiver.SetSwidTag(assemblyPath);
}
}))
.Concat(assembly.FindCompatibleTypes<IDownloader>().AsyncForEach(serviceProviderClass => {
var downloader = RegisterDownloader(serviceProviderClass.Create<IDownloader>(), asmVersion, request);
if (downloader != null) {
found = true;
downloader.ProviderPath = assemblyPath;
downloader.SetSwidTag(assemblyPath);
}
})).WaitAll();
} catch (Exception e) {
request.Debug(e.Message);
request.Debug(e.StackTrace);
}
return found;
}
private static FourPartVersion GetAssemblyVersion(Assembly asm) {
FourPartVersion result = 0;
result = asm.GetName().Version;
if (result == 0) {
// what? No assembly version?
// fallback to the file version of the assembly
#if !CORECLR
var assemblyLocation = asm.Location;
#else
var assemblyLocation = asm.ManifestModule.FullyQualifiedName;
#endif
if (!string.IsNullOrWhiteSpace(assemblyLocation) && File.Exists(assemblyLocation)) {
result = FileVersionInfo.GetVersionInfo(assemblyLocation);
if (result == 0) {
// no file version either?
// use the date I guess.
try {
result = new FileInfo(assemblyLocation).LastWriteTime;
} catch {
}
}
}
if (result == 0) {
// still no version?
// I give up. call it 0.0.0.1
result = "0.0.0.1";
}
}
return result;
}
/// <summary>
/// Register the package provider
/// </summary>
/// <param name="provider">Package provider object</param>
/// <param name="asmVersion">assembly version info</param>
/// <param name="request"></param>
/// <param name="shouldRefreshCache">should refresh the internal provider list</param>
/// <returns></returns>
private PackageProvider RegisterPackageProvider(IPackageProvider provider,
FourPartVersion asmVersion,
IHostApi request,
bool shouldRefreshCache) {
string name = null;
try {
if (provider == null) {
return null;
}
FourPartVersion ver = provider.GetProviderVersion();
var version = ver == 0 ? asmVersion : ver;
name = provider.GetPackageProviderName();
if (string.IsNullOrWhiteSpace(name)) {
return null;
}
// Initialize the provider before locking the collection
// that way we're not blocking others on non-deterministic actions.
request.Debug("Initializing provider '{0}'".format(name));
provider.InitializeProvider(request.As<IRequest>());
request.Debug("Provider '{0}' Initialized".format(name));
lock (_packageProviders) {
//Check if the provider is loaded already.
if (_packageProviders.ContainsKey(name)) {
//if no -force, do nothing
if (!shouldRefreshCache) {
request.Verbose(string.Format(CultureInfo.CurrentCulture, Resources.Messages.SkipPreviousProcessedProvider, name));
//add the provider to the list
var pkgprovider = new PackageProvider(provider) {
Version = version,
IsLoaded = true
};
AddToProviderCacheTable(name, pkgprovider);
return pkgprovider;
} else {
//looks like -force is used, we need to remove the old provider first.
// this won't remove the plugin domain and unload the code yet
_packageProviders.Remove(name);
}
}
}
request.Debug("Using Package Provider {0}".format(name));
var packageProvider = new PackageProvider(provider) {
Version = version
};
//initialize the package provider
packageProvider.Initialize(request);
// addOrSet locks the collection anyway.
_packageProviders.AddOrSet(name, packageProvider);
packageProvider.IsLoaded = true;
request.Debug("The provider '{0}' is imported".format(name));
//add the provider to the list
AddToProviderCacheTable(name, packageProvider);
return packageProvider;
} catch (Exception e) {
request.Debug("Provider '{0}' Failed to import".format(name));
e.Dump();
}
return null;
}
private Archiver RegisterArchiver(IArchiver provider, FourPartVersion asmVersion, IHostApi request) {
string name = null;
try {
FourPartVersion ver = provider.GetProviderVersion();
var version = ver == 0 ? asmVersion : ver;
name = provider.GetArchiverName();
if (string.IsNullOrWhiteSpace(name)) {
return null;
}
// Initialize the provider before locking the collection
// that way we're not blocking others on non-deterministic actions.
request.Debug("Initializing provider '{0}'".format(name));
provider.InitializeProvider(request.As<IRequest>());
request.Debug("Provider '{0}' Initialized".format(name));
lock (Archivers) {
if (Archivers.ContainsKey(name)) {
if (version > Archivers[name].Version) {
// remove the old provider first.
// todo: this won't remove the plugin domain and unload the code yet
// we'll have to do that later.
Archivers.Remove(name);
}
else {
return null;
}
}
request.Debug("Using Archiver Provider {0}".format(name));
var archiver = new Archiver(provider) {
Version = version,
IsLoaded = true
};
archiver.Initialize(request);
Archivers.AddOrSet(name, archiver);
return archiver;
}
}
catch (Exception e) {
request.Debug("Provider '{0}' Failed".format(name));
e.Dump();
}
return null;
}
private Downloader RegisterDownloader(IDownloader provider, FourPartVersion asmVersion, IHostApi request) {
string name = null;
try {
FourPartVersion ver = provider.GetProviderVersion();
var version = ver == 0 ? asmVersion : ver;
name = provider.GetDownloaderName();
if (string.IsNullOrWhiteSpace(name)) {
return null;
}
// Initialize the provider before locking the collection
// that way we're not blocking others on non-deterministic actions.
request.Debug("Initializing provider '{0}'".format(name));
provider.InitializeProvider(request.As<IRequest>());
request.Debug("Provider '{0}' Initialized".format(name));
lock (Downloaders) {
if (Downloaders.ContainsKey(name)) {
if (version > Downloaders[name].Version) {
// remove the old provider first.
// todo: this won't remove the plugin domain and unload the code yet
// we'll have to do that later.
Downloaders.Remove(name);
} else {
return null;
}
}
request.Debug("Using Downloader Provider {0}".format(name));
var downloader = new Downloader(provider) {
Version = version,
IsLoaded = true
};
downloader.Initialize(request);
Downloaders.AddOrSet(name, downloader);
return downloader;
}
} catch (Exception e) {
request.Debug("Provider '{0}' Failed".format(name));
e.Dump();
}
return null;
}
internal bool TryLoadProviderViaMetaProvider(string metaproviderName, string providerNameOrPath, IHostApi request ) {
if (_metaProviders.ContainsKey(metaproviderName)) {
var metaProvider = _metaProviders[metaproviderName];
request.Debug("Using MetaProvider '{0}' to attempt to load provider from '{1}'".format(metaproviderName, providerNameOrPath));
return LoadViaMetaProvider( _metaProviders[metaproviderName], providerNameOrPath, metaProvider.GetProviderVersion(),request);
}
request.Debug("MetaProvider '{0}' is not recognized".format(metaproviderName));
return false;
}
internal bool RegisterProvidersViaMetaProvider(IMetaProvider provider, FourPartVersion asmVersion, IHostApi request) {
request.Debug("Trying to register metaprovider");
var found = false;
var metaProviderName = provider.GetMetaProviderName();
lock (_metaProviders) {
if (!_metaProviders.ContainsKey(metaProviderName)) {
// Meta Providers can't be replaced at this point
_metaProviders.AddOrSet(metaProviderName, provider);
}
}
try {
provider.InitializeProvider(request.As<IRequest>());
provider.GetProviderNames().ParallelForEach(name => {
found = LoadViaMetaProvider(provider, name, asmVersion, request);
});
} catch (Exception e) {
e.Dump();
}
return found;
}
private bool LoadViaMetaProvider(IMetaProvider metaProvider, string name, FourPartVersion asmVersion, IHostApi request) {
var found = false;
var instance = metaProvider.CreateProvider(name);
if (instance != null) {
// check if it's a Package Provider
if (typeof (IPackageProvider).CanDynamicCastFrom(instance)) {
try {
var packageProvider = RegisterPackageProvider(instance.As<IPackageProvider>(), asmVersion, request, false);
if (packageProvider != null) {
found = true;
packageProvider.IsLoaded = true;
packageProvider.ProviderPath = metaProvider.GetProviderPath(name);
packageProvider.SetSwidTag(packageProvider.ProviderPath);
}
} catch (Exception e) {
e.Dump();
}
}
// check if it's a Services Provider
if (typeof (IArchiver).CanDynamicCastFrom(instance)) {
try {
var archiver = RegisterArchiver(instance.As<IArchiver>(), asmVersion, request);
if (archiver != null) {
found = true;
archiver.IsLoaded = true;
archiver.ProviderPath = metaProvider.GetProviderPath(name);
archiver.SetSwidTag(archiver.ProviderPath);
}
} catch (Exception e) {
e.Dump();
}
}
if (typeof (IDownloader).CanDynamicCastFrom(instance)) {
try {
var downloader = RegisterDownloader(instance.As<IDownloader>(), asmVersion, request);
if (downloader != null) {
found = true;
downloader.ProviderPath = metaProvider.GetProviderPath(name);
downloader.SetSwidTag(downloader.ProviderPath);
downloader.IsLoaded = true;
}
} catch (Exception e) {
e.Dump();
}
}
}
return found;
}
}
}