public ITargetFramework GetProjectFramework(ProjectConfiguration projectConfiguration)
        {
            if (projectConfiguration.IsCrossTargeting())
            {
                string targetFrameworkMoniker = projectConfiguration.Dimensions[ConfigurationGeneral.TargetFrameworkProperty];

                return(_targetFrameworkProvider.GetTargetFramework(targetFrameworkMoniker));
            }
            else
            {
                return(TargetFrameworks.Length > 1 ? null : TargetFrameworks[0]);
            }
        }
Пример #2
0
        private 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;

            ImmutableDictionary <ITargetFramework, IDependenciesChanges> changes = ImmutableDictionary <ITargetFramework, IDependenciesChanges> .Empty.Add(targetFramework, e.Changes);

            UpdateDependenciesSnapshot(changes, catalogs: null, activeTargetFramework: null, e.Token);
        }
        private void OnSubtreeProviderDependenciesChanged(object sender, DependenciesChangedEventArgs e)
        {
            if (IsDisposing || IsDisposed || !e.Changes.AnyChanges())
            {
                return;
            }

            // TODO remove ! when https://github.com/dotnet/roslyn/issues/36018 is fixed
            ITargetFramework targetFramework =
                string.IsNullOrEmpty(e.TargetShortOrFullName) || TargetFramework.Any.Equals(e.TargetShortOrFullName !)
                    ? TargetFramework.Any
                    : _targetFrameworkProvider.GetTargetFramework(e.TargetShortOrFullName) ?? TargetFramework.Any;

            ImmutableDictionary <ITargetFramework, IDependenciesChanges> changes = ImmutableDictionary <ITargetFramework, IDependenciesChanges> .Empty.Add(targetFramework, e.Changes);

            UpdateDependenciesSnapshot(changes, catalogs: null, activeTargetFramework: null, e.Token);
        }
        /// <summary>
        /// Ensures that <see cref="_currentAggregateProjectContext"/> is updated for the latest TargetFrameworks from the project properties
        /// and returns this value.
        /// </summary>
        private async Task <AggregateCrossTargetProjectContext> UpdateProjectContextAsync()
        {
            // Ensure that only single thread is attempting to create a project context.
            AggregateCrossTargetProjectContext previousContextToDispose = null;

            return(await ExecuteWithinLockAsync(async() =>
            {
                // Check if we have already computed the project context.
                if (_currentAggregateProjectContext != 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.
                    var projectProperties = await _commonServices.ActiveConfiguredProjectProperties.GetConfigurationGeneralPropertiesAsync().ConfigureAwait(false);

                    if (!_currentAggregateProjectContext.IsCrossTargeting)
                    {
                        var newTargetFramework = _targetFrameworkProvider.GetTargetFramework((string)await projectProperties.TargetFramework.GetValueAsync().ConfigureAwait(false));
                        if (_currentAggregateProjectContext.ActiveProjectContext.TargetFramework.Equals(newTargetFramework))
                        {
                            return _currentAggregateProjectContext;
                        }
                    }
                    else
                    {
                        var targetFrameworks = (string)await projectProperties.TargetFrameworks.GetValueAsync().ConfigureAwait(false);

                        // Check if the current project context is up-to-date for the current active and known project configurations.
                        var activeProjectConfiguration = _commonServices.ActiveConfiguredProject.ProjectConfiguration;
                        var knownProjectConfigurations = await _commonServices.Project.Services.ProjectConfigurationsService.GetKnownProjectConfigurationsAsync().ConfigureAwait(false);
                        if (knownProjectConfigurations.All(c => c.IsCrossTargeting()) &&
                            _currentAggregateProjectContext.HasMatchingTargetFrameworks(activeProjectConfiguration, knownProjectConfigurations))
                        {
                            return _currentAggregateProjectContext;
                        }
                    }

                    previousContextToDispose = _currentAggregateProjectContext;
                }

                // Force refresh the CPS active project configuration (needs UI thread).
                await _commonServices.ThreadingService.SwitchToUIThread();
                await _activeProjectConfigurationRefreshService.RefreshActiveProjectConfigurationAsync().ConfigureAwait(false);

                // Dispose the old project context, if one exists.
                if (previousContextToDispose != null)
                {
                    await DisposeAggregateProjectContextAsync(previousContextToDispose).ConfigureAwait(false);
                }

                // Create new project context.
                _currentAggregateProjectContext = await _contextProvider.Value.CreateProjectContextAsync().ConfigureAwait(false);

                OnAggregateContextChanged(previousContextToDispose, _currentAggregateProjectContext);

                return _currentAggregateProjectContext;
            }).ConfigureAwait(false));
        }
        protected override async Task InitializeCoreAsync(CancellationToken cancellationToken)
        {
            await UpdateProjectContextAndSubscriptionsAsync();

            lock (_lock)
            {
                if (_isDisposed)
                {
                    throw new ObjectDisposedException(nameof(DependenciesSnapshotProvider));
                }

                _commonServices.Project.ProjectUnloading += OnUnconfiguredProjectUnloadingAsync;

                foreach (Lazy <IProjectDependenciesSubTreeProvider, IOrderPrecedenceMetadataView> provider in _subTreeProviders)
                {
                    provider.Value.DependenciesChanged += OnSubtreeProviderDependenciesChanged;
                }

                _disposables.Add(
                    new DisposableDelegate(
                        () =>
                {
                    _commonServices.Project.ProjectUnloading -= OnUnconfiguredProjectUnloadingAsync;

                    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
                DisposeCore();

                return(Task.CompletedTask);
            }

            void OnSubtreeProviderDependenciesChanged(object?sender, DependenciesChangedEventArgs e)
            {
                if (IsDisposing || IsDisposed || !e.Changes.AnyChanges())
                {
                    return;
                }

                ITargetFramework targetFramework =
                    Strings.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);
Пример #6
0
 public TargetFramework?GetProjectFramework(ProjectConfiguration projectConfiguration)
 {
     if (projectConfiguration.Dimensions.TryGetValue(ConfigurationGeneral.TargetFrameworkProperty, out string targetFrameworkMoniker))
     {
         return(_targetFrameworkProvider.GetTargetFramework(targetFrameworkMoniker));
     }
     else
     {
         return(TargetFrameworks.Length > 1 ? null : TargetFrameworks[0]);
     }
 }
Пример #7
0
        private async Task <ITargetFramework> GetTargetFrameworkAsync(
            string shortOrFullName,
            ConfigurationGeneral configurationGeneralProperties)
        {
            if (string.IsNullOrEmpty(shortOrFullName))
            {
                object?targetObject = await configurationGeneralProperties.TargetFramework.GetValueAsync();

                if (targetObject == null)
                {
                    return(TargetFramework.Empty);
                }

                shortOrFullName = targetObject.ToString();
            }

            return(_targetFrameworkProvider.GetTargetFramework(shortOrFullName) ?? TargetFramework.Empty);
        }
Пример #8
0
        private async Task <TargetFramework> GetTargetFrameworkAsync(
            string shortOrFullName,
            ConfiguredProject configuredProject)
        {
            if (string.IsNullOrEmpty(shortOrFullName))
            {
                ProjectProperties    projectProperties = configuredProject.Services.ExportProvider.GetExportedValue <ProjectProperties>();
                ConfigurationGeneral configurationGeneralProperties = await projectProperties.GetConfigurationGeneralPropertiesAsync();

                object?targetObject = await configurationGeneralProperties.TargetFramework.GetValueAsync();

                if (targetObject == null)
                {
                    return(TargetFramework.Empty);
                }

                shortOrFullName = targetObject.ToString();
            }

            return(_targetFrameworkProvider.GetTargetFramework(shortOrFullName) ?? TargetFramework.Empty);
        }
Пример #9
0
        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(
                    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(
                    originalItemSpec: itemSpec,
                    version: properties.GetStringProperty(ProjectItemMetadata.Version) ?? string.Empty,
                    isResolved: false,
                    isImplicitlyDefined,
                    isVisible: !isImplicitlyDefined,
                    properties);
            }

            return(true);
        }
Пример #10
0
            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 async Task <AggregateCrossTargetProjectContext?> TryUpdateCurrentAggregateProjectContextAsync()
            {
                AggregateCrossTargetProjectContext?previousContext = Current;

                // 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.TargetFrameworkMoniker.GetValueAsync();

                        if (string.IsNullOrEmpty(newTargetFrameworkName) && TargetFramework.Empty.Equals(previousContext.ActiveTargetFramework))
                        {
                            // No change
                            return(null);
                        }

                        TargetFramework?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.
                        Assumes.Present(_commonServices.Project.Services.ProjectConfigurationsService);
                        ProjectConfiguration activeProjectConfiguration = _commonServices.ActiveConfiguredProject.ProjectConfiguration;
                        IImmutableSet <ProjectConfiguration> knownProjectConfigurations = await _commonServices.Project.Services.ProjectConfigurationsService.GetKnownProjectConfigurationsAsync();

                        if (knownProjectConfigurations.All(c => c.IsCrossTargeting()) &&
                            HasMatchingTargetFrameworks(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();

                Current = newContext;

                return(newContext);

                bool HasMatchingTargetFrameworks(
                    ProjectConfiguration activeProjectConfiguration,
                    IReadOnlyCollection <ProjectConfiguration> knownProjectConfigurations)
                {
                    Assumes.NotNull(previousContext);
                    Assumes.True(activeProjectConfiguration.IsCrossTargeting());

                    TargetFramework?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)
                    {
                        TargetFramework?targetFramework = _targetFrameworkProvider.GetTargetFramework(targetFrameworkMoniker);

                        if (targetFramework == null || !previousContext.TargetFrameworks.Contains(targetFramework))
                        {
                            // Differing TargetFramework
                            return(false);
                        }
                    }

                    return(true);
                }
            }
Пример #12
0
            public static bool TryGetMetadata(
                string itemSpec,
                bool isResolved,
                IImmutableDictionary <string, string> properties,
                Func <string, bool>?isEvaluatedItemSpec,
                ITargetFramework targetFramework,
                ITargetFrameworkProvider targetFrameworkProvider,
                out PackageDependencyMetadata metadata)
            {
                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(targetFrameworkProvider, nameof(targetFrameworkProvider));
                    Requires.NotNull(isEvaluatedItemSpec !, nameof(isEvaluatedItemSpec));

                    DependencyType dependencyType = properties.GetEnumProperty <DependencyType>(ProjectItemMetadata.Type) ?? DependencyType.Unknown;

                    if (dependencyType == DependencyType.Target)
                    {
                        // Disregard items of type 'Target' from design-time build
                        metadata = default;
                        return(false);
                    }

                    int    slashIndex          = itemSpec.IndexOf('/');
                    string?targetFrameworkName = slashIndex == -1 ? null : s_targetFrameworkInternPool.Intern(itemSpec.Substring(0, slashIndex));

                    if (targetFrameworkName == null ||
                        targetFrameworkProvider.GetTargetFramework(targetFrameworkName)?.Equals(targetFramework) != true)
                    {
                        metadata = default;
                        return(false);
                    }

                    string name = properties.GetStringProperty(ProjectItemMetadata.Name) ?? itemSpec;

                    bool isTopLevel = isImplicitlyDefined ||
                                      (dependencyType == DependencyType.Package && isEvaluatedItemSpec(name));

                    string originalItemSpec = isTopLevel ? name : itemSpec;

                    metadata = new PackageDependencyMetadata(
                        dependencyType,
                        targetFrameworkName,
                        itemSpec,
                        originalItemSpec,
                        name,
                        isResolved: true,
                        isImplicitlyDefined,
                        isTopLevel,
                        properties);
                }
                else
                {
                    // We only have evaluation data

                    System.Diagnostics.Debug.Assert(itemSpec.IndexOf('/') == -1);

                    metadata = new PackageDependencyMetadata(
                        dependencyType: DependencyType.Package,
                        targetFrameworkName: null,
                        itemSpec,
                        originalItemSpec: itemSpec,
                        name: itemSpec,
                        isResolved: false,
                        isImplicitlyDefined,
                        isTopLevel: true,
                        properties);
                }

                return(true);
            }