private static void RunPackageInstall(string packageInstall, string arguments, ProgressReport progress) { // Run packageinstall.exe using (var process = Process.Start(new ProcessStartInfo(packageInstall, arguments) { UseShellExecute = false, CreateNoWindow = true, RedirectStandardError = true, RedirectStandardOutput = true, WorkingDirectory = Path.GetDirectoryName(packageInstall), })) { if (process == null) { throw new InvalidOperationException($"Could not start install package process [{packageInstall}] with options {arguments}"); } var errorOutput = new StringBuilder(); process.OutputDataReceived += (_, args) => { // Report progress if (progress != null && !string.IsNullOrEmpty(args.Data)) { var matches = powerShellProgressRegex.Match(args.Data); int percentageResult; if (matches.Success && int.TryParse(matches.Groups[1].Value, out percentageResult)) { progress.UpdateProgress(percentageResult); } } }; process.ErrorDataReceived += (_, args) => { if (!string.IsNullOrEmpty(args.Data)) { // Save errors lock (process) { errorOutput.AppendLine(args.Data); } } }; // Process output and wait for exit process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); // Check exit code var exitCode = process.ExitCode; if (exitCode != 0) { throw new InvalidOperationException($"Error code {exitCode} while running install package process [{packageInstall}]\n\n" + errorOutput); } } }
/// <summary> /// Fetch, if not already downloaded, and install the package represented by /// (<paramref name="packageId"/>, <paramref name="version"/>). /// </summary> /// <remarks>It is safe to call it concurrently be cause we operations are done using the FileLock.</remarks> /// <param name="packageId">Name of package to install.</param> /// <param name="version">Version of package to install.</param> /// <param name="progress">Callbacks to report progress of downloads.</param> public async Task InstallPackage(string packageId, PackageVersion version, ProgressReport progress) { using (GetLocalRepositoryLock()) { currentProgressReport = progress; try { var package = manager.LocalRepository.FindPackage(packageId, version.ToSemanticVersion(), null, allowPrereleaseVersions: true, allowUnlisted: true); if (package == null) { // Let's search in our cache try { package = MachineCache.Default.FindPackage(packageId, version.ToSemanticVersion(), allowPrereleaseVersions: true, allowUnlisted: true); } catch (InvalidDataException) { // Package is somehow corrupted. We ignore this and will redownload the file. } // It represents the name of the .nupkg in our cache var sourceName = Path.Combine(CacheDirectory, PathResolver.GetPackageFileName(packageId, version.ToSemanticVersion())); if (package == null) { // Always recreate cache in case it was deleted. if (!Directory.Exists(CacheDirectory)) { Directory.CreateDirectory(CacheDirectory); } package = manager.SourceRepository.FindPackage(packageId, version.ToSemanticVersion(), NullConstraintProvider.Instance, allowPrereleaseVersions: true, allowUnlisted: true); if (package == null) { throw new ApplicationException("Cannot find package"); } // Package has to be downloaded if it is a DataServicePackage which was not found in our cache. if (package is DataServicePackage) { var downloadPackage = (DataServicePackage)package; var url = downloadPackage.DownloadUrl; var client = new WebClient(); var tcs = new TaskCompletionSource <bool>(); progress?.UpdateProgress(ProgressAction.Download, 0); client.DownloadProgressChanged += (o, e) => progress?.UpdateProgress(ProgressAction.Download, e.ProgressPercentage); client.DownloadFileCompleted += (o, e) => tcs.SetResult(true); client.DownloadFileAsync(url, sourceName); await tcs.Task; progress?.UpdateProgress(ProgressAction.Download, 100); } } progress?.UpdateProgress(ProgressAction.Install, -1); manager.InstallPackage(package, ignoreDependencies: false, allowPrereleaseVersions: true); OptimizedZipPackage.PurgeCache(); } // Every time a new package is installed, we are updating the common targets UpdateTargetsHelper(); } finally { currentProgressReport = null; } } }