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);
        }
Beispiel #2
0
        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);
                }
            }
        }
Beispiel #4
0
        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);
        }