33
44using System . Collections . Concurrent ;
55using System . CommandLine ;
6+ using System . Runtime . CompilerServices ;
7+ using Microsoft . Build . Construction ;
68using Microsoft . Build . Evaluation ;
79using Microsoft . Build . Evaluation . Context ;
810using Microsoft . Build . Execution ;
@@ -18,28 +20,55 @@ internal static class MSBuildUtility
1820{
1921 private const string dotnetTestVerb = "dotnet-test" ;
2022
23+ // Related: https://github.com/dotnet/msbuild/pull/7992
24+ // Related: https://github.com/dotnet/msbuild/issues/12711
25+ [ UnsafeAccessor ( UnsafeAccessorKind . Method , Name = "ProjectShouldBuild" ) ]
26+ static extern bool ProjectShouldBuild ( SolutionFile solutionFile , string projectFile ) ;
27+
2128 public static ( IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > Projects , bool IsBuiltOrRestored ) GetProjectsFromSolution ( string solutionFilePath , BuildOptions buildOptions )
2229 {
23- SolutionModel solutionModel = SlnFileFactory . CreateFromFileOrDirectory ( solutionFilePath , includeSolutionFilterFiles : true , includeSolutionXmlFiles : true ) ;
24-
2530 bool isBuiltOrRestored = BuildOrRestoreProjectOrSolution ( solutionFilePath , buildOptions ) ;
2631
2732 if ( ! isBuiltOrRestored )
2833 {
2934 return ( Array . Empty < ParallelizableTestModuleGroupWithSequentialInnerModules > ( ) , isBuiltOrRestored ) ;
3035 }
3136
32- string rootDirectory = solutionFilePath . HasExtension ( ".slnf" ) ?
33- Path . GetDirectoryName ( solutionModel . Description ) ! :
34- SolutionAndProjectUtility . GetRootDirectory ( solutionFilePath ) ;
37+ var msbuildArgs = MSBuildArgs . AnalyzeMSBuildArguments ( buildOptions . MSBuildArgs , CommonOptions . PropertiesOption , CommonOptions . RestorePropertiesOption , CommonOptions . MSBuildTargetOption ( ) , CommonOptions . VerbosityOption ( ) ) ;
38+ var solutionFile = SolutionFile . Parse ( Path . GetFullPath ( solutionFilePath ) ) ;
39+ var globalProperties = CommonRunHelpers . GetGlobalPropertiesFromArgs ( msbuildArgs ) ;
3540
36- FacadeLogger ? logger = LoggerUtility . DetermineBinlogger ( [ .. buildOptions . MSBuildArgs ] , dotnetTestVerb ) ;
41+ globalProperties . TryGetValue ( "Configuration" , out var activeSolutionConfiguration ) ;
42+ globalProperties . TryGetValue ( "Platform" , out var activeSolutionPlatform ) ;
3743
38- var msbuildArgs = MSBuildArgs . AnalyzeMSBuildArguments ( buildOptions . MSBuildArgs , CommonOptions . PropertiesOption , CommonOptions . RestorePropertiesOption , CommonOptions . MSBuildTargetOption ( ) , CommonOptions . VerbosityOption ( ) ) ;
44+ if ( string . IsNullOrEmpty ( activeSolutionConfiguration ) )
45+ {
46+ activeSolutionConfiguration = solutionFile . GetDefaultConfigurationName ( ) ;
47+ }
48+
49+ if ( string . IsNullOrEmpty ( activeSolutionPlatform ) )
50+ {
51+ activeSolutionPlatform = solutionFile . GetDefaultPlatformName ( ) ;
52+ }
53+
54+ var solutionConfiguration = solutionFile . SolutionConfigurations . FirstOrDefault ( c => activeSolutionConfiguration . Equals ( c . ConfigurationName , StringComparison . OrdinalIgnoreCase ) && activeSolutionPlatform . Equals ( c . PlatformName , StringComparison . OrdinalIgnoreCase ) )
55+ ?? throw new InvalidOperationException ( $ "The solution configuration '{ activeSolutionConfiguration } |{ activeSolutionPlatform } ' is invalid.") ;
56+
57+ // Note: MSBuild seems to be special casing web projects specifically.
58+ // https://github.com/dotnet/msbuild/blob/243fb764b25affe8cc5f233001ead3b5742a297e/src/Build/Construction/Solution/SolutionProjectGenerator.cs#L659-L672
59+ // There is no interest to duplicate this workaround here in test command, unless MSBuild provides a public API that does it.
60+ // https://github.com/dotnet/msbuild/issues/12711 tracks having a better public API.
61+ var projectPaths = solutionFile . ProjectsInOrder
62+ . Where ( p => ProjectShouldBuild ( solutionFile , p . RelativePath ) && p . ProjectConfigurations . ContainsKey ( solutionConfiguration . FullName ) )
63+ . Select ( p => ( p . ProjectConfigurations [ solutionConfiguration . FullName ] , p . AbsolutePath ) )
64+ . Where ( p => p . Item1 . IncludeInBuild )
65+ . Select ( p => ( p . AbsolutePath , ( string ? ) p . Item1 . ConfigurationName , ( string ? ) p . Item1 . PlatformName ) ) ;
66+
67+ FacadeLogger ? logger = LoggerUtility . DetermineBinlogger ( [ .. buildOptions . MSBuildArgs ] , dotnetTestVerb ) ;
3968
40- using var collection = new ProjectCollection ( globalProperties : CommonRunHelpers . GetGlobalPropertiesFromArgs ( msbuildArgs ) , loggers : logger is null ? null : [ logger ] , toolsetDefinitionLocations : ToolsetDefinitionLocations . Default ) ;
69+ using var collection = new ProjectCollection ( globalProperties , loggers : logger is null ? null : [ logger ] , toolsetDefinitionLocations : ToolsetDefinitionLocations . Default ) ;
4170 var evaluationContext = EvaluationContext . Create ( EvaluationContext . SharingPolicy . Shared ) ;
42- ConcurrentBag < ParallelizableTestModuleGroupWithSequentialInnerModules > projects = GetProjectsProperties ( collection , evaluationContext , solutionModel . SolutionProjects . Select ( p => Path . Combine ( rootDirectory , p . FilePath ) ) , buildOptions ) ;
71+ ConcurrentBag < ParallelizableTestModuleGroupWithSequentialInnerModules > projects = GetProjectsProperties ( collection , evaluationContext , projectPaths , buildOptions ) ;
4372 logger ? . ReallyShutdown ( ) ;
4473 collection . UnloadAllProjects ( ) ;
4574
@@ -61,7 +90,7 @@ public static (IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModul
6190
6291 using var collection = new ProjectCollection ( globalProperties : CommonRunHelpers . GetGlobalPropertiesFromArgs ( msbuildArgs ) , logger is null ? null : [ logger ] , toolsetDefinitionLocations : ToolsetDefinitionLocations . Default ) ;
6392 var evaluationContext = EvaluationContext . Create ( EvaluationContext . SharingPolicy . Shared ) ;
64- IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > projects = SolutionAndProjectUtility . GetProjectProperties ( projectFilePath , collection , evaluationContext , buildOptions ) ;
93+ IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > projects = SolutionAndProjectUtility . GetProjectProperties ( projectFilePath , collection , evaluationContext , buildOptions , configuration : null , platform : null ) ;
6594 logger ? . ReallyShutdown ( ) ;
6695 collection . UnloadAllProjects ( ) ;
6796 return ( projects , isBuiltOrRestored ) ;
@@ -130,7 +159,11 @@ private static bool BuildOrRestoreProjectOrSolution(string filePath, BuildOption
130159 return result == ( int ) BuildResultCode . Success ;
131160 }
132161
133- private static ConcurrentBag < ParallelizableTestModuleGroupWithSequentialInnerModules > GetProjectsProperties ( ProjectCollection projectCollection , EvaluationContext evaluationContext , IEnumerable < string > projects , BuildOptions buildOptions )
162+ private static ConcurrentBag < ParallelizableTestModuleGroupWithSequentialInnerModules > GetProjectsProperties (
163+ ProjectCollection projectCollection ,
164+ EvaluationContext evaluationContext ,
165+ IEnumerable < ( string ProjectFilePath , string ? Configuration , string ? Platform ) > projects ,
166+ BuildOptions buildOptions )
134167 {
135168 var allProjects = new ConcurrentBag < ParallelizableTestModuleGroupWithSequentialInnerModules > ( ) ;
136169
@@ -141,7 +174,7 @@ private static ConcurrentBag<ParallelizableTestModuleGroupWithSequentialInnerMod
141174 new ParallelOptions { MaxDegreeOfParallelism = Environment . ProcessorCount } ,
142175 ( project ) =>
143176 {
144- IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > projectsMetadata = SolutionAndProjectUtility . GetProjectProperties ( project , projectCollection , evaluationContext , buildOptions ) ;
177+ IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > projectsMetadata = SolutionAndProjectUtility . GetProjectProperties ( project . ProjectFilePath , projectCollection , evaluationContext , buildOptions , project . Configuration , project . Platform ) ;
145178 foreach ( var projectMetadata in projectsMetadata )
146179 {
147180 allProjects . Add ( projectMetadata ) ;
0 commit comments