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)); } }
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); }
public DependenciesChange TestProcessDependenciesChanges( IProjectSubscriptionUpdate projectSubscriptionUpdate, IProjectCatalogSnapshot catalogs) { return(ProcessDependenciesChanges(projectSubscriptionUpdate, catalogs)); }
protected abstract EnumCollection Transform(IProjectSubscriptionUpdate input);
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); }
public Task <DesignTimeInputsItem> TestProcessAsync(IProjectSubscriptionUpdate compileUpdate, IProjectSubscriptionUpdate configurationGeneralUpdate) { return(TestProcessAsync(compileUpdate, configurationGeneralUpdate, new DesignTimeInputsDelta())); }
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); } }
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); }
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); }
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); }
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); } } }
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); }