1 // Copyright (c) Microsoft. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 4 using System; 5 using System.Text; 6 using System.Collections; 7 using System.Diagnostics; 8 using System.Globalization; 9 using System.IO; 10 11 using Microsoft.Build.Framework; 12 using Microsoft.Build.BuildEngine.Shared; 13 14 namespace Microsoft.Build.BuildEngine 15 { 16 #region Delegates 17 18 /// <summary> 19 /// Delegate to use for writing a string to some location like 20 /// the console window or the IDE build window. 21 /// </summary> 22 /// <param name="message"></param> WriteHandler(string message)23 public delegate void WriteHandler(string message); 24 25 /// <summary> 26 /// Type of delegate used to set console color. 27 /// </summary> 28 /// <param name="color">Text color</param> ColorSetter(ConsoleColor color)29 public delegate void ColorSetter(ConsoleColor color); 30 31 /// <summary> 32 /// Type of delegate used to reset console color. 33 /// </summary> ColorResetter()34 public delegate void ColorResetter(); 35 36 #endregion 37 38 /// <summary> 39 /// This class implements the default logger that outputs event data 40 /// to the console (stdout). 41 /// It is a facade: it creates, wraps and delegates to a kind of BaseConsoleLogger, 42 /// either SerialConsoleLogger or ParallelConsoleLogger. 43 /// </summary> 44 /// <remarks>This class is not thread safe.</remarks> 45 public class ConsoleLogger : INodeLogger 46 { 47 private BaseConsoleLogger consoleLogger; 48 private int numberOfProcessors = 1; 49 private LoggerVerbosity verbosity; 50 private WriteHandler write; 51 private ColorSetter colorSet; 52 private ColorResetter colorReset; 53 private string parameters; 54 private bool skipProjectStartedText = false; 55 private bool? showSummary; 56 57 58 #region Constructors 59 60 /// <summary> 61 /// Default constructor. 62 /// </summary> ConsoleLogger()63 public ConsoleLogger() 64 : this(LoggerVerbosity.Normal) 65 { 66 // do nothing 67 } 68 69 /// <summary> 70 /// Create a logger instance with a specific verbosity. This logs to 71 /// the default console. 72 /// </summary> 73 /// <param name="verbosity">Verbosity level.</param> ConsoleLogger(LoggerVerbosity verbosity)74 public ConsoleLogger(LoggerVerbosity verbosity) 75 : 76 this 77 ( 78 verbosity, 79 new WriteHandler(Console.Out.Write), 80 new ColorSetter(SetColor), 81 new ColorResetter(Console.ResetColor) 82 ) 83 { 84 // do nothing 85 } 86 87 /// <summary> 88 /// Initializes the logger, with alternate output handlers. 89 /// </summary> 90 /// <param name="verbosity"></param> 91 /// <param name="write"></param> 92 /// <param name="colorSet"></param> 93 /// <param name="colorReset"></param> ConsoleLogger( LoggerVerbosity verbosity, WriteHandler write, ColorSetter colorSet, ColorResetter colorReset )94 public ConsoleLogger 95 ( 96 LoggerVerbosity verbosity, 97 WriteHandler write, 98 ColorSetter colorSet, 99 ColorResetter colorReset 100 ) 101 { 102 ErrorUtilities.VerifyThrowArgumentNull(write, "write"); 103 this.verbosity = verbosity; 104 this.write = write; 105 this.colorSet = colorSet; 106 this.colorReset = colorReset; 107 } 108 109 /// <summary> 110 /// This is called by every event handler for compat reasons -- see DDB #136924 111 /// However it will skip after the first call 112 /// </summary> InitializeBaseConsoleLogger()113 private void InitializeBaseConsoleLogger() 114 { 115 if (consoleLogger == null) 116 { 117 bool useMPLogger = false; 118 if (!string.IsNullOrEmpty(parameters)) 119 { 120 string [] parameterComponents = parameters.Split(BaseConsoleLogger.parameterDelimiters); 121 for (int param = 0; param < parameterComponents.Length; param++) 122 { 123 if (parameterComponents[param].Length > 0) 124 { 125 if (0 == String.Compare(parameterComponents[param], "ENABLEMPLOGGING", StringComparison.OrdinalIgnoreCase)) 126 { 127 useMPLogger = true; 128 } 129 if (0 == String.Compare(parameterComponents[param], "DISABLEMPLOGGING", StringComparison.OrdinalIgnoreCase)) 130 { 131 useMPLogger = false; 132 } 133 } 134 } 135 } 136 137 if (numberOfProcessors == 1 && !useMPLogger) 138 { 139 consoleLogger = new SerialConsoleLogger(verbosity, write, colorSet, colorReset); 140 } 141 else 142 { 143 consoleLogger = new ParallelConsoleLogger(verbosity, write, colorSet, colorReset); 144 } 145 146 if(!string.IsNullOrEmpty(parameters)) 147 { 148 consoleLogger.Parameters = parameters; 149 parameters = null; 150 } 151 152 if (showSummary != null) 153 { 154 consoleLogger.ShowSummary = (bool)showSummary; 155 } 156 157 consoleLogger.SkipProjectStartedText = skipProjectStartedText; 158 } 159 } 160 161 #endregion 162 163 #region Properties 164 165 /// <summary> 166 /// Gets or sets the level of detail to show in the event log. 167 /// </summary> 168 /// <value>Verbosity level.</value> 169 public LoggerVerbosity Verbosity 170 { 171 get 172 { 173 return consoleLogger == null ? verbosity : consoleLogger.Verbosity; 174 } 175 176 set 177 { 178 if (consoleLogger == null) 179 { 180 verbosity = value; 181 } 182 else 183 { 184 consoleLogger.Verbosity = value; 185 } 186 } 187 } 188 189 /// <summary> 190 /// The console logger takes a single parameter to suppress the output of the errors 191 /// and warnings summary at the end of a build. 192 /// </summary> 193 /// <value>null</value> 194 public string Parameters 195 { 196 get 197 { 198 return consoleLogger == null ? parameters : consoleLogger.Parameters; 199 } 200 201 set 202 { 203 if (consoleLogger == null) 204 { 205 parameters = value; 206 } 207 else 208 { 209 consoleLogger.Parameters = value; 210 } 211 } 212 } 213 214 /// <summary> 215 /// Suppresses the display of project headers. Project headers are 216 /// displayed by default unless this property is set. 217 /// </summary> 218 /// <remarks>This is only needed by the IDE logger.</remarks> 219 public bool SkipProjectStartedText 220 { 221 get 222 { 223 return consoleLogger == null ? skipProjectStartedText : consoleLogger.SkipProjectStartedText; 224 } 225 226 set 227 { 228 if (consoleLogger == null) 229 { 230 skipProjectStartedText = value; 231 } 232 else 233 { 234 consoleLogger.SkipProjectStartedText = value; 235 } 236 } 237 } 238 239 /// <summary> 240 /// Suppresses the display of error and warnings summary. 241 /// </summary> 242 public bool ShowSummary 243 { 244 get 245 { 246 return (consoleLogger == null ? showSummary : consoleLogger.ShowSummary) ?? false; 247 } 248 249 set 250 { 251 if (consoleLogger == null) 252 { 253 showSummary = value; 254 } 255 else 256 { 257 consoleLogger.ShowSummary = value; 258 259 } 260 } 261 } 262 263 /// <summary> 264 /// Provide access to the write hander delegate so that it can be redirected 265 /// if necessary (e.g. to a file) 266 /// </summary> 267 protected WriteHandler WriteHandler 268 { 269 get 270 { 271 return consoleLogger == null ? write : consoleLogger.write; 272 } 273 274 set 275 { 276 if (consoleLogger == null) 277 { 278 write = value; 279 } 280 else 281 { 282 consoleLogger.write = value; 283 } 284 } 285 } 286 287 #endregion 288 289 #region Methods 290 291 /// <summary> 292 /// Apply a parameter. 293 /// NOTE: This method was public by accident in Whidbey, so it cannot be made internal now. It has 294 /// no good reason for being public. 295 /// </summary> ApplyParameter(string parameterName, string parameterValue)296 public void ApplyParameter(string parameterName, string parameterValue) 297 { 298 ErrorUtilities.VerifyThrowInvalidOperation(consoleLogger != null, "MustCallInitializeBeforeApplyParameter"); 299 consoleLogger.ApplyParameter(parameterName, parameterValue); 300 } 301 302 /// <summary> 303 /// Signs up the console logger for all build events. 304 /// </summary> 305 /// <param name="eventSource">Available events.</param> Initialize(IEventSource eventSource)306 public virtual void Initialize(IEventSource eventSource) 307 { 308 InitializeBaseConsoleLogger(); 309 consoleLogger.Initialize(eventSource); 310 } 311 Initialize(IEventSource eventSource, int nodeCount)312 public virtual void Initialize(IEventSource eventSource, int nodeCount) 313 { 314 this.numberOfProcessors = nodeCount; 315 InitializeBaseConsoleLogger(); 316 consoleLogger.Initialize(eventSource, nodeCount); 317 } 318 319 /// <summary> 320 /// The console logger does not need to release any resources. 321 /// This method does nothing. 322 /// </summary> Shutdown()323 public virtual void Shutdown() 324 { 325 if (consoleLogger != null) 326 { 327 consoleLogger.Shutdown(); 328 } 329 } 330 331 /// <summary> 332 /// Handler for build started events 333 /// </summary> 334 /// <param name="sender">sender (should be null)</param> 335 /// <param name="e">event arguments</param> BuildStartedHandler(object sender, BuildStartedEventArgs e)336 public void BuildStartedHandler(object sender, BuildStartedEventArgs e) 337 { 338 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 339 340 consoleLogger.BuildStartedHandler(sender, e); 341 } 342 343 /// <summary> 344 /// Handler for build finished events 345 /// </summary> 346 /// <param name="sender">sender (should be null)</param> 347 /// <param name="e">event arguments</param> BuildFinishedHandler(object sender, BuildFinishedEventArgs e)348 public void BuildFinishedHandler(object sender, BuildFinishedEventArgs e) 349 { 350 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 351 352 consoleLogger.BuildFinishedHandler(sender, e); 353 } 354 355 /// <summary> 356 /// Handler for project started events 357 /// </summary> 358 /// <param name="sender">sender (should be null)</param> 359 /// <param name="e">event arguments</param> ProjectStartedHandler(object sender, ProjectStartedEventArgs e)360 public void ProjectStartedHandler(object sender, ProjectStartedEventArgs e) 361 { 362 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 363 364 consoleLogger.ProjectStartedHandler(sender, e); 365 } 366 367 /// <summary> 368 /// Handler for project finished events 369 /// </summary> 370 /// <param name="sender">sender (should be null)</param> 371 /// <param name="e">event arguments</param> ProjectFinishedHandler(object sender, ProjectFinishedEventArgs e)372 public void ProjectFinishedHandler(object sender, ProjectFinishedEventArgs e) 373 { 374 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 375 376 consoleLogger.ProjectFinishedHandler(sender, e); 377 } 378 379 /// <summary> 380 /// Handler for target started events 381 /// </summary> 382 /// <param name="sender">sender (should be null)</param> 383 /// <param name="e">event arguments</param> TargetStartedHandler(object sender, TargetStartedEventArgs e)384 public void TargetStartedHandler(object sender, TargetStartedEventArgs e) 385 { 386 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 387 388 consoleLogger.TargetStartedHandler(sender, e); 389 } 390 391 /// <summary> 392 /// Handler for target finished events 393 /// </summary> 394 /// <param name="sender">sender (should be null)</param> 395 /// <param name="e">event arguments</param> TargetFinishedHandler(object sender, TargetFinishedEventArgs e)396 public void TargetFinishedHandler(object sender, TargetFinishedEventArgs e) 397 { 398 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 399 400 consoleLogger.TargetFinishedHandler(sender, e); 401 } 402 403 /// <summary> 404 /// Handler for task started events 405 /// </summary> 406 /// <param name="sender">sender (should be null)</param> 407 /// <param name="e">event arguments</param> TaskStartedHandler(object sender, TaskStartedEventArgs e)408 public void TaskStartedHandler(object sender, TaskStartedEventArgs e) 409 { 410 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 411 412 consoleLogger.TaskStartedHandler(sender, e); 413 } 414 415 /// <summary> 416 /// Handler for task finished events 417 /// </summary> 418 /// <param name="sender">sender (should be null)</param> 419 /// <param name="e">event arguments</param> TaskFinishedHandler(object sender, TaskFinishedEventArgs e)420 public void TaskFinishedHandler(object sender, TaskFinishedEventArgs e) 421 { 422 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 423 424 consoleLogger.TaskFinishedHandler(sender, e); 425 } 426 427 /// <summary> 428 /// Prints an error event 429 /// </summary> ErrorHandler(object sender, BuildErrorEventArgs e)430 public void ErrorHandler(object sender, BuildErrorEventArgs e) 431 { 432 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 433 434 consoleLogger.ErrorHandler(sender, e); 435 } 436 437 /// <summary> 438 /// Prints a warning event 439 /// </summary> WarningHandler(object sender, BuildWarningEventArgs e)440 public void WarningHandler(object sender, BuildWarningEventArgs e) 441 { 442 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 443 444 consoleLogger.WarningHandler(sender, e); 445 } 446 447 /// <summary> 448 /// Prints a message event 449 /// </summary> MessageHandler(object sender, BuildMessageEventArgs e)450 public void MessageHandler(object sender, BuildMessageEventArgs e) 451 { 452 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 453 454 consoleLogger.MessageHandler(sender, e); 455 } 456 457 /// <summary> 458 /// Prints a custom event 459 /// </summary> CustomEventHandler(object sender, CustomBuildEventArgs e)460 public void CustomEventHandler(object sender, CustomBuildEventArgs e) 461 { 462 InitializeBaseConsoleLogger(); // for compat: see DDB#136924 463 464 consoleLogger.CustomEventHandler(sender, e); 465 } 466 467 /// <summary> 468 /// Sets foreground color to color specified 469 /// </summary> 470 /// <param name="c">foreground color</param> SetColor(ConsoleColor c)471 internal static void SetColor(ConsoleColor c) 472 { 473 Console.ForegroundColor = 474 TransformColor(c, Console.BackgroundColor); 475 } 476 477 /// <summary> 478 /// Changes the foreground color to black if the foreground is the 479 /// same as the background. Changes the foreground to white if the 480 /// background is black. 481 /// </summary> 482 /// <param name="foreground">foreground color for black</param> 483 /// <param name="background">current background</param> TransformColor(ConsoleColor foreground, ConsoleColor background)484 private static ConsoleColor TransformColor(ConsoleColor foreground, 485 ConsoleColor background) 486 { 487 ConsoleColor result = foreground; //typically do nothing ... 488 489 if (foreground == background) 490 { 491 if (background != ConsoleColor.Black) 492 { 493 result = ConsoleColor.Black; 494 } 495 else 496 { 497 result = ConsoleColor.Gray; 498 } 499 } 500 501 return result; 502 } 503 504 #endregion 505 } 506 } 507