示例#1
0
        private async Task HandleAsync(Tuple <IProjectSubscriptionUpdate, IProjectSharedFoldersSnapshot, IProjectCatalogSnapshot> e)
        {
            AggregateCrossTargetProjectContext currentAggregateContext = await _host.GetCurrentAggregateProjectContextAsync();

            if (currentAggregateContext == null)
            {
                return;
            }

            IProjectSubscriptionUpdate    projectUpdate        = e.Item1;
            IProjectSharedFoldersSnapshot sharedProjectsUpdate = e.Item2;
            IProjectCatalogSnapshot       catalogs             = e.Item3;

            // Get the target framework to update for this change.
            ITargetFramework targetFrameworkToUpdate = currentAggregateContext.GetProjectFramework(projectUpdate.ProjectConfiguration);

            if (targetFrameworkToUpdate == null)
            {
                return;
            }

            var changesBuilder = new CrossTargetDependenciesChangesBuilder();

            ProcessSharedProjectsUpdates(sharedProjectsUpdate, targetFrameworkToUpdate, changesBuilder);

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

            if (changes != null)
            {
                DependenciesChanged?.Invoke(
                    this,
                    new DependencySubscriptionChangedEventArgs(
                        currentAggregateContext.ActiveTargetFramework,
                        catalogs,
                        changes));
            }
        }
示例#2
0
        private HostDocument[] GetCurrentDocuments(IProjectSubscriptionUpdate update)
        {
            var documents = new List <HostDocument>();

            if (update.CurrentState.TryGetValue(Rules.RazorComponentWithTargetPath.SchemaName, out var rule))
            {
                foreach (var kvp in rule.Items)
                {
                    if (kvp.Value.TryGetValue(Rules.RazorComponentWithTargetPath.TargetPathProperty, out var targetPath) &&
                        !string.IsNullOrWhiteSpace(kvp.Key) &&
                        !string.IsNullOrWhiteSpace(targetPath))
                    {
                        var filePath = CommonServices.UnconfiguredProject.MakeRooted(kvp.Key);
                        var fileKind = FileKinds.GetComponentFileKindFromFilePath(filePath);

                        documents.Add(new HostDocument(filePath, targetPath, fileKind));
                    }
                }
            }

            if (update.CurrentState.TryGetValue(Rules.RazorGenerateWithTargetPath.SchemaName, out rule))
            {
                foreach (var kvp in rule.Items)
                {
                    if (kvp.Value.TryGetValue(Rules.RazorGenerateWithTargetPath.TargetPathProperty, out var targetPath) &&
                        !string.IsNullOrWhiteSpace(kvp.Key) &&
                        !string.IsNullOrWhiteSpace(targetPath))
                    {
                        var filePath = CommonServices.UnconfiguredProject.MakeRooted(kvp.Key);
                        documents.Add(new HostDocument(filePath, targetPath, FileKinds.Legacy));
                    }
                }
            }

            return(documents.ToArray());
        }
        protected virtual DependenciesChange ProcessDependenciesChanges(
            IProjectSubscriptionUpdate projectSubscriptionUpdate,
            IProjectCatalogSnapshot catalogs)
        {
            var changes = projectSubscriptionUpdate.ProjectChanges;
            var resolvedReferenceChanges =
                ResolvedReferenceRuleNames.Where(x => changes.Keys.Contains(x))
                .Select(ruleName => changes[ruleName]).ToImmutableHashSet();

            var unresolvedReferenceSnapshots = changes.Values
                                               .Where(cd => !ResolvedReferenceRuleNames.Any(ruleName =>
                                                                                            string.Equals(ruleName,
                                                                                                          cd.After.RuleName,
                                                                                                          StringComparison.OrdinalIgnoreCase)))
                                               .ToDictionary(d => d.After.RuleName, d => d, StringComparer.OrdinalIgnoreCase);

            var rootTreeNodes      = new HashSet <IDependencyNode>(RootNode.Children);
            var dependenciesChange = new DependenciesChange();

            foreach (var unresolvedChange in unresolvedReferenceSnapshots.Values)
            {
                if (!unresolvedChange.Difference.AnyChanges)
                {
                    continue;
                }

                var itemType = GetItemTypeFromRuleName(unresolvedChange.After.RuleName,
                                                       catalogs,
                                                       true);
                if (itemType == null)
                {
                    // We must be missing that rule. Skip it.
                    continue;
                }

                foreach (string removedItemSpec in unresolvedChange.Difference.RemovedItems)
                {
                    var node = rootTreeNodes.FindNode(removedItemSpec, itemType);
                    if (node != null)
                    {
                        dependenciesChange.RemovedNodes.Add(node);
                    }
                }

                foreach (string addedItemSpec in unresolvedChange.Difference.AddedItems)
                {
                    var node = rootTreeNodes.FindNode(addedItemSpec, itemType);
                    if (node == null)
                    {
                        var properties = GetProjectItemProperties(unresolvedChange.After, addedItemSpec);
                        node = CreateDependencyNode(addedItemSpec,
                                                    itemType,
                                                    properties: properties,
                                                    resolved: false);
                        dependenciesChange.AddedNodes.Add(node);
                    }
                }
            }

            var updatedUnresolvedSnapshots = unresolvedReferenceSnapshots.Values.Select(cd => cd.After);

            foreach (var resolvedReferenceRuleChanges in resolvedReferenceChanges)
            {
                if (!resolvedReferenceRuleChanges.Difference.AnyChanges)
                {
                    continue;
                }

                // if resolved reference appears in Removed list, it means that it is either removed from
                // project or can not be resolved anymore. In case when it can not be resolved,
                // we must remove old "resolved" node and add new unresolved node with corresponding
                // properties changes (rules, icon, etc)
                // Note: removed resolved node is not added to "added unresolved diff", which we process
                // above, thus we need to do this properties update here. It is just cleaner to re-add node
                // instead of modifying properties.
                foreach (string removedItemSpec in resolvedReferenceRuleChanges.Difference.RemovedItems)
                {
                    string unresolvedItemSpec = resolvedReferenceRuleChanges.Before
                                                .Items[removedItemSpec][OriginalItemSpecPropertyName];
                    IProjectRuleSnapshot unresolvedReferenceSnapshot = null;
                    string unresolvedItemType = GetUnresolvedReferenceItemType(unresolvedItemSpec,
                                                                               updatedUnresolvedSnapshots,
                                                                               catalogs,
                                                                               out unresolvedReferenceSnapshot);
                    var node = rootTreeNodes.FindNode(removedItemSpec, unresolvedItemType);
                    if (node != null)
                    {
                        dependenciesChange.RemovedNodes.Add(node);

                        IImmutableDictionary <string, string> properties = null;
                        if (unresolvedReferenceSnapshot != null)
                        {
                            properties = GetProjectItemProperties(unresolvedReferenceSnapshot, unresolvedItemSpec);
                        }

                        node = CreateDependencyNode(unresolvedItemSpec,
                                                    unresolvedItemType,
                                                    properties: properties,
                                                    resolved: false);
                        dependenciesChange.AddedNodes.Add(node);
                    }
                }

                foreach (string addedItemSpec in resolvedReferenceRuleChanges.Difference.AddedItems)
                {
                    var properties = GetProjectItemProperties(resolvedReferenceRuleChanges.After, addedItemSpec);
                    if (properties == null || !properties.Keys.Contains(OriginalItemSpecPropertyName))
                    {
                        // if there no OriginalItemSpec, we can not associate item with the rule
                        continue;
                    }

                    var originalItemSpec = properties[OriginalItemSpecPropertyName];
                    IProjectRuleSnapshot unresolvedReferenceSnapshot = null;
                    var itemType = GetUnresolvedReferenceItemType(originalItemSpec,
                                                                  updatedUnresolvedSnapshots,
                                                                  catalogs,
                                                                  out unresolvedReferenceSnapshot);
                    if (string.IsNullOrEmpty(itemType))
                    {
                        // Note: design time build resolves not only our unresolved assemblies, but also
                        // all transitive assembly dependencies, which ar enot direct references and
                        // we should not show them. If reference does not have an unresolved reference
                        // corresponded to it, i.e. itemType = null here - we skip it.
                        continue;
                    }

                    // avoid adding unresolved dependency along with resolved one
                    var existingUnresolvedNode = dependenciesChange.AddedNodes.FindNode(originalItemSpec, itemType);
                    if (existingUnresolvedNode != null)
                    {
                        dependenciesChange.AddedNodes.Remove(existingUnresolvedNode);
                    }

                    // if unresolved dependency was added earlier, remove it, since it will be substituted by resolved one
                    existingUnresolvedNode = rootTreeNodes.FindNode(originalItemSpec, itemType);
                    if (existingUnresolvedNode != null)
                    {
                        dependenciesChange.RemovedNodes.Add(existingUnresolvedNode);
                    }

                    var newNode = CreateDependencyNode(originalItemSpec,
                                                       itemType: itemType,
                                                       properties: properties);
                    dependenciesChange.AddedNodes.Add(newNode);
                }
            }

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

            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);
        }
示例#5
0
 public DependenciesChange TestProcessDependenciesChanges(
     IProjectSubscriptionUpdate projectSubscriptionUpdate,
     IProjectCatalogSnapshot catalogs)
 {
     return(ProcessDependenciesChanges(projectSubscriptionUpdate, catalogs));
 }
示例#6
0
 protected abstract EnumCollection Transform(IProjectSubscriptionUpdate input);
示例#7
0
            public async Task <DesignTimeInputsItem> TestProcessAsync(IProjectSubscriptionUpdate compileUpdate, IProjectSubscriptionUpdate configurationGeneralUpdate, DesignTimeInputsDelta previousOutput)
            {
                var input = IProjectVersionedValueFactory.Create(Tuple.Create(compileUpdate, configurationGeneralUpdate));

                // We always pretend this isn't the first process, which occurs on project load, because we have SetInputs for that
                var result = await base.PreprocessAsync(input, previousOutput);

                await base.ApplyAsync(result);

                return(AppliedValue.Value);
            }
示例#8
0
 public Task <DesignTimeInputsItem> TestProcessAsync(IProjectSubscriptionUpdate compileUpdate, IProjectSubscriptionUpdate configurationGeneralUpdate)
 {
     return(TestProcessAsync(compileUpdate, configurationGeneralUpdate, new DesignTimeInputsDelta()));
 }
示例#9
0
        private async Task HandleAsync(
            IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > e,
            RuleHandlerType handlerType)
        {
            var currentAggregateContext = await _host.GetCurrentAggregateProjectContext().ConfigureAwait(false);

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

            IProjectSubscriptionUpdate update   = e.Value.Item1;
            IProjectCatalogSnapshot    catalogs = e.Value.Item2;
            var handlers = Handlers.Select(h => h.Value)
                           .Where(h => h.SupportsHandlerType(handlerType));

            // We need to process the update within a lock to ensure that we do not release this context during processing.
            // TODO: Enable concurrent execution of updates themeselves, i.e. two separate invocations of HandleAsync
            //       should be able to run concurrently.
            using (await _gate.DisposableWaitAsync().ConfigureAwait(true))
            {
                // Get the inner workspace project context to update for this change.
                var projectContextToUpdate = currentAggregateContext
                                             .GetInnerProjectContext(update.ProjectConfiguration, out bool isActiveContext);
                if (projectContextToUpdate == 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 (update.ProjectChanges.Count == 0)
                {
                    if (handlerType == RuleHandlerType.DesignTimeBuild)
                    {
                        projectContextToUpdate.LastDesignTimeBuildSucceeded = false;
                    }

                    return;
                }

                var ruleChangeContext = CreateRuleChangeContext(
                    currentAggregateContext.ActiveProjectContext.TargetFramework, catalogs);
                foreach (var handler in handlers)
                {
                    var builder      = ImmutableDictionary.CreateBuilder <string, IProjectChangeDescription>(StringComparers.RuleNames);
                    var handlerRules = handler.GetRuleNames(handlerType);
                    builder.AddRange(update.ProjectChanges.Where(
                                         x => handlerRules.Contains(x.Key)));
                    var projectChanges = builder.ToImmutable();

                    if (handler.ReceiveUpdatesWithEmptyProjectChange ||
                        projectChanges.Any(x => x.Value.Difference.AnyChanges))
                    {
                        await handler.HandleAsync(e,
                                                  projectChanges,
                                                  projectContextToUpdate,
                                                  isActiveContext,
                                                  ruleChangeContext)
                        .ConfigureAwait(true);
                    }
                }

                await CompleteHandleAsync(ruleChangeContext).ConfigureAwait(false);

                // record all the rules that have occurred
                _treeTelemetryService.ObserveTargetFrameworkRules(projectContextToUpdate.TargetFramework, update.ProjectChanges.Keys);
            }
        }
示例#10
0
        protected override DependenciesChange ProcessDependenciesChanges(
            IProjectSubscriptionUpdate projectSubscriptionUpdate,
            IProjectCatalogSnapshot catalogs)
        {
            var changes            = projectSubscriptionUpdate.ProjectChanges;
            var dependenciesChange = new DependenciesChange();

            lock (_snapshotLock)
            {
                var newDependencies = new HashSet <DependencyMetadata>();
                foreach (var change in changes.Values)
                {
                    if (!change.Difference.AnyChanges)
                    {
                        continue;
                    }

                    foreach (string removedItemSpec in change.Difference.RemovedItems)
                    {
                        CurrentSnapshot.RemoveDependency(removedItemSpec);

                        var itemNode = RootNode.Children.FirstOrDefault(
                            x => x.Id.ItemSpec.Equals(removedItemSpec, StringComparison.OrdinalIgnoreCase));
                        if (itemNode != null)
                        {
                            dependenciesChange.RemovedNodes.Add(itemNode);
                        }
                    }

                    foreach (string changedItemSpec in change.Difference.ChangedItems)
                    {
                        var properties = GetProjectItemProperties(change.After, changedItemSpec);
                        if (properties == null)
                        {
                            continue;
                        }

                        CurrentSnapshot.UpdateDependency(changedItemSpec, properties);

                        var itemNode = RootNode.Children.FirstOrDefault(
                            x => x.Id.ItemSpec.Equals(changedItemSpec, StringComparison.OrdinalIgnoreCase));
                        if (itemNode != null)
                        {
                            dependenciesChange.UpdatedNodes.Add(itemNode);
                        }
                    }

                    foreach (string addedItemSpec in change.Difference.AddedItems)
                    {
                        var properties = GetProjectItemProperties(change.After, addedItemSpec);
                        if (properties == null)
                        {
                            continue;
                        }

                        var newDependency = CurrentSnapshot.AddDependency(addedItemSpec, properties);

                        newDependencies.Add(newDependency);
                    }
                }

                // since we have limited implementation for multi targeted projects,
                // we assume that there is only one target - take first target and add
                // top level nodes for it

                var currentTarget = CurrentSnapshot.Targets.Keys.FirstOrDefault();
                if (currentTarget == null)
                {
                    return(dependenciesChange);
                }

                var currentTargetDependency           = CurrentSnapshot.DependenciesWorld[currentTarget];
                var currentTargetTopLevelDependencies = currentTargetDependency.DependenciesItemSpecs;
                var addedTopLevelDependencies         = newDependencies.Where(
                    x => currentTargetTopLevelDependencies.Contains(x.ItemSpec));
                foreach (var addedDependency in addedTopLevelDependencies)
                {
                    var itemNode = RootNode.Children.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(addedDependency.ItemSpec,
                                                  StringComparison.OrdinalIgnoreCase));
                    if (itemNode == null)
                    {
                        itemNode = CreateDependencyNode(addedDependency, topLevel: true);
                        dependenciesChange.AddedNodes.Add(itemNode);
                    }
                }
            }

            return(dependenciesChange);
        }
示例#11
0
        private NugetDependenciesChange ProcessResolvedChanges(IProjectSubscriptionUpdate projectSubscriptionUpdate,
                                                               HashSet <IDependencyNode> rootTreeNodes)
        {
            var changes         = projectSubscriptionUpdate.ProjectChanges;
            var resolvedChanges = ResolvedReferenceRuleNames.Where(x => changes.Keys.Contains(x))
                                  .Select(ruleName => changes[ruleName])
                                  .ToImmutableHashSet();
            var dependenciesChange = new NugetDependenciesChange();
            var newDependencies    = new HashSet <DependencyMetadata>();

            foreach (var change in resolvedChanges)
            {
                if (!change.Difference.AnyChanges)
                {
                    continue;
                }

                foreach (string removedItemSpec in change.Difference.RemovedItems)
                {
                    var metadata = CurrentSnapshot.RemoveDependency(removedItemSpec);
                    var itemNode = rootTreeNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(removedItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        dependenciesChange.RemovedNodes.Add(metadata);
                    }
                }

                foreach (string changedItemSpec in change.Difference.ChangedItems)
                {
                    var properties = GetProjectItemProperties(change.After, changedItemSpec);
                    if (properties == null)
                    {
                        continue;
                    }

                    var metadata = CurrentSnapshot.UpdateDependency(changedItemSpec, properties);
                    var itemNode = rootTreeNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(changedItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        dependenciesChange.UpdatedNodes.Add(metadata);
                    }
                }

                foreach (string addedItemSpec in change.Difference.AddedItems)
                {
                    var properties = GetProjectItemProperties(change.After, addedItemSpec);
                    if (properties == null)
                    {
                        continue;
                    }

                    var newDependency = CurrentSnapshot.AddDependency(addedItemSpec, properties);
                    newDependencies.Add(newDependency);
                }
            }

            // Note: currently deisgn time build is limited and is not aware of conditional on TFM
            // PackageReference items: Unresolved PackageReference items for conditional TFMs are not sent.
            // Thus we will display conditional PackageReferences if they were resolved and are in assets.json.
            // This limitation should go away, when we have final design for cross target dependencies and
            // DesignTime build.
            var allTargetsDependencies = CurrentSnapshot.GetUniqueTopLevelDependencies();

            if (allTargetsDependencies.Count == 0)
            {
                return(dependenciesChange);
            }

            var addedTopLevelDependencies = newDependencies.Where(
                x => allTargetsDependencies.Contains(x.ItemSpec));

            foreach (var addedDependency in addedTopLevelDependencies)
            {
                dependenciesChange.AddedNodes.Add(addedDependency);
            }

            return(dependenciesChange);
        }
示例#12
0
        private NugetDependenciesChange ProcessUnresolvedChanges(IProjectSubscriptionUpdate projectSubscriptionUpdate)
        {
            var unresolvedChanges = projectSubscriptionUpdate.ProjectChanges.Values
                                    .Where(cd => !ResolvedReferenceRuleNames.Any(ruleName =>
                                                                                 string.Equals(ruleName,
                                                                                               cd.After.RuleName,
                                                                                               StringComparison.OrdinalIgnoreCase)))
                                    .ToDictionary(d => d.After.RuleName, d => d, StringComparer.OrdinalIgnoreCase);

            var dependenciesChange = new NugetDependenciesChange();

            foreach (var change in unresolvedChanges.Values)
            {
                if (!change.Difference.AnyChanges)
                {
                    continue;
                }

                foreach (string removedItemSpec in change.Difference.RemovedItems)
                {
                    if (TopLevelDependencies.TryGetValue(removedItemSpec, out DependencyMetadata metadata))
                    {
                        TopLevelDependencies.Remove(removedItemSpec);
                        dependenciesChange.RemovedNodes.Add(metadata);
                    }
                }

                foreach (string changedItemSpec in change.Difference.ChangedItems)
                {
                    var properties = GetProjectItemProperties(change.After, changedItemSpec);
                    if (properties == null)
                    {
                        continue;
                    }

                    if (TopLevelDependencies.TryGetValue(changedItemSpec, out DependencyMetadata metadata))
                    {
                        metadata = CreateUnresolvedMetadata(changedItemSpec, properties);
                        TopLevelDependencies[changedItemSpec] = metadata;

                        dependenciesChange.UpdatedNodes.Add(metadata);
                    }
                }

                foreach (string addedItemSpec in change.Difference.AddedItems)
                {
                    var properties = GetProjectItemProperties(change.After, addedItemSpec);
                    if (properties == null)
                    {
                        continue;
                    }

                    if (!TopLevelDependencies.TryGetValue(addedItemSpec, out DependencyMetadata metadata))
                    {
                        metadata = CreateUnresolvedMetadata(addedItemSpec, properties);
                        TopLevelDependencies.Add(addedItemSpec, metadata);

                        dependenciesChange.AddedNodes.Add(metadata);
                    }
                }
            }

            return(dependenciesChange);
        }
示例#13
0
        protected override DependenciesChange ProcessDependenciesChanges(
            IProjectSubscriptionUpdate projectSubscriptionUpdate,
            IProjectCatalogSnapshot catalogs)
        {
            var dependenciesChange = new DependenciesChange();
            // take a snapshot for current top level tree nodes
            var rootNodes = new HashSet <IDependencyNode>(RootNode.Children);

            lock (_snapshotLock)
            {
                var unresolvedChanges = ProcessUnresolvedChanges(projectSubscriptionUpdate);
                var resolvedChanges   = ProcessResolvedChanges(projectSubscriptionUpdate, rootNodes);

                // Logic below merges unresolved and resolved pending changes. Resolved should win if there
                // is similar unresolved dependency. Thus
                //  - for pending removals, we don't care about the order and just remove whatever valid
                //    pending changes are there (valid=existing in the RootNode top level children)
                //  - for pending updates, since ItemSpecs must exist (otherwise it would not be an Update pending
                //    change, but an Add or Remove), we first try to merge resolved pending changes, where for
                //    each change we try to remove similar unresolved pending Update request.
                //  - for pending additions we need to match itemSpecs of the resolved dependencies to names of the
                //    unresolved, since resolved ItemSpec is "tfm/packagename/version", but unresolved is just
                //    "packagename". Thus to avoid same tree node name collision we need to be smart when detecting
                //    similar resolved vs unresolved pending changes.
                //    The algorithm is, first merge resolved changes and if there is a
                //          - matching unresolved item in the RootNode already, submit a removal
                //          - matching unresolved item in pending unresolved additions - remove it form there too
                //    Then, process remaining unresolved changes and before mergin each of them, check if matching
                //    name already exists in RootNode or already merged additions.
                //  Note: if it would became too complicated with time, create a separate PackageDependencyChangesResolver
                //  class that would hide it and make logic here simpler.

                // remove
                foreach (var metadata in unresolvedChanges.RemovedNodes)
                {
                    var itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(metadata.ItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        dependenciesChange.RemovedNodes.Add(itemNode);
                    }
                }

                foreach (var metadata in resolvedChanges.RemovedNodes)
                {
                    var itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(metadata.ItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        dependenciesChange.RemovedNodes.Add(itemNode);
                    }
                }

                // update
                foreach (var resolvedMetadata in resolvedChanges.UpdatedNodes)
                {
                    // since it is an update root node must have those item specs, so we can check them
                    var itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(resolvedMetadata.ItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        itemNode = CreateDependencyNode(resolvedMetadata, topLevel: true);
                        dependenciesChange.UpdatedNodes.Add(itemNode);
                    }

                    var unresolvedMatch = unresolvedChanges.UpdatedNodes.FirstOrDefault(x => x.ItemSpec.Equals(resolvedMetadata.Name));
                    if (unresolvedMatch != null)
                    {
                        unresolvedChanges.UpdatedNodes.Remove(unresolvedMatch);
                    }
                }

                foreach (var unresolvedMetadata in unresolvedChanges.UpdatedNodes)
                {
                    // since it is an update root node must have those item specs, so we can check them
                    var itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(unresolvedMetadata.ItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        dependenciesChange.UpdatedNodes.Add(itemNode);
                    }
                }

                // add
                foreach (var resolvedMetadata in resolvedChanges.AddedNodes)
                {
                    // see if there is already node created for unresolved package - if yes, delete it
                    // Note: unresolved packages ItemSpec contain only package name, when resolved package ItemSpec
                    // contains TFM/PackageName/version, so we need to check for name if we want to find unresolved
                    // packages.
                    var itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(resolvedMetadata.Name, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        dependenciesChange.RemovedNodes.Add(itemNode);
                    }

                    // see if there no node with the same resolved metadata - if no, create it
                    itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(resolvedMetadata.ItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode == null)
                    {
                        itemNode = CreateDependencyNode(resolvedMetadata, topLevel: true);
                        dependenciesChange.AddedNodes.Add(itemNode);
                    }

                    // avoid adding matching unresolved packages
                    var unresolvedMatch = unresolvedChanges.AddedNodes.FirstOrDefault(x => x.ItemSpec.Equals(resolvedMetadata.Name));
                    if (unresolvedMatch != null)
                    {
                        unresolvedChanges.AddedNodes.Remove(unresolvedMatch);
                    }
                }

                foreach (var unresolvedMetadata in unresolvedChanges.AddedNodes)
                {
                    var itemNode = rootNodes.FirstOrDefault(x => DoesNodeMatchByNameOrItemSpec(x, unresolvedMetadata.ItemSpec));
                    if (itemNode == null)
                    {
                        // in case when unresolved come together with resolved data, root nodes might not yet have
                        // an unresolved node and we need to check if we did add resolved one above to avoid collision.
                        itemNode = dependenciesChange.AddedNodes.FirstOrDefault(
                            x => DoesNodeMatchByNameOrItemSpec(x, unresolvedMetadata.ItemSpec));
                    }

                    if (itemNode == null)
                    {
                        itemNode = CreateDependencyNode(unresolvedMetadata, topLevel: true);
                        dependenciesChange.AddedNodes.Add(itemNode);
                    }
                }
            }

            return(dependenciesChange);
        }
        public async Task SearchAsync(IDependenciesTreeProjectSearchContext context)
        {
            // get latest snapshot
            ExportProvider exportProvider = context.UnconfiguredProject.Services.ExportProvider;

            Lazy <IAssetsFileDependenciesDataSource, IAppliesToMetadataView> dataSource
                = exportProvider
                  .GetExports <IAssetsFileDependenciesDataSource, IAppliesToMetadataView>()
                  .SingleOrDefault(export => export.Metadata.AppliesTo(context.UnconfiguredProject.Capabilities));

            if (dataSource == null)
            {
                // dataSource will be null for shared projects, for example
                return;
            }

            IProjectDataSourceRegistry?dataSourceRegistry = context.UnconfiguredProject.Services.DataSourceRegistry;

            Assumes.Present(dataSourceRegistry);

            AssetsFileDependenciesSnapshot snapshot = (await dataSource.Value.GetLatestVersionAsync <AssetsFileDependenciesSnapshot>(dataSourceRegistry, cancellationToken: context.CancellationToken)).Value;

            if (!(context.UnconfiguredProject.Services.ExportProvider.GetExportedValue <IActiveConfigurationGroupService>() is IActiveConfigurationGroupService3 activeConfigurationGroupService))
            {
                return;
            }

            IConfigurationGroup <ConfiguredProject> configuredProjects = await activeConfigurationGroupService.GetActiveLoadedConfiguredProjectGroupAsync();

            foreach ((_, AssetsFileTarget target) in snapshot.DataByTarget)
            {
                ConfiguredProject?configuredProject = await FindConfiguredProjectAsync(target.TargetFrameworkMoniker);

                if (configuredProject == null)
                {
                    continue;
                }

                IDependenciesTreeConfiguredProjectSearchContext?targetContext = await context.ForConfiguredProjectAsync(configuredProject);

                if (targetContext == null)
                {
                    continue;
                }

                foreach ((_, AssetsFileTargetLibrary library) in target.LibraryByName)
                {
                    if (context.CancellationToken.IsCancellationRequested)
                    {
                        // Search was cancelled
                        return;
                    }

                    if (targetContext.IsMatch(library.Name))
                    {
                        targetContext.SubmitResult(CreateLibraryItem(library));
                    }

                    SearchAssemblies(library, library.CompileTimeAssemblies, PackageAssemblyGroupType.CompileTime);
                    SearchAssemblies(library, library.FrameworkAssemblies, PackageAssemblyGroupType.Framework);
                    SearchContentFiles(library);
                }

                SearchLogMessages();

                continue;

                async Task <ConfiguredProject?> FindConfiguredProjectAsync(string tfm)
                {
                    foreach (ConfiguredProject configuredProject in configuredProjects)
                    {
                        if (configuredProject.Services.ProjectSubscription == null)
                        {
                            continue;
                        }

                        IProjectSubscriptionUpdate subscriptionUpdate = (await configuredProject.Services.ProjectSubscription.ProjectRuleSource.GetLatestVersionAsync(configuredProject, cancellationToken: context.CancellationToken)).Value;

                        if (subscriptionUpdate.CurrentState.TryGetValue(NuGetRestoreRule.SchemaName, out IProjectRuleSnapshot nuGetRestoreSnapshot) &&
                            nuGetRestoreSnapshot.Properties.TryGetValue(NuGetRestoreRule.NuGetTargetMonikerProperty, out string nuGetTargetMoniker) &&
                            StringComparer.OrdinalIgnoreCase.Equals(nuGetTargetMoniker, tfm))
                        {
                            // Assets file 'target' string matches the configure project's NuGetTargetMoniker property value
                            return(configuredProject);
                        }

                        if (subscriptionUpdate.CurrentState.TryGetValue(ConfigurationGeneralRule.SchemaName, out IProjectRuleSnapshot configurationGeneralSnapshot) &&
                            configurationGeneralSnapshot.Properties.TryGetValue(ConfigurationGeneralRule.TargetFrameworkMonikerProperty, out string targetFrameworkMoniker) &&
                            StringComparer.OrdinalIgnoreCase.Equals(targetFrameworkMoniker, tfm))
                        {
                            // Assets file 'target' string matches the configure project's TargetFrameworkMoniker property value
                            return(configuredProject);
                        }
                    }

                    // No project found
                    return(null);
                }

                void SearchAssemblies(AssetsFileTargetLibrary library, ImmutableArray <string> assemblies, PackageAssemblyGroupType groupType)
                {
                    foreach (string assembly in assemblies)
                    {
                        if (targetContext.IsMatch(Path.GetFileName(assembly)))
                        {
                            targetContext.SubmitResult(new PackageAssemblyItem(target, library, assembly, groupType));
                        }
                    }
                }

                void SearchContentFiles(AssetsFileTargetLibrary library)
                {
                    foreach (AssetsFileTargetLibraryContentFile contentFile in library.ContentFiles)
                    {
                        if (targetContext.IsMatch(contentFile.Path))
                        {
                            targetContext.SubmitResult(new PackageContentFileItem(target, library, contentFile, _fileIconProvider));
                        }
                    }
                }

                IRelatableItem CreateLibraryItem(AssetsFileTargetLibrary library)
                {
                    return(library.Type switch
                    {
                        AssetsFileLibraryType.Package => new PackageReferenceItem(target, library),
                        AssetsFileLibraryType.Project => new ProjectReferenceItem(target, library),
                        _ => throw Assumes.NotReachable()
                    });
                }

                void SearchLogMessages()
                {
                    foreach (AssetsFileLogMessage log in target.Logs)
                    {
                        if (targetContext.IsMatch(log.Message))
                        {
                            targetContext.SubmitResult(CreateLogItem(log));
                        }
                    }

                    DiagnosticItem?CreateLogItem(AssetsFileLogMessage log)
                    {
                        if (target.LibraryByName.TryGetValue(log.LibraryName, out AssetsFileTargetLibrary? library))
                        {
                            return(new DiagnosticItem(target, library, log));
                        }

                        return(null);
                    }
                }
            }
示例#15
0
        private async Task HandleAsync(
            IProjectSubscriptionUpdate projectUpdate,
            IProjectCatalogSnapshot catalogSnapshot,
            RuleHandlerType handlerType)
        {
            AggregateCrossTargetProjectContext currentAggregateContext = await _host.GetCurrentAggregateProjectContext();

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

            IEnumerable <ICrossTargetRuleHandler <T> > handlers
                = Handlers
                  .Select(h => h.Value)
                  .Where(h => h.SupportsHandlerType(handlerType));

            ITargetedProjectContext projectContextToUpdate;

            // We need to process the update within a lock to ensure that we do not release this context during processing.
            // TODO: Enable concurrent execution of updates themselves, i.e. two separate invocations of HandleAsync
            //       should be able to run concurrently.
            using (await _gate.DisposableWaitAsync())
            {
                // Get the inner workspace project context to update for this change.
                projectContextToUpdate = currentAggregateContext
                                         .GetInnerProjectContext(projectUpdate.ProjectConfiguration, out bool isActiveContext);

                if (projectContextToUpdate == 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)
                {
                    if (handlerType == RuleHandlerType.DesignTimeBuild)
                    {
                        projectContextToUpdate.LastDesignTimeBuildSucceeded = false;
                    }

                    return;
                }

                // Get the subclass-specific context that will aggregate data during the processing of rule data by handlers.
                T ruleChangeContext = CreateRuleChangeContext(
                    currentAggregateContext.ActiveProjectContext.TargetFramework,
                    catalogSnapshot);

                // Give each handler a chance to modify the rule change context.
                foreach (ICrossTargetRuleHandler <T> handler in handlers)
                {
                    ImmutableHashSet <string> handlerRules = handler.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 (handler.ReceiveUpdatesWithEmptyProjectChange ||
                        projectChanges.Any(x => x.Value.Difference.AnyChanges))
                    {
                        // Handlers respond to rule changes in a way that's specific to the rule change context
                        // type (T). For example, DependencyRulesSubscriber uses DependenciesRuleChangeContext
                        // which holds IDependencyModel, so its ICrossTargetRuleHandler<DependenciesRuleChangeContext>
                        // implementations will produce IDependencyModel objects in response to rule changes.
                        handler.Handle(projectChanges, projectContextToUpdate.TargetFramework, ruleChangeContext);
                    }
                }

                // Notify the subclass that their rule change context object is ready for finalization.
                CompleteHandle(ruleChangeContext);
            }

            // record all the rules that have occurred
            _treeTelemetryService.ObserveTargetFrameworkRules(projectContextToUpdate.TargetFramework, projectUpdate.ProjectChanges.Keys);
        }