in src/NuGet.Core/NuGet.PackageManagement/NuGetPackageManager.cs [3261:3504]
public async Task ExecuteBuildIntegratedProjectActionsAsync(
BuildIntegratedNuGetProject buildIntegratedProject,
IEnumerable<NuGetProjectAction> nuGetProjectActions,
INuGetProjectContext nuGetProjectContext,
CancellationToken token)
{
if (buildIntegratedProject == null)
{
throw new ArgumentNullException(nameof(buildIntegratedProject));
}
BuildIntegratedProjectAction projectAction = null;
if (nuGetProjectActions.Count() == 1
&& nuGetProjectActions.All(action => action is BuildIntegratedProjectAction))
{
projectAction = nuGetProjectActions.Single() as BuildIntegratedProjectAction;
}
else if (nuGetProjectActions.Any())
{
projectAction = await PreviewBuildIntegratedProjectActionsAsync(
buildIntegratedProject,
nuGetProjectActions,
nuGetProjectContext,
token);
}
else
{
// There are no actions, this is a no-op
return;
}
var actions = projectAction.GetProjectActions();
// Check if all actions are uninstalls
var uninstallOnly = projectAction.NuGetProjectActionType == NuGetProjectActionType.Uninstall
&& actions.All(action => action.NuGetProjectActionType == NuGetProjectActionType.Uninstall);
var restoreResult = projectAction.RestoreResult;
// Avoid committing the changes if the restore did not succeed
// For uninstalls continue even if the restore failed to avoid blocking the user
if (restoreResult.Success || uninstallOnly)
{
// Get all install actions
var ignoreActions = new HashSet<NuGetProjectAction>();
var installedIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach ((var action, _) in projectAction.ActionAndContextList.Reverse())
{
if (action.NuGetProjectActionType == NuGetProjectActionType.Install)
{
installedIds.Add(action.PackageIdentity.Id);
}
else if (installedIds.Contains(action.PackageIdentity.Id))
{
ignoreActions.Add(action);
}
}
var pathResolver = new FallbackPackagePathResolver(
projectAction.RestoreResult.LockFile.PackageSpec.RestoreMetadata.PackagesPath,
projectAction.RestoreResult.LockFile.PackageSpec.RestoreMetadata.FallbackFolders);
foreach ((var originalAction, var installationContext) in projectAction.ActionAndContextList.Where(e => !ignoreActions.Contains(e.Item1)))
{
// This is where we calculate what goes into CPSPackageReferenceProject
if (originalAction.NuGetProjectActionType == NuGetProjectActionType.Install)
{
if (buildIntegratedProject.ProjectStyle == ProjectStyle.PackageReference)
{
BuildIntegratedRestoreUtility.UpdatePackageReferenceMetadata(
projectAction.RestoreResult.LockFile,
pathResolver,
originalAction.PackageIdentity);
var framework = installationContext.SuccessfulFrameworks.First();
var resolvedAction = projectAction.RestoreResult.LockFile.PackageSpec.TargetFrameworks.FirstOrDefault(fm => fm.FrameworkName.Equals(framework))
.Dependencies.First(dependency => dependency.Name.Equals(originalAction.PackageIdentity.Id, StringComparison.OrdinalIgnoreCase));
installationContext.SuppressParent = resolvedAction.SuppressParent;
installationContext.IncludeType = resolvedAction.IncludeType;
}
// Install the package to the project
await buildIntegratedProject.InstallPackageAsync(
originalAction.PackageIdentity.Id,
originalAction.VersionRange ?? new VersionRange(originalAction.PackageIdentity.Version),
nuGetProjectContext,
installationContext,
token: token);
}
else if (originalAction.NuGetProjectActionType == NuGetProjectActionType.Uninstall)
{
await buildIntegratedProject.UninstallPackageAsync(
originalAction.PackageIdentity.Id,
installationContext,
token: token);
}
}
var logger = new ProjectContextLogger(nuGetProjectContext);
var referenceContext = new DependencyGraphCacheContext(logger, Settings);
var now = DateTime.UtcNow;
void cacheContextModifier(SourceCacheContext c) => c.MaxAge = now;
// Write out the lock file, now no need bubbling re-evaluating of parent projects when you restore from PM UI.
// We already taken account of that concern in PreviewBuildIntegratedProjectsActionsAsync method.
bool isNoOp = projectAction.RestoreResultPair.Result is NoOpRestoreResult;
IReadOnlyList<string> filesToBeUpdated = isNoOp ? null : GetFilesToBeUpdated(projectAction.RestoreResultPair);
if (!isNoOp)
{
RestoreProgressReporter?.StartProjectUpdate(projectAction.RestoreResultPair.SummaryRequest.Request.Project.FilePath, filesToBeUpdated);
}
try
{
await RestoreRunner.CommitAsync(projectAction.RestoreResultPair, token);
}
finally
{
if (!isNoOp)
{
RestoreProgressReporter?.EndProjectUpdate(projectAction.RestoreResultPair.SummaryRequest.Request.Project.FilePath, filesToBeUpdated);
}
}
// add packages lock file into project
if (PackagesLockFileUtilities.IsNuGetLockFileEnabled(projectAction.RestoreResult.LockFile.PackageSpec))
{
var lockFilePath = PackagesLockFileUtilities.GetNuGetLockFilePath(projectAction.RestoreResult.LockFile.PackageSpec);
await buildIntegratedProject.AddFileToProjectAsync(lockFilePath);
}
// Write out a message for each action
foreach (var action in actions)
{
var identityString = string.Format(CultureInfo.InvariantCulture, "{0} {1}",
action.PackageIdentity.Id,
action.PackageIdentity.Version.ToNormalizedString());
if (action.NuGetProjectActionType == NuGetProjectActionType.Install)
{
nuGetProjectContext.Log(
MessageLevel.Info,
Strings.SuccessfullyInstalled,
identityString,
buildIntegratedProject.GetMetadata<string>(NuGetProjectMetadataKeys.Name));
}
else
{
// uninstall
nuGetProjectContext.Log(
MessageLevel.Info,
Strings.SuccessfullyUninstalled,
identityString,
buildIntegratedProject.GetMetadata<string>(NuGetProjectMetadataKeys.Name));
}
}
// Run init.ps1 scripts
var addedPackages = BuildIntegratedRestoreUtility.GetAddedPackages(
projectAction.OriginalLockFile,
restoreResult.LockFile);
await BuildIntegratedRestoreUtility.ExecuteInitPs1ScriptsAsync(
buildIntegratedProject,
addedPackages,
pathResolver,
nuGetProjectContext,
token);
// find list of buildintegrated projects
var projects = (await SolutionManager.GetNuGetProjectsAsync()).OfType<BuildIntegratedNuGetProject>().ToList();
// build reference cache if not done already
if (_buildIntegratedProjectsCache == null)
{
_buildIntegratedProjectsCache = await
DependencyGraphRestoreUtility.GetSolutionRestoreSpec(SolutionManager, referenceContext);
}
// Restore parent projects. These will be updated to include the transitive changes.
var parents = BuildIntegratedRestoreUtility.GetParentProjectsInClosure(
projects,
buildIntegratedProject,
_buildIntegratedProjectsCache);
// The settings contained in the context are applied to the dg spec.
var dgSpecForParents = await DependencyGraphRestoreUtility.GetSolutionRestoreSpec(SolutionManager, referenceContext);
dgSpecForParents = dgSpecForParents.WithoutRestores();
foreach (var parent in parents)
{
// We only evaluate unseen parents.
if (_buildIntegratedProjectsUpdateSet == null ||
!_buildIntegratedProjectsUpdateSet.Contains(parent.MSBuildProjectPath))
{
// Mark project for restore
dgSpecForParents.AddRestore(parent.MSBuildProjectPath);
_buildIntegratedProjectsUpdateSet?.Add(parent.MSBuildProjectPath);
}
}
if (dgSpecForParents.Restore.Count > 0)
{
// Restore and commit the lock file to disk regardless of the result
// This will restore all parents in a single restore
await DependencyGraphRestoreUtility.RestoreAsync(
dgSpecForParents,
referenceContext,
GetRestoreProviderCache(),
cacheContextModifier,
projectAction.Sources,
nuGetProjectContext.OperationId,
forceRestore: false, // No need to force restore as the inputs would've changed here anyways
isRestoreOriginalAction: false, // not an explicit restore request instead being done as part of install or update
additionalMessages: null,
progressReporter: RestoreProgressReporter,
log: logger,
token: token);
}
}
else
{
// Fail and display a rollback message to let the user know they have returned to the original state
var message = string.Format(
CultureInfo.InvariantCulture,
Strings.RestoreFailedRollingBack,
buildIntegratedProject.ProjectName);
// Read additional errors from the lock file if one exists
var logMessages = restoreResult.LockFile?
.LogMessages
.Where(e => e.Level == LogLevel.Error || e.Level == LogLevel.Warning)
.Select(e => e.AsRestoreLogMessage())
?? Enumerable.Empty<ILogMessage>();
// Throw an exception containing all errors, these will be displayed in the error list
throw new PackageReferenceRollbackException(message, logMessages);
}
await OpenReadmeFile(buildIntegratedProject, nuGetProjectContext, token);
}