2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
4
using System . Diagnostics ;
5
+ using Microsoft . Build . Definition ;
5
6
using Microsoft . Build . Evaluation ;
7
+ using Microsoft . Build . Evaluation . Context ;
6
8
using Microsoft . Build . Execution ;
7
- using Microsoft . Build . Framework ;
8
9
using Microsoft . DotNet . Cli . Commands . Run ;
9
10
using Microsoft . DotNet . Cli . Commands . Run . LaunchSettings ;
10
11
using Microsoft . DotNet . Cli . Utils ;
@@ -14,7 +15,7 @@ namespace Microsoft.DotNet.Cli.Commands.Test;
14
15
15
16
internal static class SolutionAndProjectUtility
16
17
{
17
- private static readonly string s_computeRunArgumentsTarget = " ComputeRunArguments" ;
18
+ private static readonly string [ ] s_computeRunArgumentsTarget = [ Constants . ComputeRunArguments ] ;
18
19
private static readonly Lock s_buildLock = new ( ) ;
19
20
20
21
public static ( bool SolutionOrProjectFileFound , string Message ) TryGetProjectOrSolutionFilePath ( string directory , out string projectOrSolutionFilePath , out bool isSolution )
@@ -176,18 +177,36 @@ private static string[] GetSolutionFilterFilePaths(string directory)
176
177
177
178
private static string [ ] GetProjectFilePaths ( string directory ) => Directory . GetFiles ( directory , CliConstants . ProjectExtensionPattern , SearchOption . TopDirectoryOnly ) ;
178
179
179
- private static ProjectInstance EvaluateProject ( ProjectCollection collection , string projectFilePath , string ? tfm )
180
+ private static ProjectInstance EvaluateProject ( ProjectCollection collection , EvaluationContext evaluationContext , string projectFilePath , string ? tfm )
180
181
{
181
182
Debug . Assert ( projectFilePath is not null ) ;
182
183
183
- var project = collection . LoadProject ( projectFilePath ) ;
184
+ Dictionary < string , string > ? globalProperties = null ;
184
185
if ( tfm is not null )
185
186
{
186
- project . SetGlobalProperty ( ProjectProperties . TargetFramework , tfm ) ;
187
- project . ReevaluateIfNecessary ( ) ;
187
+ globalProperties = new Dictionary < string , string > ( capacity : 1 )
188
+ {
189
+ { ProjectProperties . TargetFramework , tfm }
190
+ } ;
188
191
}
189
192
190
- return project . CreateProjectInstance ( ) ;
193
+ // Merge the global properties from the project collection.
194
+ // It's unclear why MSBuild isn't considering the global properties defined in the ProjectCollection when
195
+ // the collection is passed in ProjectOptions below.
196
+ foreach ( var property in collection . GlobalProperties )
197
+ {
198
+ if ( ! ( globalProperties ??= new Dictionary < string , string > ( ) ) . ContainsKey ( property . Key ) )
199
+ {
200
+ globalProperties . Add ( property . Key , property . Value ) ;
201
+ }
202
+ }
203
+
204
+ return ProjectInstance . FromFile ( projectFilePath , new ProjectOptions
205
+ {
206
+ GlobalProperties = globalProperties ,
207
+ EvaluationContext = evaluationContext ,
208
+ ProjectCollection = collection ,
209
+ } ) ;
191
210
}
192
211
193
212
public static string GetRootDirectory ( string solutionOrProjectFilePath )
@@ -197,10 +216,10 @@ public static string GetRootDirectory(string solutionOrProjectFilePath)
197
216
return string . IsNullOrEmpty ( fileDirectory ) ? Directory . GetCurrentDirectory ( ) : fileDirectory ;
198
217
}
199
218
200
- public static IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > GetProjectProperties ( string projectFilePath , ProjectCollection projectCollection , BuildOptions buildOptions )
219
+ public static IEnumerable < ParallelizableTestModuleGroupWithSequentialInnerModules > GetProjectProperties ( string projectFilePath , ProjectCollection projectCollection , EvaluationContext evaluationContext , BuildOptions buildOptions )
201
220
{
202
221
var projects = new List < ParallelizableTestModuleGroupWithSequentialInnerModules > ( ) ;
203
- ProjectInstance projectInstance = EvaluateProject ( projectCollection , projectFilePath , null ) ;
222
+ ProjectInstance projectInstance = EvaluateProject ( projectCollection , evaluationContext , projectFilePath , null ) ;
204
223
205
224
var targetFramework = projectInstance . GetPropertyValue ( ProjectProperties . TargetFramework ) ;
206
225
var targetFrameworks = projectInstance . GetPropertyValue ( ProjectProperties . TargetFrameworks ) ;
@@ -209,7 +228,7 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
209
228
210
229
if ( ! string . IsNullOrEmpty ( targetFramework ) || string . IsNullOrEmpty ( targetFrameworks ) )
211
230
{
212
- if ( GetModuleFromProject ( projectInstance , projectCollection . Loggers , buildOptions ) is { } module )
231
+ if ( GetModuleFromProject ( projectInstance , buildOptions ) is { } module )
213
232
{
214
233
projects . Add ( new ParallelizableTestModuleGroupWithSequentialInnerModules ( module ) ) ;
215
234
}
@@ -234,10 +253,10 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
234
253
{
235
254
foreach ( var framework in frameworks )
236
255
{
237
- projectInstance = EvaluateProject ( projectCollection , projectFilePath , framework ) ;
256
+ projectInstance = EvaluateProject ( projectCollection , evaluationContext , projectFilePath , framework ) ;
238
257
Logger . LogTrace ( $ "Loaded inner project '{ Path . GetFileName ( projectFilePath ) } ' has '{ ProjectProperties . IsTestingPlatformApplication } ' = '{ projectInstance . GetPropertyValue ( ProjectProperties . IsTestingPlatformApplication ) } ' (TFM: '{ framework } ').") ;
239
258
240
- if ( GetModuleFromProject ( projectInstance , projectCollection . Loggers , buildOptions ) is { } module )
259
+ if ( GetModuleFromProject ( projectInstance , buildOptions ) is { } module )
241
260
{
242
261
projects . Add ( new ParallelizableTestModuleGroupWithSequentialInnerModules ( module ) ) ;
243
262
}
@@ -248,10 +267,10 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
248
267
List < TestModule > ? innerModules = null ;
249
268
foreach ( var framework in frameworks )
250
269
{
251
- projectInstance = EvaluateProject ( projectCollection , projectFilePath , framework ) ;
270
+ projectInstance = EvaluateProject ( projectCollection , evaluationContext , projectFilePath , framework ) ;
252
271
Logger . LogTrace ( $ "Loaded inner project '{ Path . GetFileName ( projectFilePath ) } ' has '{ ProjectProperties . IsTestingPlatformApplication } ' = '{ projectInstance . GetPropertyValue ( ProjectProperties . IsTestingPlatformApplication ) } ' (TFM: '{ framework } ').") ;
253
272
254
- if ( GetModuleFromProject ( projectInstance , projectCollection . Loggers , buildOptions ) is { } module )
273
+ if ( GetModuleFromProject ( projectInstance , buildOptions ) is { } module )
255
274
{
256
275
innerModules ??= new List < TestModule > ( ) ;
257
276
innerModules . Add ( module ) ;
@@ -268,7 +287,7 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
268
287
return projects ;
269
288
}
270
289
271
- private static TestModule ? GetModuleFromProject ( ProjectInstance project , ICollection < ILogger > ? loggers , BuildOptions buildOptions )
290
+ private static TestModule ? GetModuleFromProject ( ProjectInstance project , BuildOptions buildOptions )
272
291
{
273
292
_ = bool . TryParse ( project . GetPropertyValue ( ProjectProperties . IsTestProject ) , out bool isTestProject ) ;
274
293
_ = bool . TryParse ( project . GetPropertyValue ( ProjectProperties . IsTestingPlatformApplication ) , out bool isTestingPlatformApplication ) ;
@@ -286,7 +305,7 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
286
305
RunProperties runProperties ;
287
306
if ( isTestingPlatformApplication )
288
307
{
289
- runProperties = GetRunProperties ( project , loggers ) ;
308
+ runProperties = GetRunProperties ( project ) ;
290
309
291
310
// dotnet run throws the same if RunCommand is null or empty.
292
311
// In dotnet test, we are additionally checking that RunCommand is not dll.
@@ -326,7 +345,7 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
326
345
327
346
return new TestModule ( runProperties , PathUtility . FixFilePath ( projectFullPath ) , targetFramework , isTestingPlatformApplication , launchSettings , project . GetPropertyValue ( ProjectProperties . TargetPath ) , rootVariableName ) ;
328
347
329
- static RunProperties GetRunProperties ( ProjectInstance project , ICollection < ILogger > ? loggers )
348
+ static RunProperties GetRunProperties ( ProjectInstance project )
330
349
{
331
350
// Build API cannot be called in parallel, even if the projects are different.
332
351
// Otherwise, BuildManager in MSBuild will fail:
@@ -336,7 +355,7 @@ static RunProperties GetRunProperties(ProjectInstance project, ICollection<ILogg
336
355
{
337
356
if ( ! project . Build ( s_computeRunArgumentsTarget , loggers : null ) )
338
357
{
339
- throw new GracefulException ( CliCommandStrings . RunCommandEvaluationExceptionBuildFailed , s_computeRunArgumentsTarget ) ;
358
+ throw new GracefulException ( CliCommandStrings . RunCommandEvaluationExceptionBuildFailed , s_computeRunArgumentsTarget [ 0 ] ) ;
340
359
}
341
360
}
342
361
0 commit comments