public static void Write(string filePath, PackagesLockFile lockFile) { // Create the directory if it does not exist var fileInfo = new FileInfo(filePath); fileInfo.Directory.Create(); using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) { Write(stream, lockFile); } }
/// <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. /// </summary> /// <param name="dgSpec">The <see cref="DependencyGraphSpec"/> for the new project defintion.</param> /// <param name="nuGetLockFile">The current <see cref="PackagesLockFile"/>.</param> /// <returns>True if the lock file is valid false otherwise. </returns> public static bool IsLockFileStillValid(DependencyGraphSpec dgSpec, PackagesLockFile nuGetLockFile) { var uniqueName = dgSpec.Restore.First(); var project = dgSpec.GetProjectSpec(uniqueName); // Validate all the direct dependencies var lockFileFrameworks = nuGetLockFile.Targets .Where(t => t.TargetFramework != null) .Select(t => t.TargetFramework) .Distinct(); if (project.TargetFrameworks.Count != lockFileFrameworks.Count()) { return(false); } 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. return(false); } var directDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Direct); if (HasProjectDependencyChanged(framework.Dependencies, directDependencies)) { // lock file is out of sync return(false); } } // 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.SequenceEqual(lockFileRuntimes)) { return(false); } // Validate all P2P references foreach (var framework in project.RestoreMetadata.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. return(false); } var queue = new Queue <Tuple <string, string> >(); var visitedP2PReference = new HashSet <string>(); foreach (var projectReference in framework.ProjectReferences) { if (visitedP2PReference.Add(projectReference.ProjectUniqueName)) { var 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) { // project dependency doesn't exist in lock file. return(false); } var p2pSpec = dgSpec.GetProjectSpec(p2pUniqueName); // The package spec not found in the dg spec. This could mean that the project does not exist anymore. if (p2pSpec != null) { // This does not consider ATF. var p2pSpecTargetFrameworkInformation = NuGetFrameworkUtility.GetNearest(p2pSpec.TargetFrameworks, framework.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) { if (HasP2PDependencyChanged(p2pSpecTargetFrameworkInformation.Dependencies, p2pSpecProjectRestoreMetadataFrameworkInfo.ProjectReferences, projectDependency, dgSpec)) { // P2P transitive package dependencies have changed return(false); } foreach (var reference in p2pSpecProjectRestoreMetadataFrameworkInfo.ProjectReferences) { if (visitedP2PReference.Add(reference.ProjectUniqueName)) { var referenceSpec = dgSpec.GetProjectSpec(reference.ProjectUniqueName); queue.Enqueue(new Tuple <string, string>(referenceSpec.Name, reference.ProjectUniqueName)); } } } else // This should never happen. { return(false); } } else { return(false); } } else { return(false); } } } } } return(true); }
/// <summary>Compares two lock files to check if the structure is the same (all values are the same, other /// than SHA hash), and matches dependencies so the caller can easily compare SHA hashes.</summary> /// <param name="expected">The expected lock file structure. Usuaully generated from the project.</param> /// <param name="actual">The lock file that was loaded from the file on disk.</param> /// <returns>A <see cref="LockFileValidityWithMatchedResults"/>.</returns> public static LockFileValidityWithMatchedResults IsLockFileStillValid(PackagesLockFile expected, PackagesLockFile actual) { if (expected == null) { throw new ArgumentNullException(nameof(expected)); } if (actual == null) { throw new ArgumentNullException(nameof(actual)); } // do quick checks for obvious structure differences if (expected.Version != actual.Version) { return(LockFileValidityWithMatchedResults.Invalid); } if (expected.Targets.Count != actual.Targets.Count) { return(LockFileValidityWithMatchedResults.Invalid); } foreach (var expectedTarget in expected.Targets) { PackagesLockFileTarget actualTarget = null; for (var i = 0; i < actual.Targets.Count; i++) { if (actual.Targets[i].TargetFramework == expectedTarget.TargetFramework) { if (actualTarget == null) { actualTarget = actual.Targets[i]; } else { // more than 1? possible bug or bad hand edited lock file. return(LockFileValidityWithMatchedResults.Invalid); } } if (actualTarget == null) { return(LockFileValidityWithMatchedResults.Invalid); } if (actualTarget.Dependencies.Count != expectedTarget.Dependencies.Count) { return(LockFileValidityWithMatchedResults.Invalid); } } } // no obvious structure difference, so start trying to match individual dependencies var matchedDependencies = new List <KeyValuePair <LockFileDependency, LockFileDependency> >(); var isLockFileStillValid = true; var dependencyComparer = LockFileDependencyComparerWithoutContentHash.Default; foreach (PackagesLockFileTarget expectedTarget in expected.Targets) { PackagesLockFileTarget actualTarget = actual.Targets.Single(t => t.TargetFramework == expectedTarget.TargetFramework); // Duplicate dependencies list so we can remove matches to validate that all dependencies were matched var actualDependencies = new Dictionary <LockFileDependency, LockFileDependency>( actualTarget.Dependencies.Count, dependencyComparer); foreach (LockFileDependency actualDependency in actualTarget.Dependencies) { actualDependencies.Add(actualDependency, actualDependency); } foreach (LockFileDependency expectedDependency in expectedTarget.Dependencies) { if (actualDependencies.TryGetValue(expectedDependency, out var actualDependency)) { matchedDependencies.Add(new KeyValuePair <LockFileDependency, LockFileDependency>(expectedDependency, actualDependency)); actualDependencies.Remove(actualDependency); } else { return(LockFileValidityWithMatchedResults.Invalid); } } if (actualDependencies.Count != 0) { return(LockFileValidityWithMatchedResults.Invalid); } } return(new LockFileValidityWithMatchedResults(isLockFileStillValid, matchedDependencies)); }
/// <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>True if the lock file is valid false otherwise. </returns> public static bool IsLockFileStillValid(DependencyGraphSpec dgSpec, PackagesLockFile nuGetLockFile) { // Current tools know how to read only previous formats including the current if (PackagesLockFileFormat.PackagesLockFileVersion < nuGetLockFile.Version) { return(false); } var uniqueName = dgSpec.Restore.First(); var project = dgSpec.GetProjectSpec(uniqueName); // Validate all the direct dependencies var lockFileFrameworks = nuGetLockFile.Targets .Where(t => t.TargetFramework != null) .Select(t => t.TargetFramework) .Distinct(); if (project.TargetFrameworks.Count != lockFileFrameworks.Count()) { return(false); } 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. return(false); } var directDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Direct); if (HasProjectDependencyChanged(framework.Dependencies, directDependencies)) { // lock file is out of sync return(false); } var transitiveDependenciesEnforcedByCentralVersions = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.CentralTransitive).ToList(); var transitiveDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Transitive).ToList(); if (HasProjectTransitiveDependencyChanged(framework.CentralPackageVersions, transitiveDependenciesEnforcedByCentralVersions, transitiveDependencies)) { // lock file is out of sync return(false); } } // 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)) { return(false); } // Validate all P2P references foreach (var framework in project.RestoreMetadata.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. return(false); } var queue = new Queue <Tuple <string, string> >(); var visitedP2PReference = new HashSet <string>(); foreach (var projectReference in framework.ProjectReferences) { if (visitedP2PReference.Add(projectReference.ProjectUniqueName)) { var 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) { // project dependency doesn't exist in lock file. return(false); } var p2pSpec = dgSpec.GetProjectSpec(p2pUniqueName); // The package spec not found in the dg spec. This could mean that the project does not exist anymore. 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, framework.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) { if (HasP2PDependencyChanged(p2pSpecTargetFrameworkInformation.Dependencies, p2pSpecProjectRestoreMetadataFrameworkInfo.ProjectReferences, projectDependency, dgSpec)) { // P2P transitive package dependencies have changed return(false); } 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. { return(false); } } else { return(false); } } else { return(false); } } } } } return(true); }
/// <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)); }
public static bool IsLockFileStillValid(DependencyGraphSpec dgSpec, PackagesLockFile nuGetLockFile) { return(IsLockFileValid(dgSpec, nuGetLockFile).IsValid); }
public static bool IsLockFileStillValid(DependencyGraphSpec dgSpec, PackagesLockFile nuGetLockFile) { var uniqueName = dgSpec.Restore.First(); var project = dgSpec.GetProjectSpec(uniqueName); // Validate all the direct dependencies 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. return(false); } var directDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Direct); if (HasProjectDependencyChanged(framework.Dependencies, directDependencies)) { // lock file is out of sync return(false); } } // Validate all P2P references foreach (var framework in project.RestoreMetadata.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. return(false); } var queue = new Queue <Tuple <string, string> >(); var visitedP2PReference = new HashSet <string>(); foreach (var projectReference in framework.ProjectReferences) { if (visitedP2PReference.Add(projectReference.ProjectUniqueName)) { var 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 && PathUtility.GetStringComparerBasedOnOS().Equals(dep.Id, p2pProjectName)); if (projectDependency == null) { // project dependency doesn't exist in lock file. return(false); } var p2pSpec = dgSpec.GetProjectSpec(p2pUniqueName); // The package spec not found in the dg spec. This could mean that the project does not exist anymore. if (p2pSpec != null) { var p2pSpecTarget = NuGetFrameworkUtility.GetNearest(p2pSpec.TargetFrameworks, framework.FrameworkName, e => e.FrameworkName); // No compatible framework found if (p2pSpecTarget != null) { var p2pSpecProjectRefTarget = p2pSpec.RestoreMetadata.TargetFrameworks.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(p2pSpecTarget.FrameworkName, t.FrameworkName)); if (p2pSpecProjectRefTarget != null) // This should never happen. { if (HasP2PDependencyChanged(p2pSpecTarget.Dependencies, p2pSpecProjectRefTarget.ProjectReferences, projectDependency, dgSpec)) { // P2P transitive package dependencies have changed return(false); } foreach (var reference in p2pSpecProjectRefTarget.ProjectReferences) { if (visitedP2PReference.Add(reference.ProjectUniqueName)) { var referenceSpec = dgSpec.GetProjectSpec(reference.ProjectUniqueName); queue.Enqueue(new Tuple <string, string>(referenceSpec.Name, reference.ProjectUniqueName)); } } } else { return(false); } } else { return(false); } } else { return(false); } } } } } return(true); }
public static bool IsLockFileStillValid(DependencyGraphSpec dgSpec, PackagesLockFile nuGetLockFile) { var uniqueName = dgSpec.Restore.First(); var project = dgSpec.GetProjectSpec(uniqueName); // Validate all the direct dependencies 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. return(false); } var directDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Direct); if (HasProjectDependencyChanged(framework.Dependencies, directDependencies)) { // lock file is out of sync return(false); } } // Validate all P2P references foreach (var framework in project.RestoreMetadata.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. return(false); } var queue = new Queue <string>(); var visitedP2PReference = new HashSet <string>(); foreach (var projectReference in framework.ProjectReferences) { if (visitedP2PReference.Add(projectReference.ProjectUniqueName)) { queue.Enqueue(projectReference.ProjectUniqueName); while (queue.Count > 0) { var p2pUniqueName = queue.Dequeue(); var p2pProjectName = Path.GetFileNameWithoutExtension(p2pUniqueName); var projectDependency = target.Dependencies.FirstOrDefault( dep => dep.Type == PackageDependencyType.Project && PathUtility.GetStringComparerBasedOnOS().Equals(dep.Id, p2pProjectName)); if (projectDependency == null) { // project dependency doesn't exist in lock file. return(false); } var p2pSpec = dgSpec.GetProjectSpec(p2pUniqueName); // ignore if this p2p packageSpec not found in DGSpec, It'll fail restore with different error at later stage if (p2pSpec != null) { var p2pSpecTarget = NuGetFrameworkUtility.GetNearest(p2pSpec.TargetFrameworks, framework.FrameworkName, e => e.FrameworkName); // ignore if compatible framework not found for p2p reference which means current project didn't // get anything transitively from this p2p if (p2pSpecTarget != null) { if (HasP2PDependencyChanged(p2pSpecTarget.Dependencies, projectDependency)) { // P2P transitive package dependencies has changed return(false); } var p2pSpecProjectRefTarget = p2pSpec.RestoreMetadata.TargetFrameworks.FirstOrDefault( t => PathUtility.GetStringComparerBasedOnOS().Equals(p2pSpecTarget.FrameworkName.GetShortFolderName(), t.FrameworkName.GetShortFolderName())); if (p2pSpecProjectRefTarget != null) { foreach (var reference in p2pSpecProjectRefTarget.ProjectReferences) { if (visitedP2PReference.Add(reference.ProjectUniqueName)) { queue.Enqueue(reference.ProjectUniqueName); } } } } } } } } } return(true); }