From 0b8d32fb619f2dafc4172940a01f558c1a334bd3 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald <daniel@danielgrunwald.de> Date: Fri, 26 Mar 2010 20:44:12 +0000 Subject: [PATCH] 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 --- AddIns/ICSharpCode.SharpDevelop.addin | 1 + clean.bat | 4 +- .../Project/Src/VbcEncodingFixingLogger.cs | 70 ++------ .../VBNetBinding/Project/VBNetBinding.addin | 4 +- .../Misc/CodeAnalysis/CodeAnalysis.addin | 4 +- .../Misc/CodeAnalysis/Src/FxCopLogger.cs | 106 +++++------- .../Project/ICSharpCode.SharpDevelop.csproj | 3 +- .../Base/Project/Src/Project/BuildEngine.cs | 4 +- .../Base/Project/Src/Project/BuildError.cs | 12 ++ .../MSBuildEngine/BuildWorkerManager.cs | 32 ++-- .../MSBuildAdditionalLogger.cs | 0 .../Project/MSBuildEngine/MSBuildEngine.cs | 153 +++++++++--------- .../MSBuildEngine/MSBuildLoggerFilter.cs | 136 ++++++++++++++++ .../NumericUpDown.cs | 2 +- 14 files changed, 301 insertions(+), 230 deletions(-) rename src/Main/Base/Project/Src/Project/{ => MSBuildEngine}/MSBuildAdditionalLogger.cs (100%) create mode 100644 src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildLoggerFilter.cs diff --git a/AddIns/ICSharpCode.SharpDevelop.addin b/AddIns/ICSharpCode.SharpDevelop.addin index 7d23279705..e6cc4e923b 100644 --- a/AddIns/ICSharpCode.SharpDevelop.addin +++ b/AddIns/ICSharpCode.SharpDevelop.addin @@ -46,6 +46,7 @@ <Doozer name="Debugger" class="ICSharpCode.SharpDevelop.Debugging.DebuggerDoozer"/> <Doozer name="Directory" class="ICSharpCode.SharpDevelop.DirectoryDoozer"/> <Doozer name="TaskBoundAdditionalLogger" class="ICSharpCode.SharpDevelop.Project.TaskBoundAdditionalLoggerDoozer"/> + <Doozer name="TaskBoundLoggerFilter" class="ICSharpCode.SharpDevelop.Project.TaskBoundLoggerFilterDoozer"/> </Import> </Runtime> diff --git a/clean.bat b/clean.bat index 898504355c..a40479eaf5 100755 --- a/clean.bat +++ b/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 \ No newline at end of file diff --git a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs index fe1e503e80..76a549c721 100644 --- a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs +++ b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs @@ -16,77 +16,39 @@ namespace VBNetBinding /// <summary> /// Fixes SD2-995 : Special characters not correctly encoded for languages others than English /// </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.nextFilter = nextFilter; } - public LoggerVerbosity Verbosity { - 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) + static string FixEncoding(string text) { - 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); - engineWorker.CurrentErrorOrWarning.FileName = FixEncoding(engineWorker.CurrentErrorOrWarning.FileName); + error.ErrorText = FixEncoding(error.ErrorText); + 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); } } } diff --git a/src/AddIns/BackendBindings/VBNetBinding/Project/VBNetBinding.addin b/src/AddIns/BackendBindings/VBNetBinding/Project/VBNetBinding.addin index 343ef77d0f..8f5701300e 100644 --- a/src/AddIns/BackendBindings/VBNetBinding/Project/VBNetBinding.addin +++ b/src/AddIns/BackendBindings/VBNetBinding/Project/VBNetBinding.addin @@ -39,8 +39,8 @@ <String id="vbc" text = "vbc"/> </Path> - <Path name = "/SharpDevelop/MSBuildEngine/AdditionalLoggers"> - <TaskBoundAdditionalLogger + <Path name = "/SharpDevelop/MSBuildEngine/LoggerFilters"> + <TaskBoundLoggerFilter id = "VbcEncodingFixingLogger" taskname = "vbc" class = "VBNetBinding.VbcEncodingFixingLogger"/> diff --git a/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.addin b/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.addin index 633ddcd62e..043ef5de48 100644 --- a/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.addin +++ b/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.addin @@ -42,8 +42,8 @@ </ComplexCondition> </Path> - <Path name = "/SharpDevelop/MSBuildEngine/AdditionalLoggers"> - <TaskBoundAdditionalLogger + <Path name = "/SharpDevelop/MSBuildEngine/LoggerFilters"> + <TaskBoundLoggerFilter id = "FxCopLogger" taskname = "FxCop" class = "ICSharpCode.CodeAnalysis.FxCopLogger"/> diff --git a/src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs b/src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs index 9c5a960e6a..f9d49ce4f6 100644 --- a/src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs +++ b/src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs @@ -20,114 +20,80 @@ namespace ICSharpCode.CodeAnalysis /// so this logger fixes the position. /// Additionally, it registers the context menu containing the 'suppress message' command. /// </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.nextChainElement = nextChainElement; } - public LoggerVerbosity Verbosity { get; set; } - 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) + public void HandleError(BuildError error) { - AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Message, false, - e.HelpKeyword, e.Code, e.Subcategory); - } - - void OnWarning(object sender, BuildWarningEventArgs e) - { - AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Message, true, - e.HelpKeyword, e.Code, e.Subcategory); - } - - 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); + LoggingService.Debug("FxCopLogger got " + error.ToString()); - 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") + 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") { - err.FileName = null; + error.FileName = null; } - IProject project = ProjectService.GetProject(engineWorker.CurrentProjectFile); + IProject project = ProjectService.GetProject(engineWorker.ProjectFileName); if (project != null) { IProjectContent pc = ParserService.GetProjectContent(project); if (pc != null) { - if (file.StartsWith("positionof#")) { - string memberName = file.Substring(11); - file = ""; + if (error.FileName != null && error.FileName.StartsWith("positionof#")) { + string memberName = error.FileName.Substring(11); FilePosition pos = GetPosition(pc, memberName); if (pos.IsEmpty == false && pos.CompilationUnit != null) { - err.FileName = pos.FileName ?? ""; - err.Line = pos.Line; - err.Column = pos.Column; + error.FileName = pos.FileName ?? ""; + error.Line = pos.Line; + error.Column = pos.Column; } else { - err.FileName = null; + error.FileName = null; } } if (moreData.Length > 1 && !string.IsNullOrEmpty(moreData[0])) { - err.Tag = new FxCopTaskTag { + error.Tag = new FxCopTaskTag { ProjectContent = pc, TypeName = moreData[0], MemberName = moreData[1], - Category = category, + Category = error.HelpKeyword, CheckID = checkId }; } else { - err.Tag = new FxCopTaskTag { + error.Tag = new FxCopTaskTag { ProjectContent = pc, - Category = category, + Category = error.HelpKeyword, CheckID = checkId }; } - err.ContextMenuAddInTreeEntry = "/SharpDevelop/Pads/ErrorList/CodeAnalysisTaskContextMenu"; + error.ContextMenuAddInTreeEntry = "/SharpDevelop/Pads/ErrorList/CodeAnalysisTaskContextMenu"; 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) diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 7859a48b49..c97c7afcdc 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -260,6 +260,8 @@ <Compile Include="Src\Project\Converter\UpgradeViewContent.cs" /> <Compile Include="Src\Project\IBuildFeedbackSink.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\BuildWorkerManager.cs" /> <Compile Include="Src\Project\MSBuildEngine\MSBuildEngine.cs" /> @@ -711,7 +713,6 @@ <Compile Include="Src\TextEditor\Gui\Editor\TextNavigationPoint.cs" /> <Compile Include="Src\Project\BuildResults.cs" /> <Compile Include="Src\Project\BuildError.cs" /> - <Compile Include="Src\Project\MSBuildAdditionalLogger.cs" /> <Compile Include="Src\Services\HelpProvider.cs" /> <Compile Include="Src\Services\ParserService\CodeCompletionOptions.cs" /> <Compile Include="Src\Services\RefactoringService\TextEditorDocument.cs" /> diff --git a/src/Main/Base/Project/Src/Project/BuildEngine.cs b/src/Main/Base/Project/Src/Project/BuildEngine.cs index e356387759..9108095798 100644 --- a/src/Main/Base/Project/Src/Project/BuildEngine.cs +++ b/src/Main/Base/Project/Src/Project/BuildEngine.cs @@ -45,7 +45,9 @@ namespace ICSharpCode.SharpDevelop.Project if (guiBuildCancellation != null) { BuildResults results = new BuildResults(); 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; if (options.Callback != null) { options.Callback(results); diff --git a/src/Main/Base/Project/Src/Project/BuildError.cs b/src/Main/Base/Project/Src/Project/BuildError.cs index 27d231b666..a80e3b6fe8 100644 --- a/src/Main/Base/Project/Src/Project/BuildError.cs +++ b/src/Main/Base/Project/Src/Project/BuildError.cs @@ -50,6 +50,18 @@ namespace ICSharpCode.SharpDevelop.Project [NonSerialized] object tag; 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 { get { diff --git a/src/Main/Base/Project/Src/Project/MSBuildEngine/BuildWorkerManager.cs b/src/Main/Base/Project/Src/Project/MSBuildEngine/BuildWorkerManager.cs index e309db5228..ab51025e8e 100644 --- a/src/Main/Base/Project/Src/Project/MSBuildEngine/BuildWorkerManager.cs +++ b/src/Main/Base/Project/Src/Project/MSBuildEngine/BuildWorkerManager.cs @@ -31,10 +31,10 @@ namespace ICSharpCode.SharpDevelop.Project 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(); - worker.RunJob(job, loggers, reportWhenDone); + worker.RunJob(job, loggerChain, reportWhenDone, cancellationToken); } BuildWorker GetFreeWorker() @@ -70,26 +70,21 @@ namespace ICSharpCode.SharpDevelop.Project process.Start(startInfo); } - EventSource source; - IEnumerable<ILogger> loggers; - IBuildFeedbackSink reportWhenDone; + IMSBuildChainedLoggerFilter loggerChain; + Action<bool> reportWhenDone; 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.loggers = loggers; + this.loggerChain = loggerChain; this.reportWhenDone = reportWhenDone; - foreach (var logger in loggers) { - logger.Initialize(source); - } try { process.Writer.Write("StartBuild"); job.WriteTo(process.Writer); - this.cancellationRegistration = reportWhenDone.ProgressMonitor.CancellationToken.Register(OnCancel); + this.cancellationRegistration = cancellationToken.Register(OnCancel); } catch (IOException ex) { // "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); } } @@ -105,7 +100,7 @@ namespace ICSharpCode.SharpDevelop.Project LoggingService.Debug("Received command " + command); switch (command) { case "ReportEvent": - source.ForwardEvent(EventSource.DecodeEvent(reader)); + loggerChain.HandleBuildEvent(EventSource.DecodeEvent(reader)); break; case "BuildDone": bool success = reader.ReadBoolean(); @@ -127,10 +122,7 @@ namespace ICSharpCode.SharpDevelop.Project if (reportWhenDone == null) return; cancellationRegistration.Dispose(); - foreach (var logger in loggers) { - logger.Shutdown(); - } - reportWhenDone.Done(success); + reportWhenDone(success); reportWhenDone = null; } } @@ -163,11 +155,11 @@ namespace ICSharpCode.SharpDevelop.Project parentManager.freeWorkers.Remove(this); MarkAsInUse(); } else { - reportBuildError = true; + reportBuildError = (reportWhenDone != null); // only if not done } } 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); } process.Dispose(); diff --git a/src/Main/Base/Project/Src/Project/MSBuildAdditionalLogger.cs b/src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildAdditionalLogger.cs similarity index 100% rename from src/Main/Base/Project/Src/Project/MSBuildAdditionalLogger.cs rename to src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildAdditionalLogger.cs diff --git a/src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildEngine.cs b/src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildEngine.cs index 4b17ad49d1..41a052c885 100755 --- a/src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildEngine.cs +++ b/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 AdditionalTargetFilesPath = "/SharpDevelop/MSBuildEngine/AdditionalTargetFiles"; const string AdditionalLoggersPath = "/SharpDevelop/MSBuildEngine/AdditionalLoggers"; + const string LoggerFiltersPath = "/SharpDevelop/MSBuildEngine/LoggerFilters"; internal const string AdditionalPropertiesPath = "/SharpDevelop/MSBuildEngine/AdditionalProperties"; /// <summary> @@ -75,6 +76,13 @@ namespace ICSharpCode.SharpDevelop.Project /// </summary> 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 { get { return Path.GetDirectoryName(typeof(MSBuildEngine).Assembly.Location); @@ -89,6 +97,7 @@ namespace ICSharpCode.SharpDevelop.Project ); AdditionalTargetFiles = AddInTree.BuildItems<string>(AdditionalTargetFilesPath, 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) @@ -110,7 +119,8 @@ namespace ICSharpCode.SharpDevelop.Project engine.StartBuild(); } - IProject project; + readonly string projectFileName; + readonly int projectMinimumSolutionVersion; ProjectBuildOptions options; IBuildFeedbackSink feedbackSink; IEnumerable<string> additionalTargetFiles; @@ -118,7 +128,8 @@ namespace ICSharpCode.SharpDevelop.Project private MSBuildEngine(IProject project, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink) { - this.project = project; + this.projectFileName = project.FileName; + this.projectMinimumSolutionVersion = project.MinimumSolutionVersion; this.options = options; this.feedbackSink = feedbackSink; } @@ -163,6 +174,13 @@ namespace ICSharpCode.SharpDevelop.Project /// </summary> 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>(); string temporaryFileName; @@ -175,6 +193,10 @@ namespace ICSharpCode.SharpDevelop.Project get { return interestingTasks; } } + readonly EventSource eventSource = new EventSource(); + List<ILogger> loggers = new List<ILogger>(); + IMSBuildChainedLoggerFilter loggerChain; + void StartBuild() { Dictionary<string, string> globalProperties = new Dictionary<string, string>(); @@ -192,7 +214,6 @@ namespace ICSharpCode.SharpDevelop.Project InterestingTasks.AddRange(MSBuildEngine.CompileTaskNames); - List<ILogger> loggers = new List<ILogger>(); loggers.Add(new SharpDevelopLogger(this)); if (options.BuildOutputVerbosity == BuildOutputVerbosity.Diagnostic) { this.ReportMessageEvents = true; @@ -207,10 +228,16 @@ namespace ICSharpCode.SharpDevelop.Project foreach (IMSBuildAdditionalLogger loggerProvider in MSBuildEngine.AdditionalMSBuildLoggers) { loggers.Add(loggerProvider.CreateLogger(this)); } + + loggerChain = new EndOfChain(this); + foreach (IMSBuildLoggerFilter loggerFilter in MSBuildEngine.MSBuildLoggerFilters) { + loggerChain = loggerFilter.CreateFilter(this, loggerChain) ?? loggerChain; + } + WriteAdditionalTargetsToTempFile(globalProperties); BuildJob job = new BuildJob(); - job.ProjectFileName = project.FileName; + job.ProjectFileName = projectFileName; job.Target = options.Target.TargetName; // First remove the flags for the controllable events. @@ -237,13 +264,25 @@ namespace ICSharpCode.SharpDevelop.Project job.Properties.Add(pair.Key, pair.Value); } - if (project.MinimumSolutionVersion <= Solution.SolutionVersionVS2008) { - BuildWorkerManager.MSBuild35.RunBuildJob(job, loggers, feedbackSink); + foreach (ILogger logger in loggers) { + logger.Initialize(eventSource); + } + + if (projectMinimumSolutionVersion <= Solution.SolutionVersionVS2008) { + BuildWorkerManager.MSBuild35.RunBuildJob(job, loggerChain, OnDone, feedbackSink.ProgressMonitor.CancellationToken); } 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) { // 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, // but on MSBuild 2.0 we need to fall back to 'CodeAnalysisTargets'. string hijackedProperty = "MsTestToolsTargets"; - if (project.MinimumSolutionVersion == Solution.SolutionVersionVS2005) + if (projectMinimumSolutionVersion == Solution.SolutionVersionVS2005) hijackedProperty = "CodeAnalysisTargets"; // because we'll replace the hijackedProperty, manually write the corresponding include @@ -295,33 +334,7 @@ namespace ICSharpCode.SharpDevelop.Project #endif } - BuildError currentErrorOrWarning; - - /// <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) + public void OutputTextLine(string message) { feedbackSink.ReportMessage(message); } @@ -331,40 +344,33 @@ namespace ICSharpCode.SharpDevelop.Project feedbackSink.ReportError(error); } - class SharpDevelopLogger : ILogger + sealed class EndOfChain : IMSBuildChainedLoggerFilter { - MSBuildEngine worker; - - public SharpDevelopLogger(MSBuildEngine engine) - { - this.worker = engine; - } + readonly MSBuildEngine engine; - void AppendLine(string text) + public EndOfChain(MSBuildEngine engine) { - worker.OutputText(text); + this.engine = engine; } - internal void FlushCurrentError() + public void HandleError(BuildError error) { - if (worker.currentErrorOrWarning != null) { - worker.ReportError(worker.currentErrorOrWarning); - worker.currentErrorOrWarning = null; - } + engine.ReportError(error); } - void OnProjectStarted(object sender, ProjectStartedEventArgs e) + public void HandleBuildEvent(Microsoft.Build.Framework.BuildEventArgs e) { - worker.projectFiles.Push(e.ProjectFile); + engine.eventSource.ForwardEvent(e); } + } + + sealed class SharpDevelopLogger : ILogger + { + MSBuildEngine engine; - void OnProjectFinished(object sender, ProjectFinishedEventArgs e) + public SharpDevelopLogger(MSBuildEngine engine) { - FlushCurrentError(); - // 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(); + this.engine = engine; } string activeTaskName; @@ -373,34 +379,28 @@ namespace ICSharpCode.SharpDevelop.Project { activeTaskName = e.TaskName; 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) { - 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) { - 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, bool isWarning) + void AppendError(string file, int lineNumber, int columnNumber, string code, string message, string projectFile, string subcategory, string helpKeyword, bool isWarning) { if (string.Equals(file, activeTaskName, StringComparison.OrdinalIgnoreCase)) { file = ""; } else if (FileUtility.IsValidPath(file)) { bool isShortFileName = file == Path.GetFileNameWithoutExtension(file); - if (worker.CurrentProjectFile != null) { - file = Path.Combine(Path.GetDirectoryName(worker.CurrentProjectFile), file); + if (projectFile != null) { + file = Path.Combine(Path.GetDirectoryName(projectFile), file); } if (isShortFileName && !File.Exists(file)) { file = ""; @@ -413,10 +413,11 @@ namespace ICSharpCode.SharpDevelop.Project file = ""; } } - FlushCurrentError(); BuildError error = new BuildError(file, lineNumber, columnNumber, code, message); error.IsWarning = isWarning; - worker.currentErrorOrWarning = error; + error.Subcategory = subcategory; + error.HelpKeyword = helpKeyword; + engine.loggerChain.HandleError(error); } #region ILogger interface implementation @@ -425,10 +426,7 @@ namespace ICSharpCode.SharpDevelop.Project public void Initialize(IEventSource eventSource) { - eventSource.ProjectStarted += OnProjectStarted; - eventSource.ProjectFinished += OnProjectFinished; eventSource.TaskStarted += OnTaskStarted; - eventSource.TaskFinished += OnTaskFinished; eventSource.ErrorRaised += OnError; eventSource.WarningRaised += OnWarning; @@ -436,10 +434,9 @@ namespace ICSharpCode.SharpDevelop.Project public void Shutdown() { - FlushCurrentError(); - if (worker.temporaryFileName != null) { - File.Delete(worker.temporaryFileName); - worker.temporaryFileName = null; + if (engine.temporaryFileName != null) { + File.Delete(engine.temporaryFileName); + engine.temporaryFileName = null; } } #endregion diff --git a/src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildLoggerFilter.cs b/src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildLoggerFilter.cs new file mode 100644 index 0000000000..63487e9701 --- /dev/null +++ b/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> + /// <TaskBoundLoggerFilter + /// id = "FxCopLogger" + /// taskname = "FxCop" + /// class = "ICSharpCode.CodeAnalysis.FxCopLoggerFilter"/> + /// </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; + } + } + } + } +} diff --git a/src/Main/ICSharpCode.Core.Presentation/NumericUpDown.cs b/src/Main/ICSharpCode.Core.Presentation/NumericUpDown.cs index bb230cd528..906fe46265 100644 --- a/src/Main/ICSharpCode.Core.Presentation/NumericUpDown.cs +++ b/src/Main/ICSharpCode.Core.Presentation/NumericUpDown.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.Core.Presentation DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown))); } - + TextBox textBox; DragRepeatButton upButton; DragRepeatButton downButton;