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 ;
@@ -19,28 +21,55 @@ internal static class MSBuildUtility
1921{
2022 private const string dotnetTestVerb = "dotnet-test" ;
2123
24+ // Related: https://github.com/dotnet/msbuild/pull/7992
25+ // Related: https://github.com/dotnet/msbuild/issues/12711
26+ [ UnsafeAccessor ( UnsafeAccessorKind . Method , Name = "ProjectShouldBuild" ) ]
27+ static extern bool ProjectShouldBuild ( SolutionFile solutionFile , string projectFile ) ;
28+
2229 public static ( IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > Projects , bool IsBuiltOrRestored ) GetProjectsFromSolution ( string solutionFilePath , BuildOptions buildOptions )
2330 {
24- SolutionModel solutionModel = SlnFileFactory . CreateFromFileOrDirectory ( solutionFilePath , includeSolutionFilterFiles : true , includeSolutionXmlFiles : true ) ;
25-
2631 bool isBuiltOrRestored = BuildOrRestoreProjectOrSolution ( solutionFilePath , buildOptions ) ;
2732
2833 if ( ! isBuiltOrRestored )
2934 {
3035 return ( Array . Empty < ParallelizableTestModuleGroupWithSequentialInnerModules > ( ) , isBuiltOrRestored ) ;
3136 }
3237
33- string rootDirectory = solutionFilePath . HasExtension ( ".slnf" ) ?
34- Path . GetDirectoryName ( solutionModel . Description ) ! :
35- SolutionAndProjectUtility . GetRootDirectory ( solutionFilePath ) ;
38+ var msbuildArgs = MSBuildArgs . AnalyzeMSBuildArguments ( buildOptions . MSBuildArgs , CommonOptions . PropertiesOption , CommonOptions . RestorePropertiesOption , CommonOptions . MSBuildTargetOption ( ) , CommonOptions . VerbosityOption ( ) , CommonOptions . NoLogoOption ( ) ) ;
39+ var solutionFile = SolutionFile . Parse ( Path . GetFullPath ( solutionFilePath ) ) ;
40+ var globalProperties = CommonRunHelpers . GetGlobalPropertiesFromArgs ( msbuildArgs ) ;
3641
37- FacadeLogger ? logger = LoggerUtility . DetermineBinlogger ( [ .. buildOptions . MSBuildArgs ] , dotnetTestVerb ) ;
42+ globalProperties . TryGetValue ( "Configuration" , out var activeSolutionConfiguration ) ;
43+ globalProperties . TryGetValue ( "Platform" , out var activeSolutionPlatform ) ;
3844
39- var msbuildArgs = MSBuildArgs . AnalyzeMSBuildArguments ( buildOptions . MSBuildArgs , CommonOptions . PropertiesOption , CommonOptions . RestorePropertiesOption , CommonOptions . MSBuildTargetOption ( ) , CommonOptions . VerbosityOption ( ) , CommonOptions . NoLogoOption ( ) ) ;
45+ if ( string . IsNullOrEmpty ( activeSolutionConfiguration ) )
46+ {
47+ activeSolutionConfiguration = solutionFile . GetDefaultConfigurationName ( ) ;
48+ }
49+
50+ if ( string . IsNullOrEmpty ( activeSolutionPlatform ) )
51+ {
52+ activeSolutionPlatform = solutionFile . GetDefaultPlatformName ( ) ;
53+ }
54+
55+ var solutionConfiguration = solutionFile . SolutionConfigurations . FirstOrDefault ( c => activeSolutionConfiguration . Equals ( c . ConfigurationName , StringComparison . OrdinalIgnoreCase ) && activeSolutionPlatform . Equals ( c . PlatformName , StringComparison . OrdinalIgnoreCase ) )
56+ ?? throw new InvalidOperationException ( $ "The solution configuration '{ activeSolutionConfiguration } |{ activeSolutionPlatform } ' is invalid.") ;
57+
58+ // Note: MSBuild seems to be special casing web projects specifically.
59+ // https://github.com/dotnet/msbuild/blob/243fb764b25affe8cc5f233001ead3b5742a297e/src/Build/Construction/Solution/SolutionProjectGenerator.cs#L659-L672
60+ // There is no interest to duplicate this workaround here in test command, unless MSBuild provides a public API that does it.
61+ // https://github.com/dotnet/msbuild/issues/12711 tracks having a better public API.
62+ var projectPaths = solutionFile . ProjectsInOrder
63+ . Where ( p => ProjectShouldBuild ( solutionFile , p . RelativePath ) && p . ProjectConfigurations . ContainsKey ( solutionConfiguration . FullName ) )
64+ . Select ( p => ( p . ProjectConfigurations [ solutionConfiguration . FullName ] , p . AbsolutePath ) )
65+ . Where ( p => p . Item1 . IncludeInBuild )
66+ . Select ( p => ( p . AbsolutePath , ( string ? ) p . Item1 . ConfigurationName , ( string ? ) p . Item1 . PlatformName ) ) ;
67+
68+ FacadeLogger ? logger = LoggerUtility . DetermineBinlogger ( [ .. buildOptions . MSBuildArgs ] , dotnetTestVerb ) ;
4069
41- using var collection = new ProjectCollection ( globalProperties : CommonRunHelpers . GetGlobalPropertiesFromArgs ( msbuildArgs ) , loggers : logger is null ? null : [ logger ] , toolsetDefinitionLocations : ToolsetDefinitionLocations . Default ) ;
70+ using var collection = new ProjectCollection ( globalProperties , loggers : logger is null ? null : [ logger ] , toolsetDefinitionLocations : ToolsetDefinitionLocations . Default ) ;
4271 var evaluationContext = EvaluationContext . Create ( EvaluationContext . SharingPolicy . Shared ) ;
43- ConcurrentBag < ParallelizableTestModuleGroupWithSequentialInnerModules > projects = GetProjectsProperties ( collection , evaluationContext , solutionModel . SolutionProjects . Select ( p => Path . Combine ( rootDirectory , p . FilePath ) ) , buildOptions ) ;
72+ ConcurrentBag < ParallelizableTestModuleGroupWithSequentialInnerModules > projects = GetProjectsProperties ( collection , evaluationContext , projectPaths , buildOptions ) ;
4473 logger ? . ReallyShutdown ( ) ;
4574 collection . UnloadAllProjects ( ) ;
4675
@@ -62,7 +91,7 @@ public static (IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModul
6291
6392 using var collection = new ProjectCollection ( globalProperties : CommonRunHelpers . GetGlobalPropertiesFromArgs ( msbuildArgs ) , logger is null ? null : [ logger ] , toolsetDefinitionLocations : ToolsetDefinitionLocations . Default ) ;
6493 var evaluationContext = EvaluationContext . Create ( EvaluationContext . SharingPolicy . Shared ) ;
65- IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > projects = SolutionAndProjectUtility . GetProjectProperties ( projectFilePath , collection , evaluationContext , buildOptions ) ;
94+ IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > projects = SolutionAndProjectUtility . GetProjectProperties ( projectFilePath , collection , evaluationContext , buildOptions , configuration : null , platform : null ) ;
6695 logger ? . ReallyShutdown ( ) ;
6796 collection . UnloadAllProjects ( ) ;
6897 return ( projects , isBuiltOrRestored ) ;
@@ -131,7 +160,11 @@ private static bool BuildOrRestoreProjectOrSolution(string filePath, BuildOption
131160 return result == ( int ) BuildResultCode . Success ;
132161 }
133162
134- private static ConcurrentBag < ParallelizableTestModuleGroupWithSequentialInnerModules > GetProjectsProperties ( ProjectCollection projectCollection , EvaluationContext evaluationContext , IEnumerable < string > projects , BuildOptions buildOptions )
163+ private static ConcurrentBag < ParallelizableTestModuleGroupWithSequentialInnerModules > GetProjectsProperties (
164+ ProjectCollection projectCollection ,
165+ EvaluationContext evaluationContext ,
166+ IEnumerable < ( string ProjectFilePath , string ? Configuration , string ? Platform ) > projects ,
167+ BuildOptions buildOptions )
135168 {
136169 var allProjects = new ConcurrentBag < ParallelizableTestModuleGroupWithSequentialInnerModules > ( ) ;
137170
@@ -142,7 +175,7 @@ private static ConcurrentBag<ParallelizableTestModuleGroupWithSequentialInnerMod
142175 new ParallelOptions { MaxDegreeOfParallelism = Environment . ProcessorCount } ,
143176 ( project ) =>
144177 {
145- IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > projectsMetadata = SolutionAndProjectUtility . GetProjectProperties ( project , projectCollection , evaluationContext , buildOptions ) ;
178+ IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > projectsMetadata = SolutionAndProjectUtility . GetProjectProperties ( project . ProjectFilePath , projectCollection , evaluationContext , buildOptions , project . Configuration , project . Platform ) ;
146179 foreach ( var projectMetadata in projectsMetadata )
147180 {
148181 allProjects . Add ( projectMetadata ) ;
0 commit comments