Browse Source

Allow SharpDevelop AddIn to filter the logger output. Removed mutable 'CurrentErrorOrWarning' in BuildEngine and use logger filters instead.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5643 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Daniel Grunwald 16 years ago
parent
commit
0b8d32fb61
  1. 1
      AddIns/ICSharpCode.SharpDevelop.addin
  2. 4
      clean.bat
  3. 70
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs
  4. 4
      src/AddIns/BackendBindings/VBNetBinding/Project/VBNetBinding.addin
  5. 4
      src/AddIns/Misc/CodeAnalysis/CodeAnalysis.addin
  6. 106
      src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs
  7. 3
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  8. 4
      src/Main/Base/Project/Src/Project/BuildEngine.cs
  9. 12
      src/Main/Base/Project/Src/Project/BuildError.cs
  10. 32
      src/Main/Base/Project/Src/Project/MSBuildEngine/BuildWorkerManager.cs
  11. 0
      src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildAdditionalLogger.cs
  12. 147
      src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildEngine.cs
  13. 136
      src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildLoggerFilter.cs

1
AddIns/ICSharpCode.SharpDevelop.addin

@ -46,6 +46,7 @@
<Doozer name="Debugger" class="ICSharpCode.SharpDevelop.Debugging.DebuggerDoozer"/> <Doozer name="Debugger" class="ICSharpCode.SharpDevelop.Debugging.DebuggerDoozer"/>
<Doozer name="Directory" class="ICSharpCode.SharpDevelop.DirectoryDoozer"/> <Doozer name="Directory" class="ICSharpCode.SharpDevelop.DirectoryDoozer"/>
<Doozer name="TaskBoundAdditionalLogger" class="ICSharpCode.SharpDevelop.Project.TaskBoundAdditionalLoggerDoozer"/> <Doozer name="TaskBoundAdditionalLogger" class="ICSharpCode.SharpDevelop.Project.TaskBoundAdditionalLoggerDoozer"/>
<Doozer name="TaskBoundLoggerFilter" class="ICSharpCode.SharpDevelop.Project.TaskBoundLoggerFilterDoozer"/>
</Import> </Import>
</Runtime> </Runtime>

4
clean.bat

@ -1,2 +1,4 @@
%windir%\microsoft.net\framework\v4.0.30128\msbuild /m SharpDevelop.sln /t:clean "/p:Platform=Any CPU" %windir%\microsoft.net\framework\v4.0.30128\msbuild /m SharpDevelop.sln /t:clean "/p:Platform=Any CPU" /p:Configuration=Debug
@IF %ERRORLEVEL% NEQ 0 PAUSE
%windir%\microsoft.net\framework\v4.0.30128\msbuild /m SharpDevelop.sln /t:clean "/p:Platform=Any CPU" /p:Configuration=Release
@IF %ERRORLEVEL% NEQ 0 PAUSE @IF %ERRORLEVEL% NEQ 0 PAUSE

70
src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs

@ -16,77 +16,39 @@ namespace VBNetBinding
/// <summary> /// <summary>
/// Fixes SD2-995 : Special characters not correctly encoded for languages others than English /// Fixes SD2-995 : Special characters not correctly encoded for languages others than English
/// </summary> /// </summary>
public class VbcEncodingFixingLogger : IMSBuildAdditionalLogger public sealed class VbcEncodingFixingLogger : IMSBuildLoggerFilter
{ {
public ILogger CreateLogger(MSBuildEngine engineWorker) public IMSBuildChainedLoggerFilter CreateFilter(MSBuildEngine engine, IMSBuildChainedLoggerFilter nextFilter)
{ {
return new VbcLoggerImpl(engineWorker); return new VbcLoggerImpl(engine, nextFilter);
} }
private class VbcLoggerImpl : ILogger sealed class VbcLoggerImpl : IMSBuildChainedLoggerFilter
{ {
MSBuildEngine engineWorker; readonly MSBuildEngine engineWorker;
readonly IMSBuildChainedLoggerFilter nextFilter;
public VbcLoggerImpl(MSBuildEngine engineWorker) public VbcLoggerImpl(MSBuildEngine engineWorker, IMSBuildChainedLoggerFilter nextFilter)
{ {
this.engineWorker = engineWorker; this.engineWorker = engineWorker;
this.nextFilter = nextFilter;
} }
public LoggerVerbosity Verbosity { static string FixEncoding(string text)
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
public string Parameters {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
IEventSource eventSource;
public void Initialize(IEventSource eventSource)
{
this.eventSource = eventSource;
eventSource.ErrorRaised += OnError;
eventSource.WarningRaised += OnWarning;
}
public void Shutdown()
{
if (eventSource != null) {
eventSource.ErrorRaised -= OnError;
eventSource.WarningRaised -= OnWarning;
eventSource = null;
}
}
void OnError(object sender, BuildErrorEventArgs e)
{
FixMessage();
}
void OnWarning(object sender, BuildWarningEventArgs e)
{ {
FixMessage(); return Encoding.Default.GetString(ICSharpCode.SharpDevelop.Util.ProcessRunner.OemEncoding.GetBytes(text));
} }
void FixMessage() public void HandleError(BuildError error)
{ {
engineWorker.CurrentErrorOrWarning.ErrorText = FixEncoding(engineWorker.CurrentErrorOrWarning.ErrorText); error.ErrorText = FixEncoding(error.ErrorText);
engineWorker.CurrentErrorOrWarning.FileName = FixEncoding(engineWorker.CurrentErrorOrWarning.FileName); error.FileName = FixEncoding(error.FileName);
nextFilter.HandleError(error);
} }
static string FixEncoding(string text) public void HandleBuildEvent(Microsoft.Build.Framework.BuildEventArgs e)
{ {
return Encoding.Default.GetString(ICSharpCode.SharpDevelop.Util.ProcessRunner.OemEncoding.GetBytes(text)); nextFilter.HandleBuildEvent(e);
} }
} }
} }

4
src/AddIns/BackendBindings/VBNetBinding/Project/VBNetBinding.addin

@ -39,8 +39,8 @@
<String id="vbc" text = "vbc"/> <String id="vbc" text = "vbc"/>
</Path> </Path>
<Path name = "/SharpDevelop/MSBuildEngine/AdditionalLoggers"> <Path name = "/SharpDevelop/MSBuildEngine/LoggerFilters">
<TaskBoundAdditionalLogger <TaskBoundLoggerFilter
id = "VbcEncodingFixingLogger" id = "VbcEncodingFixingLogger"
taskname = "vbc" taskname = "vbc"
class = "VBNetBinding.VbcEncodingFixingLogger"/> class = "VBNetBinding.VbcEncodingFixingLogger"/>

4
src/AddIns/Misc/CodeAnalysis/CodeAnalysis.addin

@ -42,8 +42,8 @@
</ComplexCondition> </ComplexCondition>
</Path> </Path>
<Path name = "/SharpDevelop/MSBuildEngine/AdditionalLoggers"> <Path name = "/SharpDevelop/MSBuildEngine/LoggerFilters">
<TaskBoundAdditionalLogger <TaskBoundLoggerFilter
id = "FxCopLogger" id = "FxCopLogger"
taskname = "FxCop" taskname = "FxCop"
class = "ICSharpCode.CodeAnalysis.FxCopLogger"/> class = "ICSharpCode.CodeAnalysis.FxCopLogger"/>

106
src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs

@ -20,114 +20,80 @@ namespace ICSharpCode.CodeAnalysis
/// so this logger fixes the position. /// so this logger fixes the position.
/// Additionally, it registers the context menu containing the 'suppress message' command. /// Additionally, it registers the context menu containing the 'suppress message' command.
/// </summary> /// </summary>
public class FxCopLogger : IMSBuildAdditionalLogger public class FxCopLogger : IMSBuildLoggerFilter
{ {
public ILogger CreateLogger(MSBuildEngine engineWorker) public IMSBuildChainedLoggerFilter CreateFilter(MSBuildEngine engine, IMSBuildChainedLoggerFilter nextFilter)
{ {
return new FxCopLoggerImpl(engineWorker); engine.OutputTextLine(StringParser.Parse("${res:ICSharpCode.CodeAnalysis.RunningFxCopOn} " + Path.GetFileNameWithoutExtension(engine.ProjectFileName)));
return new FxCopLoggerImpl(engine, nextFilter);
} }
private class FxCopLoggerImpl : ILogger sealed class FxCopLoggerImpl : IMSBuildChainedLoggerFilter
{ {
MSBuildEngine engineWorker; readonly MSBuildEngine engineWorker;
readonly IMSBuildChainedLoggerFilter nextChainElement;
public FxCopLoggerImpl(MSBuildEngine engineWorker) public FxCopLoggerImpl(MSBuildEngine engineWorker, IMSBuildChainedLoggerFilter nextChainElement)
{ {
this.engineWorker = engineWorker; this.engineWorker = engineWorker;
this.nextChainElement = nextChainElement;
} }
public LoggerVerbosity Verbosity { get; set; } public void HandleError(BuildError error)
public string Parameters { get; set; }
IEventSource eventSource;
public void Initialize(IEventSource eventSource)
{
this.eventSource = eventSource;
engineWorker.OutputText(StringParser.Parse("${res:ICSharpCode.CodeAnalysis.RunningFxCopOn} " + Path.GetFileNameWithoutExtension(engineWorker.CurrentProjectFile)));
eventSource.ErrorRaised += OnError;
eventSource.WarningRaised += OnWarning;
}
public void Shutdown()
{
if (eventSource != null) {
eventSource.ErrorRaised -= OnError;
eventSource.WarningRaised -= OnWarning;
eventSource = null;
}
}
void OnError(object sender, BuildErrorEventArgs e)
{ {
AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Message, false, LoggingService.Debug("FxCopLogger got " + error.ToString());
e.HelpKeyword, e.Code, e.Subcategory);
}
void OnWarning(object sender, BuildWarningEventArgs e) string[] moreData = (error.Subcategory ?? "").Split('|');
string checkId = error.ErrorCode;
error.ErrorCode = (error.ErrorCode != null) ? error.ErrorCode.Split(':')[0] : null;
if (FileUtility.IsValidPath(error.FileName) &&
Path.GetFileName(error.FileName) == "SharpDevelop.CodeAnalysis.targets")
{ {
AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Message, true, error.FileName = null;
e.HelpKeyword, e.Code, e.Subcategory);
} }
IProject project = ProjectService.GetProject(engineWorker.ProjectFileName);
void AppendError(string file, int lineNumber, int columnNumber,
string message, bool isWarning,
string category, string checkId, string subcategory)
{
LoggingService.Debug("Got " + (isWarning ? "warning" : "error") + ":\n"
+ " file: " + file + "\n"
+ " line: " + lineNumber + ", col: " + columnNumber + "\n"
+ " message: " + message + "\n"
+ " category: " + category + "\n"
+ " checkId: " + checkId + "\n"
+ " subcategory: " + subcategory);
string[] moreData = (subcategory ?? "").Split('|');
BuildError err = engineWorker.CurrentErrorOrWarning;
err.ErrorCode = (checkId != null) ? checkId.Split(':')[0] : null;
if (FileUtility.IsValidPath(file) &&
Path.GetFileName(file) == "SharpDevelop.CodeAnalysis.targets")
{
err.FileName = null;
}
IProject project = ProjectService.GetProject(engineWorker.CurrentProjectFile);
if (project != null) { if (project != null) {
IProjectContent pc = ParserService.GetProjectContent(project); IProjectContent pc = ParserService.GetProjectContent(project);
if (pc != null) { if (pc != null) {
if (file.StartsWith("positionof#")) { if (error.FileName != null && error.FileName.StartsWith("positionof#")) {
string memberName = file.Substring(11); string memberName = error.FileName.Substring(11);
file = "";
FilePosition pos = GetPosition(pc, memberName); FilePosition pos = GetPosition(pc, memberName);
if (pos.IsEmpty == false && pos.CompilationUnit != null) { if (pos.IsEmpty == false && pos.CompilationUnit != null) {
err.FileName = pos.FileName ?? ""; error.FileName = pos.FileName ?? "";
err.Line = pos.Line; error.Line = pos.Line;
err.Column = pos.Column; error.Column = pos.Column;
} else { } else {
err.FileName = null; error.FileName = null;
} }
} }
if (moreData.Length > 1 && !string.IsNullOrEmpty(moreData[0])) { if (moreData.Length > 1 && !string.IsNullOrEmpty(moreData[0])) {
err.Tag = new FxCopTaskTag { error.Tag = new FxCopTaskTag {
ProjectContent = pc, ProjectContent = pc,
TypeName = moreData[0], TypeName = moreData[0],
MemberName = moreData[1], MemberName = moreData[1],
Category = category, Category = error.HelpKeyword,
CheckID = checkId CheckID = checkId
}; };
} else { } else {
err.Tag = new FxCopTaskTag { error.Tag = new FxCopTaskTag {
ProjectContent = pc, ProjectContent = pc,
Category = category, Category = error.HelpKeyword,
CheckID = checkId CheckID = checkId
}; };
} }
err.ContextMenuAddInTreeEntry = "/SharpDevelop/Pads/ErrorList/CodeAnalysisTaskContextMenu"; error.ContextMenuAddInTreeEntry = "/SharpDevelop/Pads/ErrorList/CodeAnalysisTaskContextMenu";
if (moreData.Length > 2) { if (moreData.Length > 2) {
(err.Tag as FxCopTaskTag).MessageID = moreData[2]; (error.Tag as FxCopTaskTag).MessageID = moreData[2];
}
} }
} }
nextChainElement.HandleError(error);
} }
public void HandleBuildEvent(Microsoft.Build.Framework.BuildEventArgs e)
{
nextChainElement.HandleBuildEvent(e);
} }
static FilePosition GetPosition(IProjectContent pc, string memberName) static FilePosition GetPosition(IProjectContent pc, string memberName)

3
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -260,6 +260,8 @@
<Compile Include="Src\Project\Converter\UpgradeViewContent.cs" /> <Compile Include="Src\Project\Converter\UpgradeViewContent.cs" />
<Compile Include="Src\Project\IBuildFeedbackSink.cs" /> <Compile Include="Src\Project\IBuildFeedbackSink.cs" />
<Compile Include="Src\Project\IProjectItemBackendStore.cs" /> <Compile Include="Src\Project\IProjectItemBackendStore.cs" />
<Compile Include="Src\Project\MSBuildEngine\MSBuildAdditionalLogger.cs" />
<Compile Include="Src\Project\MSBuildEngine\MSBuildLoggerFilter.cs" />
<Compile Include="Src\Project\MSBuildEngine\SDConsoleLogger.cs" /> <Compile Include="Src\Project\MSBuildEngine\SDConsoleLogger.cs" />
<Compile Include="Src\Project\MSBuildEngine\BuildWorkerManager.cs" /> <Compile Include="Src\Project\MSBuildEngine\BuildWorkerManager.cs" />
<Compile Include="Src\Project\MSBuildEngine\MSBuildEngine.cs" /> <Compile Include="Src\Project\MSBuildEngine\MSBuildEngine.cs" />
@ -711,7 +713,6 @@
<Compile Include="Src\TextEditor\Gui\Editor\TextNavigationPoint.cs" /> <Compile Include="Src\TextEditor\Gui\Editor\TextNavigationPoint.cs" />
<Compile Include="Src\Project\BuildResults.cs" /> <Compile Include="Src\Project\BuildResults.cs" />
<Compile Include="Src\Project\BuildError.cs" /> <Compile Include="Src\Project\BuildError.cs" />
<Compile Include="Src\Project\MSBuildAdditionalLogger.cs" />
<Compile Include="Src\Services\HelpProvider.cs" /> <Compile Include="Src\Services\HelpProvider.cs" />
<Compile Include="Src\Services\ParserService\CodeCompletionOptions.cs" /> <Compile Include="Src\Services\ParserService\CodeCompletionOptions.cs" />
<Compile Include="Src\Services\RefactoringService\TextEditorDocument.cs" /> <Compile Include="Src\Services\RefactoringService\TextEditorDocument.cs" />

4
src/Main/Base/Project/Src/Project/BuildEngine.cs

@ -45,7 +45,9 @@ namespace ICSharpCode.SharpDevelop.Project
if (guiBuildCancellation != null) { if (guiBuildCancellation != null) {
BuildResults results = new BuildResults(); BuildResults results = new BuildResults();
StatusBarService.ShowErrorMessage(Core.ResourceService.GetString("MainWindow.CompilerMessages.MSBuildAlreadyRunning")); StatusBarService.ShowErrorMessage(Core.ResourceService.GetString("MainWindow.CompilerMessages.MSBuildAlreadyRunning"));
results.Add(new BuildError(null, Core.ResourceService.GetString("MainWindow.CompilerMessages.MSBuildAlreadyRunning"))); BuildError error = new BuildError(null, Core.ResourceService.GetString("MainWindow.CompilerMessages.MSBuildAlreadyRunning"));
results.Add(error);
TaskService.Add(new Task(error));
results.Result = BuildResultCode.MSBuildAlreadyRunning; results.Result = BuildResultCode.MSBuildAlreadyRunning;
if (options.Callback != null) { if (options.Callback != null) {
options.Callback(results); options.Callback(results);

12
src/Main/Base/Project/Src/Project/BuildError.cs

@ -50,6 +50,18 @@ namespace ICSharpCode.SharpDevelop.Project
[NonSerialized] [NonSerialized]
object tag; object tag;
string contextMenuAddInTreeEntry; string contextMenuAddInTreeEntry;
string subcategory;
string helpKeyword;
public string HelpKeyword {
get { return helpKeyword; }
set { helpKeyword = value; }
}
public string Subcategory {
get { return subcategory; }
set { subcategory = value; }
}
public int Column { public int Column {
get { get {

32
src/Main/Base/Project/Src/Project/MSBuildEngine/BuildWorkerManager.cs

@ -31,10 +31,10 @@ namespace ICSharpCode.SharpDevelop.Project
this.workerProcessName = workerProcessName; this.workerProcessName = workerProcessName;
} }
public void RunBuildJob(BuildJob job, IEnumerable<ILogger> loggers, IBuildFeedbackSink reportWhenDone) public void RunBuildJob(BuildJob job, IMSBuildChainedLoggerFilter loggerChain, Action<bool> reportWhenDone, CancellationToken cancellationToken)
{ {
BuildWorker worker = GetFreeWorker(); BuildWorker worker = GetFreeWorker();
worker.RunJob(job, loggers, reportWhenDone); worker.RunJob(job, loggerChain, reportWhenDone, cancellationToken);
} }
BuildWorker GetFreeWorker() BuildWorker GetFreeWorker()
@ -70,26 +70,21 @@ namespace ICSharpCode.SharpDevelop.Project
process.Start(startInfo); process.Start(startInfo);
} }
EventSource source; IMSBuildChainedLoggerFilter loggerChain;
IEnumerable<ILogger> loggers; Action<bool> reportWhenDone;
IBuildFeedbackSink reportWhenDone;
CancellationTokenRegistration cancellationRegistration; CancellationTokenRegistration cancellationRegistration;
public void RunJob(BuildJob job, IEnumerable<ILogger> loggers, IBuildFeedbackSink reportWhenDone) public void RunJob(BuildJob job, IMSBuildChainedLoggerFilter loggerChain, Action<bool> reportWhenDone, CancellationToken cancellationToken)
{ {
this.source = new EventSource(); this.loggerChain = loggerChain;
this.loggers = loggers;
this.reportWhenDone = reportWhenDone; this.reportWhenDone = reportWhenDone;
foreach (var logger in loggers) {
logger.Initialize(source);
}
try { try {
process.Writer.Write("StartBuild"); process.Writer.Write("StartBuild");
job.WriteTo(process.Writer); job.WriteTo(process.Writer);
this.cancellationRegistration = reportWhenDone.ProgressMonitor.CancellationToken.Register(OnCancel); this.cancellationRegistration = cancellationToken.Register(OnCancel);
} catch (IOException ex) { } catch (IOException ex) {
// "Pipe is broken" // "Pipe is broken"
source.ForwardEvent(new BuildErrorEventArgs(null, null, null, 0, 0, 0, 0, "Error talking to build worker: " + ex.Message, null, null)); loggerChain.HandleError(new BuildError(null, 0, 0, null, "Error talking to build worker: " + ex.Message));
BuildDone(false); BuildDone(false);
} }
} }
@ -105,7 +100,7 @@ namespace ICSharpCode.SharpDevelop.Project
LoggingService.Debug("Received command " + command); LoggingService.Debug("Received command " + command);
switch (command) { switch (command) {
case "ReportEvent": case "ReportEvent":
source.ForwardEvent(EventSource.DecodeEvent(reader)); loggerChain.HandleBuildEvent(EventSource.DecodeEvent(reader));
break; break;
case "BuildDone": case "BuildDone":
bool success = reader.ReadBoolean(); bool success = reader.ReadBoolean();
@ -127,10 +122,7 @@ namespace ICSharpCode.SharpDevelop.Project
if (reportWhenDone == null) if (reportWhenDone == null)
return; return;
cancellationRegistration.Dispose(); cancellationRegistration.Dispose();
foreach (var logger in loggers) { reportWhenDone(success);
logger.Shutdown();
}
reportWhenDone.Done(success);
reportWhenDone = null; reportWhenDone = null;
} }
} }
@ -163,11 +155,11 @@ namespace ICSharpCode.SharpDevelop.Project
parentManager.freeWorkers.Remove(this); parentManager.freeWorkers.Remove(this);
MarkAsInUse(); MarkAsInUse();
} else { } else {
reportBuildError = true; reportBuildError = (reportWhenDone != null); // only if not done
} }
} }
if (reportBuildError) { if (reportBuildError) {
source.ForwardEvent(new BuildErrorEventArgs(null, null, null, 0, 0, 0, 0, "Build worker process exited unexpectedly.", null, null)); loggerChain.HandleError(new BuildError(null, 0, 0, null, "Build worker process exited unexpectedly."));
BuildDone(false); BuildDone(false);
} }
process.Dispose(); process.Dispose();

0
src/Main/Base/Project/Src/Project/MSBuildAdditionalLogger.cs → src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildAdditionalLogger.cs

147
src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildEngine.cs

@ -34,6 +34,7 @@ namespace ICSharpCode.SharpDevelop.Project
const string CompileTaskNamesPath = "/SharpDevelop/MSBuildEngine/CompileTaskNames"; const string CompileTaskNamesPath = "/SharpDevelop/MSBuildEngine/CompileTaskNames";
const string AdditionalTargetFilesPath = "/SharpDevelop/MSBuildEngine/AdditionalTargetFiles"; const string AdditionalTargetFilesPath = "/SharpDevelop/MSBuildEngine/AdditionalTargetFiles";
const string AdditionalLoggersPath = "/SharpDevelop/MSBuildEngine/AdditionalLoggers"; const string AdditionalLoggersPath = "/SharpDevelop/MSBuildEngine/AdditionalLoggers";
const string LoggerFiltersPath = "/SharpDevelop/MSBuildEngine/LoggerFilters";
internal const string AdditionalPropertiesPath = "/SharpDevelop/MSBuildEngine/AdditionalProperties"; internal const string AdditionalPropertiesPath = "/SharpDevelop/MSBuildEngine/AdditionalProperties";
/// <summary> /// <summary>
@ -75,6 +76,13 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
public static readonly IList<IMSBuildAdditionalLogger> AdditionalMSBuildLoggers; public static readonly IList<IMSBuildAdditionalLogger> AdditionalMSBuildLoggers;
/// <summary>
/// Gets a list of MSBuild logger filter.
/// You can register your loggers by putting them into
/// "/SharpDevelop/MSBuildEngine/LoggerFilters"
/// </summary>
public static readonly IList<IMSBuildLoggerFilter> MSBuildLoggerFilters;
public static string SharpDevelopBinPath { public static string SharpDevelopBinPath {
get { get {
return Path.GetDirectoryName(typeof(MSBuildEngine).Assembly.Location); return Path.GetDirectoryName(typeof(MSBuildEngine).Assembly.Location);
@ -89,6 +97,7 @@ namespace ICSharpCode.SharpDevelop.Project
); );
AdditionalTargetFiles = AddInTree.BuildItems<string>(AdditionalTargetFilesPath, null, false); AdditionalTargetFiles = AddInTree.BuildItems<string>(AdditionalTargetFilesPath, null, false);
AdditionalMSBuildLoggers = AddInTree.BuildItems<IMSBuildAdditionalLogger>(AdditionalLoggersPath, null, false); AdditionalMSBuildLoggers = AddInTree.BuildItems<IMSBuildAdditionalLogger>(AdditionalLoggersPath, null, false);
MSBuildLoggerFilters = AddInTree.BuildItems<IMSBuildLoggerFilter>(LoggerFiltersPath, null, false);
} }
public static void StartBuild(IProject project, ThreadSafeServiceContainer serviceContainer, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink, IEnumerable<string> additionalTargetFiles) public static void StartBuild(IProject project, ThreadSafeServiceContainer serviceContainer, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink, IEnumerable<string> additionalTargetFiles)
@ -110,7 +119,8 @@ namespace ICSharpCode.SharpDevelop.Project
engine.StartBuild(); engine.StartBuild();
} }
IProject project; readonly string projectFileName;
readonly int projectMinimumSolutionVersion;
ProjectBuildOptions options; ProjectBuildOptions options;
IBuildFeedbackSink feedbackSink; IBuildFeedbackSink feedbackSink;
IEnumerable<string> additionalTargetFiles; IEnumerable<string> additionalTargetFiles;
@ -118,7 +128,8 @@ namespace ICSharpCode.SharpDevelop.Project
private MSBuildEngine(IProject project, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink) private MSBuildEngine(IProject project, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink)
{ {
this.project = project; this.projectFileName = project.FileName;
this.projectMinimumSolutionVersion = project.MinimumSolutionVersion;
this.options = options; this.options = options;
this.feedbackSink = feedbackSink; this.feedbackSink = feedbackSink;
} }
@ -163,6 +174,13 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
public bool ReportUnknownEvents { get; set; } public bool ReportUnknownEvents { get; set; }
/// <summary>
/// Gets the name of the project file being compiled by this engine.
/// </summary>
public string ProjectFileName {
get { return projectFileName; }
}
List<string> interestingTasks = new List<string>(); List<string> interestingTasks = new List<string>();
string temporaryFileName; string temporaryFileName;
@ -175,6 +193,10 @@ namespace ICSharpCode.SharpDevelop.Project
get { return interestingTasks; } get { return interestingTasks; }
} }
readonly EventSource eventSource = new EventSource();
List<ILogger> loggers = new List<ILogger>();
IMSBuildChainedLoggerFilter loggerChain;
void StartBuild() void StartBuild()
{ {
Dictionary<string, string> globalProperties = new Dictionary<string, string>(); Dictionary<string, string> globalProperties = new Dictionary<string, string>();
@ -192,7 +214,6 @@ namespace ICSharpCode.SharpDevelop.Project
InterestingTasks.AddRange(MSBuildEngine.CompileTaskNames); InterestingTasks.AddRange(MSBuildEngine.CompileTaskNames);
List<ILogger> loggers = new List<ILogger>();
loggers.Add(new SharpDevelopLogger(this)); loggers.Add(new SharpDevelopLogger(this));
if (options.BuildOutputVerbosity == BuildOutputVerbosity.Diagnostic) { if (options.BuildOutputVerbosity == BuildOutputVerbosity.Diagnostic) {
this.ReportMessageEvents = true; this.ReportMessageEvents = true;
@ -207,10 +228,16 @@ namespace ICSharpCode.SharpDevelop.Project
foreach (IMSBuildAdditionalLogger loggerProvider in MSBuildEngine.AdditionalMSBuildLoggers) { foreach (IMSBuildAdditionalLogger loggerProvider in MSBuildEngine.AdditionalMSBuildLoggers) {
loggers.Add(loggerProvider.CreateLogger(this)); loggers.Add(loggerProvider.CreateLogger(this));
} }
loggerChain = new EndOfChain(this);
foreach (IMSBuildLoggerFilter loggerFilter in MSBuildEngine.MSBuildLoggerFilters) {
loggerChain = loggerFilter.CreateFilter(this, loggerChain) ?? loggerChain;
}
WriteAdditionalTargetsToTempFile(globalProperties); WriteAdditionalTargetsToTempFile(globalProperties);
BuildJob job = new BuildJob(); BuildJob job = new BuildJob();
job.ProjectFileName = project.FileName; job.ProjectFileName = projectFileName;
job.Target = options.Target.TargetName; job.Target = options.Target.TargetName;
// First remove the flags for the controllable events. // First remove the flags for the controllable events.
@ -237,13 +264,25 @@ namespace ICSharpCode.SharpDevelop.Project
job.Properties.Add(pair.Key, pair.Value); job.Properties.Add(pair.Key, pair.Value);
} }
if (project.MinimumSolutionVersion <= Solution.SolutionVersionVS2008) { foreach (ILogger logger in loggers) {
BuildWorkerManager.MSBuild35.RunBuildJob(job, loggers, feedbackSink); logger.Initialize(eventSource);
}
if (projectMinimumSolutionVersion <= Solution.SolutionVersionVS2008) {
BuildWorkerManager.MSBuild35.RunBuildJob(job, loggerChain, OnDone, feedbackSink.ProgressMonitor.CancellationToken);
} else { } else {
BuildWorkerManager.MSBuild40.RunBuildJob(job, loggers, feedbackSink); BuildWorkerManager.MSBuild40.RunBuildJob(job, loggerChain, OnDone, feedbackSink.ProgressMonitor.CancellationToken);
} }
} }
void OnDone(bool success)
{
foreach (ILogger logger in loggers) {
logger.Shutdown();
}
feedbackSink.Done(success);
}
void WriteAdditionalTargetsToTempFile(Dictionary<string, string> globalProperties) void WriteAdditionalTargetsToTempFile(Dictionary<string, string> globalProperties)
{ {
// Using projects with in-memory modifications doesn't work with parallel build. // Using projects with in-memory modifications doesn't work with parallel build.
@ -273,7 +312,7 @@ namespace ICSharpCode.SharpDevelop.Project
// 'MsTestToolsTargets' is preferred because it's at the end of the MSBuild 3.5 and 4.0 target file, // 'MsTestToolsTargets' is preferred because it's at the end of the MSBuild 3.5 and 4.0 target file,
// but on MSBuild 2.0 we need to fall back to 'CodeAnalysisTargets'. // but on MSBuild 2.0 we need to fall back to 'CodeAnalysisTargets'.
string hijackedProperty = "MsTestToolsTargets"; string hijackedProperty = "MsTestToolsTargets";
if (project.MinimumSolutionVersion == Solution.SolutionVersionVS2005) if (projectMinimumSolutionVersion == Solution.SolutionVersionVS2005)
hijackedProperty = "CodeAnalysisTargets"; hijackedProperty = "CodeAnalysisTargets";
// because we'll replace the hijackedProperty, manually write the corresponding include // because we'll replace the hijackedProperty, manually write the corresponding include
@ -295,33 +334,7 @@ namespace ICSharpCode.SharpDevelop.Project
#endif #endif
} }
BuildError currentErrorOrWarning; public void OutputTextLine(string message)
/// <summary>
/// Gets the last build error/warning created by the default
/// SharpDevelop logger.
/// </summary>
public BuildError CurrentErrorOrWarning {
get {
return currentErrorOrWarning;
}
}
Stack<string> projectFiles = new Stack<string>();
/// <summary>
/// Gets the name of the currently building project file.
/// </summary>
public string CurrentProjectFile {
get {
if (projectFiles.Count == 0)
return null;
else
return projectFiles.Peek();
}
}
public void OutputText(string message)
{ {
feedbackSink.ReportMessage(message); feedbackSink.ReportMessage(message);
} }
@ -331,40 +344,33 @@ namespace ICSharpCode.SharpDevelop.Project
feedbackSink.ReportError(error); feedbackSink.ReportError(error);
} }
class SharpDevelopLogger : ILogger sealed class EndOfChain : IMSBuildChainedLoggerFilter
{ {
MSBuildEngine worker; readonly MSBuildEngine engine;
public SharpDevelopLogger(MSBuildEngine engine) public EndOfChain(MSBuildEngine engine)
{ {
this.worker = engine; this.engine = engine;
} }
void AppendLine(string text) public void HandleError(BuildError error)
{ {
worker.OutputText(text); engine.ReportError(error);
} }
internal void FlushCurrentError() public void HandleBuildEvent(Microsoft.Build.Framework.BuildEventArgs e)
{ {
if (worker.currentErrorOrWarning != null) { engine.eventSource.ForwardEvent(e);
worker.ReportError(worker.currentErrorOrWarning);
worker.currentErrorOrWarning = null;
} }
} }
void OnProjectStarted(object sender, ProjectStartedEventArgs e) sealed class SharpDevelopLogger : ILogger
{ {
worker.projectFiles.Push(e.ProjectFile); MSBuildEngine engine;
}
void OnProjectFinished(object sender, ProjectFinishedEventArgs e) public SharpDevelopLogger(MSBuildEngine engine)
{ {
FlushCurrentError(); this.engine = engine;
// it's possible that MSBuild raises ProjectFinished without a matching
// ProjectStarted - e.g. if an additional import is missing
if (worker.projectFiles.Count > 0)
worker.projectFiles.Pop();
} }
string activeTaskName; string activeTaskName;
@ -373,34 +379,28 @@ namespace ICSharpCode.SharpDevelop.Project
{ {
activeTaskName = e.TaskName; activeTaskName = e.TaskName;
if (MSBuildEngine.CompileTaskNames.Contains(e.TaskName.ToLowerInvariant())) { if (MSBuildEngine.CompileTaskNames.Contains(e.TaskName.ToLowerInvariant())) {
AppendLine(StringParser.Parse("${res:MainWindow.CompilerMessages.CompileVerb} " + Path.GetFileNameWithoutExtension(e.ProjectFile))); engine.OutputTextLine(StringParser.Parse("${res:MainWindow.CompilerMessages.CompileVerb} " + Path.GetFileNameWithoutExtension(e.ProjectFile)));
}
} }
void OnTaskFinished(object sender, TaskFinishedEventArgs e)
{
FlushCurrentError();
} }
void OnError(object sender, BuildErrorEventArgs e) void OnError(object sender, BuildErrorEventArgs e)
{ {
AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, false); AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, e.ProjectFile, e.Subcategory, e.HelpKeyword, false);
} }
void OnWarning(object sender, BuildWarningEventArgs e) void OnWarning(object sender, BuildWarningEventArgs e)
{ {
AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, true); AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, e.ProjectFile, e.Subcategory, e.HelpKeyword, true);
} }
// TODO: Add XmlDocBloc to MSBuildError.AppendError() void AppendError(string file, int lineNumber, int columnNumber, string code, string message, string projectFile, string subcategory, string helpKeyword, bool isWarning)
void AppendError(string file, int lineNumber, int columnNumber, string code, string message, bool isWarning)
{ {
if (string.Equals(file, activeTaskName, StringComparison.OrdinalIgnoreCase)) { if (string.Equals(file, activeTaskName, StringComparison.OrdinalIgnoreCase)) {
file = ""; file = "";
} else if (FileUtility.IsValidPath(file)) { } else if (FileUtility.IsValidPath(file)) {
bool isShortFileName = file == Path.GetFileNameWithoutExtension(file); bool isShortFileName = file == Path.GetFileNameWithoutExtension(file);
if (worker.CurrentProjectFile != null) { if (projectFile != null) {
file = Path.Combine(Path.GetDirectoryName(worker.CurrentProjectFile), file); file = Path.Combine(Path.GetDirectoryName(projectFile), file);
} }
if (isShortFileName && !File.Exists(file)) { if (isShortFileName && !File.Exists(file)) {
file = ""; file = "";
@ -413,10 +413,11 @@ namespace ICSharpCode.SharpDevelop.Project
file = ""; file = "";
} }
} }
FlushCurrentError();
BuildError error = new BuildError(file, lineNumber, columnNumber, code, message); BuildError error = new BuildError(file, lineNumber, columnNumber, code, message);
error.IsWarning = isWarning; error.IsWarning = isWarning;
worker.currentErrorOrWarning = error; error.Subcategory = subcategory;
error.HelpKeyword = helpKeyword;
engine.loggerChain.HandleError(error);
} }
#region ILogger interface implementation #region ILogger interface implementation
@ -425,10 +426,7 @@ namespace ICSharpCode.SharpDevelop.Project
public void Initialize(IEventSource eventSource) public void Initialize(IEventSource eventSource)
{ {
eventSource.ProjectStarted += OnProjectStarted;
eventSource.ProjectFinished += OnProjectFinished;
eventSource.TaskStarted += OnTaskStarted; eventSource.TaskStarted += OnTaskStarted;
eventSource.TaskFinished += OnTaskFinished;
eventSource.ErrorRaised += OnError; eventSource.ErrorRaised += OnError;
eventSource.WarningRaised += OnWarning; eventSource.WarningRaised += OnWarning;
@ -436,10 +434,9 @@ namespace ICSharpCode.SharpDevelop.Project
public void Shutdown() public void Shutdown()
{ {
FlushCurrentError(); if (engine.temporaryFileName != null) {
if (worker.temporaryFileName != null) { File.Delete(engine.temporaryFileName);
File.Delete(worker.temporaryFileName); engine.temporaryFileName = null;
worker.temporaryFileName = null;
} }
} }
#endregion #endregion

136
src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildLoggerFilter.cs

@ -0,0 +1,136 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.Core;
using Microsoft.Build.Framework;
namespace ICSharpCode.SharpDevelop.Project
{
/// <summary>
/// Interface for elements in /SharpDevelop/MSBuildEngine/LoggerFilters
/// </summary>
public interface IMSBuildLoggerFilter
{
IMSBuildChainedLoggerFilter CreateFilter(MSBuildEngine engine, IMSBuildChainedLoggerFilter nextFilter);
}
/// <summary>
/// Element in the logger filter chain.
/// Receives build events and errors and forwards them to the next element in the chain (possibly after modifying the event).
/// </summary>
public interface IMSBuildChainedLoggerFilter
{
void HandleError(BuildError error);
void HandleBuildEvent(Microsoft.Build.Framework.BuildEventArgs e);
}
/// <summary>
/// Creates <see cref="IMSBuildLoggerFilter"/> objects that are only
/// activated when a specific MSBuild task is running.
/// </summary>
/// <attribute name="class" use="required">
/// Name of the IMSBuildLoggerFilter class.
/// </attribute>
/// <attribute name="taskname" use="required">
/// Specifies the name of the MSBuild task that must be running for
/// this logger to be active.
/// </attribute>
/// <example>
/// &lt;TaskBoundLoggerFilter
/// id = "FxCopLogger"
/// taskname = "FxCop"
/// class = "ICSharpCode.CodeAnalysis.FxCopLoggerFilter"/&gt;
/// </example>
/// <usage>Only in /SharpDevelop/MSBuildEngine/LoggerFilters</usage>
/// <returns>
/// A IMSBuildLoggerFilter object that lazy-loads the specified
/// IMSBuildLoggerFilter when the specified task is running.
/// </returns>
public class TaskBoundLoggerFilterDoozer : IDoozer
{
public bool HandleConditions {
get {
return false;
}
}
public object BuildItem(object caller, Codon codon, System.Collections.ArrayList subItems)
{
return new TaskBoundLoggerFilterDescriptor(codon);
}
sealed class TaskBoundLoggerFilterDescriptor : IMSBuildLoggerFilter
{
internal string taskname;
internal string classname;
internal AddIn addIn;
public TaskBoundLoggerFilterDescriptor(Codon codon)
{
classname = codon.Properties["class"];
taskname = codon.Properties["taskname"];
addIn = codon.AddIn;
}
public IMSBuildChainedLoggerFilter CreateFilter(MSBuildEngine engine, IMSBuildChainedLoggerFilter nextFilter)
{
if (nextFilter == null)
throw new ArgumentNullException("nextFilter");
// Create a Filter that tracks whether the task is active.
// If active, forward to 'baseFilter', otherwise forward to 'nextFilter'.
return new TaskBoundLoggerFilter(this, engine, nextFilter);
}
}
sealed class TaskBoundLoggerFilter : IMSBuildChainedLoggerFilter
{
readonly TaskBoundLoggerFilterDescriptor desc;
readonly MSBuildEngine engine;
readonly IMSBuildChainedLoggerFilter nextFilter;
IMSBuildChainedLoggerFilter baseFilter = null;
bool insideTask = false;
public TaskBoundLoggerFilter(TaskBoundLoggerFilterDescriptor desc, MSBuildEngine engine, IMSBuildChainedLoggerFilter nextFilter)
{
this.desc = desc;
this.engine = engine;
this.nextFilter = nextFilter;
}
public void HandleError(BuildError error)
{
if (insideTask)
baseFilter.HandleError(error);
else
nextFilter.HandleError(error);
}
public void HandleBuildEvent(Microsoft.Build.Framework.BuildEventArgs e)
{
TaskStartedEventArgs start = e as TaskStartedEventArgs;
if (start != null && string.Equals(start.TaskName, desc.taskname, StringComparison.OrdinalIgnoreCase)) {
insideTask = true;
if (baseFilter == null) {
IMSBuildLoggerFilter baseLoggerFilter = (IMSBuildLoggerFilter)desc.addIn.CreateObject(desc.classname);
if (baseLoggerFilter != null)
baseFilter = baseLoggerFilter.CreateFilter(engine, nextFilter) ?? nextFilter;
else
baseFilter = nextFilter;
}
}
if (insideTask)
baseFilter.HandleBuildEvent(e);
else
nextFilter.HandleBuildEvent(e);
if (insideTask && e is TaskFinishedEventArgs) {
insideTask = false;
}
}
}
}
}
Loading…
Cancel
Save