/// <summary> /// </summary> /// <param name="fastPackageReference"></param> /// <param name="request"> /// An object passed in from the CORE that contains functions that can be used to interact with the /// CORE and HOST /// </param> public void InstallPackage(string fastPackageReference, NuGetRequest request) { // Nice-to-have put a debug message in that tells what's going on. request.Debug("Calling '{0}::InstallPackage' '{1}'", PackageProviderName, fastPackageReference); var pkgRef = request.GetPackageByFastpath(fastPackageReference); if (pkgRef == null) { request.Error(ErrorCategory.InvalidArgument, fastPackageReference, Constants.Messages.UnableToResolvePackage); return; } // need to make sure that the original package's sources list is tried first. request.OriginalSources = pkgRef.Sources; var dependencies = request.GetUninstalledPackageDependencies(pkgRef).Reverse().ToArray(); int progressId = 0; if (dependencies.Length > 0) { progressId = request.StartProgress(0, "Installing package '{0}'", pkgRef.GetCanonicalId(request)); } var n = 0; foreach (var d in dependencies) { request.Progress(progressId, (n*100/(dependencies.Length + 1)) + 1, "Installing dependent package '{0}'", d.GetCanonicalId(request)); if (!request.InstallSinglePackage(d)) { request.Error(ErrorCategory.InvalidResult, pkgRef.GetCanonicalId(request), Constants.Messages.DependentPackageFailedInstall, d.GetCanonicalId(request)); return; } n++; request.Progress(progressId, (n*100/(dependencies.Length + 1)), "Installed dependent package '{0}'", d.GetCanonicalId(request)); } // got this far, let's install the package we came here for. if (!request.InstallSinglePackage(pkgRef)) { // package itself didn't install. // roll that back out everything we did install. // and get out of here. request.Error(ErrorCategory.InvalidResult, pkgRef.GetCanonicalId(request), Constants.Messages.PackageFailedInstall, pkgRef.GetCanonicalId(request)); request.CompleteProgress(progressId, false); } request.CompleteProgress(progressId, true); }
/// <summary> /// Download data from remote via uri query. /// </summary> /// <param name="fileName">A file to store the downloaded data.</param> /// <param name="query">Uri query</param> /// <param name="request">An object passed in from the PackageManagement platform that contains APIs that can be used to interact with it </param> /// <param name="networkCredential">Credential to pass along to get httpclient</param> /// <param name="progressTracker">Utility class to help track progress</param> /// <returns></returns> internal static async Task<long> DownloadDataToFileAsync(string fileName, string query, NuGetRequest request, NetworkCredential networkCredential, ProgressTracker progressTracker) { request.Verbose(Messages.DownloadingPackage, query); var httpClient = request.Client; // try downloading for 3 times int remainingTry = 3; long totalDownloaded = 0; long totalBytesToReceive = 0; bool cleanUp = false; CancellationTokenSource cts = new CancellationTokenSource(); ; Stream input = null; Timer timer = null; FileStream output = null; object lockObject = new object(); // function to perform cleanup Action cleanUpAction = () => { lock (lockObject) { // if clean up is done before, don't need to do again if (!cleanUp) { try { // dispose timer if (timer != null) { timer.Change(Timeout.Infinite, Timeout.Infinite); timer.Dispose(); } // dispose cts token if (cts != null) { cts.Cancel(); cts.Dispose(); } } catch { } try { // dispose input and output stream if (input != null) { input.Dispose(); } // it is important that we dispose of the output here, otherwise we may not be able to delete the file if (output != null) { output.Dispose(); } // if the download didn't complete, log verbose message if (totalBytesToReceive != totalDownloaded) { request.Verbose(string.Format(Resources.Messages.IncompleteDownload, totalDownloaded, totalBytesToReceive)); } // if we couldn't download anything if (totalDownloaded == 0 && File.Exists(fileName)) { File.Delete(fileName); } } catch { } cleanUp = true; } } }; while (remainingTry > 0) { // if user cancel the request, no need to do anything if (request.IsCanceled) { break; } input = null; output = null; totalDownloaded = 0; try { // decrease try by 1 remainingTry -= 1; // create new timer and cancellation token source lock (lockObject) { // check every second to see whether request is cancelled timer = new Timer(_ => { if (request.IsCanceled) { cleanUpAction(); } }, null, 500, 1000); cts = new CancellationTokenSource(); cleanUp = false; } var response = await httpClient.GetAsync(query, HttpCompletionOption.ResponseHeadersRead, cts.Token); if (response.Content != null && response.Content.Headers != null) { totalBytesToReceive = response.Content.Headers.ContentLength ?? 0; // the total amount of bytes we need to download in megabytes double totalBytesToReceiveMB = (totalBytesToReceive / 1024f) / 1024f; // Read response asynchronously and write out a file // The return value is for the caller to wait for the async operation to complete. input = await response.Content.ReadAsStreamAsync(); // buffer size of 64 KB, this seems to be preferable buffer size, not too small and not too big byte[] bytes = new byte[1024 * 64]; output = File.Open(fileName, FileMode.OpenOrCreate); int current = 0; double lastPercent = 0; // here we read content that we got from the http response stream into the bytes array current = await input.ReadAsync(bytes, 0, bytes.Length, cts.Token); // report initial progress request.Progress(progressTracker.ProgressID, progressTracker.StartPercent, string.Format(CultureInfo.CurrentCulture, Resources.Messages.DownloadingProgress, 0, (totalBytesToReceive / 1024f) / 1024f)); while (current > 0) { totalDownloaded += current; // here we write the bytes array content into the file await output.WriteAsync(bytes, 0, current, cts.Token); double percent = totalDownloaded * 1.0 / totalBytesToReceive; // don't want to report too often (slow down performance) if (percent > lastPercent + 0.1) { lastPercent = percent; // percent between startProgress and endProgress var progressPercent = progressTracker.ConvertPercentToProgress(percent); // report the progress request.Progress(progressTracker.ProgressID, (int)progressPercent, string.Format(CultureInfo.CurrentCulture, Resources.Messages.DownloadingProgress, (totalDownloaded / 1024f) / 1024f, totalBytesToReceiveMB)); } // here we read content that we got from the http response stream into the bytes array current = await input.ReadAsync(bytes, 0, bytes.Length, cts.Token); } // check that we download everything if (totalDownloaded == totalBytesToReceive) { // report that we finished with the download request.Progress(progressTracker.ProgressID, progressTracker.EndPercent, string.Format(CultureInfo.CurrentCulture, Resources.Messages.DownloadingProgress, totalDownloaded, totalBytesToReceive)); request.Verbose(Messages.CompletedDownload, query); break; } // otherwise, we have to retry again } // if request is canceled, don't retry if (request.IsCanceled) { break; } request.Verbose(Resources.Messages.RetryingDownload, query, remainingTry); } catch (Exception ex) { request.Verbose(ex.Message); request.Debug(ex.StackTrace); // if there is exception, we will retry too request.Verbose(Resources.Messages.RetryingDownload, query, remainingTry); } finally { cleanUpAction(); } } return totalDownloaded; }
/// <summary> /// Install a single package. Also install any of its dependency if they are available (the dependency will be installed first). /// For dependencies, we will only get those that are not installed. /// Operation is either install or download /// installOrDownloadFunction is a function that takes in a packageitem and performs either install or download on it /// </summary> /// <param name="pkgItem"></param> /// <param name="request"></param> /// <param name="operation"></param> /// <param name="installOrDownloadFunction"></param> /// <returns></returns> internal static bool InstallOrDownloadPackageHelper(PackageItem pkgItem, NuGetRequest request, string operation, Func<PackageItem, bool> installOrDownloadFunction) { // pkgItem.Sources is the source that the user input. The request will try this source. request.OriginalSources = pkgItem.Sources; int progressId = 0; bool hasDependencyLoop = false; // Get the dependencies that are not already installed var dependencies = NuGetClient.GetPackageDependenciesToInstall(request, pkgItem, ref hasDependencyLoop).ToArray(); // If there is a dependency loop. Warn the user and don't install the package if (hasDependencyLoop) { // package itself didn't install. Report error request.WriteError(ErrorCategory.DeadlockDetected, pkgItem.Id, Constants.Messages.DependencyLoopDetected, pkgItem.Id); return false; } // request may get canceled if there is a package dependencies missing if (request.IsCanceled) { return false; } int n = 0; int numberOfDependencies = dependencies.Count(); // Start progress progressId = request.StartProgress(0, string.Format(CultureInfo.InvariantCulture, Messages.InstallingOrDownloadingPackage, operation, pkgItem.Id)); try { // check that this package has dependency and the user didn't want to skip dependencies if (numberOfDependencies > 0) { // let's install dependencies foreach (var dep in dependencies) { request.Progress(progressId, (n * 100 / (numberOfDependencies + 1)) + 1, string.Format(CultureInfo.InvariantCulture, Messages.InstallingOrDownloadingDependencyPackage, operation, dep.Id)); // Check that we successfully installed the dependency if (!installOrDownloadFunction(dep)) { request.WriteError(ErrorCategory.InvalidResult, dep.Id, Constants.Messages.DependentPackageFailedInstallOrDownload, dep.Id, CultureInfo.CurrentCulture.TextInfo.ToLower(operation)); return false; } n++; request.Progress(progressId, (n * 100 / (numberOfDependencies + 1)), string.Format(CultureInfo.InvariantCulture, Messages.InstalledOrDownloadedDependencyPackage, operation, dep.Id)); } } // Now let's install the main package if (installOrDownloadFunction(pkgItem)) { return true; } } catch (Exception ex) { ex.Dump(request); } finally { // Report that we have completed installing the package and its dependency this does not mean there are no errors. // Just that it's completed. request.CompleteProgress(progressId, false); } // package itself didn't install. Report error request.WriteError(ErrorCategory.InvalidResult, pkgItem.Id, Constants.Messages.PackageFailedInstallOrDownload, pkgItem.Id, CultureInfo.CurrentCulture.TextInfo.ToLower(operation)); return false; }
/// <summary> /// Download a package from a file repository that matches the given version and name and install it on the local system. /// </summary> /// <param name="packageName">Package name</param> /// <param name="version">Package version</param> /// <param name="request">An object passed in from the PackageManagement platform that contains APIs that can be used to interact with it </param> /// <param name="source">Package source</param> /// <param name="sourceFilePath">File source path pointing to the package to be installed</param> /// <param name="progressTracker">progress tracker to help keep track of progressid, start and end of the progress</param> /// <returns>PackageItem object</returns> internal static PackageItem InstallPackageLocal( string packageName, string version, NuGetRequest request, PackageSource source, string sourceFilePath, ProgressTracker progressTracker ) { request.Debug(Messages.DebugInfoCallMethod, "NuGetClient", "InstallPackageLocal"); string tempSourceFilePath = null; string tempSourceDirectory = null; string directoryToDeleteWhenFailed = String.Empty; bool needToDelete = false; try { string destinationFilePath = request.Destination; request.Verbose(string.Format(CultureInfo.InvariantCulture, "InstallPackageLocal' - name='{0}', version='{1}',destination='{2}'", packageName, version, destinationFilePath)); request.Debug(sourceFilePath); if (string.IsNullOrWhiteSpace(sourceFilePath)) { throw new ArgumentNullException(sourceFilePath); } if (!File.Exists(sourceFilePath)) { throw new FileNotFoundException(sourceFilePath); } //Create the destination directory if it does not exist if (!Directory.Exists(destinationFilePath)) { Directory.CreateDirectory(destinationFilePath); directoryToDeleteWhenFailed = destinationFilePath; } //Make a temp folder in the user appdata temp directory tempSourceFilePath = FileUtility.GetTempFileFullPath(fileExtension: NuGetConstant.PackageExtension); //Copy over the source file from the folder repository to the temp folder File.Copy(sourceFilePath, tempSourceFilePath, true); request.Progress(progressTracker.ProgressID, progressTracker.StartPercent, string.Format(CultureInfo.CurrentCulture, Messages.Unzipping)); //Unzip it tempSourceDirectory = PackageUtility.DecompressFile(tempSourceFilePath); //Get a package directory under the destination path to store the package string installedFolder = FileUtility.MakePackageDirectoryName(request.ExcludeVersion.Value, destinationFilePath, packageName, version); // if we did not set the directory before, then the destinationFilePath already exists, so we should not delete it if (string.IsNullOrWhiteSpace(directoryToDeleteWhenFailed)) { directoryToDeleteWhenFailed = installedFolder; } //File folder format of the Nuget packages looks like the following after installed: //Jquery.2.0.1 // - JQuery.2.0.1.nupkg // - contents and other stuff // unzipping should take most of the time (assuming 70%) request.Progress(progressTracker.ProgressID, progressTracker.ConvertPercentToProgress(0.7), string.Format(CultureInfo.CurrentCulture, Messages.CopyUnzippedFiles, installedFolder)); //Copy the unzipped files to under the package installed folder FileUtility.CopyDirectory(tempSourceDirectory, installedFolder, true); // copying should take another 15% // copy the nupkg file if it's not in var nupkgFilePath = Path.Combine(installedFolder, FileUtility.MakePackageFileName(request.ExcludeVersion.Value, packageName, version, NuGetConstant.PackageExtension)); if (!File.Exists(nupkgFilePath)) { File.Copy(sourceFilePath, nupkgFilePath); } request.Progress(progressTracker.ProgressID, progressTracker.ConvertPercentToProgress(0.85), string.Format(CultureInfo.CurrentCulture, Messages.ReadingManifest)); //Read the package manifest and return the package object string nuspec = Path.Combine(installedFolder, packageName) + NuGetConstant.ManifestExtension; PackageBase package = PackageUtility.ProcessNuspec(nuspec); var pkgItem = new PackageItem { Package = package, PackageSource = source, FastPath = request.MakeFastPath(source, package.Id, package.Version), FullPath = installedFolder }; // Delete the nuspec file //Get a package file path var nuspecFilePath = Path.Combine(installedFolder, packageName + NuGetConstant.ManifestExtension); if (File.Exists(nuspecFilePath)) { FileUtility.DeleteFile(nuspecFilePath, false); } request.Debug(Messages.DebugInfoReturnCall, "NuGetClient", "InstallPackageLocal"); request.Progress(progressTracker.ProgressID, progressTracker.EndPercent, string.Format(CultureInfo.CurrentCulture, Messages.FinishInstalling, packageName)); return pkgItem; } catch (Exception ex) { needToDelete = true; // the error will be package "packageName" failed to install because : "reason" ex.Dump(request); request.WriteError(ErrorCategory.InvalidResult, packageName, Resources.Messages.PackageFailedToInstallReason, packageName, ex.Message); throw; } finally { if (needToDelete && Directory.Exists(directoryToDeleteWhenFailed)) { FileUtility.DeleteDirectory(directoryToDeleteWhenFailed, true, isThrow: false); } FileUtility.DeleteFile(tempSourceFilePath, isThrow:false); FileUtility.DeleteDirectory(tempSourceDirectory, recursive: true, isThrow: false); } }