diff --git a/src/NuGet.Core/NuGet.ProjectManagement/Projects/IMSBuildNuGetProjectSystem.cs b/src/NuGet.Core/NuGet.ProjectManagement/Projects/IMSBuildNuGetProjectSystem.cs index a211a9a9da1..da82aca2a32 100644 --- a/src/NuGet.Core/NuGet.ProjectManagement/Projects/IMSBuildNuGetProjectSystem.cs +++ b/src/NuGet.Core/NuGet.ProjectManagement/Projects/IMSBuildNuGetProjectSystem.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; -using System.IO.Compression; using System.Threading.Tasks; using NuGet.Frameworks; using NuGet.Packaging.Core; diff --git a/src/NuGet.Core/NuGet.ProjectManagement/Projects/MSBuildNuGetProject.cs b/src/NuGet.Core/NuGet.ProjectManagement/Projects/MSBuildNuGetProject.cs index 2355810841f..a16e4f4fc62 100644 --- a/src/NuGet.Core/NuGet.ProjectManagement/Projects/MSBuildNuGetProject.cs +++ b/src/NuGet.Core/NuGet.ProjectManagement/Projects/MSBuildNuGetProject.cs @@ -17,33 +17,6 @@ namespace NuGet.ProjectManagement { - internal class PackageItemComparer : IComparer - { - public int Compare(string x, string y) - { - // BUG 636: We sort files so that they are added in the correct order - // e.g aspx before aspx.cs - - if (x.Equals(y, StringComparison.OrdinalIgnoreCase)) - { - return 0; - } - - // Add files that are prefixes of other files first - if (x.StartsWith(y, StringComparison.OrdinalIgnoreCase)) - { - return -1; - } - - if (y.StartsWith(x, StringComparison.OrdinalIgnoreCase)) - { - return 1; - } - - return string.Compare(y, x, StringComparison.OrdinalIgnoreCase); - } - } - /// /// This class represents a NuGetProject based on a .NET project. This also contains an instance of a /// FolderNuGetProject @@ -92,7 +65,9 @@ public class MSBuildNuGetProject : NuGetProject { new FileTransformExtensions(".install.xdt", ".uninstall.xdt"), new XdtTransformer() } }; - public MSBuildNuGetProject(IMSBuildNuGetProjectSystem msbuildNuGetProjectSystem, string folderNuGetProjectPath, string packagesConfigFolderPath) + public MSBuildNuGetProject(IMSBuildNuGetProjectSystem msbuildNuGetProjectSystem, + string folderNuGetProjectPath, + string packagesConfigFolderPath) { if (msbuildNuGetProjectSystem == null) { @@ -178,7 +153,7 @@ public override async Task InstallPackageAsync( return false; } - // Step-2: Create PackageReader using the PackageStream and obtain the various item groups + // Step-2: Create PackageReader using the PackageStream and obtain the various item groups downloadResourceResult.PackageStream.Seek(0, SeekOrigin.Begin); var packageReader = downloadResourceResult.PackageReader; if (packageReader == null) @@ -279,7 +254,7 @@ public override async Task InstallPackageAsync( } PackageEventsProvider.Instance.NotifyInstalling(packageEventArgs); - // Step-6: Install package to FolderNuGetProject + // Step-6: Install package to FolderNuGetProject await FolderNuGetProject.InstallPackageAsync(packageIdentity, downloadResourceResult, nuGetProjectContext, token); // Step-7: Raise PackageInstalled event @@ -303,10 +278,12 @@ public override async Task InstallPackageAsync( { var referenceItemFullPath = Path.Combine(packageInstallPath, referenceItem); var referenceName = Path.GetFileName(referenceItem); + if (MSBuildNuGetProjectSystem.ReferenceExists(referenceName)) { MSBuildNuGetProjectSystem.RemoveReference(referenceName); } + MSBuildNuGetProjectSystem.AddReference(referenceItemFullPath); } } @@ -403,36 +380,57 @@ public override async Task UninstallPackageAsync(PackageIdentity packageId } var packageTargetFramework = packageReference.TargetFramework ?? MSBuildNuGetProjectSystem.TargetFramework; - var packageEventArgs = new PackageEventArgs(FolderNuGetProject, packageIdentity, FolderNuGetProject.GetInstalledPath(packageIdentity)); + var packageEventArgs = new PackageEventArgs(FolderNuGetProject, + packageIdentity, + FolderNuGetProject.GetInstalledPath(packageIdentity)); + + if (PackageUninstalling != null) + { + PackageUninstalling(this, packageEventArgs); + } + using (var packageStream = File.OpenRead(FolderNuGetProject.GetInstalledPackageFilePath(packageIdentity))) { - // Step-2: Create PackageReader using the PackageStream and obtain the various item groups - // Get the package target framework instead of using project targetframework var zipArchive = new ZipArchive(packageStream); var packageReader = new PackageReader(zipArchive); + // Step-2: Execute powershell script - uninstall.ps1 + var toolItemGroups = packageReader.GetToolItems(); + var compatibleToolItemsGroup = MSBuildNuGetProjectSystemUtility + .GetMostCompatibleGroup(packageTargetFramework, toolItemGroups); + + if (MSBuildNuGetProjectSystemUtility.IsValid(compatibleToolItemsGroup)) + { + var uninstallPS1RelativePath = compatibleToolItemsGroup.Items.FirstOrDefault( + p => p.EndsWith(Path.DirectorySeparatorChar + PowerShellScripts.Uninstall, StringComparison.OrdinalIgnoreCase)); + + if (!string.IsNullOrEmpty(uninstallPS1RelativePath)) + { + var packageInstallPath = FolderNuGetProject.GetInstalledPath(packageIdentity); + await MSBuildNuGetProjectSystem.ExecuteScriptAsync(packageIdentity, packageInstallPath, uninstallPS1RelativePath, this, throwOnFailure: false); + } + } + + // Step-3: Obtain the various item groups + // Get the package target framework instead of using project targetframework var referenceItemGroups = packageReader.GetReferenceItems(); var contentFileGroups = packageReader.GetContentItems(); var buildFileGroups = packageReader.GetBuildItems(); - // Step-3: Get the most compatible items groups for all items groups + // Step-4: Get the most compatible items groups for all items groups var compatibleReferenceItemsGroup = MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(packageTargetFramework, referenceItemGroups); + var compatibleContentFilesGroup = MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(packageTargetFramework, contentFileGroups); + var compatibleBuildFilesGroup = MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(packageTargetFramework, buildFileGroups); - // TODO: Need to handle References element?? - // Step-4: Raise PackageUninstalling event - if (PackageUninstalling != null) - { - PackageUninstalling(this, packageEventArgs); - } PackageEventsProvider.Instance.NotifyUninstalling(packageEventArgs); - // Step-5: Uninstall package from packages.config + // Step-5: Remove package reference from packages.config await PackagesConfigNuGetProject.UninstallPackageAsync(packageIdentity, nuGetProjectContext, token); // Step-6: Remove packages.config from MSBuildNuGetProject if there are no packages @@ -484,38 +482,27 @@ public override async Task UninstallPackageAsync(PackageIdentity packageId } // Step-7.5: Remove binding redirects. This is a no-op + // Binding redirects will be removed when all packages have finished + // uninstalling for performance reasons // Step-8: Raise PackageReferenceRemoved event if (PackageReferenceRemoved != null) { PackageReferenceRemoved(this, packageEventArgs); } - PackageEventsProvider.Instance.NotifyReferenceRemoved(packageEventArgs); - // Step-9: Execute powershell script - uninstall.ps1 - var toolItemGroups = packageReader.GetToolItems(); - var compatibleToolItemsGroup = MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(packageTargetFramework, - toolItemGroups); - if (MSBuildNuGetProjectSystemUtility.IsValid(compatibleToolItemsGroup)) - { - var uninstallPS1RelativePath = compatibleToolItemsGroup.Items.FirstOrDefault( - p => p.EndsWith(Path.DirectorySeparatorChar + PowerShellScripts.Uninstall, StringComparison.OrdinalIgnoreCase)); - if (!string.IsNullOrEmpty(uninstallPS1RelativePath)) - { - var packageInstallPath = FolderNuGetProject.GetInstalledPath(packageIdentity); - await MSBuildNuGetProjectSystem.ExecuteScriptAsync(packageIdentity, packageInstallPath, uninstallPS1RelativePath, this, throwOnFailure: false); - } - } + PackageEventsProvider.Instance.NotifyReferenceRemoved(packageEventArgs); } - // Step-10: Uninstall package from the folderNuGetProject + // Step-9: Uninstall package from the folderNuGetProject await FolderNuGetProject.UninstallPackageAsync(packageIdentity, nuGetProjectContext, token); - // Step-11: Raise PackageUninstalled event + // Step-10: Raise PackageUninstalled event if (PackageUninstalled != null) { PackageUninstalled(this, packageEventArgs); } + PackageEventsProvider.Instance.NotifyUninstalled(packageEventArgs); return true; diff --git a/src/NuGet.Core/NuGet.ProjectManagement/Utility/MSBuildNuGetProjectSystemUtility.cs b/src/NuGet.Core/NuGet.ProjectManagement/Utility/MSBuildNuGetProjectSystemUtility.cs index a00fd90db80..bd6fd17597e 100644 --- a/src/NuGet.Core/NuGet.ProjectManagement/Utility/MSBuildNuGetProjectSystemUtility.cs +++ b/src/NuGet.Core/NuGet.ProjectManagement/Utility/MSBuildNuGetProjectSystemUtility.cs @@ -77,7 +77,7 @@ internal static void AddFiles(IMSBuildNuGetProjectSystem msBuildNuGetProjectSyst IDictionary fileTransformers) { var packageTargetFramework = frameworkSpecificGroup.TargetFramework; - + var packageItemListAsArchiveEntryNames = frameworkSpecificGroup.Items.ToList(); packageItemListAsArchiveEntryNames.Sort(new PackageItemComparer()); try @@ -154,9 +154,9 @@ internal static void DeleteFiles(IMSBuildNuGetProjectSystem msBuildNuGetProjectS // 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; + from directory in FileSystemUtility.GetDirectories(grouping.Key, altDirectorySeparator: false) + orderby directory.Length descending + select directory; // Remove files from every directory foreach (var directory in directories) @@ -329,6 +329,7 @@ internal static void DeleteDirectory(IMSBuildNuGetProjectSystem msBuildNuGetProj msBuildNuGetProjectSystem.NuGetProjectContext.Log(MessageLevel.Warning, Strings.Warning_DirectoryNotEmpty, path); return; } + msBuildNuGetProjectSystem.DeleteDirectory(path, recursive); // Workaround for update-package TFS issue. If we're bound to TFS, do not try and delete directories. @@ -339,7 +340,10 @@ internal static void DeleteDirectory(IMSBuildNuGetProjectSystem msBuildNuGetProj return; } - try + // For potential project systems that do not remove items from disk, we delete the folder directly + // There is no actual scenario where we know this is broken without the code below, but since the + // code was always there, we are leaving it behind for now. + if (!Directory.Exists(fullPath)) { Directory.Delete(fullPath, recursive); @@ -349,13 +353,9 @@ internal static void DeleteDirectory(IMSBuildNuGetProjectSystem msBuildNuGetProj { Thread.Sleep(100); } - msBuildNuGetProjectSystem.RemoveFile(path); msBuildNuGetProjectSystem.NuGetProjectContext.Log(MessageLevel.Debug, Strings.Debug_RemovedFolder, fullPath); } - catch (DirectoryNotFoundException) - { - } } private static void PerformSafeAction(Action action, INuGetProjectContext nuGetProjectContext) @@ -510,5 +510,32 @@ internal static void AddFile(IMSBuildNuGetProjectSystem msBuildNuGetProjectSyste msBuildNuGetProjectSystem.AddFile(path, memoryStream); } } + + private class PackageItemComparer : IComparer + { + public int Compare(string x, string y) + { + // BUG 636: We sort files so that they are added in the correct order + // e.g aspx before aspx.cs + + if (x.Equals(y, StringComparison.OrdinalIgnoreCase)) + { + return 0; + } + + // Add files that are prefixes of other files first + if (x.StartsWith(y, StringComparison.OrdinalIgnoreCase)) + { + return -1; + } + + if (y.StartsWith(x, StringComparison.OrdinalIgnoreCase)) + { + return 1; + } + + return string.Compare(y, x, StringComparison.OrdinalIgnoreCase); + } + } } -} +} \ No newline at end of file