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 // </copyright> 5 // <summary>Unit tests for the BuildManager object.</summary> 6 //----------------------------------------------------------------------- 7 8 using System; 9 using System.CodeDom.Compiler; 10 using System.Collections; 11 using System.Collections.Generic; 12 using System.Diagnostics; 13 using System.Globalization; 14 using System.IO; 15 using System.Linq; 16 using System.Threading; 17 using System.Xml; 18 19 using Microsoft.Build.BackEnd; 20 using Microsoft.Build.Collections; 21 using Microsoft.Build.Construction; 22 using Microsoft.Build.Engine.UnitTests; 23 using Microsoft.Build.Evaluation; 24 using Microsoft.Build.Execution; 25 using Microsoft.Build.Framework; 26 using Microsoft.Build.Logging; 27 using Microsoft.Build.Shared; 28 using Microsoft.Build.Utilities; 29 using Shouldly; 30 using Xunit; 31 using Xunit.Abstractions; 32 using static Microsoft.Build.UnitTests.ObjectModelHelpers; 33 34 namespace Microsoft.Build.UnitTests.BackEnd 35 { 36 /// <summary> 37 /// The test fixture for the BuildManager 38 /// </summary> 39 public class BuildManager_Tests : IDisposable 40 { 41 /// <summary> 42 /// The mock logger for testing. 43 /// </summary> 44 private readonly MockLogger _logger; 45 46 /// <summary> 47 /// The standard build manager for each test. 48 /// </summary> 49 private BuildManager _buildManager; 50 51 /// <summary> 52 /// The build parameters. 53 /// </summary> 54 private readonly BuildParameters _parameters; 55 56 /// <summary> 57 /// The project collection used. 58 /// </summary> 59 private readonly ProjectCollection _projectCollection; 60 61 private readonly TestEnvironment _env; 62 private readonly ITestOutputHelper _output; 63 64 /// <summary> 65 /// SetUp 66 /// </summary> BuildManager_Tests(ITestOutputHelper output)67 public BuildManager_Tests(ITestOutputHelper output) 68 { 69 _output = output; 70 // Ensure that any previous tests which may have been using the default BuildManager do not conflict with us. 71 BuildManager.DefaultBuildManager.Dispose(); 72 73 _logger = new MockLogger(output); 74 _parameters = new BuildParameters 75 { 76 ShutdownInProcNodeOnBuildFinish = true, 77 Loggers = new ILogger[] { _logger }, 78 EnableNodeReuse = false 79 }; 80 _buildManager = new BuildManager(); 81 _projectCollection = new ProjectCollection(); 82 83 _env = TestEnvironment.Create(output); 84 _env.SetEnvironmentVariable("MSBUILDINPROCENVCHECK", "1"); 85 } 86 87 /// <summary> 88 /// TearDown 89 /// </summary> Dispose()90 public void Dispose() 91 { 92 _buildManager.Dispose(); 93 _projectCollection.Dispose(); 94 _env.Dispose(); 95 } 96 97 /// <summary> 98 /// Check that we behave reasonably when passed a null ProjectCollection 99 /// </summary> 100 [Fact] BuildParametersWithNullCollection()101 public void BuildParametersWithNullCollection() 102 { 103 Assert.Throws<ArgumentNullException>(() => { new BuildParameters(null); }); 104 } 105 106 /// <summary> 107 /// A simple successful build. 108 /// </summary> 109 [Fact] SimpleBuild()110 public void SimpleBuild() 111 { 112 string contents = CleanupFileContents(@" 113 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 114 <PropertyGroup> 115 <InitialProperty1>InitialProperty1</InitialProperty1> 116 <InitialProperty2>InitialProperty2</InitialProperty2> 117 <InitialProperty3>InitialProperty3</InitialProperty3> 118 </PropertyGroup> 119 <Target Name='test'> 120 <Message Text='[success]'/> 121 </Target> 122 </Project> 123 "); 124 BuildRequestData data = GetBuildRequestData(contents); 125 BuildResult result = _buildManager.Build(_parameters, data); 126 Assert.Equal(BuildResultCode.Success, result.OverallResult); 127 _logger.AssertLogContains("[success]"); 128 Assert.Equal(1, _logger.ProjectStartedEvents.Count); 129 130 ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0]; 131 Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties); 132 133 string propertyValue; 134 Assert.True(properties.TryGetValue("InitialProperty1", out propertyValue)); 135 Assert.True(String.Equals(propertyValue, "InitialProperty1", StringComparison.OrdinalIgnoreCase)); 136 137 Assert.True(properties.TryGetValue("InitialProperty2", out propertyValue)); 138 Assert.True(String.Equals(propertyValue, "InitialProperty2", StringComparison.OrdinalIgnoreCase)); 139 140 Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue)); 141 Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase)); 142 } 143 144 #if FEATURE_CODETASKFACTORY 145 /// <summary> 146 /// Verify that the environment between two msbuild calls to the same project are stored 147 /// so that on the next call we get access to them 148 /// </summary> 149 [Fact] VerifyEnvironmentSavedBetweenCalls()150 public void VerifyEnvironmentSavedBetweenCalls() 151 { 152 string contents1 = CleanupFileContents(@" 153 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 154 <UsingTask TaskName='SetEnvv' TaskFactory='CodeTaskFactory' AssemblyFile='$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll' > 155 <Task> 156 <Code Language='cs'> 157 System.Environment.SetEnvironmentVariable(""MOO"", ""When the dawn comes, tonight will be a memory too""); 158 </Code> 159 </Task> 160 </UsingTask> 161 <Target Name='SetEnv'> 162 <SetEnvv/> 163 </Target> 164 <Target Name='Message1'> 165 <Exec Command='echo What does a cat say : " + (NativeMethodsShared.IsWindows ? "%MOO%" : "$MOO") + @"' /> 166 </Target> 167 </Project> 168 "); 169 170 var project = new Project(XmlReader.Create(new StringReader(contents1)), null, null, _projectCollection) 171 { 172 FullPath = _env.CreateFile(".proj").Path 173 }; 174 175 project.Save(); 176 177 178 string contents2 = CleanupFileContents(@" 179 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 180 <Target Name='Build' > 181 <MSBuild Targets='SetEnv' Projects='" + project.FullPath + "'/>" + 182 "<MSBuild Targets='Message1' Projects='" + project.FullPath + "'/>" + 183 @"</Target> 184 </Project> 185 "); 186 187 ProjectInstance instance = CreateProjectInstance(contents2, null, _projectCollection, true); 188 BuildRequestData data = new BuildRequestData(instance, new[] { "Build" }, _projectCollection.HostServices); 189 190 BuildResult result = _buildManager.Build(_parameters, data); 191 Assert.Equal(BuildResultCode.Success, result.OverallResult); 192 _logger.AssertLogContains("What does a cat say : When the dawn comes, tonight will be a memory too"); 193 } 194 #endif 195 196 /// <summary> 197 /// Verify if idle nodes are shutdown when BuildManager.ShutdownAllNodes is evoked. 198 /// The final number of nodes has to be less or equal the number of nodes already in 199 /// the system before this method was called. 200 /// </summary> 201 #if RUNTIME_TYPE_NETCORE 202 [Theory(Skip = "https://github.com/Microsoft/msbuild/issues/1975")] 203 #elif MONO 204 [Theory(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 205 #else 206 [Theory(Skip = "https://github.com/Microsoft/msbuild/issues/2057")] 207 [InlineData(8, false)] 208 #endif ShutdownNodesAfterParallelBuild(int numberOfParallelProjectsToBuild, bool enbaleDebugComm)209 public void ShutdownNodesAfterParallelBuild(int numberOfParallelProjectsToBuild, bool enbaleDebugComm) 210 { 211 // This test has previously been failing silently. With the addition of TestEnvironment the 212 // failure is now noticed (worker node is crashing with "Pipe is broken" exception. See #2057: 213 // https://github.com/Microsoft/msbuild/issues/2057 214 _env.ClearTestInvariants(); 215 216 // Communications debug log enabled, picked up by TestEnvironment 217 if (enbaleDebugComm) _env.SetEnvironmentVariable("MSBUILDDEBUGCOMM", "1"); 218 219 ProjectCollection projectCollection = new ProjectCollection(); 220 221 // Get number of MSBuild processes currently instantiated 222 int numberProcsOriginally = (new List<Process>(Process.GetProcessesByName("MSBuild"))).Count; 223 _output.WriteLine($"numberProcsOriginally = {numberProcsOriginally}"); 224 225 // Generate a theoretically unique directory to put our dummy projects in. 226 string shutdownProjectDirectory = Path.Combine(Path.GetTempPath(), String.Format(CultureInfo.InvariantCulture, "VSNodeShutdown_{0}_UnitTest", Process.GetCurrentProcess().Id)); 227 228 // Create the dummy projects we'll be "building" as our excuse to connect to and shut down 229 // all the nodes. 230 ProjectInstance rootProject = GenerateDummyProjects(shutdownProjectDirectory, numberOfParallelProjectsToBuild, projectCollection); 231 232 // Build the projects. 233 var buildParameters = new BuildParameters(projectCollection) 234 { 235 OnlyLogCriticalEvents = true, 236 MaxNodeCount = numberOfParallelProjectsToBuild, 237 EnableNodeReuse = true, 238 DisableInProcNode = true, 239 SaveOperatingEnvironment = false, 240 Loggers = new List<ILogger> {new MockLogger(_output)} 241 }; 242 243 // Tell the build manager to not disturb process wide state 244 245 BuildRequestData requestData = new BuildRequestData(rootProject, new[] { "Build" }, null); 246 247 // Use a separate BuildManager for the node shutdown build, so that we don't have 248 // to worry about taking dependencies on whether or not the existing ones have already 249 // disappeared. 250 BuildManager shutdownManager = new BuildManager("IdleNodeShutdown"); 251 shutdownManager.Build(buildParameters, requestData); 252 253 // Number of nodes after the build has to be greater than the original number 254 int numberProcsAfterBuild = (new List<Process>(Process.GetProcessesByName("MSBuild"))).Count; 255 _output.WriteLine($"numberProcsAfterBuild = {numberProcsAfterBuild}"); 256 Assert.True(numberProcsOriginally < numberProcsAfterBuild, $"Expected '{numberProcsOriginally}' < '{numberProcsAfterBuild}'"); 257 258 // Shutdown all nodes 259 shutdownManager.ShutdownAllNodes(); 260 261 // Wait until all processes shut down 262 Thread.Sleep(3000); 263 264 // Number of nodes after the shutdown has to be smaller or equal the original number 265 int numberProcsAfterShutdown = (new List<Process>(Process.GetProcessesByName("MSBuild"))).Count; 266 _output.WriteLine($"numberProcsAfterShutdown = {numberProcsAfterShutdown}"); 267 Assert.True(numberProcsAfterShutdown <= numberProcsOriginally); 268 269 // Delete directory with the dummy project 270 if (Directory.Exists(shutdownProjectDirectory)) 271 { 272 FileUtilities.DeleteWithoutTrailingBackslash(shutdownProjectDirectory, true /* recursive delete */); 273 } 274 } 275 276 /// <summary> 277 /// A simple successful build, out of process only. 278 /// </summary> 279 #if MONO 280 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 281 #else 282 [Fact] 283 #endif SimpleBuildOutOfProcess()284 public void SimpleBuildOutOfProcess() 285 { 286 RunOutOfProcBuild(_ => _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1")); 287 } 288 289 /// <summary> 290 /// A simple successful build, out of process only. Triggered by setting build parameters' DisableInProcNode to true. 291 /// </summary> 292 #if MONO 293 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 294 #else 295 [Fact] 296 #endif DisableInProcNode()297 public void DisableInProcNode() 298 { 299 RunOutOfProcBuild(buildParameters => buildParameters.DisableInProcNode = true); 300 } 301 302 /// <summary> 303 /// Runs a build and verifies it happens out of proc by checking the process ID. 304 /// </summary> 305 /// <param name="buildParametersModifier">Runs a test out of proc.</param> RunOutOfProcBuild(Action<BuildParameters> buildParametersModifier)306 public void RunOutOfProcBuild(Action<BuildParameters> buildParametersModifier) 307 { 308 const string Contents = @" 309 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 310 <ItemGroup> 311 <InitialProperty Include='InitialProperty2'/> 312 <InitialProperty Include='InitialProperty3'/> 313 </ItemGroup> 314 <Target Name='test' Returns='@(InitialProperty)'> 315 <ItemGroup> 316 <InitialProperty Include='$([System.Diagnostics.Process]::GetCurrentProcess().Id)'/> 317 </ItemGroup> 318 <Message Text='[success]'/> 319 </Target> 320 </Project> 321 "; 322 323 // Need to set this env variable to enable Process.GetCurrentProcess().Id in the project file. 324 _env.SetEnvironmentVariable("MSBUILDENABLEALLPROPERTYFUNCTIONS", "1"); 325 326 Project project = CreateProject(CleanupFileContents(Contents), MSBuildDefaultToolsVersion, _projectCollection, false); 327 328 BuildRequestData data = new BuildRequestData(project.CreateProjectInstance(), new string[0], _projectCollection.HostServices); 329 BuildParameters customparameters = new BuildParameters { EnableNodeReuse = false, Loggers = new ILogger[] { _logger } }; 330 buildParametersModifier(customparameters); 331 332 BuildResult result = _buildManager.Build(customparameters, data); 333 TargetResult targetresult = result.ResultsByTarget["test"]; 334 ITaskItem[] item = targetresult.Items; 335 336 Assert.Equal(BuildResultCode.Success, result.OverallResult); 337 Assert.Equal(3, item.Length); 338 int processId; 339 Assert.True(int.TryParse(item[2].ItemSpec, out processId), $"Process ID passed from the 'test' target is not a valid integer (actual is '{item[2].ItemSpec}')"); 340 Assert.NotEqual(Process.GetCurrentProcess().Id, processId); // "Build is expected to be out-of-proc. In fact it was in-proc." 341 } 342 343 #if MONO 344 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 345 #else 346 [Fact] 347 #endif RequestedResultsAreSatisfied()348 public void RequestedResultsAreSatisfied() 349 { 350 const string contents = @" 351 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 352 <PropertyGroup> 353 <UnrequestedProperty>IsUnrequested</UnrequestedProperty> 354 <RequestedProperty>IsRequested</RequestedProperty> 355 <UpdatedProperty>Stale</UpdatedProperty> 356 </PropertyGroup> 357 <ItemGroup> 358 <AnItem Include='Item1' UnexpectedMetadatum='Unexpected' /> 359 <AnItem Include='Item2'/> 360 </ItemGroup> 361 <Target Name='test' Returns='@(ItemWithMetadata)'> 362 <ItemGroup> 363 <AnItem Include='$([System.Diagnostics.Process]::GetCurrentProcess().Id)' /> 364 <ItemWithMetadata Metadatum1='m1' Metadatum2='m2' Include='ItemFromTarget' /> 365 </ItemGroup> 366 <PropertyGroup> 367 <NewProperty>FunValue</NewProperty> 368 <UpdatedProperty>Updated</UpdatedProperty> 369 </PropertyGroup> 370 <Message Text='[success]'/> 371 </Target> 372 373 <Target Name='other' Returns='@(ItemWithMetadata)' DependsOnTargets='test' /> 374 375 </Project> 376 "; 377 378 // Need to set this env variable to enable Process.GetCurrentProcess().Id in the project file. 379 _env.SetEnvironmentVariable("MSBUILDENABLEALLPROPERTYFUNCTIONS", "1"); 380 381 Project project = CreateProject(CleanupFileContents(contents), MSBuildDefaultToolsVersion, 382 _projectCollection, false); 383 384 var requestedProjectState = new RequestedProjectState 385 { 386 ItemFilters = new Dictionary<string, List<string>> 387 { 388 {"AnItem", null}, 389 {"ItemWithMetadata", new List<string> {"Metadatum1"}}, 390 }, 391 PropertyFilters = new List<string> {"NewProperty", "RequestedProperty"}, 392 }; 393 394 BuildRequestData data = new BuildRequestData(project.CreateProjectInstance(), new [] {"test", "other"}, 395 _projectCollection.HostServices, BuildRequestDataFlags.ProvideSubsetOfStateAfterBuild, null, 396 requestedProjectState); 397 BuildParameters customparameters = new BuildParameters 398 { 399 EnableNodeReuse = false, 400 Loggers = new ILogger[] {_logger}, 401 DisableInProcNode = true, 402 }; 403 404 BuildResult result = _buildManager.Build(customparameters, data); 405 406 result.OverallResult.ShouldBe(BuildResultCode.Success); 407 408 result.ProjectStateAfterBuild.ShouldNotBeNull(); 409 410 result.ProjectStateAfterBuild.Properties.ShouldNotContain(p => p.Name == "UnrequestedProperty"); 411 412 result.ProjectStateAfterBuild.Properties.ShouldContain(p => p.Name == "NewProperty"); 413 result.ProjectStateAfterBuild.GetPropertyValue("NewProperty").ShouldBe("FunValue"); 414 415 result.ProjectStateAfterBuild.Properties.ShouldContain(p => p.Name == "RequestedProperty"); 416 result.ProjectStateAfterBuild.GetPropertyValue("RequestedProperty").ShouldBe("IsRequested"); 417 418 result.ProjectStateAfterBuild.Items.Count.ShouldBe(4); 419 420 result.ProjectStateAfterBuild.GetItems("ItemWithMetadata").ShouldHaveSingleItem(); 421 result.ProjectStateAfterBuild.GetItems("ItemWithMetadata").First().DirectMetadataCount.ShouldBe(1); 422 result.ProjectStateAfterBuild.GetItems("ItemWithMetadata").First().GetMetadataValue("Metadatum1") 423 .ShouldBe("m1"); 424 result.ProjectStateAfterBuild.GetItems("ItemWithMetadata").First().GetMetadataValue("Metadatum2") 425 .ShouldBeNullOrEmpty(); 426 427 result.ProjectStateAfterBuild.GetItems("AnItem").Count.ShouldBe(3); 428 result.ProjectStateAfterBuild.GetItems("AnItem").ShouldContain(p => p.EvaluatedInclude == "Item2"); 429 430 result.ProjectStateAfterBuild.GetItemsByItemTypeAndEvaluatedInclude("AnItem", "Item1") 431 .ShouldHaveSingleItem(); 432 result.ProjectStateAfterBuild.GetItemsByItemTypeAndEvaluatedInclude("AnItem", "Item1").First() 433 .GetMetadataValue("UnexpectedMetadatum").ShouldBe("Unexpected"); 434 } 435 436 /// <summary> 437 /// Make sure when we are doing an in-process build that even if the environment variable MSBUILDFORWARDPROPERTIESFROMCHILD is set that we still 438 /// get all of the initial properties. 439 /// </summary> 440 [Fact] InProcForwardPropertiesFromChild()441 public void InProcForwardPropertiesFromChild() 442 { 443 string contents = CleanupFileContents(@" 444 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 445 <PropertyGroup> 446 <InitialProperty1>InitialProperty1</InitialProperty1> 447 <InitialProperty2>InitialProperty2</InitialProperty2> 448 <InitialProperty3>InitialProperty3</InitialProperty3> 449 </PropertyGroup> 450 <Target Name='test'> 451 <Message Text='[success]'/> 452 </Target> 453 </Project> 454 "); 455 456 _env.SetEnvironmentVariable("MSBuildForwardPropertiesFromChild", "InitialProperty2;IAMNOTREAL"); 457 BuildRequestData data = GetBuildRequestData(contents); 458 BuildResult result = _buildManager.Build(_parameters, data); 459 Assert.Equal(BuildResultCode.Success, result.OverallResult); 460 _logger.AssertLogContains("[success]"); 461 Assert.Equal(1, _logger.ProjectStartedEvents.Count); 462 463 ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0]; 464 Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties); 465 466 string propertyValue; 467 Assert.True(properties.TryGetValue("InitialProperty1", out propertyValue)); 468 Assert.True(String.Equals(propertyValue, "InitialProperty1", StringComparison.OrdinalIgnoreCase)); 469 470 Assert.True(properties.TryGetValue("InitialProperty2", out propertyValue)); 471 Assert.True(String.Equals(propertyValue, "InitialProperty2", StringComparison.OrdinalIgnoreCase)); 472 473 Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue)); 474 Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase)); 475 } 476 477 /// <summary> 478 /// Make sure when we are doing an in-process build that even if the environment variable MsBuildForwardAllPropertiesFromChild is set that we still 479 /// get all of the initial properties. 480 /// </summary> 481 [Fact] InProcMsBuildForwardAllPropertiesFromChild()482 public void InProcMsBuildForwardAllPropertiesFromChild() 483 { 484 string contents = CleanupFileContents(@" 485 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 486 <PropertyGroup> 487 <InitialProperty1>InitialProperty1</InitialProperty1> 488 <InitialProperty2>InitialProperty2</InitialProperty2> 489 <InitialProperty3>InitialProperty3</InitialProperty3> 490 </PropertyGroup> 491 <Target Name='test'> 492 <Message Text='[success]'/> 493 </Target> 494 </Project> 495 "); 496 _env.SetEnvironmentVariable("MsBuildForwardAllPropertiesFromChild", "InitialProperty2;IAMNOTREAL"); 497 498 BuildRequestData data = GetBuildRequestData(contents); 499 BuildResult result = _buildManager.Build(_parameters, data); 500 Assert.Equal(BuildResultCode.Success, result.OverallResult); 501 _logger.AssertLogContains("[success]"); 502 Assert.Equal(1, _logger.ProjectStartedEvents.Count); 503 504 ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0]; 505 Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties); 506 507 string propertyValue = null; 508 Assert.True(properties.TryGetValue("InitialProperty1", out propertyValue)); 509 Assert.True(String.Equals(propertyValue, "InitialProperty1", StringComparison.OrdinalIgnoreCase)); 510 511 Assert.True(properties.TryGetValue("InitialProperty2", out propertyValue)); 512 Assert.True(String.Equals(propertyValue, "InitialProperty2", StringComparison.OrdinalIgnoreCase)); 513 514 Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue)); 515 Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase)); 516 } 517 518 /// <summary> 519 /// Make sure when we launch a child node and set MsBuildForwardAllPropertiesFromChild that we get all of our properties. This needs to happen 520 /// even if the msbuildforwardpropertiesfromchild is set to something. 521 /// </summary> 522 [Fact] MsBuildForwardAllPropertiesFromChildLaunchChildNode()523 public void MsBuildForwardAllPropertiesFromChildLaunchChildNode() 524 { 525 string contents = CleanupFileContents(@" 526 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 527 <PropertyGroup> 528 <InitialProperty1>InitialProperty1</InitialProperty1> 529 <InitialProperty2>InitialProperty2</InitialProperty2> 530 <InitialProperty3>InitialProperty3</InitialProperty3> 531 </PropertyGroup> 532 <Target Name='test'> 533 <Message Text='[success]'/> 534 </Target> 535 </Project> 536 "); 537 538 _env.SetEnvironmentVariable("MsBuildForwardAllPropertiesFromChild", "InitialProperty2;IAMNOTREAL"); 539 _env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", "Something"); 540 541 var project = CreateProject(contents, null, _projectCollection, false); 542 var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new string[] { }, null); 543 544 BuildResult result = _buildManager.Build(_parameters, data); 545 Assert.Equal(BuildResultCode.Success, result.OverallResult); 546 _logger.AssertLogContains("[success]"); 547 Assert.Equal(1, _logger.ProjectStartedEvents.Count); 548 549 ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0]; 550 Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties); 551 552 string propertyValue; 553 Assert.True(properties.TryGetValue("InitialProperty1", out propertyValue)); 554 Assert.True(String.Equals(propertyValue, "InitialProperty1", StringComparison.OrdinalIgnoreCase)); 555 556 Assert.True(properties.TryGetValue("InitialProperty2", out propertyValue)); 557 Assert.True(String.Equals(propertyValue, "InitialProperty2", StringComparison.OrdinalIgnoreCase)); 558 559 Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue)); 560 Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase)); 561 } 562 563 /// <summary> 564 /// Make sure when if the environment variable MsBuildForwardPropertiesFromChild is set to a value and 565 /// we launch a child node that we get only that value. 566 /// </summary> 567 #if RUNTIME_TYPE_NETCORE 568 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1976")] 569 #elif MONO 570 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 571 #else 572 [Fact] 573 #endif OutOfProcNodeForwardCertainproperties()574 public void OutOfProcNodeForwardCertainproperties() 575 { 576 string contents = CleanupFileContents(@" 577 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 578 <PropertyGroup> 579 <InitialProperty1>InitialProperty1</InitialProperty1> 580 <InitialProperty2>InitialProperty2</InitialProperty2> 581 <InitialProperty3>InitialProperty3</InitialProperty3> 582 </PropertyGroup> 583 <Target Name='test'> 584 <Message Text='[success]'/> 585 </Target> 586 </Project> 587 "); 588 589 _env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", "InitialProperty3;IAMNOTREAL"); 590 _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1"); 591 592 var project = CreateProject(contents, null, _projectCollection, false); 593 var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(), 594 MSBuildDefaultToolsVersion, new string[] { }, null); 595 596 BuildResult result = _buildManager.Build(_parameters, data); 597 Assert.Equal(BuildResultCode.Success, result.OverallResult); 598 _logger.AssertLogContains("[success]"); 599 Assert.Equal(1, _logger.ProjectStartedEvents.Count); 600 601 ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0]; 602 Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties); 603 604 Assert.Equal(1, properties.Count); 605 606 string propertyValue; 607 Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue)); 608 Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase)); 609 } 610 611 /// <summary> 612 /// Make sure when if the environment variable MsBuildForwardPropertiesFromChild is set to a value and 613 /// we launch a child node that we get only that value. Also, make sure that when a project is pulled from the results cache 614 /// and we have a list of properties to serialize that we do not crash. This is to prevent a regression of 826594 615 /// </summary> 616 #if RUNTIME_TYPE_NETCORE 617 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1976")] 618 #elif MONO 619 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 620 #else 621 [Fact] 622 #endif OutOfProcNodeForwardCertainpropertiesAlsoGetResultsFromCache()623 public void OutOfProcNodeForwardCertainpropertiesAlsoGetResultsFromCache() 624 { 625 string tempProject = _env.CreateFile(".proj").Path; 626 627 string contents = CleanupFileContents($@" 628 <Project ToolsVersion='msbuilddefaulttoolsversion' DefaultTargets='Build' xmlns='msbuildnamespace'> 629 <Target Name='Build'> 630 <MsBuild Projects='{tempProject}' Targets='BuildA'/> 631 <MsBuild Projects='{tempProject}' Targets='BuildA'/> 632 </Target> 633 </Project> 634 "); 635 636 string projectContents = CleanupFileContents(@" 637 <Project ToolsVersion='msbuilddefaulttoolsversion' DefaultTargets='Build' xmlns='msbuildnamespace'> 638 <PropertyGroup> 639 <InitialProperty1>InitialProperty1</InitialProperty1> 640 <InitialProperty2>InitialProperty2</InitialProperty2> 641 <InitialProperty3>InitialProperty3</InitialProperty3> 642 </PropertyGroup> 643 <Target Name='BuildA'> 644 <Message Text='BuildA' Importance='High'/> 645 <Message Text='[success]'/> 646 </Target> 647 </Project> 648 "); 649 650 File.WriteAllText(tempProject, projectContents); 651 652 _env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", "InitialProperty3;IAMNOTREAL"); 653 _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1"); 654 655 var project = CreateProject(contents, null, _projectCollection, false); 656 var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(), 657 MSBuildDefaultToolsVersion, new string[] { }, null); 658 659 BuildResult result = _buildManager.Build(_parameters, data); 660 Assert.Equal(BuildResultCode.Success, result.OverallResult); 661 _logger.AssertLogContains("[success]"); 662 Assert.Equal(3, _logger.ProjectStartedEvents.Count); 663 664 ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[1]; 665 666 // After conversion to xunit, this test sometimes fails at this assertion. 667 // Related to shared state that the test touches that's getting handled 668 // differently in xunit? 669 Assert.NotNull(projectStartedEvent.Properties); 670 671 Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties); 672 673 Assert.NotNull(properties); 674 Assert.Equal(1, properties.Count); 675 676 string propertyValue; 677 Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue)); 678 Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase)); 679 680 projectStartedEvent = _logger.ProjectStartedEvents[2]; 681 Assert.Null(projectStartedEvent.Properties); 682 } 683 684 /// <summary> 685 /// Make sure when if the environment variable MsBuildForwardPropertiesFromChild is set to empty and 686 /// we launch a child node that we get no properties 687 /// </summary> 688 #if MONO 689 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 690 #else 691 [Fact] 692 #endif ForwardNoPropertiesLaunchChildNode()693 public void ForwardNoPropertiesLaunchChildNode() 694 { 695 string contents = CleanupFileContents(@" 696 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 697 <PropertyGroup> 698 <InitialProperty1>InitialProperty1</InitialProperty1> 699 <InitialProperty2>InitialProperty2</InitialProperty2> 700 <InitialProperty3>InitialProperty3</InitialProperty3> 701 </PropertyGroup> 702 <Target Name='test'> 703 <Message Text='[success]'/> 704 </Target> 705 </Project> 706 "); 707 708 _env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", ""); 709 _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1"); 710 711 var project = CreateProject(contents, null, _projectCollection, false); 712 var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(), 713 MSBuildDefaultToolsVersion, new string[] { }, null); 714 BuildResult result = _buildManager.Build(_parameters, data); 715 Assert.Equal(BuildResultCode.Success, result.OverallResult); 716 717 _logger.AssertLogContains("[success]"); 718 Assert.Equal(1, _logger.ProjectStartedEvents.Count); 719 720 ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0]; 721 Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties); 722 Assert.Null(properties); 723 } 724 725 /// <summary> 726 /// We want to pass the toolsets from the parent to the child nodes so that any custom toolsets 727 /// defined on the parent are also available on the child nodes for tasks which use the global project 728 /// collection 729 /// </summary> 730 #if RUNTIME_TYPE_NETCORE 731 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/933")] 732 #elif MONO 733 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 734 #else 735 [Fact] 736 #endif VerifyCustomToolSetsPropagated()737 public void VerifyCustomToolSetsPropagated() 738 { 739 string netFrameworkDirectory = ToolLocationHelper.GetPathToDotNetFrameworkReferenceAssemblies(TargetDotNetFrameworkVersion.Version45); 740 741 string contents = CleanupFileContents(@" 742 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 743 <UsingTask TaskName='VerifyGlobalProjectCollection' TaskFactory='CodeTaskFactory' AssemblyFile='$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll'> 744 <Task> 745 <Using Namespace='Microsoft.Build.Evaluation'/> 746 <Reference Include='$(MSBuildToolsPath)\Microsoft.Build.dll'/> 747 <Code Type='Method'> 748 <![CDATA[ 749 750 public override bool Execute() 751 { 752 bool foundToolSet = false; 753 foreach(Toolset t in ProjectCollection.GlobalProjectCollection.Toolsets) 754 { 755 if(t.ToolsVersion.Equals(""CustomToolSet"", StringComparison.OrdinalIgnoreCase)) 756 { 757 foundToolSet = true; 758 break; 759 } 760 } 761 762 Log.LogMessage(MessageImportance.High, ""foundToolset:"" + foundToolSet.ToString()); 763 return foundToolSet; 764 } 765 ]]> 766 </Code> 767 </Task> 768 </UsingTask> 769 <Target Name='Build'> 770 <VerifyGlobalProjectCollection/> 771 </Target> 772 </Project>"); 773 774 _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1"); 775 776 ProjectCollection projectCollection = new ProjectCollection(); 777 Toolset newToolSet = new Toolset("CustomToolSet", "c:\\SomePath", projectCollection, null); 778 projectCollection.AddToolset(newToolSet); 779 780 var project = CreateProject(contents, null, projectCollection, false); 781 var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(), 782 MSBuildDefaultToolsVersion, new string[] { }, null); 783 784 BuildParameters customParameters = new BuildParameters(projectCollection); 785 customParameters.Loggers = new ILogger[] { _logger }; 786 BuildResult result = _buildManager.Build(customParameters, data); 787 Assert.Equal(BuildResultCode.Success, result.OverallResult); 788 } 789 790 /// <summary> 791 /// When a child node is launched by default we should not send any properties. 792 /// we launch a child node that we get no properties 793 /// </summary> 794 #if MONO 795 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 796 #else 797 [Fact] 798 #endif ForwardNoPropertiesLaunchChildNodeDefault()799 public void ForwardNoPropertiesLaunchChildNodeDefault() 800 { 801 string contents = CleanupFileContents(@" 802 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 803 <PropertyGroup> 804 <InitialProperty1>InitialProperty1</InitialProperty1> 805 <InitialProperty2>InitialProperty2</InitialProperty2> 806 <InitialProperty3>InitialProperty3</InitialProperty3> 807 </PropertyGroup> 808 <Target Name='test'> 809 <Message Text='[success]'/> 810 </Target> 811 </Project> 812 "); _env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", null); 813 _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1"); 814 815 var project = CreateProject(contents, null, _projectCollection, false); 816 var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(), 817 MSBuildDefaultToolsVersion, new string[] { }, null); 818 BuildResult result = _buildManager.Build(_parameters, data); 819 Assert.Equal(BuildResultCode.Success, result.OverallResult); 820 _logger.AssertLogContains("[success]"); 821 Assert.Equal(1, _logger.ProjectStartedEvents.Count); 822 823 ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0]; 824 Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties); 825 Assert.Null(properties); 826 } 827 828 /// <summary> 829 /// A simple failing build. 830 /// </summary> 831 [Fact] SimpleBuildWithFailure()832 public void SimpleBuildWithFailure() 833 { 834 string contents = CleanupFileContents(@" 835 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 836 <Target Name='test'> 837 <Error Text='[fail]'/> 838 </Target> 839 </Project> 840 "); 841 BuildRequestData data = GetBuildRequestData(contents); 842 BuildResult result = _buildManager.Build(_parameters, data); 843 Assert.Equal(BuildResultCode.Failure, result.OverallResult); 844 _logger.AssertLogContains("[fail]"); 845 } 846 847 /// <summary> 848 /// A build with a message, error and warning, verify that 849 /// we only get errors, warnings, and project started and finished when OnlyLogCriticalEvents is true 850 /// </summary> 851 [Fact] SimpleBuildWithFailureAndWarningOnlyLogCriticalEventsTrue()852 public void SimpleBuildWithFailureAndWarningOnlyLogCriticalEventsTrue() 853 { 854 string contents = CleanupFileContents(@" 855 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 856 <Target Name='test'> 857 <Message Text='[Message]' Importance='high'/> 858 <Warning Text='[warn]'/> 859 <Error Text='[fail]'/> 860 </Target> 861 </Project> 862 "); 863 864 BuildRequestData data = GetBuildRequestData(contents); 865 _parameters.OnlyLogCriticalEvents = true; 866 BuildResult result = _buildManager.Build(_parameters, data); 867 Assert.Equal(BuildResultCode.Failure, result.OverallResult); 868 _logger.AssertLogContains("[fail]"); 869 _logger.AssertLogContains("[warn]"); 870 _logger.AssertLogDoesntContain("[message]"); 871 Assert.Equal(1, _logger.BuildStartedEvents.Count); 872 Assert.Equal(1, _logger.BuildFinishedEvents.Count); 873 Assert.Equal(1, _logger.ProjectStartedEvents.Count); 874 Assert.Equal(1, _logger.ProjectFinishedEvents.Count); 875 Assert.Equal(0, _logger.TargetStartedEvents.Count); 876 Assert.Equal(0, _logger.TargetFinishedEvents.Count); 877 Assert.Equal(0, _logger.TaskStartedEvents.Count); 878 Assert.Equal(0, _logger.TaskFinishedEvents.Count); 879 } 880 881 /// <summary> 882 /// A build with a message, error and warning, verify that 883 /// we only get errors, warnings, messages, task and target messages OnlyLogCriticalEvents is false 884 /// </summary> 885 [Fact] SimpleBuildWithFailureAndWarningOnlyLogCriticalEventsFalse()886 public void SimpleBuildWithFailureAndWarningOnlyLogCriticalEventsFalse() 887 { 888 string contents = CleanupFileContents(@" 889 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 890 <Target Name='test'> 891 <Message Text='[message]' Importance='high'/> 892 <Warning Text='[warn]'/> 893 <Error Text='[fail]'/> 894 </Target> 895 </Project> 896 "); 897 898 BuildRequestData data = GetBuildRequestData(contents); 899 _parameters.OnlyLogCriticalEvents = false; 900 BuildResult result = _buildManager.Build(_parameters, data); 901 Assert.Equal(BuildResultCode.Failure, result.OverallResult); 902 _logger.AssertLogContains("[fail]"); 903 _logger.AssertLogContains("[warn]"); 904 _logger.AssertLogContains("[message]"); 905 Assert.Equal(1, _logger.BuildStartedEvents.Count); 906 Assert.Equal(1, _logger.BuildFinishedEvents.Count); 907 Assert.Equal(1, _logger.ProjectStartedEvents.Count); 908 Assert.Equal(1, _logger.ProjectFinishedEvents.Count); 909 Assert.Equal(1, _logger.TargetStartedEvents.Count); 910 Assert.Equal(1, _logger.TargetFinishedEvents.Count); 911 Assert.Equal(3, _logger.TaskStartedEvents.Count); 912 Assert.Equal(3, _logger.TaskFinishedEvents.Count); 913 } 914 915 /// <summary> 916 /// Submitting a synchronous build request before calling BeginBuild yields an InvalidOperationException. 917 /// </summary> 918 [Fact] BuildRequestWithoutBegin()919 public void BuildRequestWithoutBegin() 920 { 921 Assert.Throws<InvalidOperationException>(() => 922 { 923 BuildRequestData data = new BuildRequestData("foo", new Dictionary<string, string>(), "2.0", new string[0], null); 924 _buildManager.BuildRequest(data); 925 } 926 ); 927 } 928 /// <summary> 929 /// Pending a build request before calling BeginBuild yields an InvalidOperationException. 930 /// </summary> 931 [Fact] PendBuildRequestWithoutBegin()932 public void PendBuildRequestWithoutBegin() 933 { 934 Assert.Throws<InvalidOperationException>(() => 935 { 936 BuildRequestData data = new BuildRequestData("foo", new Dictionary<string, string>(), "2.0", new string[0], null); 937 _buildManager.PendBuildRequest(data); 938 } 939 ); 940 } 941 942 /// <summary> 943 /// Calling EndBuild before BeginBuild yields an InvalidOperationException. 944 /// </summary> 945 [Fact] EndWithoutBegin()946 public void EndWithoutBegin() 947 { 948 Assert.Throws<InvalidOperationException>(() => 949 { 950 _buildManager.EndBuild(); 951 } 952 ); 953 } 954 955 [Fact] DisposeAfterUse()956 public void DisposeAfterUse() 957 { 958 string contents = CleanupFileContents(@" 959 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 960 </Project> 961 "); 962 var project = CreateProject(contents, null, _projectCollection, false); 963 var globalProperties = new Dictionary<string, string>(); 964 var targets = new string[0]; 965 var brd = new BuildRequestData(project.FullPath, globalProperties, null, targets, new HostServices()); 966 using (var bm = new BuildManager()) 967 { 968 bm.Build(new BuildParameters(), brd); 969 } 970 } 971 972 [Fact] DisposeWithoutUse()973 public void DisposeWithoutUse() 974 { 975 var bm = new BuildManager(); 976 bm.Dispose(); 977 } 978 979 /// <summary> 980 /// Calling BeginBuild after BeginBuild has already been called yields an InvalidOperationException. 981 /// </summary> 982 [Fact] OverlappingBegin()983 public void OverlappingBegin() 984 { 985 try 986 { 987 _buildManager.BeginBuild(new BuildParameters()); 988 Assert.Throws<InvalidOperationException>(() => _buildManager.BeginBuild(new BuildParameters())); 989 } 990 finally 991 { 992 // Call EndBuild to get us back into a state that approximates reasonable 993 _buildManager.EndBuild(); 994 } 995 } 996 997 /// <summary> 998 /// Starting and ending a build without submitting any requests is valid. 999 /// </summary> 1000 [Fact] EmptyBuild()1001 public void EmptyBuild() 1002 { 1003 _buildManager.BeginBuild(_parameters); 1004 _buildManager.EndBuild(); 1005 1006 Assert.Equal(0, _logger.ErrorCount); 1007 Assert.Equal(0, _logger.WarningCount); 1008 } 1009 1010 /// <summary> 1011 /// Calling EndBuild after it has already been called yields an InvalidOperationException. 1012 /// </summary> 1013 [Fact] ExtraEnds()1014 public void ExtraEnds() 1015 { 1016 Assert.Throws<InvalidOperationException>(() => 1017 { 1018 _buildManager.BeginBuild(new BuildParameters()); 1019 _buildManager.EndBuild(); 1020 _buildManager.EndBuild(); 1021 } 1022 ); 1023 } 1024 /// <summary> 1025 /// Pending a request after EndBuild has been called yields an InvalidOperationException. 1026 /// </summary> 1027 [Fact] PendBuildRequestAfterEnd()1028 public void PendBuildRequestAfterEnd() 1029 { 1030 Assert.Throws<InvalidOperationException>(() => 1031 { 1032 BuildRequestData data = new BuildRequestData("foo", new Dictionary<string, string>(), "2.0", new string[0], null); 1033 _buildManager.BeginBuild(new BuildParameters()); 1034 _buildManager.EndBuild(); 1035 1036 _buildManager.PendBuildRequest(data); 1037 } 1038 ); 1039 } 1040 1041 /// <summary> 1042 /// Attempting a synchronous build when a build is in progress yields an InvalidOperationException. 1043 /// </summary> 1044 [Fact] BuildDuringBuild()1045 public void BuildDuringBuild() 1046 { 1047 try 1048 { 1049 BuildRequestData data = 1050 new BuildRequestData("foo", new Dictionary<string, string>(), "2.0", new string[0], null); 1051 _buildManager.BeginBuild(new BuildParameters()); 1052 1053 Assert.Throws<InvalidOperationException>(() => { _buildManager.Build(new BuildParameters(), data); }); 1054 } 1055 finally 1056 { 1057 // Call EndBuild to get us back into a state that approximates reasonable 1058 _buildManager.EndBuild(); 1059 } 1060 } 1061 1062 /// <summary> 1063 /// A sequential build. 1064 /// </summary> 1065 [Fact] EndBuildBlocks()1066 public void EndBuildBlocks() 1067 { 1068 string contents = CleanupFileContents(@" 1069 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1070 <Target Name='test'> 1071 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(1)) + @"'/> 1072 <Message Text='[success 1]'/> 1073 </Target> 1074 </Project> 1075 "); 1076 1077 BuildRequestData data = GetBuildRequestData(contents); 1078 _buildManager.BeginBuild(_parameters); 1079 BuildSubmission submission1 = _buildManager.PendBuildRequest(data); 1080 submission1.ExecuteAsync(null, null); 1081 Assert.False(submission1.IsCompleted); 1082 _buildManager.EndBuild(); 1083 Assert.True(submission1.IsCompleted); 1084 _logger.AssertLogContains("[success 1]"); 1085 } 1086 1087 /// <summary> 1088 /// Validate that EndBuild can be called during a submission completion callback. 1089 /// </summary> 1090 [Fact] EndBuildCalledWithinSubmissionCallback()1091 public void EndBuildCalledWithinSubmissionCallback() 1092 { 1093 string contents = CleanupFileContents(@" 1094 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1095 <Target Name='test'> 1096 <Message Text='[success 1]'/> 1097 </Target> 1098 </Project> 1099 "); 1100 1101 BuildRequestData data = GetBuildRequestData(contents); 1102 _buildManager.BeginBuild(_parameters); 1103 BuildSubmission submission1 = _buildManager.PendBuildRequest(data); 1104 AutoResetEvent callbackFinished = new AutoResetEvent(false); 1105 submission1.ExecuteAsync(submission => 1106 { 1107 _buildManager.EndBuild(); 1108 callbackFinished.Set(); 1109 }, null); 1110 1111 // Wait for the build to finish 1112 Assert.True(callbackFinished.WaitOne(5000)); // "Build is hung." 1113 1114 // EndBuild should now have been called, so invoking it again should give us an invalid operation error. 1115 Assert.Throws<InvalidOperationException>(() => _buildManager.EndBuild()); 1116 } 1117 1118 /// <summary> 1119 /// A sequential build. 1120 /// </summary> 1121 [Fact] SequentialBuild()1122 public void SequentialBuild() 1123 { 1124 string contents = CleanupFileContents(@" 1125 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1126 <Target Name='test'> 1127 <Message Text='[success 1]'/> 1128 </Target> 1129 </Project> 1130 "); 1131 1132 string contents2 = CleanupFileContents(@" 1133 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1134 <Target Name='test'> 1135 <Message Text='[success 2]'/> 1136 </Target> 1137 </Project> 1138 "); 1139 1140 BuildRequestData data = GetBuildRequestData(contents); 1141 BuildRequestData data2 = GetBuildRequestData(contents2); 1142 _buildManager.BeginBuild(_parameters); 1143 BuildResult result = _buildManager.BuildRequest(data); 1144 Assert.Equal(BuildResultCode.Success, result.OverallResult); 1145 1146 BuildResult result2 = _buildManager.BuildRequest(data2); 1147 Assert.Equal(BuildResultCode.Success, result2.OverallResult); 1148 _buildManager.EndBuild(); 1149 1150 _logger.AssertLogContains("[success 1]"); 1151 _logger.AssertLogContains("[success 2]"); 1152 } 1153 1154 /// <summary> 1155 /// A sequential build. 1156 /// </summary> 1157 [Fact] OverlappingBuildSubmissions()1158 public void OverlappingBuildSubmissions() 1159 { 1160 string contents = CleanupFileContents(@" 1161 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1162 <Target Name='test'> 1163 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"'/> 1164 <Message Text='[success 1]'/> 1165 </Target> 1166 </Project> 1167 "); 1168 1169 string contents2 = CleanupFileContents(@" 1170 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1171 <Target Name='test'> 1172 <Message Text='[success 2]'/> 1173 </Target> 1174 </Project> 1175 "); 1176 1177 BuildRequestData data = GetBuildRequestData(contents); 1178 BuildRequestData data2 = GetBuildRequestData(contents2); 1179 _buildManager.BeginBuild(_parameters); 1180 BuildSubmission submission1 = _buildManager.PendBuildRequest(data); 1181 submission1.ExecuteAsync(null, null); 1182 BuildResult result2 = _buildManager.BuildRequest(data2); 1183 submission1.WaitHandle.WaitOne(); 1184 BuildResult result = submission1.BuildResult; 1185 _buildManager.EndBuild(); 1186 1187 Assert.Equal(BuildResultCode.Success, result2.OverallResult); 1188 Assert.Equal(BuildResultCode.Success, result.OverallResult); 1189 1190 _logger.AssertLogContains("[success 1]"); 1191 _logger.AssertLogContains("[success 2]"); 1192 } 1193 1194 /// <summary> 1195 /// If two overlapping submissions are executed against the same project, with at least one 1196 /// target involved that skipped, make sure that the second one successfully completes 1197 /// (retrieved from the cache). 1198 /// </summary> 1199 [Fact] OverlappingIdenticalBuildSubmissions()1200 public void OverlappingIdenticalBuildSubmissions() 1201 { 1202 string contents = CleanupFileContents(@" 1203 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1204 <Target Name='test' Condition='false' /> 1205 </Project> 1206 "); 1207 1208 BuildRequestData data = GetBuildRequestData(contents); 1209 BuildRequestData data2 = new BuildRequestData(data.ProjectInstance, data.TargetNames.ToArray(), data.HostServices); 1210 1211 _buildManager.BeginBuild(_parameters); 1212 BuildSubmission submission1 = _buildManager.PendBuildRequest(data); 1213 BuildSubmission submission2 = _buildManager.PendBuildRequest(data2); 1214 1215 submission2.ExecuteAsync(null, null); 1216 submission1.ExecuteAsync(null, null); 1217 1218 submission1.WaitHandle.WaitOne(); 1219 submission2.WaitHandle.WaitOne(); 1220 1221 _buildManager.EndBuild(); 1222 1223 Assert.Equal(BuildResultCode.Success, submission1.BuildResult.OverallResult); 1224 Assert.Equal(BuildResultCode.Success, submission2.BuildResult.OverallResult); 1225 } 1226 1227 /// <summary> 1228 /// With two overlapping submissions, the first of which skips a target and the second 1229 /// of which should not, ensure that the second submission does not, in fact, skip 1230 /// the target. (E.g. despite the fact that the target results are in the cache already 1231 /// as 'skipped', ensure that we retry execution in case conditions have changed.) 1232 /// </summary> 1233 [Fact] OverlappingBuildSubmissions_OnlyOneSucceeds()1234 public void OverlappingBuildSubmissions_OnlyOneSucceeds() 1235 { 1236 string contents = CleanupFileContents(@" 1237 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1238 <Target Name='A' DependsOnTargets='SetProp;MaySkip;UnsetProp' /> 1239 1240 <Target Name='SetProp'> 1241 <PropertyGroup> 1242 <ShouldSkip>true</ShouldSkip> 1243 </PropertyGroup> 1244 </Target> 1245 1246 <Target Name='MaySkip' Condition='!$(ShouldSkip)'> 1247 <Error Text='[ERROR]' /> 1248 </Target> 1249 1250 <Target Name='UnsetProp'> 1251 <PropertyGroup> 1252 <ShouldSkip>false</ShouldSkip> 1253 </PropertyGroup> 1254 </Target> 1255 1256 </Project> 1257 "); 1258 1259 BuildRequestData data = GetBuildRequestData(contents, new[] { "A" }); 1260 BuildRequestData data2 = new BuildRequestData(data.ProjectInstance, new[] { "MaySkip" }, data.HostServices); 1261 1262 _buildManager.BeginBuild(_parameters); 1263 BuildSubmission submission1 = _buildManager.PendBuildRequest(data); 1264 BuildSubmission submission2 = _buildManager.PendBuildRequest(data2); 1265 1266 submission1.ExecuteAsync(null, null); 1267 submission2.ExecuteAsync(null, null); 1268 1269 submission1.WaitHandle.WaitOne(); 1270 submission2.WaitHandle.WaitOne(); 1271 1272 _buildManager.EndBuild(); 1273 1274 Assert.Equal(BuildResultCode.Success, submission1.BuildResult.OverallResult); 1275 Assert.Equal(BuildResultCode.Failure, submission2.BuildResult.OverallResult); 1276 } 1277 1278 /// <summary> 1279 /// Calling EndBuild with an unexecuted submission. 1280 /// </summary> 1281 [Fact] EndWithUnexecutedSubmission()1282 public void EndWithUnexecutedSubmission() 1283 { 1284 string contents = CleanupFileContents(@" 1285 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1286 <Target Name='test'> 1287 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(20)) + @"'/> 1288 <Message Text='[fail]'/> 1289 </Target> 1290 </Project> 1291 "); 1292 BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion); 1293 _buildManager.BeginBuild(_parameters); 1294 _buildManager.PendBuildRequest(data); 1295 _buildManager.EndBuild(); 1296 } 1297 1298 /// <summary> 1299 /// A canceled build with a submission which is not executed yet. 1300 /// </summary> 1301 [Fact] CancelledBuildWithUnexecutedSubmission()1302 public void CancelledBuildWithUnexecutedSubmission() 1303 { 1304 string contents = CleanupFileContents(@" 1305 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1306 <Target Name='test'> 1307 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(20)) + @"'/> 1308 <Message Text='[fail]'/> 1309 </Target> 1310 </Project> 1311 "); 1312 BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion); 1313 _buildManager.BeginBuild(_parameters); 1314 _buildManager.PendBuildRequest(data); 1315 _buildManager.CancelAllSubmissions(); 1316 _buildManager.EndBuild(); 1317 } 1318 1319 /// <summary> 1320 /// A canceled build 1321 /// </summary> 1322 [Fact] 1323 [Trait("Category", "mono-osx-failing")] CancelledBuild()1324 public void CancelledBuild() 1325 { 1326 string contents = CleanupFileContents(@" 1327 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1328 <Target Name='test'> 1329 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(60)) + @"'/> 1330 <Message Text='[fail]'/> 1331 </Target> 1332 </Project> 1333 "); 1334 BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion); 1335 _buildManager.BeginBuild(_parameters); 1336 BuildSubmission asyncResult = _buildManager.PendBuildRequest(data); 1337 1338 asyncResult.ExecuteAsync(null, null); 1339 _buildManager.CancelAllSubmissions(); 1340 asyncResult.WaitHandle.WaitOne(); 1341 BuildResult result = asyncResult.BuildResult; 1342 _buildManager.EndBuild(); 1343 1344 Assert.Equal(BuildResultCode.Failure, result.OverallResult); // "Build should have failed." 1345 _logger.AssertLogDoesntContain("[fail]"); 1346 } 1347 1348 /// <summary> 1349 /// A canceled build which waits for the task to get started before canceling. Because it is a 2.0 task, we should 1350 /// wait until the task finishes normally (cancellation not supported.) 1351 /// </summary> 1352 [Fact] CancelledBuildWithDelay20()1353 public void CancelledBuildWithDelay20() 1354 { 1355 if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null) return; 1356 1357 string contents = CleanupFileContents(@" 1358 <Project xmlns='msbuildnamespace' ToolsVersion='2.0'> 1359 <Target Name='test'> 1360 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(5)) + @"'/> 1361 <Message Text='[fail]'/> 1362 </Target> 1363 </Project> 1364 "); 1365 BuildRequestData data = GetBuildRequestData(contents); 1366 _buildManager.BeginBuild(_parameters); 1367 BuildSubmission asyncResult = _buildManager.PendBuildRequest(data); 1368 asyncResult.ExecuteAsync(null, null); 1369 1370 Thread.Sleep(500); 1371 _buildManager.CancelAllSubmissions(); 1372 asyncResult.WaitHandle.WaitOne(); 1373 BuildResult result = asyncResult.BuildResult; 1374 _buildManager.EndBuild(); 1375 1376 Assert.Equal(BuildResultCode.Failure, result.OverallResult); // "Build should have failed." 1377 _logger.AssertLogDoesntContain("[fail]"); 1378 } 1379 1380 #if FEATURE_TASKHOST 1381 /// <summary> 1382 /// A canceled build which waits for the task to get started before canceling. Because it is a 2.0 task, we should 1383 /// wait until the task finishes normally (cancellation not supported.) 1384 /// </summary> 1385 [Fact] 1386 [Trait("Category", "mono-osx-failing")] CancelledBuildInTaskHostWithDelay20()1387 public void CancelledBuildInTaskHostWithDelay20() 1388 { 1389 if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null) return; 1390 1391 string contents = CleanupFileContents(@" 1392 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1393 <UsingTask TaskName='Microsoft.Build.Tasks.Exec' AssemblyName='Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' TaskFactory='TaskHostFactory' /> 1394 <Target Name='test'> 1395 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(10)) + @"'/> 1396 <Message Text='[fail]'/> 1397 </Target> 1398 </Project> 1399 "); 1400 BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion); 1401 _buildManager.BeginBuild(_parameters); 1402 BuildSubmission asyncResult = _buildManager.PendBuildRequest(data); 1403 asyncResult.ExecuteAsync(null, null); 1404 1405 Thread.Sleep(500); 1406 _buildManager.CancelAllSubmissions(); 1407 asyncResult.WaitHandle.WaitOne(); 1408 BuildResult result = asyncResult.BuildResult; 1409 _buildManager.EndBuild(); 1410 1411 Assert.Equal(BuildResultCode.Failure, result.OverallResult); // "Build should have failed." 1412 _logger.AssertLogDoesntContain("[fail]"); 1413 1414 // Task host should not have exited prematurely 1415 _logger.AssertLogDoesntContain("MSB4217"); 1416 } 1417 #endif 1418 1419 /// <summary> 1420 /// A canceled build which waits for the task to get started before canceling. Because it is a 12.. task, we should 1421 /// cancel the task and exit out after a short period wherein we wait for the task to exit cleanly. 1422 /// </summary> 1423 [Fact] 1424 [Trait("Category", "mono-osx-failing")] CancelledBuildWithDelay40()1425 public void CancelledBuildWithDelay40() 1426 { 1427 string contents = CleanupFileContents(@" 1428 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1429 <Target Name='test'> 1430 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(10)) + @"'/> 1431 <Message Text='[fail]'/> 1432 </Target> 1433 </Project> 1434 "); 1435 BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion); 1436 _buildManager.BeginBuild(_parameters); 1437 BuildSubmission asyncResult = _buildManager.PendBuildRequest(data); 1438 asyncResult.ExecuteAsync(null, null); 1439 1440 Thread.Sleep(500); 1441 _buildManager.CancelAllSubmissions(); 1442 asyncResult.WaitHandle.WaitOne(); 1443 BuildResult result = asyncResult.BuildResult; 1444 _buildManager.EndBuild(); 1445 1446 Assert.Equal(BuildResultCode.Failure, result.OverallResult); // "Build should have failed." 1447 _logger.AssertLogDoesntContain("[fail]"); 1448 } 1449 1450 #if FEATURE_TASKHOST 1451 /// <summary> 1452 /// A canceled build which waits for the task to get started before canceling. Because it is a 12.0 task, we should 1453 /// cancel the task and exit out after a short period wherein we wait for the task to exit cleanly. 1454 /// </summary> 1455 [Fact] 1456 [Trait("Category", "mono-osx-failing")] CancelledBuildInTaskHostWithDelay40()1457 public void CancelledBuildInTaskHostWithDelay40() 1458 { 1459 string contents = CleanupFileContents(@" 1460 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1461 <UsingTask TaskName='Microsoft.Build.Tasks.Exec' AssemblyName='Microsoft.Build.Tasks.Core, Version=msbuildassemblyversion, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' TaskFactory='TaskHostFactory' /> 1462 <Target Name='test'> 1463 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(10)) + @"'/> 1464 <Message Text='[fail]'/> 1465 </Target> 1466 </Project> 1467 "); 1468 BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion); 1469 _buildManager.BeginBuild(_parameters); 1470 BuildSubmission asyncResult = _buildManager.PendBuildRequest(data); 1471 asyncResult.ExecuteAsync(null, null); 1472 1473 Thread.Sleep(500); 1474 _buildManager.CancelAllSubmissions(); 1475 asyncResult.WaitHandle.WaitOne(); 1476 BuildResult result = asyncResult.BuildResult; 1477 _buildManager.EndBuild(); 1478 1479 Assert.Equal(BuildResultCode.Failure, result.OverallResult); // "Build should have failed." 1480 _logger.AssertLogDoesntContain("[fail]"); 1481 1482 // Task host should not have exited prematurely 1483 _logger.AssertLogDoesntContain("MSB4217"); 1484 } 1485 #endif 1486 1487 /// <summary> 1488 /// This test verifies that builds of the same project instance in sequence are permitted. 1489 /// </summary> 1490 [Fact] SequentialBuildsOfTheSameProjectAllowed()1491 public void SequentialBuildsOfTheSameProjectAllowed() 1492 { 1493 string contents = CleanupFileContents(@" 1494 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1495 <Target Name='target1'> 1496 <Message Text='text'/> 1497 </Target> 1498 <Target Name='target2'> 1499 <Message Text='text'/> 1500 </Target> 1501 </Project> 1502 "); 1503 Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true); 1504 ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); 1505 _buildManager.BeginBuild(_parameters); 1506 BuildResult result1 = _buildManager.BuildRequest(new BuildRequestData(instance, new[] {"target1"})); 1507 BuildResult result2 = _buildManager.BuildRequest(new BuildRequestData(instance, new[] {"target2"})); 1508 _buildManager.EndBuild(); 1509 1510 Assert.Equal(BuildResultCode.Success, result1.OverallResult); 1511 Assert.True(result1.HasResultsForTarget("target1")); // "Results for target1 missing" 1512 Assert.Equal(BuildResultCode.Success, result2.OverallResult); 1513 Assert.True(result2.HasResultsForTarget("target2")); // "Results for target2 missing" 1514 } 1515 1516 /// <summary> 1517 /// This test verifies that overlapping builds of the same project are allowed. 1518 /// </summary> 1519 [Fact] OverlappingBuildsOfTheSameProjectDifferentTargetsAreAllowed()1520 public void OverlappingBuildsOfTheSameProjectDifferentTargetsAreAllowed() 1521 { 1522 string contents = CleanupFileContents(@" 1523 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1524 <Target Name='target1'> 1525 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(3)) + @"'/> 1526 <Message Text='text'/> 1527 </Target> 1528 <Target Name='target2'> 1529 <Message Text='text'/> 1530 </Target> 1531 </Project> 1532 "); 1533 1534 Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true); 1535 ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); 1536 _buildManager.BeginBuild(_parameters); 1537 1538 BuildSubmission submission =_buildManager.PendBuildRequest(new BuildRequestData(instance, new[] {"target1"})); 1539 submission.ExecuteAsync(null, null); 1540 BuildResult result2 =_buildManager.BuildRequest(new BuildRequestData(project.CreateProjectInstance(), new[] {"target2"})); 1541 1542 submission.WaitHandle.WaitOne(); 1543 var result1 = submission.BuildResult; 1544 1545 Assert.Equal(BuildResultCode.Success, result1.OverallResult); 1546 Assert.True(result1.HasResultsForTarget("target1")); // "Results for target1 missing" 1547 Assert.Equal(BuildResultCode.Success, result2.OverallResult); 1548 Assert.True(result2.HasResultsForTarget("target2")); // "Results for target2 missing" 1549 _buildManager.EndBuild(); 1550 } 1551 1552 /// <summary> 1553 /// This test verifies that overlapping builds of the same project are allowed. 1554 /// </summary> 1555 [Fact] OverlappingBuildsOfTheSameProjectSameTargetsAreAllowed()1556 public void OverlappingBuildsOfTheSameProjectSameTargetsAreAllowed() 1557 { 1558 string contents = CleanupFileContents(@" 1559 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1560 <Target Name='target1'> 1561 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(3)) + @"'/> 1562 <Message Text='text'/> 1563 </Target> 1564 <Target Name='target2'> 1565 <Message Text='text'/> 1566 </Target> 1567 </Project> 1568 "); 1569 Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true); 1570 ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); 1571 _buildManager.BeginBuild(_parameters); 1572 1573 BuildSubmission submission = _buildManager.PendBuildRequest(new BuildRequestData(instance, new[] {"target1"})); 1574 submission.ExecuteAsync(null, null); 1575 BuildResult result2 = _buildManager.BuildRequest(new BuildRequestData(project.CreateProjectInstance(), new[] {"target1"})); 1576 submission.WaitHandle.WaitOne(); 1577 var result1 = submission.BuildResult; 1578 1579 Assert.Equal(BuildResultCode.Success, result1.OverallResult); 1580 Assert.True(result1.HasResultsForTarget("target1")); // "Results for target1 missing" 1581 Assert.Equal(BuildResultCode.Success, result2.OverallResult); 1582 Assert.True(result2.HasResultsForTarget("target1")); // "Results for target1 (second call) missing" 1583 _buildManager.EndBuild(); 1584 } 1585 1586 /// <summary> 1587 /// This test verifies that the out-of-proc node won't lock the directory containing the target project. 1588 /// </summary> 1589 #if RUNTIME_TYPE_NETCORE 1590 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/933")] 1591 #else 1592 [Fact] 1593 #endif OutOfProcNodeDoesntLockWorkingDirectory()1594 public void OutOfProcNodeDoesntLockWorkingDirectory() 1595 { 1596 string contents = CleanupFileContents(@" 1597 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1598 <Target Name='test'> 1599 <Message Text='[success]'/> 1600 </Target> 1601 </Project> 1602 "); 1603 1604 var projectFolder = _env.CreateFolder(); 1605 string projectFile = _env.CreateFile(projectFolder, ".proj").Path; 1606 1607 File.WriteAllText(projectFile, contents); 1608 _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1"); 1609 BuildRequestData data = new BuildRequestData(projectFile, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new string[] { }, null); 1610 _buildManager.Build(_parameters, data); 1611 } 1612 1613 /// <summary> 1614 /// Retrieving a ProjectInstance from the BuildManager stores it in the cache 1615 /// </summary> 1616 [Fact] ProjectInstanceStoredInCache()1617 public void ProjectInstanceStoredInCache() 1618 { 1619 string contents = CleanupFileContents(@" 1620 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1621 <Target Name='test'> 1622 <Message Text='text'/> 1623 </Target> 1624 </Project> 1625 "); 1626 Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true); 1627 ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); 1628 ProjectInstance instance2 = _buildManager.GetProjectInstanceForBuild(project); 1629 1630 Assert.Equal(instance, instance2); // "Instances don't match" 1631 } 1632 1633 /// <summary> 1634 /// Retrieving a ProjectInstance from the BuildManager after a build. 1635 /// </summary> 1636 [Fact] ProjectInstanceRetrievedAfterBuildMatchesSourceProject()1637 public void ProjectInstanceRetrievedAfterBuildMatchesSourceProject() 1638 { 1639 string contents = CleanupFileContents(@" 1640 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1641 <Target Name='test'> 1642 <PropertyGroup> 1643 <Foo>bar</Foo> 1644 </PropertyGroup> 1645 <Message Text='[success]'/> 1646 </Target> 1647 </Project> 1648 "); 1649 IBuildComponentHost host = _buildManager; 1650 host.GetComponent(BuildComponentType.ConfigCache); 1651 BuildRequestData data = GetBuildRequestData(contents); 1652 _buildManager.Build(_parameters, data); 1653 1654 Project project = _projectCollection.LoadProject(data.ProjectFullPath); 1655 ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); 1656 Assert.Equal(instance.GetPropertyValue("Foo"), "bar"); 1657 } 1658 1659 /// <summary> 1660 /// Retrieving a ProjectInstance after resetting the cache clears the instances. 1661 /// </summary> 1662 [Fact] ResetCacheClearsInstances()1663 public void ResetCacheClearsInstances() 1664 { 1665 string contents = CleanupFileContents(@" 1666 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1667 <Target Name='test'> 1668 <PropertyGroup> 1669 <Foo>bar</Foo> 1670 </PropertyGroup> 1671 <Message Text='[success]'/> 1672 </Target> 1673 </Project> 1674 "); 1675 IBuildComponentHost host = _buildManager; 1676 host.GetComponent(BuildComponentType.ConfigCache); 1677 BuildRequestData data = GetBuildRequestData(contents); 1678 _buildManager.Build(_parameters, data); 1679 1680 Project project = _projectCollection.LoadProject(data.ProjectFullPath); 1681 ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); 1682 Assert.Equal("bar", instance.GetPropertyValue("Foo")); 1683 1684 _buildManager.BeginBuild(_parameters); 1685 _buildManager.EndBuild(); 1686 1687 instance = _buildManager.GetProjectInstanceForBuild(project); 1688 Assert.Null(instance.GetProperty("Foo")); 1689 } 1690 1691 /// <summary> 1692 /// Retrieving a ProjectInstance after another build without resetting the cache keeps the existing instance 1693 /// </summary> 1694 [Fact] DisablingCacheResetKeepsInstance()1695 public void DisablingCacheResetKeepsInstance() 1696 { 1697 string contents = CleanupFileContents(@" 1698 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1699 <Target Name='test'> 1700 <PropertyGroup> 1701 <Foo>bar</Foo> 1702 </PropertyGroup> 1703 <Message Text='[success]'/> 1704 </Target> 1705 </Project> 1706 "); 1707 IBuildComponentHost host = _buildManager; 1708 host.GetComponent(BuildComponentType.ConfigCache); 1709 BuildRequestData data = GetBuildRequestData(contents); 1710 _buildManager.Build(_parameters, data); 1711 1712 Project project = _projectCollection.LoadProject(data.ProjectFullPath); 1713 ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); 1714 Assert.Equal(instance.GetPropertyValue("Foo"), "bar"); 1715 1716 _logger.ClearLog(); 1717 _parameters.ResetCaches = false; 1718 _buildManager.BeginBuild(_parameters); 1719 _buildManager.BuildRequest(data); 1720 _buildManager.EndBuild(); 1721 1722 // We should have built the same instance, with the same results, so the target will be skipped. 1723 string skippedMessage = ResourceUtilities.FormatResourceString("TargetAlreadyCompleteSuccess", "test"); 1724 Assert.Equal(true, _logger.FullLog.Contains(skippedMessage)); 1725 1726 ProjectInstance instance2 = _buildManager.GetProjectInstanceForBuild(project); 1727 Assert.Equal(instance, instance2); // "Instances are not the same" 1728 } 1729 1730 /// <summary> 1731 /// Retrieving a ProjectInstance after another build without resetting the cache keeps the existing instance 1732 /// </summary> 1733 [Fact] GhostProjectRootElementCache()1734 public void GhostProjectRootElementCache() 1735 { 1736 string p2pProject = _env.CreateFile(".Project2.proj").Path; 1737 1738 string contents1 = CleanupFileContents($@" 1739 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1740 <Target Name='test'> 1741 <Msbuild Projects='{p2pProject}'> 1742 <Output TaskParameter='TargetOutputs' ItemName='P2pOutput'/> 1743 </Msbuild> 1744 1745 <Message Text='Value:@(P2pOutput)' Importance='high'/> 1746 </Target> 1747 </Project> 1748 "); 1749 1750 string contents2 = CleanupFileContents(@" 1751 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1752 <PropertyGroup> 1753 <Bar Condition=""'$(Bar)' == ''"">Baz</Bar> 1754 </PropertyGroup> 1755 1756 <Target Name='test' Returns='$(Bar)'/> 1757 </Project> 1758 "); 1759 IBuildComponentHost host = _buildManager; 1760 host.GetComponent(BuildComponentType.ConfigCache); 1761 1762 // Create Project 1 1763 ProjectInstance projectInstance = CreateProjectInstance(contents1, null, _projectCollection, false); 1764 BuildRequestData data = new BuildRequestData(projectInstance, new string[0]); 1765 1766 _logger.ClearLog(); 1767 1768 // Write the second project to disk and load it into its own project collection 1769 ProjectCollection projectCollection2 = new ProjectCollection(); 1770 File.WriteAllText(p2pProject, contents2); 1771 1772 Project project2 = projectCollection2.LoadProject(p2pProject); 1773 1774 _parameters.ResetCaches = false; 1775 1776 // Build the first project to make sure we get the expected default values out for the p2p call. 1777 _parameters.ProjectRootElementCache = _projectCollection.ProjectRootElementCache; 1778 _buildManager.BeginBuild(_parameters); 1779 _buildManager.BuildRequest(data); 1780 _buildManager.EndBuild(); 1781 1782 _logger.AssertLogContains("Value:Baz"); 1783 _logger.ClearLog(); 1784 1785 // Modify the property in the second project and save it to disk. 1786 project2.SetProperty("Bar", "FOO"); 1787 project2.Save(); 1788 1789 // Create a new build. 1790 ProjectInstance projectInstance2 = CreateProjectInstance(contents1, null, _projectCollection, false); 1791 BuildRequestData data2 = new BuildRequestData(projectInstance2, new string[0]); 1792 1793 // Build again. 1794 _parameters.ResetCaches = false; 1795 _buildManager.BeginBuild(_parameters); 1796 _buildManager.BuildRequest(data2); 1797 _buildManager.EndBuild(); 1798 _logger.AssertLogContains("Value:FOO"); 1799 } 1800 1801 /// <summary> 1802 /// Verifies that explicitly loaded projects' imports are all marked as also explicitly loaded. 1803 /// </summary> 1804 [Fact] VerifyImportedProjectRootElementsInheritExplicitLoadFlag()1805 public void VerifyImportedProjectRootElementsInheritExplicitLoadFlag() 1806 { 1807 string contents1 = CleanupFileContents(@" 1808 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1809 <Import Project='{0}' /> 1810 <Target Name='test' /> 1811 </Project> 1812 "); 1813 1814 string contents2 = CleanupFileContents(@" 1815 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1816 <PropertyGroup> 1817 <ImportedProperty>ImportedValue</ImportedProperty> 1818 </PropertyGroup> 1819 </Project> 1820 "); 1821 1822 string importedProjectPath = _env.CreateFile(".proj").Path; 1823 string rootProjectPath = _env.CreateFile(".proj").Path; 1824 1825 File.WriteAllText(importedProjectPath, contents2); 1826 File.WriteAllText(rootProjectPath, String.Format(CultureInfo.InvariantCulture, contents1, importedProjectPath)); 1827 1828 var projectCollection = new ProjectCollection(); 1829 1830 // Run a simple build just to prove that nothing is left in the cache. 1831 BuildRequestData data = new BuildRequestData(rootProjectPath, ReadOnlyEmptyDictionary<string, string>.Instance, null, new[] { "test" }, null); 1832 _parameters.ResetCaches = true; 1833 _parameters.ProjectRootElementCache = projectCollection.ProjectRootElementCache; 1834 _buildManager.BeginBuild(_parameters); 1835 _buildManager.BuildRequest(data); 1836 _buildManager.EndBuild(); 1837 _buildManager.ResetCaches(); 1838 1839 // The semantic of TryOpen is to only retrieve the PRE if it is already in the weak cache. 1840 Assert.Null(ProjectRootElement.TryOpen(rootProjectPath, projectCollection)); // "The built project shouldn't be in the cache anymore." 1841 Assert.Null(ProjectRootElement.TryOpen(importedProjectPath, projectCollection)); // "The built project's import shouldn't be in the cache anymore." 1842 1843 Project project = projectCollection.LoadProject(rootProjectPath); 1844 ProjectRootElement preRoot, preImported; 1845 Assert.NotNull(preRoot = ProjectRootElement.TryOpen(rootProjectPath, projectCollection)); // "The root project file should be in the weak cache." 1846 Assert.NotNull(preImported = ProjectRootElement.TryOpen(importedProjectPath, projectCollection)); // "The imported project file should be in the weak cache." 1847 Assert.True(preRoot.IsExplicitlyLoaded); 1848 Assert.True(preImported.IsExplicitlyLoaded); 1849 1850 // Run a simple build just to prove that it doesn't impact what is in the cache. 1851 data = new BuildRequestData(rootProjectPath, ReadOnlyEmptyDictionary<string, string>.Instance, null, new[] { "test" }, null); 1852 _parameters.ResetCaches = true; 1853 _parameters.ProjectRootElementCache = projectCollection.ProjectRootElementCache; 1854 _buildManager.BeginBuild(_parameters); 1855 _buildManager.BuildRequest(data); 1856 _buildManager.EndBuild(); 1857 _buildManager.ResetCaches(); 1858 1859 // Now make sure they are still in the weak cache. Since they were loaded explicitly before the build, the build shouldn't have unloaded them from the cache. 1860 Assert.Same(preRoot, ProjectRootElement.TryOpen(rootProjectPath, projectCollection)); // "The root project file should be in the weak cache after a build." 1861 Assert.Same(preImported, ProjectRootElement.TryOpen(importedProjectPath, projectCollection)); // "The imported project file should be in the weak cache after a build." 1862 Assert.True(preRoot.IsExplicitlyLoaded); 1863 Assert.True(preImported.IsExplicitlyLoaded); 1864 1865 projectCollection.UnloadProject(project); 1866 projectCollection.UnloadAllProjects(); 1867 Assert.Null(ProjectRootElement.TryOpen(rootProjectPath, projectCollection)); // "The unloaded project shouldn't be in the cache anymore." 1868 Assert.Null(ProjectRootElement.TryOpen(importedProjectPath, projectCollection)); // "The unloaded project's import shouldn't be in the cache anymore." 1869 } 1870 1871 /// <summary> 1872 /// Verify that using a second BuildManager doesn't cause the system to crash. 1873 /// </summary> 1874 [Fact] Regress251333()1875 public void Regress251333() 1876 { 1877 string contents = CleanupFileContents(@" 1878 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1879 <PropertyGroup> 1880 <InitialProperty1>InitialProperty1</InitialProperty1> 1881 <InitialProperty2>InitialProperty2</InitialProperty2> 1882 <InitialProperty3>InitialProperty3</InitialProperty3> 1883 </PropertyGroup> 1884 <Target Name='test'> 1885 <Message Text='[success]'/> 1886 </Target> 1887 </Project> 1888 "); 1889 1890 // First a normal build 1891 BuildRequestData data = GetBuildRequestData(contents); 1892 BuildResult result = _buildManager.Build(_parameters, data); 1893 Assert.Equal(result.OverallResult, BuildResultCode.Success); 1894 1895 // Now a build using a different build manager. 1896 using (BuildManager newBuildManager = new BuildManager()) 1897 { 1898 GetBuildRequestData(contents); 1899 BuildResult result2 = newBuildManager.Build(_parameters, data); 1900 Assert.Equal(result2.OverallResult, BuildResultCode.Success); 1901 } 1902 } 1903 1904 /// <summary> 1905 /// Verify that disabling the in-proc node doesn't cause projects which don't require it to fail. 1906 /// </summary> 1907 #if MONO 1908 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 1909 #else 1910 [Fact] 1911 #endif Regress239661()1912 public void Regress239661() 1913 { 1914 string contents = CleanupFileContents(@" 1915 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1916 <PropertyGroup> 1917 <InitialProperty1>InitialProperty1</InitialProperty1> 1918 <InitialProperty2>InitialProperty2</InitialProperty2> 1919 <InitialProperty3>InitialProperty3</InitialProperty3> 1920 </PropertyGroup> 1921 <Target Name='test'> 1922 <Message Text='[success]'/> 1923 </Target> 1924 </Project> 1925 "); 1926 1927 string fileName = _env.CreateFile(".proj").Path; 1928 File.WriteAllText(fileName, contents); 1929 BuildRequestData data = new BuildRequestData(fileName, _projectCollection.GlobalProperties, MSBuildDefaultToolsVersion, new string[0], null); 1930 _parameters.DisableInProcNode = true; 1931 BuildResult result = _buildManager.Build(_parameters, data); 1932 Assert.Equal(BuildResultCode.Success, result.OverallResult); 1933 _logger.AssertLogContains("[success]"); 1934 } 1935 1936 /// <summary> 1937 /// Verify that disabling the in-proc node when a project requires it will cause the build to fail, but not crash. 1938 /// </summary> 1939 [Fact] Regress239661_NodeUnavailable()1940 public void Regress239661_NodeUnavailable() 1941 { 1942 string contents = CleanupFileContents(@" 1943 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1944 <PropertyGroup> 1945 <InitialProperty1>InitialProperty1</InitialProperty1> 1946 <InitialProperty2>InitialProperty2</InitialProperty2> 1947 <InitialProperty3>InitialProperty3</InitialProperty3> 1948 </PropertyGroup> 1949 <Target Name='test'> 1950 <Message Text='[success]'/> 1951 </Target> 1952 </Project> 1953 "); 1954 BuildRequestData data = GetBuildRequestData(contents); 1955 _parameters.DisableInProcNode = true; 1956 1957 // Require that this project build on the in-proc node, which will not be available. 1958 data.HostServices.SetNodeAffinity(data.ProjectFullPath, NodeAffinity.InProc); 1959 BuildResult result = _buildManager.Build(_parameters, data); 1960 Assert.Equal(BuildResultCode.Failure, result.OverallResult); 1961 _logger.AssertLogDoesntContain("[success]"); 1962 _logger.AssertLogContains("MSB4223"); 1963 } 1964 1965 /// <summary> 1966 /// Ensures that properties and items are transferred to the out-of-proc node when an instance is used to start the build. 1967 /// </summary> 1968 #if MONO 1969 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 1970 #else 1971 [Fact] 1972 #endif ProjectInstanceTransfersToOOPNode()1973 public void ProjectInstanceTransfersToOOPNode() 1974 { 1975 string contents = CleanupFileContents(@" 1976 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 1977 <PropertyGroup> 1978 <DeleteMe>deleteme</DeleteMe> 1979 <Unmodified>unmodified</Unmodified> 1980 <VirtualProp>original</VirtualProp> 1981 </PropertyGroup> 1982 <ItemGroup> 1983 <Foo Include='foo'/> 1984 <Foo2 Include='foo2'/> 1985 </ItemGroup> 1986 <Target Name='test'> 1987 <Message Text='[$(DeleteMe)]'/> 1988 <Message Text='[$(Unmodified)]'/> 1989 <Message Text='[$(VirtualProp)]'/> 1990 <Message Text='[$(NewProp)]'/> 1991 <Message Text='[@(Foo)]'/> 1992 <Message Text='[@(Foo2)]'/> 1993 <Message Text='[@(Baz)]'/> 1994 </Target> 1995 </Project> 1996 "); 1997 1998 string fileName = _env.CreateFile(".proj").Path; 1999 File.WriteAllText(fileName, contents); 2000 Project project = new Project(fileName); 2001 ProjectInstance instance = project.CreateProjectInstance(); 2002 instance.RemoveProperty("DeleteMe"); 2003 instance.SetProperty("VirtualProp", "overridden"); 2004 instance.SetProperty("NewProp", "new"); 2005 instance.AddItem("Baz", "baz"); 2006 instance.AddItem("Foo2", "foo21"); 2007 foreach (var item in instance.Items) 2008 { 2009 if (item.EvaluatedInclude == "foo") 2010 { 2011 instance.RemoveItem(item); 2012 break; 2013 } 2014 } 2015 2016 BuildRequestData data = new BuildRequestData(instance, new string[0]); 2017 2018 // Force this to build out-of-proc 2019 _parameters.DisableInProcNode = true; 2020 _buildManager.Build(_parameters, data); 2021 _logger.AssertLogDoesntContain("[deleteme]"); 2022 _logger.AssertLogContains("[overridden]"); 2023 _logger.AssertLogContains("[unmodified]"); 2024 _logger.AssertLogContains("[new]"); 2025 _logger.AssertLogDoesntContain("[foo]"); 2026 _logger.AssertLogContains("[foo2;foo21]"); 2027 _logger.AssertLogContains("[baz]"); 2028 } 2029 2030 /// <summary> 2031 /// Ensures that a limited set of properties are transferred from a project instance to an OOP node. 2032 /// </summary> 2033 #if MONO 2034 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 2035 #else 2036 [Fact] 2037 #endif ProjectInstanceLimitedTransferToOOPNode()2038 public void ProjectInstanceLimitedTransferToOOPNode() 2039 { 2040 string contents = CleanupFileContents(@" 2041 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 2042 <PropertyGroup> 2043 <Unmodified>unmodified</Unmodified> 2044 <VirtualProp>original</VirtualProp> 2045 </PropertyGroup> 2046 <Target Name='test'> 2047 <Message Text='[$(Unmodified)]'/> 2048 <Message Text='[$(VirtualProp)]'/> 2049 </Target> 2050 </Project> 2051 "); 2052 2053 string fileName = _env.CreateFile(".proj").Path; 2054 File.WriteAllText(fileName, contents); 2055 Project project = new Project(fileName); 2056 ProjectInstance instance = project.CreateProjectInstance(); 2057 instance.SetProperty("VirtualProp", "overridden"); 2058 instance.SetProperty("Unmodified", "changed"); 2059 2060 BuildRequestData data = new BuildRequestData(instance, new string[0], null, BuildRequestDataFlags.None, new string[] { "VirtualProp" }); 2061 2062 // Force this to build out-of-proc 2063 _parameters.DisableInProcNode = true; 2064 _buildManager.Build(_parameters, data); 2065 _logger.AssertLogContains("[overridden]"); 2066 _logger.AssertLogContains("[unmodified]"); 2067 _logger.AssertLogDoesntContain("[changed]"); 2068 } 2069 2070 /// <summary> 2071 /// Tests that cache files are created as expected and their lifetime is controlled appropriately. 2072 /// </summary> 2073 [Fact] 2074 [Trait("Category", "netcore-osx-failing")] 2075 [Trait("Category", "netcore-linux-failing")] 2076 [Trait("Category", "mono-osx-failing")] CacheLifetime()2077 public void CacheLifetime() 2078 { 2079 2080 FileUtilities.ClearCacheDirectory(); 2081 2082 _env.SetEnvironmentVariable("MSBUILDDEBUGFORCECACHING", "1"); 2083 string outerBuildCacheDirectory; 2084 string innerBuildCacheDirectory; 2085 2086 // Do a build with one build manager. 2087 using (var outerBuildManager = new BuildManager()) 2088 { 2089 outerBuildCacheDirectory = BuildAndCheckCache(outerBuildManager, new string[] { }); 2090 2091 // Do another build with a second build manager while the first still exists. Since both BuildManagers 2092 // share a process-wide cache directory, we want to verify that they don't stomp on each other, either 2093 // by accidentally sharing results, or by clearing them away. 2094 using (var innerBuildManager = new BuildManager()) 2095 { 2096 innerBuildCacheDirectory = BuildAndCheckCache(innerBuildManager, new string[] { outerBuildCacheDirectory }); 2097 2098 // Force the cache for this build manager (and only this build manager) to be cleared. It should leave 2099 // behind the results from the other one. 2100 innerBuildManager.ResetCaches(); 2101 } 2102 2103 Assert.False(Directory.Exists(innerBuildCacheDirectory)); // "Inner build cache directory still exists after inner build manager was disposed." 2104 Assert.True(Directory.Exists(outerBuildCacheDirectory)); // "Outer build cache directory doesn't exist after inner build manager was disposed." 2105 2106 // Force the cache for this build manager to be cleared. 2107 outerBuildManager.ResetCaches(); 2108 } 2109 2110 Assert.False(Directory.Exists(outerBuildCacheDirectory)); // "Outer build cache directory still exists after outer build manager was disposed." 2111 } 2112 2113 /// <summary> 2114 /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the 2115 /// overall build result -- and thus the return value of the MSBuild task -- should reflect 2116 /// that failure. 2117 /// </summary> 2118 [Fact] FailedAfterTargetInP2PShouldCauseOverallBuildFailure()2119 public void FailedAfterTargetInP2PShouldCauseOverallBuildFailure() 2120 { 2121 var projA = _env.CreateFile(".proj").Path; 2122 var projB = _env.CreateFile(".proj").Path; 2123 2124 string contentsA = @" 2125 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2126 <Target Name=`Build`> 2127 <MSBuild Projects=`" + projB + @"` /> 2128 2129 <Warning Text=`We shouldn't reach here.` /> 2130 </Target> 2131 </Project> 2132 "; 2133 2134 string contentsB = @" 2135 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2136 <Target Name=`Build`> 2137 <Message Text=`Build` /> 2138 </Target> 2139 2140 <Target Name=`Error` AfterTargets=`Build`> 2141 <Error Text=`Error!` /> 2142 </Target> 2143 </Project> 2144 "; 2145 2146 File.WriteAllText(projA, CleanupFileContents(contentsA)); 2147 File.WriteAllText(projB, CleanupFileContents(contentsB)); 2148 2149 _buildManager.BeginBuild(_parameters); 2150 BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, new[] { "Build" }, new HostServices()); 2151 BuildResult result = _buildManager.PendBuildRequest(data).Execute(); 2152 2153 Assert.Equal(BuildResultCode.Failure, result.OverallResult); 2154 _logger.AssertNoWarnings(); 2155 _buildManager.EndBuild(); 2156 } 2157 2158 /// <summary> 2159 /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the 2160 /// overall build result -- and thus the return value of the MSBuild task -- should reflect 2161 /// that failure. Specifically tests where there are multiple entrypoint targets with 2162 /// AfterTargets, only one of which fails. 2163 /// </summary> 2164 [Fact] FailedAfterTargetInP2PShouldCauseOverallBuildFailure_MultipleEntrypoints()2165 public void FailedAfterTargetInP2PShouldCauseOverallBuildFailure_MultipleEntrypoints() 2166 { 2167 var projA = _env.CreateFile(".proj").Path; 2168 var projB = _env.CreateFile(".proj").Path; 2169 2170 string contentsA = @" 2171 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2172 <Target Name=`Build`> 2173 <MSBuild Projects=`" + projB + @"` Targets=`Build;Build2` /> 2174 2175 <Warning Text=`We shouldn't reach here.` /> 2176 </Target> 2177 </Project> 2178 "; 2179 2180 string contentsB = @" 2181 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2182 <Target Name=`Build`> 2183 <Message Text=`[Build]` /> 2184 </Target> 2185 2186 <Target Name=`Build2`> 2187 <Message Text=`[Build2]` /> 2188 </Target> 2189 2190 <Target Name=`AT1` AfterTargets=`Build`> 2191 <Message Text=`[AT1]` /> 2192 </Target> 2193 2194 <Target Name=`AT2` AfterTargets=`Build2`> 2195 <Message Text=`[AT2]` /> 2196 </Target> 2197 2198 <Target Name=`Error` AfterTargets=`Build2`> 2199 <Error Text=`Error!` /> 2200 </Target> 2201 </Project> 2202 "; 2203 2204 File.WriteAllText(projA, CleanupFileContents(contentsA)); 2205 File.WriteAllText(projB, CleanupFileContents(contentsB)); 2206 2207 _buildManager.BeginBuild(_parameters); 2208 BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, new[] { "Build" }, new HostServices()); 2209 BuildResult result = _buildManager.PendBuildRequest(data).Execute(); 2210 2211 Assert.Equal(BuildResultCode.Failure, result.OverallResult); 2212 _logger.AssertNoWarnings(); 2213 _logger.AssertLogContains("[Build]"); 2214 _logger.AssertLogContains("[Build2]"); 2215 _logger.AssertLogContains("[AT1]"); 2216 _logger.AssertLogContains("[AT2]"); 2217 2218 _buildManager.EndBuild(); 2219 } 2220 2221 /// <summary> 2222 /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the 2223 /// overall build result -- and thus the return value of the MSBuild task -- should reflect 2224 /// that failure. This should also be true if the AfterTarget is an AfterTarget of the 2225 /// entrypoint target. 2226 /// </summary> 2227 [Fact] FailedNestedAfterTargetInP2PShouldCauseOverallBuildFailure()2228 public void FailedNestedAfterTargetInP2PShouldCauseOverallBuildFailure() 2229 { 2230 var projA = _env.CreateFile(".proj").Path; 2231 var projB = _env.CreateFile(".proj").Path; 2232 2233 string contentsA = @" 2234 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2235 <Target Name=`Build`> 2236 <MSBuild Projects=`" + projB + @"` /> 2237 2238 <Warning Text=`We shouldn't reach here.` /> 2239 </Target> 2240 </Project> 2241 "; 2242 2243 string contentsB = @" 2244 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2245 <Target Name=`Build`> 2246 <Message Text=`Build` /> 2247 </Target> 2248 2249 <Target Name=`Target1` AfterTargets=`Build`> 2250 <Message Text=`Target1` /> 2251 </Target> 2252 2253 <Target Name=`Error` AfterTargets=`Target1`> 2254 <Error Text=`Error!` /> 2255 </Target> 2256 </Project> 2257 "; 2258 2259 File.WriteAllText(projA, CleanupFileContents(contentsA)); 2260 File.WriteAllText(projB, CleanupFileContents(contentsB)); 2261 2262 _buildManager.BeginBuild(_parameters); 2263 BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, new[] { "Build" }, new HostServices()); 2264 BuildResult result = _buildManager.PendBuildRequest(data).Execute(); 2265 2266 Assert.Equal(BuildResultCode.Failure, result.OverallResult); 2267 _logger.AssertNoWarnings(); 2268 _buildManager.EndBuild(); 2269 } 2270 2271 /// <summary> 2272 /// If a project is called into twice, with two different entrypoint targets that 2273 /// depend on non-overlapping sets of targets, and the first fails, the second 2274 /// should not inherit that failure if all the targets it calls succeed. 2275 /// </summary> 2276 [Fact] NonOverlappingEnusingTrypointTargetsShouldNotInfluenceEachOthersResults()2277 public void NonOverlappingEnusingTrypointTargetsShouldNotInfluenceEachOthersResults() 2278 { 2279 var projA = _env.CreateFile(".proj").Path; 2280 var projB = _env.CreateFile(".proj").Path; 2281 2282 string contentsA = @" 2283 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2284 <Target Name=`Build`> 2285 2286 <Message Text=`The next MSBuild call should FAIL, but the build will continue.` /> 2287 <MSBuild Projects=`" + projB + @"` Targets=`Build` ContinueOnError=`true` /> 2288 <Message Text=`The next MSBuild call should SUCCEED without error.` /> 2289 <MSBuild Projects=`" + projB + @"` Targets=`GetTargetPath` /> 2290 </Target> 2291 </Project> 2292 "; 2293 2294 string contentsB = @" 2295 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2296 <Target Name=`Build`> 2297 <Error Text=`Forced error in Build` /> 2298 </Target> 2299 2300 2301 <Target Name=`GetTargetPath`> 2302 <Message Text=`Success` /> 2303 </Target> 2304 </Project> 2305 "; 2306 2307 File.WriteAllText(projA, CleanupFileContents(contentsA)); 2308 File.WriteAllText(projB, CleanupFileContents(contentsB)); 2309 2310 _buildManager.BeginBuild(_parameters); 2311 BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, new[] { "Build" }, new HostServices()); 2312 BuildResult result = _buildManager.PendBuildRequest(data).Execute(); 2313 2314 Assert.Equal(BuildResultCode.Success, result.OverallResult); 2315 Assert.Equal(1, _logger.ErrorCount); 2316 _buildManager.EndBuild(); 2317 } 2318 2319 /// <summary> 2320 /// In a situation where we have two requests calling into the same project, with different entry point 2321 /// targets, one of which depends on "A;B", the other of which depends on "B", which has a dependency of 2322 /// its own on "A", that we still properly build. 2323 /// </summary> 2324 #if RUNTIME_TYPE_NETCORE 2325 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/933")] 2326 #elif MONO 2327 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 2328 #else 2329 [Fact] 2330 #endif Regress473114()2331 public void Regress473114() 2332 { 2333 var projA = _env.CreateFile(".proj").Path; 2334 var projB = _env.CreateFile(".proj").Path; 2335 var projC = _env.CreateFile(".proj").Path; 2336 var projD = _env.CreateFile(".proj").Path; 2337 2338 string contentsA = @"<?xml version='1.0' encoding='utf-8'?> 2339 <Project ToolsVersion='4.0' DefaultTargets='Build' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 2340 <ItemGroup> 2341 <ProjectReference Include='" + projD + @"' /> 2342 <ProjectReference Include='" + projC + @"' /> 2343 <ProjectReference Include='" + projB + @"' /> 2344 </ItemGroup> 2345 2346 <Target Name='Build'> 2347 <MSBuild Projects='@(ProjectReference)' BuildInParallel='true' /> 2348 </Target> 2349 </Project> 2350 "; 2351 2352 string contentsB = @"<?xml version='1.0' encoding='utf-8'?> 2353 <Project ToolsVersion='4.0' DefaultTargets='CallsGenerateImpLib' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 2354 <Target Name='CallsGenerateImpLib'> 2355 <MSBuild Projects='" + projC + @"' Targets='GenerateImpLib' BuildInParallel='true' /> 2356 </Target> 2357 2358 </Project> 2359 "; 2360 2361 string contentsC = @"<?xml version='1.0' encoding='utf-8'?> 2362 <Project ToolsVersion='4.0' DefaultTargets='Default' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 2363 <Target Name='Default' DependsOnTargets='ResolveReferences;BuildCompile'> 2364 <Message Text='Executed Default' /> 2365 </Target> 2366 2367 <Target Name='GenerateImpLib' DependsOnTargets='BuildCompile'> 2368 <Message Text='Executed GenerateImpLib' /> 2369 </Target> 2370 2371 <Target Name='ResolveReferences'> 2372 <MSBuild Projects='" + projD + @"' Targets='BuildSlower' BuildInParallel='true' /> 2373 </Target> 2374 2375 <Target Name='BuildCompile' DependsOnTargets='ResolveReferences'> 2376 <Message Text='Executed BuildCompile' /> 2377 </Target> 2378 2379 </Project> 2380 "; 2381 2382 string contentsD = @"<?xml version='1.0' encoding='utf-8'?> 2383 <Project ToolsVersion='4.0' DefaultTargets='Build' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 2384 <Target Name='Build'> 2385 <Message Text='In d.proj' /> 2386 </Target> 2387 2388 <Target Name='BuildSlower'> 2389 <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"' /> 2390 </Target> 2391 </Project> 2392 "; 2393 2394 File.WriteAllText(projA, contentsA); 2395 File.WriteAllText(projB, contentsB); 2396 File.WriteAllText(projC, contentsC); 2397 File.WriteAllText(projD, contentsD); 2398 2399 _parameters.MaxNodeCount = 3; 2400 _parameters.EnableNodeReuse = false; 2401 _buildManager.BeginBuild(_parameters); 2402 BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), "4.0", new[] { "Build" }, new HostServices()); 2403 BuildResult result = _buildManager.PendBuildRequest(data).Execute(); 2404 2405 Assert.Equal(BuildResultCode.Success, result.OverallResult); 2406 2407 _buildManager.EndBuild(); 2408 } 2409 2410 /// <summary> 2411 /// If two requests are made for the same project, and they call in with 2412 /// just the right timing such that: 2413 /// - request 1 builds for a while, reaches a P2P, and blocks 2414 /// - request 2 starts building, skips for a while, reaches the above P2P, and 2415 /// blocks waiting for request 1's results 2416 /// - request 1 resumes building, errors, and exits 2417 /// - request 2 resumes building 2418 /// 2419 /// Then request 2 should end up exiting in the same fashion. 2420 /// 2421 /// This simple test verifies that if there are two error targets in a row, the 2422 /// second request will bail out where the first request did, as though it had 2423 /// executed the target, rather than skipping and continuing. 2424 /// </summary> 2425 #if MONO 2426 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 2427 #else 2428 [Fact] 2429 #endif VerifyMultipleRequestForSameProjectWithErrors_Simple()2430 public void VerifyMultipleRequestForSameProjectWithErrors_Simple() 2431 { 2432 var projA = _env.CreateFile(".proj").Path; 2433 var projB = _env.CreateFile(".proj").Path; 2434 var projC = _env.CreateFile(".proj").Path; 2435 2436 string contentsA = $@" 2437 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2438 <Target Name=`Build`> 2439 <MSBuild Projects=`{projB};{projC};{projB}` BuildInParallel=`true` /> 2440 </Target> 2441 </Project>"; 2442 2443 string contentsB = $@" 2444 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2445 <Target Name=`Build` DependsOnTargets=`CallMSBuild;Error1;Error2` /> 2446 <Target Name=`CallMSBuild`> 2447 <MSBuild Projects=`{projC}` Targets=`Sleep` BuildInParallel=`true` /> 2448 </Target> 2449 <Target Name=`Error1`> 2450 <Error Text=`Error 1` /> 2451 </Target> 2452 <Target Name=`Error2`> 2453 <Error Text=`Error 2` /> 2454 </Target> 2455 </Project>"; 2456 2457 string contentsC = @" 2458 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2459 2460 <Target Name=`Build`> 2461 <Message Text=`foo` /> 2462 </Target> 2463 2464 <Target Name=`Sleep`> 2465 <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"` /> 2466 </Target> 2467 </Project>"; 2468 2469 File.WriteAllText(projA, CleanupFileContents(contentsA)); 2470 File.WriteAllText(projB, CleanupFileContents(contentsB)); 2471 File.WriteAllText(projC, CleanupFileContents(contentsC)); 2472 2473 _parameters.MaxNodeCount = 2; 2474 _parameters.EnableNodeReuse = false; 2475 _buildManager.BeginBuild(_parameters); 2476 BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, 2477 new[] {"Build"}, new HostServices()); 2478 BuildResult result = _buildManager.PendBuildRequest(data).Execute(); 2479 2480 Assert.Equal(BuildResultCode.Failure, result.OverallResult); 2481 2482 // We should never get to Error2, because it's supposed to execute after Error1, which failed. 2483 _logger.AssertLogDoesntContain("Error 2"); 2484 2485 // We should, however, end up skipping Error1 on the second call to B. 2486 string skippedMessage = 2487 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteFailure", "Error1"); 2488 _logger.AssertLogContains(skippedMessage); 2489 _buildManager.EndBuild(); 2490 } 2491 2492 /// <summary> 2493 /// If two requests are made for the same project, and they call in with 2494 /// just the right timing such that: 2495 /// - request 1 builds for a while, reaches a P2P, and blocks 2496 /// - request 2 starts building, skips for a while, reaches the above P2P, and 2497 /// blocks waiting for request 1's results 2498 /// - request 1 resumes building, errors, and exits 2499 /// - request 2 resumes building 2500 /// 2501 /// Then request 2 should end up exiting in the same fashion. 2502 /// 2503 /// This simple test verifies that if there are two error targets in a row, and the 2504 /// first has a chain of OnError targets, the OnError targets will all execute as 2505 /// expected in the first request, but be skipped by the second (since if it's "skipping 2506 /// unsuccessful", it can assume that all other OnError targets have also already been run) 2507 /// </summary> 2508 #if MONO 2509 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 2510 #else 2511 [Fact] 2512 #endif VerifyMultipleRequestForSameProjectWithErrors_OnErrorChain()2513 public void VerifyMultipleRequestForSameProjectWithErrors_OnErrorChain() 2514 { 2515 var projA = _env.CreateFile(".proj").Path; 2516 var projB = _env.CreateFile(".proj").Path; 2517 var projC = _env.CreateFile(".proj").Path; 2518 2519 string contentsA = @" 2520 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2521 <Target Name=`Build`> 2522 <MSBuild Projects=`" + String.Join(";", projB, projC, projB) + @"` BuildInParallel=`true` /> 2523 </Target> 2524 </Project> 2525 "; 2526 2527 string contentsB = @" 2528 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2529 <Target Name=`Build` DependsOnTargets=`CallMSBuild;Error1;Error2` /> 2530 <Target Name=`CallMSBuild`> 2531 <MSBuild Projects=`" + projC + @"` Targets=`Sleep` BuildInParallel=`true` /> 2532 </Target> 2533 <Target Name=`Error1`> 2534 <Error Text=`Error 1` /> 2535 <OnError ExecuteTargets=`Target2;Target3` /> 2536 </Target> 2537 <Target Name=`Error2`> 2538 <Error Text=`Error 2` /> 2539 </Target> 2540 2541 <Target Name=`Target2`> 2542 <Error Text=`Error in Target2` /> 2543 <OnError ExecuteTargets=`Target4` /> 2544 </Target> 2545 2546 <Target Name=`Target3`> 2547 <Message Text=`Target 3` /> 2548 </Target> 2549 2550 <Target Name=`Target4`> 2551 <Message Text=`Target 4` /> 2552 </Target> 2553 2554 </Project> 2555 "; 2556 2557 string contentsC = @" 2558 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2559 2560 <Target Name=`Build`> 2561 <Message Text=`foo` /> 2562 </Target> 2563 2564 <Target Name=`Sleep`> 2565 <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"` /> 2566 </Target> 2567 </Project> 2568 "; 2569 2570 File.WriteAllText(projA, CleanupFileContents(contentsA)); 2571 File.WriteAllText(projB, CleanupFileContents(contentsB)); 2572 File.WriteAllText(projC, CleanupFileContents(contentsC)); 2573 2574 _parameters.MaxNodeCount = 2; 2575 _parameters.EnableNodeReuse = false; 2576 _buildManager.BeginBuild(_parameters); 2577 BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, 2578 new[] {"Build"}, new HostServices()); 2579 BuildResult result = _buildManager.PendBuildRequest(data).Execute(); 2580 2581 Assert.Equal(BuildResultCode.Failure, result.OverallResult); 2582 2583 // We should never get to Error2, because it's supposed to execute after Error1, which failed. 2584 _logger.AssertLogDoesntContain("Error 2"); 2585 2586 // We should, however, get to Target2, Target3, and Target4, since they're part of the OnError 2587 // chain for Error1 2588 _logger.AssertLogContains("Error in Target2"); 2589 _logger.AssertLogContains("Target 3"); 2590 _logger.AssertLogContains("Target 4"); 2591 2592 // We should end up skipping Error1 on the second call to B. 2593 string skippedMessage1 = 2594 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteFailure", "Error1"); 2595 _logger.AssertLogContains(skippedMessage1); 2596 2597 // We shouldn't, however, see skip messages for the OnError targets 2598 string skippedMessage2 = 2599 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteFailure", "Target2"); 2600 _logger.AssertLogDoesntContain(skippedMessage2); 2601 2602 string skippedMessage3 = 2603 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteSuccess", "Target3"); 2604 _logger.AssertLogDoesntContain(skippedMessage3); 2605 2606 string skippedMessage4 = 2607 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteSuccess", "Target4"); 2608 _logger.AssertLogDoesntContain(skippedMessage4); 2609 _buildManager.EndBuild(); 2610 } 2611 2612 /// <summary> 2613 /// If two requests are made for the same project, and they call in with 2614 /// just the right timing such that: 2615 /// - request 1 builds for a while, reaches a P2P, and blocks 2616 /// - request 2 starts building, skips for a while, reaches the above P2P, and 2617 /// blocks waiting for request 1's results 2618 /// - request 1 resumes building, errors, and exits 2619 /// - request 2 resumes building 2620 /// 2621 /// Then request 2 should end up exiting in the same fashion. 2622 /// 2623 /// This simple test verifies that if there are two error targets in a row, AND 2624 /// they're marked as ContinueOnError=ErrorAndContinue, then we won't bail, but 2625 /// will continue executing (on the first request) or skipping (on the second) 2626 /// </summary> 2627 #if MONO 2628 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 2629 #else 2630 [Fact] 2631 #endif VerifyMultipleRequestForSameProjectWithErrors_ErrorAndContinue()2632 public void VerifyMultipleRequestForSameProjectWithErrors_ErrorAndContinue() 2633 { 2634 var projA = _env.CreateFile(".proj").Path; 2635 var projB = _env.CreateFile(".proj").Path; 2636 var projC = _env.CreateFile(".proj").Path; 2637 2638 string contentsA = @" 2639 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2640 <Target Name=`Build`> 2641 <MSBuild Projects=`" + String.Join(";", projB, projC, projB) + @"` BuildInParallel=`true` /> 2642 </Target> 2643 </Project> 2644 "; 2645 2646 string contentsB = @" 2647 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2648 <Target Name=`Build` DependsOnTargets=`CallMSBuild;Error1;Error2` /> 2649 <Target Name=`CallMSBuild`> 2650 <MSBuild Projects=`" + projC + @"` Targets=`Sleep` BuildInParallel=`true` /> 2651 </Target> 2652 <Target Name=`Error1`> 2653 <Error Text=`Error 1` ContinueOnError=`ErrorAndContinue` /> 2654 </Target> 2655 <Target Name=`Error2`> 2656 <Error Text=`Error 2` /> 2657 </Target> 2658 </Project> 2659 "; 2660 2661 string contentsC = @" 2662 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2663 2664 <Target Name=`Build`> 2665 <Message Text=`foo` /> 2666 </Target> 2667 2668 <Target Name=`Sleep`> 2669 <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"` /> 2670 </Target> 2671 </Project> 2672 "; 2673 2674 File.WriteAllText(projA, CleanupFileContents(contentsA)); 2675 File.WriteAllText(projB, CleanupFileContents(contentsB)); 2676 File.WriteAllText(projC, CleanupFileContents(contentsC)); 2677 2678 _parameters.MaxNodeCount = 2; 2679 _parameters.EnableNodeReuse = false; 2680 _buildManager.BeginBuild(_parameters); 2681 BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, 2682 new[] {"Build"}, new HostServices()); 2683 BuildResult result = _buildManager.PendBuildRequest(data).Execute(); 2684 2685 Assert.Equal(BuildResultCode.Failure, result.OverallResult); 2686 2687 // We should see both Error1 and Error2 2688 _logger.AssertLogContains("Error 1"); 2689 _logger.AssertLogContains("Error 2"); 2690 2691 // We should also end up skipping them both. 2692 string skippedMessage1 = 2693 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteFailure", "Error1"); 2694 _logger.AssertLogContains(skippedMessage1); 2695 2696 string skippedMessage2 = 2697 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteFailure", "Error2"); 2698 _logger.AssertLogContains(skippedMessage2); 2699 2700 _buildManager.EndBuild(); 2701 } 2702 2703 /// <summary> 2704 /// If two requests are made for the same project, and they call in with 2705 /// just the right timing such that: 2706 /// - request 1 builds for a while, reaches a P2P, and blocks 2707 /// - request 2 starts building, skips for a while, reaches the above P2P, and 2708 /// blocks waiting for request 1's results 2709 /// - request 1 resumes building, errors, and exits 2710 /// - request 2 resumes building 2711 /// 2712 /// Then request 2 should end up exiting in the same fashion. 2713 /// 2714 /// This test verifies that if the errors are in AfterTargets, we still 2715 /// exit as though the target that those targets run after has already run. 2716 /// </summary> 2717 #if MONO 2718 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] 2719 #else 2720 [Fact] 2721 #endif VerifyMultipleRequestForSameProjectWithErrors_AfterTargets()2722 public void VerifyMultipleRequestForSameProjectWithErrors_AfterTargets() 2723 { 2724 var projA = _env.CreateFile(".proj").Path; 2725 var projB = _env.CreateFile(".proj").Path; 2726 var projC = _env.CreateFile(".proj").Path; 2727 2728 string contentsA = @" 2729 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2730 <Target Name=`Build`> 2731 <MSBuild Projects=`" + String.Join(";", projB, projC, projB) + @"` BuildInParallel=`true` /> 2732 </Target> 2733 </Project> 2734 "; 2735 2736 string contentsB = @" 2737 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2738 <Target Name=`Build` DependsOnTargets=`CallMSBuild` /> 2739 <Target Name=`CallMSBuild`> 2740 <MSBuild Projects=`" + projC + @"` Targets=`Sleep` BuildInParallel=`true` /> 2741 </Target> 2742 <Target Name=`Error1` AfterTargets=`CallMSBuild`> 2743 <Error Text=`Error 1` /> 2744 </Target> 2745 <Target Name=`Error2` AfterTargets=`CallMSBuild`> 2746 <Error Text=`Error 2` /> 2747 </Target> 2748 </Project> 2749 "; 2750 2751 string contentsC = @" 2752 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2753 2754 <Target Name=`Build`> 2755 <Message Text=`foo` /> 2756 </Target> 2757 2758 <Target Name=`Sleep`> 2759 <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"` /> 2760 </Target> 2761 </Project> 2762 "; 2763 2764 File.WriteAllText(projA, CleanupFileContents(contentsA)); 2765 File.WriteAllText(projB, CleanupFileContents(contentsB)); 2766 File.WriteAllText(projC, CleanupFileContents(contentsC)); 2767 2768 _parameters.MaxNodeCount = 2; 2769 _parameters.EnableNodeReuse = false; 2770 _buildManager.BeginBuild(_parameters); 2771 BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, 2772 new[] {"Build"}, new HostServices()); 2773 BuildResult result = _buildManager.PendBuildRequest(data).Execute(); 2774 2775 Assert.Equal(BuildResultCode.Failure, result.OverallResult); 2776 2777 // We should never get to Error2, because we should never run its AfterTarget, after 2778 // the AfterTarget with Error1 failed 2779 _logger.AssertLogDoesntContain("Error 2"); 2780 2781 _buildManager.EndBuild(); 2782 } 2783 2784 /// <summary> 2785 /// Related to the two tests above, if two requests are made for the same project, but 2786 /// for different entry targets, and a target fails in the first request, if the second 2787 /// request also runs that target, its skip-unsuccessful should behave in the same 2788 /// way as if the target had actually errored. 2789 /// </summary> 2790 [Fact] VerifyMultipleRequestForSameProjectWithErrors_DifferentEntrypoints()2791 public void VerifyMultipleRequestForSameProjectWithErrors_DifferentEntrypoints() 2792 { 2793 var projA = _env.CreateFile(".proj").Path; 2794 var projB = _env.CreateFile(".proj").Path; 2795 2796 string contentsA = @" 2797 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2798 <ItemGroup> 2799 <PR Include=`" + projB + @"`> 2800 <Targets>Build</Targets> 2801 </PR> 2802 <PR Include=`" + projB + @"`> 2803 <Targets>Build2</Targets> 2804 </PR> 2805 </ItemGroup> 2806 2807 <Target Name=`Build`> 2808 <MSBuild Projects=`@(PR)` Targets=`%(PR.Targets)` ContinueOnError=`true` /> 2809 </Target> 2810 </Project> 2811 "; 2812 2813 string contentsB = @" 2814 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2815 <Target Name=`Build` DependsOnTargets=`Target1;Error1`> 2816 <Message Text=`[Build]` /> 2817 </Target> 2818 <Target Name=`Build2` DependsOnTargets=`Target2;Error1;Error2`> 2819 <Message Text=`[Build2]` /> 2820 </Target> 2821 <Target Name=`Target1`> 2822 <Message Text=`[Target1]` /> 2823 </Target> 2824 <Target Name=`Target2`> 2825 <Message Text=`[Target2]` /> 2826 </Target> 2827 <Target Name=`Error1`> 2828 <Error Text=`[Error1]` /> 2829 </Target> 2830 <Target Name=`Error2`> 2831 <Error Text=`[Error2]` /> 2832 </Target> 2833 </Project> 2834 "; 2835 2836 File.WriteAllText(projA, CleanupFileContents(contentsA)); 2837 File.WriteAllText(projB, CleanupFileContents(contentsB)); 2838 2839 _buildManager.BeginBuild(_parameters); 2840 BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, 2841 new[] {"Build"}, new HostServices()); 2842 BuildResult result = _buildManager.PendBuildRequest(data).Execute(); 2843 2844 Assert.Equal(BuildResultCode.Success, result.OverallResult); 2845 2846 // We should never get to Error2, because it's only ever executed in the second 2847 // request after Error1, which should skip-unsuccessful and exit 2848 _logger.AssertLogDoesntContain("[Error2]"); 2849 2850 _buildManager.EndBuild(); 2851 } 2852 2853 /// <summary> 2854 /// Verify that we can submit multiple simultaneous submissions with 2855 /// legacy threading mode active and successfully build. 2856 /// </summary> 2857 [Fact] TestSimultaneousSubmissionsWithLegacyThreadingData()2858 public void TestSimultaneousSubmissionsWithLegacyThreadingData() 2859 { 2860 string projectContent = @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2861 <Target Name=`Build`> 2862 <!-- Wait 200 ms --> 2863 <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(200)) + @"` /> 2864 </Target> 2865 2866 </Project> 2867 "; 2868 var projectPath1 = _env.CreateFile(".proj").Path; 2869 File.WriteAllText(projectPath1, CleanupFileContents(projectContent)); 2870 2871 Project project1 = new Project(projectPath1); 2872 2873 var projectPath2 = _env.CreateFile(".proj").Path; 2874 File.WriteAllText(projectPath2, CleanupFileContents(projectContent)); 2875 2876 Project project2 = new Project(projectPath2); 2877 2878 ConsoleLogger cl = new ConsoleLogger(); 2879 BuildParameters buildParameters = new BuildParameters(ProjectCollection.GlobalProjectCollection); 2880 buildParameters.Loggers = new ILogger[] { cl }; 2881 buildParameters.LegacyThreadingSemantics = true; 2882 BuildManager.DefaultBuildManager.BeginBuild(buildParameters); 2883 2884 AutoResetEvent project1DoneEvent = new AutoResetEvent(false); 2885 ThreadPool.QueueUserWorkItem(delegate 2886 { 2887 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project1); 2888 2889 BuildRequestData requestData = new BuildRequestData(pi, new[] { "Build" }); 2890 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData); 2891 BuildResult br = submission.Execute(); 2892 project1DoneEvent.Set(); 2893 }); 2894 2895 AutoResetEvent project2DoneEvent = new AutoResetEvent(false); 2896 ThreadPool.QueueUserWorkItem(delegate 2897 { 2898 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project2); 2899 BuildRequestData requestData = new BuildRequestData(pi, new[] { "Build" }); 2900 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData); 2901 BuildResult br = submission.Execute(); 2902 project2DoneEvent.Set(); 2903 }); 2904 2905 project1DoneEvent.WaitOne(); 2906 project2DoneEvent.WaitOne(); 2907 2908 BuildManager.DefaultBuildManager.EndBuild(); 2909 } 2910 2911 /// <summary> 2912 /// Verify that we can submit multiple simultaneous submissions with 2913 /// legacy threading mode active and successfully build, and that one of those 2914 /// submissions can P2P to the other. 2915 /// </summary> 2916 [Fact] TestSimultaneousSubmissionsWithLegacyThreadingData_P2P()2917 public void TestSimultaneousSubmissionsWithLegacyThreadingData_P2P() 2918 { 2919 string projectContent1 = @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2920 <Target Name=`CopyRunEnvironmentFiles`> 2921 <!-- Wait 100 ms --> 2922 <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(100)) + @"` /> 2923 </Target> 2924 2925 <Target Name=`MyConsoleTarget`> 2926 <!-- Wait 100 ms --> 2927 <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(100)) + @"` /> 2928 </Target> 2929 2930 </Project> 2931 "; 2932 2933 var projectPath1 = _env.CreateFile(".proj").Path; 2934 File.WriteAllText(projectPath1, CleanupFileContents(projectContent1)); 2935 2936 Project project1 = new Project(projectPath1); 2937 2938 string projectContent2 = @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 2939 <Target Name=`MSDeployPublish` /> 2940 2941 <Target Name=`DoStuff` AfterTargets=`MSDeployPublish`> 2942 <MSBuild Projects=`" + projectPath1 + @"` Targets=`MyConsoleTarget` /> 2943 </Target> 2944 2945 </Project> 2946 "; 2947 2948 var projectPath2 = _env.CreateFile(".proj").Path; 2949 File.WriteAllText(projectPath2, CleanupFileContents(projectContent2)); 2950 2951 Project project2 = new Project(projectPath2); 2952 2953 ConsoleLogger cl = new ConsoleLogger(); 2954 BuildParameters buildParameters = new BuildParameters(ProjectCollection.GlobalProjectCollection); 2955 buildParameters.Loggers = new ILogger[] { cl }; 2956 buildParameters.LegacyThreadingSemantics = true; 2957 BuildManager.DefaultBuildManager.BeginBuild(buildParameters); 2958 2959 AutoResetEvent project1DoneEvent = new AutoResetEvent(false); 2960 ThreadPool.QueueUserWorkItem(delegate 2961 { 2962 // need to kick off project 2 first so that it project 1 can get submitted before the P2P happens 2963 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project2); 2964 2965 BuildRequestData requestData = new BuildRequestData(pi, new[] { "MSDeployPublish" }); 2966 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData); 2967 BuildResult br = submission.Execute(); 2968 Assert.Equal(BuildResultCode.Success, br.OverallResult); 2969 project1DoneEvent.Set(); 2970 }); 2971 2972 AutoResetEvent project2DoneEvent = new AutoResetEvent(false); 2973 ThreadPool.QueueUserWorkItem(delegate 2974 { 2975 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project1); 2976 BuildRequestData requestData = new BuildRequestData(pi, new string[] { "CopyRunEnvironmentFiles" }); 2977 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData); 2978 BuildResult br = submission.Execute(); 2979 Assert.Equal(BuildResultCode.Success, br.OverallResult); 2980 project2DoneEvent.Set(); 2981 }); 2982 2983 project1DoneEvent.WaitOne(); 2984 project2DoneEvent.WaitOne(); 2985 2986 BuildManager.DefaultBuildManager.EndBuild(); 2987 } 2988 2989 /// <summary> 2990 /// Verify that we can submit multiple simultaneous submissions with 2991 /// legacy threading mode active and successfully build, and that one of those 2992 /// submissions can P2P to the other. 2993 /// 2994 /// A variation of the above test, where multiple nodes are available, so the 2995 /// submissions aren't restricted to running strictly serially by the single in-proc 2996 /// node. 2997 /// </summary> 2998 #if MONO 2999 [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1245")] 3000 #else 3001 [Fact] 3002 #endif TestSimultaneousSubmissionsWithLegacyThreadingData_P2P_MP()3003 public void TestSimultaneousSubmissionsWithLegacyThreadingData_P2P_MP() 3004 { 3005 string projectContent1 = @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 3006 <Target Name=`CopyRunEnvironmentFiles`> 3007 <!-- Wait 100 ms --> 3008 <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(100)) + @"` /> 3009 </Target> 3010 3011 <Target Name=`MyConsoleTarget`> 3012 <!-- Wait 100 ms --> 3013 <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(100)) + @"` /> 3014 </Target> 3015 3016 </Project> 3017 "; 3018 3019 var projectPath1 = _env.CreateFile(".proj").Path; 3020 File.WriteAllText(projectPath1, CleanupFileContents(projectContent1)); 3021 3022 Project project1 = new Project(projectPath1); 3023 3024 string projectContent2 = @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> 3025 <Target Name=`MSDeployPublish` /> 3026 3027 <Target Name=`DoStuff` AfterTargets=`MSDeployPublish`> 3028 <MSBuild Projects=`" + projectPath1 + @"` Targets=`MyConsoleTarget` /> 3029 </Target> 3030 3031 </Project> 3032 "; 3033 3034 var projectPath2 = _env.CreateFile(".proj").Path; 3035 File.WriteAllText(projectPath2, CleanupFileContents(projectContent2)); 3036 3037 Project project2 = new Project(projectPath2); 3038 3039 ConsoleLogger cl = new ConsoleLogger(); 3040 BuildParameters buildParameters = new BuildParameters(ProjectCollection.GlobalProjectCollection); 3041 buildParameters.Loggers = new ILogger[] { cl }; 3042 buildParameters.LegacyThreadingSemantics = true; 3043 buildParameters.MaxNodeCount = 2; 3044 buildParameters.EnableNodeReuse = false; 3045 BuildManager.DefaultBuildManager.BeginBuild(buildParameters); 3046 3047 AutoResetEvent project1DoneEvent = new AutoResetEvent(false); 3048 ThreadPool.QueueUserWorkItem(delegate 3049 { 3050 // need to kick off project 2 first so that it project 1 can get submitted before the P2P happens 3051 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project2); 3052 3053 BuildRequestData requestData = new BuildRequestData(pi, new string[] { "MSDeployPublish" }); 3054 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData); 3055 BuildResult br = submission.Execute(); 3056 Assert.Equal(BuildResultCode.Success, br.OverallResult); 3057 project1DoneEvent.Set(); 3058 }); 3059 3060 AutoResetEvent project2DoneEvent = new AutoResetEvent(false); 3061 ThreadPool.QueueUserWorkItem(delegate 3062 { 3063 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project1); 3064 BuildRequestData requestData = new BuildRequestData(pi, new string[] { "CopyRunEnvironmentFiles" }); 3065 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData); 3066 BuildResult br = submission.Execute(); 3067 Assert.Equal(BuildResultCode.Success, br.OverallResult); 3068 project2DoneEvent.Set(); 3069 }); 3070 3071 project1DoneEvent.WaitOne(); 3072 project2DoneEvent.WaitOne(); 3073 3074 BuildManager.DefaultBuildManager.EndBuild(); 3075 } 3076 3077 /// <summary> 3078 /// Ensures that properties and items are transferred from an out-of-proc project to an in-proc project. 3079 /// </summary> 3080 /// <remarks> 3081 /// This differs from transferring a project instance to an out-of-proc node because in this case the project 3082 /// was loaded by MSBuild, not supplied directly by the user. 3083 /// </remarks> 3084 [Fact] 3085 [Trait("Category", "mono-osx-failing")] Regress265010()3086 public void Regress265010() 3087 { 3088 string contents = CleanupFileContents(@" 3089 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 3090 <PropertyGroup> 3091 <Prop>BaseValue</Prop> 3092 </PropertyGroup> 3093 <ItemGroup> 3094 <Item Include='BaseItem'/> 3095 </ItemGroup> 3096 <Target Name='BaseTest'> 3097 <Message Text='[$(Prop)]'/> 3098 <Message Text='[@(Item)]'/> 3099 <PropertyGroup> 3100 <Prop>NewValue</Prop> 3101 </PropertyGroup> 3102 <ItemGroup> 3103 <Item Include='NewItem'/> 3104 </ItemGroup> 3105 </Target> 3106 3107 <Target Name='MovedTest'> 3108 <Message Text='[$(Prop)]'/> 3109 <Message Text='[@(Item)]'/> 3110 </Target> 3111 3112 </Project> 3113 "); 3114 3115 string fileName = _env.CreateFile(".proj").Path; 3116 File.WriteAllText(fileName, contents); 3117 _buildManager.BeginBuild(_parameters); 3118 3119 HostServices services = new HostServices(); 3120 services.SetNodeAffinity(fileName, NodeAffinity.OutOfProc); 3121 BuildRequestData data = new BuildRequestData(fileName, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new[] { "BaseTest" }, services); 3122 _buildManager.PendBuildRequest(data).Execute(); 3123 _logger.AssertLogContains("[BaseValue]"); 3124 _logger.AssertLogContains("[BaseItem]"); 3125 _logger.ClearLog(); 3126 3127 _parameters.ResetCaches = false; 3128 services.SetNodeAffinity(fileName, NodeAffinity.InProc); 3129 data = new BuildRequestData(fileName, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new[] { "MovedTest" }, services); 3130 _buildManager.PendBuildRequest(data).Execute(); 3131 _logger.AssertLogContains("[NewValue]"); 3132 _logger.AssertLogContains("[BaseItem;NewItem]"); 3133 _logger.AssertLogDoesntContain("[BaseValue]"); 3134 3135 _buildManager.EndBuild(); 3136 } 3137 3138 /// <summary> 3139 /// Verifies that all warnings are treated as errors and that the overall build result is a failure. 3140 /// </summary> 3141 [Fact] WarningsAreTreatedAsErrorsAll()3142 public void WarningsAreTreatedAsErrorsAll() 3143 { 3144 string contents = CleanupFileContents(@" 3145 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 3146 <Target Name='target1'> 3147 <Warning Text='This warning should be treated as an error' Code='ABC123'/> 3148 <Warning Text='This warning should NOT be treated as an error' /> 3149 </Target> 3150 </Project> 3151 "); 3152 _parameters.WarningsAsErrors = new HashSet<string>(); 3153 3154 Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true); 3155 ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); 3156 _buildManager.BeginBuild(_parameters); 3157 BuildResult result1 = _buildManager.BuildRequest(new BuildRequestData(instance, new[] { "target1" })); 3158 _buildManager.EndBuild(); 3159 3160 Assert.Equal(0, _logger.WarningCount); 3161 Assert.Equal(2, _logger.ErrorCount); 3162 3163 Assert.Equal(BuildResultCode.Failure, result1.OverallResult); 3164 Assert.True(result1.HasResultsForTarget("target1")); 3165 } 3166 3167 /// <summary> 3168 /// Verifies that only the specified warnings are treated as errors and that the overall build result is a failure. 3169 /// </summary> 3170 [Fact] WarningsAreTreatedAsErrorsSpecific()3171 public void WarningsAreTreatedAsErrorsSpecific() 3172 { 3173 string contents = CleanupFileContents(@" 3174 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 3175 <Target Name='target1'> 3176 <Warning Text='This warning should be treated as an error' Code='ABC123'/> 3177 <Warning Text='This warning should NOT be treated as an error' Code='NA123' /> 3178 <Warning Text='This warning should NOT be treated as an error' /> 3179 </Target> 3180 </Project> 3181 "); 3182 _parameters.WarningsAsErrors = new HashSet<string> { "ABC123" }; 3183 3184 Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true); 3185 ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); 3186 _buildManager.BeginBuild(_parameters); 3187 BuildResult result1 = _buildManager.BuildRequest(new BuildRequestData(instance, new string[] { "target1" })); 3188 _buildManager.EndBuild(); 3189 3190 Assert.Equal(2, _logger.WarningCount); 3191 Assert.Equal(1, _logger.ErrorCount); 3192 3193 Assert.Equal(BuildResultCode.Failure, result1.OverallResult); 3194 Assert.True(result1.HasResultsForTarget("target1")); 3195 } 3196 3197 /// <summary> 3198 /// Verifies that when building targets which emit warnings, they still show as succeeding but the overall build result is a failure. 3199 /// </summary> 3200 [Fact] WarningsAreTreatedAsErrorsButTargetsStillSucceed()3201 public void WarningsAreTreatedAsErrorsButTargetsStillSucceed() 3202 { 3203 string contents = CleanupFileContents(@" 3204 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 3205 <Target Name='target1'> 3206 <Message Text='text'/> 3207 </Target> 3208 <Target Name='target2'> 3209 <Warning Text='This warning should be treated as an error' Code='ABC123'/> 3210 </Target> 3211 </Project> 3212 "); 3213 _parameters.WarningsAsErrors = new HashSet<string> { "ABC123" }; 3214 3215 Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true); 3216 ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); 3217 _buildManager.BeginBuild(_parameters); 3218 BuildResult buildResult = _buildManager.BuildRequest(new BuildRequestData(instance, new string[] { "target1", "target2" })); 3219 _buildManager.EndBuild(); 3220 3221 Assert.Equal(0, _logger.WarningCount); 3222 Assert.Equal(1, _logger.ErrorCount); 3223 3224 Assert.Equal(BuildResultCode.Failure, buildResult.OverallResult); 3225 Assert.True(buildResult.HasResultsForTarget("target1")); 3226 Assert.True(buildResult.HasResultsForTarget("target2")); 3227 // The two targets should still show as success because they don't know their warning was changed to an error 3228 // Logging a warning as an error does not change execution, only the final result of the build 3229 Assert.Equal(TargetResultCode.Success, buildResult.ResultsByTarget["target1"].ResultCode); 3230 Assert.Equal(TargetResultCode.Success, buildResult.ResultsByTarget["target2"].ResultCode); 3231 } 3232 3233 /// <summary> 3234 /// Helper for cache tests. Builds a project and verifies the right cache files are created. 3235 /// </summary> BuildAndCheckCache(BuildManager localBuildManager, IEnumerable<string> exceptCacheDirectories)3236 private string BuildAndCheckCache(BuildManager localBuildManager, IEnumerable<string> exceptCacheDirectories) 3237 { 3238 string contents = CleanupFileContents(@" 3239 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'> 3240 <Target Name='One' Outputs='one.txt'> 3241 </Target> 3242 3243 <Target Name='Two' Outputs='two.txt'> 3244 </Target> 3245 3246 <Target Name='Three' Outputs='three.txt'> 3247 </Target> 3248 </Project> 3249 "); 3250 string fileName = Path.GetTempFileName(); 3251 File.WriteAllText(fileName, contents); 3252 3253 string cacheDirectory = FileUtilities.GetCacheDirectory(); 3254 3255 BuildParameters parameters = new BuildParameters(); 3256 localBuildManager.BeginBuild(parameters); 3257 try 3258 { 3259 var services = new HostServices(); 3260 BuildRequestData data = new BuildRequestData(fileName, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new[] { "One", "Two", "Three" }, services); 3261 var result = localBuildManager.PendBuildRequest(data).Execute(); 3262 Assert.Equal(result.OverallResult, BuildResultCode.Success); // "Test project failed to build correctly." 3263 } 3264 finally 3265 { 3266 localBuildManager.EndBuild(); 3267 } 3268 3269 // Ensure that we got the cache files we expected. There should be one set of results in there, once we exclude 3270 // any of the specified directories from previous builds in the same test. 3271 string directory = Directory.EnumerateDirectories(cacheDirectory).Except(exceptCacheDirectories).First(); 3272 3273 // Within this directory should be a set of target results files, one for each of the targets we invoked. 3274 var resultsFiles = Directory.EnumerateFiles(directory).Select(Path.GetFileName); 3275 3276 Assert.Equal(3, resultsFiles.Count()); 3277 Assert.True(resultsFiles.Contains("One.cache")); 3278 Assert.True(resultsFiles.Contains("Two.cache")); 3279 Assert.True(resultsFiles.Contains("Three.cache")); 3280 3281 // Return the cache directory created for this build. 3282 return directory; 3283 } 3284 3285 /// <summary> 3286 /// Extract a string dictionary from the property enumeration on a project started event. 3287 /// </summary> ExtractProjectStartedPropertyList(IEnumerable properties)3288 private Dictionary<string, string> ExtractProjectStartedPropertyList(IEnumerable properties) 3289 { 3290 // Gather a sorted list of all the properties. 3291 return properties?.Cast<DictionaryEntry>() 3292 .ToDictionary(prop => (string) prop.Key, prop => (string) prop.Value, StringComparer.OrdinalIgnoreCase); 3293 } 3294 3295 /// <summary> 3296 /// Retrieves a BuildRequestData using the specified contents, default targets and an empty project collection. 3297 /// </summary> GetBuildRequestData(string projectContents)3298 private BuildRequestData GetBuildRequestData(string projectContents) 3299 { 3300 return GetBuildRequestData(projectContents, new string[] { }); 3301 } 3302 3303 /// <summary> 3304 /// Retrieves a BuildRequestData using the specified contents, targets and project collection. 3305 /// </summary> GetBuildRequestData(string projectContents, string[] targets, string toolsVersion = null)3306 private BuildRequestData GetBuildRequestData(string projectContents, string[] targets, string toolsVersion = null) 3307 { 3308 BuildRequestData data = new BuildRequestData( 3309 CreateProjectInstance(projectContents, toolsVersion, _projectCollection, true), targets, 3310 _projectCollection.HostServices); 3311 3312 return data; 3313 } 3314 3315 /// <summary> 3316 /// Retrieve a ProjectInstance evaluated with the specified contents using the specified projectCollection 3317 /// </summary> CreateProjectInstance(string contents, string toolsVersion, ProjectCollection projectCollection, bool deleteTempProject)3318 private ProjectInstance CreateProjectInstance(string contents, string toolsVersion, ProjectCollection projectCollection, bool deleteTempProject) 3319 { 3320 Project project = CreateProject(contents, toolsVersion, projectCollection, deleteTempProject); 3321 return project.CreateProjectInstance(); 3322 } 3323 3324 /// <summary> 3325 /// Retrieve a Project with the specified contents using the specified projectCollection 3326 /// </summary> CreateProject(string contents, string toolsVersion, ProjectCollection projectCollection, bool deleteTempProject)3327 private Project CreateProject(string contents, string toolsVersion, ProjectCollection projectCollection, bool deleteTempProject) 3328 { 3329 Project project = new Project(XmlReader.Create(new StringReader(contents)), null, toolsVersion, projectCollection) 3330 { 3331 FullPath = _env.CreateFile().Path 3332 }; 3333 3334 if (!deleteTempProject) 3335 { 3336 project.Save(); 3337 } 3338 3339 if (deleteTempProject) 3340 { 3341 File.Delete(project.FullPath); 3342 } 3343 3344 return project; 3345 } 3346 3347 /// <summary> 3348 /// Generate dummy projects 3349 /// </summary> GenerateDummyProjects(string shutdownProjectDirectory, int parallelProjectCount, ProjectCollection projectCollection)3350 private ProjectInstance GenerateDummyProjects(string shutdownProjectDirectory, int parallelProjectCount, ProjectCollection projectCollection) 3351 { 3352 Directory.CreateDirectory(shutdownProjectDirectory); 3353 3354 // Generate the project. It will have the following format. Setting the AdditionalProperties 3355 // causes the projects to be built to be separate configs, which allows us to build the same project 3356 // a bunch of times in parallel. 3357 // 3358 // <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'/> 3359 // <ItemGroup> 3360 // <ProjectReference Include="RootProjectName.proj"> 3361 // <AdditionalProperties>p={incremented value}</AdditionalProperties> 3362 // </ProjectReference> 3363 // ... 3364 // </ItemGroup> 3365 // 3366 // <Target Name="Build"> 3367 // <MSBuild Projects="@(ProjectReference) Targets="ChildBuild" BuildInParallel="true" /> 3368 // </Target> 3369 // 3370 // <Target Name="ChildBuild" /> 3371 // </Project> 3372 string rootProjectPath = Path.Combine(shutdownProjectDirectory, String.Format(CultureInfo.InvariantCulture, "RootProj_{0}.proj", Guid.NewGuid().ToString("N"))); 3373 ProjectRootElement rootProject = ProjectRootElement.Create(rootProjectPath, projectCollection); 3374 3375 ProjectTargetElement buildTarget = rootProject.AddTarget("Build"); 3376 ProjectTaskElement buildTask = buildTarget.AddTask("MSBuild"); 3377 buildTask.SetParameter("Projects", "@(ProjectReference)"); 3378 buildTask.SetParameter("BuildInParallel", "true"); 3379 buildTask.SetParameter("Targets", "ChildBuild"); 3380 3381 rootProject.AddTarget("ChildBuild"); 3382 3383 IDictionary<string, string> metadata = new Dictionary<string, string>(1); 3384 for (int i = 0; i < parallelProjectCount; i++) 3385 { 3386 // Add the ProjectReference item for this actual config. 3387 metadata["AdditionalProperties"] = String.Format(CultureInfo.InvariantCulture, "p={0}", i); 3388 rootProject.AddItem("ProjectReference", rootProjectPath, metadata); 3389 } 3390 3391 rootProject.Save(); 3392 return new ProjectInstance(rootProject); 3393 } 3394 3395 [Fact] 3396 [Trait("Category", "mono-osx-failing")] // out-of-proc nodes not working on mono yet ShouldBuildMutatedProjectInstanceWhoseProjectWasPreviouslyBuiltAsAP2PDependency()3397 public void ShouldBuildMutatedProjectInstanceWhoseProjectWasPreviouslyBuiltAsAP2PDependency() 3398 { 3399 var mainProjectContents = 3400 @"<Project> 3401 3402 <Target Name=""BuildOther"" Outputs=""@(ReturnValue)""> 3403 <MSBuild Projects=""{0}"" Targets=""Foo""> 3404 <Output TaskParameter=""TargetOutputs"" ItemName=""ReturnValue"" /> 3405 </MSBuild> 3406 </Target> 3407 3408 </Project>"; 3409 3410 3411 var p2pProjectContents = 3412 @"<Project> 3413 3414 <PropertyGroup> 3415 <P>InitialValue</P> 3416 </PropertyGroup> 3417 3418 <Target Name=""Foo"" Outputs=""$(P)""/> 3419 3420 </Project>"; 3421 3422 using (var env = TestEnvironment.Create()) 3423 using (var collection = new ProjectCollection()) 3424 using (var manager = new BuildManager()) 3425 { 3426 try 3427 { 3428 var testFiles = env.CreateTestProjectWithFiles(string.Empty, new[] { "p2p", "main" }); 3429 var p2pProjectPath = testFiles.CreatedFiles[0]; 3430 File.WriteAllText(p2pProjectPath, p2pProjectContents); 3431 3432 var mainRootElement = ProjectRootElement.Create(XmlReader.Create(new StringReader(string.Format(mainProjectContents, p2pProjectPath))), collection); 3433 3434 mainRootElement.FullPath = testFiles.CreatedFiles[1]; 3435 mainRootElement.Save(); 3436 3437 // build p2p project as a real p2p dependency of some other project. This loads the p2p into msbuild's caches 3438 3439 var mainProject = new Project(mainRootElement, new Dictionary<string, string>(), MSBuildConstants.CurrentToolsVersion, collection); 3440 var mainInstance = mainProject.CreateProjectInstance(ProjectInstanceSettings.Immutable).DeepCopy(isImmutable: false); 3441 3442 Assert.Equal(0, mainInstance.GlobalProperties.Count); 3443 3444 var request = new BuildRequestData(mainInstance, new[] {"BuildOther"}); 3445 3446 var parameters = new BuildParameters 3447 { 3448 DisableInProcNode = true, 3449 EnableNodeReuse = false, 3450 }; 3451 3452 manager.BeginBuild(parameters); 3453 3454 var submission = manager.PendBuildRequest(request); 3455 3456 var results = submission.Execute(); 3457 Assert.Equal(BuildResultCode.Success, results.OverallResult); 3458 Assert.Equal("InitialValue", results.ResultsByTarget["BuildOther"].Items.First().ItemSpec); 3459 3460 // build p2p directly via mutated ProjectInstances based of the same Project. 3461 // This should rebuild and the result should reflect the in-memory changes and not reuse stale cache info 3462 3463 var p2pProject = new Project(p2pProjectPath, new Dictionary<string, string>(), MSBuildConstants.CurrentToolsVersion, collection); 3464 3465 for (var i = 0; i < 2; i++) 3466 { 3467 var p2pInstance = p2pProject.CreateProjectInstance(ProjectInstanceSettings.Immutable).DeepCopy(isImmutable: false); 3468 3469 var newPropertyValue = $"NewValue_{i}"; 3470 3471 p2pInstance.SetProperty("P", newPropertyValue); 3472 3473 request = new BuildRequestData(p2pInstance, new[] {"Foo"}); 3474 submission = manager.PendBuildRequest(request); 3475 results = submission.Execute(); 3476 3477 Assert.Equal(0, p2pInstance.GlobalProperties.Count); 3478 3479 Assert.Equal(BuildResultCode.Success, results.OverallResult); 3480 Assert.Equal(newPropertyValue, results.ResultsByTarget["Foo"].Items.First().ItemSpec); 3481 } 3482 } 3483 finally 3484 { 3485 manager.EndBuild(); 3486 } 3487 } 3488 } 3489 3490 [Fact] 3491 [Trait("Category", "mono-osx-failing")] // out-of-proc nodes not working on mono yet OutOfProcFileBasedP2PBuildSucceeds()3492 public void OutOfProcFileBasedP2PBuildSucceeds() 3493 { 3494 var mainProject = 3495 @"<Project> 3496 3497 <Target Name=`MainTarget` Returns=`foo;@(P2PReturnValue)`> 3498 <MSBuild Projects=`{0}` Targets=`P2PTarget`> 3499 <Output TaskParameter=`TargetOutputs` ItemName=`P2PReturnValue` /> 3500 </MSBuild> 3501 </Target> 3502 3503 </Project>"; 3504 3505 var p2pProject = 3506 @"<Project> 3507 3508 <Target Name=`P2PTarget` Returns=`bar`> 3509 <Message Text=`Bar` Importance=`High` /> 3510 </Target> 3511 3512 </Project>"; 3513 var testFiles = _env.CreateTestProjectWithFiles(string.Empty, new[] {"main", "p2p"}, string.Empty); 3514 3515 var buildParameters = new BuildParameters(_projectCollection) 3516 { 3517 DisableInProcNode = true, 3518 EnableNodeReuse = false, 3519 Loggers = new ILogger[] {_logger} 3520 }; 3521 3522 _buildManager.BeginBuild(buildParameters); 3523 3524 try 3525 { 3526 var p2pProjectPath = testFiles.CreatedFiles[1]; 3527 var cleanedUpP2pContents = CleanupFileContents(p2pProject); 3528 File.WriteAllText(p2pProjectPath, cleanedUpP2pContents); 3529 3530 var mainProjectPath = testFiles.CreatedFiles[0]; 3531 var cleanedUpMainContents = CleanupFileContents(string.Format(mainProject, p2pProjectPath)); 3532 File.WriteAllText(mainProjectPath, cleanedUpMainContents); 3533 3534 3535 var buildRequestData = new BuildRequestData( 3536 mainProjectPath, 3537 new Dictionary<string, string>(), 3538 MSBuildConstants.CurrentToolsVersion, 3539 new[] {"MainTarget"}, 3540 null 3541 ); 3542 3543 var submission = _buildManager.PendBuildRequest(buildRequestData); 3544 3545 var result = submission.Execute(); 3546 3547 Assert.Equal(BuildResultCode.Success, result.OverallResult); 3548 Assert.Equal("foo;bar", 3549 string.Join(";", result.ResultsByTarget["MainTarget"].Items.Select(i => i.ItemSpec))); 3550 } 3551 finally 3552 { 3553 _buildManager.EndBuild(); 3554 } 3555 } 3556 3557 /// When a ProjectInstance based BuildRequestData is built out of proc, the node should 3558 /// not reload it from disk but instead fully utilize the entire translate project instance state 3559 /// to do the build 3560 [Theory] 3561 [InlineData(false)] 3562 [InlineData(true)] 3563 [Trait("Category", "mono-osx-failing")] // out-of-proc nodes not working on mono yet OutOfProcProjectInstanceBasedBuildDoesNotReloadFromDisk(bool shouldSerializeEntireState)3564 public void OutOfProcProjectInstanceBasedBuildDoesNotReloadFromDisk(bool shouldSerializeEntireState) 3565 { 3566 var mainProject = 3567 @"<Project> 3568 <PropertyGroup> 3569 <ImportIt>true</ImportIt> 3570 </PropertyGroup> 3571 3572 <Import Project=""{0}"" Condition=""'$(ImportIt)' == 'true'""/> 3573 3574 </Project>"; 3575 3576 var importProject = 3577 @"<Project> 3578 <Target Name=""Foo""> 3579 <Message Text=""Bar"" Importance=""High"" /> 3580 </Target> 3581 </Project>"; 3582 3583 var testFiles = _env.CreateTestProjectWithFiles(string.Empty, new[] {"main", "import"}, string.Empty); 3584 3585 try 3586 { 3587 var importPath = testFiles.CreatedFiles[1]; 3588 File.WriteAllText(importPath, CleanupFileContents(importProject)); 3589 3590 var root = ProjectRootElement.Create( 3591 XmlReader.Create(new StringReader(string.Format(mainProject, importPath))), _projectCollection); 3592 root.FullPath = Path.GetTempFileName(); 3593 root.Save(); 3594 3595 // build a project which runs a target from an imported file 3596 3597 var project = new Project(root, new Dictionary<string, string>(), MSBuildConstants.CurrentToolsVersion, 3598 _projectCollection); 3599 var instance = project.CreateProjectInstance(ProjectInstanceSettings.Immutable).DeepCopy(false); 3600 3601 instance.TranslateEntireState = shouldSerializeEntireState; 3602 3603 var request = new BuildRequestData(instance, new[] {"Foo"}); 3604 3605 3606 var parameters = new BuildParameters(_projectCollection) 3607 { 3608 DisableInProcNode = true, 3609 EnableNodeReuse = false, 3610 Loggers = new ILogger[] {_logger} 3611 }; 3612 3613 _buildManager.BeginBuild(parameters); 3614 3615 3616 var submission = _buildManager.PendBuildRequest(request); 3617 3618 var results = submission.Execute(); 3619 3620 Assert.True(results.OverallResult == BuildResultCode.Success); 3621 3622 // reset caches to ensure nothing is reused 3623 3624 _buildManager.EndBuild(); 3625 _buildManager.ResetCaches(); 3626 3627 // mutate the file on disk such that the import (containing the target to get executed) 3628 // is no longer imported 3629 3630 project.SetProperty("ImportIt", "false"); 3631 project.Save(); 3632 3633 // Build the initial project instance again. 3634 // The project instance is not in sync with the file anymore, making it an in-memory build: 3635 // the file does not contain the target Foo, but the project instance does 3636 // Building the stale project instance should still succeed when the entire state is translated: MSBuild should use the 3637 // in-memory state to build and not reload from disk. 3638 3639 _buildManager.BeginBuild(parameters); 3640 request = new BuildRequestData(instance, new[] {"Foo"}, null, 3641 BuildRequestDataFlags.ReplaceExistingProjectInstance); 3642 submission = _buildManager.PendBuildRequest(request); 3643 3644 3645 results = submission.Execute(); 3646 3647 if (shouldSerializeEntireState) 3648 { 3649 Assert.Equal(BuildResultCode.Success, results.OverallResult); 3650 } 3651 else 3652 { 3653 Assert.Equal(BuildResultCode.Failure, results.OverallResult); 3654 Assert.Contains("The target \"Foo\" does not exist in the project", _logger.FullLog, 3655 StringComparison.OrdinalIgnoreCase); 3656 } 3657 } 3658 finally 3659 { 3660 _buildManager.EndBuild(); 3661 } 3662 } 3663 3664 [Fact] 3665 [Trait("Category", "mono-osx-failing")] // out-of-proc nodes not working on mono yet OutOfProcEvaluationIdsUnique()3666 public void OutOfProcEvaluationIdsUnique() 3667 { 3668 var mainProject = 3669 @"<Project> 3670 3671 <Target Name=`MainTarget`> 3672 <MSBuild Projects=`{0};{1}` Targets=`DummyTarget` /> 3673 </Target> 3674 3675 </Project>"; 3676 3677 var childProject = 3678 @"<Project> 3679 3680 <Target Name=`DummyTarget`> 3681 <Message Text=`Bar` Importance=`High` /> 3682 </Target> 3683 3684 </Project>"; 3685 3686 var testFiles = _env.CreateTestProjectWithFiles(string.Empty, new[] { "main", "child1", "child2" }, string.Empty); 3687 3688 var buildParameters = new BuildParameters(_projectCollection) 3689 { 3690 DisableInProcNode = true, 3691 EnableNodeReuse = false, 3692 Loggers = new ILogger[] { _logger } 3693 }; 3694 3695 _buildManager.BeginBuild(buildParameters); 3696 3697 try 3698 { 3699 var child1ProjectPath = testFiles.CreatedFiles[1]; 3700 var child2ProjectPath = testFiles.CreatedFiles[2]; 3701 var cleanedUpChildContents = CleanupFileContents(childProject); 3702 File.WriteAllText(child1ProjectPath, cleanedUpChildContents); 3703 File.WriteAllText(child2ProjectPath, cleanedUpChildContents); 3704 3705 var mainProjectPath = testFiles.CreatedFiles[0]; 3706 var cleanedUpMainContents = CleanupFileContents(string.Format(mainProject, child1ProjectPath, child2ProjectPath)); 3707 File.WriteAllText(mainProjectPath, cleanedUpMainContents); 3708 3709 var buildRequestData = new BuildRequestData( 3710 mainProjectPath, 3711 new Dictionary<string, string>(), 3712 MSBuildConstants.CurrentToolsVersion, 3713 new[] { "MainTarget" }, 3714 null 3715 ); 3716 3717 var submission = _buildManager.PendBuildRequest(buildRequestData); 3718 3719 var result = submission.Execute(); 3720 3721 Assert.Equal(BuildResultCode.Success, result.OverallResult); 3722 Assert.True(_logger.AllBuildEvents.OfType<ProjectEvaluationStartedEventArgs>().GroupBy(args => args.BuildEventContext.EvaluationId).All(g => g.Count() == 1)); 3723 } 3724 finally 3725 { 3726 _buildManager.EndBuild(); 3727 } 3728 } 3729 3730 /// <summary> 3731 /// Regression test for https://github.com/Microsoft/msbuild/issues/3047 3732 /// </summary> 3733 [Fact] 3734 [Trait("Category", "mono-osx-failing")] // out-of-proc nodes not working on mono yet MultiProcReentrantProjectWithCallTargetDoesNotFail()3735 public void MultiProcReentrantProjectWithCallTargetDoesNotFail() 3736 { 3737 var a = 3738 @"<Project> 3739 <Target Name=`EntryTarget`> 3740 <MSBuild Projects=`b;c` BuildInParallel=`true` /> 3741 </Target> 3742 </Project>".Cleanup(); 3743 3744 var b = 3745 @"<Project> 3746 <Target Name=`BTarget`> 3747 <MSBuild Projects=`reentrant` Targets=`BuildGenerateSources` BuildInParallel=`true` /> 3748 </Target> 3749 </Project>".Cleanup(); 3750 3751 var c = 3752 $@"<Project> 3753 <Target Name=`CTarget`> 3754 <Exec Command=`{Helpers.GetSleepCommand(TimeSpan.FromSeconds(1))}` /> 3755 <MSBuild Projects=`reentrant` Targets=`BuildGenerated` BuildInParallel=`true` /> 3756 </Target> 3757 </Project>".Cleanup(); 3758 3759 var delay = 3760 $@"<Project> 3761 <Target Name=`Delay`> 3762 <Exec Command=`{Helpers.GetSleepCommand(TimeSpan.FromSeconds(2))}` /> 3763 </Target> 3764 </Project>".Cleanup(); 3765 3766 var reentrant = 3767 $@"<Project DefaultTargets=`Build`> 3768 <Target Name=`BuildGenerateSources` DependsOnTargets=`_Get;Build`></Target> 3769 <Target Name=`BuildGenerated`> 3770 <CallTarget Targets=`Build` /> 3771 </Target> 3772 <Target Name=`Build`> 3773 <CallTarget Targets=`_Get` /> 3774 </Target> 3775 <Target Name=`_Get`> 3776 <MSBuild Projects=`delay` BuildInParallel=`true` /> 3777 <Exec Command=`{Helpers.GetSleepCommand(TimeSpan.FromSeconds(5))}` YieldDuringToolExecution=`true` StandardOutputImportance=`low` /> 3778 </Target> 3779 </Project>".Cleanup(); 3780 3781 using (var env = TestEnvironment.Create(_output)) 3782 { 3783 var entryFile = env.CreateFile(nameof(a), a).Path; 3784 env.CreateFile(nameof(b), b); 3785 env.CreateFile(nameof(c), c); 3786 env.CreateFile(nameof(delay), delay); 3787 env.CreateFile(nameof(reentrant), reentrant); 3788 3789 var mockLogger = new MockLogger(_output); 3790 3791 var buildParameters = new BuildParameters() 3792 { 3793 DisableInProcNode = true, 3794 MaxNodeCount = Environment.ProcessorCount, 3795 EnableNodeReuse = false, 3796 Loggers = new List<ILogger>() 3797 { 3798 mockLogger 3799 } 3800 }; 3801 3802 var buildRequestData = new BuildRequestData(entryFile, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new[]{ "EntryTarget" }, null); 3803 3804 var result = _buildManager.Build(buildParameters, buildRequestData); 3805 3806 result.OverallResult.ShouldBe(BuildResultCode.Success); 3807 } 3808 } 3809 } 3810 } 3811