/// <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); }
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; }
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); }
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); }
/// <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); }
/// <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); }
private void UpdateInMemoryCachedData(VersionCompatibilityData newData) { _curVersionCompatibilityData = newData; _timeCurVersionDataLastUpdatedUtc = DateTime.UtcNow; }
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); }
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); } } }
/// <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; }
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); }