1 // Copyright (c) Microsoft. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 4 using System; 5 using System.IO; 6 using System.Reflection; 7 using System.Collections; 8 using Microsoft.Build.Evaluation; 9 using Microsoft.Build.Framework; 10 using Microsoft.Build.Tasks; 11 using Microsoft.Build.Utilities; 12 using System.Text.RegularExpressions; 13 14 using Microsoft.Build.Shared; 15 using Shouldly; 16 using Xunit; 17 18 namespace Microsoft.Build.UnitTests 19 { 20 sealed public class MSBuildTask_Tests : IDisposable 21 { MSBuildTask_Tests()22 public MSBuildTask_Tests() 23 { 24 ProjectCollection.GlobalProjectCollection.UnloadAllProjects(); 25 } 26 Dispose()27 public void Dispose() 28 { 29 ProjectCollection.GlobalProjectCollection.UnloadAllProjects(); 30 } 31 32 /// <summary> 33 /// If we pass in an item spec that is over the max path but it can be normalized down to something under the max path, we should still work and not 34 /// throw a path too long exception 35 /// </summary> 36 [Fact] ProjectItemSpecTooLong()37 public void ProjectItemSpecTooLong() 38 { 39 string currentDirectory = Directory.GetCurrentDirectory(); 40 try 41 { 42 Directory.SetCurrentDirectory(Path.GetTempPath()); 43 44 string tempPath = Path.GetTempPath(); 45 46 string tempProject = ObjectModelHelpers.CreateTempFileOnDisk(@" 47 <Project DefaultTargets=`TargetA; TargetB; TargetC` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 48 49 <Target Name=`TargetA` Outputs=`a1.dll`/> 50 <Target Name=`TargetB` Outputs=`b1.dll; b2.dll`/> 51 <Target Name=`TargetC` Outputs=`@(C_Outputs)`> 52 <CreateItem Include=`c1.dll` AdditionalMetadata=`MSBuildSourceProjectFile=birch; MSBuildSourceTargetName=oak`> 53 <Output ItemName=`C_Outputs` TaskParameter=`Include`/> 54 </CreateItem> 55 </Target> 56 </Project> 57 "); 58 59 string fileName = Path.GetFileName(tempProject); 60 61 string projectFile1 = null; 62 for (int i = 0; i < 250; i++) 63 { 64 projectFile1 += "..\\"; 65 } 66 67 int rootLength = Path.GetPathRoot(tempPath).Length; 68 string tempPathNoRoot = tempPath.Substring(rootLength); 69 70 projectFile1 += Path.Combine(tempPathNoRoot, fileName); 71 try 72 { 73 MSBuild msbuildTask = new MSBuild(); 74 msbuildTask.BuildEngine = new MockEngine(); 75 76 msbuildTask.Projects = new ITaskItem[] { new TaskItem(projectFile1) }; 77 78 bool success = msbuildTask.Execute(); 79 Assert.True(success); // "Build failed. See 'Standard Out' tab for details." 80 } 81 finally 82 { 83 File.Delete(tempProject); 84 } 85 } 86 finally 87 { 88 Directory.SetCurrentDirectory(currentDirectory); 89 } 90 } 91 92 /// <summary> 93 /// Ensure that the MSBuild task tags any output items with two pieces of metadata -- MSBuildSourceProjectFile and 94 /// MSBuildSourceTargetName -- that give an indication of where the items came from. 95 /// </summary> 96 [Fact] OutputItemsAreTaggedWithProjectFileAndTargetName()97 public void OutputItemsAreTaggedWithProjectFileAndTargetName() 98 { 99 string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 100 <Project DefaultTargets=`TargetA; TargetB; TargetC` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 101 102 <Target Name=`TargetA` Outputs=`a1.dll`/> 103 <Target Name=`TargetB` Outputs=`b1.dll; b2.dll`/> 104 <Target Name=`TargetC` Outputs=`@(C_Outputs)`> 105 <CreateItem Include=`c1.dll`> 106 <Output ItemName=`C_Outputs` TaskParameter=`Include`/> 107 </CreateItem> 108 </Target> 109 </Project> 110 "); 111 112 string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 113 <Project DefaultTargets=`TargetG; TargetH` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 114 <Target Name=`TargetG` Outputs=`g1.dll; g2.dll`/> 115 <Target Name=`TargetH` Outputs=`h1.dll`/> 116 </Project> 117 "); 118 119 try 120 { 121 MSBuild msbuildTask = new MSBuild(); 122 msbuildTask.BuildEngine = new MockEngine(); 123 124 msbuildTask.Projects = new ITaskItem[] { new TaskItem(projectFile1), new TaskItem(projectFile2) }; 125 126 bool success = msbuildTask.Execute(); 127 Assert.True(success); // "Build failed. See 'Standard Out' tab for details." 128 129 string expectedItemOutputs = string.Format(@" 130 a1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetA 131 b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB 132 b2.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB 133 c1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetC 134 g1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetG 135 g2.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetG 136 h1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetH 137 ", projectFile1, projectFile2); 138 139 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */); 140 } 141 finally 142 { 143 File.Delete(projectFile1); 144 File.Delete(projectFile2); 145 } 146 } 147 148 /// <summary> 149 /// Ensures that it is possible to call the MSBuild task 150 /// with an empty Projects parameter, and it shouldn't error, and it shouldn't try to 151 /// build itself. 152 /// </summary> 153 [Fact] EmptyProjectsParameterResultsInNoop()154 public void EmptyProjectsParameterResultsInNoop() 155 { 156 string projectContents = ObjectModelHelpers.CleanupFileContents(@" 157 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 158 <Target Name=`t` > 159 <MSBuild Projects=` @(empty) ` /> 160 </Target> 161 </Project> 162 "); 163 164 MockLogger logger = new MockLogger(); 165 Project project = ObjectModelHelpers.CreateInMemoryProject(projectContents, logger); 166 167 bool success = project.Build(); 168 Assert.True(success); // "Build failed. See Standard Out tab for details" 169 } 170 171 /// <summary> 172 /// Verifies that nonexistent projects aren't normally skipped 173 /// </summary> 174 [Fact] NormallyDoNotSkipNonexistentProjects()175 public void NormallyDoNotSkipNonexistentProjects() 176 { 177 ObjectModelHelpers.DeleteTempProjectDirectory(); 178 ObjectModelHelpers.CreateFileInTempProjectDirectory( 179 "SkipNonexistentProjectsMain.csproj", 180 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 181 <Target Name=`t` > 182 <MSBuild Projects=`this_project_does_not_exist.csproj` /> 183 </Target> 184 </Project> 185 "); 186 187 MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectFailure(@"SkipNonexistentProjectsMain.csproj"); 188 Assert.True(logger.FullLog.Contains("MSB3202")); // project file not found 189 } 190 191 /// <summary> 192 /// Verifies that nonexistent projects aren't normally skipped 193 /// </summary> 194 [Fact] NormallyDoNotSkipNonexistentProjectsBuildInParallel()195 public void NormallyDoNotSkipNonexistentProjectsBuildInParallel() 196 { 197 ObjectModelHelpers.DeleteTempProjectDirectory(); 198 ObjectModelHelpers.CreateFileInTempProjectDirectory( 199 "SkipNonexistentProjectsMain.csproj", 200 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 201 <Target Name=`t` > 202 <MSBuild Projects=`this_project_does_not_exist.csproj` BuildInParallel=`true`/> 203 </Target> 204 </Project> 205 "); 206 207 MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectFailure(@"SkipNonexistentProjectsMain.csproj"); 208 Assert.Equal(0, logger.WarningCount); 209 Assert.Equal(1, logger.ErrorCount); 210 Assert.True(logger.FullLog.Contains("MSB3202")); // project file not found 211 } 212 213 /// <summary> 214 /// Verifies that nonexistent projects are skipped when requested 215 /// </summary> 216 [Fact] SkipNonexistentProjects()217 public void SkipNonexistentProjects() 218 { 219 ObjectModelHelpers.DeleteTempProjectDirectory(); 220 ObjectModelHelpers.CreateFileInTempProjectDirectory( 221 "SkipNonexistentProjectsMain.csproj", 222 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 223 <Target Name=`t` > 224 <MSBuild Projects=`this_project_does_not_exist.csproj;foo.csproj` SkipNonexistentProjects=`true` /> 225 </Target> 226 </Project> 227 "); 228 229 ObjectModelHelpers.CreateFileInTempProjectDirectory( 230 "foo.csproj", 231 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 232 <Target Name=`t` > 233 <Message Text=`Hello from foo.csproj`/> 234 </Target> 235 </Project> 236 "); 237 238 MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectSuccess(@"SkipNonexistentProjectsMain.csproj"); 239 240 logger.AssertLogContains("Hello from foo.csproj"); 241 Assert.Equal(0, logger.WarningCount); 242 Assert.Equal(0, logger.ErrorCount); 243 Assert.True(logger.FullLog.Contains("this_project_does_not_exist.csproj")); // for the missing project 244 Assert.False(logger.FullLog.Contains("MSB3202")); // project file not found error 245 } 246 247 /// <summary> 248 /// Verifies that nonexistent projects are skipped when requested when building in parallel. 249 /// DDB # 125831 250 /// </summary> 251 [Fact] SkipNonexistentProjectsBuildingInParallel()252 public void SkipNonexistentProjectsBuildingInParallel() 253 { 254 ObjectModelHelpers.DeleteTempProjectDirectory(); 255 ObjectModelHelpers.CreateFileInTempProjectDirectory( 256 "SkipNonexistentProjectsMain.csproj", 257 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 258 <Target Name=`t` > 259 <MSBuild Projects=`this_project_does_not_exist.csproj;foo.csproj` SkipNonexistentProjects=`true` BuildInParallel=`true` /> 260 </Target> 261 </Project> 262 "); 263 264 ObjectModelHelpers.CreateFileInTempProjectDirectory( 265 "foo.csproj", 266 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 267 <Target Name=`t` > 268 <Message Text=`Hello from foo.csproj`/> 269 </Target> 270 </Project> 271 "); 272 273 MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectSuccess(@"SkipNonexistentProjectsMain.csproj"); 274 275 logger.AssertLogContains("Hello from foo.csproj"); 276 Assert.Equal(0, logger.WarningCount); 277 Assert.Equal(0, logger.ErrorCount); 278 Assert.True(logger.FullLog.Contains("this_project_does_not_exist.csproj")); // for the missing project 279 Assert.False(logger.FullLog.Contains("MSB3202")); // project file not found error 280 } 281 282 [Fact] LogErrorWhenBuildingVCProj()283 public void LogErrorWhenBuildingVCProj() 284 { 285 ObjectModelHelpers.DeleteTempProjectDirectory(); 286 ObjectModelHelpers.CreateFileInTempProjectDirectory( 287 "BuildingVCProjMain.csproj", 288 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 289 <Target Name=`t` > 290 <MSBuild Projects=`blah.vcproj;foo.csproj` StopOnFirstFailure=`false` /> 291 </Target> 292 </Project> 293 "); 294 295 ObjectModelHelpers.CreateFileInTempProjectDirectory( 296 "foo.csproj", 297 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 298 <Target Name=`t` > 299 <Message Text=`Hello from foo.csproj`/> 300 </Target> 301 </Project> 302 "); 303 304 ObjectModelHelpers.CreateFileInTempProjectDirectory( 305 "blah.vcproj", 306 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 307 <NotWellFormedMSBuildFormatTag /> 308 <Target Name=`t` > 309 <Message Text=`Hello from blah.vcproj`/> 310 </Target> 311 </Project> 312 "); 313 314 MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectFailure(@"BuildingVCProjMain.csproj"); 315 316 logger.AssertLogContains("Hello from foo.csproj"); 317 Assert.Equal(0, logger.WarningCount); 318 Assert.Equal(1, logger.ErrorCount); 319 Assert.True(logger.FullLog.Contains("MSB3204")); // upgrade to vcxproj needed 320 } 321 322 /// <summary> 323 /// Calling the MSBuild task, passing in a property 324 /// in the Properties parameter that has a special character in its value, such as semicolon. 325 /// However, it's a situation where the project author doesn't have control over the 326 /// property value and so he can't escape it himself. 327 /// </summary> 328 #if RUNTIME_TYPE_NETCORE 329 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/259")] 330 #else 331 [Fact] 332 #endif 333 [Trait("Category", "mono-osx-failing")] PropertyOverridesContainSemicolon()334 public void PropertyOverridesContainSemicolon() 335 { 336 ObjectModelHelpers.DeleteTempProjectDirectory(); 337 338 // ------------------------------------------------------- 339 // ConsoleApplication1.csproj 340 // ------------------------------------------------------- 341 342 // Just a normal console application project. 343 ObjectModelHelpers.CreateFileInTempProjectDirectory( 344 Path.Combine("bug'533'369", "Sub;Dir", "ConsoleApplication1", "ConsoleApplication1.csproj"), @" 345 346 <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 347 <PropertyGroup> 348 <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration> 349 <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform> 350 <OutputType>Exe</OutputType> 351 <AssemblyName>ConsoleApplication1</AssemblyName> 352 </PropertyGroup> 353 <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `> 354 <DebugSymbols>true</DebugSymbols> 355 <DebugType>full</DebugType> 356 <Optimize>false</Optimize> 357 <OutputPath>bin\Debug\</OutputPath> 358 </PropertyGroup> 359 <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Release|AnyCPU' `> 360 <DebugType>pdbonly</DebugType> 361 <Optimize>true</Optimize> 362 <OutputPath>bin\Release\</OutputPath> 363 </PropertyGroup> 364 <ItemGroup> 365 <Reference Include=`System` /> 366 <Reference Include=`System.Data` /> 367 <Reference Include=`System.Xml` /> 368 </ItemGroup> 369 <ItemGroup> 370 <Compile Include=`Program.cs` /> 371 </ItemGroup> 372 <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` /> 373 </Project> 374 "); 375 376 // ------------------------------------------------------- 377 // Program.cs 378 // ------------------------------------------------------- 379 380 // Just a normal console application project. 381 ObjectModelHelpers.CreateFileInTempProjectDirectory( 382 Path.Combine("bug'533'369", "Sub;Dir", "ConsoleApplication1", "Program.cs"), @" 383 using System; 384 385 namespace ConsoleApplication32 386 { 387 class Program 388 { 389 static void Main(string[] args) 390 { 391 Console.WriteLine(`Hello world`); 392 } 393 } 394 } 395 "); 396 397 398 // ------------------------------------------------------- 399 // TeamBuild.proj 400 // ------------------------------------------------------- 401 // Attempts to build the above ConsoleApplication1.csproj by calling the MSBuild task, 402 // and overriding the OutDir property. However, the value being passed into OutDir 403 // is coming from another property which is produced by CreateProperty and has 404 // some special characters in it. 405 ObjectModelHelpers.CreateFileInTempProjectDirectory( 406 Path.Combine("bug'533'369", "Sub;Dir", "TeamBuild.proj"), @" 407 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 408 409 <Target Name=`Build`> 410 411 <CreateProperty Value=`$(MSBuildProjectDirectory)\binaries\`> 412 <Output PropertyName=`MasterOutDir` TaskParameter=`Value`/> 413 </CreateProperty> 414 415 <MSBuild Projects=`ConsoleApplication1\ConsoleApplication1.csproj` 416 Properties=`OutDir=$(MasterOutDir)` 417 Targets=`Rebuild`/> 418 </Target> 419 420 </Project> 421 "); 422 423 ObjectModelHelpers.BuildTempProjectFileExpectSuccess(Path.Combine("bug'533'369", "Sub;Dir", "TeamBuild.proj")); 424 425 ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(Path.Combine("bug'533'369", "Sub;Dir", "binaries", "ConsoleApplication1.exe")); 426 } 427 428 /// <summary> 429 /// Check if passing different global properties via metadata works 430 /// </summary> 431 [Fact] DifferentGlobalPropertiesWithDefault()432 public void DifferentGlobalPropertiesWithDefault() 433 { 434 string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 435 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 436 437 <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyProp)'=='0'`/> 438 <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyProp)'=='1'`/> 439 440 </Project> 441 "); 442 443 string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 444 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 445 <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyProp)'=='0'` /> 446 <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyProp)'=='1'` /> 447 </Project> 448 "); 449 450 try 451 { 452 ITaskItem[] projects = new ITaskItem[] 453 { 454 new TaskItem(projectFile1), 455 new TaskItem(projectFile1), 456 new TaskItem(projectFile2), 457 new TaskItem(projectFile2) 458 }; 459 projects[1].SetMetadata("Properties", "MyProp=1"); 460 projects[3].SetMetadata("Properties", "MyProp=1"); 461 462 MSBuild msbuildTask = new MSBuild(); 463 msbuildTask.BuildEngine = new MockEngine(); 464 msbuildTask.Projects = projects; 465 msbuildTask.Properties = new string[] { "MyProp=0" }; 466 467 bool success = msbuildTask.Execute(); 468 Assert.True(success); // "Build failed. See 'Standard Out' tab for details." 469 470 string expectedItemOutputs = string.Format(@" 471 a1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetA 472 b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB 473 g1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetG 474 h1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetH 475 ", projectFile1, projectFile2); 476 477 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */); 478 } 479 finally 480 { 481 File.Delete(projectFile1); 482 File.Delete(projectFile2); 483 } 484 } 485 486 /// <summary> 487 /// Check if passing different global properties via metadata works 488 /// </summary> 489 [Fact] DifferentGlobalPropertiesWithoutDefault()490 public void DifferentGlobalPropertiesWithoutDefault() 491 { 492 string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 493 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 494 495 <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyProp)'=='0'`/> 496 <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyProp)'=='1'`/> 497 498 </Project> 499 "); 500 501 string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 502 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 503 <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyProp)'=='0'` /> 504 <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyProp)'=='1'` /> 505 </Project> 506 "); 507 508 try 509 { 510 ITaskItem[] projects = new ITaskItem[] 511 { 512 new TaskItem(projectFile1), 513 new TaskItem(projectFile1), 514 new TaskItem(projectFile2), 515 new TaskItem(projectFile2) 516 }; 517 projects[1].SetMetadata("Properties", "MyProp=1"); 518 projects[3].SetMetadata("Properties", "MyProp=1"); 519 520 MSBuild msbuildTask = new MSBuild(); 521 msbuildTask.BuildEngine = new MockEngine(); 522 523 msbuildTask.Projects = projects; 524 525 bool success = msbuildTask.Execute(); 526 Assert.True(success); // "Build failed. See 'Standard Out' tab for details." 527 528 string expectedItemOutputs = string.Format(@" 529 b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB 530 h1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetH 531 ", projectFile1, projectFile2); 532 533 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */); 534 } 535 finally 536 { 537 File.Delete(projectFile1); 538 File.Delete(projectFile2); 539 } 540 } 541 542 543 /// <summary> 544 /// Check if passing different global properties via metadata works 545 /// </summary> 546 [Fact] DifferentGlobalPropertiesWithBlanks()547 public void DifferentGlobalPropertiesWithBlanks() 548 { 549 string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 550 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 551 552 <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyProp)'=='0'`/> 553 <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyProp)'=='1'`/> 554 555 </Project> 556 "); 557 558 string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 559 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 560 <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyProp)'=='0'` /> 561 <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyProp)'=='1'` /> 562 </Project> 563 "); 564 565 try 566 { 567 ITaskItem[] projects = new ITaskItem[] 568 { 569 new TaskItem(projectFile1), 570 new TaskItem(projectFile1), 571 new TaskItem(projectFile2), 572 new TaskItem(projectFile2) 573 }; 574 projects[1].SetMetadata("Properties", ""); 575 projects[3].SetMetadata("Properties", "MyProp=1"); 576 577 MSBuild msbuildTask = new MSBuild(); 578 msbuildTask.BuildEngine = new MockEngine(); 579 580 msbuildTask.Projects = projects; 581 582 bool success = msbuildTask.Execute(); 583 Assert.True(success); // "Build failed. See 'Standard Out' tab for details." 584 585 string expectedItemOutputs = string.Format(@" 586 h1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetH 587 ", projectFile2); 588 589 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */); 590 } 591 finally 592 { 593 File.Delete(projectFile1); 594 File.Delete(projectFile2); 595 } 596 } 597 598 599 /// <summary> 600 /// Check if passing different global properties via metadata works 601 /// </summary> 602 [Fact] DifferentGlobalPropertiesInvalid()603 public void DifferentGlobalPropertiesInvalid() 604 { 605 string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 606 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 607 608 <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyProp)'=='0'`/> 609 <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyProp)'=='1'`/> 610 611 </Project> 612 "); 613 614 string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 615 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 616 <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyProp)'=='0'` /> 617 <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyProp)'=='1'` /> 618 </Project> 619 "); 620 621 try 622 { 623 ITaskItem[] projects = new ITaskItem[] 624 { 625 new TaskItem(projectFile1), 626 new TaskItem(projectFile1), 627 new TaskItem(projectFile2), 628 new TaskItem(projectFile2) 629 }; 630 projects[1].SetMetadata("Properties", "=1"); 631 projects[3].SetMetadata("Properties", "=;1"); 632 633 MSBuild msbuildTask = new MSBuild(); 634 msbuildTask.BuildEngine = new MockEngine(); 635 636 msbuildTask.Projects = projects; 637 638 bool success = msbuildTask.Execute(); 639 Assert.False(success); // "Build succeeded. See 'Standard Out' tab for details." 640 } 641 finally 642 { 643 File.Delete(projectFile1); 644 File.Delete(projectFile2); 645 } 646 } 647 648 /// <summary> 649 /// Check if passing additional global properties via metadata works 650 /// </summary> 651 [Fact] DifferentAdditionalPropertiesWithDefault()652 public void DifferentAdditionalPropertiesWithDefault() 653 { 654 string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 655 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 656 657 <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyPropG)'=='1'`/> 658 <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyPropA)'=='1'`/> 659 660 </Project> 661 "); 662 663 string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 664 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 665 <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyPropG)'=='1'` /> 666 <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyPropA)'=='1'` /> 667 </Project> 668 "); 669 670 try 671 { 672 ITaskItem[] projects = new ITaskItem[] 673 { 674 new TaskItem(projectFile1), 675 new TaskItem(projectFile2) 676 }; 677 projects[0].SetMetadata("AdditionalProperties", "MyPropA=1"); 678 projects[1].SetMetadata("AdditionalProperties", "MyPropA=0"); 679 680 MSBuild msbuildTask = new MSBuild(); 681 msbuildTask.BuildEngine = new MockEngine(); 682 msbuildTask.Properties = new string[] { "MyPropG=1" }; 683 msbuildTask.Projects = projects; 684 685 bool success = msbuildTask.Execute(); 686 Assert.True(success); // "Build failed. See 'Standard Out' tab for details." 687 688 string expectedItemOutputs = string.Format(@" 689 a1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetA 690 b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB 691 g1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetG 692 ", projectFile1, projectFile2); 693 694 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */); 695 } 696 finally 697 { 698 File.Delete(projectFile1); 699 File.Delete(projectFile2); 700 } 701 } 702 703 704 /// <summary> 705 /// Check if passing additional global properties via metadata works 706 /// </summary> 707 [Fact] DifferentAdditionalPropertiesWithGlobalProperties()708 public void DifferentAdditionalPropertiesWithGlobalProperties() 709 { 710 string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 711 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 712 713 <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyPropG)'=='0'`/> 714 <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyPropA)'=='1'`/> 715 716 </Project> 717 "); 718 719 string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 720 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 721 <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyPropG)'=='0'` /> 722 <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyPropA)'=='1'` /> 723 </Project> 724 "); 725 726 try 727 { 728 ITaskItem[] projects = new ITaskItem[] 729 { 730 new TaskItem(projectFile1), 731 new TaskItem(projectFile2) 732 }; 733 734 projects[0].SetMetadata("AdditionalProperties", "MyPropA=1"); 735 projects[1].SetMetadata("AdditionalProperties", "MyPropA=1"); 736 737 projects[0].SetMetadata("Properties", "MyPropG=1"); 738 projects[1].SetMetadata("Properties", "MyPropG=0"); 739 740 MSBuild msbuildTask = new MSBuild(); 741 msbuildTask.BuildEngine = new MockEngine(); 742 msbuildTask.Projects = projects; 743 744 bool success = msbuildTask.Execute(); 745 Assert.True(success); // "Build failed. See 'Standard Out' tab for details." 746 747 string expectedItemOutputs = string.Format(@" 748 b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB 749 g1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetG 750 h1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetH 751 ", projectFile1, projectFile2); 752 753 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */); 754 } 755 finally 756 { 757 File.Delete(projectFile1); 758 File.Delete(projectFile2); 759 } 760 } 761 762 763 /// <summary> 764 /// Check if passing additional global properties via metadata works 765 /// </summary> 766 [Fact] DifferentAdditionalPropertiesWithoutDefault()767 public void DifferentAdditionalPropertiesWithoutDefault() 768 { 769 string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 770 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 771 772 <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyPropG)'=='1'`/> 773 <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyPropA)'=='1'`/> 774 775 </Project> 776 "); 777 778 string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 779 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 780 <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyPropG)'=='1'` /> 781 <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyPropA)'=='1'` /> 782 </Project> 783 "); 784 785 try 786 { 787 ITaskItem[] projects = new ITaskItem[] 788 { 789 new TaskItem(projectFile1), 790 new TaskItem(projectFile2) 791 }; 792 793 projects[0].SetMetadata("AdditionalProperties", "MyPropA=1"); 794 projects[1].SetMetadata("AdditionalProperties", "MyPropA=1"); 795 796 MSBuild msbuildTask = new MSBuild(); 797 msbuildTask.BuildEngine = new MockEngine(); 798 msbuildTask.Projects = projects; 799 800 bool success = msbuildTask.Execute(); 801 Assert.True(success); // "Build failed. See 'Standard Out' tab for details." 802 803 string expectedItemOutputs = string.Format(@" 804 b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB 805 h1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetH 806 ", projectFile1, projectFile2); 807 808 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */); 809 } 810 finally 811 { 812 File.Delete(projectFile1); 813 File.Delete(projectFile2); 814 } 815 } 816 817 /// <summary> 818 /// Properties and Targets that use non-standard separation chars 819 /// </summary> 820 [Fact] TargetsWithSeparationChars()821 public void TargetsWithSeparationChars() 822 { 823 string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 824 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`> 825 <Target Name=`Clean` /> 826 <Target Name=`Build` /> 827 <Target Name=`BuildAgain` /> 828 </Project> 829 "); 830 831 string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 832 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`> 833 <PropertyGroup> 834 <Targets>Clean%3BBuild%3CBuildAgain</Targets> 835 </PropertyGroup> 836 837 <ItemGroup> 838 <ProjectFile Include=`" + projectFile1 + @"` /> 839 </ItemGroup> 840 841 <Target Name=`Build` Outputs=`$(SomeOutputs)`> 842 <MSBuild Projects=`@(ProjectFile)` Targets=`$(Targets)` TargetAndPropertyListSeparators=`%3B;%3C` /> 843 </Target> 844 </Project> 845 "); 846 847 try 848 { 849 ITaskItem[] projects = new ITaskItem[] 850 { 851 new TaskItem(projectFile2) 852 }; 853 854 MSBuild msbuildTask = new MSBuild(); 855 msbuildTask.BuildEngine = new MockEngine(); 856 msbuildTask.Projects = projects; 857 858 bool success = msbuildTask.Execute(); 859 Assert.True(success); // "Build failed. See 'Standard Out' tab for details." 860 } 861 finally 862 { 863 File.Delete(projectFile1); 864 File.Delete(projectFile2); 865 } 866 } 867 868 /// <summary> 869 /// Verify stopOnFirstFailure with BuildInParallel override message are correctly logged 870 /// Also verify stop on first failure will not build the second project if the first one failed 871 /// The Aardvark tests which also test StopOnFirstFailure are at: 872 /// qa\md\wd\DTP\MSBuild\ShippingExtensions\ShippingTasks\MSBuild\_Tst\MSBuild.StopOnFirstFailure 873 /// </summary> 874 [Fact] StopOnFirstFailureandBuildInParallelSingleNode()875 public void StopOnFirstFailureandBuildInParallelSingleNode() 876 { 877 string project1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 878 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 879 <Target Name='msbuild'> 880 <Error Text='Error'/> 881 </Target> 882 </Project> 883 "); 884 885 string project2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 886 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 887 <Target Name='msbuild'> 888 <Message Text='SecondProject'/> 889 </Target> 890 </Project> 891 "); 892 893 try 894 { 895 ITaskItem[] projects = new ITaskItem[] 896 { 897 new TaskItem(project1), new TaskItem(project2) 898 }; 899 900 // Test the various combinations of BuildInParallel and StopOnFirstFailure when the msbuild task is told there are not multiple nodes 901 // running in the system 902 for (int i = 0; i < 4; i++) 903 { 904 MSBuild msbuildTask = new MSBuild(); 905 // By default IsMultipleNodesIs false 906 MockEngine mockEngine = new MockEngine(); 907 mockEngine.IsRunningMultipleNodes = false; 908 msbuildTask.BuildEngine = mockEngine; 909 msbuildTask.Projects = projects; 910 msbuildTask.Targets = new string[] { "msbuild" }; 911 // Make success true as the expected result is false 912 bool success = true; 913 switch (i) 914 { 915 case 0: 916 // Verify setting BuildInParallel and StopOnFirstFailure to 917 // true will cause the msbuild task to set BuildInParallel to false during the execute 918 msbuildTask.BuildInParallel = true; 919 msbuildTask.StopOnFirstFailure = true; 920 success = msbuildTask.Execute(); 921 // Verify build did not build second project which has the message SecondProject 922 mockEngine.AssertLogDoesntContain("SecondProject"); 923 // Verify the correct msbuild task messages are in the log 924 Assert.False(msbuildTask.BuildInParallel); // "Iteration of 0 Expected BuildInParallel to be false" 925 break; 926 case 1: 927 // Verify setting BuildInParallel to true and StopOnFirstFailure to 928 // false will cause no change in BuildInParallel 929 msbuildTask.BuildInParallel = true; 930 msbuildTask.StopOnFirstFailure = false; 931 success = msbuildTask.Execute(); 932 // Verify build did build second project which has the message SecondProject 933 mockEngine.AssertLogContains("SecondProject"); 934 // Verify the correct msbuild task messages are in the log 935 Assert.True(msbuildTask.BuildInParallel); // "Iteration of 1 Expected BuildInParallel to be true" 936 break; 937 case 2: 938 // Verify setting BuildInParallel to false and StopOnFirstFailure to 939 // true will cause no change in BuildInParallel 940 msbuildTask.BuildInParallel = false; 941 msbuildTask.StopOnFirstFailure = true; 942 success = msbuildTask.Execute(); 943 // Verify build did not build second project which has the message SecondProject 944 mockEngine.AssertLogDoesntContain("SecondProject"); 945 // Verify the correct msbuild task messages are in the log 946 Assert.False(msbuildTask.BuildInParallel); // "Iteration of 2 Expected BuildInParallel to be false" 947 break; 948 949 case 3: 950 // Verify setting BuildInParallel to false and StopOnFirstFailure to 951 // false will cause no change in BuildInParallel 952 msbuildTask.BuildInParallel = false; 953 msbuildTask.StopOnFirstFailure = false; 954 success = msbuildTask.Execute(); 955 // Verify build did build second project which has the message SecondProject 956 mockEngine.AssertLogContains("SecondProject"); 957 // Verify the correct msbuild task messages are in the log 958 Assert.False(msbuildTask.BuildInParallel); // "Iteration of 3 Expected BuildInParallel to be false" 959 break; 960 } 961 // The build should fail as the first project has an error 962 Assert.False(success, "Iteration of i " + i + " Build Succeeded. See 'Standard Out' tab for details."); 963 } 964 } 965 finally 966 { 967 File.Delete(project1); 968 File.Delete(project2); 969 } 970 } 971 972 #if FEATURE_APPDOMAIN 973 /// <summary> 974 /// Verify stopOnFirstFailure with BuildInParallel override message are correctly logged when there are multiple nodes 975 /// </summary> 976 [Fact] StopOnFirstFailureandBuildInParallelMultipleNode()977 public void StopOnFirstFailureandBuildInParallelMultipleNode() 978 { 979 string project1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 980 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 981 <Target Name='msbuild'> 982 <Error Text='Error'/> 983 </Target> 984 </Project> 985 "); 986 987 string project2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 988 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 989 <Target Name='msbuild'> 990 <Message Text='SecondProject'/> 991 </Target> 992 </Project> 993 "); 994 995 try 996 { 997 ITaskItem[] projects = new ITaskItem[] 998 { 999 new TaskItem(project1), new TaskItem(project2) 1000 }; 1001 1002 // Test the various combinations of BuildInParallel and StopOnFirstFailure when the msbuild task is told there are multiple nodes 1003 // running in the system 1004 for (int i = 0; i < 4; i++) 1005 { 1006 MSBuild msbuildTask = new MSBuild(); 1007 MockEngine mockEngine = new MockEngine(); 1008 mockEngine.IsRunningMultipleNodes = true; 1009 msbuildTask.BuildEngine = mockEngine; 1010 msbuildTask.Projects = projects; 1011 msbuildTask.Targets = new string[] { "msbuild" }; 1012 // Make success true as the expected result is false 1013 bool success = true; 1014 switch (i) 1015 { 1016 case 0: 1017 // Verify setting BuildInParallel and StopOnFirstFailure to 1018 // true will not cause the msbuild task to set BuildInParallel to false during the execute 1019 msbuildTask.BuildInParallel = true; 1020 msbuildTask.StopOnFirstFailure = true; 1021 success = msbuildTask.Execute(); 1022 // Verify build did build second project which has the message SecondProject 1023 mockEngine.AssertLogContains("SecondProject"); 1024 // Verify the correct msbuild task messages are in the log 1025 Assert.True(msbuildTask.BuildInParallel); // "Iteration of 0 Expected BuildInParallel to be true" 1026 break; 1027 case 1: 1028 // Verify setting BuildInParallel to true and StopOnFirstFailure to 1029 // false will cause no change in BuildInParallel 1030 msbuildTask.BuildInParallel = true; 1031 msbuildTask.StopOnFirstFailure = false; 1032 success = msbuildTask.Execute(); 1033 // Verify build did build second project which has the message SecondProject 1034 mockEngine.AssertLogContains("SecondProject"); 1035 // Verify the correct msbuild task messages are in the log 1036 Assert.True(msbuildTask.BuildInParallel); // "Iteration of 1 Expected BuildInParallel to be true" 1037 break; 1038 case 2: 1039 // Verify setting BuildInParallel to false and StopOnFirstFailure to 1040 // true will cause no change in BuildInParallel 1041 msbuildTask.BuildInParallel = false; 1042 msbuildTask.StopOnFirstFailure = true; 1043 success = msbuildTask.Execute(); 1044 // Verify build did not build second project which has the message SecondProject 1045 mockEngine.AssertLogDoesntContain("SecondProject"); 1046 // Verify the correct msbuild task messages are in the log 1047 Assert.False(msbuildTask.BuildInParallel); // "Iteration of 2 Expected BuildInParallel to be false" 1048 break; 1049 1050 case 3: 1051 // Verify setting BuildInParallel to false and StopOnFirstFailure to 1052 // false will cause no change in BuildInParallel 1053 msbuildTask.BuildInParallel = false; 1054 msbuildTask.StopOnFirstFailure = false; 1055 success = msbuildTask.Execute(); 1056 // Verify build did build second project which has the message SecondProject 1057 mockEngine.AssertLogContains("SecondProject"); 1058 // Verify the correct msbuild task messages are in the log 1059 Assert.False(msbuildTask.BuildInParallel); // "Iteration of 3 Expected BuildInParallel to be false" 1060 break; 1061 } 1062 // The build should fail as the first project has an error 1063 Assert.False(success, "Iteration of i " + i + " Build Succeeded. See 'Standard Out' tab for details."); 1064 } 1065 } 1066 finally 1067 { 1068 File.Delete(project1); 1069 File.Delete(project2); 1070 } 1071 } 1072 #endif 1073 1074 /// <summary> 1075 /// Test the skipping of the remaining projects. Verify the skip message is only displayed when there are projects to skip. 1076 /// </summary> 1077 [Fact] SkipRemainingProjects()1078 public void SkipRemainingProjects() 1079 { 1080 string project1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 1081 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1082 <Target Name='msbuild'> 1083 <Error Text='Error'/> 1084 </Target> 1085 </Project> 1086 "); 1087 1088 string project2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 1089 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1090 <Target Name='msbuild'> 1091 <Message Text='SecondProject'/> 1092 </Target> 1093 </Project> 1094 "); 1095 1096 try 1097 { 1098 // Test the case where there is only one project and it has an error 1099 ITaskItem[] projects = new ITaskItem[] 1100 { 1101 new TaskItem(project1) 1102 }; 1103 1104 MSBuild msbuildTask = new MSBuild(); 1105 MockEngine mockEngine = new MockEngine(); 1106 mockEngine.IsRunningMultipleNodes = true; 1107 msbuildTask.BuildEngine = mockEngine; 1108 msbuildTask.Projects = projects; 1109 msbuildTask.Targets = new string[] { "msbuild" }; 1110 msbuildTask.BuildInParallel = false; 1111 msbuildTask.StopOnFirstFailure = true; 1112 bool success = msbuildTask.Execute(); 1113 Assert.False(success); // "Build Succeeded. See 'Standard Out' tab for details." 1114 1115 // Test the case where there are two projects and the last one has an error 1116 projects = new ITaskItem[] 1117 { 1118 new TaskItem(project2), new TaskItem(project1) 1119 }; 1120 1121 msbuildTask = new MSBuild(); 1122 mockEngine = new MockEngine(); 1123 mockEngine.IsRunningMultipleNodes = true; 1124 msbuildTask.BuildEngine = mockEngine; 1125 msbuildTask.Projects = projects; 1126 msbuildTask.Targets = new string[] { "msbuild" }; 1127 msbuildTask.BuildInParallel = false; 1128 msbuildTask.StopOnFirstFailure = true; 1129 success = msbuildTask.Execute(); 1130 Assert.False(success); // "Build Succeeded. See 'Standard Out' tab for details." 1131 } 1132 finally 1133 { 1134 File.Delete(project1); 1135 File.Delete(project2); 1136 } 1137 } 1138 1139 /// <summary> 1140 /// Verify the behavior of Target execution with StopOnFirstFailure 1141 /// </summary> 1142 [Fact] TargetStopOnFirstFailureBuildInParallel()1143 public void TargetStopOnFirstFailureBuildInParallel() 1144 { 1145 string project1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 1146 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1147 <Target Name='T1'> 1148 <Message Text='Proj2 T1 message'/> 1149 </Target> 1150 <Target Name='T2'> 1151 <Message Text='Proj2 T2 message'/> 1152 </Target> 1153 <Target Name='T3'> 1154 <Error Text='Error'/> 1155 </Target> 1156 </Project> 1157 "); 1158 1159 try 1160 { 1161 ITaskItem[] projects = new ITaskItem[] 1162 { 1163 new TaskItem(project1) 1164 }; 1165 for (int i = 0; i < 6; i++) 1166 { 1167 // Test the case where the error is in the last target 1168 MSBuild msbuildTask = new MSBuild(); 1169 MockEngine mockEngine = new MockEngine(); 1170 msbuildTask.BuildEngine = mockEngine; 1171 msbuildTask.Projects = projects; 1172 // Set to true as the expected result is false 1173 bool success = true; 1174 switch (i) 1175 { 1176 case 0: 1177 // Test the case where the error is in the last project and RunEachTargetSeparately = true 1178 msbuildTask.StopOnFirstFailure = true; 1179 msbuildTask.RunEachTargetSeparately = true; 1180 msbuildTask.Targets = new string[] { "T1", "T2", "T3" }; 1181 success = msbuildTask.Execute(); 1182 mockEngine.AssertLogContains("Proj2 T1 message"); 1183 mockEngine.AssertLogContains("Proj2 T2 message"); 1184 break; 1185 case 1: 1186 // Test the case where the error is in the second target out of 3. 1187 msbuildTask.StopOnFirstFailure = true; 1188 msbuildTask.RunEachTargetSeparately = true; 1189 msbuildTask.Targets = new string[] { "T1", "T3", "T2" }; 1190 success = msbuildTask.Execute(); 1191 mockEngine.AssertLogContains("Proj2 T1 message"); 1192 mockEngine.AssertLogDoesntContain("Proj2 T2 message"); 1193 // The build should fail as the first project has an error 1194 break; 1195 case 2: 1196 // Test case where error is in second last target but stopOnFirstFailure is false 1197 msbuildTask.RunEachTargetSeparately = true; 1198 msbuildTask.StopOnFirstFailure = false; 1199 msbuildTask.Targets = new string[] { "T1", "T3", "T2" }; 1200 success = msbuildTask.Execute(); 1201 mockEngine.AssertLogContains("Proj2 T1 message"); 1202 mockEngine.AssertLogContains("Proj2 T2 message"); 1203 break; 1204 // Test the cases where RunEachTargetSeparately is false. In these cases all of the targets should be submitted at once 1205 case 3: 1206 // Test the case where the error is in the last project and RunEachTargetSeparately = true 1207 msbuildTask.StopOnFirstFailure = true; 1208 msbuildTask.Targets = new string[] { "T1", "T2", "T3" }; 1209 success = msbuildTask.Execute(); 1210 mockEngine.AssertLogContains("Proj2 T1 message"); 1211 mockEngine.AssertLogContains("Proj2 T2 message"); 1212 // The build should fail as the first project has an error 1213 break; 1214 case 4: 1215 // Test the case where the error is in the second target out of 3. 1216 msbuildTask.StopOnFirstFailure = true; 1217 msbuildTask.Targets = new string[] { "T1", "T3", "T2" }; 1218 success = msbuildTask.Execute(); 1219 mockEngine.AssertLogContains("Proj2 T1 message"); 1220 mockEngine.AssertLogDoesntContain("Proj2 T2 message"); 1221 // The build should fail as the first project has an error 1222 break; 1223 case 5: 1224 // Test case where error is in second last target but stopOnFirstFailure is false 1225 msbuildTask.StopOnFirstFailure = false; 1226 msbuildTask.Targets = new string[] { "T1", "T3", "T2" }; 1227 success = msbuildTask.Execute(); 1228 mockEngine.AssertLogContains("Proj2 T1 message"); 1229 mockEngine.AssertLogDoesntContain("Proj2 T2 message"); 1230 break; 1231 } 1232 1233 // The build should fail as the first project has an error 1234 Assert.False(success, "Iteration of i:" + i + "Build Succeeded. See 'Standard Out' tab for details."); 1235 } 1236 } 1237 finally 1238 { 1239 File.Delete(project1); 1240 } 1241 } 1242 1243 /// <summary> 1244 /// Properties and Targets that use non-standard separation chars 1245 /// </summary> 1246 [Fact] PropertiesWithSeparationChars()1247 public void PropertiesWithSeparationChars() 1248 { 1249 string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 1250 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 1251 <Target Name=`Build` Outputs=`|$(a)|$(b)|$(C)|$(D)|` /> 1252 </Project> 1253 "); 1254 1255 string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 1256 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`> 1257 <PropertyGroup> 1258 <AValues>a%3BA</AValues> 1259 <BValues>b;B</BValues> 1260 <CValues>c;C</CValues> 1261 <DValues>d%3BD</DValues> 1262 </PropertyGroup> 1263 1264 <ItemGroup> 1265 <ProjectFile Include=`" + projectFile1 + @"`> 1266 <AdditionalProperties>C=$(CValues)%3BD=$(DValues)</AdditionalProperties> 1267 </ProjectFile> 1268 </ItemGroup> 1269 1270 <Target Name=`Build` Outputs=`$(SomeOutputs)`> 1271 <MSBuild Projects=`@(ProjectFile)` Targets=`Build` Properties=`a=$(AValues)%3Bb=$(BValues)` TargetAndPropertyListSeparators=`%3B`> 1272 <Output TaskParameter=`TargetOutputs` PropertyName=`SomeOutputs`/> 1273 </MSBuild> 1274 </Target> 1275 </Project> 1276 "); 1277 1278 try 1279 { 1280 ITaskItem[] projects = new ITaskItem[] 1281 { 1282 new TaskItem(projectFile2) 1283 }; 1284 1285 MSBuild msbuildTask = new MSBuild(); 1286 msbuildTask.BuildEngine = new MockEngine(); 1287 msbuildTask.Projects = projects; 1288 1289 bool success = msbuildTask.Execute(); 1290 Assert.True(success); // "Build failed. See 'Standard Out' tab for details." 1291 1292 Assert.Equal(5, msbuildTask.TargetOutputs.Length); 1293 Assert.Equal("|a", msbuildTask.TargetOutputs[0].ItemSpec); 1294 Assert.Equal("A|b", msbuildTask.TargetOutputs[1].ItemSpec); 1295 Assert.Equal("B|c", msbuildTask.TargetOutputs[2].ItemSpec); 1296 Assert.Equal("C|d", msbuildTask.TargetOutputs[3].ItemSpec); 1297 Assert.Equal("D|", msbuildTask.TargetOutputs[4].ItemSpec); 1298 } 1299 finally 1300 { 1301 File.Delete(projectFile1); 1302 File.Delete(projectFile2); 1303 } 1304 } 1305 1306 /// <summary> 1307 /// Orcas had a bug that if the target casing specified was not correct, we would still build it, 1308 /// but not return any target outputs! 1309 /// </summary> 1310 [Fact] TargetNameIsCaseInsensitive()1311 public void TargetNameIsCaseInsensitive() 1312 { 1313 string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@" 1314 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'> 1315 <Target Name=`bUiLd` Outputs=`foo.out` /> 1316 </Project> 1317 "); 1318 1319 string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@" 1320 <Project DefaultTargets=`t` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`> 1321 <Target Name=`t`> 1322 <MSBuild Projects=`" + projectFile1 + @"` Targets=`BUILD`> 1323 <Output TaskParameter=`TargetOutputs` ItemName=`out`/> 1324 </MSBuild> 1325 <Message Text=`[@(out)]`/> 1326 </Target> 1327 </Project> 1328 "); 1329 1330 try 1331 { 1332 Project project = new Project(projectFile2); 1333 MockLogger logger = new MockLogger(); 1334 1335 project.Build(logger); 1336 1337 logger.AssertLogContains("[foo.out]"); 1338 } 1339 finally 1340 { 1341 File.Delete(projectFile1); 1342 File.Delete(projectFile2); 1343 } 1344 } 1345 1346 [Fact] NonexistentTargetSkippedWhenSkipNonexistentTargetsSet()1347 public void NonexistentTargetSkippedWhenSkipNonexistentTargetsSet() 1348 { 1349 ObjectModelHelpers.DeleteTempProjectDirectory(); 1350 ObjectModelHelpers.CreateFileInTempProjectDirectory( 1351 "SkipNonexistentTargets.csproj", 1352 @"<Project> 1353 <Target Name=`t` > 1354 <MSBuild Projects=`SkipNonexistentTargets.csproj` Targets=`t_nonexistent;t2` SkipNonexistentTargets=`true` /> 1355 </Target> 1356 <Target Name=`t2`> <Message Text=`t2 executing` /> </Target> 1357 </Project> 1358 "); 1359 1360 MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectSuccess(@"SkipNonexistentTargets.csproj"); 1361 1362 // Target t2 should still execute 1363 logger.FullLog.ShouldContain("t2 executing"); 1364 logger.WarningCount.ShouldBe(0); 1365 logger.ErrorCount.ShouldBe(0); 1366 1367 // t_missing target should be skipped 1368 logger.FullLog.ShouldContain("Target \"t_nonexistent\" skipped"); 1369 } 1370 1371 [Fact] NonexistentTargetFailsAfterSkipped()1372 public void NonexistentTargetFailsAfterSkipped() 1373 { 1374 ObjectModelHelpers.DeleteTempProjectDirectory(); 1375 ObjectModelHelpers.CreateFileInTempProjectDirectory( 1376 "SkipNonexistentTargets.csproj", 1377 @"<Project> 1378 <Target Name=`t` > 1379 <MSBuild Projects=`SkipNonexistentTargets.csproj` Targets=`t_nonexistent` SkipNonexistentTargets=`true` /> 1380 <MSBuild Projects=`SkipNonexistentTargets.csproj` Targets=`t_nonexistent` SkipNonexistentTargets=`false` /> 1381 </Target> 1382 </Project> 1383 "); 1384 1385 MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectFailure(@"SkipNonexistentTargets.csproj"); 1386 1387 logger.WarningCount.ShouldBe(0); 1388 logger.ErrorCount.ShouldBe(1); 1389 1390 // t_missing target should be skipped 1391 logger.FullLog.ShouldContain("Target \"t_nonexistent\" skipped"); 1392 1393 // 2nd invocation of t_missing should fail the build resulting in target not found error (MSB4057) 1394 logger.FullLog.ShouldContain("MSB4057"); 1395 } 1396 } 1397 } 1398