/// <summary> /// Accounts for using the restore commands on 2 projects living in the same path /// </summary> private bool VerifyAssetsFileMatchesProject() { if (_request.Project.RestoreMetadata.ProjectStyle == ProjectStyle.DotnetCliTool) { return(true); } var pathComparer = PathUtility.GetStringComparerBasedOnOS(); return(_request.ExistingLockFile != null && pathComparer.Equals(_request.ExistingLockFile.PackageSpec.FilePath, _request.Project.FilePath)); }
/// <summary> /// Create restore requests but do not execute them. /// </summary> public static async Task <IReadOnlyList <RestoreSummaryRequest> > GetRequests(RestoreArgs restoreContext) { // Get requests var requests = new List <RestoreSummaryRequest>(); var inputs = new List <string>(restoreContext.Inputs); // If there are no inputs, use the current directory if (restoreContext.PreLoadedRequestProviders.Count < 1 && !inputs.Any()) { inputs.Add(Path.GetFullPath(".")); } var uniqueRequest = new HashSet <string>(PathUtility.GetStringComparerBasedOnOS()); // Create requests // Pre-loaded requests foreach (var request in await CreatePreLoadedRequests(restoreContext)) { // De-dupe requests if (request.Request.LockFilePath == null || uniqueRequest.Add(request.Request.LockFilePath)) { requests.Add(request); } } // Input based requests foreach (var input in inputs) { var inputRequests = await CreateRequests(input, restoreContext); if (inputRequests.Count == 0) { // No need to throw here - the situation is harmless, and we want to report all possible // inputs that don't resolve to a project. var message = string.Format( CultureInfo.CurrentCulture, Strings.Error_UnableToLocateRestoreTarget, Path.GetFullPath(input)); await restoreContext.Log.LogAsync(RestoreLogMessage.CreateWarning(NuGetLogCode.NU1501, message)); } foreach (var request in inputRequests) { // De-dupe requests if (uniqueRequest.Add(request.Request.LockFilePath)) { requests.Add(request); } } } return(requests); }
private static void AddProjectReferences(PackageSpec spec, IEnumerable <IMSBuildItem> items) { // Add groups for each spec framework var aliasGroups = new Dictionary <string, List <ProjectRestoreReference> >(); foreach (string alias in spec.TargetFrameworks.Select(e => e.TargetAlias).Distinct()) { aliasGroups.Add(alias, new List <ProjectRestoreReference>()); } var flatReferences = GetItemByType(items, "ProjectReference") .Select(GetProjectRestoreReference); var comparer = PathUtility.GetStringComparerBasedOnOS(); // Add project paths foreach (var frameworkPair in flatReferences) { // If no frameworks were given, apply to all var addToFrameworks = frameworkPair.Item1.Count == 0 ? aliasGroups.Keys.ToList() : frameworkPair.Item1; foreach (var framework in addToFrameworks) { List <ProjectRestoreReference> references; if (aliasGroups.TryGetValue(framework, out references)) { // Ensure unique if (!references.Any(e => comparer.Equals(e.ProjectUniqueName, frameworkPair.Item2.ProjectUniqueName))) { references.Add(frameworkPair.Item2); } } } } // Add groups to spec foreach (KeyValuePair <string, List <ProjectRestoreReference> > frameworkPair in aliasGroups) { TargetFrameworkInformation targetFrameworkInformation = spec.TargetFrameworks.Single(e => e.TargetAlias.Equals(frameworkPair.Key, StringComparison.Ordinal)); spec.RestoreMetadata.TargetFrameworks.Add(new ProjectRestoreMetadataFrameworkInfo(targetFrameworkInformation.FrameworkName) { ProjectReferences = frameworkPair.Value, TargetAlias = targetFrameworkInformation.TargetAlias }); } }
/// <summary> /// Remove missing project dependencies. These are typically caused by /// non-NuGet projects which are missing the targets needed to walk them. /// Visual Studio ignores these projects so from the command line we should /// also. Build will fail with the appropriate errors for missing projects /// restore should not warn or message for this. /// </summary> public static void RemoveMissingProjects(DependencyGraphSpec graphSpec) { var existingProjects = new HashSet <string>( graphSpec.Projects.Select(e => e.RestoreMetadata.ProjectPath), PathUtility.GetStringComparerBasedOnOS()); foreach (var project in graphSpec.Projects) { foreach (var framework in project.RestoreMetadata.TargetFrameworks) { foreach (var projectReference in framework.ProjectReferences.ToArray()) { if (!existingProjects.Contains(projectReference.ProjectPath)) { framework.ProjectReferences.Remove(projectReference); } } } } }
/// <summary> /// Convert MSBuild items to a DependencyGraphSpec. /// </summary> public static DependencyGraphSpec GetDependencySpec(IEnumerable <IMSBuildItem> items) { if (items == null) { throw new ArgumentNullException(nameof(items)); } // Unique names created by the MSBuild restore target are project paths, these // can be different on case-insensitive file systems for the same project file. // To workaround this unique names should be compared based on the OS. var uniqueNameComparer = PathUtility.GetStringComparerBasedOnOS(); var graphSpec = new DependencyGraphSpec(); var itemsById = new Dictionary <string, List <IMSBuildItem> >(uniqueNameComparer); var restoreSpecs = new HashSet <string>(uniqueNameComparer); var validForRestore = new HashSet <string>(uniqueNameComparer); var projectPathLookup = new Dictionary <string, string>(uniqueNameComparer); var toolItems = new List <IMSBuildItem>(); // Sort items and add restore specs foreach (var item in items) { var projectUniqueName = item.GetProperty("ProjectUniqueName"); if (item.IsType("restorespec")) { restoreSpecs.Add(projectUniqueName); } else if (!string.IsNullOrEmpty(projectUniqueName)) { List <IMSBuildItem> idItems; if (!itemsById.TryGetValue(projectUniqueName, out idItems)) { idItems = new List <IMSBuildItem>(1); itemsById.Add(projectUniqueName, idItems); } idItems.Add(item); } } // Add projects var validProjectSpecs = itemsById.Values.Select(GetPackageSpec).Where(e => e != null); foreach (var spec in validProjectSpecs) { // Keep track of all project path casings var uniqueName = spec.RestoreMetadata.ProjectUniqueName; if (uniqueName != null && !projectPathLookup.ContainsKey(uniqueName)) { projectPathLookup.Add(uniqueName, uniqueName); } var projectPath = spec.RestoreMetadata.ProjectPath; if (projectPath != null && !projectPathLookup.ContainsKey(projectPath)) { projectPathLookup.Add(projectPath, projectPath); } if (spec.RestoreMetadata.ProjectStyle == ProjectStyle.PackageReference || spec.RestoreMetadata.ProjectStyle == ProjectStyle.ProjectJson || spec.RestoreMetadata.ProjectStyle == ProjectStyle.DotnetCliTool || spec.RestoreMetadata.ProjectStyle == ProjectStyle.Standalone || spec.RestoreMetadata.ProjectStyle == ProjectStyle.DotnetToolReference) { validForRestore.Add(spec.RestoreMetadata.ProjectUniqueName); } graphSpec.AddProject(spec); } // Fix project reference casings to match the original project on case insensitive file systems. NormalizePathCasings(projectPathLookup, graphSpec); // Remove references to projects that could not be read by restore. RemoveMissingProjects(graphSpec); // Add valid projects to restore section foreach (var projectUniqueName in restoreSpecs.Intersect(validForRestore)) { graphSpec.AddRestore(projectUniqueName); } return(graphSpec); }
public PackagesLockFile CreateNuGetLockFile(LockFile assetsFile) { var lockFile = new PackagesLockFile(GetPackagesLockFileVersion(assetsFile)); var libraryLookup = assetsFile.Libraries.Where(e => e.Type == LibraryType.Package) .ToDictionary(e => new PackageIdentity(e.Name, e.Version)); foreach (var target in assetsFile.Targets) { var nuGettarget = new PackagesLockFileTarget() { TargetFramework = target.TargetFramework, RuntimeIdentifier = target.RuntimeIdentifier }; var framework = assetsFile.PackageSpec.TargetFrameworks.FirstOrDefault( f => EqualityUtility.EqualsWithNullCheck(f.FrameworkName, target.TargetFramework)); IEnumerable <LockFileTargetLibrary> libraries = target.Libraries; // check if this is RID-based graph then only add those libraries which differ from original TFM. if (!string.IsNullOrEmpty(target.RuntimeIdentifier)) { var onlyTFM = assetsFile.Targets.First(t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, target.TargetFramework)); libraries = target.Libraries.Where(lib => !onlyTFM.Libraries.Any(tfmLib => tfmLib.Equals(lib))); } foreach (var library in libraries.Where(e => e.Type == LibraryType.Package)) { var identity = new PackageIdentity(library.Name, library.Version); var dependency = new LockFileDependency() { Id = library.Name, ResolvedVersion = library.Version, ContentHash = libraryLookup[identity].Sha512, Dependencies = library.Dependencies }; var framework_dep = framework?.Dependencies.FirstOrDefault( dep => StringComparer.OrdinalIgnoreCase.Equals(dep.Name, library.Name)); CentralPackageVersion centralPackageVersion = null; framework?.CentralPackageVersions.TryGetValue(library.Name, out centralPackageVersion); if (framework_dep != null) { dependency.Type = PackageDependencyType.Direct; dependency.RequestedVersion = framework_dep.LibraryRange.VersionRange; } // The dgspec has a list of the direct dependencies and changes in the direct dependencies will invalidate the lock file // A dgspec does not have information about transitive dependencies // At the restore time the transitive dependencies could be pinned from central package version management file // By marking them will allow to evaluate when to invalidate the packages.lock.json // in cases that a central transitive version is updated, removed or added the lock file will be invalidated else if (centralPackageVersion != null) { // This is a transitive dependency that is in the list of central dependencies. dependency.Type = PackageDependencyType.CentralTransitive; dependency.RequestedVersion = centralPackageVersion.VersionRange; } else { dependency.Type = PackageDependencyType.Transitive; } nuGettarget.Dependencies.Add(dependency); } var projectFullPaths = assetsFile.Libraries .Where(l => l.Type == LibraryType.Project || l.Type == LibraryType.ExternalProject) .ToDictionary(l => new PackageIdentity(l.Name, l.Version), l => l.MSBuildProject); foreach (var projectReference in libraries.Where(e => e.Type == LibraryType.Project || e.Type == LibraryType.ExternalProject)) { var projectIdentity = new PackageIdentity(projectReference.Name, projectReference.Version); var projectFullPath = projectFullPaths[projectIdentity]; var id = PathUtility.GetStringComparerBasedOnOS().Equals(Path.GetFileNameWithoutExtension(projectFullPath), projectReference.Name) ? projectReference.Name.ToLowerInvariant() : projectReference.Name; var dependency = new LockFileDependency() { Id = id, Dependencies = projectReference.Dependencies, Type = PackageDependencyType.Project }; nuGettarget.Dependencies.Add(dependency); } nuGettarget.Dependencies = nuGettarget.Dependencies.OrderBy(d => d.Type).ToList(); lockFile.Targets.Add(nuGettarget); } return(lockFile); }
public static void Log(ILogger logger, IReadOnlyList <RestoreSummary> restoreSummaries, bool logErrors = false) { if (restoreSummaries.Count == 0) { return; } var noOpCount = 0; var feedsUsed = new HashSet <string>(); var configFiles = new HashSet <string>(); var installed = new Dictionary <string, int>(restoreSummaries.Count, PathUtility.GetStringComparerBasedOnOS()); foreach (RestoreSummary restoreSummary in restoreSummaries) { if (restoreSummary.NoOpRestore) { noOpCount++; } foreach (var feed in restoreSummary.FeedsUsed) { feedsUsed.Add(feed); } foreach (var configFile in restoreSummary.ConfigFiles) { configFiles.Add(configFile); } if (!string.IsNullOrEmpty(restoreSummary.InputPath)) { if (installed.ContainsKey(restoreSummary.InputPath)) { installed[restoreSummary.InputPath] += restoreSummary.InstallCount; } else { installed[restoreSummary.InputPath] = restoreSummary.InstallCount; } } } // This should only be true by nuget exe since it does not have msbuild logger if (logErrors) { // Display the errors summary foreach (var restoreSummary in restoreSummaries) { // log errors LogErrorsToConsole( restoreSummary, string.Format(CultureInfo.CurrentCulture, Strings.Log_ErrorSummary, restoreSummary.InputPath), logger); } } // Display the information summary if (configFiles.Any()) { logger.LogInformationSummary(string.Empty); logger.LogInformationSummary(Strings.Log_ConfigFileSummary); foreach (var configFile in configFiles) { logger.LogInformationSummary($" {configFile}"); } } if (feedsUsed.Any()) { logger.LogInformationSummary(string.Empty); logger.LogInformationSummary(Strings.Log_FeedsUsedSummary); foreach (var feedUsed in feedsUsed) { logger.LogInformationSummary($" {feedUsed}"); } } if (installed.Any(i => i.Value > 0)) { logger.LogInformationSummary(string.Empty); logger.LogInformationSummary(Strings.Log_InstalledSummary); foreach (var pair in installed.Where(i => i.Value > 0)) { logger.LogInformationSummary(" " + string.Format( CultureInfo.CurrentCulture, Strings.Log_InstalledSummaryCount, pair.Value, pair.Key)); } } if (!RuntimeEnvironmentHelper.IsRunningInVisualStudio) { if (noOpCount == restoreSummaries.Count) { logger.LogMinimal(Strings.Log_AllProjectsUpToDate); } else if (noOpCount > 0) { logger.LogMinimal(string.Format(CultureInfo.CurrentCulture, Strings.Log_ProjectUpToDateSummary, noOpCount, restoreSummaries.Count)); } } }
public PackagesLockFile CreateNuGetLockFile(LockFile assetsFile) { var lockFile = new PackagesLockFile(); var libraryLookup = assetsFile.Libraries.Where(e => e.Type == LibraryType.Package) .ToDictionary(e => new PackageIdentity(e.Name, e.Version)); foreach (var target in assetsFile.Targets) { var nuGettarget = new PackagesLockFileTarget() { TargetFramework = target.TargetFramework, RuntimeIdentifier = target.RuntimeIdentifier }; var framework = assetsFile.PackageSpec.TargetFrameworks.FirstOrDefault( f => EqualityUtility.EqualsWithNullCheck(f.FrameworkName, target.TargetFramework)); var libraries = target.Libraries; // check if this is RID-based graph then only add those libraries which differ from original TFM. if (!string.IsNullOrEmpty(target.RuntimeIdentifier)) { var onlyTFM = assetsFile.Targets.First(t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, target.TargetFramework)); libraries = target.Libraries.Where(lib => !onlyTFM.Libraries.Any(tfmLib => tfmLib.Equals(lib))).ToList(); } foreach (var library in libraries.Where(e => e.Type == LibraryType.Package)) { var identity = new PackageIdentity(library.Name, library.Version); var dependency = new LockFileDependency() { Id = library.Name, ResolvedVersion = library.Version, ContentHash = libraryLookup[identity].Sha512, Dependencies = library.Dependencies }; var framework_dep = framework?.Dependencies.FirstOrDefault( dep => StringComparer.OrdinalIgnoreCase.Equals(dep.Name, library.Name)); if (framework_dep != null) { dependency.Type = PackageDependencyType.Direct; dependency.RequestedVersion = framework_dep.LibraryRange.VersionRange; } else { dependency.Type = PackageDependencyType.Transitive; } nuGettarget.Dependencies.Add(dependency); } var projectFullPaths = assetsFile.Libraries .Where(l => l.Type == LibraryType.Project || l.Type == LibraryType.ExternalProject) .ToDictionary(l => new PackageIdentity(l.Name, l.Version), l => l.MSBuildProject); foreach (var projectReference in libraries.Where(e => e.Type == LibraryType.Project || e.Type == LibraryType.ExternalProject)) { var projectIdentity = new PackageIdentity(projectReference.Name, projectReference.Version); var projectFullPath = projectFullPaths[projectIdentity]; var id = PathUtility.GetStringComparerBasedOnOS().Equals(Path.GetFileNameWithoutExtension(projectFullPath), projectReference.Name) ? projectReference.Name.ToLowerInvariant() : projectReference.Name; var dependency = new LockFileDependency() { Id = id, Dependencies = projectReference.Dependencies, Type = PackageDependencyType.Project }; nuGettarget.Dependencies.Add(dependency); } nuGettarget.Dependencies = nuGettarget.Dependencies.OrderBy(d => d.Type).ToList(); lockFile.Targets.Add(nuGettarget); } return(lockFile); }