public void FrameworkReducer_ReduceToHighest() { // both frameworks are equivalent var fw1 = new NuGetFramework(FrameworkConstants.FrameworkIdentifiers.Windows, FrameworkConstants.EmptyVersion); var fw2 = FrameworkConstants.CommonFrameworks.Win8; var packageFrameworks = new List <NuGetFramework>() { fw1, fw2 }; FrameworkReducer reducer = new FrameworkReducer(); // the non-zero version should win in both cases var upwards = reducer.ReduceUpwards(packageFrameworks).Single(); var downwards = reducer.ReduceUpwards(packageFrameworks).Single(); Assert.Equal(fw2, upwards); Assert.Equal(fw2, downwards); }
public void FrameworkReducer_ReduceUpPCL() { FrameworkReducer reducer = new FrameworkReducer(); var framework1 = NuGetFramework.Parse("portable-net45+win8"); var framework2 = NuGetFramework.Parse("portable-net451+win81"); var all = new NuGetFramework[] { framework1, framework2 }; var result = reducer.ReduceUpwards(all); Assert.Equal(framework2, result.Single()); }
public void FrameworkReducer_ReduceUpEqual() { FrameworkReducer reducer = new FrameworkReducer(); var framework1 = NuGetFramework.Parse("net40"); var framework2 = NuGetFramework.Parse("net40"); var all = new NuGetFramework[] { framework1, framework2 }; var result = reducer.ReduceUpwards(all); Assert.Equal(framework2, result.Single()); }
public void FrameworkReducer_ReduceUpEquivalent() { FrameworkReducer reducer = new FrameworkReducer(); var framework1 = NuGetFramework.Parse("net40"); var framework2 = NuGetFramework.Parse("net40-client"); var all = new NuGetFramework[] { framework1, framework2 }; var result = reducer.ReduceUpwards(all).ToArray(); Assert.Equal(2, result.Length); Assert.Equal(framework1, result.First()); Assert.Equal(framework2, result.Last()); }
public void FrameworkReducer_ReduceUpwardsBasic() { FrameworkReducer reducer = new FrameworkReducer(); var net35 = NuGetFramework.Parse("net35"); var net40 = NuGetFramework.Parse("net40"); var net45 = NuGetFramework.Parse("net45"); var net451 = NuGetFramework.Parse("net451"); var net453 = NuGetFramework.Parse("net453"); var all = new NuGetFramework[] { net35, net40, net45, net451, net453 }; var result = reducer.ReduceUpwards(all); Assert.Equal(net453, result.Single()); }
public void FrameworkReducer_ReduceUpwardsNonSingle() { FrameworkReducer reducer = new FrameworkReducer(); var net35 = NuGetFramework.Parse("net35"); var net40 = NuGetFramework.Parse("net40"); var net45 = NuGetFramework.Parse("net45"); var net451 = NuGetFramework.Parse("net451"); var net453 = NuGetFramework.Parse("net453"); var wp8 = NuGetFramework.Parse("wp8"); var wp81 = NuGetFramework.Parse("wp81"); var all = new NuGetFramework[] { net35, net40, net45, net451, net453, wp8, wp81 }; var result = reducer.ReduceUpwards(all); Assert.Equal(net453, result.First()); Assert.Equal(wp81, result.Skip(1).First()); }
public static void Generate(string outDir, Nuspec spec) { if (Directory.Exists(outDir)) { Directory.Delete(outDir, true); } Directory.CreateDirectory(outDir); /* * This approach creates a lib/<framework> as necessary, depending on the target framework of each assembly. * An issue will arise when referencing such nuget package via SlnX, because as per today there only one * targetFramework can be selected on each <package ... /> element. * Another potential issue is caused by NuGet.exe (see https://docs.microsoft.com/en-us/nuget/reference/nuspec#dependencies-element). * For each lib/<framework> there should be a * <group targetFramework="<framework>"> in the <dependencies> element. * This code creates such a structure, but due to the nature of the Slnx nuget packages handling * it cannot know weather a file (DLL) in a specific framwork does reference one of the listed packages. * It fact, it is well possible that this code would generate invalid dependencies. * e.g. * <group targetFramework="net45"> ---> Caused by a project present in the SlnX (no reference to NUinit here) * <dependency id = "NUnit" version="3.13.1" /> ---> Caused by another project in the SlnX * <group> * For this reason, the code will now check for the "highest" compatible framwork, and create the package for that one only * * var dependencies = new StringBuilder(); * foreach (var frameworkItems in spec.Files) * { * var frameworkDir = Path.Combine(outDir, "lib", frameworkItems.Key); * Directory.CreateDirectory(frameworkDir); * foreach(var f in frameworkItems.Value) * { * File.Copy(f, Path.Combine(frameworkDir, Path.GetFileName(f)), true); * } * * if (string.IsNullOrEmpty(frameworkItems.Key)) * { * dependencies.Append(_dependencyGroupOpening); * } * else * { * dependencies.AppendFormat(_dependencyGroupFrameworkTemplate, frameworkItems.Key); * } * dependencies.AppendLine(); * foreach (var d in spec.Dependecies) * { * dependencies.AppendFormat(string.Format(_dependencyTemplate, d.Id, d.Version)); * dependencies.AppendLine(); * } * dependencies.Append(_dependencyGroupClosing); * dependencies.AppendLine(); * } */ var frameworkReducer = new FrameworkReducer(); var highestFramework = frameworkReducer.ReduceUpwards(spec.Elements.Select(x => NuGetFramework.Parse(x.Key))).ToList(); if (highestFramework.Count != 1) { var required = string.Join(Environment.NewLine, highestFramework); throw new Exception($"For the required package {spec.Id} multiple .NET frameworks would be necessary. This is probably caused by a mixture of '.NET Framework' and '.NET Core' based projects.\nThis is not supported.\nRequired frameworks: {required}"); } var highestFrameworkName = highestFramework.Single().GetShortFolderName(); var packageVersion = spec.Version; if (packageVersion == null) { if (!spec.Elements.ContainsKey(highestFrameworkName)) { throw new Exception($"The .NET version of the required package {spec.Id} has been resolved to {highestFrameworkName}. No file has been found under that framework."); } var assemblies = spec.Elements[highestFrameworkName].Where(x => File.Exists(x) && (x.ToLower().EndsWith(".dll") || x.ToLower().EndsWith(".exe"))); if (assemblies.Count() == 0) { throw new Exception("Error while loading the nuget information. No DLL found from which automatically retrieve the version information."); } var assemblyPath = assemblies.First(); if (File.Exists(assemblyPath)) { var assemblyName = AssemblyName.GetAssemblyName(assemblyPath); packageVersion = assemblyName.Version.ToString(3); } else { throw new Exception(string.Format("Unable to automatically retrieve the package version. Assembly {0} not found", assemblyPath)); } } foreach (var frameworkItems in spec.Elements) { var frameworkDir = Path.Combine(outDir, "lib", highestFrameworkName); Directory.CreateDirectory(frameworkDir); foreach (var f in frameworkItems.Value) { FileAttributes attr = File.GetAttributes(f); if ((attr & FileAttributes.Directory) == FileAttributes.Directory) { CopyDirectory(f, Path.Combine(frameworkDir, Path.GetFileName(f))); } else { File.Copy(f, Path.Combine(frameworkDir, Path.GetFileName(f)), true); } } } var dependencies = new StringBuilder(); if (string.IsNullOrEmpty(highestFrameworkName)) { dependencies.Append(_dependencyGroupOpening); } else { dependencies.AppendFormat(_dependencyGroupFrameworkTemplate, highestFrameworkName); } dependencies.AppendLine(); foreach (var d in spec.Dependecies) { dependencies.AppendFormat(string.Format(_dependencyTemplate, d.Id, d.VersionRange.OriginalString)); dependencies.AppendLine(); } dependencies.Append(_dependencyGroupClosing); dependencies.AppendLine(); string customElements = null; if (spec.AdditionalElements != null) { var lines = spec.AdditionalElements.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); customElements = string.Join($"{Environment.NewLine} ", lines); } var content = string.Format(_nuspecTemplate, spec.Id, packageVersion, customElements, dependencies); Utilities.WriteAllText(Path.Combine(outDir, spec.FileName), content); }
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); } }
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; } }
public IEnumerable <NuGetFramework> GetHighest() { return(_frameworkReducer.ReduceUpwards(_nuGetFrameworks)); }