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 // </copyright> 4 // <summary>Sink which will take in a build event and raise it on its internal event source</summary> 5 //----------------------------------------------------------------------- 6 7 using System; 8 using System.Collections.Generic; 9 using Microsoft.Build.Framework; 10 using Microsoft.Build.Shared; 11 12 using InternalLoggerException = Microsoft.Build.Exceptions.InternalLoggerException; 13 14 namespace Microsoft.Build.BackEnd.Logging 15 { 16 /// <summary> 17 /// This class raises events on behalf of the build engine to all registered loggers. 18 /// </summary> 19 internal sealed class EventSourceSink : 20 #if FEATURE_APPDOMAIN 21 MarshalByRefObject, 22 #endif 23 IEventSource2, IBuildEventSink 24 { 25 #region Events 26 27 /// <summary> 28 /// This event is raised to log a message. 29 /// </summary> 30 public event BuildMessageEventHandler MessageRaised; 31 32 /// <summary> 33 /// This event is raised to log an error. 34 /// </summary> 35 public event BuildErrorEventHandler ErrorRaised; 36 37 /// <summary> 38 /// This event is raised to log a warning. 39 /// </summary> 40 public event BuildWarningEventHandler WarningRaised; 41 42 /// <summary> 43 /// this event is raised to log the start of a build 44 /// </summary> 45 public event BuildStartedEventHandler BuildStarted; 46 47 /// <summary> 48 /// this event is raised to log the end of a build 49 /// </summary> 50 public event BuildFinishedEventHandler BuildFinished; 51 52 /// <summary> 53 /// this event is raised to log the start of a project build 54 /// </summary> 55 public event ProjectStartedEventHandler ProjectStarted; 56 57 /// <summary> 58 /// this event is raised to log the end of a project build 59 /// </summary> 60 public event ProjectFinishedEventHandler ProjectFinished; 61 62 /// <summary> 63 /// this event is raised to log the start of a target build 64 /// </summary> 65 public event TargetStartedEventHandler TargetStarted; 66 67 /// <summary> 68 /// this event is raised to log the end of a target build 69 /// </summary> 70 public event TargetFinishedEventHandler TargetFinished; 71 72 /// <summary> 73 /// this event is raised to log the start of task execution 74 /// </summary> 75 public event TaskStartedEventHandler TaskStarted; 76 77 /// <summary> 78 /// this event is raised to log the end of task execution 79 /// </summary> 80 public event TaskFinishedEventHandler TaskFinished; 81 82 /// <summary> 83 /// this event is raised to log a custom event 84 /// </summary> 85 public event CustomBuildEventHandler CustomEventRaised; 86 87 /// <summary> 88 /// this event is raised to log build status events, such as 89 /// build/project/target/task started/stopped 90 /// </summary> 91 public event BuildStatusEventHandler StatusEventRaised; 92 93 /// <summary> 94 /// This event is raised to log that some event has 95 /// occurred. It is raised on every event. 96 /// </summary> 97 public event AnyEventHandler AnyEventRaised; 98 99 /// <summary> 100 /// This event is raised to log telemetry. 101 /// </summary> 102 public event TelemetryEventHandler TelemetryLogged; 103 #endregion 104 105 #region Properties 106 /// <summary> 107 /// Provide a friendly name for the sink to make it easier to differentiate during 108 /// debugging and display 109 /// </summary> 110 public string Name 111 { 112 get; 113 set; 114 } 115 116 /// <summary> 117 /// Has the sink logged the BuildStartedEvent. This is important to know because we only want to log the build started event once 118 /// </summary> 119 public bool HaveLoggedBuildStartedEvent 120 { 121 get; 122 set; 123 } 124 125 /// <summary> 126 /// Has the sink logged the BuildFinishedEvent. This is important to know because we only want to log the build finished event once 127 /// </summary> 128 public bool HaveLoggedBuildFinishedEvent 129 { 130 get; 131 set; 132 } 133 134 /// <summary> 135 /// A list of warnings to treat as errors. If null, nothing is treated as an error. If an empty set, all warnings are treated as errors. 136 /// </summary> 137 public ISet<string> WarningsAsErrors 138 { 139 get; 140 set; 141 } 142 143 /// <summary> 144 /// A list of warnings to treat as errors for an associated <see cref="BuildEventContext.ProjectInstanceId"/>. If the set associated with a ProjectInstanceId is null, nothing is treated as an error. If an empty set, all warnings are treated as errors. 145 /// </summary> 146 public IDictionary<int, ISet<string>> WarningsAsErrorsByProject 147 { 148 get; 149 set; 150 } 151 152 /// <summary> 153 /// A list of warnings to treat as low importance messages. 154 /// </summary> 155 public ISet<string> WarningsAsMessages 156 { 157 get; 158 set; 159 } 160 161 /// <summary> 162 /// A list of warnings to treat as low importance messages for an associated <see cref="BuildEventContext.ProjectInstanceId"/>. 163 /// </summary> 164 public IDictionary<int, ISet<string>> WarningsAsMessagesByProject 165 { 166 get; 167 set; 168 } 169 170 /// <summary> 171 /// A list of build submission IDs that have logged errors. If an error is logged outside of a submission, the submission ID is <see cref="BuildEventContext.InvalidSubmissionId"/>. 172 /// </summary> 173 public ISet<int> BuildSubmissionIdsThatHaveLoggedErrors 174 { 175 get; 176 } = new HashSet<int>(); 177 178 #endregion 179 180 #region Methods 181 182 #region IEventSink Methods 183 184 /// <summary> 185 /// Raises the given event to all registered loggers. This method up-cast the events 186 /// extracted from the queue. 187 /// </summary> 188 /// <param name="buildEvent">BuildEventArgs</param> 189 /// <param name="sinkId">Note this is not used in the eventsource sink</param> Consume(BuildEventArgs buildEvent, int sinkId)190 public void Consume(BuildEventArgs buildEvent, int sinkId) 191 { 192 Consume(buildEvent); 193 } 194 195 /// <summary> 196 /// Raises the given event to all registered loggers. This method up-cast the events 197 /// extracted from the queue. 198 /// </summary> Consume(BuildEventArgs buildEvent)199 public void Consume(BuildEventArgs buildEvent) 200 { 201 // FXCop may complain that there are unecessary casts here, and there are, but 202 // using "as" and allocating another variable for each event is extremely costly 203 // and is much slower then this approach even with the additional casts 204 if (buildEvent is BuildMessageEventArgs) 205 { 206 this.RaiseMessageEvent(null, (BuildMessageEventArgs)buildEvent); 207 } 208 else if (buildEvent is TaskStartedEventArgs) 209 { 210 this.RaiseTaskStartedEvent(null, (TaskStartedEventArgs)buildEvent); 211 } 212 else if (buildEvent is TaskFinishedEventArgs) 213 { 214 this.RaiseTaskFinishedEvent(null, (TaskFinishedEventArgs)buildEvent); 215 } 216 else if (buildEvent is TargetStartedEventArgs) 217 { 218 this.RaiseTargetStartedEvent(null, (TargetStartedEventArgs)buildEvent); 219 } 220 else if (buildEvent is TargetFinishedEventArgs) 221 { 222 this.RaiseTargetFinishedEvent(null, (TargetFinishedEventArgs)buildEvent); 223 } 224 else if (buildEvent is ProjectStartedEventArgs) 225 { 226 this.RaiseProjectStartedEvent(null, (ProjectStartedEventArgs)buildEvent); 227 } 228 else if (buildEvent is ProjectFinishedEventArgs) 229 { 230 this.RaiseProjectFinishedEvent(null, (ProjectFinishedEventArgs)buildEvent); 231 232 if (buildEvent.BuildEventContext != null && buildEvent.BuildEventContext.ProjectInstanceId != BuildEventContext.InvalidProjectInstanceId) 233 { 234 WarningsAsErrorsByProject?.Remove(buildEvent.BuildEventContext.ProjectInstanceId); 235 WarningsAsMessagesByProject?.Remove(buildEvent.BuildEventContext.ProjectInstanceId); 236 } 237 } 238 else if (buildEvent is BuildStartedEventArgs) 239 { 240 HaveLoggedBuildStartedEvent = true; 241 this.RaiseBuildStartedEvent(null, (BuildStartedEventArgs)buildEvent); 242 } 243 else if (buildEvent is BuildFinishedEventArgs) 244 { 245 HaveLoggedBuildFinishedEvent = true; 246 this.RaiseBuildFinishedEvent(null, (BuildFinishedEventArgs)buildEvent); 247 } 248 else if (buildEvent is CustomBuildEventArgs) 249 { 250 this.RaiseCustomEvent(null, (CustomBuildEventArgs)buildEvent); 251 } 252 else if (buildEvent is BuildStatusEventArgs) 253 { 254 this.RaiseStatusEvent(null, (BuildStatusEventArgs)buildEvent); 255 } 256 else if (buildEvent is BuildWarningEventArgs) 257 { 258 BuildWarningEventArgs warningEvent = (BuildWarningEventArgs) buildEvent; 259 260 if (ShouldTreatWarningAsMessage(warningEvent)) 261 { 262 // Treat this warning as a message with low importance if its in the list 263 BuildMessageEventArgs errorEvent = new BuildMessageEventArgs( 264 warningEvent.Subcategory, 265 warningEvent.Code, 266 warningEvent.File, 267 warningEvent.LineNumber, 268 warningEvent.ColumnNumber, 269 warningEvent.EndLineNumber, 270 warningEvent.EndColumnNumber, 271 warningEvent.Message, 272 warningEvent.HelpKeyword, 273 warningEvent.SenderName, 274 MessageImportance.Low, 275 warningEvent.Timestamp) 276 { 277 BuildEventContext = warningEvent.BuildEventContext, 278 ProjectFile = warningEvent.ProjectFile, 279 }; 280 281 this.RaiseMessageEvent(null, errorEvent); 282 283 } 284 else if (ShouldTreatWarningAsError(warningEvent)) 285 { 286 // Treat this warning as an error if an empty set of warnings was specified or this code was specified 287 BuildErrorEventArgs errorEvent = new BuildErrorEventArgs( 288 warningEvent.Subcategory, 289 warningEvent.Code, 290 warningEvent.File, 291 warningEvent.LineNumber, 292 warningEvent.ColumnNumber, 293 warningEvent.EndLineNumber, 294 warningEvent.EndColumnNumber, 295 warningEvent.Message, 296 warningEvent.HelpKeyword, 297 warningEvent.SenderName, 298 warningEvent.Timestamp) 299 { 300 BuildEventContext = warningEvent.BuildEventContext, 301 ProjectFile = warningEvent.ProjectFile, 302 }; 303 304 this.RaiseErrorEvent(null, errorEvent); 305 } 306 else 307 { 308 this.RaiseWarningEvent(null, warningEvent); 309 } 310 } 311 else if (buildEvent is BuildErrorEventArgs) 312 { 313 this.RaiseErrorEvent(null, (BuildErrorEventArgs)buildEvent); 314 } 315 else if (buildEvent is TelemetryEventArgs) 316 { 317 this.RaiseTelemetryEvent(null, (TelemetryEventArgs) buildEvent); 318 } 319 else 320 { 321 ErrorUtilities.VerifyThrow(false, "Unknown event args type."); 322 } 323 } 324 325 /// <summary> 326 /// Shutdown and displose of any resource this object is holding onto. 327 /// </summary> ShutDown()328 public void ShutDown() 329 { 330 this.UnregisterAllEventHandlers(); 331 } 332 #endregion 333 334 #region Internal Methods 335 336 /// <summary> 337 /// Clears out all events. 338 /// </summary> UnregisterAllEventHandlers()339 internal void UnregisterAllEventHandlers() 340 { 341 MessageRaised = null; 342 ErrorRaised = null; 343 WarningRaised = null; 344 BuildStarted = null; 345 BuildFinished = null; 346 ProjectStarted = null; 347 ProjectFinished = null; 348 TargetStarted = null; 349 TargetFinished = null; 350 TaskStarted = null; 351 TaskFinished = null; 352 CustomEventRaised = null; 353 StatusEventRaised = null; 354 AnyEventRaised = null; 355 TelemetryLogged = null; 356 } 357 358 #endregion 359 360 #region Private Methods 361 362 /// <summary> 363 /// Raises a message event to all registered loggers. 364 /// </summary> 365 /// <param name="sender">sender of the event</param> 366 /// <param name="buildEvent">BuildMessageEventArgs</param> 367 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 368 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 369 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseMessageEvent(object sender, BuildMessageEventArgs buildEvent)370 private void RaiseMessageEvent(object sender, BuildMessageEventArgs buildEvent) 371 { 372 if (MessageRaised != null) 373 { 374 try 375 { 376 MessageRaised(sender, buildEvent); 377 } 378 catch (LoggerException) 379 { 380 // if a logger has failed politely, abort immediately 381 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 382 // if a fellow logger is throwing in an event handler. 383 this.UnregisterAllEventHandlers(); 384 throw; 385 } 386 catch (Exception exception) 387 { 388 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 389 // if a fellow logger is throwing in an event handler. 390 this.UnregisterAllEventHandlers(); 391 392 if (ExceptionHandling.IsCriticalException(exception)) 393 { 394 throw; 395 } 396 397 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 398 } 399 } 400 401 RaiseAnyEvent(sender, buildEvent); 402 } 403 404 /// <summary> 405 /// Raises an error event to all registered loggers. 406 /// </summary> 407 /// <param name="sender">sender of the event</param> 408 /// <param name="buildEvent">BuildErrorEventArgs</param> 409 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 410 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 411 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseErrorEvent(object sender, BuildErrorEventArgs buildEvent)412 private void RaiseErrorEvent(object sender, BuildErrorEventArgs buildEvent) 413 { 414 // Keep track of build submissions that have logged errors. If there is no build context, add BuildEventContext.InvalidSubmissionId. 415 BuildSubmissionIdsThatHaveLoggedErrors.Add(buildEvent?.BuildEventContext?.SubmissionId ?? BuildEventContext.InvalidSubmissionId); 416 417 if (ErrorRaised != null) 418 { 419 try 420 { 421 ErrorRaised(sender, buildEvent); 422 } 423 catch (LoggerException) 424 { 425 // if a logger has failed politely, abort immediately 426 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 427 // if a fellow logger is throwing in an event handler. 428 this.UnregisterAllEventHandlers(); 429 throw; 430 } 431 catch (Exception exception) 432 { 433 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 434 // if a fellow logger is throwing in an event handler. 435 this.UnregisterAllEventHandlers(); 436 437 if (ExceptionHandling.IsCriticalException(exception)) 438 { 439 throw; 440 } 441 442 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 443 } 444 } 445 446 RaiseAnyEvent(sender, buildEvent); 447 } 448 449 /// <summary> 450 /// Raises a warning event to all registered loggers. 451 /// </summary> 452 /// <param name="sender">sender of the event</param> 453 /// <param name="buildEvent">BuildWarningEventArgs</param> 454 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 455 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 456 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseWarningEvent(object sender, BuildWarningEventArgs buildEvent)457 private void RaiseWarningEvent(object sender, BuildWarningEventArgs buildEvent) 458 { 459 if (WarningRaised != null) 460 { 461 try 462 { 463 WarningRaised(sender, buildEvent); 464 } 465 catch (LoggerException) 466 { 467 // if a logger has failed politely, abort immediately 468 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 469 // if a fellow logger is throwing in an event handler. 470 this.UnregisterAllEventHandlers(); 471 throw; 472 } 473 catch (Exception exception) 474 { 475 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 476 // if a fellow logger is throwing in an event handler. 477 this.UnregisterAllEventHandlers(); 478 479 if (ExceptionHandling.IsCriticalException(exception)) 480 { 481 throw; 482 } 483 484 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 485 } 486 } 487 488 RaiseAnyEvent(sender, buildEvent); 489 } 490 491 /// <summary> 492 /// Raises a "build started" event to all registered loggers. 493 /// </summary> 494 /// <param name="sender">sender of the event</param> 495 /// <param name="buildEvent">BuildStartedEventArgs</param> 496 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 497 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 498 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseBuildStartedEvent(object sender, BuildStartedEventArgs buildEvent)499 private void RaiseBuildStartedEvent(object sender, BuildStartedEventArgs buildEvent) 500 { 501 if (BuildStarted != null) 502 { 503 try 504 { 505 BuildStarted(sender, buildEvent); 506 } 507 catch (LoggerException) 508 { 509 // if a logger has failed politely, abort immediately 510 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 511 // if a fellow logger is throwing in an event handler. 512 this.UnregisterAllEventHandlers(); 513 throw; 514 } 515 catch (Exception exception) 516 { 517 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 518 // if a fellow logger is throwing in an event handler. 519 this.UnregisterAllEventHandlers(); 520 521 if (ExceptionHandling.IsCriticalException(exception)) 522 { 523 throw; 524 } 525 526 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 527 } 528 } 529 530 RaiseStatusEvent(sender, buildEvent); 531 } 532 533 /// <summary> 534 /// Raises a "build finished" event to all registered loggers. 535 /// </summary> 536 /// <param name="sender">sender of the event</param> 537 /// <param name="buildEvent">BuildFinishedEventArgs</param> 538 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 539 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 540 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseBuildFinishedEvent(object sender, BuildFinishedEventArgs buildEvent)541 private void RaiseBuildFinishedEvent(object sender, BuildFinishedEventArgs buildEvent) 542 { 543 if (BuildFinished != null) 544 { 545 try 546 { 547 BuildFinished(sender, buildEvent); 548 } 549 catch (LoggerException) 550 { 551 // if a logger has failed politely, abort immediately 552 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 553 // if a fellow logger is throwing in an event handler. 554 this.UnregisterAllEventHandlers(); 555 throw; 556 } 557 catch (Exception exception) 558 { 559 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 560 // if a fellow logger is throwing in an event handler. 561 this.UnregisterAllEventHandlers(); 562 563 if (ExceptionHandling.IsCriticalException(exception)) 564 { 565 throw; 566 } 567 568 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 569 } 570 } 571 572 RaiseStatusEvent(sender, buildEvent); 573 } 574 575 /// <summary> 576 /// Raises a "project build started" event to all registered loggers. 577 /// </summary> 578 /// <param name="sender">sender of the event</param> 579 /// <param name="buildEvent">ProjectStartedEventArgs</param> 580 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 581 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 582 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseProjectStartedEvent(object sender, ProjectStartedEventArgs buildEvent)583 private void RaiseProjectStartedEvent(object sender, ProjectStartedEventArgs buildEvent) 584 { 585 if (ProjectStarted != null) 586 { 587 try 588 { 589 ProjectStarted(sender, buildEvent); 590 } 591 catch (LoggerException) 592 { 593 // if a logger has failed politely, abort immediately 594 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 595 // if a fellow logger is throwing in an event handler. 596 this.UnregisterAllEventHandlers(); 597 throw; 598 } 599 catch (Exception exception) 600 { 601 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 602 // if a fellow logger is throwing in an event handler. 603 this.UnregisterAllEventHandlers(); 604 605 if (ExceptionHandling.IsCriticalException(exception)) 606 { 607 throw; 608 } 609 610 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 611 } 612 } 613 614 RaiseStatusEvent(sender, buildEvent); 615 } 616 617 /// <summary> 618 /// Raises a "project build finished" event to all registered loggers. 619 /// </summary> 620 /// <param name="sender">sender of the event</param> 621 /// <param name="buildEvent">ProjectFinishedEventArgs</param> 622 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 623 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 624 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseProjectFinishedEvent(object sender, ProjectFinishedEventArgs buildEvent)625 private void RaiseProjectFinishedEvent(object sender, ProjectFinishedEventArgs buildEvent) 626 { 627 if (ProjectFinished != null) 628 { 629 try 630 { 631 ProjectFinished(sender, buildEvent); 632 } 633 catch (LoggerException) 634 { 635 // if a logger has failed politely, abort immediately 636 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 637 // if a fellow logger is throwing in an event handler. 638 this.UnregisterAllEventHandlers(); 639 throw; 640 } 641 catch (Exception exception) 642 { 643 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 644 // if a fellow logger is throwing in an event handler. 645 this.UnregisterAllEventHandlers(); 646 647 if (ExceptionHandling.IsCriticalException(exception)) 648 { 649 throw; 650 } 651 652 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 653 } 654 } 655 656 RaiseStatusEvent(sender, buildEvent); 657 } 658 659 /// <summary> 660 /// Raises a "target build started" event to all registered loggers. 661 /// </summary> 662 /// <param name="sender">sender of the event</param> 663 /// <param name="buildEvent">TargetStartedEventArgs</param> 664 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 665 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 666 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseTargetStartedEvent(object sender, TargetStartedEventArgs buildEvent)667 private void RaiseTargetStartedEvent(object sender, TargetStartedEventArgs buildEvent) 668 { 669 if (TargetStarted != null) 670 { 671 try 672 { 673 TargetStarted(sender, buildEvent); 674 } 675 catch (LoggerException) 676 { 677 // if a logger has failed politely, abort immediately 678 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 679 // if a fellow logger is throwing in an event handler. 680 this.UnregisterAllEventHandlers(); 681 throw; 682 } 683 catch (Exception exception) 684 { 685 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 686 // if a fellow logger is throwing in an event handler. 687 this.UnregisterAllEventHandlers(); 688 689 if (ExceptionHandling.IsCriticalException(exception)) 690 { 691 throw; 692 } 693 694 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 695 } 696 } 697 698 RaiseStatusEvent(sender, buildEvent); 699 } 700 701 /// <summary> 702 /// Raises a "target build finished" event to all registered loggers. 703 /// </summary> 704 /// <param name="sender">sender of the event</param> 705 /// <param name="buildEvent">TargetFinishedEventArgs</param> 706 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 707 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 708 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseTargetFinishedEvent(object sender, TargetFinishedEventArgs buildEvent)709 private void RaiseTargetFinishedEvent(object sender, TargetFinishedEventArgs buildEvent) 710 { 711 if (TargetFinished != null) 712 { 713 try 714 { 715 TargetFinished(sender, buildEvent); 716 } 717 catch (LoggerException) 718 { 719 // if a logger has failed politely, abort immediately 720 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 721 // if a fellow logger is throwing in an event handler. 722 this.UnregisterAllEventHandlers(); 723 throw; 724 } 725 catch (Exception exception) 726 { 727 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 728 // if a fellow logger is throwing in an event handler. 729 this.UnregisterAllEventHandlers(); 730 731 if (ExceptionHandling.IsCriticalException(exception)) 732 { 733 throw; 734 } 735 736 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 737 } 738 } 739 740 RaiseStatusEvent(sender, buildEvent); 741 } 742 743 /// <summary> 744 /// Raises a "task execution started" event to all registered loggers. 745 /// </summary> 746 /// <param name="sender">sender of the event</param> 747 /// <param name="buildEvent">TaskStartedEventArgs</param> 748 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 749 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 750 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseTaskStartedEvent(object sender, TaskStartedEventArgs buildEvent)751 private void RaiseTaskStartedEvent(object sender, TaskStartedEventArgs buildEvent) 752 { 753 if (TaskStarted != null) 754 { 755 try 756 { 757 TaskStarted(sender, buildEvent); 758 } 759 catch (LoggerException) 760 { 761 // if a logger has failed politely, abort immediately 762 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 763 // if a fellow logger is throwing in an event handler. 764 this.UnregisterAllEventHandlers(); 765 throw; 766 } 767 catch (Exception exception) 768 { 769 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 770 // if a fellow logger is throwing in an event handler. 771 this.UnregisterAllEventHandlers(); 772 773 if (ExceptionHandling.IsCriticalException(exception)) 774 { 775 throw; 776 } 777 778 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 779 } 780 } 781 782 RaiseStatusEvent(sender, buildEvent); 783 } 784 785 /// <summary> 786 /// Raises a "task finished executing" event to all registered loggers. 787 /// </summary> 788 /// <param name="sender">sender of the event</param> 789 /// <param name="buildEvent">TaskFinishedEventArgs</param> 790 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 791 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 792 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseTaskFinishedEvent(object sender, TaskFinishedEventArgs buildEvent)793 private void RaiseTaskFinishedEvent(object sender, TaskFinishedEventArgs buildEvent) 794 { 795 if (TaskFinished != null) 796 { 797 try 798 { 799 TaskFinished(sender, buildEvent); 800 } 801 catch (LoggerException) 802 { 803 // if a logger has failed politely, abort immediately 804 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 805 // if a fellow logger is throwing in an event handler. 806 this.UnregisterAllEventHandlers(); 807 throw; 808 } 809 catch (Exception exception) 810 { 811 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 812 // if a fellow logger is throwing in an event handler. 813 this.UnregisterAllEventHandlers(); 814 815 if (ExceptionHandling.IsCriticalException(exception)) 816 { 817 throw; 818 } 819 820 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 821 } 822 } 823 824 RaiseStatusEvent(sender, buildEvent); 825 } 826 827 /// <summary> 828 /// Raises a custom event to all registered loggers. 829 /// </summary> 830 /// <param name="sender">sender of the event</param> 831 /// <param name="buildEvent">CustomBuildEventArgs</param> 832 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 833 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 834 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseCustomEvent(object sender, CustomBuildEventArgs buildEvent)835 private void RaiseCustomEvent(object sender, CustomBuildEventArgs buildEvent) 836 { 837 if (CustomEventRaised != null) 838 { 839 try 840 { 841 CustomEventRaised(sender, buildEvent); 842 } 843 catch (LoggerException) 844 { 845 // if a logger has failed politely, abort immediately 846 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 847 // if a fellow logger is throwing in an event handler. 848 this.UnregisterAllEventHandlers(); 849 throw; 850 } 851 catch (Exception exception) 852 { 853 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 854 // if a fellow logger is throwing in an event handler. 855 this.UnregisterAllEventHandlers(); 856 857 if (ExceptionHandling.IsCriticalException(exception)) 858 { 859 throw; 860 } 861 862 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 863 } 864 } 865 866 RaiseAnyEvent(sender, buildEvent); 867 } 868 869 /// <summary> 870 /// Raises a catch-all build status event to all registered loggers. 871 /// </summary> 872 /// <param name="sender">sender of the event</param> 873 /// <param name="buildEvent">BuildStatusEventArgs</param> 874 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 875 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 876 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseStatusEvent(object sender, BuildStatusEventArgs buildEvent)877 private void RaiseStatusEvent(object sender, BuildStatusEventArgs buildEvent) 878 { 879 if (StatusEventRaised != null) 880 { 881 try 882 { 883 StatusEventRaised(sender, buildEvent); 884 } 885 catch (LoggerException) 886 { 887 // if a logger has failed politely, abort immediately 888 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 889 // if a fellow logger is throwing in an event handler. 890 this.UnregisterAllEventHandlers(); 891 throw; 892 } 893 catch (Exception exception) 894 { 895 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 896 // if a fellow logger is throwing in an event handler. 897 this.UnregisterAllEventHandlers(); 898 899 if (ExceptionHandling.IsCriticalException(exception)) 900 { 901 throw; 902 } 903 904 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 905 } 906 } 907 908 RaiseAnyEvent(sender, buildEvent); 909 } 910 911 /// <summary> 912 /// Raises a catch-all build event to all registered loggers. 913 /// </summary> 914 /// <param name="sender">sender of the event</param> 915 /// <param name="buildEvent">Build EventArgs</param> 916 /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception> 917 /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception> 918 /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception> RaiseAnyEvent(object sender, BuildEventArgs buildEvent)919 private void RaiseAnyEvent(object sender, BuildEventArgs buildEvent) 920 { 921 if (AnyEventRaised != null) 922 { 923 try 924 { 925 AnyEventRaised(sender, buildEvent); 926 } 927 catch (LoggerException exception) 928 { 929 // if a logger has failed politely, abort immediately 930 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 931 // if a fellow logger is throwing in an event handler. 932 this.UnregisterAllEventHandlers(); 933 934 // We ought to dump this farther up the stack, but if for example a task is logging an event within a 935 // catch(Exception) block and not rethrowing it, there's the possibility that this exception could 936 // just get silently eaten. So better to have duplicates than to not log the problem at all. :) 937 ExceptionHandling.DumpExceptionToFile(exception); 938 939 throw; 940 } 941 catch (Exception exception) 942 { 943 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 944 // if a fellow logger is throwing in an event handler. 945 this.UnregisterAllEventHandlers(); 946 947 // We ought to dump this farther up the stack, but if for example a task is logging an event within a 948 // catch(Exception) block and not rethrowing it, there's the possibility that this exception could 949 // just get silently eaten. So better to have duplicates than to not log the problem at all. :) 950 ExceptionHandling.DumpExceptionToFile(exception); 951 952 if (ExceptionHandling.IsCriticalException(exception)) 953 { 954 throw; 955 } 956 957 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 958 } 959 } 960 } 961 962 /// <summary> 963 /// Raises the a telemetry event to all registered loggers. 964 /// </summary> RaiseTelemetryEvent(object sender, TelemetryEventArgs buildEvent)965 private void RaiseTelemetryEvent(object sender, TelemetryEventArgs buildEvent) 966 { 967 if (TelemetryLogged != null) 968 { 969 try 970 { 971 TelemetryLogged(sender, buildEvent); 972 } 973 catch (LoggerException) 974 { 975 // if a logger has failed politely, abort immediately 976 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 977 // if a fellow logger is throwing in an event handler. 978 this.UnregisterAllEventHandlers(); 979 throw; 980 } 981 catch (Exception exception) 982 { 983 // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings 984 // if a fellow logger is throwing in an event handler. 985 this.UnregisterAllEventHandlers(); 986 987 if (ExceptionHandling.IsCriticalException(exception)) 988 { 989 throw; 990 } 991 992 InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false); 993 } 994 } 995 } 996 997 /// <summary> 998 /// Determines if the specified warning should be treated as an error. 999 /// </summary> 1000 /// <param name="warningEvent">A <see cref="BuildWarningEventArgs"/> that specifies the warning.</param> 1001 /// <returns><code>true</code> if the warning should be treated as an error, otherwise <code>false</code>.</returns> ShouldTreatWarningAsError(BuildWarningEventArgs warningEvent)1002 private bool ShouldTreatWarningAsError(BuildWarningEventArgs warningEvent) 1003 { 1004 // This only applies if the user specified /warnaserror from the command-line or added an empty set through the object model 1005 // 1006 if (WarningsAsErrors != null) 1007 { 1008 // Global warnings as errors apply to all projects. If the list is empty or contains the code, the warning should be treated as an error 1009 // 1010 if (WarningsAsErrors.Count == 0 || WarningsAsErrors.Contains(warningEvent.Code)) 1011 { 1012 return true; 1013 } 1014 } 1015 1016 // This only applies if the user specified <MSBuildTreatWarningsAsErrors>true</MSBuildTreatWarningsAsErrors or <MSBuildWarningsAsErrors /> 1017 // and there is a valid ProjectInstanceId for the warning. 1018 // 1019 if (WarningsAsErrorsByProject != null && warningEvent.BuildEventContext != null && warningEvent.BuildEventContext.ProjectInstanceId != BuildEventContext.InvalidProjectInstanceId) 1020 { 1021 ISet<string> codesByProject; 1022 1023 // Attempt to get the list of warnings to treat as errors for the current project 1024 // 1025 if (WarningsAsErrorsByProject.TryGetValue(warningEvent.BuildEventContext.ProjectInstanceId, out codesByProject) && codesByProject != null) 1026 { 1027 // We create an empty set if all warnings should be treated as errors so that should be checked first. 1028 // If the set is not empty, check the specific code. 1029 // 1030 return codesByProject.Count == 0 || codesByProject.Contains(warningEvent.Code); 1031 } 1032 } 1033 1034 return false; 1035 } 1036 1037 /// <summary> 1038 /// Determines if the specified warning should be treated as a low importance message. 1039 /// </summary> 1040 /// <param name="warningEvent">A <see cref="BuildWarningEventArgs"/> that specifies the warning.</param> 1041 /// <returns><code>true</code> if the warning should be treated as a low importance message, otherwise <code>false</code>.</returns> ShouldTreatWarningAsMessage(BuildWarningEventArgs warningEvent)1042 private bool ShouldTreatWarningAsMessage(BuildWarningEventArgs warningEvent) 1043 { 1044 // This only applies if the user specified /nowarn at the command-line or added the warning code through the object model 1045 // 1046 if (WarningsAsMessages != null && WarningsAsMessages.Contains(warningEvent.Code)) 1047 { 1048 return true; 1049 } 1050 1051 // This only applies if the user specified <MSBuildWarningsAsMessages /> and there is a valid ProjectInstanceId 1052 // 1053 if (WarningsAsMessagesByProject != null && warningEvent.BuildEventContext != null && warningEvent.BuildEventContext.ProjectInstanceId != BuildEventContext.InvalidProjectInstanceId) 1054 { 1055 ISet<string> codesByProject; 1056 1057 if (WarningsAsMessagesByProject.TryGetValue(warningEvent.BuildEventContext.ProjectInstanceId, out codesByProject) && codesByProject != null) 1058 { 1059 return codesByProject.Contains(warningEvent.Code); 1060 } 1061 } 1062 1063 return false; 1064 } 1065 1066 #endregion 1067 #endregion 1068 } 1069 } 1070