private async Task HandleAsync(
            IProjectSubscriptionUpdate projectUpdate,
            IProjectCatalogSnapshot catalogSnapshot,
            RuleHandlerType handlerType)
        {
            AggregateCrossTargetProjectContext currentAggregateContext = await _host.GetCurrentAggregateProjectContext();

            if (currentAggregateContext == null || _currentProjectContext != currentAggregateContext)
            {
                return;
            }

            // Get the inner workspace project context to update for this change.
            ITargetFramework targetFrameworkToUpdate = currentAggregateContext.GetProjectFramework(projectUpdate.ProjectConfiguration);

            if (targetFrameworkToUpdate == null)
            {
                return;
            }

            // Broken design time builds sometimes cause updates with no project changes and sometimes
            // cause updates with a project change that has no difference.
            // We handle the former case here, and the latter case is handled in the CommandLineItemHandler.
            if (projectUpdate.ProjectChanges.Count == 0)
            {
                return;
            }

            // Create an object to track dependency changes.
            var changesBuilder = new CrossTargetDependenciesChangesBuilder();

            // Give each handler a chance to register dependency changes.
            foreach (Lazy <IDependenciesRuleHandler, IOrderPrecedenceMetadataView> handler in _handlers)
            {
                ImmutableHashSet <string> handlerRules = handler.Value.GetRuleNames(handlerType);

                // Slice project changes to include only rules the handler claims an interest in.
                var projectChanges = projectUpdate.ProjectChanges
                                     .Where(x => handlerRules.Contains(x.Key))
                                     .ToImmutableDictionary();

                if (projectChanges.Any(x => x.Value.Difference.AnyChanges))
                {
                    handler.Value.Handle(projectChanges, targetFrameworkToUpdate, changesBuilder);
                }
            }

            ImmutableDictionary <ITargetFramework, IDependenciesChanges> changes = changesBuilder.TryBuildChanges();

            if (changes != null)
            {
                // Notify subscribers of a change in dependency data
                DependenciesChanged?.Invoke(
                    this,
                    new DependencySubscriptionChangedEventArgs(
                        currentAggregateContext.ActiveTargetFramework,
                        catalogSnapshot,
                        changes));
            }

            // record all the rules that have occurred
            _treeTelemetryService.ObserveTargetFrameworkRules(targetFrameworkToUpdate, projectUpdate.ProjectChanges.Keys);
        }
示例#2
0
        public Dependency(IDependencyModel dependencyModel, ITargetFramework targetFramework, string containingProjectPath)
        {
            Requires.NotNull(dependencyModel, nameof(dependencyModel));
            Requires.NotNullOrEmpty(dependencyModel.ProviderType, nameof(dependencyModel.ProviderType));
            Requires.NotNullOrEmpty(dependencyModel.Id, nameof(dependencyModel.Id));
            Requires.NotNull(targetFramework, nameof(targetFramework));
            Requires.NotNullOrEmpty(containingProjectPath, nameof(containingProjectPath));

            TargetFramework = targetFramework;

            _modelId = dependencyModel.Id;
            _containingProjectPath = containingProjectPath;

            ProviderType     = dependencyModel.ProviderType;
            Name             = dependencyModel.Name ?? string.Empty;
            Caption          = dependencyModel.Caption ?? string.Empty;
            OriginalItemSpec = dependencyModel.OriginalItemSpec ?? string.Empty;
            Path             = dependencyModel.Path ?? string.Empty;
            SchemaName       = dependencyModel.SchemaName ?? Folder.SchemaName;
            _schemaItemType  = dependencyModel.SchemaItemType ?? Folder.PrimaryDataSourceItemType;
            Resolved         = dependencyModel.Resolved;
            TopLevel         = dependencyModel.TopLevel;
            Implicit         = dependencyModel.Implicit;
            Visible          = dependencyModel.Visible;
            Priority         = dependencyModel.Priority;
            Flags            = dependencyModel.Flags;

            // Just in case custom providers don't do it, add corresponding flags for Resolved state.
            // This is needed for tree update logic to track if tree node changing state from unresolved
            // to resolved or vice-versa (it helps to decide if we need to remove it or update in-place
            // in the tree to avoid flicks).
            if (Resolved)
            {
                if (!Flags.Contains(DependencyTreeFlags.Resolved))
                {
                    Flags += DependencyTreeFlags.Resolved;
                }
            }
            else
            {
                if (!Flags.Contains(DependencyTreeFlags.Unresolved))
                {
                    Flags += DependencyTreeFlags.Unresolved;
                }
            }

            // If this is one of our implementations of IDependencyModel then we can just reuse the icon
            // set rather than creating a new one.
            if (dependencyModel is DependencyModel model)
            {
                IconSet = model.IconSet;
            }
            else
            {
                IconSet = DependencyIconSetCache.Instance.GetOrAddIconSet(dependencyModel.Icon, dependencyModel.ExpandedIcon, dependencyModel.UnresolvedIcon, dependencyModel.UnresolvedExpandedIcon);
            }

            Properties = dependencyModel.Properties
                         ?? ImmutableStringDictionary <string> .EmptyOrdinal
                         .Add(Folder.IdentityProperty, Caption)
                         .Add(Folder.FullPathProperty, Path);

            if (dependencyModel.DependencyIDs == null || dependencyModel.DependencyIDs.Count == 0)
            {
                DependencyIDs = ImmutableArray <string> .Empty;
            }
            else
            {
                int count = dependencyModel.DependencyIDs.Count;
                ImmutableArray <string> .Builder ids = ImmutableArray.CreateBuilder <string>(count);
                for (int i = 0; i < count; i++)
                {
                    ids.Add(GetID(TargetFramework, ProviderType, dependencyModel.DependencyIDs[i]));
                }
                DependencyIDs = ids.MoveToImmutable();
            }
        }
 public void Added(ITargetFramework targetFramework, IDependencyModel model)
 {
     GetChanges(targetFramework).Added(model);
 }
示例#4
0
        /// <summary>
        /// Determines whether the current project context object is out of date based on the project's target frameworks.
        /// If so, a new one is created and subscriptions are updated accordingly.
        /// </summary>
        private async Task UpdateProjectContextAndSubscriptionsAsync()
        {
            // Ensure that only single thread is attempting to create a project context.
            AggregateCrossTargetProjectContext newProjectContext = await ExecuteWithinLockAsync(TryUpdateCurrentAggregateProjectContextAsync);

            if (newProjectContext != null)
            {
                // Dispose existing subscriptions.
                DisposeAndClearSubscriptions();

                // Add subscriptions for the configured projects in the new project context.
                await AddSubscriptionsAsync(newProjectContext);
            }

            return;

            async Task <AggregateCrossTargetProjectContext> TryUpdateCurrentAggregateProjectContextAsync()
            {
                AggregateCrossTargetProjectContext previousContext = _currentAggregateProjectContext;

                // Check if we have already computed the project context.
                if (previousContext != null)
                {
                    // For non-cross targeting projects, we can use the current project context if the TargetFramework hasn't changed.
                    // For cross-targeting projects, we need to verify that the current project context matches latest frameworks targeted by the project.
                    // If not, we create a new one and dispose the current one.
                    ConfigurationGeneral projectProperties = await _commonServices.ActiveConfiguredProjectProperties.GetConfigurationGeneralPropertiesAsync();

                    if (!previousContext.IsCrossTargeting)
                    {
                        string newTargetFrameworkName = (string)await projectProperties.TargetFramework.GetValueAsync();

                        ITargetFramework newTargetFramework = _targetFrameworkProvider.GetTargetFramework(newTargetFrameworkName);
                        if (previousContext.ActiveTargetFramework.Equals(newTargetFramework))
                        {
                            // No change
                            return(null);
                        }
                    }
                    else
                    {
                        // Check if the current project context is up-to-date for the current active and known project configurations.
                        ProjectConfiguration activeProjectConfiguration = _commonServices.ActiveConfiguredProject.ProjectConfiguration;
                        IImmutableSet <ProjectConfiguration> knownProjectConfigurations = await _commonServices.Project.Services.ProjectConfigurationsService.GetKnownProjectConfigurationsAsync();

                        if (knownProjectConfigurations.All(c => c.IsCrossTargeting()) &&
                            HasMatchingTargetFrameworks(previousContext, activeProjectConfiguration, knownProjectConfigurations))
                        {
                            // No change
                            return(null);
                        }
                    }
                }

                // Force refresh the CPS active project configuration (needs UI thread).
                await _commonServices.ThreadingService.SwitchToUIThread();

                await _activeProjectConfigurationRefreshService.RefreshActiveProjectConfigurationAsync();

                // Create new project context.
                AggregateCrossTargetProjectContext newContext = await _contextProvider.Value.CreateProjectContextAsync();

                _currentAggregateProjectContext = newContext;

                OnAggregateContextChanged(previousContext, newContext);

                return(newContext);
            }

            bool HasMatchingTargetFrameworks(
                AggregateCrossTargetProjectContext previousContext,
                ProjectConfiguration activeProjectConfiguration,
                IReadOnlyCollection <ProjectConfiguration> knownProjectConfigurations)
            {
                Assumes.True(activeProjectConfiguration.IsCrossTargeting());

                ITargetFramework activeTargetFramework = _targetFrameworkProvider.GetTargetFramework(activeProjectConfiguration.Dimensions[ConfigurationGeneral.TargetFrameworkProperty]);

                if (!previousContext.ActiveTargetFramework.Equals(activeTargetFramework))
                {
                    // Active target framework is different.
                    return(false);
                }

                var targetFrameworkMonikers = knownProjectConfigurations
                                              .Select(c => c.Dimensions[ConfigurationGeneral.TargetFrameworkProperty])
                                              .Distinct()
                                              .ToList();

                if (targetFrameworkMonikers.Count != previousContext.TargetFrameworks.Length)
                {
                    // Different number of target frameworks.
                    return(false);
                }

                foreach (string targetFrameworkMoniker in targetFrameworkMonikers)
                {
                    ITargetFramework targetFramework = _targetFrameworkProvider.GetTargetFramework(targetFrameworkMoniker);

                    if (!previousContext.TargetFrameworks.Contains(targetFramework))
                    {
                        // Differing TargetFramework
                        return(false);
                    }
                }

                return(true);
            }

            void OnAggregateContextChanged(
                AggregateCrossTargetProjectContext oldContext,
                AggregateCrossTargetProjectContext newContext)
            {
                if (oldContext == null)
                {
                    // all new rules will be sent to new context, we don't need to clean up anything
                    return;
                }

                var targetsToClean = new HashSet <ITargetFramework>();

                ImmutableArray <ITargetFramework> oldTargets = oldContext.TargetFrameworks;

                if (newContext == null)
                {
                    targetsToClean.AddRange(oldTargets);
                }
                else
                {
                    ImmutableArray <ITargetFramework> newTargets = newContext.TargetFrameworks;

                    targetsToClean.AddRange(oldTargets.Except(newTargets));
                }

                if (targetsToClean.Count != 0)
                {
                    TryUpdateSnapshot(snapshot => snapshot.RemoveTargets(targetsToClean));
                }
            }
        }
        /// <summary>
        /// Builds all available sub trees under root: target framework or Dependencies node
        /// when there is only one target.
        /// </summary>
        private async Task <IProjectTree> BuildSubTreesAsync(
            IProjectTree rootNode,
            ITargetFramework activeTarget,
            ITargetedDependenciesSnapshot targetedSnapshot,
            Func <IProjectTree, IEnumerable <IProjectTree>, IProjectTree> syncFunc)
        {
            var groupedByProviderType = new Dictionary <string, List <IDependency> >(StringComparers.DependencyProviderTypes);

            foreach (IDependency dependency in targetedSnapshot.TopLevelDependencies)
            {
                if (!dependency.Visible)
                {
                    if (dependency.Flags.Contains(DependencyTreeFlags.ShowEmptyProviderRootNode))
                    {
                        // if provider sends special invisible node with flag ShowEmptyProviderRootNode, we
                        // need to show provider node even if it does not have any dependencies.
                        groupedByProviderType.Add(dependency.ProviderType, new List <IDependency>());
                    }

                    continue;
                }

                if (!groupedByProviderType.TryGetValue(dependency.ProviderType, out List <IDependency> dependencies))
                {
                    dependencies = new List <IDependency>();
                    groupedByProviderType.Add(dependency.ProviderType, dependencies);
                }

                dependencies.Add(dependency);
            }

            var currentNodes = new List <IProjectTree>(capacity: groupedByProviderType.Count);

            bool isActiveTarget = targetedSnapshot.TargetFramework.Equals(activeTarget);

            foreach ((string providerType, List <IDependency> dependencies) in groupedByProviderType)
            {
                IDependencyViewModel subTreeViewModel = ViewModelFactory.CreateRootViewModel(
                    providerType, targetedSnapshot.CheckForUnresolvedDependencies(providerType));
                IProjectTree subTreeNode      = rootNode.FindChildWithCaption(subTreeViewModel.Caption);
                bool         isNewSubTreeNode = subTreeNode == null;

                ProjectTreeFlags excludedFlags = targetedSnapshot.TargetFramework.Equals(TargetFramework.Any)
                    ? ProjectTreeFlags.Create(ProjectTreeFlags.Common.BubbleUp)
                    : ProjectTreeFlags.Empty;

                subTreeNode = CreateOrUpdateNode(
                    subTreeNode,
                    subTreeViewModel,
                    rule: null,
                    isProjectItem: false,
                    excludedFlags: excludedFlags);

                subTreeNode = await BuildSubTreeAsync(
                    subTreeNode,
                    targetedSnapshot,
                    dependencies,
                    isActiveTarget,
                    shouldCleanup : !isNewSubTreeNode);

                currentNodes.Add(subTreeNode);

                rootNode = isNewSubTreeNode
                    ? rootNode.Add(subTreeNode).Parent
                    : subTreeNode.Parent;
            }

            return(syncFunc(rootNode, currentNodes));
        }
示例#6
0
        public static IDependency FromJson(
            string jsonString,
            ProjectTreeFlags?flags                 = null,
            ImageMoniker?icon                      = null,
            ImageMoniker?expandedIcon              = null,
            ImageMoniker?unresolvedIcon            = null,
            ImageMoniker?unresolvedExpandedIcon    = null,
            Dictionary <string, string> properties = null,
            IEnumerable <string> dependenciesIds   = null,
            ITargetFramework targetFramework       = null)
        {
            if (string.IsNullOrEmpty(jsonString))
            {
                return(null);
            }

            var json = JObject.Parse(jsonString);
            var data = json.ToObject <TestDependency>();

            if (flags != null && flags.HasValue)
            {
                data.Flags = data.Flags.Union(flags.Value);
            }

            if (icon != null && icon.HasValue)
            {
                data.Icon = icon.Value;
            }

            if (expandedIcon != null && expandedIcon.HasValue)
            {
                data.ExpandedIcon = expandedIcon.Value;
            }

            if (unresolvedIcon != null && unresolvedIcon.HasValue)
            {
                data.UnresolvedIcon = unresolvedIcon.Value;
            }

            if (unresolvedExpandedIcon != null && unresolvedExpandedIcon.HasValue)
            {
                data.UnresolvedExpandedIcon = unresolvedExpandedIcon.Value;
            }

            if (properties != null)
            {
                data.Properties = ImmutableStringDictionary <string> .EmptyOrdinal.AddRange(properties);
            }

            if (dependenciesIds != null)
            {
                data.DependencyIDs = ImmutableList <string> .Empty.AddRange(dependenciesIds);
            }

            if (targetFramework != null)
            {
                data.TargetFramework = targetFramework;
            }

            return(data);
        }
示例#7
0
        public Dependency(IDependencyModel dependencyModel, ITargetFramework targetFramework, string containingProjectPath)
        {
            Requires.NotNull(dependencyModel, nameof(dependencyModel));
            Requires.NotNullOrEmpty(dependencyModel.ProviderType, nameof(dependencyModel.ProviderType));
            Requires.NotNullOrEmpty(dependencyModel.Id, nameof(dependencyModel.Id));
            Requires.NotNull(targetFramework, nameof(targetFramework));
            Requires.NotNullOrEmpty(containingProjectPath, nameof(containingProjectPath));

            TargetFramework = targetFramework;

            _modelId = dependencyModel.Id;
            _containingProjectPath = containingProjectPath;

            ProviderType     = dependencyModel.ProviderType;
            Name             = dependencyModel.Name ?? string.Empty;
            Version          = dependencyModel.Version ?? string.Empty;
            Caption          = dependencyModel.Caption ?? string.Empty;
            OriginalItemSpec = dependencyModel.OriginalItemSpec ?? string.Empty;
            Path             = dependencyModel.Path ?? string.Empty;
            SchemaName       = dependencyModel.SchemaName ?? Folder.SchemaName;
            _schemaItemType  = dependencyModel.SchemaItemType ?? Folder.PrimaryDataSourceItemType;
            Resolved         = dependencyModel.Resolved;
            TopLevel         = dependencyModel.TopLevel;
            Implicit         = dependencyModel.Implicit;
            Visible          = dependencyModel.Visible;
            Priority         = dependencyModel.Priority;
            Flags            = dependencyModel.Flags;

            // Just in case custom providers don't do it, add corresponding flags for Resolved state.
            // This is needed for tree update logic to track if tree node changing state from unresolved
            // to resolved or vice-versa (it helps to decide if we need to remove it or update in-place
            // in the tree to avoid flicks).
            if (Resolved)
            {
                Flags = Flags.Union(DependencyTreeFlags.ResolvedFlags);
            }
            else
            {
                Flags = Flags.Union(DependencyTreeFlags.UnresolvedFlags);
            }

            Icon                   = dependencyModel.Icon;
            ExpandedIcon           = dependencyModel.ExpandedIcon;
            UnresolvedIcon         = dependencyModel.UnresolvedIcon;
            UnresolvedExpandedIcon = dependencyModel.UnresolvedExpandedIcon;
            Properties             = dependencyModel.Properties ??
                                     ImmutableDictionary <string, string> .Empty
                                     .Add(Folder.IdentityProperty, Caption)
                                     .Add(Folder.FullPathProperty, Path);

            if (dependencyModel.DependencyIDs == null)
            {
                DependencyIDs = ImmutableList <string> .Empty;
            }
            else
            {
                var normalizedDependencyIDs = ImmutableList.CreateBuilder <string>();
                foreach (var id in dependencyModel.DependencyIDs)
                {
                    normalizedDependencyIDs.Add(GetID(TargetFramework, ProviderType, id));
                }

                DependencyIDs = normalizedDependencyIDs.ToImmutable();
            }
        }
示例#8
0
 protected abstract T CreateRuleChangeContext(ITargetFramework target, IProjectCatalogSnapshot catalogs);
示例#9
0
        /// <summary>
        /// Builds all available sub trees under root: target framework or Dependencies node
        /// when there is only one target.
        /// </summary>
        private IProjectTree BuildSubTrees(
            IProjectTree rootNode,
            ITargetFramework activeTarget,
            ITargetedDependenciesSnapshot targetedSnapshot,
            IProjectCatalogSnapshot catalogs,
            Func <IProjectTree, IEnumerable <IProjectTree>, IProjectTree> syncFunc)
        {
            var currentNodes           = new List <IProjectTree>();
            var grouppedByProviderType = new Dictionary <string, List <IDependency> >(StringComparer.OrdinalIgnoreCase);

            foreach (var dependency in targetedSnapshot.TopLevelDependencies)
            {
                if (!dependency.Visible)
                {
                    if (dependency.Flags.Contains(DependencyTreeFlags.ShowEmptyProviderRootNode))
                    {
                        // if provider sends special invisible node with flag ShowEmptyProviderRootNode, we
                        // need to show provider node even if it does not have any dependencies.
                        grouppedByProviderType.Add(dependency.ProviderType, new List <IDependency>());
                    }

                    continue;
                }

                if (!grouppedByProviderType.TryGetValue(dependency.ProviderType, out List <IDependency> dependencies))
                {
                    dependencies = new List <IDependency>();
                    grouppedByProviderType.Add(dependency.ProviderType, dependencies);
                }

                dependencies.Add(dependency);
            }

            var isActiveTarget = targetedSnapshot.TargetFramework.Equals(activeTarget);

            foreach (var dependencyGroup in grouppedByProviderType)
            {
                var subTreeViewModel = ViewModelFactory.CreateRootViewModel(
                    dependencyGroup.Key, targetedSnapshot.CheckForUnresolvedDependencies(dependencyGroup.Key));
                var subTreeNode      = rootNode.FindNodeByCaption(subTreeViewModel.Caption);
                var isNewSubTreeNode = subTreeNode == null;

                var excludedFlags = ProjectTreeFlags.Empty;
                if (targetedSnapshot.TargetFramework.Equals(TargetFramework.Any))
                {
                    excludedFlags = ProjectTreeFlags.Create(ProjectTreeFlags.Common.BubbleUp);
                }

                subTreeNode = CreateOrUpdateNode(
                    subTreeNode,
                    subTreeViewModel,
                    rule: null,
                    isProjectItem: false,
                    excludedFlags: excludedFlags);

                subTreeNode = BuildSubTree(
                    subTreeNode,
                    targetedSnapshot,
                    dependencyGroup.Value,
                    catalogs,
                    isActiveTarget,
                    shouldCleanup: !isNewSubTreeNode);

                currentNodes.Add(subTreeNode);

                if (isNewSubTreeNode)
                {
                    rootNode = rootNode.Add(subTreeNode).Parent;
                }
                else
                {
                    rootNode = subTreeNode.Parent;
                }
            }

            return(syncFunc(rootNode, currentNodes));
        }
        public override IDependency BeforeAdd(
            string projectPath,
            ITargetFramework targetFramework,
            IDependency dependency,
            ImmutableDictionary <string, IDependency> .Builder worldBuilder,
            IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType,
            IImmutableSet <string> projectItemSpecs,
            out bool filterAnyChanges)
        {
            filterAnyChanges = false;

            if (!dependency.TopLevel)
            {
                return(dependency);
            }

            if (dependency.Flags.Contains(DependencyTreeFlags.SdkSubTreeNodeFlags))
            {
                // This is an SDK dependency.
                //
                // Try to find a package dependency with the same name.

                string packageId = Dependency.GetID(targetFramework, PackageRuleHandler.ProviderTypeString, modelId: dependency.Name);

                if (worldBuilder.TryGetValue(packageId, out IDependency package) && package.Resolved)
                {
                    // Set to resolved, and copy dependencies.

                    filterAnyChanges = true;
                    return(dependency.ToResolved(
                               schemaName: ResolvedSdkReference.SchemaName,
                               dependencyIDs: package.DependencyIDs));
                }
            }
            else if (dependency.Flags.Contains(DependencyTreeFlags.PackageNodeFlags) && dependency.Resolved)
            {
                // This is a package dependency.
                //
                // Try to find an SDK dependency with the same name.

                string sdkId = Dependency.GetID(targetFramework, SdkRuleHandler.ProviderTypeString, modelId: dependency.Name);

                if (worldBuilder.TryGetValue(sdkId, out IDependency sdk))
                {
                    // We have an SDK dependency for this package. Such dependencies, when implicit, are created
                    // as unresolved by SdkRuleHandler, and are only marked resolved here once we have resolved the
                    // corresponding package.
                    //
                    // Set to resolved, and copy dependencies.

                    filterAnyChanges = true;
                    sdk = sdk.ToResolved(
                        schemaName: ResolvedSdkReference.SchemaName,
                        dependencyIDs: dependency.DependencyIDs);

                    worldBuilder[sdk.Id] = sdk;
                }
            }

            return(dependency);
        }
示例#11
0
        public override void BeforeAddOrUpdate(
            ITargetFramework targetFramework,
            IDependency dependency,
            IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType,
            IImmutableSet <string>?projectItemSpecs,
            AddDependencyContext context)
        {
            // Only apply to top-level dependencies
            if (!dependency.TopLevel)
            {
                context.Accept(dependency);
                return;
            }

            IDependency?matchingDependency = null;
            bool        shouldApplyAlias   = false;

            foreach ((string _, IDependency other) in context)
            {
                if (!other.TopLevel ||
                    other.Id.Equals(dependency.Id, StringComparison.OrdinalIgnoreCase) ||
                    !StringComparers.DependencyProviderTypes.Equals(other.ProviderType, dependency.ProviderType))
                {
                    continue;
                }

                if (other.Caption.StartsWith(dependency.Caption, StringComparison.OrdinalIgnoreCase))
                {
                    if (other.Caption.Length == dependency.Caption.Length)
                    {
                        // Exact match.
                        matchingDependency = other;
                        shouldApplyAlias   = true;
                        break;
                    }

                    // Prefix matches.
                    // Check whether we have a match of form "Caption (ItemSpec)".

                    string itemSpec = other.OriginalItemSpec;
                    int    expectedItemSpecIndex = dependency.Caption.Length + 2;               // " (".Length
                    int    expectedLength        = expectedItemSpecIndex + itemSpec.Length + 1; // ")".Length

                    if (other.Caption.Length == expectedLength &&
                        string.Compare(other.Caption, expectedItemSpecIndex, itemSpec, 0, itemSpec.Length, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        shouldApplyAlias = true;
                    }
                }
            }

            if (shouldApplyAlias)
            {
                if (matchingDependency != null)
                {
                    // Change the matching dependency's alias too
                    context.AddOrUpdate(matchingDependency.SetProperties(caption: GetAlias(matchingDependency)));
                }

                // Use the alias for the caption
                context.Accept(dependency.SetProperties(caption: GetAlias(dependency)));
            }
            else
            {
                // Accept without changes
                context.Accept(dependency);
            }

            return;
示例#12
0
 public TargetDependencyViewModel(ITargetFramework targetFramework, bool hasReachableVisibleUnresolvedDependency)
 {
     Caption = targetFramework.FriendlyName;
     Flags   = GetCachedFlags(targetFramework);
     _hasUnresolvedDependency = hasReachableVisibleUnresolvedDependency;
 public ConfiguredProject?GetInnerConfiguredProject(ITargetFramework target)
 {
     return(_configuredProjectByTargetFramework.FirstOrDefault((x, t) => t.Equals(x.Key), target).Value);
 }
        /// <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,
            ImmutableDictionary <ITargetFramework, IDependenciesChanges> changes,
            IProjectCatalogSnapshot catalogs,
            ITargetFramework activeTargetFramework,
            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));
            // catalogs can be null
            Requires.Argument(!snapshotFilters.IsDefault, nameof(snapshotFilters), "Cannot be default.");
            Requires.NotNull(subTreeProviderByProviderType, nameof(subTreeProviderByProviderType));
            // projectItemSpecs can be null

            var builder = previousSnapshot.Targets.ToBuilder();

            bool targetChanged = false;

            foreach ((ITargetFramework targetFramework, IDependenciesChanges dependenciesChanges) in changes)
            {
                if (!builder.TryGetValue(targetFramework, out ITargetedDependenciesSnapshot previousTargetedSnapshot))
                {
                    previousTargetedSnapshot = TargetedDependenciesSnapshot.CreateEmpty(projectPath, targetFramework, catalogs);
                }

                ITargetedDependenciesSnapshot newTargetedSnapshot = TargetedDependenciesSnapshot.FromChanges(
                    projectPath,
                    previousTargetedSnapshot,
                    dependenciesChanges,
                    catalogs,
                    snapshotFilters,
                    subTreeProviderByProviderType,
                    projectItemSpecs);

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

            targetChanged |= RemoveTargetFrameworksWithNoDependencies();

            ITargetFramework activeTarget = activeTargetFramework ?? previousSnapshot.ActiveTarget;

            if (targetChanged)
            {
                // Targets have changed
                return(new DependenciesSnapshot(
                           previousSnapshot.ProjectPath,
                           activeTarget,
                           builder.ToImmutable()));
            }

            if (!activeTarget.Equals(previousSnapshot.ActiveTarget))
            {
                // The active target changed
                return(new DependenciesSnapshot(
                           previousSnapshot.ProjectPath,
                           activeTarget,
                           previousSnapshot.Targets));
            }

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

            // Active target differs

            bool RemoveTargetFrameworksWithNoDependencies()
            {
                // This is a long-winded way of doing this that minimises allocations

                List <ITargetFramework> emptyFrameworks = null;
                bool anythingRemoved = false;

                foreach ((ITargetFramework targetFramework, ITargetedDependenciesSnapshot targetedSnapshot) in builder)
                {
                    if (targetedSnapshot.DependenciesWorld.Count == 0)
                    {
                        if (emptyFrameworks == null)
                        {
                            anythingRemoved = true;
                            emptyFrameworks = new List <ITargetFramework>(builder.Count);
                        }

                        emptyFrameworks.Add(targetFramework);
                    }
                }

                if (emptyFrameworks != null)
                {
                    foreach (ITargetFramework framework in emptyFrameworks)
                    {
                        builder.Remove(framework);
                    }
                }

                return(anythingRemoved);
            }
        }
        private IDependencyModel GetDependencyModel(
            string itemSpec,
            bool resolved,
            IImmutableDictionary <string, string> properties,
            IProjectChangeDescription projectChange,
            HashSet <string> unresolvedChanges,
            ITargetFramework targetFramework)
        {
            PackageDependencyMetadata metadata;
            bool isTopLevel = true;
            bool isTarget   = false;

            if (resolved)
            {
                metadata   = new PackageDependencyMetadata(itemSpec, properties);
                isTopLevel = metadata.IsImplicitlyDefined ||
                             (metadata.DependencyType == DependencyType.Package &&
                              unresolvedChanges != null &&
                              unresolvedChanges.Contains(metadata.Name));
                isTarget = metadata.IsTarget;
                ITargetFramework packageTargetFramework = TargetFrameworkProvider.GetTargetFramework(metadata.Target);
                if (!(packageTargetFramework?.Equals(targetFramework) == true))
                {
                    return(null);
                }
            }
            else
            {
                metadata = CreateUnresolvedMetadata(itemSpec, properties);
            }

            if (isTarget)
            {
                return(null);
            }

            string originalItemSpec = itemSpec;

            if (resolved && isTopLevel)
            {
                originalItemSpec = metadata.Name;
            }

            IDependencyModel dependencyModel = null;

            switch (metadata.DependencyType)
            {
            case DependencyType.Package:
                dependencyModel = new PackageDependencyModel(
                    ProviderType,
                    itemSpec,
                    originalItemSpec,
                    metadata.Name,
                    DependencyTreeFlags.NuGetSubTreeNodeFlags,
                    metadata.Version,
                    resolved,
                    metadata.IsImplicitlyDefined,
                    isTopLevel,
                    !metadata.IsImplicitlyDefined /*visible*/,
                    properties,
                    metadata.DependenciesItemSpecs);
                break;

            case DependencyType.Assembly:
            case DependencyType.FrameworkAssembly:
                dependencyModel = new PackageAssemblyDependencyModel(
                    ProviderType,
                    itemSpec,
                    originalItemSpec,
                    metadata.Name,
                    DependencyTreeFlags.NuGetSubTreeNodeFlags,
                    resolved,
                    properties,
                    metadata.DependenciesItemSpecs);
                break;

            case DependencyType.AnalyzerAssembly:
                dependencyModel = new PackageAnalyzerAssemblyDependencyModel(
                    ProviderType,
                    itemSpec,
                    originalItemSpec,
                    metadata.Name,
                    DependencyTreeFlags.NuGetSubTreeNodeFlags,
                    resolved,
                    properties,
                    metadata.DependenciesItemSpecs);
                break;

            case DependencyType.Diagnostic:
                dependencyModel = new DiagnosticDependencyModel(
                    ProviderType,
                    itemSpec,
                    metadata.Severity,
                    metadata.DiagnosticCode,
                    metadata.Name,
                    DependencyTreeFlags.NuGetSubTreeNodeFlags,
                    isVisible: true,
                    properties: properties);
                break;

            default:
                dependencyModel = new PackageUnknownDependencyModel(
                    ProviderType,
                    itemSpec,
                    originalItemSpec,
                    metadata.Name,
                    DependencyTreeFlags.NuGetSubTreeNodeFlags,
                    resolved,
                    properties,
                    metadata.DependenciesItemSpecs);
                break;
            }

            return(dependencyModel);
        }
        /// <inheritdoc />
        protected override async Task InitializeCoreAsync(CancellationToken cancellationToken)
        {
            await UpdateProjectContextAndSubscriptionsAsync();

            lock (_lock)
            {
                if (_isDisposed)
                {
                    throw new ObjectDisposedException(nameof(DependenciesSnapshotProvider));
                }

                IDisposable unregister = _aggregateSnapshotProvider.RegisterSnapshotProvider(this);

                _disposables.Add(unregister);

                _commonServices.Project.ProjectUnloading += OnUnconfiguredProjectUnloadingAsync;
                _commonServices.Project.ProjectRenamed   += OnUnconfiguredProjectRenamedAsync;

                foreach (Lazy <IProjectDependenciesSubTreeProvider, IOrderPrecedenceMetadataView> provider in _subTreeProviders)
                {
                    provider.Value.DependenciesChanged += OnSubtreeProviderDependenciesChanged;
                }

                _disposables.Add(
                    new DisposableDelegate(
                        () =>
                {
                    _commonServices.Project.ProjectUnloading -= OnUnconfiguredProjectUnloadingAsync;
                    _commonServices.Project.ProjectRenamed   -= OnUnconfiguredProjectRenamedAsync;

                    foreach (Lazy <IProjectDependenciesSubTreeProvider, IOrderPrecedenceMetadataView> provider in _subTreeProviders)
                    {
                        provider.Value.DependenciesChanged -= OnSubtreeProviderDependenciesChanged;
                    }
                }));
            }

            return;

            Task OnUnconfiguredProjectUnloadingAsync(object sender, EventArgs args)
            {
                // If our project unloads, we have no more work to do. Notify listeners and clean everything up.

                SnapshotProviderUnloading?.Invoke(this, new SnapshotProviderUnloadingEventArgs(this));

                DisposeCore();

                return(Task.CompletedTask);
            }

            Task OnUnconfiguredProjectRenamedAsync(object sender, ProjectRenamedEventArgs e)
            {
                SnapshotRenamed?.Invoke(this, e);

                return(Task.CompletedTask);
            }

            void OnSubtreeProviderDependenciesChanged(object sender, DependenciesChangedEventArgs e)
            {
                if (IsDisposing || IsDisposed || !e.Changes.AnyChanges())
                {
                    return;
                }

                ITargetFramework targetFramework =
                    string.IsNullOrEmpty(e.TargetShortOrFullName) || TargetFramework.Any.Equals(e.TargetShortOrFullName)
                        ? TargetFramework.Any
                        : _targetFrameworkProvider.GetTargetFramework(e.TargetShortOrFullName !) ?? TargetFramework.Any;

                UpdateDependenciesSnapshot(targetFramework, e.Changes, catalogs: null, targetFrameworks: default, activeTargetFramework: null, e.Token);
示例#17
0
            public static bool TryGetMetadata(
                string itemSpec,
                bool isResolved,
                IImmutableDictionary <string, string> properties,
                HashSet <string> unresolvedChanges,
                ITargetFramework targetFramework,
                ITargetFrameworkProvider targetFrameworkProvider,
                out PackageDependencyMetadata metadata)
            {
                Requires.NotNull(itemSpec, nameof(itemSpec));
                Requires.NotNull(properties, nameof(properties));
                // unresolvedChanges can be null
                Requires.NotNull(targetFramework, nameof(targetFramework));
                Requires.NotNull(targetFrameworkProvider, nameof(targetFrameworkProvider));

                bool isTopLevel;

                string target = GetTargetFromDependencyId(itemSpec);

                DependencyType dependencyType = properties.GetEnumProperty <DependencyType>(ProjectItemMetadata.Type)
                                                ?? (isResolved ? DependencyType.Unknown : DependencyType.Package);

                string name = properties.GetStringProperty(ProjectItemMetadata.Name) ?? itemSpec;

                bool isImplicitlyDefined = properties.GetBoolProperty(ProjectItemMetadata.IsImplicitlyDefined) ?? false;

                if (isResolved)
                {
                    isTopLevel = isImplicitlyDefined ||
                                 (dependencyType == DependencyType.Package && unresolvedChanges?.Contains(name) == true);

                    bool isTarget = itemSpec.IndexOf('/') == -1;

                    if (isTarget)
                    {
                        metadata = default;
                        return(false);
                    }

                    ITargetFramework packageTargetFramework = targetFrameworkProvider.GetTargetFramework(target);

                    if (packageTargetFramework?.Equals(targetFramework) != true)
                    {
                        metadata = default;
                        return(false);
                    }
                }
                else
                {
                    isTopLevel = true;
                }

                string originalItemSpec = isResolved && isTopLevel
                    ? name
                    : itemSpec;

                metadata = new PackageDependencyMetadata(
                    dependencyType,
                    target,
                    itemSpec,
                    originalItemSpec,
                    name,
                    isResolved,
                    isImplicitlyDefined,
                    isTopLevel,
                    properties);
                return(true);
示例#18
0
        public static Mock <IDependency> Implement(string providerType                 = null,
                                                   string id                           = null,
                                                   string originalItemSpec             = null,
                                                   string path                         = null,
                                                   string fullPath                     = null,
                                                   string name                         = null,
                                                   string caption                      = null,
                                                   string alias                        = null,
                                                   IEnumerable <string> dependencyIDs  = null,
                                                   bool?resolved                       = null,
                                                   bool?topLevel                       = null,
                                                   bool?isImplicit                     = null,
                                                   ProjectTreeFlags?flags              = null,
                                                   string setPropertiesCaption         = null,
                                                   bool?setPropertiesResolved          = null,
                                                   ProjectTreeFlags?setPropertiesFlags = null,
                                                   bool?setPropertiesImplicit          = null,
                                                   IDependency setPropertiesReturn     = null,
                                                   bool?equals                         = null,
                                                   ImmutableArray <string> setPropertiesDependencyIDs = default,
                                                   string setPropertiesSchemaName         = null,
                                                   ITargetFramework targetFramework       = null,
                                                   DependencyIconSet iconSet              = null,
                                                   DependencyIconSet setPropertiesIconSet = null,
                                                   MockBehavior mockBehavior              = MockBehavior.Strict)
        {
            var mock = new Mock <IDependency>(mockBehavior);

            if (providerType != null)
            {
                mock.Setup(x => x.ProviderType).Returns(providerType);
            }

            if (id != null)
            {
                mock.Setup(x => x.Id).Returns(id);
            }

            if (originalItemSpec != null)
            {
                mock.Setup(x => x.OriginalItemSpec).Returns(originalItemSpec);
            }

            if (fullPath != null)
            {
                mock.Setup(x => x.FullPath).Returns(fullPath);
            }

            if (path != null)
            {
                mock.Setup(x => x.Path).Returns(path);
            }

            if (name != null)
            {
                mock.Setup(x => x.Name).Returns(name);
            }

            if (caption != null)
            {
                mock.Setup(x => x.Caption).Returns(caption);
            }

            if (alias != null)
            {
                mock.Setup(x => x.Alias).Returns(alias);
            }

            if (dependencyIDs != null)
            {
                mock.Setup(x => x.DependencyIDs).Returns(ImmutableArray.CreateRange(dependencyIDs));
            }

            if (resolved.HasValue)
            {
                mock.Setup(x => x.Resolved).Returns(resolved.Value);
            }

            if (topLevel.HasValue)
            {
                mock.Setup(x => x.TopLevel).Returns(topLevel.Value);
            }

            if (isImplicit.HasValue)
            {
                mock.Setup(x => x.Implicit).Returns(isImplicit.Value);
            }

            if (flags.HasValue)
            {
                mock.Setup(x => x.Flags).Returns(flags.Value);
            }

            if (targetFramework != null)
            {
                mock.Setup(x => x.TargetFramework).Returns(targetFramework);
            }

            if (iconSet != null)
            {
                mock.Setup(x => x.IconSet).Returns(iconSet);
            }

            if (setPropertiesCaption != null ||
                !setPropertiesDependencyIDs.IsDefault ||
                setPropertiesResolved != null ||
                setPropertiesFlags != null ||
                setPropertiesImplicit != null ||
                setPropertiesIconSet != null)
            {
                mock.Setup(x => x.SetProperties(
                               setPropertiesCaption,
                               setPropertiesResolved,
                               setPropertiesFlags,
                               setPropertiesSchemaName,
                               setPropertiesDependencyIDs,
                               setPropertiesIconSet,
                               setPropertiesImplicit))
                .Returns(setPropertiesReturn ?? mock.Object);
            }

            if (equals == true)
            {
                mock.Setup(x => x.Equals(It.IsAny <IDependency>())).Returns(true);
            }

            return(mock);
        }
示例#19
0
        public static Mock <IDependency> Implement(string providerType                 = null,
                                                   string id                           = null,
                                                   string originalItemSpec             = null,
                                                   string path                         = null,
                                                   string fullPath                     = null,
                                                   string name                         = null,
                                                   string caption                      = null,
                                                   string alias                        = null,
                                                   IEnumerable <string> dependencyIDs  = null,
                                                   bool?resolved                       = null,
                                                   bool?topLevel                       = null,
                                                   bool?isImplicit                     = null,
                                                   ProjectTreeFlags?flags              = null,
                                                   string setPropertiesCaption         = null,
                                                   bool?setPropertiesResolved          = null,
                                                   ProjectTreeFlags?setPropertiesFlags = null,
                                                   bool?setPropertiesImplicit          = null,
                                                   bool?equals                         = null,
                                                   IImmutableList <string> setPropertiesDependencyIDs = null,
                                                   string setPropertiesSchemaName   = null,
                                                   ITargetFramework targetFramework = null,
                                                   MockBehavior?mockBehavior        = null)
        {
            var behavior = mockBehavior ?? MockBehavior.Strict;
            var mock     = new Mock <IDependency>(behavior);

            if (providerType != null)
            {
                mock.Setup(x => x.ProviderType).Returns(providerType);
            }

            if (id != null)
            {
                mock.Setup(x => x.Id).Returns(id);
            }

            if (originalItemSpec != null)
            {
                mock.Setup(x => x.OriginalItemSpec).Returns(originalItemSpec);
            }

            if (fullPath != null)
            {
                mock.Setup(x => x.FullPath).Returns(fullPath);
            }

            if (path != null)
            {
                mock.Setup(x => x.Path).Returns(path);
            }

            if (name != null)
            {
                mock.Setup(x => x.Name).Returns(name);
            }

            if (caption != null)
            {
                mock.Setup(x => x.Caption).Returns(caption);
            }

            if (alias != null)
            {
                mock.Setup(x => x.Alias).Returns(alias);
            }

            if (dependencyIDs != null)
            {
                mock.Setup(x => x.DependencyIDs).Returns(ImmutableList <string> .Empty.AddRange(dependencyIDs));
            }

            if (resolved != null && resolved.HasValue)
            {
                mock.Setup(x => x.Resolved).Returns(resolved.Value);
            }

            if (topLevel != null && topLevel.HasValue)
            {
                mock.Setup(x => x.TopLevel).Returns(topLevel.Value);
            }

            if (isImplicit != null && isImplicit.HasValue)
            {
                mock.Setup(x => x.Implicit).Returns(isImplicit.Value);
            }

            if (flags != null && flags.HasValue)
            {
                mock.Setup(x => x.Flags).Returns(flags.Value);
            }

            if (targetFramework != null)
            {
                mock.Setup(x => x.TargetFramework).Returns(targetFramework);
            }

            if (setPropertiesCaption != null ||
                setPropertiesDependencyIDs != null ||
                setPropertiesResolved != null ||
                setPropertiesFlags != null ||
                setPropertiesImplicit != null)
            {
                mock.Setup(x => x.SetProperties(
                               setPropertiesCaption,
                               setPropertiesResolved,
                               setPropertiesFlags,
                               setPropertiesSchemaName,
                               setPropertiesDependencyIDs,
                               It.IsAny <ImageMoniker>(),
                               It.IsAny <ImageMoniker>(),
                               setPropertiesImplicit))
                .Returns(mock.Object);
            }

            if (equals != null && equals.HasValue && equals.Value)
            {
                mock.Setup(x => x.Equals(It.IsAny <IDependency>())).Returns(true);
            }

            return(mock);
        }
 public MyLib(ITargetFramework targetFramework) =>
示例#21
0
 public ConfiguredProject GetConfiguredProject(ITargetFramework target)
 {
     return(_currentAggregateProjectContext.GetInnerConfiguredProject(target));
 }
        private async Task <IProjectTree> BuildSubTreesAsync(
            IProjectTree rootNode,
            ITargetFramework activeTarget,
            ITargetedDependenciesSnapshot targetedSnapshot,
            Func <IProjectTree, IEnumerable <IProjectTree>, IProjectTree> syncFunc)
        {
            var groupedByProviderType = new Dictionary <string, List <IDependency> >(StringComparers.DependencyProviderTypes);

            foreach (IDependency dependency in targetedSnapshot.TopLevelDependencies)
            {
                if (!dependency.Visible)
                {
                    // If a dependency is not visible we will still register a top-level group if it
                    // has the ShowEmptyProviderRootNode flag.
                    if (!dependency.Flags.Contains(DependencyTreeFlags.ShowEmptyProviderRootNode))
                    {
                        // No such flag, so skip it completely.
                        continue;
                    }
                }

                if (!groupedByProviderType.TryGetValue(dependency.ProviderType, out List <IDependency> dependencies))
                {
                    dependencies = new List <IDependency>();
                    groupedByProviderType.Add(dependency.ProviderType, dependencies);
                }

                // Only add visible dependencies. See note above.
                if (dependency.Visible)
                {
                    dependencies.Add(dependency);
                }
            }

            var currentNodes = new List <IProjectTree>(capacity: groupedByProviderType.Count);

            bool isActiveTarget = targetedSnapshot.TargetFramework.Equals(activeTarget);

            foreach ((string providerType, List <IDependency> dependencies) in groupedByProviderType)
            {
                IDependencyViewModel?subTreeViewModel = _viewModelFactory.CreateRootViewModel(
                    providerType,
                    targetedSnapshot.CheckForUnresolvedDependencies(providerType));

                if (subTreeViewModel == null)
                {
                    // In theory this should never happen, as it means we have a dependency model of a type
                    // that no provider claims. https://github.com/dotnet/project-system/issues/3653
                    continue;
                }

                IProjectTree subTreeNode      = rootNode.FindChildWithCaption(subTreeViewModel.Caption);
                bool         isNewSubTreeNode = subTreeNode == null;

                ProjectTreeFlags excludedFlags = targetedSnapshot.TargetFramework.Equals(TargetFramework.Any)
                    ? ProjectTreeFlags.Create(ProjectTreeFlags.Common.BubbleUp)
                    : ProjectTreeFlags.Empty;

                subTreeNode = CreateOrUpdateNode(
                    subTreeNode,
                    subTreeViewModel,
                    rule: null,
                    isProjectItem: false,
                    excludedFlags: excludedFlags);

                subTreeNode = await BuildSubTreeAsync(
                    subTreeNode,
                    targetedSnapshot,
                    dependencies,
                    isActiveTarget,
                    shouldCleanup : !isNewSubTreeNode);

                currentNodes.Add(subTreeNode);

                rootNode = isNewSubTreeNode
                    ? rootNode.Add(subTreeNode).Parent
                    : subTreeNode.Parent;
            }

            return(syncFunc(rootNode, currentNodes));
        }
        /// <summary>
        /// Updates the <see cref="TargetedDependenciesSnapshot"/> corresponding to <paramref name="changedTargetFramework"/>,
        /// returning either:
        /// <list type="bullet">
        ///   <item>An updated <see cref="DependenciesSnapshot"/> object, or</item>
        ///   <item>the immutable <paramref name="previousSnapshot"/> if no changes were made.</item>
        /// </list>
        /// </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(
            DependenciesSnapshot previousSnapshot,
            ITargetFramework changedTargetFramework,
            IDependenciesChanges?changes,
            IProjectCatalogSnapshot?catalogs,
            ImmutableArray <ITargetFramework> targetFrameworks,
            ITargetFramework?activeTargetFramework,
            ImmutableArray <IDependenciesSnapshotFilter> snapshotFilters,
            IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType,
            IImmutableSet <string>?projectItemSpecs)
        {
            Requires.NotNull(previousSnapshot, nameof(previousSnapshot));
            Requires.NotNull(changedTargetFramework, nameof(changedTargetFramework));
            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(changedTargetFramework, catalogs);
            }

            bool builderChanged = false;

            var newTargetedSnapshot = TargetedDependenciesSnapshot.FromChanges(
                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(
                           activeTargetFramework,
                           builder.ToImmutable()));
            }

            if (!activeTargetFramework.Equals(previousSnapshot.ActiveTargetFramework))
            {
                // The active target framework changed
                return(new DependenciesSnapshot(
                           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(targetFramework, catalogs));
                        builderChanged = true;
                    }
                }

                // Remove any extra target frameworks
                if (builder.Count != targetFrameworks.Length)
                {
                    // NOTE We need "ToList" here as "Except" is lazy, and attempts to remove from the builder
                    // while iterating will throw "Collection was modified"
                    IEnumerable <ITargetFramework> targetFrameworksToRemove = builder.Keys.Except(targetFrameworks).ToList();

                    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(
            TargetedDependenciesSnapshot previousSnapshot,
            IDependenciesChanges?changes,
            IProjectCatalogSnapshot?catalogs,
            ImmutableArray <IDependenciesSnapshotFilter> snapshotFilters,
            IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType,
            IImmutableSet <string>?projectItemSpecs)
        {
            Requires.NotNull(previousSnapshot, nameof(previousSnapshot));
            Requires.Argument(!snapshotFilters.IsDefault, nameof(snapshotFilters), "Cannot be default.");
            Requires.NotNull(subTreeProviderByProviderType, nameof(subTreeProviderByProviderType));

            bool anyChanges = false;

            ITargetFramework targetFramework = previousSnapshot.TargetFramework;

            var dependencyById = previousSnapshot.Dependencies.ToDictionary(d => d.Id, StringComparers.DependencyTreeIds);

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

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

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

                foreach (IDependencyModel added in changes.AddedNodes)
                {
#pragma warning disable CS0618 // Type or member is obsolete
                    // NOTE we still need to check this in case extensions (eg. WebTools) provide us with top level items that need to be filtered out
                    if (!added.TopLevel)
                    {
                        continue;
                    }
#pragma warning restore CS0618 // Type or member is obsolete

                    Add(context, added);
                }
            }

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

            if (anyChanges)
            {
                return(new TargetedDependenciesSnapshot(
                           targetFramework,
                           catalogs,
                           dependencyById.ToImmutableValueArray()));
            }

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

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

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

                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
                    dependencyById.Remove(dependency.Id);
                    dependencyById.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;
                }
            }
        }
        public virtual void Handle(
            IImmutableDictionary <string, IProjectChangeDescription> changesByRuleName,
            ITargetFramework targetFramework,
            CrossTargetDependenciesChangesBuilder changesBuilder)
        {
            // We receive unresolved and resolved changes separately.

            // Process all unresolved changes.
            if (changesByRuleName.TryGetValue(UnresolvedRuleName, out IProjectChangeDescription unresolvedChanges))
            {
                HandleChangesForRule(
                    resolved: false,
                    projectChange: unresolvedChanges,
                    shouldProcess: dependencyId => true);
            }

            // Process only resolved changes that have a corresponding unresolved item.
            if (unresolvedChanges != null &&
                changesByRuleName.TryGetValue(ResolvedRuleName, out IProjectChangeDescription resolvedChanges))
            {
                HandleChangesForRule(
                    resolved: true,
                    projectChange: resolvedChanges,
                    shouldProcess: unresolvedChanges.After.Items.ContainsKey);
            }

            return;

            void HandleChangesForRule(bool resolved, IProjectChangeDescription projectChange, Func <string, bool> shouldProcess)
            {
                foreach (string removedItem in projectChange.Difference.RemovedItems)
                {
                    string dependencyId = resolved
                        ? projectChange.Before.GetProjectItemProperties(removedItem).GetStringProperty(ResolvedAssemblyReference.OriginalItemSpecProperty) ?? removedItem
                        : removedItem;

                    if (shouldProcess(dependencyId))
                    {
                        changesBuilder.Removed(targetFramework, ProviderType, removedItem);
                    }
                }

                foreach (string changedItem in projectChange.Difference.ChangedItems)
                {
                    IDependencyModel model = CreateDependencyModelForRule(changedItem, projectChange.After);
                    if (shouldProcess(model.Id))
                    {
                        // For changes we try to add new dependency. If it is a resolved dependency, it would just override
                        // old one with new properties. If it is unresolved dependency, it would be added only when there no
                        // resolved version in the snapshot.
                        changesBuilder.Added(targetFramework, model);
                    }
                }

                foreach (string addedItem in projectChange.Difference.AddedItems)
                {
                    IDependencyModel model = CreateDependencyModelForRule(addedItem, projectChange.After);
                    if (shouldProcess(model.Id))
                    {
                        changesBuilder.Added(targetFramework, model);
                    }
                }

                return;

                IDependencyModel CreateDependencyModelForRule(string itemSpec, IProjectRuleSnapshot projectRuleSnapshot)
                {
                    IImmutableDictionary <string, string> properties = projectRuleSnapshot.GetProjectItemProperties(itemSpec);

                    string originalItemSpec = resolved
                        ? properties.GetStringProperty(ResolvedAssemblyReference.OriginalItemSpecProperty)
                        : itemSpec;

                    bool isImplicit = properties.GetBoolProperty(ProjectItemMetadata.IsImplicitlyDefined) ?? false;

                    return(CreateDependencyModel(
                               itemSpec,
                               originalItemSpec,
                               resolved,
                               isImplicit,
                               properties));
                }
            }
        }
            static (ImmutableArray <IDependency> TopLevelDependencies, Dictionary <string, IDependency> topLevelDependencyByPath, bool hasVisibleUnresolvedDependency) Scan(ImmutableDictionary <string, IDependency> dependenciesWorld, ITargetFramework targetFramework)
            {
                // TODO use ToImmutableAndFree?
                ImmutableArray <IDependency> .Builder topLevelDependencies = ImmutableArray.CreateBuilder <IDependency>();

                bool hasVisibleUnresolvedDependency = false;
                var  topLevelDependencyByPath       = new Dictionary <string, IDependency>(StringComparer.OrdinalIgnoreCase);

                foreach ((string id, IDependency dependency) in dependenciesWorld)
                {
                    System.Diagnostics.Debug.Assert(
                        string.Equals(id, dependency.Id),
                        "dependenciesWorld dictionary entry keys must match their value's ids.");

                    if (!dependency.Resolved && dependency.Visible)
                    {
                        hasVisibleUnresolvedDependency = true;
                    }

                    if (dependency.TopLevel)
                    {
                        topLevelDependencies.Add(dependency);

                        if (!string.IsNullOrEmpty(dependency.Path))
                        {
                            topLevelDependencyByPath.Add(
                                Dependency.GetID(targetFramework, dependency.ProviderType, dependency.Path),
                                dependency);
                        }
                    }
                }

                return(topLevelDependencies.ToImmutable(), topLevelDependencyByPath, hasVisibleUnresolvedDependency);
            }
        private bool TryCreatePackageDependencyModel(
            string itemSpec,
            bool isResolved,
            IImmutableDictionary <string, string> properties,
            Func <string, bool>?isEvaluatedItemSpec,
            ITargetFramework targetFramework,
            [NotNullWhen(returnValue: true)] out PackageDependencyModel?dependencyModel)
        {
            Requires.NotNullOrEmpty(itemSpec, nameof(itemSpec));
            Requires.NotNull(properties, nameof(properties));

            bool isImplicitlyDefined = properties.GetBoolProperty(ProjectItemMetadata.IsImplicitlyDefined) ?? false;

            if (isResolved)
            {
                // We have design-time build data

                Requires.NotNull(targetFramework, nameof(targetFramework));
                Requires.NotNull(isEvaluatedItemSpec !, nameof(isEvaluatedItemSpec));

                string?name = properties.GetStringProperty(ProjectItemMetadata.Name);

                string?dependencyType = properties.GetStringProperty(ProjectItemMetadata.Type);

                if (dependencyType != null)
                {
                    // LEGACY MODE
                    //
                    // In 16.7 (SDK 3.1.4xx) the format of ResolvedPackageReference items was changed in task PreprocessPackageDependenciesDesignTime.
                    //
                    // If we observe "Type" metadata then we are running with an older SDK and need to preserve some
                    // legacy behaviour to avoid breaking the dependencies node too much. Transitive dependencies will
                    // not be displayed, but we should be able to provide an equivalent experience for top-level items.

                    if (!StringComparers.PropertyLiteralValues.Equals(dependencyType, "Package"))
                    {
                        // Legacy behaviour included items of various types. We now only accept "Package".
                        dependencyModel = null;
                        return(false);
                    }

                    // Legacy behaviour was to return packages for all targets, even though we have a build per-target.
                    // The package's target was prefixed to its ItemSpec (for example: ".NETFramework,Version=v4.8/MetadataExtractor/2.3.0").
                    // We would then filter out items for the wrong target here.
                    //
                    // From 16.7 we no longer return items from other target frameworks during DTB, and we remove the target prefix from ItemSpec.
                    //
                    // This code preserves filtering logic when processing legacy items.
                    int slashIndex = itemSpec.IndexOf('/');
                    if (slashIndex != -1)
                    {
                        string targetFrameworkName = s_targetFrameworkInternPool.Intern(itemSpec.Substring(0, slashIndex));

                        if (_targetFrameworkProvider.GetTargetFramework(targetFrameworkName)?.Equals(targetFramework) != true)
                        {
                            // Item is not for the correct target
                            dependencyModel = null;
                            return(false);
                        }
                    }

                    // Name metadata is required in 16.7. Legacy behaviour uses ItemSpec as a fallback.
                    name ??= itemSpec;
                }
                else
                {
                    if (Strings.IsNullOrEmpty(name))
                    {
                        // This should not happen as Name is required in PreprocessPackageDependenciesDesignTime from 16.7
                        dependencyModel = null;
                        return(false);
                    }
                }

                bool isTopLevel = isImplicitlyDefined || isEvaluatedItemSpec(name);

                if (!isTopLevel)
                {
                    // We no longer accept non-top-level dependencies from DTB data. See note above about legacy mode support.
                    dependencyModel = null;
                    return(false);
                }

                dependencyModel = new PackageDependencyModel(
                    itemSpec,
                    originalItemSpec: name,
                    version: properties.GetStringProperty(ProjectItemMetadata.Version) ?? string.Empty,
                    isResolved: true,
                    isImplicitlyDefined,
                    isVisible: !isImplicitlyDefined,
                    properties);
            }
            else
            {
                // We only have evaluation data

                System.Diagnostics.Debug.Assert(itemSpec.IndexOf('/') == -1);

                dependencyModel = new PackageDependencyModel(
                    itemSpec,
                    originalItemSpec: itemSpec,
                    version: properties.GetStringProperty(ProjectItemMetadata.Version) ?? string.Empty,
                    isResolved: false,
                    isImplicitlyDefined,
                    isVisible: !isImplicitlyDefined,
                    properties);
            }

            return(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;
                }
            }
        }
 public void Removed(ITargetFramework targetFramework, string providerType, string dependencyId)
 {
     GetChanges(targetFramework).Removed(providerType, dependencyId);
 }
示例#30
0
 public ConfiguredProject GetInnerConfiguredProject(ITargetFramework target)
 {
     return(_configuredProjectsByTargetFramework.FirstOrDefault(x => target.Equals(x.Key)).Value);
 }