Exemplo n.º 1
0
 public DependenciesChangedEventArgs(
     IProjectDependenciesSubTreeProvider provider,
     string?targetShortOrFullName,
     IDependenciesChanges changes,
     IProjectCatalogSnapshot catalogs,
     IImmutableDictionary <NamedIdentity, IComparable> dataSourceVersions)
     : this(provider, targetShortOrFullName, changes, CancellationToken.None)
 {
 }
Exemplo n.º 2
0
        public static bool AnyChanges(this IDependenciesChanges changes)
        {
            if (changes is DependenciesChanges c)
            {
                // Non-allocating path when using our own type.
                // Ideally this property would be on the IDependenciesChanges interface directly.
                return(c.AnyChanges);
            }

            return(changes.AddedNodes.Any() || changes.RemovedNodes.Any());
        }
Exemplo n.º 3
0
 /// <summary>
 /// Initializes a new instance of <see cref="DependenciesChangedEventArgs"/>.
 /// </summary>
 /// <param name="provider">The subtree provider whose dependencies changed.</param>
 /// <param name="targetShortOrFullName">
 /// The short or full name of the target framework to which <paramref name="changes"/> apply.
 /// Optional if the project is not multi-targeting.
 /// </param>
 /// <param name="changes">The collection of dependency changes.</param>
 /// <param name="token">A cancellation token that allows cancelling the update.</param>
 public DependenciesChangedEventArgs(
     IProjectDependenciesSubTreeProvider provider,
     string?targetShortOrFullName,
     IDependenciesChanges changes,
     CancellationToken token)
 {
     Provider = provider;
     TargetShortOrFullName = targetShortOrFullName;
     Changes = changes;
     Token   = token;
 }
 public DependenciesChangedEventArgs(IProjectDependenciesSubTreeProvider provider,
                                     string targetShortOrFullName,
                                     IDependenciesChanges changes,
                                     IProjectCatalogSnapshot catalogs,
                                     IImmutableDictionary <NamedIdentity, IComparable> dataSourceVersions)
 {
     Provider = provider;
     TargetShortOrFullName = targetShortOrFullName;
     Changes            = changes;
     Catalogs           = catalogs;
     DataSourceVersions = dataSourceVersions;
 }
        public static TargetedDependenciesSnapshot FromChanges(
            string projectPath,
            ITargetFramework targetFramework,
            ITargetedDependenciesSnapshot previousSnapshot,
            IDependenciesChanges changes,
            IProjectCatalogSnapshot catalogs,
            IEnumerable <IDependenciesSnapshotFilter> snapshotFilters,
            out bool anyChanges)
        {
            var newSnapshot = new TargetedDependenciesSnapshot(projectPath, targetFramework, previousSnapshot, catalogs);

            anyChanges = newSnapshot.MergeChanges(changes, snapshotFilters);
            return(newSnapshot);
        }
        public DependencySubscriptionChangedEventArgs(
            ImmutableArray <ITargetFramework> targetFrameworks,
            ITargetFramework activeTarget,
            ITargetFramework changedTargetFramework,
            IDependenciesChanges changes,
            IProjectCatalogSnapshot catalogs)
        {
            Requires.Argument(!targetFrameworks.IsDefaultOrEmpty, nameof(targetFrameworks), "Must not be default or empty.");

            TargetFrameworks       = targetFrameworks;
            ActiveTarget           = activeTarget;
            Catalogs               = catalogs;
            Changes                = changes;
            ChangedTargetFramework = changedTargetFramework;
        }
        /// <summary>
        /// For each target framework in <paramref name="changes"/>, applies the corresponding
        /// <see cref="IDependenciesChanges"/> to <paramref name="previousSnapshot"/> in order to produce
        /// and return an updated <see cref="DependenciesSnapshot"/> object.
        /// If no changes are made, <paramref name="previousSnapshot"/> is returned unmodified.
        /// </summary>
        /// <remarks>
        /// As part of the update, each <see cref="IDependenciesSnapshotFilter"/> in <paramref name="snapshotFilters"/>
        /// is given a chance to influence the addition and removal of dependency data in the returned snapshot.
        /// </remarks>
        /// <returns>An updated snapshot, or <paramref name="previousSnapshot"/> if no changes occured.</returns>
        public static DependenciesSnapshot FromChanges(
            string projectPath,
            DependenciesSnapshot previousSnapshot,
            ITargetFramework changedTargetFramework,
            IDependenciesChanges changes,
            IProjectCatalogSnapshot?catalogs,
            ImmutableArray <ITargetFramework> targetFrameworks,
            ITargetFramework?activeTargetFramework,
            ImmutableArray <IDependenciesSnapshotFilter> snapshotFilters,
            IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType,
            IImmutableSet <string>?projectItemSpecs)
        {
            Requires.NotNullOrWhiteSpace(projectPath, nameof(projectPath));
            Requires.NotNull(previousSnapshot, nameof(previousSnapshot));
            Requires.NotNull(changedTargetFramework, nameof(changedTargetFramework));
            Requires.NotNull(changes, nameof(changes));
            Requires.Argument(!snapshotFilters.IsDefault, nameof(snapshotFilters), "Cannot be default.");
            Requires.NotNull(subTreeProviderByProviderType, nameof(subTreeProviderByProviderType));

            var builder = previousSnapshot.DependenciesByTargetFramework.ToBuilder();

            if (!builder.TryGetValue(changedTargetFramework, out TargetedDependenciesSnapshot previousTargetedSnapshot))
            {
                previousTargetedSnapshot = TargetedDependenciesSnapshot.CreateEmpty(projectPath, changedTargetFramework, catalogs);
            }

            bool builderChanged = false;

            var newTargetedSnapshot = TargetedDependenciesSnapshot.FromChanges(
                projectPath,
                previousTargetedSnapshot,
                changes,
                catalogs,
                snapshotFilters,
                subTreeProviderByProviderType,
                projectItemSpecs);

            if (!ReferenceEquals(previousTargetedSnapshot, newTargetedSnapshot))
            {
                builder[changedTargetFramework] = newTargetedSnapshot;
                builderChanged = true;
            }

            SyncTargetFrameworks();

            activeTargetFramework ??= previousSnapshot.ActiveTargetFramework;

            if (builderChanged)
            {
                // Dependencies-by-target-framework has changed
                return(new DependenciesSnapshot(
                           projectPath,
                           activeTargetFramework,
                           builder.ToImmutable()));
            }

            if (!activeTargetFramework.Equals(previousSnapshot.ActiveTargetFramework))
            {
                // The active target framework changed
                return(new DependenciesSnapshot(
                           projectPath,
                           activeTargetFramework,
                           previousSnapshot.DependenciesByTargetFramework));
            }

            if (projectPath != previousSnapshot.ProjectPath)
            {
                // The project path changed
                return(new DependenciesSnapshot(
                           projectPath,
                           activeTargetFramework,
                           previousSnapshot.DependenciesByTargetFramework));
            }

            // Nothing has changed, so return the same snapshot
            return(previousSnapshot);

            void SyncTargetFrameworks()
            {
                // Only sync if a the full list of target frameworks has been provided
                if (targetFrameworks.IsDefault)
                {
                    return;
                }

                // This is a long-winded way of doing this that minimises allocations

                // Ensure all required target frameworks are present
                foreach (ITargetFramework targetFramework in targetFrameworks)
                {
                    if (!builder.ContainsKey(targetFramework))
                    {
                        builder.Add(targetFramework, TargetedDependenciesSnapshot.CreateEmpty(projectPath, targetFramework, catalogs));
                        builderChanged = true;
                    }
                }

                // Remove any extra target frameworks
                if (builder.Count != targetFrameworks.Length)
                {
                    IEnumerable <ITargetFramework> targetFrameworksToRemove = builder.Keys.Except(targetFrameworks);

                    foreach (ITargetFramework targetFramework in targetFrameworksToRemove)
                    {
                        builder.Remove(targetFramework);
                    }

                    builderChanged = true;
                }
            }
        }
        /// <summary>
        /// Applies changes to <paramref name="previousSnapshot"/> and produces a new snapshot if required.
        /// If no changes are made, <paramref name="previousSnapshot"/> is returned unmodified.
        /// </summary>
        /// <returns>An updated snapshot, or <paramref name="previousSnapshot"/> if no changes occured.</returns>
        public static TargetedDependenciesSnapshot FromChanges(
            string projectPath,
            TargetedDependenciesSnapshot previousSnapshot,
            IDependenciesChanges changes,
            IProjectCatalogSnapshot?catalogs,
            ImmutableArray <IDependenciesSnapshotFilter> snapshotFilters,
            IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType,
            IImmutableSet <string>?projectItemSpecs)
        {
            Requires.NotNullOrWhiteSpace(projectPath, nameof(projectPath));
            Requires.NotNull(previousSnapshot, nameof(previousSnapshot));
            Requires.NotNull(changes, nameof(changes));
            Requires.Argument(!snapshotFilters.IsDefault, nameof(snapshotFilters), "Cannot be default.");
            Requires.NotNull(subTreeProviderByProviderType, nameof(subTreeProviderByProviderType));

            bool anyChanges = false;

            ITargetFramework targetFramework = previousSnapshot.TargetFramework;

            var worldBuilder = previousSnapshot.DependenciesWorld.ToBuilder();

            if (changes.RemovedNodes.Count != 0)
            {
                var context = new RemoveDependencyContext(worldBuilder);

                foreach (IDependencyModel removed in changes.RemovedNodes)
                {
                    Remove(context, removed);
                }
            }

            if (changes.AddedNodes.Count != 0)
            {
                var context = new AddDependencyContext(worldBuilder);

                foreach (IDependencyModel added in changes.AddedNodes)
                {
                    Add(context, added);
                }
            }

            // Also factor in any changes to path/framework/catalogs
            anyChanges =
                anyChanges ||
                !StringComparers.Paths.Equals(projectPath, previousSnapshot.ProjectPath) ||
                !targetFramework.Equals(previousSnapshot.TargetFramework) ||
                !Equals(catalogs, previousSnapshot.Catalogs);

            if (anyChanges)
            {
                return(new TargetedDependenciesSnapshot(
                           projectPath,
                           targetFramework,
                           catalogs,
                           worldBuilder.ToImmutable()));
            }

            return(previousSnapshot);

            void Remove(RemoveDependencyContext context, IDependencyModel dependencyModel)
            {
                string dependencyId = Dependency.GetID(
                    targetFramework, dependencyModel.ProviderType, dependencyModel.Id);

                if (!context.TryGetDependency(dependencyId, out IDependency dependency))
                {
                    return;
                }

                context.Reset();

                foreach (IDependenciesSnapshotFilter filter in snapshotFilters)
                {
                    filter.BeforeRemove(
                        targetFramework,
                        dependency,
                        context);

                    anyChanges |= context.Changed;

                    if (!context.GetResult(filter))
                    {
                        // TODO breaking here denies later filters the opportunity to modify builders
                        return;
                    }
                }

                worldBuilder.Remove(dependencyId);
                anyChanges = true;
            }

            void Add(AddDependencyContext context, IDependencyModel dependencyModel)
            {
                // Create the unfiltered dependency
                IDependency?dependency = new Dependency(dependencyModel, targetFramework, projectPath);

                context.Reset();

                foreach (IDependenciesSnapshotFilter filter in snapshotFilters)
                {
                    filter.BeforeAddOrUpdate(
                        targetFramework,
                        dependency,
                        subTreeProviderByProviderType,
                        projectItemSpecs,
                        context);

                    dependency = context.GetResult(filter);

                    if (dependency == null)
                    {
                        break;
                    }
                }

                if (dependency != null)
                {
                    // A dependency was accepted
                    worldBuilder.Remove(dependency.Id);
                    worldBuilder.Add(dependency.Id, dependency);
                    anyChanges = true;
                }
                else
                {
                    // Even though the dependency was rejected, it's possible that filters made
                    // changes to other dependencies.
                    anyChanges |= context.Changed;
                }
            }
        }
        private bool MergeChanges(
            IDependenciesChanges changes,
            IEnumerable <IDependenciesSnapshotFilter> snapshotFilters)
        {
            var worldBuilder = ImmutableDictionary.CreateBuilder <string, IDependency>(
                StringComparer.OrdinalIgnoreCase);

            worldBuilder.AddRange(DependenciesWorld);
            var topLevelBuilder = ImmutableHashSet.CreateBuilder <IDependency>();

            topLevelBuilder.AddRange(TopLevelDependencies);

            var anyChanges = false;

            foreach (var removed in changes.RemovedNodes)
            {
                var targetedId = Dependency.GetID(TargetFramework, removed.ProviderType, removed.Id);
                if (!worldBuilder.TryGetValue(targetedId, out IDependency dependency))
                {
                    continue;
                }

                if (snapshotFilters != null)
                {
                    foreach (var filter in snapshotFilters)
                    {
                        dependency = filter.BeforeRemove(
                            ProjectPath, TargetFramework, dependency, worldBuilder, topLevelBuilder, out bool filterAnyChanges);

                        anyChanges |= filterAnyChanges;

                        if (dependency == null)
                        {
                            break;
                        }
                    }
                }

                if (dependency == null)
                {
                    continue;
                }

                anyChanges = true;

                worldBuilder.Remove(targetedId);
                topLevelBuilder.Remove(dependency);
            }

            foreach (var added in changes.AddedNodes)
            {
                IDependency newDependency = new Dependency(added, TargetFramework);

                if (snapshotFilters != null)
                {
                    foreach (var filter in snapshotFilters)
                    {
                        newDependency = filter.BeforeAdd(
                            ProjectPath, TargetFramework, newDependency, worldBuilder, topLevelBuilder, out bool filterAnyChanges);

                        anyChanges |= filterAnyChanges;

                        if (newDependency == null)
                        {
                            break;
                        }
                    }
                }

                if (newDependency == null)
                {
                    continue;
                }

                anyChanges = true;

                worldBuilder.Remove(newDependency.Id);
                worldBuilder.Add(newDependency.Id, newDependency);
                if (newDependency.TopLevel)
                {
                    topLevelBuilder.Remove(newDependency);
                    topLevelBuilder.Add(newDependency);
                }
            }

            DependenciesWorld    = worldBuilder.ToImmutable();
            TopLevelDependencies = topLevelBuilder.ToImmutable();

            ConstructTopLevelDependenciesByPathMap();

            return(anyChanges);
        }
 public static bool AnyChanges(this IDependenciesChanges changes)
 {
     return(changes.AddedNodes.Any() || changes.RemovedNodes.Any());
 }
Exemplo n.º 11
0
        private bool MergeChanges(
            IDependenciesChanges changes,
            IEnumerable <IDependenciesSnapshotFilter> snapshotFilters,
            IEnumerable <IProjectDependenciesSubTreeProvider> subTreeProviders,
            HashSet <string> projectItemSpecs)
        {
            var worldBuilder    = DependenciesWorld.ToBuilder();
            var topLevelBuilder = TopLevelDependencies.ToBuilder();

            var anyChanges = false;

            foreach (var removed in changes.RemovedNodes)
            {
                var targetedId = Dependency.GetID(TargetFramework, removed.ProviderType, removed.Id);
                if (!worldBuilder.TryGetValue(targetedId, out IDependency dependency))
                {
                    continue;
                }

                if (snapshotFilters != null)
                {
                    foreach (var filter in snapshotFilters)
                    {
                        dependency = filter.BeforeRemove(
                            ProjectPath, TargetFramework, dependency, worldBuilder, topLevelBuilder, out bool filterAnyChanges);

                        anyChanges |= filterAnyChanges;

                        if (dependency == null)
                        {
                            break;
                        }
                    }
                }

                if (dependency == null)
                {
                    continue;
                }

                anyChanges = true;

                worldBuilder.Remove(targetedId);
                topLevelBuilder.Remove(dependency);
            }

            var subTreeProvidersMap = GetSubTreeProviderMap(subTreeProviders);

            foreach (var added in changes.AddedNodes)
            {
                IDependency newDependency = new Dependency(added, TargetFramework, ProjectPath);

                if (snapshotFilters != null)
                {
                    foreach (var filter in snapshotFilters)
                    {
                        newDependency = filter.BeforeAdd(
                            ProjectPath,
                            TargetFramework,
                            newDependency,
                            worldBuilder,
                            topLevelBuilder,
                            subTreeProvidersMap,
                            projectItemSpecs,
                            out bool filterAnyChanges);

                        anyChanges |= filterAnyChanges;

                        if (newDependency == null)
                        {
                            break;
                        }
                    }
                }

                if (newDependency == null)
                {
                    continue;
                }

                anyChanges = true;

                worldBuilder.Remove(newDependency.Id);
                worldBuilder.Add(newDependency.Id, newDependency);
                if (newDependency.TopLevel)
                {
                    topLevelBuilder.Remove(newDependency);
                    topLevelBuilder.Add(newDependency);
                }
            }

            DependenciesWorld    = worldBuilder.ToImmutable();
            TopLevelDependencies = topLevelBuilder.ToImmutable();

            ConstructTopLevelDependenciesByPathMap();

            return(anyChanges);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Applies changes to <paramref name="previousSnapshot"/> and produces a new snapshot if required.
        /// If no changes are made, <paramref name="previousSnapshot"/> is returned unmodified.
        /// </summary>
        /// <returns>An updated snapshot, or <paramref name="previousSnapshot"/> if no changes occured.</returns>
        public static ITargetedDependenciesSnapshot FromChanges(
            string projectPath,
            ITargetedDependenciesSnapshot previousSnapshot,
            IDependenciesChanges changes,
            IProjectCatalogSnapshot catalogs,
            IReadOnlyList <IDependenciesSnapshotFilter> snapshotFilters,
            IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType,
            IImmutableSet <string> projectItemSpecs)
        {
            Requires.NotNullOrWhiteSpace(projectPath, nameof(projectPath));
            Requires.NotNull(previousSnapshot, nameof(previousSnapshot));
            // catalogs can be null
            Requires.NotNull(changes, nameof(changes));
            Requires.NotNull(snapshotFilters, nameof(snapshotFilters));
            Requires.NotNull(subTreeProviderByProviderType, nameof(subTreeProviderByProviderType));
            // projectItemSpecs can be null

            bool anyChanges = false;

            ITargetFramework targetFramework = previousSnapshot.TargetFramework;

            var worldBuilder = previousSnapshot.DependenciesWorld.ToBuilder();

            foreach (IDependencyModel removedNode in changes.RemovedNodes)
            {
                string targetedId = Dependency.GetID(targetFramework, removedNode.ProviderType, removedNode.Id);

                if (!worldBuilder.TryGetValue(targetedId, out IDependency dependency))
                {
                    continue;
                }

                bool canRemove = true;

                if (snapshotFilters != null)
                {
                    foreach (IDependenciesSnapshotFilter filter in snapshotFilters)
                    {
                        canRemove = filter.BeforeRemove(
                            projectPath, targetFramework, dependency, worldBuilder, out bool filterAnyChanges);

                        anyChanges |= filterAnyChanges;

                        if (!canRemove)
                        {
                            // TODO breaking here denies later filters the opportunity to modify builders
                            break;
                        }
                    }
                }

                if (canRemove)
                {
                    anyChanges = true;
                    worldBuilder.Remove(targetedId);
                }
            }

            foreach (IDependencyModel added in changes.AddedNodes)
            {
                IDependency newDependency = new Dependency(added, targetFramework, projectPath);

                if (snapshotFilters != null)
                {
                    foreach (IDependenciesSnapshotFilter filter in snapshotFilters)
                    {
                        newDependency = filter.BeforeAdd(
                            projectPath,
                            targetFramework,
                            newDependency,
                            worldBuilder,
                            subTreeProviderByProviderType,
                            projectItemSpecs,
                            out bool filterAnyChanges);

                        anyChanges |= filterAnyChanges;

                        if (newDependency == null)
                        {
                            break;
                        }
                    }
                }

                if (newDependency == null)
                {
                    continue;
                }

                anyChanges = true;

                worldBuilder.Remove(newDependency.Id);
                worldBuilder.Add(newDependency.Id, newDependency);
            }

            // Also factor in any changes to path/framework/catalogs
            anyChanges =
                anyChanges ||
                !StringComparers.Paths.Equals(projectPath, previousSnapshot.ProjectPath) ||
                !targetFramework.Equals(previousSnapshot.TargetFramework) ||
                !Equals(catalogs, previousSnapshot.Catalogs);

            if (anyChanges)
            {
                return(new TargetedDependenciesSnapshot(
                           projectPath,
                           targetFramework,
                           catalogs,
                           worldBuilder.ToImmutable()));
            }

            return(previousSnapshot);
        }
 public static bool AnyChanges(this IDependenciesChanges self)
 {
     return(self.AddedNodes.Count > 0 || self.RemovedNodes.Count > 0);
 }