From 1143e5cddceffaa1954412c9cb1f12c23f3d2031 Mon Sep 17 00:00:00 2001 From: Stuart Lang Date: Thu, 6 Feb 2025 00:33:51 +0000 Subject: [PATCH 1/4] Pass TFM into Buildalyzer build --- .../Stryker.Core/Initialisation/InputFileResolver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs b/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs index 15b910366..58be310b3 100644 --- a/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs +++ b/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs @@ -246,7 +246,7 @@ private IAnalyzerResults AnalyzeSingleProject(IProjectAnalyzer project, IStryker } var projectLogName = Path.GetRelativePath(options.WorkingDirectory, project.ProjectFile.Path); _logger.LogDebug("Analyzing {ProjectFilePath}", projectLogName); - var buildResult = project.Build(); + var buildResult = project.Build(options.TargetFramework); var buildResultOverallSuccess = buildResult.OverallSuccess || Array. TrueForAll(project.ProjectFile.TargetFrameworks, tf => @@ -338,7 +338,7 @@ private void LogAnalyzerResult(IAnalyzerResults analyzerResults, IStrykerOptions { if (importantProperties.Contains(property.Key)) { - continue; // already logged + continue; // already logged } log.AppendLine($"Property {property.Key}={property.Value.Replace(Environment.NewLine, "\\n")}"); From b37b1f6ad8d7db3a323899a89dc6ad2780ae7c0f Mon Sep 17 00:00:00 2001 From: Stuart Lang Date: Thu, 6 Feb 2025 01:59:28 +0000 Subject: [PATCH 2/4] Fix tests --- .../Initialisation/BuildAnalyzerTestsBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/BuildAnalyzerTestsBase.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/BuildAnalyzerTestsBase.cs index 1444a9e82..b51a844ef 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/BuildAnalyzerTestsBase.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/BuildAnalyzerTestsBase.cs @@ -253,6 +253,7 @@ internal Mock BuildProjectAnalyzerMock(string csprojPathName, projectAnalyzerMock.Setup(x => x.Build(It.IsAny())).Returns(sourceProjectAnalyzerResultsMock); projectAnalyzerMock.Setup(x => x.Build(It.IsAny(), It.IsAny())).Returns(sourceProjectAnalyzerResultsMock); projectAnalyzerMock.Setup(x => x.Build(It.IsAny())).Returns(sourceProjectAnalyzerResultsMock); + projectAnalyzerMock.Setup(x => x.Build(It.IsAny())).Returns(sourceProjectAnalyzerResultsMock); projectAnalyzerMock.Setup(x => x.Build()).Returns(sourceProjectAnalyzerResultsMock); projectAnalyzerMock.Setup(x => x.ProjectFile).Returns(projectFileMock.Object); From b25dee5a538c5a1aa820239d80e50ed0e91fdb48 Mon Sep 17 00:00:00 2001 From: Stuart Lang Date: Thu, 6 Feb 2025 11:19:20 +0000 Subject: [PATCH 3/4] Determin closest matching TFM --- .../Initialisation/InputFileResolver.cs | 58 +++++++++++++++++-- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs b/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs index 58be310b3..9f6ce34d5 100644 --- a/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs +++ b/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs @@ -9,6 +9,7 @@ using Buildalyzer; using Buildalyzer.Environment; using Microsoft.Extensions.Logging; +using NuGet.Frameworks; using Stryker.Abstractions.Exceptions; using Stryker.Abstractions.Logging; using Stryker.Abstractions.Options; @@ -183,7 +184,7 @@ private IEnumerable GetProjectAndAddIt(IStrykerOptions options, ConcurrentBag<(IEnumerable result, bool isTest)> mutableProjectsAnalyzerResults) { var project = manager.GetProject(entry.projectFile); - IEnumerable buildResult = AnalyzeSingleProject(project, options); + IEnumerable buildResult = AnalyzeSingleProject(project, options, entry.framework); if (!buildResult.Any()) { // analysis failed @@ -237,7 +238,7 @@ private List ScanReferences(ScanMode mode, IEnumerable return referencesToAdd; } - private IAnalyzerResults AnalyzeSingleProject(IProjectAnalyzer project, IStrykerOptions options) + private IAnalyzerResults AnalyzeSingleProject(IProjectAnalyzer project, IStrykerOptions options, string targetFramework = null) { if (options.DevMode) { @@ -246,7 +247,18 @@ private IAnalyzerResults AnalyzeSingleProject(IProjectAnalyzer project, IStryker } var projectLogName = Path.GetRelativePath(options.WorkingDirectory, project.ProjectFile.Path); _logger.LogDebug("Analyzing {ProjectFilePath}", projectLogName); - var buildResult = project.Build(options.TargetFramework); + + IAnalyzerResults buildResult; + string bestFramework = null; + if (targetFramework is not null) + { + bestFramework = DetermineBestTargetFramework(project, targetFramework); + buildResult = project.Build(bestFramework); + } + else + { + buildResult = project.Build(); + } var buildResultOverallSuccess = buildResult.OverallSuccess || Array. TrueForAll(project.ProjectFile.TargetFrameworks, tf => @@ -273,7 +285,18 @@ private IAnalyzerResults AnalyzeSingleProject(IProjectAnalyzer project, IStryker Restore = true }; // retry the analysis - buildResult = project.Build(buildOptions); + if (targetFramework is not null) + { + if (bestFramework is null) + { + bestFramework = DetermineBestTargetFramework(project, targetFramework); + } + buildResult = project.Build(bestFramework, buildOptions); + } + else + { + buildResult = project.Build(buildOptions); + } // check the new status buildResultOverallSuccess = Array.TrueForAll(project.ProjectFile.TargetFrameworks, tf => @@ -302,6 +325,33 @@ private IAnalyzerResults AnalyzeSingleProject(IProjectAnalyzer project, IStryker return buildResult.All(br => !IsValid(br)) ? new AnalyzerResults() : buildResult; } + private string DetermineBestTargetFramework(IProjectAnalyzer project, string targetFramework) + { + var targetFrameworks = project.ProjectFile.TargetFrameworks; + + if (targetFrameworks.Contains(targetFramework)) + { + return targetFramework; + } + + var reducer = new FrameworkReducer(); + var framework = NuGetFramework.Parse(targetFramework); + var availableFrameworks = targetFrameworks.Select(NuGetFramework.Parse).ToList(); + + var nearest = reducer.GetNearest(framework, availableFrameworks); + + if (nearest is null) + { + throw new InputException($"Could not find any compatible target frameworks for project '{project.ProjectFile.Path}' that are compatible with '{targetFramework}'."); + } + + var bestFramework = nearest.GetShortFolderName(); + + _logger.LogWarning($"Target framework '{targetFramework}' not found in project '{project.ProjectFile.Path}'. Using '{bestFramework}' instead."); + + return bestFramework; + } + private void LogAnalyzerResult(IAnalyzerResults analyzerResults, IStrykerOptions options) { // do not log if trace is not enabled From dfc6de266cb6e672448656e11a4fec7b62ecf476 Mon Sep 17 00:00:00 2001 From: Stuart Lang Date: Thu, 6 Feb 2025 11:44:23 +0000 Subject: [PATCH 4/4] Be more permissive --- .../Initialisation/BuildAnalyzerTestsBase.cs | 1 + .../Initialisation/InputFileResolverTests.cs | 2 +- .../Initialisation/InputFileResolver.cs | 26 +++++-------------- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/BuildAnalyzerTestsBase.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/BuildAnalyzerTestsBase.cs index b51a844ef..0f322e9a3 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/BuildAnalyzerTestsBase.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/BuildAnalyzerTestsBase.cs @@ -254,6 +254,7 @@ internal Mock BuildProjectAnalyzerMock(string csprojPathName, projectAnalyzerMock.Setup(x => x.Build(It.IsAny(), It.IsAny())).Returns(sourceProjectAnalyzerResultsMock); projectAnalyzerMock.Setup(x => x.Build(It.IsAny())).Returns(sourceProjectAnalyzerResultsMock); projectAnalyzerMock.Setup(x => x.Build(It.IsAny())).Returns(sourceProjectAnalyzerResultsMock); + projectAnalyzerMock.Setup(x => x.Build(It.IsAny(), It.IsAny())).Returns(sourceProjectAnalyzerResultsMock); projectAnalyzerMock.Setup(x => x.Build()).Returns(sourceProjectAnalyzerResultsMock); projectAnalyzerMock.Setup(x => x.ProjectFile).Returns(projectFileMock.Object); diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InputFileResolverTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InputFileResolverTests.cs index 44f948355..3f706ab05 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InputFileResolverTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InputFileResolverTests.cs @@ -908,7 +908,7 @@ public void ShouldSelectFrameworkBasedOnTestProject(string testFrameworks, strin var testProjectPath = Path.Combine(_sourcePath, "TestProjectFolder", "TestProject.csproj"); var sourceProjectPath = Path.Combine(_sourcePath, "ExampleProject", "ExampleProject.csproj"); var sourceProjectNameFilter = "ExampleProject.csproj"; - + var fileSystem = new MockFileSystem(new Dictionary { { sourceProjectPath, new MockFileData(_defaultTestProjectFileContents)}, diff --git a/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs b/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs index 9f6ce34d5..af4d0a6ff 100644 --- a/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs +++ b/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs @@ -248,18 +248,14 @@ private IAnalyzerResults AnalyzeSingleProject(IProjectAnalyzer project, IStryker var projectLogName = Path.GetRelativePath(options.WorkingDirectory, project.ProjectFile.Path); _logger.LogDebug("Analyzing {ProjectFilePath}", projectLogName); - IAnalyzerResults buildResult; string bestFramework = null; if (targetFramework is not null) { bestFramework = DetermineBestTargetFramework(project, targetFramework); - buildResult = project.Build(bestFramework); - } - else - { - buildResult = project.Build(); } + var buildResult = project.Build(bestFramework!); + var buildResultOverallSuccess = buildResult.OverallSuccess || Array. TrueForAll(project.ProjectFile.TargetFrameworks, tf => buildResult.Any(br => IsValid(br) && br.TargetFramework == tf)); @@ -285,18 +281,7 @@ private IAnalyzerResults AnalyzeSingleProject(IProjectAnalyzer project, IStryker Restore = true }; // retry the analysis - if (targetFramework is not null) - { - if (bestFramework is null) - { - bestFramework = DetermineBestTargetFramework(project, targetFramework); - } - buildResult = project.Build(bestFramework, buildOptions); - } - else - { - buildResult = project.Build(buildOptions); - } + buildResult = project.Build(bestFramework, buildOptions); // check the new status buildResultOverallSuccess = Array.TrueForAll(project.ProjectFile.TargetFrameworks, tf => @@ -342,12 +327,13 @@ private string DetermineBestTargetFramework(IProjectAnalyzer project, string tar if (nearest is null) { - throw new InputException($"Could not find any compatible target frameworks for project '{project.ProjectFile.Path}' that are compatible with '{targetFramework}'."); + _logger.LogWarning("Could not find any compatible target frameworks for project '{ProjectFilePath}' that are compatible with '{TargetFramework}'.", project.ProjectFile.Path, targetFramework); + return null; } var bestFramework = nearest.GetShortFolderName(); - _logger.LogWarning($"Target framework '{targetFramework}' not found in project '{project.ProjectFile.Path}'. Using '{bestFramework}' instead."); + _logger.LogInformation("Target framework '{TargetFramework}' not found in project '{ProjectFilePath}'. Using '{BestFramework}' instead.", targetFramework, project.ProjectFile.Path, bestFramework); return bestFramework; }