예제 #1
0
        // 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;
        }
예제 #2
0
 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;
        }
예제 #4
0
        // 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);
        }
예제 #8
0
        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);
        }