in src/NuGet.Core/NuGet.PackageManagement/NuGetPackageManager.cs [2498:2822]
public async Task ExecuteNuGetProjectActionsAsync(NuGetProject nuGetProject,
IEnumerable<NuGetProjectAction> nuGetProjectActions,
INuGetProjectContext nuGetProjectContext,
PackageDownloadContext downloadContext,
CancellationToken token)
{
if (nuGetProject == null)
{
throw new ArgumentNullException(nameof(nuGetProject));
}
if (nuGetProjectActions == null)
{
throw new ArgumentNullException(nameof(nuGetProjectActions));
}
if (nuGetProjectContext == null)
{
throw new ArgumentNullException(nameof(nuGetProjectContext));
}
var stopWatch = Stopwatch.StartNew();
ExceptionDispatchInfo exceptionInfo = null;
// DNU: Find the closure before executing the actions
var buildIntegratedProject = nuGetProject as BuildIntegratedNuGetProject;
if (buildIntegratedProject != null)
{
await ExecuteBuildIntegratedProjectActionsAsync(buildIntegratedProject,
nuGetProjectActions,
nuGetProjectContext,
token);
}
else
{
// Set the original packages config if it exists
var msbuildProject = nuGetProject as MSBuildNuGetProject;
if (msbuildProject != null)
{
nuGetProjectContext.OriginalPackagesConfig =
msbuildProject.PackagesConfigNuGetProject?.GetPackagesConfig();
}
var executedNuGetProjectActions = new Stack<NuGetProjectAction>();
var packageWithDirectoriesToBeDeleted = new HashSet<PackageIdentity>(PackageIdentity.Comparer);
var ideExecutionContext = nuGetProjectContext.ExecutionContext as IDEExecutionContext;
if (ideExecutionContext != null)
{
await ideExecutionContext.SaveExpandedNodeStates(SolutionManager);
}
var logger = new ProjectContextLogger(nuGetProjectContext);
Dictionary<PackageIdentity, PackagePreFetcherResult> downloadTasks = null;
CancellationTokenSource downloadTokenSource = null;
// batch events argument object
PackageProjectEventArgs packageProjectEventArgs = null;
try
{
// PreProcess projects
await nuGetProject.PreProcessAsync(nuGetProjectContext, token);
var actionsList = nuGetProjectActions.ToList();
var hasInstalls = actionsList.Any(action =>
action.NuGetProjectActionType == NuGetProjectActionType.Install);
if (hasInstalls)
{
// Make this independently cancelable.
downloadTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);
// Download all packages up front in parallel
downloadTasks = await PackagePreFetcher.GetPackagesAsync(
actionsList,
PackagesFolderNuGetProject,
downloadContext,
SettingsUtility.GetGlobalPackagesFolder(Settings),
logger,
downloadTokenSource.Token);
// Log download information
PackagePreFetcher.LogFetchMessages(
downloadTasks.Values,
PackagesFolderNuGetProject.Root,
logger);
}
// raise Nuget batch start event
var batchId = Guid.NewGuid().ToString();
string name;
nuGetProject.TryGetMetadata(NuGetProjectMetadataKeys.Name, out name);
var projectPath = msbuildProject?.MSBuildProjectPath;
packageProjectEventArgs = new PackageProjectEventArgs(batchId, name, projectPath);
BatchStart?.Invoke(this, packageProjectEventArgs);
PackageProjectEventsProvider.Instance.NotifyBatchStart(packageProjectEventArgs);
try
{
if (msbuildProject != null)
{
//start batch processing for msbuild
await msbuildProject.ProjectSystem.BeginProcessingAsync();
}
foreach (var nuGetProjectAction in actionsList)
{
if (nuGetProjectAction.NuGetProjectActionType == NuGetProjectActionType.Uninstall)
{
executedNuGetProjectActions.Push(nuGetProjectAction);
await ExecuteUninstallAsync(nuGetProject,
nuGetProjectAction.PackageIdentity,
packageWithDirectoriesToBeDeleted,
nuGetProjectContext, token);
nuGetProjectContext.Log(
ProjectManagement.MessageLevel.Info,
Strings.SuccessfullyUninstalled,
nuGetProjectAction.PackageIdentity,
nuGetProject.GetMetadata<string>(NuGetProjectMetadataKeys.Name));
}
}
}
finally
{
if (msbuildProject != null)
{
// end batch for msbuild and let it save everything.
// always calls it before PostProcessAsync or binding redirects
await msbuildProject.ProjectSystem.EndProcessingAsync();
}
}
try
{
if (msbuildProject != null)
{
//start batch processing for msbuild
await msbuildProject.ProjectSystem.BeginProcessingAsync();
}
foreach (var nuGetProjectAction in actionsList)
{
if (nuGetProjectAction.NuGetProjectActionType == NuGetProjectActionType.Install)
{
executedNuGetProjectActions.Push(nuGetProjectAction);
// Retrieve the downloaded package
// This will wait on the package if it is still downloading
var preFetchResult = downloadTasks[nuGetProjectAction.PackageIdentity];
using (var downloadPackageResult = await preFetchResult.GetResultAsync())
{
// use the version exactly as specified in the nuspec file
var packageIdentity = await downloadPackageResult.PackageReader.GetIdentityAsync(token);
await ExecuteInstallAsync(
nuGetProject,
packageIdentity,
downloadPackageResult,
packageWithDirectoriesToBeDeleted,
nuGetProjectContext,
token);
}
var identityString = string.Format(CultureInfo.InvariantCulture, "{0} {1}",
nuGetProjectAction.PackageIdentity.Id,
nuGetProjectAction.PackageIdentity.Version.ToNormalizedString());
preFetchResult.EmitTelemetryEvent(nuGetProjectContext.OperationId);
nuGetProjectContext.Log(
ProjectManagement.MessageLevel.Info,
Strings.SuccessfullyInstalled,
identityString,
nuGetProject.GetMetadata<string>(NuGetProjectMetadataKeys.Name));
}
}
}
finally
{
if (msbuildProject != null)
{
// end batch for msbuild and let it save everything.
// always calls it before PostProcessAsync or binding redirects
await msbuildProject.ProjectSystem.EndProcessingAsync();
}
}
PackagesConfigLockFileUtility.UpdateLockFile(msbuildProject,
actionsList,
token);
// Post process
await nuGetProject.PostProcessAsync(nuGetProjectContext, token);
// Open readme file
await OpenReadmeFile(nuGetProject, nuGetProjectContext, token);
}
catch (SignatureException ex)
{
var errors = ex.Results.SelectMany(r => r.GetErrorIssues());
var warnings = ex.Results.SelectMany(r => r.GetWarningIssues());
SignatureException unwrappedException = null;
if (errors.Count() == 1)
{
// In case of one error, throw it as the exception
var error = errors.First();
unwrappedException = new SignatureException(error.Code, error.Message, ex.PackageIdentity);
}
else
{
// In case of multiple errors, wrap them in a general NU3000 error
var errorMessage = string.Format(CultureInfo.CurrentCulture,
Strings.SignatureVerificationMultiple,
$"{Environment.NewLine}{string.Join(Environment.NewLine, errors.Select(e => e.FormatWithCode()))}");
unwrappedException = new SignatureException(NuGetLogCode.NU3000, errorMessage, ex.PackageIdentity);
}
foreach (var warning in warnings)
{
nuGetProjectContext.Log(warning);
}
exceptionInfo = ExceptionDispatchInfo.Capture(unwrappedException);
}
catch (Exception ex)
{
exceptionInfo = ExceptionDispatchInfo.Capture(ex);
}
finally
{
if (downloadTasks != null)
{
// Wait for all downloads to cancel and dispose
downloadTokenSource.Cancel();
foreach (var result in downloadTasks.Values)
{
await result.EnsureResultAsync();
result.Dispose();
}
}
downloadTokenSource?.Dispose();
if (msbuildProject != null)
{
// raise nuget batch end event
if (packageProjectEventArgs != null)
{
BatchEnd?.Invoke(this, packageProjectEventArgs);
PackageProjectEventsProvider.Instance.NotifyBatchEnd(packageProjectEventArgs);
}
}
}
if (exceptionInfo != null)
{
await RollbackAsync(nuGetProject, executedNuGetProjectActions, packageWithDirectoriesToBeDeleted, nuGetProjectContext, token);
}
if (ideExecutionContext != null)
{
await ideExecutionContext.CollapseAllNodes(SolutionManager);
}
// Delete the package directories as the last step, so that, if an uninstall had to be rolled back, we can just use the package file on the directory
// Also, always perform deletion of package directories, even in a rollback, so that there are no stale package directories
foreach (var packageWithDirectoryToBeDeleted in packageWithDirectoriesToBeDeleted)
{
var packageFolderPath = PackagesFolderNuGetProject.GetInstalledPath(packageWithDirectoryToBeDeleted);
try
{
await DeletePackageAsync(packageWithDirectoryToBeDeleted, nuGetProjectContext, token);
}
finally
{
if (DeleteOnRestartManager != null)
{
if (Directory.Exists(packageFolderPath))
{
DeleteOnRestartManager.MarkPackageDirectoryForDeletion(
packageWithDirectoryToBeDeleted,
packageFolderPath,
nuGetProjectContext);
// Raise the event to notify listners to update the UI etc.
DeleteOnRestartManager.CheckAndRaisePackageDirectoriesMarkedForDeletion();
}
}
}
}
// Save project
await nuGetProject.SaveAsync(token);
// Clear direct install
SetDirectInstall(null, nuGetProjectContext);
}
// calculate total time taken to execute all nuget actions
stopWatch.Stop();
nuGetProjectContext.Log(
MessageLevel.Info, Strings.NugetActionsTotalTime,
DatetimeUtility.ToReadableTimeFormat(stopWatch.Elapsed));
// emit resolve actions telemetry event
var actionTelemetryEvent = new ActionTelemetryStepEvent(
nuGetProjectContext.OperationId.ToString(),
TelemetryConstants.ExecuteActionStepName, stopWatch.Elapsed.TotalSeconds);
NuGetTelemetryService?.EmitTelemetryEvent(actionTelemetryEvent);
if (exceptionInfo != null)
{
exceptionInfo.Throw();
}
}