Skip to content

Commit

Permalink
Merge pull request #9235 from melytc/platform-properties
Browse files Browse the repository at this point in the history
  • Loading branch information
melytc authored Aug 30, 2023
2 parents 15f5972 + 3255da0 commit 7a43226
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.ProjectSystem.Properties
TargetPlatformVersionProperty
},
ExportInterceptingPropertyValueProviderFile.ProjectFile)]
internal sealed class CompoundTargetFrameworkValueProvider : InterceptingPropertyValueProviderBase
internal class CompoundTargetFrameworkValueProvider : InterceptingPropertyValueProviderBase
{
private const string InterceptedTargetFrameworkProperty = "InterceptedTargetFramework";
private const string TargetPlatformProperty = ConfigurationGeneral.TargetPlatformIdentifierProperty;
Expand All @@ -44,10 +44,10 @@ private struct ComplexTargetFramework
}

[ImportingConstructor]
public CompoundTargetFrameworkValueProvider(ProjectProperties properties, ConfiguredProject configuredProject)
public CompoundTargetFrameworkValueProvider(ProjectProperties properties)
{
_properties = properties;
_configuredProject = configuredProject;
_configuredProject = properties.ConfiguredProject;
}

private async Task<ComplexTargetFramework> GetStoredPropertiesAsync()
Expand All @@ -70,21 +70,30 @@ public override Task<bool> IsValueDefinedInContextAsync(string propertyName, IPr
// Changing the Target Framework Moniker
if (StringComparers.PropertyLiteralValues.Equals(propertyName, InterceptedTargetFrameworkProperty))
{
// Delete stored platform properties
storedProperties.TargetPlatformIdentifier = null;
storedProperties.TargetPlatformVersion = null;
await ResetPlatformPropertiesAsync(defaultProperties);

if (Strings.IsNullOrEmpty(unevaluatedPropertyValue))
{
return null;
}
storedProperties.TargetFrameworkMoniker = unevaluatedPropertyValue;

// Only projects targeting .NET 5 or higher use platform properties.
string targetFrameworkAlias = await GetTargetFrameworkAliasAsync(unevaluatedPropertyValue);
if (!IsNetCore5OrHigher(targetFrameworkAlias))
{
// Delete platform properties
storedProperties.TargetPlatformIdentifier = null;
storedProperties.TargetPlatformVersion = null;
await ResetPlatformPropertiesAsync(defaultProperties);
}
}

// Changing the Target Platform Identifier
else if (StringComparers.PropertyLiteralValues.Equals(propertyName, TargetPlatformProperty))
{
if (unevaluatedPropertyValue != storedProperties.TargetPlatformIdentifier)
{
// Delete stored platform properties
storedProperties.TargetPlatformIdentifier = null;
// Delete platform properties.
storedProperties.TargetPlatformVersion = null;
await ResetPlatformPropertiesAsync(defaultProperties);

Expand Down Expand Up @@ -198,7 +207,8 @@ private async Task<string> ComputeValueAsync(ComplexTargetFramework complexTarge
return targetFrameworkAlias + "-windows" + complexTargetFramework.TargetPlatformVersion;
}

if (!Strings.IsNullOrEmpty(complexTargetFramework.TargetPlatformIdentifier))
// We only keep the platform properties for projects targeting .NET 5 or higher.
if (!Strings.IsNullOrEmpty(complexTargetFramework.TargetPlatformIdentifier) && IsNetCore5OrHigher(targetFrameworkAlias))
{
string targetPlatformAlias = await GetTargetPlatformAliasAsync(complexTargetFramework.TargetPlatformIdentifier);

Expand Down Expand Up @@ -269,7 +279,7 @@ private async Task<bool> IsWindowsPlatformNeededAsync()
/// Retrieves the target framework alias (i.e. net5.0) from the project's subscription service.
/// </summary>
/// <returns></returns>
private async Task<string> GetTargetFrameworkAliasAsync(string targetFrameworkMoniker)
internal virtual async Task<string> GetTargetFrameworkAliasAsync(string targetFrameworkMoniker)
{
IProjectSubscriptionService? subscriptionService = _configuredProject.Services.ProjectSubscription;
Assumes.Present(subscriptionService);
Expand Down Expand Up @@ -318,6 +328,7 @@ private async Task<string> GetTargetPlatformAliasAsync(string targetPlatformIden
/// <returns></returns>
private static async Task ResetPlatformPropertiesAsync(IProjectProperties projectProperties)
{
await projectProperties.DeletePropertyAsync(TargetPlatformProperty);
await projectProperties.DeletePropertyAsync(TargetPlatformVersionProperty);
await projectProperties.DeletePropertyAsync(SupportedOSPlatformVersionProperty);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information.

namespace Microsoft.VisualStudio.ProjectSystem.Properties;

public class CompoundTargetFrameworkValueProviderTests
{
[Theory]
[InlineData("net6.0-windows7.0", ".NETCoreApp,Version=v6.0", ".NETCoreApp", "Windows", "7.0", true, null, null)] // Kept: changing the useWPF, useWindowsForms and useWinUI keeps the platform properties
[InlineData("net6.0-windows7.0", ".NETCoreApp,Version=v6.0", ".NETCoreApp", "Windows", "7.0", null, true, null)] // Kept
[InlineData("net6.0-windows7.0", ".NETCoreApp,Version=v6.0", ".NETCoreApp", "Windows", "7.0", null, null, true)] // Kept
[InlineData("net7.0-windows-7.0", ".NETCoreApp,Version=v7.0", ".NETCoreApp", "Windows", "7.0", true, null, null)] // Kept: upgrading tf
[InlineData("net8.0-windows7.0", ".NETCoreApp,Version=v8.0", ".NETCoreApp", "Windows", "7.0", true, null, null)] // Kept
[InlineData("net5.0-windows10.0", ".NETCoreApp,Version=v5.0", ".NETCoreApp", "Windows", "10.0", true, null, null)] // Kept: changing platform version
[InlineData("netcoreapp3.1", ".NETCoreApp,Version=v3.1", ".NETCoreApp", "", "", true, null, null)] // Removed: downgrading tf
[InlineData("net5.0-android", ".NETCoreApp,Version=v5.0", ".NETCoreApp", "Android", "", null, null, null)] // Removed: changing platform
public async Task WhenChangingTargetFrameworkProperties_PlatformPropertiesAreKeptOrRemoved(string tf, string tfm, string tfi, string tpi, string tpv, bool? useWF, bool? useWPF, bool? useWUI)
{
// Previous target framework properties
var propertiesAndValues = new Dictionary<string, string?>
{
{ "InterceptedTargetFramework", ".net5.0-windows" },
{ "TargetPlatformIdentifier", "windows" },
{ "TargetPlatformVersion", "7.0" }
};
var iProjectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(propertiesAndValues);

// New target framework properties
var projectProperties = ProjectPropertiesFactory.Create(
new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetFrameworkProperty, tf, null),
new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetFrameworkMonikerProperty, tfm, null),
new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetFrameworkIdentifierProperty, tfi, null),
new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetPlatformIdentifierProperty, tpi, null),
new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetPlatformVersionProperty, tpv, null),
new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.UseWPFProperty, useWPF, null),
new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.UseWindowsFormsProperty, useWF, null),
new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.UseWinUIProperty, useWUI, null));

var mockProvider = new Mock<CompoundTargetFrameworkValueProvider>(MockBehavior.Strict, projectProperties);
mockProvider.Setup(m => m.GetTargetFrameworkAliasAsync(tfm)).Returns(Task.FromResult(tf));
mockProvider.Setup(m => m.OnSetPropertyValueAsync("InterceptedTargetFramework", tfm, iProjectProperties, null)).CallBase();

var provider = mockProvider.Object;
await provider.OnSetPropertyValueAsync("InterceptedTargetFramework", tfm, iProjectProperties);

mockProvider.VerifyAll();
mockProvider.Setup(m => m.OnGetEvaluatedPropertyValueAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IProjectProperties>())).CallBase();

// The value of the property we surface in the UI is stored as a moniker.
var actualTargetFramework = await provider.OnGetEvaluatedPropertyValueAsync("InterceptedTargetFramework", "", iProjectProperties);
Assert.Equal(tfm, actualTargetFramework);

var actualPlatformIdentifier = await provider.OnGetEvaluatedPropertyValueAsync(ConfigurationGeneral.TargetPlatformIdentifierProperty, "", iProjectProperties);
Assert.Equal(tpi, actualPlatformIdentifier);

var actualPlatformVersion = await provider.OnGetEvaluatedPropertyValueAsync(ConfigurationGeneral.TargetPlatformVersionProperty, "", iProjectProperties);
Assert.Equal(tpv, actualPlatformVersion);

var actualComputedTargetFramework = await provider.OnGetEvaluatedPropertyValueAsync(ConfigurationGeneral.TargetFrameworkProperty, "", iProjectProperties);
Assert.Equal(tf, actualComputedTargetFramework);
}
}

0 comments on commit 7a43226

Please sign in to comment.