public async Task <IEnumerable <PackageIdentity> > ResolveAsync(PackageIdentity package, NuGetFramework targetFramework, bool includePrelease = true, bool ignoreMissingPackages = true) { DependencyBehavior dependencyBehavior = DependencyBehavior.Lowest; //gathered dependencies var availabePackageStorage = new HashSet <SourcePackageDependencyInfo>(PackageIdentityComparer.Default); //gathering resource from available sources var getDependencyResourcesTasks = repositories.Select(repo => repo.GetResourceAsync <DependencyInfoResource>()); var dependencyResources = (await getDependencyResourcesTasks.WhenAllOrException()).Where(x => x.IsSuccess && x.Result != null) .Select(x => x.Result).ToArray(); var dependencyInfoResources = new DependencyInfoResourceCollection(dependencyResources); //recursively collect all dependencies dependencyBehavior = await ResolveDependenciesRecursivelyAsync(package, targetFramework, dependencyInfoResources, NullSourceCacheContext.Instance, availabePackageStorage, ignoreMissingPackages, default); return(availabePackageStorage.ToList()); }
public async Task UninstallAsync(PackageIdentity package, IExtensibleProject project, IEnumerable <PackageReference> installedPackageReferences, CancellationToken cancellationToken = default) { List <string> failedEntries = null; ICollection <PackageIdentity> uninstalledPackages; var targetFramework = FrameworkParser.TryParseFrameworkName(project.Framework, _frameworkNameProvider); #if NET5_0_OR_GREATER var reducer = new FrameworkReducer(); var mostSpecific = reducer.ReduceUpwards(project.SupportedPlatforms).FirstOrDefault(); targetFramework = mostSpecific; #endif var projectConfig = _nuGetProjectConfigurationProvider.GetProjectConfig(project); var uninstallationContext = new UninstallationContext(false, false); _nugetLogger.LogInformation($"Uninstall package {package}, Target framework: {targetFramework}"); if (projectConfig is null) { _nugetLogger.LogWarning($"Project {project.Name} doesn't implement any configuration for own packages"); } using (var cacheContext = new SourceCacheContext() { NoCache = false, DirectDownload = false, }) { var dependencyInfoResource = await project.AsSourceRepository(_sourceRepositoryProvider) .GetResourceAsync <DependencyInfoResource>(cancellationToken); var dependencyInfoResourceCollection = new DependencyInfoResourceCollection(dependencyInfoResource); var resolverContext = await ResolveDependenciesAsync(package, targetFramework, PackageIdentity.Comparer, dependencyInfoResourceCollection, cacheContext, project, true, cancellationToken); var packageReferences = installedPackageReferences.ToList(); if (uninstallationContext.RemoveDependencies) { uninstalledPackages = await GetPackagesCanBeUninstalledAsync(resolverContext.AvailablePackages, packageReferences.Select(x => x.PackageIdentity)); } else { uninstalledPackages = new List <PackageIdentity>() { package }; } } try { foreach (var removedPackage in uninstalledPackages) { if (removedPackage.Version is null) { _nugetLogger.LogWarning($"Skip package {removedPackage.Id} uninstall. Check your package.config for references of this packages"); continue; } var folderProject = new FolderNuGetProject(project.ContentPath); if (folderProject.PackageExists(removedPackage)) { _directoryService.ForceDeleteDirectory(_fileService, folderProject.GetInstalledPath(removedPackage), out failedEntries); } if (projectConfig is null) { continue; } var result = await projectConfig.UninstallPackageAsync(removedPackage, _nuGetProjectContextProvider.GetProjectContext(FileConflictAction.PromptUser), cancellationToken); if (!result) { _nugetLogger.LogError($"Saving package configuration failed in project {project} when installing package {package}"); } } } catch (IOException ex) { Log.Error(ex); _nugetLogger.LogError("Package files cannot be complete deleted by unexpected error (may be directory in use by another process?"); } finally { LogHelper.LogUnclearedPaths(failedEntries, Log); } }
private async Task <Resolver.PackageResolverContext> ResolveDependenciesAsync(PackageIdentity identity, NuGetFramework targetFramework, IEqualityComparer <PackageIdentity> equalityComparer, DependencyInfoResourceCollection dependencyInfoResource, SourceCacheContext cacheContext, IExtensibleProject project, bool ignoreMissingPackages = false, CancellationToken cancellationToken = default) { // The collection of already processed packages var packageStore = new HashSet <SourcePackageDependencyInfo>(equalityComparer); var ignoredPackages = new HashSet <PackageIdentity>(); var downloadStack = new Stack <SourcePackageDependencyInfo>(); var resolvingBehavior = DependencyBehavior.Lowest; // get top dependency var dependencyInfo = await dependencyInfoResource.ResolvePackageAsync( identity, targetFramework, cacheContext, _nugetLogger, cancellationToken); if (dependencyInfo is null) { _nugetLogger.LogError($"Cannot resolve {identity} package for target framework {targetFramework}"); return(Resolver.PackageResolverContext.Empty); } downloadStack.Push(dependencyInfo); //and add it to package store while (downloadStack.Count > 0) { var nextPackage = downloadStack.Pop(); if (packageStore.Contains(nextPackage)) { continue; } else { packageStore.Add(nextPackage); } foreach (var dependency in nextPackage.Dependencies) { // currently we use specific version during child dependency resolving // but possibly it should be configured in project var dependencyIdentity = new PackageIdentity(dependency.Id, dependency.VersionRange.MinVersion); var isPackageRequiresOwnDependencies = !_apiPackageRegistry.IsRegistered(dependencyIdentity.Id); if (isPackageRequiresOwnDependencies) { // We can't determine the unknown version yet from range, but can exclude min required if it was already processed if (packageStore.Contains(dependencyIdentity)) { continue; } var relatedDepInfos = await dependencyInfoResource.ResolvePackagesWithVersionSatisfyRangeAsync(dependencyIdentity, dependency.VersionRange, targetFramework, cacheContext, _nugetLogger, cancellationToken); foreach (var relatedDepedencyInfoResource in relatedDepInfos) { downloadStack.Push(relatedDepedencyInfoResource); } if (relatedDepInfos.Any()) { // we found compatible (at least with target framework) packages and leave decision to Package Resolver in the future continue; } } // Determine behavior if package cannot be resolved in any way if (ignoreMissingPackages) { if (_apiPackageRegistry.IsRegistered(dependencyIdentity.Id)) { resolvingBehavior = DependencyBehavior.Lowest; if (!packageStore.Contains(dependencyIdentity)) { packageStore.Add(new SourcePackageDependencyInfo(dependencyIdentity.Id, dependencyIdentity.Version, Enumerable.Empty <PackageDependency>(), false, null)); } if (ignoredPackages.Add(dependencyIdentity)) { // Show only for top package, not much effort to see this message multiple times if (nextPackage == dependencyInfo) { await _nugetLogger.LogAsync(LogLevel.Information, $"The package dependency {dependencyIdentity.Id} listed as part of API and can be safely skipped"); } } } else { resolvingBehavior = DependencyBehavior.Ignore; await _nugetLogger.LogAsync(LogLevel.Warning, $"Available sources doesn't contain package {dependencyIdentity}. Package {dependencyIdentity} is missing"); } } else { throw new MissingPackageException($"Cannot find package {dependencyIdentity}"); } } } // Pass packages.config to resolver var nugetPackagesConfigProject = _nuGetProjectConfigurationProvider.GetProjectConfig(project); var packagesConfigReferences = await nugetPackagesConfigProject.GetInstalledPackagesAsync(cancellationToken); // Construct context for package resolver return(GetResolverContext(identity, resolvingBehavior, packageStore, packagesConfigReferences, ignoredPackages)); }
public async Task <InstallerResult> InstallAsync( PackageIdentity package, IExtensibleProject project, IReadOnlyList <SourceRepository> repositories, bool ignoreMissingPackages = false, CancellationToken cancellationToken = default) { try { // Step 1. Decide what framework version used on package resolving // Enforce platform-specific framework for .NET 5.0 var targetFramework = FrameworkParser.TryParseFrameworkName(project.Framework, _frameworkNameProvider); var reducer = new FrameworkReducer(); #if NET5_0_OR_GREATER var mostSpecific = reducer.ReduceUpwards(project.SupportedPlatforms).FirstOrDefault(); targetFramework = mostSpecific; #endif _nugetLogger.LogInformation($"Installing package {package}, Target framework: {targetFramework}"); // Prepare to step 2. Add globals if cache enabled as available repository with highest priority. // Note: This part falls under responsibility of RepositoryContextService but the same logic used to determine what packages are found by IPackageLoaderService // To not break behavior for now add here if (!project.NoCache) { var repositoryList = repositories.ToList(); repositoryList.Insert(0, new SourceRepository(new PackageSource(DefaultNuGetFolders.GetGlobalPackagesFolder(), ".nuget"), Repository.Provider.GetCoreV3())); repositories = repositoryList; } // Step 2. Build list of dependencies and determine DependencyBehavior if some packages are misssed in current feed Resolver.PackageResolverContext resolverContext = null; using (var cacheContext = new SourceCacheContext()) { #pragma warning disable IDISP013 // Await in using. var getDependencyResourcesTasks = repositories.Select(repo => repo.GetResourceAsync <DependencyInfoResource>()); #pragma warning restore IDISP013 // Await in using. var dependencyResources = (await getDependencyResourcesTasks.WhenAllOrExceptionAsync()).Where(x => x.IsSuccess && x.Result is not null) .Select(x => x.Result).ToArray(); var dependencyInfoResources = new DependencyInfoResourceCollection(dependencyResources); resolverContext = await ResolveDependenciesAsync(package, targetFramework, PackageIdentityComparer.Default, dependencyInfoResources, cacheContext, project, ignoreMissingPackages, cancellationToken); if (resolverContext is null || !(resolverContext?.AvailablePackages?.Any() ?? false)) { var errorMessage = $"Package {package} cannot be resolved with current settings (TFM: {targetFramework}) for chosen destination"; _nugetLogger.LogWarning(errorMessage); return(new InstallerResult(errorMessage)); } // Step 3. Try to check is main package can be downloaded from resource var mainPackageInfo = resolverContext.AvailablePackages.FirstOrDefault(p => p.Id == package.Id); _nugetLogger.LogInformation($"Downloading {package}..."); var mainDownloadedFiles = await DownloadPackageResourceAsync(mainPackageInfo, cacheContext, cancellationToken); _nugetLogger.LogInformation($"{package} download completed"); if (!mainDownloadedFiles.IsAvailable()) { // Downlod failed by some reasons (probably connection issue or package goes deleted before feed updated) var errorMessage = $"Current source lists package {package} but attempts to download it have failed. The source in invalid or required packages were removed while the current operation was in progress"; _nugetLogger.LogError(errorMessage); return(new InstallerResult(errorMessage)); } // Step 4. Check is main package compatible with target Framework var canBeInstalled = await CheckCanBeInstalledAsync(project, mainDownloadedFiles.PackageReader, targetFramework, cancellationToken); if (!canBeInstalled) { throw new IncompatiblePackageException($"Package {package} incompatible with project target platform {targetFramework}"); } // Step 5. Build install list using NuGet Resolver and select available resources. // Track packages which already installed and make sure only one version of package exists var resolver = new Resolver.PackageResolver(); var availablePackagesToInstall = await resolver.ResolveWithVersionOverrideAsync(resolverContext, project, DependencyBehavior.Highest, (project, conflict) => _fileSystemService.CreateDeleteme(conflict.PackageIdentity.Id, project.GetInstallPath(conflict.PackageIdentity)), cancellationToken); // Step 6. Download everything except main package and extract all availablePackagesToInstall.Remove(mainPackageInfo); _nugetLogger.LogInformation($"Downloading package dependencies..."); var downloadResults = await DownloadPackagesResourcesAsync(availablePackagesToInstall, cacheContext, cancellationToken); downloadResults[mainPackageInfo] = mainDownloadedFiles; _nugetLogger.LogInformation($"{downloadResults.Count - 1} dependencies downloaded"); var extractionContext = GetExtractionContext(); await ExtractPackagesResourcesAsync(downloadResults, project, extractionContext, cancellationToken); await CheckLibAndFrameworkItemsAsync(downloadResults, targetFramework, cancellationToken); return(new InstallerResult(downloadResults)); } } catch (NuGetResolverInputException ex) { throw new IncompatiblePackageException($"Package {package} or some of it dependencies are missed for current target framework", ex); } catch (Exception ex) { Log.Error(ex); throw; } }
private async Task <DependencyBehavior> ResolveDependenciesRecursivelyAsync(PackageIdentity identity, NuGetFramework targetFramework, DependencyInfoResourceCollection dependencyInfoResource, SourceCacheContext cacheContext, HashSet <SourcePackageDependencyInfo> storage, bool ignoreMissingPackages = false, CancellationToken cancellationToken = default) { Argument.IsNotNull(() => storage); HashSet <SourcePackageDependencyInfo> packageStore = storage; Stack <SourcePackageDependencyInfo> downloadStack = new Stack <SourcePackageDependencyInfo>(); var resolvedBehavior = DependencyBehavior.Lowest; //get top dependency var dependencyInfo = await dependencyInfoResource.ResolvePackage( identity, targetFramework, cacheContext, NuGetLogger, cancellationToken); if (dependencyInfo == null) { Log.Error($"Cannot resolve {identity} package for target framework {targetFramework}"); return(resolvedBehavior); } downloadStack.Push(dependencyInfo); //and add it to package store while (downloadStack.Count > 0) { var rootDependency = downloadStack.Pop(); //store all new packges if (!packageStore.Contains(rootDependency)) { packageStore.Add(rootDependency); } else { continue; } foreach (var dependency in rootDependency.Dependencies) { var relatedIdentity = new PackageIdentity(dependency.Id, dependency.VersionRange.MinVersion); var relatedDepInfo = await dependencyInfoResource.ResolvePackage(relatedIdentity, targetFramework, cacheContext, NuGetLogger, cancellationToken); if (relatedDepInfo != null) { downloadStack.Push(relatedDepInfo); Log.Info($"Add dependency {relatedIdentity} for package {identity}"); continue; } if (ignoreMissingPackages) { resolvedBehavior = DependencyBehavior.Ignore; Log.Warning($"Available sources doesn't contain package {relatedIdentity}. Package {relatedIdentity} is missing"); } else { throw new MissingPackageException($"Cannot find package {relatedIdentity}"); } } } return(resolvedBehavior); }