diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.BeforeCommon.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.BeforeCommon.targets
index 8b42b2d6e847..c3f08df257a4 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.BeforeCommon.targets
+++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.BeforeCommon.targets
@@ -206,10 +206,19 @@ Copyright (c) .NET Foundation. All rights reserved.
+
+
+
+ <_SupportedPlatformCompatibleVersions Include="@(SupportedTargetPlatform)" Condition=" %(Identity) != '' and $([MSBuild]::VersionLessThan(%(Identity), $(TargetPlatformVersion))) " />
+ <_ImplicitDefineConstant Include="@(_SupportedPlatformCompatibleVersions->'$(TargetPlatformIdentifier.ToUpper())%(Identity)'->Replace('.', '_'))" />
+
+
+
$(DefineConstants);@(_ImplicitDefineConstant)
diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildALibrary.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildALibrary.cs
index ef98b64c861d..01a79dc6de4d 100644
--- a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildALibrary.cs
+++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildALibrary.cs
@@ -447,6 +447,42 @@ public void It_implicitly_defines_compilation_constants_for_the_target_platform(
AssertDefinedConstantsOutput(testAsset, targetFramework, new[] { "NETCOREAPP", "NET", "NET5_0", "NETCOREAPP3_1" }.Concat(expectedDefines).ToArray());
}
+ [Theory]
+ [InlineData(new[] { "1.0", "1.1" }, "ios", "1.1", new[] { "IOS", "IOS1_1", "IOS1_0" })]
+ [InlineData(new[] { "11.11", "12.12", "13.13" }, "android", "12.12", new[] { "ANDROID", "ANDROID11_11", "ANDROID12_12" })]
+ [InlineData(new[] { "7.0", "8.0", "10.0.19041", "11.0.0" }, "windows", "11.0.0", new[] { "WINDOWS", "WINDOWS7_0", "WINDOWS8_0", "WINDOWS10_0_19041", "WINDOWS11_0_0" })]
+ public void It_implicitly_defines_compilation_constants_for_the_target_platform_with_backwards_compatibility(string[] supportedTargetPlatform, string targetPlatformIdentifier, string targetPlatformVersion, string[] expectedDefines)
+ {
+ var targetFramework = "net5.0";
+ var testAsset = _testAssetsManager
+ .CopyTestAsset("AppWithLibrary", "ImplicitFrameworkConstants", targetFramework)
+ .WithSource()
+ .WithTargetFramework(targetFramework)
+ .WithProjectChanges(project =>
+ {
+ // Manually set target plaform properties
+ var ns = project.Root.Name.Namespace;
+ var propGroup = new XElement(ns + "PropertyGroup");
+ project.Root.Add(propGroup);
+
+ var platformIdentifier = new XElement(ns + "TargetPlatformIdentifier", targetPlatformIdentifier);
+ propGroup.Add(platformIdentifier);
+ var platformVersion = new XElement(ns + "TargetPlatformVersion", targetPlatformVersion);
+ propGroup.Add(platformVersion);
+ var disableUnnecessaryImplicitFrameworkReferencesForThisTest = new XElement(ns + "DisableImplicitFrameworkReferences", "true");
+ propGroup.Add(disableUnnecessaryImplicitFrameworkReferencesForThisTest);
+
+ var itemGroup = new XElement(ns + "ItemGroup");
+ project.Root.Add(itemGroup);
+ foreach (var targetPlatform in supportedTargetPlatform)
+ {
+ itemGroup.Add(new XElement(ns + "SupportedTargetPlatform", new XAttribute("Include", targetPlatform)));
+ }
+ });
+
+ AssertDefinedConstantsOutput(testAsset, targetFramework, new[] { "NETCOREAPP", "NET", "NET5_0", "NETCOREAPP3_1" }.Concat(expectedDefines).ToArray());
+ }
+
private void AssertDefinedConstantsOutput(TestAsset testAsset, string targetFramework, string[] expectedDefines)
{
var libraryProjectDirectory = Path.Combine(testAsset.TestRoot, "TestLibrary");
@@ -468,6 +504,71 @@ private void AssertDefinedConstantsOutput(TestAsset testAsset, string targetFram
definedConstants.Should().BeEquivalentTo(new[] { "DEBUG", "TRACE" }.Concat(expectedDefines).ToArray());
}
+ [WindowsOnlyTheory]
+ [InlineData("netcoreapp3.1", new[] { "NETCOREAPP", "NETCOREAPP3_1" })]
+ [InlineData("net5.0", new[] { "NETCOREAPP", "NETCOREAPP3_1", "NET", "NET5_0", "WINDOWS", "WINDOWS7_0" }, "windows", "7.0")]
+ public void It_can_use_implicitly_defined_compilation_constants(string targetFramework, string[] expectedOutput, string targetPlatformIdentifier = null, string targetPlatformVersion = null)
+ {
+ var testProj = new TestProject()
+ {
+ Name = "CompilationConstants",
+ TargetFrameworks = targetFramework,
+ IsExe = true,
+ IsSdkProject = true
+ };
+ if (targetPlatformIdentifier != null)
+ {
+ testProj.AdditionalProperties["TargetPlatformIdentifier"] = targetPlatformIdentifier;
+ testProj.AdditionalProperties["TargetPlatformVersion"] = targetPlatformVersion;
+ }
+ var testAsset = _testAssetsManager.CreateTestProject(testProj);
+ File.WriteAllText(Path.Combine(testAsset.Path, testProj.Name, $"{testProj.Name}.cs"), @"
+using System;
+class Program
+{
+ static void Main(string[] args)
+ {
+ #if NETCOREAPP
+ Console.WriteLine(""NETCOREAPP"");
+ #endif
+ #if NETCOREAPP2_1
+ Console.WriteLine(""NETCOREAPP2_1"");
+ #endif
+ #if NETCOREAPP3_1
+ Console.WriteLine(""NETCOREAPP3_1"");
+ #endif
+ #if NET
+ Console.WriteLine(""NET"");
+ #endif
+ #if NET5_0
+ Console.WriteLine(""NET5_0"");
+ #endif
+ #if WINDOWS
+ Console.WriteLine(""WINDOWS"");
+ #endif
+ #if WINDOWS7_0
+ Console.WriteLine(""WINDOWS7_0"");
+ #endif
+ #if IOS
+ Console.WriteLine(""IOS"");
+ #endif
+ #if IOS7_0
+ Console.WriteLine(""IOS7_0"");
+ #endif
+ }
+}");
+
+ var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.Path, testProj.Name));
+ buildCommand
+ .Execute()
+ .Should()
+ .Pass();
+
+ var runCommand = new RunExeCommand(Log, Path.Combine(buildCommand.GetOutputDirectory(targetFramework).FullName, $"{testProj.Name}.exe"));
+ var stdOut = runCommand.Execute().StdOut.Split(Environment.NewLine.ToCharArray()).Where(line => !string.IsNullOrWhiteSpace(line));
+ stdOut.Should().BeEquivalentTo(expectedOutput);
+ }
+
[Theory]
[InlineData(false)]
[InlineData(true)]