public static async Task InstallFromSourceAsync()

in src/NuGet.Core/NuGet.Packaging/PackageExtractor.cs [638:878]


        public static async Task<bool> InstallFromSourceAsync(
            PackageIdentity packageIdentity,
            IPackageDownloader packageDownloader,
            VersionFolderPathResolver versionFolderPathResolver,
            PackageExtractionContext packageExtractionContext,
            CancellationToken token,
            Guid parentId = default(Guid))
        {
            if (packageDownloader == null)
            {
                throw new ArgumentNullException(nameof(packageDownloader));
            }

            if (packageExtractionContext == null)
            {
                throw new ArgumentNullException(nameof(packageExtractionContext));
            }

            var logger = packageExtractionContext.Logger;

            var packageExtractionTelemetryEvent = new PackageExtractionTelemetryEvent(packageExtractionContext.PackageSaveMode, NuGetOperationStatus.Failed, ExtractionSource.RestoreCommand, packageIdentity);
            using (var telemetry = TelemetryActivity.Create(parentId, packageExtractionTelemetryEvent))
            {
                var targetPath = versionFolderPathResolver.GetInstallPath(packageIdentity.Id, packageIdentity.Version);
                var targetNuspec = versionFolderPathResolver.GetManifestFilePath(packageIdentity.Id, packageIdentity.Version);
                var targetNupkg = versionFolderPathResolver.GetPackageFilePath(packageIdentity.Id, packageIdentity.Version);
                var hashPath = versionFolderPathResolver.GetHashPath(packageIdentity.Id, packageIdentity.Version);
                var nupkgMetadataFilePath = versionFolderPathResolver.GetNupkgMetadataPath(packageIdentity.Id, packageIdentity.Version);

                logger.LogVerbose(
                    $"Acquiring lock for the installation of {packageIdentity.Id} {packageIdentity.Version}");

                // Acquire the lock on a nukpg before we extract it to prevent the race condition when multiple
                // processes are extracting to the same destination simultaneously
                return await ConcurrencyUtilities.ExecuteWithFileLockedAsync(targetNupkg,
                    action: async cancellationToken =>
                    {
                        // If this is the first process trying to install the target nupkg, go ahead
                        // After this process successfully installs the package, all other processes
                        // waiting on this lock don't need to install it again.
                        if (!File.Exists(nupkgMetadataFilePath))
                        {
                            logger.LogVerbose(
                                $"Acquired lock for the installation of {packageIdentity.Id} {packageIdentity.Version}");

                            cancellationToken.ThrowIfCancellationRequested();

                            // We do not stop the package extraction after this point
                            // based on CancellationToken, but things can still be stopped if the process is killed.
                            if (Directory.Exists(targetPath))
                            {
                                // If we had a broken restore, clean out the files first
                                var info = new DirectoryInfo(targetPath);

                                foreach (var file in info.GetFiles())
                                {
                                    file.Delete();
                                }

                                foreach (var dir in info.GetDirectories())
                                {
                                    dir.Delete(true);
                                }
                            }
                            else
                            {
                                Directory.CreateDirectory(targetPath);
                            }

                            var targetTempNupkg = Path.Combine(targetPath, Path.GetRandomFileName());
                            var tempHashPath = Path.Combine(targetPath, Path.GetRandomFileName());
                            var tempNupkgMetadataFilePath = Path.Combine(targetPath, Path.GetRandomFileName());
                            var packageSaveMode = packageExtractionContext.PackageSaveMode;

                            // Extract the nupkg
                            var copiedNupkg = await packageDownloader.CopyNupkgFileToAsync(targetTempNupkg, cancellationToken);

                            if (packageSaveMode.HasFlag(PackageSaveMode.Nuspec) || packageSaveMode.HasFlag(PackageSaveMode.Files))
                            {
                                try
                                {
                                    telemetry.StartIntervalMeasure();

                                    await VerifyPackageSignatureAsync(
                                        packageDownloader.Source,
                                        telemetry.OperationId,
                                        packageIdentity,
                                        packageExtractionContext,
                                        packageDownloader.SignedPackageReader,
                                        token);

                                    telemetry.EndIntervalMeasure(PackagingConstants.PackageVerifyDurationName);

                                }
                                catch (SignatureException)
                                {
                                    try
                                    {
                                        // Dispose of it now because it is holding a lock on the temporary .nupkg file.
                                        packageDownloader.Dispose();

                                        DeleteTargetAndTempPaths(targetPath, targetTempNupkg);
                                    }
                                    catch (IOException ex)
                                    {
                                        logger.LogWarning(string.Format(
                                            CultureInfo.CurrentCulture,
                                            Strings.ErrorUnableToDeleteFile,
                                            targetTempNupkg,
                                            ex.Message));
                                    }

                                    throw;
                                }
                            }

                            if (packageSaveMode.HasFlag(PackageSaveMode.Nuspec))
                            {
                                var nuspecFileNameFromReader = await packageDownloader.CoreReader.GetNuspecFileAsync(cancellationToken);
                                var packageFiles = new[] { nuspecFileNameFromReader };
                                var packageFileExtractor = new PackageFileExtractor(
                                    packageFiles,
                                    XmlDocFileSaveMode.None);
                                var packageDirectoryPath = Path.GetDirectoryName(targetNuspec);

                                var extractedNuspecFilePath = (await packageDownloader.CoreReader.CopyFilesAsync(
                                        packageDirectoryPath,
                                        packageFiles,
                                        packageFileExtractor.ExtractPackageFile,
                                        logger,
                                        cancellationToken))
                                    .SingleOrDefault();

                                // CopyFilesAsync(...) just extracts files to a directory.
                                // We may have to fix up the casing of the .nuspec file name.
                                if (!string.IsNullOrEmpty(extractedNuspecFilePath))
                                {
                                    if (PathUtility.IsFileSystemCaseInsensitive)
                                    {
                                        var nuspecFileName = Path.GetFileName(targetNuspec);
                                        var actualNuspecFileName = Path.GetFileName(extractedNuspecFilePath);

                                        if (!string.Equals(nuspecFileName, actualNuspecFileName, StringComparison.Ordinal))
                                        {
                                            var tempNuspecFilePath = Path.Combine(packageDirectoryPath, Path.GetRandomFileName());

                                            File.Move(extractedNuspecFilePath, tempNuspecFilePath);
                                            File.Move(tempNuspecFilePath, targetNuspec);
                                        }
                                    }
                                    else if (!File.Exists(targetNuspec))
                                    {
                                        File.Move(extractedNuspecFilePath, targetNuspec);
                                    }
                                }
                            }

                            if (packageSaveMode.HasFlag(PackageSaveMode.Files))
                            {
                                var hashFileName = Path.GetFileName(hashPath);
                                var nupkgMetadataFileName = Path.GetFileName(nupkgMetadataFilePath);
                                var packageFiles = (await packageDownloader.CoreReader.GetFilesAsync(cancellationToken))
                                    .Where(file => ShouldInclude(file, hashFileName, nupkgMetadataFileName));
                                var packageFileExtractor = new PackageFileExtractor(
                                    packageFiles,
                                    packageExtractionContext.XmlDocFileSaveMode);
                                await packageDownloader.CoreReader.CopyFilesAsync(
                                    targetPath,
                                    packageFiles,
                                    packageFileExtractor.ExtractPackageFile,
                                    logger,
                                    token);
                            }

                            var packageHash = await packageDownloader.GetPackageHashAsync("SHA512", cancellationToken);

                            File.WriteAllText(tempHashPath, packageHash);

                            // get hash for the unsigned content of package
                            var contentHash = packageDownloader.SignedPackageReader.GetContentHash(cancellationToken, GetUnsignedPackageHash: () => packageHash);

                            // write the new hash file
                            var hashFile = new NupkgMetadataFile()
                            {
                                ContentHash = contentHash,
                                Source = packageDownloader.Source
                            };

                            NupkgMetadataFileFormat.Write(tempNupkgMetadataFilePath, hashFile);

                            // Now rename the tmp file
                            if (packageExtractionContext.PackageSaveMode.HasFlag(PackageSaveMode.Nupkg))
                            {
                                if (copiedNupkg)
                                {
                                    // Dispose of it now because it is holding a lock on the temporary .nupkg file.
                                    packageDownloader.Dispose();

                                    File.Move(targetTempNupkg, targetNupkg);
                                }
                            }
                            else
                            {
                                try
                                {
                                    File.Delete(targetTempNupkg);
                                }
                                catch (IOException ex)
                                {
                                    logger.LogWarning(string.Format(
                                        CultureInfo.CurrentCulture,
                                        Strings.ErrorUnableToDeleteFile,
                                        targetTempNupkg,
                                        ex.Message));
                                }
                            }

                            // Note: PackageRepository relies on the hash file being written out as the
                            // final operation as part of a package install to assume a package was fully installed.
                            // Rename the tmp hash file
                            File.Move(tempHashPath, hashPath);

                            File.Move(tempNupkgMetadataFilePath, nupkgMetadataFilePath);

                            logger.LogInformation(StringFormatter.Log_InstalledPackage(packageIdentity.Id, packageIdentity.Version.OriginalVersion, packageDownloader.Source, contentHash, targetPath));

                            packageExtractionTelemetryEvent.SetResult(NuGetOperationStatus.Succeeded);
                            return true;
                        }
                        else
                        {
                            logger.LogVerbose("Lock not required - Package already installed "
                                                + $"{packageIdentity.Id} {packageIdentity.Version}");

                            packageExtractionTelemetryEvent.SetResult(NuGetOperationStatus.NoOp);
                            return false;
                        }
                    },
                    token: token);
            }
        }