Skip to content

Commit 9324107

Browse files
Add $(LatestDotNetCoreForMSBuild) infrastructure for centralized framework targeting (#13189)
### Context This PR adds the `$(LatestDotNetCoreForMSBuild)` infrastructure that centralizes the target framework version across test infrastructure. This infrastructure provides a single source of truth for the latest .NET Core target framework, making it easier to update framework versions across all test projects and test code. ### Changes Made **Build infrastructure:** - Added `latestDotNetCoreForMSBuild` parameter to `BootstrapLocationAttribute` - Added `_Parameter4` to `Microsoft.Build.UnitTests.Shared.csproj` to pass the latest .NET Core TFM - Added `LatestDotNetCoreForMSBuild` property to `RunnerUtilities` for test access **Test code:** - Added `-p:LatestDotNetCoreForMSBuild={RunnerUtilities.LatestDotNetCoreForMSBuild}` to MSBuild command invocations in `BuildCheck.UnitTests/EndToEndTests.cs` (2 locations) - Changed framework references to use `RunnerUtilities.LatestDotNetCoreForMSBuild` instead of hardcoded `"net10.0"` in: - `Copy_Tests.cs` (2 locations for path construction) - `TerminalLogger_Tests.cs` (1 test property) - Added `using Microsoft.Build.UnitTests.Shared;` to `Copy_Tests.cs` for access to `RunnerUtilities` **Test assets:** - Changed `<TargetFramework>net10.0</TargetFramework>` to `<TargetFramework>$(LatestDotNetCoreForMSBuild)</TargetFramework>` in 8 test project files: - `ExampleTask.csproj` - `TestNetTask.csproj` - `TestMSBuildTaskInNet.csproj` - `TestNetTaskWithImplicitParams.csproj` - `TaskHostLifecycleTestApp.csproj` - `CopyAlwaysTest/EntryProject.csproj` - `CopyAlwaysTest/ReferencedProject.csproj` - `EmbeddedResourceTest/EntryProject.csproj` ### Testing Build succeeds. The infrastructure changes are tested through existing test suites that use the updated test assets. ### Notes The $(LatestDotNetCoreForMSBuild) infrastructure provides flexibility for future .NET version updates by centralizing the target framework definition. This avoids hardcoding "net10.0" throughout the test codebase and makes it easier to update to newer framework versions by changing the value in a single location (`Directory.Build.props`). <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: YuliiaKovalova <ykovalova@microsoft.com>
1 parent 4f6355e commit 9324107

16 files changed

Lines changed: 98 additions & 78 deletions

File tree

src/Build.UnitTests/NetTaskHost_E2E_Tests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public void NetTaskHostTest()
3636

3737
string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestNetTask", "TestNetTask.csproj");
3838

39-
string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n", out bool successTestTask);
39+
string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n -p:LatestDotNetCoreForMSBuild={RunnerUtilities.LatestDotNetCoreForMSBuild}", out bool successTestTask);
4040

4141
if (!successTestTask)
4242
{
@@ -63,7 +63,7 @@ public void NetTaskHost_CorrectPathsEscapingTest()
6363
using TestEnvironment env = TestEnvironment.Create(_output);
6464
var dotnetPath = env.GetEnvironmentVariable("DOTNET_ROOT");
6565
string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestNetTask", "TestNetTask.csproj");
66-
string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n", out bool successTestTask);
66+
string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n -p:LatestDotNetCoreForMSBuild={RunnerUtilities.LatestDotNetCoreForMSBuild}", out bool successTestTask);
6767

6868
if (!successTestTask)
6969
{
@@ -124,7 +124,7 @@ public void MSBuildTaskInNetHostTest()
124124

125125
string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestMSBuildTaskInNet", "TestMSBuildTaskInNet.csproj");
126126

127-
string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n", out bool successTestTask);
127+
string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n -p:LatestDotNetCoreForMSBuild={RunnerUtilities.LatestDotNetCoreForMSBuild}", out bool successTestTask);
128128

129129
if (!successTestTask)
130130
{
@@ -143,7 +143,7 @@ public void NetTaskWithImplicitHostParamsTest()
143143

144144
string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestNetTaskWithImplicitParams", "TestNetTaskWithImplicitParams.csproj");
145145

146-
string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n", out bool successTestTask);
146+
string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n -p:LatestDotNetCoreForMSBuild={RunnerUtilities.LatestDotNetCoreForMSBuild}", out bool successTestTask);
147147

148148
if (!successTestTask)
149149
{
@@ -164,4 +164,4 @@ public void NetTaskWithImplicitHostParamsTest()
164164
testTaskOutput.ShouldContain("Found item: Banana");
165165
}
166166
}
167-
}
167+
}

src/Build.UnitTests/TaskHostFactoryLifecycle_E2E_Tests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private string ExecuteBuildWithTaskHost(string runtimeToUse, string taskFactoryT
9595
string testProjectPath = Path.Combine(TestAssetsRootPath, "TaskHostLifecycleTestApp.csproj");
9696

9797
string output = RunnerUtilities.ExecBootstrapedMSBuild(
98-
$"{testProjectPath} -v:n -restore /p:RuntimeToUse={runtimeToUse} /p:TaskFactoryToUse={taskFactoryToUse}",
98+
$"{testProjectPath} -v:n -restore /p:RuntimeToUse={runtimeToUse} /p:TaskFactoryToUse={taskFactoryToUse} /p:LatestDotNetCoreForMSBuild={RunnerUtilities.LatestDotNetCoreForMSBuild}",
9999
out bool success,
100100
outputHelper: _output);
101101

src/Build.UnitTests/TerminalLogger_Tests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,7 @@ public async Task ProjectFinishedReportsTargetFrameworkAndRuntimeIdentifier()
10111011
{
10121012
// this project will report a TFM and a RID and so will show a both in the output
10131013
var buildOutputEvent = MakeBuildOutputEventArgs(_projectFile);
1014-
InvokeLoggerCallbacksForSimpleProject(succeeded: true, properties: [("TargetFramework", "net10.0"), ("RuntimeIdentifier", "win-x64")], additionalCallbacks: () =>
1014+
InvokeLoggerCallbacksForSimpleProject(succeeded: true, properties: [("TargetFramework", RunnerUtilities.LatestDotNetCoreForMSBuild), ("RuntimeIdentifier", "win-x64")], additionalCallbacks: () =>
10151015
{
10161016
_centralNodeEventSource.InvokeMessageRaised(buildOutputEvent);
10171017
});

src/Build.UnitTests/TestAssets/ExampleNetTask/ExampleTask/ExampleTask.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net10.0</TargetFramework>
4+
<TargetFramework>$(LatestDotNetCoreForMSBuild)</TargetFramework>
55
</PropertyGroup>
66

77
<ItemGroup>

src/Build.UnitTests/TestAssets/ExampleNetTask/TestMSBuildTaskInNet/TestMSBuildTaskInNet.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net10.0</TargetFrameworks>
4+
<TargetFrameworks>$(LatestDotNetCoreForMSBuild)</TargetFrameworks>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
</PropertyGroup>

src/Build.UnitTests/TestAssets/ExampleNetTask/TestNetTask/TestNetTask.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net10.0</TargetFramework>
4+
<TargetFramework>$(LatestDotNetCoreForMSBuild)</TargetFramework>
55
</PropertyGroup>
66

77
<PropertyGroup>

src/Build.UnitTests/TestAssets/ExampleNetTask/TestNetTaskWithImplicitParams/TestNetTaskWithImplicitParams.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net10.0</TargetFramework>
4+
<TargetFramework>$(LatestDotNetCoreForMSBuild)</TargetFramework>
55
</PropertyGroup>
66

77
<PropertyGroup>

src/Build.UnitTests/TestAssets/TaskHostLifecycle/TaskHostLifecycleTestApp.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net10.0</TargetFramework>
4+
<TargetFramework>$(LatestDotNetCoreForMSBuild)</TargetFramework>
55
<TestProjectFolder>$([System.IO.Path]::GetFullPath('$([System.IO.Path]::Combine('$(AssemblyLocation)', '..'))'))</TestProjectFolder>
66
<ExampleTaskPath>$([System.IO.Path]::Combine('$(TestProjectFolder)', '$(TargetFramework)', 'ExampleTask.dll'))</ExampleTaskPath>
77
</PropertyGroup>

src/BuildCheck.UnitTests/EndToEndTests.cs

Lines changed: 71 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ namespace Microsoft.Build.BuildCheck.UnitTests;
2121
public class EndToEndTests : IDisposable
2222
{
2323
private const string EditorConfigFileName = ".editorconfig";
24-
private const string LatestDotNetCoreForMSBuild = "net10.0";
2524

2625
private readonly TestEnvironment _env;
2726

@@ -171,7 +170,7 @@ private EmbedResourceTestOutput RunEmbeddedResourceTest(string resourceXmlToAdd,
171170

172171
_env.SetCurrentDirectory(Path.Combine(workFolder.Path, entryProjectName));
173172

174-
string output = RunnerUtilities.ExecBootstrapedMSBuild("-check -restore /p:WarnOnCultureOverwritten=True /p:RespectCulture=" + (respectCulture ? "True" : "\"\""), out bool success, timeoutMilliseconds: timeoutInMilliseconds);
173+
string output = RunnerUtilities.ExecBootstrapedMSBuild($"-check -restore /p:WarnOnCultureOverwritten=True /p:RespectCulture={(respectCulture ? "True" : "\"\"")} /p:LatestDotNetCoreForMSBuild={RunnerUtilities.LatestDotNetCoreForMSBuild}", out bool success, timeoutMilliseconds: timeoutInMilliseconds);
175174
_env.Output.WriteLine(output);
176175
_env.Output.WriteLine("=========================");
177176
success.ShouldBeTrue();
@@ -232,7 +231,7 @@ private readonly record struct CopyTestOutput(
232231

233232
private CopyTestOutput RunCopyToOutputTest(bool restore, bool skipUnchangedDuringCopy)
234233
{
235-
string output = RunnerUtilities.ExecBootstrapedMSBuild($"-check {(restore ? "-restore" : null)} /p:SkipUnchanged={(skipUnchangedDuringCopy ? "True" : "\"\"")}", out bool success, timeoutMilliseconds: timeoutInMilliseconds);
234+
string output = RunnerUtilities.ExecBootstrapedMSBuild($"-check {(restore ? "-restore" : null)} /p:SkipUnchanged={(skipUnchangedDuringCopy ? "True" : "\"\"")} /p:LatestDotNetCoreForMSBuild={RunnerUtilities.LatestDotNetCoreForMSBuild}", out bool success, timeoutMilliseconds: timeoutInMilliseconds);
236235
_env.Output.WriteLine(output);
237236
_env.Output.WriteLine("=========================");
238237
success.ShouldBeTrue();
@@ -361,19 +360,26 @@ public void WarningsCountExceedsLimitTest(bool buildInOutOfProcessNode, bool lim
361360
}
362361
}
363362

363+
private const string testAssetsFolder = "TFMConfusionCheck";
364+
365+
public static IEnumerable<object[]> TFMConfusionCheckTestData()
366+
{
367+
yield return [$"""<TargetFramework>{RunnerUtilities.LatestDotNetCoreForMSBuild}</TargetFramework>""", "", false];
368+
yield return [$"""<TargetFrameworks>{RunnerUtilities.LatestDotNetCoreForMSBuild};net472</TargetFrameworks>""", "", false];
369+
yield return [$"""<TargetFrameworks>{RunnerUtilities.LatestDotNetCoreForMSBuild};net472</TargetFrameworks>""", $" /p:TargetFramework={RunnerUtilities.LatestDotNetCoreForMSBuild}", false];
370+
yield return [$"""<TargetFramework></TargetFramework><TargetFrameworks>{RunnerUtilities.LatestDotNetCoreForMSBuild};net472</TargetFrameworks>""", "", false];
371+
yield return [$"""<TargetFramework /><TargetFrameworks>{RunnerUtilities.LatestDotNetCoreForMSBuild};net472</TargetFrameworks>""", "", false];
372+
yield return [$"""<TargetFramework>{RunnerUtilities.LatestDotNetCoreForMSBuild}</TargetFramework><TargetFrameworks></TargetFrameworks>""", "", false];
373+
yield return [$"""<TargetFramework>{RunnerUtilities.LatestDotNetCoreForMSBuild}</TargetFramework><TargetFrameworks />""", "", false];
374+
yield return [$"""<TargetFramework>{RunnerUtilities.LatestDotNetCoreForMSBuild}</TargetFramework><TargetFrameworks>{RunnerUtilities.LatestDotNetCoreForMSBuild};net472</TargetFrameworks>""", "", true];
375+
}
376+
364377
[Theory]
365-
[InlineData($"""<TargetFramework>{LatestDotNetCoreForMSBuild}</TargetFramework>""", "", false)]
366-
[InlineData($"""<TargetFrameworks>{LatestDotNetCoreForMSBuild};net472</TargetFrameworks>""", "", false)]
367-
[InlineData($"""<TargetFrameworks>{LatestDotNetCoreForMSBuild};net472</TargetFrameworks>""", $" /p:TargetFramework={LatestDotNetCoreForMSBuild}", false)]
368-
[InlineData($"""<TargetFramework></TargetFramework><TargetFrameworks>{LatestDotNetCoreForMSBuild};net472</TargetFrameworks>""", "", false)]
369-
[InlineData($"""<TargetFramework /><TargetFrameworks>{LatestDotNetCoreForMSBuild};net472</TargetFrameworks>""", "", false)]
370-
[InlineData($"""<TargetFramework>{LatestDotNetCoreForMSBuild}</TargetFramework><TargetFrameworks></TargetFrameworks>""", "", false)]
371-
[InlineData($"""<TargetFramework>{LatestDotNetCoreForMSBuild}</TargetFramework><TargetFrameworks />""", "", false)]
372-
[InlineData($"""<TargetFramework>{LatestDotNetCoreForMSBuild}</TargetFramework><TargetFrameworks>{LatestDotNetCoreForMSBuild};net472</TargetFrameworks>""", "", true)]
378+
[MemberData(nameof(TFMConfusionCheckTestData))]
373379
public void TFMConfusionCheckTest(string tfmString, string cliSuffix, bool shouldTriggerCheck)
374380
{
375381
const string testAssetsFolderName = "TFMConfusionCheck";
376-
const string projectName = testAssetsFolderName;
382+
const string projectName = testAssetsFolder;
377383
const string templateToReplace = "###TFM";
378384
TransientTestFolder workFolder = _env.CreateFolder(createFolder: true);
379385

@@ -407,56 +413,61 @@ void ReplaceStringInFile(string filePath, string original, string replacement)
407413
}
408414
}
409415

416+
public static IEnumerable<object[]> TFMinNonSdkCheckTestData()
417+
{
418+
yield return [
419+
"""
420+
<Project ToolsVersion="msbuilddefaulttoolsversion">
421+
<PropertyGroup>
422+
<TargetFramework>net48</TargetFramework>
423+
</PropertyGroup>
424+
<Target Name="Build">
425+
<Message Text="Build done"/>
426+
</Target>
427+
</Project>
428+
""",
429+
false];
430+
yield return [
431+
$"""
432+
<Project Sdk="Microsoft.NET.Sdk">
433+
<PropertyGroup>
434+
<TargetFramework>{RunnerUtilities.LatestDotNetCoreForMSBuild}</TargetFramework>
435+
</PropertyGroup>
436+
</Project>
437+
""",
438+
false];
439+
yield return [
440+
"""
441+
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
442+
<PropertyGroup>
443+
<OutputType>Library</OutputType>
444+
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
445+
<OutputPath>bin\Debug\</OutputPath>
446+
<NoWarn>CS2008</NoWarn>
447+
</PropertyGroup>
448+
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
449+
</Project>
450+
""",
451+
false];
452+
yield return [
453+
"""
454+
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
455+
<PropertyGroup>
456+
<OutputType>Library</OutputType>
457+
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
458+
<TargetFramework>v4.8</TargetFramework>
459+
<OutputPath>bin\Debug\</OutputPath>
460+
<NoWarn>CS2008</NoWarn>
461+
</PropertyGroup>
462+
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
463+
</Project>
464+
""",
465+
true];
466+
}
467+
410468
// Windows only - due to targeting NetFx
411469
[WindowsOnlyTheory]
412-
[InlineData(
413-
"""
414-
<Project ToolsVersion="msbuilddefaulttoolsversion">
415-
<PropertyGroup>
416-
<TargetFramework>net48</TargetFramework>
417-
</PropertyGroup>
418-
<Target Name="Build">
419-
<Message Text="Build done"/>
420-
</Target>
421-
</Project>
422-
""",
423-
false)]
424-
[InlineData(
425-
$"""
426-
<Project Sdk="Microsoft.NET.Sdk">
427-
<PropertyGroup>
428-
<TargetFramework>{LatestDotNetCoreForMSBuild}</TargetFramework>
429-
</PropertyGroup>
430-
</Project>
431-
""",
432-
false)]
433-
[InlineData(
434-
"""
435-
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
436-
<PropertyGroup>
437-
<OutputType>Library</OutputType>
438-
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
439-
<OutputPath>bin\Debug\</OutputPath>
440-
<NoWarn>CS2008</NoWarn>
441-
</PropertyGroup>
442-
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
443-
</Project>
444-
""",
445-
false)]
446-
[InlineData(
447-
"""
448-
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
449-
<PropertyGroup>
450-
<OutputType>Library</OutputType>
451-
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
452-
<TargetFramework>v4.8</TargetFramework>
453-
<OutputPath>bin\Debug\</OutputPath>
454-
<NoWarn>CS2008</NoWarn>
455-
</PropertyGroup>
456-
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
457-
</Project>
458-
""",
459-
true)]
470+
[MemberData(nameof(TFMinNonSdkCheckTestData))]
460471
public void TFMinNonSdkCheckTest(string projectContent, bool expectCheckTrigger)
461472
{
462473
TransientTestFolder workFolder = _env.CreateFolder(createFolder: true);

src/BuildCheck.UnitTests/TestAssets/CopyAlwaysTest/EntryProject/EntryProject.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net10.0</TargetFramework>
4+
<TargetFramework>$(LatestDotNetCoreForMSBuild)</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
</PropertyGroup>

0 commit comments

Comments
 (0)