public override async Task <bool> InstallPackageAsync( PackageIdentity packageIdentity, DownloadResourceResult downloadResourceResult, INuGetProjectContext nuGetProjectContext, CancellationToken token) { if (packageIdentity == null) { throw new ArgumentNullException(nameof(packageIdentity)); } if (downloadResourceResult == null) { throw new ArgumentNullException(nameof(downloadResourceResult)); } if (nuGetProjectContext == null) { throw new ArgumentNullException(nameof(nuGetProjectContext)); } if (!downloadResourceResult.PackageStream.CanSeek) { throw new ArgumentException(Strings.PackageStreamShouldBeSeekable); } // Step-1: Check if the package already exists after setting the nuGetProjectContext MSBuildNuGetProjectSystem.SetNuGetProjectContext(nuGetProjectContext); var packageReference = (await GetInstalledPackagesAsync(token)) .FirstOrDefault(p => p.PackageIdentity.Equals(packageIdentity)); if (packageReference != null) { nuGetProjectContext.Log(MessageLevel.Warning, Strings.PackageAlreadyExistsInProject, packageIdentity, MSBuildNuGetProjectSystem.ProjectName); return(false); } // Step-2: Create PackageArchiveReader using the PackageStream and obtain the various item groups downloadResourceResult.PackageStream.Seek(0, SeekOrigin.Begin); var packageReader = downloadResourceResult.PackageReader ?? new PackageArchiveReader(downloadResourceResult.PackageStream, leaveStreamOpen: true); var libItemGroups = packageReader.GetLibItems(); var referenceItemGroups = packageReader.GetReferenceItems(); var frameworkReferenceGroups = packageReader.GetFrameworkItems(); var contentFileGroups = packageReader.GetContentItems(); var buildFileGroups = packageReader.GetBuildItems(); var toolItemGroups = packageReader.GetToolItems(); // Step-3: Get the most compatible items groups for all items groups var hasCompatibleProjectLevelContent = false; var compatibleLibItemsGroup = MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(MSBuildNuGetProjectSystem.TargetFramework, libItemGroups); var compatibleReferenceItemsGroup = MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(MSBuildNuGetProjectSystem.TargetFramework, referenceItemGroups); var compatibleFrameworkReferencesGroup = MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(MSBuildNuGetProjectSystem.TargetFramework, frameworkReferenceGroups); var compatibleContentFilesGroup = MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(MSBuildNuGetProjectSystem.TargetFramework, contentFileGroups); var compatibleBuildFilesGroup = MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(MSBuildNuGetProjectSystem.TargetFramework, buildFileGroups); var compatibleToolItemsGroup = MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(MSBuildNuGetProjectSystem.TargetFramework, toolItemGroups); compatibleLibItemsGroup = MSBuildNuGetProjectSystemUtility.Normalize(compatibleLibItemsGroup); compatibleReferenceItemsGroup = MSBuildNuGetProjectSystemUtility.Normalize(compatibleReferenceItemsGroup); compatibleFrameworkReferencesGroup = MSBuildNuGetProjectSystemUtility.Normalize(compatibleFrameworkReferencesGroup); compatibleContentFilesGroup = MSBuildNuGetProjectSystemUtility.Normalize(compatibleContentFilesGroup); compatibleBuildFilesGroup = MSBuildNuGetProjectSystemUtility.Normalize(compatibleBuildFilesGroup); compatibleToolItemsGroup = MSBuildNuGetProjectSystemUtility.Normalize(compatibleToolItemsGroup); hasCompatibleProjectLevelContent = MSBuildNuGetProjectSystemUtility.IsValid(compatibleLibItemsGroup) || MSBuildNuGetProjectSystemUtility.IsValid(compatibleFrameworkReferencesGroup) || MSBuildNuGetProjectSystemUtility.IsValid(compatibleContentFilesGroup) || MSBuildNuGetProjectSystemUtility.IsValid(compatibleBuildFilesGroup); // Check if package has any content for project var hasProjectLevelContent = libItemGroups.Any() || frameworkReferenceGroups.Any() || contentFileGroups.Any() || buildFileGroups.Any(); var onlyHasCompatibleTools = false; var onlyHasDependencies = false; if (!hasProjectLevelContent) { // Since it does not have project-level content, check if it has dependencies or compatible tools // Note that we are not checking if it has compatible project level content, but, just that it has project level content // If the package has project-level content, but nothing compatible, we still need to throw // If a package does not have any project-level content, it can be a // Legacy solution level packages which only has compatible tools group onlyHasCompatibleTools = MSBuildNuGetProjectSystemUtility.IsValid(compatibleToolItemsGroup) && compatibleToolItemsGroup.Items.Any(); if (!onlyHasCompatibleTools) { // If it does not have compatible tool items either, check if it at least has dependencies onlyHasDependencies = packageReader.GetPackageDependencies().Any(); } } else { var shortFramework = MSBuildNuGetProjectSystem.TargetFramework.GetShortFolderName(); nuGetProjectContext.Log(MessageLevel.Debug, Strings.Debug_TargetFrameworkInfoPrefix, packageIdentity, GetMetadata <string>(NuGetProjectMetadataKeys.Name), shortFramework); } // Step-4: Check if there are any compatible items in the package or that this is not a package with only tools group. If not, throw if (!hasCompatibleProjectLevelContent && !onlyHasCompatibleTools && !onlyHasDependencies) { throw new InvalidOperationException( string.Format(CultureInfo.CurrentCulture, Strings.UnableToFindCompatibleItems, packageIdentity.Id + " " + packageIdentity.Version.ToNormalizedString(), MSBuildNuGetProjectSystem.TargetFramework)); } if (hasCompatibleProjectLevelContent) { var shortFramework = MSBuildNuGetProjectSystem.TargetFramework.GetShortFolderName(); nuGetProjectContext.Log(MessageLevel.Debug, Strings.Debug_TargetFrameworkInfoPrefix, packageIdentity, GetMetadata <string>(NuGetProjectMetadataKeys.Name), shortFramework); } else if (onlyHasCompatibleTools) { nuGetProjectContext.Log(MessageLevel.Info, Strings.AddingPackageWithOnlyToolsGroup, packageIdentity, GetMetadata <string>(NuGetProjectMetadataKeys.Name)); } else if (onlyHasDependencies) { nuGetProjectContext.Log(MessageLevel.Info, Strings.AddingPackageWithOnlyDependencies, packageIdentity, GetMetadata <string>(NuGetProjectMetadataKeys.Name)); } // Step-5: Raise PackageInstalling event // At this point, GetInstalledPath is pointless since the package is, likely, not already installed. It will be empty // Using PackagePathResolver.GetInstallPath would be wrong, since, package version from the nuspec is always used var packageEventArgs = new PackageEventArgs(FolderNuGetProject, packageIdentity, installPath: string.Empty); if (PackageInstalling != null) { PackageInstalling(this, packageEventArgs); } PackageEventsProvider.Instance.NotifyInstalling(packageEventArgs); // Step-6: Install package to FolderNuGetProject await FolderNuGetProject.InstallPackageAsync(packageIdentity, downloadResourceResult, nuGetProjectContext, token); // Step-7: Raise PackageInstalled event // Call GetInstalledPath to get the package installed path var packageInstallPath = FolderNuGetProject.GetInstalledPath(packageIdentity); packageEventArgs = new PackageEventArgs(FolderNuGetProject, packageIdentity, packageInstallPath); if (PackageInstalled != null) { PackageInstalled(this, packageEventArgs); } PackageEventsProvider.Instance.NotifyInstalled(packageEventArgs); // Step-8: MSBuildNuGetProjectSystem operations // Step-8.1: Add references to project if (!IsSkipAssemblyReferences(nuGetProjectContext) && MSBuildNuGetProjectSystemUtility.IsValid(compatibleReferenceItemsGroup)) { foreach (var referenceItem in compatibleReferenceItemsGroup.Items) { if (IsAssemblyReference(referenceItem)) { var referenceItemFullPath = Path.Combine(packageInstallPath, referenceItem); var referenceName = Path.GetFileName(referenceItem); if (MSBuildNuGetProjectSystem.ReferenceExists(referenceName)) { MSBuildNuGetProjectSystem.RemoveReference(referenceName); } MSBuildNuGetProjectSystem.AddReference(referenceItemFullPath); } } } // Step-8.2: Add Frameworkreferences to project if (!IsSkipAssemblyReferences(nuGetProjectContext) && MSBuildNuGetProjectSystemUtility.IsValid(compatibleFrameworkReferencesGroup)) { foreach (var frameworkReference in compatibleFrameworkReferencesGroup.Items) { if (!MSBuildNuGetProjectSystem.ReferenceExists(frameworkReference)) { MSBuildNuGetProjectSystem.AddFrameworkReference(frameworkReference, packageIdentity.Id); } } } // Step-8.3: Add Content Files if (MSBuildNuGetProjectSystemUtility.IsValid(compatibleContentFilesGroup)) { MSBuildNuGetProjectSystemUtility.AddFiles(MSBuildNuGetProjectSystem, packageReader, compatibleContentFilesGroup, FileTransformers); } // Step-8.4: Add Build imports if (MSBuildNuGetProjectSystemUtility.IsValid(compatibleBuildFilesGroup)) { foreach (var buildImportFile in compatibleBuildFilesGroup.Items) { var fullImportFilePath = Path.Combine(packageInstallPath, buildImportFile); MSBuildNuGetProjectSystem.AddImport(fullImportFilePath, fullImportFilePath.EndsWith(".props", StringComparison.OrdinalIgnoreCase) ? ImportLocation.Top : ImportLocation.Bottom); } } // Step-9: Install package to PackagesConfigNuGetProject await PackagesConfigNuGetProject.InstallPackageAsync(packageIdentity, downloadResourceResult, nuGetProjectContext, token); // Step-10: Add packages.config to MSBuildNuGetProject MSBuildNuGetProjectSystem.AddExistingFile(Path.GetFileName(PackagesConfigNuGetProject.FullPath)); // Step 11: Raise PackageReferenceAdded event PackageReferenceAdded?.Invoke(this, packageEventArgs); PackageEventsProvider.Instance.NotifyReferenceAdded(packageEventArgs); // Step-12: Execute powershell script - install.ps1 var anyFrameworkToolsGroup = toolItemGroups.FirstOrDefault(g => g.TargetFramework.Equals(NuGetFramework.AnyFramework)); if (anyFrameworkToolsGroup != null) { var initPS1RelativePath = anyFrameworkToolsGroup.Items.Where(p => p.StartsWith(PowerShellScripts.InitPS1RelativePath, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (!string.IsNullOrEmpty(initPS1RelativePath)) { initPS1RelativePath = PathUtility.ReplaceAltDirSeparatorWithDirSeparator(initPS1RelativePath); await MSBuildNuGetProjectSystem.ExecuteScriptAsync(packageIdentity, packageInstallPath, initPS1RelativePath, this, throwOnFailure : true); } } if (MSBuildNuGetProjectSystemUtility.IsValid(compatibleToolItemsGroup)) { var installPS1RelativePath = compatibleToolItemsGroup.Items.FirstOrDefault( p => p.EndsWith(Path.DirectorySeparatorChar + PowerShellScripts.Install, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(installPS1RelativePath)) { await MSBuildNuGetProjectSystem.ExecuteScriptAsync(packageIdentity, packageInstallPath, installPS1RelativePath, this, throwOnFailure : true); } } return(true); }
internal static void AddFiles(IMSBuildNuGetProjectSystem msBuildNuGetProjectSystem, ZipArchive zipArchive, FrameworkSpecificGroup frameworkSpecificGroup, IDictionary <FileTransformExtensions, IPackageFileTransformer> fileTransformers) { var packageTargetFramework = frameworkSpecificGroup.TargetFramework; // Content files are maintained with AltDirectorySeparatorChar List <string> packageItemListAsArchiveEntryNames = frameworkSpecificGroup.Items.Select(i => PathUtility.ReplaceDirSeparatorWithAltDirSeparator(i)).ToList(); packageItemListAsArchiveEntryNames.Sort(new PackageItemComparer()); try { var zipArchiveEntryList = packageItemListAsArchiveEntryNames.Select(i => zipArchive.GetEntry(i)).Where(i => i != null).ToList(); try { var paths = zipArchiveEntryList.Select(file => ResolvePath(fileTransformers, fte => fte.InstallExtension, GetEffectivePathForContentFile(packageTargetFramework, file.FullName))); paths = paths.Where(p => !String.IsNullOrEmpty(p)); msBuildNuGetProjectSystem.BeginProcessing(paths); } catch (Exception) { // Ignore all exceptions for now } foreach (ZipArchiveEntry zipArchiveEntry in zipArchiveEntryList) { if (zipArchiveEntry == null) { throw new ArgumentNullException("zipArchiveEntry"); } if (IsEmptyFolder(zipArchiveEntry.FullName)) { continue; } var effectivePathForContentFile = GetEffectivePathForContentFile(packageTargetFramework, zipArchiveEntry.FullName); // Resolve the target path IPackageFileTransformer installTransformer; string path = ResolveTargetPath(msBuildNuGetProjectSystem, fileTransformers, fte => fte.InstallExtension, effectivePathForContentFile, out installTransformer); if (msBuildNuGetProjectSystem.IsSupportedFile(path)) { if (installTransformer != null) { installTransformer.TransformFile(zipArchiveEntry, path, msBuildNuGetProjectSystem); } else { // Ignore uninstall transform file during installation string truncatedPath; IPackageFileTransformer uninstallTransformer = FindFileTransformer(fileTransformers, fte => fte.UninstallExtension, effectivePathForContentFile, out truncatedPath); if (uninstallTransformer != null) { continue; } TryAddFile(msBuildNuGetProjectSystem, path, zipArchiveEntry.Open); } } } } finally { msBuildNuGetProjectSystem.EndProcessing(); } }
internal static void DeleteFiles(IMSBuildNuGetProjectSystem projectSystem, ZipArchive zipArchive, IEnumerable <string> otherPackagesPath, FrameworkSpecificGroup frameworkSpecificGroup, IDictionary <FileTransformExtensions, IPackageFileTransformer> fileTransformers) { var packageTargetFramework = frameworkSpecificGroup.TargetFramework; IPackageFileTransformer transformer; var directoryLookup = frameworkSpecificGroup.Items.ToLookup( p => Path.GetDirectoryName(ResolveTargetPath(projectSystem, fileTransformers, fte => fte.UninstallExtension, GetEffectivePathForContentFile(packageTargetFramework, p), out transformer))); // Get all directories that this package may have added var directories = from grouping in directoryLookup from directory in FileSystemUtility.GetDirectories(grouping.Key, altDirectorySeparator: false) orderby directory.Length descending select directory; string projectFullPath = projectSystem.ProjectFullPath; // Remove files from every directory foreach (var directory in directories) { var directoryFiles = directoryLookup.Contains(directory) ? directoryLookup[directory] : Enumerable.Empty <string>(); if (!Directory.Exists(Path.Combine(projectFullPath, directory))) { continue; } foreach (var file in directoryFiles) { if (IsEmptyFolder(file)) { continue; } // Resolve the path var path = ResolveTargetPath(projectSystem, fileTransformers, fte => fte.UninstallExtension, GetEffectivePathForContentFile(packageTargetFramework, file), out transformer); if (projectSystem.IsSupportedFile(path)) { // Register the file being uninstalled (used by web site project system). projectSystem.RegisterProcessedFiles(new[] { path }); if (transformer != null) { // TODO: use the framework from packages.config instead of the current framework // which may have changed during re-targeting var projectFramework = projectSystem.TargetFramework; var matchingFiles = new List <InternalZipFileInfo>(); foreach (var otherPackagePath in otherPackagesPath) { using (var otherPackageZipReader = new PackageArchiveReader(otherPackagePath)) { // use the project framework to find the group that would have been installed var mostCompatibleContentFilesGroup = GetMostCompatibleGroup( projectFramework, otherPackageZipReader.GetContentItems()); if (IsValid(mostCompatibleContentFilesGroup)) { // Should not normalize content files group. // It should be like a ZipFileEntry with a forward slash. foreach (var otherPackageItem in mostCompatibleContentFilesGroup.Items) { if (GetEffectivePathForContentFile(packageTargetFramework, otherPackageItem) .Equals( GetEffectivePathForContentFile(packageTargetFramework, file), StringComparison.OrdinalIgnoreCase)) { matchingFiles.Add(new InternalZipFileInfo(otherPackagePath, otherPackageItem)); } } } } } try { var zipArchiveFileEntry = PathUtility.GetEntry(zipArchive, file); if (zipArchiveFileEntry != null) { transformer.RevertFile(zipArchiveFileEntry.Open, path, matchingFiles, projectSystem); } } catch (Exception e) { projectSystem.NuGetProjectContext.Log(MessageLevel.Warning, e.Message); } } else { try { var zipArchiveFileEntry = PathUtility.GetEntry(zipArchive, file); if (zipArchiveFileEntry != null) { DeleteFileSafe(path, zipArchiveFileEntry.Open, projectSystem); } } catch (Exception e) { projectSystem.NuGetProjectContext.Log(MessageLevel.Warning, e.Message); } } } } // If the directory is empty then delete it if (!GetFilesSafe(projectSystem, directory).Any() && !GetDirectoriesSafe(projectSystem, directory).Any()) { DeleteDirectorySafe(projectSystem, directory); } } }
internal static FrameworkSpecificGroup GetMostCompatibleGroup(NuGetFramework projectTargetFramework, IEnumerable <FrameworkSpecificGroup> itemGroups, bool altDirSeparator = false) { FrameworkReducer reducer = new FrameworkReducer(); NuGetFramework mostCompatibleFramework = reducer.GetNearest(projectTargetFramework, itemGroups.Select(i => i.TargetFramework)); if (mostCompatibleFramework != null) { IEnumerable <FrameworkSpecificGroup> mostCompatibleGroups = itemGroups.Where(i => i.TargetFramework.Equals(mostCompatibleFramework)); var mostCompatibleGroup = mostCompatibleGroups.SingleOrDefault(); if (IsValid(mostCompatibleGroup)) { mostCompatibleGroup = new FrameworkSpecificGroup(mostCompatibleGroup.TargetFramework, GetValidPackageItems(mostCompatibleGroup.Items).Select(item => altDirSeparator ? PathUtility.ReplaceDirSeparatorWithAltDirSeparator(item) : PathUtility.ReplaceAltDirSeparatorWithDirSeparator(item))); } return(mostCompatibleGroup); } return(null); }