/// <summary> /// The lock file will get invalidated if one or more of the below are true /// 1. The target frameworks list of the current project was updated. /// 2. The runtime list of the current project waw updated. /// 3. The packages of the current project were updated. /// 4. The packages of the dependent projects were updated. /// 5. The framework list of the dependent projects were updated with frameworks incompatible with the main project framework. /// 6. If the version of the <paramref name="nuGetLockFile"/> is larger than the current tools <see cref="PackagesLockFileFormat.PackagesLockFileVersion"/>. /// </summary> /// <param name="dgSpec">The <see cref="DependencyGraphSpec"/> for the new project defintion.</param> /// <param name="nuGetLockFile">The current <see cref="PackagesLockFile"/>.</param> /// <returns>Returns LockFileValidityWithInvalidReasons object with IsValid set to true if the lock file is valid false otherwise. /// The second return type is a localized message that indicates in further detail the reason for the inconsistency.</returns> public static LockFileValidationResult IsLockFileValid(DependencyGraphSpec dgSpec, PackagesLockFile nuGetLockFile) { if (dgSpec == null) { throw new ArgumentNullException(nameof(dgSpec)); } if (nuGetLockFile == null) { throw new ArgumentNullException(nameof(nuGetLockFile)); } List <string> invalidReasons = new List <string>(); // Current tools know how to read only previous formats including the current if (PackagesLockFileFormat.PackagesLockFileVersion < nuGetLockFile.Version) { invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_IncompatibleLockFileVersion, PackagesLockFileFormat.PackagesLockFileVersion )); return(new LockFileValidationResult(false, invalidReasons)); } var uniqueName = dgSpec.Restore.First(); var project = dgSpec.GetProjectSpec(uniqueName); // Validate all the direct dependencies NuGetFramework[] lockFileFrameworks = nuGetLockFile.Targets .Where(t => t.TargetFramework != null) .Select(t => t.TargetFramework) .Distinct() .ToArray(); if (project.TargetFrameworks.Count != lockFileFrameworks.Length) { invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_MismatchedTargetFrameworks, string.Join(",", project.TargetFrameworks.Select(e => e.FrameworkName.GetShortFolderName())), string.Join(",", lockFileFrameworks.Select(e => e.GetShortFolderName())) )); } else { // Validate the runtimes for the current project did not change. var projectRuntimesKeys = project.RuntimeGraph.Runtimes.Select(r => r.Key).Where(k => k != null); var lockFileRuntimes = nuGetLockFile.Targets.Select(t => t.RuntimeIdentifier).Where(r => r != null).Distinct(); if (!projectRuntimesKeys.OrderedEquals( lockFileRuntimes, x => x, StringComparer.InvariantCultureIgnoreCase, StringComparer.InvariantCultureIgnoreCase)) { invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_RuntimeIdentifiersChanged, string.Join(";", projectRuntimesKeys.OrderBy(e => e)), string.Join(";", lockFileRuntimes.OrderBy(e => e)) )); } foreach (var framework in project.TargetFrameworks) { var target = nuGetLockFile.Targets.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, framework.FrameworkName)); if (target == null) { // a new target found in the dgSpec so invalidate existing lock file. invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_NewTargetFramework, framework.FrameworkName.GetShortFolderName()) ); continue; } IEnumerable <LockFileDependency> directDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Direct); (var hasProjectDependencyChanged, var pmessage) = HasDirectPackageDependencyChanged(framework.Dependencies, directDependencies, target.TargetFramework); if (hasProjectDependencyChanged) { // lock file is out of sync invalidReasons.Add(pmessage); } var transitiveDependenciesEnforcedByCentralVersions = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.CentralTransitive).ToList(); var transitiveDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Transitive).ToList(); (var hasTransitiveDependencyChanged, var tmessage) = HasProjectTransitiveDependencyChanged(framework.CentralPackageVersions, transitiveDependenciesEnforcedByCentralVersions, transitiveDependencies); if (hasTransitiveDependencyChanged) { // lock file is out of sync invalidReasons.Add(tmessage); } } // Validate all P2P references foreach (var restoreMetadataFramework in project.RestoreMetadata.TargetFrameworks) { var target = nuGetLockFile.Targets.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, restoreMetadataFramework.FrameworkName)); if (target == null) { continue; } var queue = new Queue <Tuple <string, string> >(); var visitedP2PReference = new HashSet <string>(); foreach (var projectReference in restoreMetadataFramework.ProjectReferences) { if (visitedP2PReference.Add(projectReference.ProjectUniqueName)) { PackageSpec spec = dgSpec.GetProjectSpec(projectReference.ProjectUniqueName); queue.Enqueue(new Tuple <string, string>(spec.Name, projectReference.ProjectUniqueName)); while (queue.Count > 0) { var projectNames = queue.Dequeue(); var p2pUniqueName = projectNames.Item2; var p2pProjectName = projectNames.Item1; var projectDependency = target.Dependencies.FirstOrDefault( dep => dep.Type == PackageDependencyType.Project && StringComparer.OrdinalIgnoreCase.Equals(dep.Id, p2pProjectName)); if (projectDependency == null) { // new direct project dependency. // If there are changes in the P2P2P references, they will be caught in HasP2PDependencyChanged. invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_ProjectReferenceAdded, p2pProjectName, target.TargetFramework.GetShortFolderName() )); continue; } var p2pSpec = dgSpec.GetProjectSpec(p2pUniqueName); if (p2pSpec != null) { TargetFrameworkInformation p2pSpecTargetFrameworkInformation = default; if (p2pSpec.RestoreMetadata.ProjectStyle == ProjectStyle.PackagesConfig || p2pSpec.RestoreMetadata.ProjectStyle == ProjectStyle.Unknown) { // Skip compat check and dependency check for non PR projects. // Projects that are not PR do not undergo compat checks by NuGet and do not contribute anything transitively. p2pSpecTargetFrameworkInformation = p2pSpec.TargetFrameworks.FirstOrDefault(); } else { // This does not consider ATF. p2pSpecTargetFrameworkInformation = NuGetFrameworkUtility.GetNearest(p2pSpec.TargetFrameworks, restoreMetadataFramework.FrameworkName, e => e.FrameworkName); } // No compatible framework found if (p2pSpecTargetFrameworkInformation != null) { // We need to compare the main framework only. Ignoring fallbacks. var p2pSpecProjectRestoreMetadataFrameworkInfo = p2pSpec.RestoreMetadata.TargetFrameworks.FirstOrDefault( t => NuGetFramework.Comparer.Equals(p2pSpecTargetFrameworkInformation.FrameworkName, t.FrameworkName)); if (p2pSpecProjectRestoreMetadataFrameworkInfo != null) { (var hasChanged, var message) = HasP2PDependencyChanged(p2pSpecTargetFrameworkInformation.Dependencies, p2pSpecProjectRestoreMetadataFrameworkInfo.ProjectReferences, projectDependency, dgSpec); if (hasChanged) { // P2P transitive package dependencies have changed invalidReasons.Add(message); } foreach (var reference in p2pSpecProjectRestoreMetadataFrameworkInfo.ProjectReferences) { // Do not add private assets for processing. if (visitedP2PReference.Add(reference.ProjectUniqueName) && reference.PrivateAssets != LibraryIncludeFlags.All) { var referenceSpec = dgSpec.GetProjectSpec(reference.ProjectUniqueName); queue.Enqueue(new Tuple <string, string>(referenceSpec.Name, reference.ProjectUniqueName)); } } } else // This should never happen. { throw new Exception(string.Format(CultureInfo.CurrentCulture, Strings.PackagesLockFile_RestoreMetadataMissingTfms)); } } else { invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_ProjectReferenceHasNoCompatibleTargetFramework, p2pProjectName, restoreMetadataFramework.FrameworkName.GetShortFolderName() )); } } else // This can't happen. When adding the queue, the referenceSpec HAS to be discovered. If the project is otherwise missing, it will be discovered in HasP2PDependencyChanged { throw new Exception(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_UnableToLoadPackagespec, p2pUniqueName)); } } } } } } bool isLockFileValid = invalidReasons.Count == 0; return(new LockFileValidationResult(isLockFileValid, invalidReasons)); }
private static bool BuildTargetFrameworkNode(PackageSpec packageSpec, KeyValuePair <string, JToken> targetFramework, string filePath) { var frameworkName = GetFramework(targetFramework.Key); var properties = targetFramework.Value.Value <JObject>(); var assetTargetFallback = GetBoolOrFalse(properties, "assetTargetFallback", filePath); var importFrameworks = GetImports(properties, packageSpec); // If a fallback framework exists, update the framework to contain both. var updatedFramework = frameworkName; if (importFrameworks.Count != 0) { if (assetTargetFallback) { updatedFramework = new AssetTargetFallbackFramework(frameworkName, importFrameworks); } else { updatedFramework = new FallbackFramework(frameworkName, importFrameworks); } } var targetFrameworkInformation = new TargetFrameworkInformation { FrameworkName = updatedFramework, Dependencies = new List <LibraryDependency>(), Imports = importFrameworks, Warn = GetWarnSetting(properties), AssetTargetFallback = assetTargetFallback, RuntimeIdentifierGraphPath = GetRuntimeIdentifierGraphPath(properties) }; PopulateDependencies( packageSpec.FilePath, targetFrameworkInformation.Dependencies, properties, "dependencies", isGacOrFrameworkReference: false); PopulateDownloadDependencies( targetFrameworkInformation.DownloadDependencies, properties, packageSpec.FilePath); var frameworkAssemblies = new List <LibraryDependency>(); PopulateDependencies( packageSpec.FilePath, frameworkAssemblies, properties, "frameworkAssemblies", isGacOrFrameworkReference: true); PopulateFrameworkReferences( targetFrameworkInformation.FrameworkReferences, properties, "frameworkReferences", packageSpec.FilePath); frameworkAssemblies.ForEach(d => targetFrameworkInformation.Dependencies.Add(d)); packageSpec.TargetFrameworks.Add(targetFrameworkInformation); return(true); }
private static bool BuildTargetFrameworkNode(PackageSpec packageSpec, KeyValuePair<string, JToken> targetFramework) { var frameworkName = GetFramework(targetFramework.Key); // If it's not unsupported then keep it if (frameworkName == NuGetFramework.UnsupportedFramework) { // REVIEW: Should we skip unsupported target frameworks return false; } var targetFrameworkInformation = new TargetFrameworkInformation { FrameworkName = frameworkName, Dependencies = new List<LibraryDependency>() }; var properties = targetFramework.Value.Value<JObject>(); PopulateDependencies( packageSpec.FilePath, targetFrameworkInformation.Dependencies, properties, "dependencies", isGacOrFrameworkReference: false); var frameworkAssemblies = new List<LibraryDependency>(); PopulateDependencies( packageSpec.FilePath, frameworkAssemblies, properties, "frameworkAssemblies", isGacOrFrameworkReference: true); frameworkAssemblies.ForEach(d => targetFrameworkInformation.Dependencies.Add(d)); packageSpec.TargetFrameworks.Add(targetFrameworkInformation); return true; }