public static LockFileValidationResult IsLockFileValid()

in src/NuGet.Core/NuGet.ProjectModel/ProjectLockFile/PackagesLockFileUtilities.cs [78:294]


        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));

                    var targetFrameworkInformation = project.TargetFrameworks.FirstOrDefault(e => e.TargetAlias == restoreMetadataFramework.TargetAlias);

                    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, p2pSpecTargetFrameworkInformation.PackagesToPrune, targetFrameworkInformation.PackagesToPrune, 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);
        }