in src/NuGet.Core/NuGet.PackageManagement/Projects/MSBuildNuGetProject.cs [142:415]
public override async Task<bool> InstallPackageAsync(
PackageIdentity packageIdentity,
DownloadResourceResult downloadResourceResult,
INuGetProjectContext nuGetProjectContext,
CancellationToken token)
{
if (packageIdentity == null)
{
throw new ArgumentNullException(nameof(packageIdentity));
}
if (downloadResourceResult == null)
{
throw new ArgumentNullException(nameof(downloadResourceResult));
}
if (nuGetProjectContext == null)
{
throw new ArgumentNullException(nameof(nuGetProjectContext));
}
if (downloadResourceResult.Status != DownloadResourceResultStatus.AvailableWithoutStream &&
!downloadResourceResult.PackageStream.CanSeek)
{
throw new ArgumentException(Strings.PackageStreamShouldBeSeekable);
}
// Step-1: Check if the package already exists after setting the nuGetProjectContext
ProjectSystem.NuGetProjectContext = nuGetProjectContext;
var packageReference = (await GetInstalledPackagesAsync(token))
.FirstOrDefault(p => p.PackageIdentity.Equals(packageIdentity));
if (packageReference != null)
{
nuGetProjectContext.Log(MessageLevel.Warning, Strings.PackageAlreadyExistsInProject,
packageIdentity, ProjectSystem.ProjectName);
return false;
}
// Step-2: Create PackageArchiveReader using the PackageStream and obtain the various item groups
if (downloadResourceResult.Status != DownloadResourceResultStatus.AvailableWithoutStream)
{
downloadResourceResult.PackageStream.Seek(0, SeekOrigin.Begin);
}
// These casts enforce use of -Async(...) methods.
var packageReader = downloadResourceResult.PackageReader
?? new PackageArchiveReader(downloadResourceResult.PackageStream, leaveStreamOpen: true);
IAsyncPackageContentReader packageContentReader = packageReader;
IAsyncPackageCoreReader packageCoreReader = packageReader;
var libItemGroups = await packageContentReader.GetLibItemsAsync(token);
var referenceItemGroups = await packageContentReader.GetReferenceItemsAsync(token);
var frameworkReferenceGroups = await packageContentReader.GetFrameworkItemsAsync(token);
var contentFileGroups = await packageContentReader.GetContentItemsAsync(token);
var buildFileGroups = await packageContentReader.GetBuildItemsAsync(token);
var toolItemGroups = await packageContentReader.GetToolItemsAsync(token);
// Step-3: Get the most compatible items groups for all items groups
var hasCompatibleProjectLevelContent = false;
var compatibleLibItemsGroup =
MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(ProjectSystem.TargetFramework, libItemGroups);
var compatibleReferenceItemsGroup =
MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(ProjectSystem.TargetFramework, referenceItemGroups);
var compatibleFrameworkReferencesGroup =
MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(ProjectSystem.TargetFramework, frameworkReferenceGroups);
var compatibleContentFilesGroup =
MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(ProjectSystem.TargetFramework, contentFileGroups);
var compatibleBuildFilesGroup =
MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(ProjectSystem.TargetFramework, buildFileGroups);
var compatibleToolItemsGroup =
MSBuildNuGetProjectSystemUtility.GetMostCompatibleGroup(ProjectSystem.TargetFramework, toolItemGroups);
compatibleLibItemsGroup
= MSBuildNuGetProjectSystemUtility.Normalize(compatibleLibItemsGroup);
compatibleReferenceItemsGroup
= MSBuildNuGetProjectSystemUtility.Normalize(compatibleReferenceItemsGroup);
compatibleFrameworkReferencesGroup
= MSBuildNuGetProjectSystemUtility.Normalize(compatibleFrameworkReferencesGroup);
compatibleContentFilesGroup
= MSBuildNuGetProjectSystemUtility.Normalize(compatibleContentFilesGroup);
compatibleBuildFilesGroup
= MSBuildNuGetProjectSystemUtility.Normalize(compatibleBuildFilesGroup);
compatibleToolItemsGroup
= MSBuildNuGetProjectSystemUtility.Normalize(compatibleToolItemsGroup);
hasCompatibleProjectLevelContent = MSBuildNuGetProjectSystemUtility.IsValid(compatibleLibItemsGroup) ||
MSBuildNuGetProjectSystemUtility.IsValid(compatibleFrameworkReferencesGroup) ||
MSBuildNuGetProjectSystemUtility.IsValid(compatibleContentFilesGroup) ||
MSBuildNuGetProjectSystemUtility.IsValid(compatibleBuildFilesGroup);
// Check if package has any content for project
var hasProjectLevelContent = libItemGroups.Any() || frameworkReferenceGroups.Any()
|| contentFileGroups.Any() || buildFileGroups.Any();
var onlyHasCompatibleTools = false;
var onlyHasDependencies = false;
if (!hasProjectLevelContent)
{
// Since it does not have project-level content, check if it has dependencies or compatible tools
// Note that we are not checking if it has compatible project level content, but, just that it has project level content
// If the package has project-level content, but nothing compatible, we still need to throw
// If a package does not have any project-level content, it can be a
// Legacy solution level packages which only has compatible tools group
onlyHasCompatibleTools = MSBuildNuGetProjectSystemUtility.IsValid(compatibleToolItemsGroup) && compatibleToolItemsGroup.Items.Any();
if (!onlyHasCompatibleTools)
{
// If it does not have compatible tool items either, check if it at least has dependencies
onlyHasDependencies = (await packageContentReader.GetPackageDependenciesAsync(token)).Any();
}
}
else
{
var shortFramework = ProjectSystem.TargetFramework.GetShortFolderName();
nuGetProjectContext.Log(MessageLevel.Debug, Strings.Debug_TargetFrameworkInfoPrefix, packageIdentity,
GetMetadata<string>(NuGetProjectMetadataKeys.Name), shortFramework);
}
// Step-4: Check if there are any compatible items in the package or that this is not a package with only tools group. If not, throw
if (!hasCompatibleProjectLevelContent
&& !onlyHasCompatibleTools
&& !onlyHasDependencies)
{
throw new InvalidOperationException(
string.Format(CultureInfo.CurrentCulture,
Strings.UnableToFindCompatibleItems, packageIdentity.Id + " " + packageIdentity.Version.ToNormalizedString(), ProjectSystem.TargetFramework));
}
if (hasCompatibleProjectLevelContent)
{
var shortFramework = ProjectSystem.TargetFramework.GetShortFolderName();
nuGetProjectContext.Log(MessageLevel.Debug, Strings.Debug_TargetFrameworkInfoPrefix, packageIdentity,
GetMetadata<string>(NuGetProjectMetadataKeys.Name), shortFramework);
}
else if (onlyHasCompatibleTools)
{
nuGetProjectContext.Log(MessageLevel.Info, Strings.AddingPackageWithOnlyToolsGroup, packageIdentity,
GetMetadata<string>(NuGetProjectMetadataKeys.Name));
}
else if (onlyHasDependencies)
{
nuGetProjectContext.Log(MessageLevel.Info, Strings.AddingPackageWithOnlyDependencies, packageIdentity,
GetMetadata<string>(NuGetProjectMetadataKeys.Name));
}
// Step-5: Raise PackageInstalling event
// At this point, GetInstalledPath is pointless since the package is, likely, not already installed. It will be empty
// Using PackagePathResolver.GetInstallPath would be wrong, since, package version from the nuspec is always used
var packageEventArgs = new PackageEventArgs(FolderNuGetProject, packageIdentity, installPath: string.Empty);
if (PackageInstalling != null)
{
PackageInstalling(this, packageEventArgs);
}
PackageEventsProvider.Instance.NotifyInstalling(packageEventArgs);
// Step-6: Install package to FolderNuGetProject
await FolderNuGetProject.InstallPackageAsync(packageIdentity, downloadResourceResult, nuGetProjectContext, token);
// Step-7: Raise PackageInstalled event
// Call GetInstalledPath to get the package installed path
var packageInstallPath = FolderNuGetProject.GetInstalledPath(packageIdentity);
packageEventArgs = new PackageEventArgs(FolderNuGetProject, packageIdentity, packageInstallPath);
if (PackageInstalled != null)
{
PackageInstalled(this, packageEventArgs);
}
PackageEventsProvider.Instance.NotifyInstalled(packageEventArgs);
// Step-8: MSBuildNuGetProjectSystem operations
// Step-8.1: Add references to project
if (!IsSkipAssemblyReferences(nuGetProjectContext) &&
MSBuildNuGetProjectSystemUtility.IsValid(compatibleReferenceItemsGroup))
{
foreach (var referenceItem in compatibleReferenceItemsGroup.Items)
{
if (IsAssemblyReference(referenceItem))
{
var referenceItemFullPath = Path.Combine(packageInstallPath, referenceItem);
var referenceName = Path.GetFileName(referenceItem);
if (await ProjectSystem.ReferenceExistsAsync(referenceName))
{
await ProjectSystem.RemoveReferenceAsync(referenceName);
}
await ProjectSystem.AddReferenceAsync(referenceItemFullPath);
}
}
}
// Step-8.2: Add Frameworkreferences to project
if (!IsSkipAssemblyReferences(nuGetProjectContext) &&
MSBuildNuGetProjectSystemUtility.IsValid(compatibleFrameworkReferencesGroup))
{
foreach (var frameworkReference in compatibleFrameworkReferencesGroup.Items)
{
if (!await ProjectSystem.ReferenceExistsAsync(frameworkReference))
{
await ProjectSystem.AddFrameworkReferenceAsync(frameworkReference, packageIdentity.Id);
}
}
}
// Step-8.3: Add Content Files
if (MSBuildNuGetProjectSystemUtility.IsValid(compatibleContentFilesGroup))
{
await MSBuildNuGetProjectSystemUtility.AddFilesAsync(
ProjectSystem,
packageCoreReader,
compatibleContentFilesGroup,
FileTransformers,
token);
}
// Step-8.4: Add Build imports
if (MSBuildNuGetProjectSystemUtility.IsValid(compatibleBuildFilesGroup))
{
foreach (var buildImportFile in compatibleBuildFilesGroup.Items)
{
var fullImportFilePath = Path.Combine(packageInstallPath, buildImportFile);
ProjectSystem.AddImport(fullImportFilePath,
fullImportFilePath.EndsWith(".props", StringComparison.OrdinalIgnoreCase) ? ImportLocation.Top : ImportLocation.Bottom);
}
}
// Step-9: Install package to PackagesConfigNuGetProject
await PackagesConfigNuGetProject.InstallPackageAsync(packageIdentity, downloadResourceResult, nuGetProjectContext, token);
// Step-10: Add packages.config to MSBuildNuGetProject
ProjectSystem.AddExistingFile(Path.GetFileName(PackagesConfigNuGetProject.FullPath));
// Step 11: Raise PackageReferenceAdded event
PackageReferenceAdded?.Invoke(this, packageEventArgs);
PackageEventsProvider.Instance.NotifyReferenceAdded(packageEventArgs);
// Step-12: Execute powershell script - install.ps1
var anyFrameworkToolsGroup = toolItemGroups.FirstOrDefault(g => g.TargetFramework.Equals(NuGetFramework.AnyFramework));
if (anyFrameworkToolsGroup != null)
{
var initPS1RelativePath = anyFrameworkToolsGroup.Items
.FirstOrDefault(p =>
p.StartsWith(PowerShellScripts.InitPS1RelativePath, StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(initPS1RelativePath))
{
initPS1RelativePath = PathUtility.ReplaceAltDirSeparatorWithDirSeparator(
initPS1RelativePath);
await ProjectServices.ScriptService.ExecutePackageScriptAsync(
packageIdentity,
packageInstallPath,
initPS1RelativePath,
nuGetProjectContext,
throwOnFailure: true,
token: token);
}
}
if (MSBuildNuGetProjectSystemUtility.IsValid(compatibleToolItemsGroup))
{
var installPS1RelativePath = compatibleToolItemsGroup.Items.FirstOrDefault(
p => p.EndsWith(Path.DirectorySeparatorChar + PowerShellScripts.Install, StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(installPS1RelativePath))
{
await ProjectServices.ScriptService.ExecutePackageScriptAsync(
packageIdentity,
packageInstallPath,
installPS1RelativePath,
nuGetProjectContext,
throwOnFailure: true,
token: token);
}
}
return true;
}