// Internal, for test use -- normal code should use the factory methods internal DependenciesSnapshot( TargetFramework activeTargetFramework, ImmutableDictionary <TargetFramework, TargetedDependenciesSnapshot> dependenciesByTargetFramework) { Requires.NotNull(activeTargetFramework, nameof(activeTargetFramework)); Requires.NotNull(dependenciesByTargetFramework, nameof(dependenciesByTargetFramework)); // We have seen NFEs where the active target framework is unsupported. Skipping validation in such cases is better than faulting the dataflow. if (!activeTargetFramework.Equals(TargetFramework.Empty) && !activeTargetFramework.Equals(TargetFramework.Unsupported) && !dependenciesByTargetFramework.ContainsKey(activeTargetFramework)) { string keyNames = dependenciesByTargetFramework.Count == 0 ? "no items" : string.Join(", ", dependenciesByTargetFramework.Keys.Select(t => $"\"{t.FullName}\"")); Requires.Argument( false, nameof(activeTargetFramework), $"Value \"{activeTargetFramework.FullName}\" is unexpected. Must be a key in {nameof(dependenciesByTargetFramework)}, which contains {keyNames}."); } ActiveTargetFramework = activeTargetFramework; DependenciesByTargetFramework = dependenciesByTargetFramework; }
public bool Equals(CompilationTarget other) { return(string.Equals(Name, other.Name, StringComparison.Ordinal) && string.Equals(Configuration, other.Configuration, StringComparison.Ordinal) && string.Equals(Aspect, other.Aspect, StringComparison.Ordinal) && TargetFramework.Equals(other.TargetFramework)); }
// Internal, for test use -- normal code should use the factory methods internal DependenciesSnapshot( TargetFramework activeTargetFramework, ImmutableDictionary <TargetFramework, TargetedDependenciesSnapshot> dependenciesByTargetFramework) { Requires.NotNull(activeTargetFramework, nameof(activeTargetFramework)); Requires.NotNull(dependenciesByTargetFramework, nameof(dependenciesByTargetFramework)); // We have seen NFEs where the active target framework is unsupported. Skipping validation in such cases is better than faulting the dataflow. if (!activeTargetFramework.Equals(TargetFramework.Empty) && !activeTargetFramework.Equals(TargetFramework.Unsupported)) { Requires.Argument( dependenciesByTargetFramework.ContainsKey(activeTargetFramework), nameof(dependenciesByTargetFramework), $"Must contain {nameof(activeTargetFramework)} ({activeTargetFramework.FullName})."); } ActiveTargetFramework = activeTargetFramework; DependenciesByTargetFramework = dependenciesByTargetFramework; }
// Internal, for test use -- normal code should use the factory methods internal DependenciesSnapshot( TargetFramework activeTargetFramework, ImmutableDictionary <TargetFramework, TargetedDependenciesSnapshot> dependenciesByTargetFramework) { Requires.NotNull(activeTargetFramework, nameof(activeTargetFramework)); Requires.NotNull(dependenciesByTargetFramework, nameof(dependenciesByTargetFramework)); #if false // The validation in this #if/#endif block is sound in theory, however is causing quite a few NFEs. // For example https://github.com/dotnet/project-system/issues/6656. // // We have disabled it for now. The consequence of this test failing is that dependencies added to // the tree are not exposed via extensibility APIs such as DTE/VSLangProj. // // At some point we should revisit how the dependencies tree models its target frameworks, likely // as part of https://github.com/dotnet/project-system/issues/6183. // We have seen NFEs where the active target framework is unsupported. Skipping validation in such cases is better than faulting the dataflow. if (!activeTargetFramework.Equals(TargetFramework.Empty) && !activeTargetFramework.Equals(TargetFramework.Unsupported) && !dependenciesByTargetFramework.ContainsKey(activeTargetFramework)) { string keyNames = dependenciesByTargetFramework.Count == 0 ? "no items" : string.Join(", ", dependenciesByTargetFramework.Keys.Select(t => $"\"{t.TargetFrameworkMoniker}\"")); Requires.Argument( false, nameof(activeTargetFramework), $"Value \"{activeTargetFramework.TargetFrameworkMoniker}\" is unexpected. Must be a key in {nameof(dependenciesByTargetFramework)}, which contains {keyNames}."); } #endif ActiveTargetFramework = activeTargetFramework; DependenciesByTargetFramework = dependenciesByTargetFramework; }
public bool Equals(FrameworkReferenceGroup other) { if (ReferenceEquals(other, null)) { return(false); } if (ReferenceEquals(this, other)) { return(true); } return(TargetFramework.Equals(other.TargetFramework) && FrameworkReferences.OrderedEquals(other.FrameworkReferences, dependency => dependency)); }
/// <summary> /// Creates a <see cref="AggregateCrossTargetProjectContext"/>. /// </summary> /// <returns> /// The created <see cref="AggregateCrossTargetProjectContext"/>. /// </returns> public async Task <AggregateCrossTargetProjectContext> CreateProjectContextAsync() { // Get the set of active configured projects ignoring target framework. #pragma warning disable CS0618 // Type or member is obsolete ImmutableDictionary <string, ConfiguredProject>?configuredProjectsMap = await _activeConfiguredProjectsProvider.GetActiveConfiguredProjectsMapAsync(); #pragma warning restore CS0618 // Type or member is obsolete if (configuredProjectsMap == null) { throw new InvalidOperationException("There are no active configured projects."); } ProjectConfiguration activeProjectConfiguration = _commonServices.ActiveConfiguredProject.ProjectConfiguration; ImmutableArray <TargetFramework> .Builder targetFrameworks = ImmutableArray.CreateBuilder <TargetFramework>(initialCapacity: configuredProjectsMap.Count); TargetFramework activeTargetFramework = TargetFramework.Empty; foreach ((string tfm, ConfiguredProject configuredProject) in configuredProjectsMap) { ProjectProperties projectProperties = configuredProject.Services.ExportProvider.GetExportedValue <ProjectProperties>(); ConfigurationGeneral configurationGeneralProperties = await projectProperties.GetConfigurationGeneralPropertiesAsync(); TargetFramework targetFramework = await GetTargetFrameworkAsync(tfm, configurationGeneralProperties); targetFrameworks.Add(targetFramework); if (activeTargetFramework.Equals(TargetFramework.Empty) && configuredProject.ProjectConfiguration.Equals(activeProjectConfiguration)) { activeTargetFramework = targetFramework; } } bool isCrossTargeting = !(configuredProjectsMap.Count == 1 && string.IsNullOrEmpty(configuredProjectsMap.First().Key)); return(new AggregateCrossTargetProjectContext( isCrossTargeting, targetFrameworks.MoveToImmutable(), configuredProjectsMap, activeTargetFramework, _targetFrameworkProvider)); }
public bool Equals(FrameworkInstalledPackages other) { if (other == null) { return(false); } if (ReferenceEquals(this, other)) { return(true); } bool equalsFramework; if (TargetFramework != null) { equalsFramework = TargetFramework.Equals(other.TargetFramework); } else { equalsFramework = other.TargetFramework == null; } bool equalsDict = false; if (Packages != null) { if (other.Packages != null) { equalsDict = Packages.Count == other.Packages.Count && !Packages.Except(other.Packages).Any(); } } else { equalsDict = other.Packages == null; } return(equalsFramework && equalsDict); }
public DependenciesSnapshot SetTargets( ImmutableArray <TargetFramework> targetFrameworks, TargetFramework activeTargetFramework) { bool activeChanged = !activeTargetFramework.Equals(ActiveTargetFramework); ImmutableDictionary <TargetFramework, TargetedDependenciesSnapshot> map = DependenciesByTargetFramework; var diff = new SetDiff <TargetFramework>(map.Keys, targetFrameworks); map = map.RemoveRange(diff.Removed); map = map.AddRange( diff.Added.Select( added => new KeyValuePair <TargetFramework, TargetedDependenciesSnapshot>( added, TargetedDependenciesSnapshot.CreateEmpty(added, null)))); if (activeChanged || !ReferenceEquals(map, DependenciesByTargetFramework)) { return(new DependenciesSnapshot(activeTargetFramework, map)); } return(this); }
public async Task <IRule?> GetBrowseObjectRuleAsync(IDependency dependency, TargetFramework targetFramework, IProjectCatalogSnapshot?catalogs) { Requires.NotNull(dependency, nameof(dependency)); IImmutableDictionary <string, IPropertyPagesCatalog> namedCatalogs = await GetNamedCatalogsAsync(); Requires.NotNull(namedCatalogs, nameof(namedCatalogs)); if (!namedCatalogs.TryGetValue(PropertyPageContexts.BrowseObject, out IPropertyPagesCatalog browseObjectsCatalog)) { // Issue https://github.com/dotnet/project-system/issues/4860 suggests this code path // can exist, however a repro was not found to dig deeper into the underlying cause. // For now just return null as the upstream caller handles null correctly anyway. return(null); } string?itemSpec = string.IsNullOrEmpty(dependency.OriginalItemSpec) ? dependency.FilePath : dependency.OriginalItemSpec; var context = ProjectPropertiesContext.GetContext( UnconfiguredProject, itemType: dependency.SchemaItemType, itemName: itemSpec); Rule?schema = dependency.SchemaName != null?browseObjectsCatalog.GetSchema(dependency.SchemaName) : null; if (schema == null) { // Since we have no browse object, we still need to create *something* so // that standard property pages can pop up. Rule emptyRule = RuleExtensions.SynthesizeEmptyRule(context.ItemType); return(GetConfiguredProjectExports().PropertyPagesDataModelProvider.GetRule( emptyRule, context.File, context.ItemType, context.ItemName)); } if (dependency.Resolved && !Strings.IsNullOrEmpty(dependency.OriginalItemSpec)) { return(GetConfiguredProjectExports().RuleFactory.CreateResolvedReferencePageRule( schema, context, dependency.OriginalItemSpec, dependency.BrowseObjectProperties)); } return(browseObjectsCatalog.BindToContext(schema.Name, context)); async Task <IImmutableDictionary <string, IPropertyPagesCatalog> > GetNamedCatalogsAsync() { if (catalogs != null) { return(catalogs.NamedCatalogs); } if (_namedCatalogs == null) { Assumes.NotNull(ActiveConfiguredProject); Assumes.Present(ActiveConfiguredProject.Services.PropertyPagesCatalog); // Note: it is unlikely that we end up here, however for cases when node providers // getting their node data not from Design time build events, we might have OnDependenciesChanged // event coming before initial design time build event updates NamedCatalogs in this class. // Thus, just in case, explicitly request it here (GetCatalogsAsync will acquire a project read lock) _namedCatalogs = await ActiveConfiguredProject.Services.PropertyPagesCatalog.GetCatalogsAsync(); } return(_namedCatalogs); } ConfiguredProjectExports GetConfiguredProjectExports() { Assumes.NotNull(ActiveConfiguredProject); ConfiguredProject project = targetFramework.Equals(TargetFramework.Any) ? ActiveConfiguredProject : _dependenciesSnapshotProvider.GetConfiguredProject(targetFramework) ?? ActiveConfiguredProject; return(GetActiveConfiguredProjectExports(project)); } }
/// <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; TargetFramework targetFramework = previousSnapshot.TargetFramework; var dependencyById = previousSnapshot.Dependencies.ToDictionary(IDependencyExtensions.GetDependencyId); 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) { if (!context.TryGetDependency(dependencyModel.GetDependencyId(), out IDependency dependency)) { return; } context.Reset(); foreach (IDependenciesSnapshotFilter filter in snapshotFilters) { filter.BeforeRemove( dependency, context); anyChanges |= context.Changed; if (!context.GetResult(filter)) { // TODO breaking here denies later filters the opportunity to modify builders return; } } dependencyById.Remove(dependencyModel.GetDependencyId()); anyChanges = true; } void Add(AddDependencyContext context, IDependencyModel dependencyModel) { // Create the unfiltered dependency IDependency?dependency = new Dependency(dependencyModel); context.Reset(); foreach (IDependenciesSnapshotFilter filter in snapshotFilters) { filter.BeforeAddOrUpdate( dependency, subTreeProviderByProviderType, projectItemSpecs, context); dependency = context.GetResult(filter); if (dependency == null) { break; } } if (dependency != null) { // A dependency was accepted DependencyId id = dependencyModel.GetDependencyId(); dependencyById.Remove(id); dependencyById.Add(id, dependency); anyChanges = true; } else { // Even though the dependency was rejected, it's possible that filters made // changes to other dependencies. anyChanges |= context.Changed; } } }
/// <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 occurred.</returns> public static TargetedDependenciesSnapshot FromChanges( TargetedDependenciesSnapshot previousSnapshot, IDependenciesChanges?changes, IProjectCatalogSnapshot?catalogs) { Requires.NotNull(previousSnapshot, nameof(previousSnapshot)); bool anyChanges = false; TargetFramework targetFramework = previousSnapshot.TargetFramework; var dependencyById = previousSnapshot.Dependencies.ToDictionary(IDependencyExtensions.GetDependencyId); if (changes != null && changes.RemovedNodes.Count != 0) { foreach (IDependencyModel removed in changes.RemovedNodes) { dependencyById.Remove(removed.GetDependencyId()); } anyChanges = true; } if (changes != null && changes.AddedNodes.Count != 0) { 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 IDependency dependency = new Dependency(added); DeduplicateCaptions(ref dependency, dependencyById); dependencyById[dependency.GetDependencyId()] = dependency; anyChanges = true; } } // 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); }