Ejemplo n.º 1
0
        /// <summary>
        /// Fired when the solution load process is fully complete, including all background loading
        /// of projects. This event always fires after the initial opening of a solution
        /// </summary>
        public int OnAfterBackgroundSolutionLoadComplete()
        {
            // Schedule this to run on idle
            _threadHandling.Value.JoinableTaskFactory.RunAsync(async() =>
            {
                // Run on the background
                await TaskScheduler.Default;

                VersionCompatibilityData compatDataToUse = GetVersionCmpatibilityData();

                CompatibilityLevel finalCompatLevel        = CompatibilityLevel.Recommended;
                IProjectService projectService             = _projectServiceAccessor.Value.GetProjectService();
                IEnumerable <UnconfiguredProject> projects = projectService.LoadedUnconfiguredProjects;
                foreach (UnconfiguredProject project in projects)
                {
                    // Track the most severe compatibility level
                    CompatibilityLevel compatLevel = await GetProjectCompatibilityAsync(project, compatDataToUse).ConfigureAwait(false);
                    if (compatLevel != CompatibilityLevel.Recommended && compatLevel > finalCompatLevel)
                    {
                        finalCompatLevel = compatLevel;
                    }
                }

                if (finalCompatLevel != CompatibilityLevel.Recommended)
                {
                    // Warn the user.
                    await WarnUserOfIncompatibleProjectAsync(finalCompatLevel, compatDataToUse).ConfigureAwait(false);
                }

                // Used so we know when to process newly added projects
                _solutionOpened = true;
            });

            return(VSConstants.S_OK);
        }
Ejemplo n.º 2
0
        private async Task CheckCompatibilityAsync()
        {
            // Run on the background
            await TaskScheduler.Default;

            VersionCompatibilityData compatDataToUse = GetVersionCompatibilityData();
            CompatibilityLevel finalCompatLevel = CompatibilityLevel.Recommended;
            IProjectService projectService = _projectServiceAccessor.Value.GetProjectService();
            IEnumerable<UnconfiguredProject> projects = projectService.LoadedUnconfiguredProjects;
            bool isPreviewSDKInUse = await IsPreviewSDKInUseAsync();
            foreach (UnconfiguredProject project in projects)
            {
                // Track the most severe compatibility level
                CompatibilityLevel compatLevel = await GetProjectCompatibilityAsync(project, compatDataToUse, isPreviewSDKInUse);
                if (compatLevel != CompatibilityLevel.Recommended && compatLevel > finalCompatLevel)
                {
                    finalCompatLevel = compatLevel;
                }
            }

            if (finalCompatLevel != CompatibilityLevel.Recommended)
            {

                // Warn the user.
                await WarnUserOfIncompatibleProjectAsync(finalCompatLevel, compatDataToUse, isPreviewSDKInUse);
            }

            // Used so we know when to process newly added projects
            SolutionOpen = true;
        }
Ejemplo n.º 3
0
        public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)
        {
            // Only check this project if the solution is opened and we haven't already warned at the maximum level. Note that fAdded
            // is true for both add and a reload of an unloaded project
            if (_solutionOpened && fAdded == 1 && _compatibilityLevelWarnedForThisSolution != CompatibilityLevel.NotSupported)
            {
                UnconfiguredProject project = pHierarchy.AsUnconfiguredProject();
                if (project != null)
                {
                    _threadHandling.Value.JoinableTaskFactory.RunAsync(async() =>
                    {
                        // Run on the background
                        await TaskScheduler.Default;

                        VersionCompatibilityData compatData = GetVersionCmpatibilityData();

                        // We need to check if this project has been newly created. Our projects will implement IProjectCreationState -we can
                        // skip any that don't
                        IProjectCreationState projectCreationState = project.Services.ExportProvider.GetExportedValueOrDefault <IProjectCreationState>();
                        if (projectCreationState != null && !projectCreationState.WasNewlyCreated)
                        {
                            CompatibilityLevel compatLevel = await GetProjectCompatibilityAsync(project, compatData).ConfigureAwait(false);
                            if (compatLevel != CompatibilityLevel.Recommended)
                            {
                                await WarnUserOfIncompatibleProjectAsync(compatLevel, compatData).ConfigureAwait(false);
                            }
                        }
                    });
                }
            }

            return(VSConstants.S_OK);
        }
Ejemplo n.º 4
0
        public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)
        {
            // Only check this project if the solution is opened and we haven't already warned at the maximum level. Note that fAdded
            // is true for both add and a reload of an unloaded project
            if (SolutionOpen && fAdded == 1 && CompatibilityLevelWarnedForCurrentSolution != CompatibilityLevel.NotSupported)
            {
                UnconfiguredProject? project = pHierarchy.AsUnconfiguredProject();
                if (project != null)
                {
                    _threadHandling.Value.RunAndForget(async () =>
                    {
                        // Run on the background
                        await TaskScheduler.Default;

                        VersionCompatibilityData compatData = GetVersionCompatibilityData();

                        // We need to check if this project has been newly created. Our projects will implement IProjectCreationState -we can 
                        // skip any that don't
                        if (IsNewlyCreated(project))
                        {
                            bool isPreviewSDKInUse = await IsPreviewSDKInUseAsync();
                            CompatibilityLevel compatLevel = await GetProjectCompatibilityAsync(project, compatData, isPreviewSDKInUse);
                            if (compatLevel != CompatibilityLevel.Recommended)
                            {
                                await WarnUserOfIncompatibleProjectAsync(compatLevel, compatData, isPreviewSDKInUse);
                            }
                        }
                    }, unconfiguredProject: null);
                }
            }

            return HResult.OK;
        }
        public void DataCorrectlySerializes()
        {
            string expectedSupportedMessage = "A newer version of Visual Studio is recommended for projects targetting .NET Core projects later then 2.2.";
            string versionDataString        = $@" {{
  ""vsVersions"": {{
    ""15.6"": {{
      ""recommendedVersion"": ""2.0"",
      ""nonRecommendedVersionSelectedMessage"": """",
      ""supportedVersion"": ""2.1"",
      ""openSupportedMessage"": """",
      ""unsupportedVersion"": ""3.0"",
      ""unsupportedVersionsInstalledMessage"": """",
      ""openUnsupportedMessage"": """"
    }},
    ""15.8"": {{
      ""recommendedVersion"": ""2.1"",
      ""nonRecommendedVersionSelectedMessage"": ""{0} requires a newer version of Visual Studio."",
      ""supportedVersion"": ""2.2"",
      ""openSupportedMessage"": ""Visual Studio 2017 version 15.9 or newer is recommended for .NET Core 2.2 projects."",
      ""unsupportedVersion"": ""3.0"",
      ""unsupportedVersionsInstalledMessage"": """",
      ""openUnsupportedMessage"": """"
    }},
    ""15.9"": {{
      ""nonRecommendedVersionSelectedMessage"": """",
      ""supportedVersion"": ""2.3"",
      ""openSupportedMessage"": ""{expectedSupportedMessage}"",
      ""unsupportedVersion"": ""3.0"",
      ""unsupportedVersionsInstalledMessage"": """",
      ""openUnsupportedMessage"": """"
    }},
    ""16.0"": {{
      ""nonRecommendedVersionSelectedMessage"": """",
      ""supportedVersion"": ""3.0"",
      ""openSupportedMessage"": ""New string for 3.0 project open scenario"",
      ""unsupportedVersion"": ""3.1"",
      ""unsupportedVersionsInstalledMessage"": """",
      ""openUnsupportedMessage"": """"
    }}
  }}
}}";
            var    data = VersionCompatibilityData.DeserializeVersionData(versionDataString);

            Assert.NotNull(data);
            Assert.False(data.TryGetValue(new Version("15.5"), out _));
            Assert.True(data.TryGetValue(new Version("15.6"), out _));
            Assert.True(data.TryGetValue(new Version("15.8"), out _));
            Assert.True(data.TryGetValue(new Version("16.0"), out _));
            Assert.True(data.TryGetValue(new Version("15.9"), out var compatibilityData));
            Assert.NotNull(compatibilityData.SupportedVersion);
            Assert.NotNull(compatibilityData.UnsupportedVersion);
            Assert.NotNull(compatibilityData.OpenSupportedMessage);
            Assert.NotNull(compatibilityData.OpenUnsupportedMessage);
            Assert.Equal(expectedSupportedMessage, compatibilityData.OpenSupportedMessage);
        }
Ejemplo n.º 6
0
 /// <summary>
 /// If the cached file exists reads the data and returns it
 /// </summary>
 private Dictionary <Version, VersionCompatibilityData> GetCompabilityDataFromCacheFile()
 {
     try
     {
         string data = _versionDataCacheFile?.ReadCacheFile();
         if (data != null)
         {
             return(VersionCompatibilityData.DeserializeVersionData(data));
         }
     }
     catch
     {
     }
     return(null);
 }
        /// <summary>
        /// If the cached file exists reads the data and returns it
        /// </summary>
        private async Task <Dictionary <Version, VersionCompatibilityData>?> GetCompatibilityDataFromCacheFileAsync()
        {
            if (_versionDataCacheFile is null)
            {
                return(null);
            }

            try
            {
                string?data = await _versionDataCacheFile.ReadCacheFileAsync();

                if (data != null)
                {
                    return(VersionCompatibilityData.DeserializeVersionData(data));
                }
            }
            catch
            {
            }

            return(null);
        }
        public void DataCorrectlySerializes_PreviewVersion()
        {
            string expectedSupportedMessage = "A newer version of Visual Studio is recommended for projects targetting .NET Core projects later then 2.2.";
            string versionDataString        = $@" {{
  ""vsVersions"": {{
    ""16.1"": {{
      ""openSupportedPreviewMessage"": ""{expectedSupportedMessage}"",
      ""supportedPreviewVersion"": ""3.0"",
    }}
  }}
}}";
            var    data = VersionCompatibilityData.DeserializeVersionData(versionDataString);

            Assert.NotNull(data);
            Assert.True(data.TryGetValue(new Version("16.1"), out var compatibilityData));
            Assert.Null(compatibilityData.SupportedVersion);
            Assert.Null(compatibilityData.UnsupportedVersion);
            Assert.Null(compatibilityData.OpenSupportedMessage);
            Assert.Null(compatibilityData.OpenUnsupportedMessage);
            Assert.NotNull(compatibilityData.SupportedPreviewVersion);
            Assert.Equal(expectedSupportedMessage, compatibilityData.OpenSupportedPreviewMessage);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Compares the passed in version to the compatibility data to determine the compat level
        /// </summary>
        private CompatibilityLevel GetCompatibilityLevelFromVersion(Version version, VersionCompatibilityData compatData)
        {
            // Omly compare major, minor. The presence of build with change the comparison. ie: 2.0 != 2.0.0
            if (version.Build != -1)
            {
                version = new Version(version.Major, version.Minor);
            }

            if (compatData.SupportedVersion != null)
            {
                if (version < compatData.SupportedVersion)
                {
                    return(CompatibilityLevel.Recommended);
                }
                else if (version == compatData.SupportedVersion || (compatData.UnsupportedVersion != null && version < compatData.UnsupportedVersion))
                {
                    return(CompatibilityLevel.Supported);
                }

                return(CompatibilityLevel.NotSupported);
            }

            // Only has an unsupported version
            if (compatData.UnsupportedVersion != null)
            {
                if (version < compatData.UnsupportedVersion)
                {
                    return(CompatibilityLevel.Recommended);
                }

                return(CompatibilityLevel.NotSupported);
            }

            // No restrictions
            return(CompatibilityLevel.Recommended);
        }
Ejemplo n.º 10
0
 private void UpdateInMemoryCachedData(VersionCompatibilityData newData)
 {
     _curVersionCompatibilityData      = newData;
     _timeCurVersionDataLastUpdatedUtc = DateTime.UtcNow;
 }
Ejemplo n.º 11
0
        private static async Task <CompatibilityLevel> GetProjectCompatibilityAsync(UnconfiguredProject project, VersionCompatibilityData compatData)
        {
            if (project.Capabilities.AppliesTo($"{ProjectCapability.DotNet} & {ProjectCapability.PackageReferences}"))
            {
                IProjectProperties properties = project.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject.Services.ProjectPropertiesProvider.GetCommonProperties();
                string             tfm        = await properties.GetEvaluatedPropertyValueAsync("TargetFrameworkMoniker").ConfigureAwait(false);

                if (!string.IsNullOrEmpty(tfm))
                {
                    var fw = new FrameworkName(tfm);
                    if (fw.Identifier.Equals(".NETCoreApp", StringComparison.OrdinalIgnoreCase))
                    {
                        return(GetCompatibilityLevelFromVersion(fw.Version, compatData));
                    }
                    else if (fw.Identifier.Equals(".NETFramework", StringComparison.OrdinalIgnoreCase))
                    {
                        // The interesting case here is Asp.Net Core on full framework
                        IImmutableSet <IUnresolvedPackageReference> pkgReferences = await project.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject.Services.PackageReferences.GetUnresolvedReferencesAsync().ConfigureAwait(false);

                        // Look through the package references
                        foreach (IUnresolvedPackageReference pkgRef in pkgReferences)
                        {
                            if (string.Equals(pkgRef.EvaluatedInclude, "Microsoft.AspNetCore.All", StringComparison.OrdinalIgnoreCase) ||
                                string.Equals(pkgRef.EvaluatedInclude, "Microsoft.AspNetCore", StringComparison.OrdinalIgnoreCase))
                            {
                                string verString = await pkgRef.Metadata.GetEvaluatedPropertyValueAsync("Version").ConfigureAwait(false);

                                if (!string.IsNullOrWhiteSpace(verString))
                                {
                                    // This is a semantic version string. We only care about the non-semantic version part
                                    int index = verString.IndexOfAny(new char[] { '-', '+' });
                                    if (index != -1)
                                    {
                                        verString = verString.Substring(0, index);
                                    }

                                    if (Version.TryParse(verString, out Version aspnetVersion))
                                    {
                                        return(GetCompatibilityLevelFromVersion(aspnetVersion, compatData));
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(CompatibilityLevel.Recommended);
        }
Ejemplo n.º 12
0
        private async Task WarnUserOfIncompatibleProjectAsync(CompatibilityLevel compatLevel, VersionCompatibilityData compatData)
        {
            // Warn the user.
            await _threadHandling.Value.SwitchToUIThread();

            // Check if already warned - this could happen in the off chance two projects are added very quickly since the detection work is
            // scheduled on idle.
            if (_compatibilityLevelWarnedForThisSolution < compatLevel)
            {
                // Only want to warn once per solution
                _compatibilityLevelWarnedForThisSolution = compatLevel;

                IVsUIShell uiShell = _serviceProvider.GetService <IVsUIShell, SVsUIShell>();
                uiShell.GetAppName(out string caption);

                if (compatLevel == CompatibilityLevel.Supported)
                {
                    // Get current dontShowAgain value
                    var  settingsManager = (ISettingsManager)_serviceProvider.GetService(typeof(SVsSettingsPersistenceManager));
                    bool suppressPrompt  = false;
                    if (settingsManager != null)
                    {
                        suppressPrompt = settingsManager.GetValueOrDefault(SuppressDotNewCoreWarningKey, defaultValue: false);
                    }

                    if (!suppressPrompt)
                    {
                        string msg = string.Format(compatData.OpenSupportedMessage, compatData.SupportedVersion.Major, compatData.SupportedVersion.Minor);
                        suppressPrompt = _dialogServices.Value.DontShowAgainMessageBox(caption, msg, VSResources.DontShowAgain, false, VSResources.LearnMore, SupportedLearnMoreFwlink);
                        if (suppressPrompt && settingsManager != null)
                        {
                            await settingsManager.SetValueAsync(SuppressDotNewCoreWarningKey, suppressPrompt, isMachineLocal : true).ConfigureAwait(true);
                        }
                    }
                }
                else
                {
                    string msg;
                    if (compatData.UnsupportedVersion != null)
                    {
                        msg = string.Format(compatData.OpenUnsupportedMessage, compatData.UnsupportedVersion.Major, compatData.UnsupportedVersion.Minor);
                    }
                    else
                    {
                        msg = string.Format(compatData.OpenUnsupportedMessage, compatData.SupportedVersion.Major, compatData.SupportedVersion.Minor);
                    }

                    _dialogServices.Value.DontShowAgainMessageBox(caption, msg, null, false, VSResources.LearnMore, UnsupportedLearnMoreFwlink);
                }
            }
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Compares the passed in version to the compatibility data to determine the compat level
        /// </summary>
        private static CompatibilityLevel GetCompatibilityLevelFromVersion(Version version, VersionCompatibilityData compatData, bool isPreviewSDKInUse)
        {
            // Only compare major, minor. The presence of build with change the comparison. ie: 2.0 != 2.0.0
            if (version.Build != -1)
            {
                version = new Version(version.Major, version.Minor);
            }

            if (compatData.SupportedPreviewVersion is null &&
                compatData.SupportedVersion is null &&
                compatData.UnsupportedVersion is null)
            {
                // No restrictions
                return CompatibilityLevel.Recommended;
            }

            // Version is less than the supported preview version and the user wants to use preview SDKs
            if (compatData.SupportedPreviewVersion is object && isPreviewSDKInUse && version <= compatData.SupportedPreviewVersion)
            {
                return CompatibilityLevel.Recommended;
            }

            // A supported version exists and the version is less than the supported version
            if (compatData.SupportedVersion is object && version < compatData.SupportedVersion)
            {
                return CompatibilityLevel.Recommended;
            }

            // The version is not unsupported and exactly matches the supported version
            if (compatData.SupportedVersion is object && compatData.UnsupportedVersion is object &&
                version == compatData.SupportedVersion && version < compatData.UnsupportedVersion)
            {
                return CompatibilityLevel.Supported;
            }

            // Supported version is null or not recommended check unsupported version
            if (version < compatData.UnsupportedVersion)
            {
                return CompatibilityLevel.Recommended;
            }

            // Unsupported version is not recommended
            return CompatibilityLevel.NotSupported;
        }
Ejemplo n.º 14
0
        private async Task WarnUserOfIncompatibleProjectAsync(CompatibilityLevel compatLevel, VersionCompatibilityData compatData, bool isPreviewSDKInUse)
        {
            if (!_threadHandling.Value.IsOnMainThread)
            {
                await _threadHandling.Value.SwitchToUIThread();
            }

            // Check if already warned - this could happen in the off chance two projects are added very quickly since the detection work is 
            // scheduled on idle.
            if (CompatibilityLevelWarnedForCurrentSolution < compatLevel)
            {
                // Only want to warn once per solution
                CompatibilityLevelWarnedForCurrentSolution = compatLevel;

                IVsUIShell uiShell = await _vsUIShellService.GetValueAsync();
                uiShell.GetAppName(out string caption);

                if (compatLevel == CompatibilityLevel.Supported)
                {
                    // Get current dontShowAgain value
                    ISettingsManager settingsManager = await _settingsManagerService.GetValueAsync();
                    bool suppressPrompt = settingsManager.GetValueOrDefault(SuppressDotNewCoreWarningKey, defaultValue: false);

                    if (compatData.OpenSupportedPreviewMessage is null && isPreviewSDKInUse)
                    {
                        // There is no message to show the user in this case so we return
                        return;
                    }

                    if (!suppressPrompt)
                    {
                        string msg;
                        if (compatData.OpenSupportedPreviewMessage is object && isPreviewSDKInUse)
                        {
                            msg = string.Format(compatData.OpenSupportedPreviewMessage, compatData.SupportedVersion!.Major, compatData.SupportedVersion.Minor);
                        }
                        else
                        {
                            msg = string.Format(compatData.OpenSupportedMessage, compatData.SupportedVersion!.Major, compatData.SupportedVersion.Minor);
                        }

                        suppressPrompt = _dialogServices.Value.DontShowAgainMessageBox(caption, msg, VSResources.DontShowAgain, false, VSResources.LearnMore, SupportedLearnMoreFwlink);
                        if (suppressPrompt && settingsManager != null)
                        {
                            await settingsManager.SetValueAsync(SuppressDotNewCoreWarningKey, suppressPrompt, isMachineLocal: true);
                        }
                    }
                }
                else
                {
                    string msg;
                    if (compatData.UnsupportedVersion != null)
                    {
                        msg = string.Format(compatData.OpenUnsupportedMessage, compatData.UnsupportedVersion.Major, compatData.UnsupportedVersion.Minor);
                    }
                    else
                    {
                        msg = string.Format(compatData.OpenUnsupportedMessage, compatData.SupportedVersion!.Major, compatData.SupportedVersion.Minor);
                    }

                    _dialogServices.Value.DontShowAgainMessageBox(caption, msg, null, false, VSResources.LearnMore, UnsupportedLearnMoreFwlink);
                }
            }
        }
        private static CompatibilityLevel GetCompatibilityLevelFromVersion(Version version, VersionCompatibilityData compatData, bool isPreviewSDKInUse)
        {
            // Only compare major, minor. The presence of build with change the comparison. ie: 2.0 != 2.0.0
            if (version.Build != -1)
            {
                version = new Version(version.Major, version.Minor);
            }

            if (compatData.SupportedPreviewVersion is null &&
                compatData.SupportedVersion is null &&
                compatData.UnsupportedVersion is null)
            {
                // No restrictions
                return(CompatibilityLevel.Recommended);
            }

            return(GetCompatibilityLevelFromPreview(version, compatData.SupportedPreviewVersion, compatData.SupportedVersion, compatData.UnsupportedVersion, isPreviewSDKInUse));
        }
        private static async Task <CompatibilityLevel> GetProjectCompatibilityAsync(UnconfiguredProject project, VersionCompatibilityData compatData, bool isPreviewSDKInUse)
        {
            if (project.Capabilities.AppliesTo(RequiredProjectCapabilities))
            {
                Assumes.Present(project.Services.ActiveConfiguredProjectProvider);
                ConfiguredProject?activeConfiguredProject = project.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject;
                Assumes.NotNull(activeConfiguredProject);
                Assumes.Present(activeConfiguredProject.Services.ProjectPropertiesProvider);
                IProjectProperties properties = activeConfiguredProject.Services.ProjectPropertiesProvider.GetCommonProperties();
                string             tfm        = await properties.GetEvaluatedPropertyValueAsync("TargetFrameworkMoniker");

                if (!string.IsNullOrEmpty(tfm))
                {
                    var fw = new FrameworkName(tfm);
                    if (fw.Identifier.Equals(TargetFrameworkIdentifiers.NetCoreApp, StringComparisons.FrameworkIdentifiers))
                    {
                        return(GetCompatibilityLevelFromVersion(fw.Version, compatData, isPreviewSDKInUse));
                    }
                    else if (fw.Identifier.Equals(TargetFrameworkIdentifiers.NetFramework, StringComparisons.FrameworkIdentifiers))
                    {
                        // The interesting case here is Asp.Net Core on full framework
                        Assumes.Present(activeConfiguredProject.Services.PackageReferences);
                        IImmutableSet <IUnresolvedPackageReference> pkgReferences = await activeConfiguredProject.Services.PackageReferences.GetUnresolvedReferencesAsync();

                        // Look through the package references
                        foreach (IUnresolvedPackageReference pkgRef in pkgReferences)
                        {
                            if (string.Equals(pkgRef.EvaluatedInclude, "Microsoft.AspNetCore.All", StringComparisons.ItemNames) ||
                                string.Equals(pkgRef.EvaluatedInclude, "Microsoft.AspNetCore", StringComparisons.ItemNames))
                            {
                                string verString = await pkgRef.Metadata.GetEvaluatedPropertyValueAsync("Version");

                                if (!string.IsNullOrWhiteSpace(verString))
                                {
                                    // This is a semantic version string. We only care about the non-semantic version part
                                    int index = verString.IndexOfAny(Delimiter.PlusAndMinus);
                                    if (index != -1)
                                    {
                                        verString = verString.Substring(0, index);
                                    }

                                    if (Version.TryParse(verString, out Version aspnetVersion))
                                    {
                                        return(GetCompatibilityLevelFromVersion(aspnetVersion, compatData, isPreviewSDKInUse));
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(CompatibilityLevel.Recommended);
        }