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); }
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); }
/// <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)); }
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); }
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(); } }
protected abstract T CreateRuleChangeContext(ITargetFramework target, IProjectCatalogSnapshot catalogs);
/// <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); }
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;
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);
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);
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); }
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) =>
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); }
public ConfiguredProject GetInnerConfiguredProject(ITargetFramework target) { return(_configuredProjectsByTargetFramework.FirstOrDefault(x => target.Equals(x.Key)).Value); }