OLD | NEW |
| (Empty) |
1 /// Copyright (c) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System; | |
4 using System.CodeDom.Compiler; | |
5 using System.Diagnostics; | |
6 using System.Globalization; | |
7 using System.Runtime.InteropServices; | |
8 using System.Text; | |
9 using Microsoft.Build.Framework; | |
10 using Microsoft.Build.Utilities; | |
11 using Microsoft.VisualStudio; | |
12 using Microsoft.VisualStudio.Shell; | |
13 using Microsoft.VisualStudio.Shell.Interop; | |
14 using Microsoft.VisualStudio.TextManager.Interop; | |
15 using Microsoft.Win32; | |
16 using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; | |
17 | |
18 namespace Microsoft.VisualStudio.Project | |
19 { | |
20 | |
21 /// <summary> | |
22 /// This class implements an MSBuild logger that output events to VS out
putwindow and tasklist. | |
23 /// </summary> | |
24 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA
1001:TypesThatOwnDisposableFieldsShouldBeDisposable"), ComVisible(true)] | |
25 internal sealed class IDEBuildLogger : Logger | |
26 { | |
27 #region fields | |
28 // TODO: Remove these constants when we have a version that supp
oerts getting the verbosity using automation. | |
29 private string buildVerbosityRegistryRoot = @"Software\Microsoft
\VisualStudio\9.0"; | |
30 private const string buildVerbosityRegistrySubKey = @"General"; | |
31 private const string buildVerbosityRegistryKey = "MSBuildLoggerV
erbosity"; | |
32 // TODO: Re-enable this constants when we have a version that su
ppoerts getting the verbosity using automation. | |
33 //private const string EnvironmentCategory = "Environment"; | |
34 //private const string ProjectsAndSolutionSubCategory = "Project
sAndSolution"; | |
35 //private const string BuildAndRunPage = "BuildAndRun"; | |
36 | |
37 private int currentIndent; | |
38 private IVsOutputWindowPane outputWindowPane; | |
39 private string errorString = SR.GetString(SR.Error, CultureInfo.
CurrentUICulture); | |
40 private string warningString = SR.GetString(SR.Warning, CultureI
nfo.CurrentUICulture); | |
41 private bool isLogTaskDone; | |
42 private TaskProvider taskProvider; | |
43 private IVsHierarchy hierarchy; | |
44 private IServiceProvider serviceProvider; | |
45 | |
46 #endregion | |
47 | |
48 #region properties | |
49 public string WarningString | |
50 { | |
51 get { return this.warningString; } | |
52 set { this.warningString = value; } | |
53 } | |
54 public string ErrorString | |
55 { | |
56 get { return this.errorString; } | |
57 set { this.errorString = value; } | |
58 } | |
59 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Perf
ormance", "CA1811:AvoidUncalledPrivateCode")] | |
60 public bool IsLogTaskDone | |
61 { | |
62 get { return this.isLogTaskDone; } | |
63 set { this.isLogTaskDone = value; } | |
64 } | |
65 /// <summary> | |
66 /// When building from within VS, setting this will | |
67 /// enable the logger to retrive the verbosity from | |
68 /// the correct registry hive. | |
69 /// </summary> | |
70 internal string BuildVerbosityRegistryRoot | |
71 { | |
72 get { return buildVerbosityRegistryRoot; } | |
73 set { buildVerbosityRegistryRoot = value; } | |
74 } | |
75 /// <summary> | |
76 /// Set to null to avoid writing to the output window | |
77 /// </summary> | |
78 internal IVsOutputWindowPane OutputWindowPane | |
79 { | |
80 get { return outputWindowPane; } | |
81 set { outputWindowPane = value; } | |
82 } | |
83 #endregion | |
84 | |
85 #region ctors | |
86 /// <summary> | |
87 /// Constructor. Inititialize member data. | |
88 /// </summary> | |
89 public IDEBuildLogger(IVsOutputWindowPane output, TaskProvider t
askProvider, IVsHierarchy hierarchy) | |
90 { | |
91 if(taskProvider == null) | |
92 throw new ArgumentNullException("taskProvider"); | |
93 if(hierarchy == null) | |
94 throw new ArgumentNullException("hierarchy"); | |
95 | |
96 this.taskProvider = taskProvider; | |
97 this.outputWindowPane = output; | |
98 this.hierarchy = hierarchy; | |
99 IOleServiceProvider site; | |
100 Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(hiera
rchy.GetSite(out site)); | |
101 this.serviceProvider = new ServiceProvider(site); | |
102 } | |
103 #endregion | |
104 | |
105 #region overridden methods | |
106 /// <summary> | |
107 /// Overridden from the Logger class. | |
108 /// </summary> | |
109 public override void Initialize(IEventSource eventSource) | |
110 { | |
111 if(null == eventSource) | |
112 { | |
113 throw new ArgumentNullException("eventSource"); | |
114 } | |
115 eventSource.BuildStarted += new BuildStartedEventHandler
(BuildStartedHandler); | |
116 eventSource.BuildFinished += new BuildFinishedEventHandl
er(BuildFinishedHandler); | |
117 eventSource.ProjectStarted += new ProjectStartedEventHan
dler(ProjectStartedHandler); | |
118 eventSource.ProjectFinished += new ProjectFinishedEventH
andler(ProjectFinishedHandler); | |
119 eventSource.TargetStarted += new TargetStartedEventHandl
er(TargetStartedHandler); | |
120 eventSource.TargetFinished += new TargetFinishedEventHan
dler(TargetFinishedHandler); | |
121 eventSource.TaskStarted += new TaskStartedEventHandler(T
askStartedHandler); | |
122 eventSource.TaskFinished += new TaskFinishedEventHandler
(TaskFinishedHandler); | |
123 eventSource.CustomEventRaised += new CustomBuildEventHan
dler(CustomHandler); | |
124 eventSource.ErrorRaised += new BuildErrorEventHandler(Er
rorHandler); | |
125 eventSource.WarningRaised += new BuildWarningEventHandle
r(WarningHandler); | |
126 eventSource.MessageRaised += new BuildMessageEventHandle
r(MessageHandler); | |
127 } | |
128 #endregion | |
129 | |
130 #region event delegates | |
131 /// <summary> | |
132 /// This is the delegate for error events. | |
133 /// </summary> | |
134 private void ErrorHandler(object sender, BuildErrorEventArgs err
orEvent) | |
135 { | |
136 AddToErrorList( | |
137 errorEvent, | |
138 errorEvent.Code, | |
139 errorEvent.File, | |
140 errorEvent.LineNumber, | |
141 errorEvent.ColumnNumber); | |
142 } | |
143 | |
144 /// <summary> | |
145 /// This is the delegate for warning events. | |
146 /// </summary> | |
147 private void WarningHandler(object sender, BuildWarningEventArgs
errorEvent) | |
148 { | |
149 AddToErrorList( | |
150 errorEvent, | |
151 errorEvent.Code, | |
152 errorEvent.File, | |
153 errorEvent.LineNumber, | |
154 errorEvent.ColumnNumber); | |
155 } | |
156 | |
157 /// <summary> | |
158 /// Add the error/warning to the error list and potentially to t
he output window. | |
159 /// </summary> | |
160 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Perf
ormance", "CA1800:DoNotCastUnnecessarily")] | |
161 private void AddToErrorList( | |
162 BuildEventArgs errorEvent, | |
163 string errorCode, | |
164 string file, | |
165 int line, | |
166 int column) | |
167 { | |
168 TaskPriority priority = (errorEvent is BuildErrorEventAr
gs) ? TaskPriority.High : TaskPriority.Normal; | |
169 if(OutputWindowPane != null | |
170 && (this.Verbosity != LoggerVerbosity.Quiet || e
rrorEvent is BuildErrorEventArgs)) | |
171 { | |
172 // Format error and output it to the output wind
ow | |
173 string message = FormatMessage(errorEvent.Messag
e); | |
174 CompilerError e = new CompilerError(file, | |
175
line, | |
176
column, | |
177
errorCode, | |
178
message); | |
179 e.IsWarning = (errorEvent is BuildWarningEventAr
gs); | |
180 | |
181 Microsoft.VisualStudio.ErrorHandler.ThrowOnFailu
re(OutputWindowPane.OutputStringThreadSafe(GetFormattedErrorMessage(e))); | |
182 } | |
183 | |
184 // Add error to task list | |
185 ErrorTask task = new ErrorTask(); | |
186 task.Document = file; | |
187 task.Line = line - 1; // The task list does +1 before sh
owing this number. | |
188 task.Column = column; | |
189 task.Text = errorEvent.Message; | |
190 task.Priority = priority; | |
191 task.Category = TaskCategory.BuildCompile; | |
192 task.HierarchyItem = hierarchy; | |
193 task.Navigate += new EventHandler(NavigateTo); | |
194 if(errorEvent is BuildWarningEventArgs) | |
195 task.ErrorCategory = TaskErrorCategory.Warning; | |
196 this.taskProvider.Tasks.Add(task); | |
197 } | |
198 | |
199 | |
200 /// <summary> | |
201 /// This is the delegate for Message event types | |
202 /// </summary> | |
203 private void MessageHandler(object sender, BuildMessageEventArgs
messageEvent) | |
204 { | |
205 if(LogAtImportance(messageEvent.Importance)) | |
206 { | |
207 LogEvent(sender, messageEvent); | |
208 } | |
209 } | |
210 | |
211 private void NavigateTo(object sender, EventArgs arguments) | |
212 { | |
213 Microsoft.VisualStudio.Shell.Task task = sender as Micro
soft.VisualStudio.Shell.Task; | |
214 if(task == null) | |
215 throw new ArgumentException("Sender is not a Mic
rosoft.VisualStudio.Shell.Task", "sender"); | |
216 | |
217 // Get the doc data for the task's document | |
218 if(String.IsNullOrEmpty(task.Document)) | |
219 return; | |
220 | |
221 IVsUIShellOpenDocument openDoc = serviceProvider.GetServ
ice(typeof(IVsUIShellOpenDocument)) as IVsUIShellOpenDocument; | |
222 if(openDoc == null) | |
223 return; | |
224 | |
225 IVsWindowFrame frame; | |
226 IOleServiceProvider sp; | |
227 IVsUIHierarchy hier; | |
228 uint itemid; | |
229 Guid logicalView = VSConstants.LOGVIEWID_Code; | |
230 | |
231 if(Microsoft.VisualStudio.ErrorHandler.Failed(openDoc.Op
enDocumentViaProject(task.Document, ref logicalView, out sp, out hier, out itemi
d, out frame)) || frame == null) | |
232 return; | |
233 | |
234 object docData; | |
235 Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(frame
.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out docData)); | |
236 | |
237 // Get the VsTextBuffer | |
238 VsTextBuffer buffer = docData as VsTextBuffer; | |
239 if(buffer == null) | |
240 { | |
241 IVsTextBufferProvider bufferProvider = docData a
s IVsTextBufferProvider; | |
242 if(bufferProvider != null) | |
243 { | |
244 IVsTextLines lines; | |
245 Microsoft.VisualStudio.ErrorHandler.Thro
wOnFailure(bufferProvider.GetTextBuffer(out lines)); | |
246 buffer = lines as VsTextBuffer; | |
247 Debug.Assert(buffer != null, "IVsTextLin
es does not implement IVsTextBuffer"); | |
248 if(buffer == null) | |
249 return; | |
250 } | |
251 } | |
252 | |
253 // Finally, perform the navigation. | |
254 IVsTextManager mgr = serviceProvider.GetService(typeof(V
sTextManagerClass)) as IVsTextManager; | |
255 if(mgr == null) | |
256 return; | |
257 | |
258 Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(mgr.N
avigateToLineAndColumn(buffer, ref logicalView, task.Line, task.Column, task.Lin
e, task.Column)); | |
259 } | |
260 | |
261 /// <summary> | |
262 /// This is the delegate for BuildStartedHandler events. | |
263 /// </summary> | |
264 private void BuildStartedHandler(object sender, BuildStartedEven
tArgs buildEvent) | |
265 { | |
266 if(LogAtImportance(MessageImportance.Low)) | |
267 { | |
268 LogEvent(sender, buildEvent); | |
269 } | |
270 // Remove all errors and warnings since we are rebuildin
g | |
271 taskProvider.Tasks.Clear(); | |
272 } | |
273 | |
274 /// <summary> | |
275 /// This is the delegate for BuildFinishedHandler events. | |
276 /// </summary> | |
277 /// <param name="sender"></param> | |
278 /// <param name="buildEvent"></param> | |
279 private void BuildFinishedHandler(object sender, BuildFinishedEv
entArgs buildEvent) | |
280 { | |
281 if(LogAtImportance(buildEvent.Succeeded ? MessageImporta
nce.Low : | |
282
MessageImportance.High)) | |
283 { | |
284 if(this.outputWindowPane != null) | |
285 Microsoft.VisualStudio.ErrorHandler.Thro
wOnFailure(this.outputWindowPane.OutputStringThreadSafe(Environment.NewLine)); | |
286 LogEvent(sender, buildEvent); | |
287 } | |
288 } | |
289 | |
290 | |
291 /// <summary> | |
292 /// This is the delegate for ProjectStartedHandler events. | |
293 /// </summary> | |
294 private void ProjectStartedHandler(object sender, ProjectStarted
EventArgs buildEvent) | |
295 { | |
296 if(LogAtImportance(MessageImportance.Low)) | |
297 { | |
298 LogEvent(sender, buildEvent); | |
299 } | |
300 } | |
301 | |
302 /// <summary> | |
303 /// This is the delegate for ProjectFinishedHandler events. | |
304 /// </summary> | |
305 private void ProjectFinishedHandler(object sender, ProjectFinish
edEventArgs buildEvent) | |
306 { | |
307 if(LogAtImportance(buildEvent.Succeeded ? MessageImporta
nce.Low | |
308
: MessageImportance.High)) | |
309 { | |
310 LogEvent(sender, buildEvent); | |
311 } | |
312 } | |
313 | |
314 /// <summary> | |
315 /// This is the delegate for TargetStartedHandler events. | |
316 /// </summary> | |
317 private void TargetStartedHandler(object sender, TargetStartedEv
entArgs buildEvent) | |
318 { | |
319 if(LogAtImportance(MessageImportance.Normal)) | |
320 { | |
321 LogEvent(sender, buildEvent); | |
322 } | |
323 ++this.currentIndent; | |
324 } | |
325 | |
326 | |
327 /// <summary> | |
328 /// This is the delegate for TargetFinishedHandler events. | |
329 /// </summary> | |
330 private void TargetFinishedHandler(object sender, TargetFinished
EventArgs buildEvent) | |
331 { | |
332 --this.currentIndent; | |
333 if((IsLogTaskDone) && | |
334 LogAtImportance(buildEvent.Succeeded ? MessageIm
portance.Low | |
335
: MessageImportance.High)) | |
336 { | |
337 LogEvent(sender, buildEvent); | |
338 } | |
339 } | |
340 | |
341 | |
342 /// <summary> | |
343 /// This is the delegate for TaskStartedHandler events. | |
344 /// </summary> | |
345 private void TaskStartedHandler(object sender, TaskStartedEventA
rgs buildEvent) | |
346 { | |
347 if(LogAtImportance(MessageImportance.Normal)) | |
348 { | |
349 LogEvent(sender, buildEvent); | |
350 } | |
351 ++this.currentIndent; | |
352 } | |
353 | |
354 | |
355 /// <summary> | |
356 /// This is the delegate for TaskFinishedHandler events. | |
357 /// </summary> | |
358 private void TaskFinishedHandler(object sender, TaskFinishedEven
tArgs buildEvent) | |
359 { | |
360 --this.currentIndent; | |
361 if((IsLogTaskDone) && | |
362 LogAtImportance(buildEvent.Succeeded ? MessageIm
portance.Normal | |
363
: MessageImportance.High)) | |
364 { | |
365 LogEvent(sender, buildEvent); | |
366 } | |
367 } | |
368 | |
369 | |
370 /// <summary> | |
371 /// This is the delegate for CustomHandler events. | |
372 /// </summary> | |
373 /// <param name="sender"></param> | |
374 /// <param name="buildEvent"></param> | |
375 private void CustomHandler(object sender, CustomBuildEventArgs b
uildEvent) | |
376 { | |
377 LogEvent(sender, buildEvent); | |
378 } | |
379 | |
380 #endregion | |
381 | |
382 #region helpers | |
383 /// <summary> | |
384 /// This method takes a MessageImportance and returns true if me
ssages | |
385 /// at importance i should be loggeed. Otherwise return false. | |
386 /// </summary> | |
387 private bool LogAtImportance(MessageImportance importance) | |
388 { | |
389 // If importance is too low for current settings, ignore
the event | |
390 bool logIt = false; | |
391 | |
392 this.SetVerbosity(); | |
393 | |
394 switch(this.Verbosity) | |
395 { | |
396 case LoggerVerbosity.Quiet: | |
397 logIt = false; | |
398 break; | |
399 case LoggerVerbosity.Minimal: | |
400 logIt = (importance == MessageImportance
.High); | |
401 break; | |
402 case LoggerVerbosity.Normal: | |
403 // Falling through... | |
404 case LoggerVerbosity.Detailed: | |
405 logIt = (importance != MessageImportance
.Low); | |
406 break; | |
407 case LoggerVerbosity.Diagnostic: | |
408 logIt = true; | |
409 break; | |
410 default: | |
411 Debug.Fail("Unknown Verbosity level. Ign
oring will cause everything to be logged"); | |
412 break; | |
413 } | |
414 | |
415 return logIt; | |
416 } | |
417 | |
418 /// <summary> | |
419 /// This is the method that does the main work of logging an eve
nt | |
420 /// when one is sent to this logger. | |
421 /// </summary> | |
422 private void LogEvent(object sender, BuildEventArgs buildEvent) | |
423 { | |
424 // Fill in the Message text | |
425 if(OutputWindowPane != null && !String.IsNullOrEmpty(bui
ldEvent.Message)) | |
426 { | |
427 StringBuilder msg = new StringBuilder(this.curre
ntIndent + buildEvent.Message.Length + 1); | |
428 if(this.currentIndent > 0) | |
429 { | |
430 msg.Append('\t', this.currentIndent); | |
431 } | |
432 msg.AppendLine(buildEvent.Message); | |
433 Microsoft.VisualStudio.ErrorHandler.ThrowOnFailu
re(this.OutputWindowPane.OutputStringThreadSafe(msg.ToString())); | |
434 } | |
435 } | |
436 | |
437 /// <summary> | |
438 /// Format error messages for the task list | |
439 /// </summary> | |
440 /// <param name="e"></param> | |
441 /// <returns></returns> | |
442 private string GetFormattedErrorMessage(CompilerError e) | |
443 { | |
444 if(e == null) return String.Empty; | |
445 | |
446 string errCode = (e.IsWarning) ? this.WarningString : th
is.ErrorString; | |
447 StringBuilder fileRef = new StringBuilder(); | |
448 | |
449 if(!string.IsNullOrEmpty(e.FileName)) | |
450 { | |
451 fileRef.AppendFormat(CultureInfo.CurrentUICultur
e, "{0}({1},{2}):", | |
452
e.FileName, e.Line, e.Column); | |
453 } | |
454 fileRef.AppendFormat(CultureInfo.CurrentUICulture, " {0}
{1}: {2}", errCode, e.ErrorNumber, e.ErrorText); | |
455 | |
456 return fileRef.ToString(); | |
457 } | |
458 | |
459 /// <summary> | |
460 /// Formats the message that is to be output. | |
461 /// </summary> | |
462 /// <param name="message">The message string.</param> | |
463 /// <returns>The new message</returns> | |
464 private static string FormatMessage(string message) | |
465 { | |
466 if(string.IsNullOrEmpty(message)) | |
467 { | |
468 return Environment.NewLine; | |
469 } | |
470 | |
471 StringBuilder sb = new StringBuilder(message.Length + En
vironment.NewLine.Length); | |
472 | |
473 sb.AppendLine(message); | |
474 return sb.ToString(); | |
475 } | |
476 | |
477 /// <summary> | |
478 /// Sets the verbosity level. | |
479 /// </summary> | |
480 private void SetVerbosity() | |
481 { | |
482 // TODO: This should be replaced when we have a version
that supports automation. | |
483 | |
484 string verbosityKey = String.Format(CultureInfo.Invarian
tCulture, @"{0}\{1}", BuildVerbosityRegistryRoot, buildVerbosityRegistrySubKey); | |
485 using(RegistryKey subKey = Registry.CurrentUser.OpenSubK
ey(verbosityKey)) | |
486 { | |
487 if(subKey != null) | |
488 { | |
489 object valueAsObject = subKey.GetValue(b
uildVerbosityRegistryKey); | |
490 if(valueAsObject != null) | |
491 { | |
492 this.Verbosity = (LoggerVerbosit
y)((int)valueAsObject); | |
493 } | |
494 } | |
495 } | |
496 | |
497 // TODO: Continue this code to get the Verbosity when we
have a version that supports automation to get the Verbosity. | |
498 //EnvDTE.DTE dte = this.serviceProvider.GetService(typeo
f(EnvDTE.DTE)) as EnvDTE.DTE; | |
499 //EnvDTE.Properties properties = dte.get_Properties(Envi
ronmentCategory, ProjectsAndSolutionSubCategory); | |
500 } | |
501 #endregion | |
502 } | |
503 | |
504 } | |
OLD | NEW |