diff options
author | Michael Hutchinson <mhutchinson@novell.com> | 2010-05-05 01:46:58 +0400 |
---|---|---|
committer | Michael Hutchinson <mhutchinson@novell.com> | 2010-05-05 01:46:58 +0400 |
commit | 134bcf6466d99029b5fb467832bddebe13c41434 (patch) | |
tree | 4bac5d251d15ef9414935155593e8896b51c0236 | |
parent | 9300721496940bf35f6c268d2c3bf5c5ef9b2383 (diff) |
* src/addins/TextTemplating/MonoDevelop.TextTemplating/TextTemplatingTool.cs:
Make cancellable, so bad user code can be aborted.
* src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomTool.cs:
* src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomToolService.cs:
* src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/CustomToolExtensionNode.cs:
Make custom tools cancellable, and add an output pad for viewing
their output and cancelling them.
svn path=/trunk/monodevelop/; revision=156688
6 files changed, 202 insertions, 57 deletions
diff --git a/main/src/addins/TextTemplating/MonoDevelop.TextTemplating/ChangeLog b/main/src/addins/TextTemplating/MonoDevelop.TextTemplating/ChangeLog index 51cbe65ff0..0c019e7a2d 100644 --- a/main/src/addins/TextTemplating/MonoDevelop.TextTemplating/ChangeLog +++ b/main/src/addins/TextTemplating/MonoDevelop.TextTemplating/ChangeLog @@ -1,3 +1,8 @@ +2010-05-04 Michael Hutchinson <mhutchinson@novell.com> + + * TextTemplatingTool.cs: Make cancellable, so bad user code + can be aborted. + 2010-04-21 Michael Hutchinson <mhutchinson@novell.com> * Gui/T4EditorExtension.cs: Free up wasted space between diff --git a/main/src/addins/TextTemplating/MonoDevelop.TextTemplating/TextTemplatingTool.cs b/main/src/addins/TextTemplating/MonoDevelop.TextTemplating/TextTemplatingTool.cs index b74b0d983d..cf51c78481 100644 --- a/main/src/addins/TextTemplating/MonoDevelop.TextTemplating/TextTemplatingTool.cs +++ b/main/src/addins/TextTemplating/MonoDevelop.TextTemplating/TextTemplatingTool.cs @@ -30,28 +30,90 @@ using System.CodeDom.Compiler; using MonoDevelop.Projects; using System.IO; using Mono.TextTemplating; +using MonoDevelop.Core; +using System.Threading; namespace MonoDevelop.TextTemplating { - public class TextTemplatingTool : CustomTool + public class TextTemplatingTool : ISingleFileCustomTool { - public override CompilerErrorCollection Generate (ProjectFile file, out string generatedFileName) + public IAsyncOperation Generate (IProgressMonitor monitor, ProjectFile file, SingleFileCustomToolResult result) { - using (var h = TextTemplatingService.GetTemplatingDomain ()) { - var host = (MonoDevelopTemplatingHost) h.Domain.CreateInstanceAndUnwrap ( - typeof (MonoDevelopTemplatingHost).Assembly.FullName, - typeof (MonoDevelopTemplatingHost).FullName); - var defaultOutputName = file.FilePath.ChangeExtension (".cs"); //cs extension for VS compat - host.ProcessTemplate (file.FilePath, defaultOutputName); - generatedFileName = host.OutputFile; - return host.Errors; + return new ThreadAsyncOperation (delegate { + using (var h = TextTemplatingService.GetTemplatingDomain ()) { + var host = (MonoDevelopTemplatingHost) h.Domain.CreateInstanceAndUnwrap ( + typeof (MonoDevelopTemplatingHost).Assembly.FullName, + typeof (MonoDevelopTemplatingHost).FullName); + var defaultOutputName = file.FilePath.ChangeExtension (".cs"); //cs extension for VS compat + host.ProcessTemplate (file.FilePath, defaultOutputName); + result.GeneratedFilePath = host.OutputFile; + result.Errors.AddRange (host.Errors); + foreach (var err in host.Errors) + monitor.Log.WriteLine (err.ToString ()); + } + }, result); + } + } + + class ThreadAsyncOperation : IAsyncOperation + { + Thread thread; + bool cancelled; + SingleFileCustomToolResult result; + Action task; + + public ThreadAsyncOperation (Action task, SingleFileCustomToolResult result) + { + if (result == null) + throw new ArgumentNullException ("result"); + + this.task = task; + this.result = result; + thread = new Thread (Run); + thread.Start (); + } + + void Run () + { + try { + task (); + } catch (ThreadAbortException ex) { + result.UnhandledException = ex; + Thread.ResetAbort (); + } catch (Exception ex) { + result.UnhandledException = ex; } + if (Completed != null) + Completed (this); + } + + public event OperationHandler Completed; + + public void Cancel () + { + thread.Abort (); + } + + public void WaitForCompleted () + { + thread.Join (); + } + + public bool IsCompleted { + get { return !thread.IsAlive; } + } + + public bool Success { + get { return !cancelled && result.Success; } + } + + public bool SuccessWithWarnings { + get { return !cancelled && result.SuccessWithWarnings; } } } class MonoDevelopTemplatingHost : TemplateGenerator { - } } diff --git a/main/src/core/MonoDevelop.Ide/ChangeLog b/main/src/core/MonoDevelop.Ide/ChangeLog index 31f6c34139..77f2284acc 100644 --- a/main/src/core/MonoDevelop.Ide/ChangeLog +++ b/main/src/core/MonoDevelop.Ide/ChangeLog @@ -1,3 +1,11 @@ +2010-05-04 Michael Hutchinson <mhutchinson@novell.com> + + * MonoDevelop.Ide.CustomTools/CustomTool.cs: + * MonoDevelop.Ide.CustomTools/CustomToolService.cs: + * MonoDevelop.Ide.Extensions/CustomToolExtensionNode.cs: Make + custom tools cancellable, and add an output pad for viewing + their output and cancelling them. + 2010-05-04 Lluis Sanchez Gual <lluis@novell.com> * templates/Workspace.xpt.xml: Set correct assembly name for diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomTool.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomTool.cs index 84ef240750..e57fdd39b2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomTool.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomTool.cs @@ -27,12 +27,45 @@ using System; using MonoDevelop.Projects; using System.CodeDom.Compiler; +using MonoDevelop.Core; namespace MonoDevelop.Ide.CustomTools { - public abstract class CustomTool + public interface ISingleFileCustomTool { - public abstract CompilerErrorCollection Generate (ProjectFile file, out string generatedFileName); + IAsyncOperation Generate (IProgressMonitor monitor, ProjectFile file, SingleFileCustomToolResult result); + } + + public class SingleFileCustomToolResult + { + CompilerErrorCollection errors = new CompilerErrorCollection (); + + /// <summary> + /// Errors and warnings from the generator. + /// </summary> + public CompilerErrorCollection Errors { get { return errors; } } + + /// <summary> + /// The absolute name of the generated file. Must be in same directory as source file. + /// </summary> + public FilePath GeneratedFilePath { get; set; } + + /// <summary> + /// Any unhandled exception from the generator. + /// </summary> + public Exception UnhandledException { get; set; } + + public bool Success { + get { + return UnhandledException == null && !Errors.HasErrors && !Errors.HasWarnings; + } + } + + public bool SuccessWithWarnings { + get { + return UnhandledException == null && !Errors.HasErrors; + } + } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomToolService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomToolService.cs index 83427f272c..4e29130b24 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomToolService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomToolService.cs @@ -34,6 +34,7 @@ using System.IO; using MonoDevelop.Ide.Tasks; using System.CodeDom.Compiler; using MonoDevelop.Ide.Gui; +using MonoDevelop.Core.ProgressMonitoring; namespace MonoDevelop.Ide.CustomTools { @@ -58,10 +59,10 @@ namespace MonoDevelop.Ide.CustomTools } }); IdeApp.Workspace.FileChangedInProject += delegate (object sender, ProjectFileEventArgs e) { - Update (e.ProjectFile); + Update (e.ProjectFile, false); }; IdeApp.Workspace.FilePropertyChangedInProject += delegate (object sender, ProjectFileEventArgs e) { - Update (e.ProjectFile); + Update (e.ProjectFile, false); }; //FIXME: handle the rename //MonoDevelop.Ide.Gui.IdeApp.Workspace.FileRenamedInProject @@ -72,7 +73,7 @@ namespace MonoDevelop.Ide.CustomTools //forces static ctor to run } - static CustomTool GetGenerator (ProjectFile file) + static ISingleFileCustomTool GetGenerator (ProjectFile file) { CustomToolExtensionNode node; if (!string.IsNullOrEmpty (file.Generator) && nodes.TryGetValue (file.Generator, out node)) @@ -80,66 +81,102 @@ namespace MonoDevelop.Ide.CustomTools return null; } - public static void Update (ProjectFile file) + public static void Update (ProjectFile file, bool force) { var tool = GetGenerator (file); if (tool == null) return; + ProjectFile genFile = null; if (!string.IsNullOrEmpty (file.LastGenOutput)) genFile = file.Project.Files.GetFile (file.FilePath.ParentDirectory.Combine (file.LastGenOutput)); - if (genFile != null && File.Exists (genFile.FilePath) && + if (!force && genFile != null && File.Exists (genFile.FilePath) && File.GetLastWriteTime (file.FilePath) < File.GetLastWriteTime (genFile.FilePath)) { return; } - CompilerErrorCollection errors = null; - bool broken = false; - FilePath genFilePath = FilePath.Null; - string genFileName = null; + string title = GettextCatalog.GetString ("Custom tool"); + var monitor = IdeApp.Workbench.ProgressMonitors.GetOutputProgressMonitor (title, null, false, true); + var result = new SingleFileCustomToolResult (); + var aggOp = new AggregatedOperationMonitor (monitor); try { - errors = tool.Generate (file, out genFileName); - genFilePath = genFileName; - if (genFilePath.IsNullOrEmpty) { - genFileName = null; - } else { - genFileName = genFilePath.ToRelative (file.FilePath.ParentDirectory).ToString (); - } + monitor.BeginTask (GettextCatalog.GetString ("Updating file {0}...", file.Name), 1); + var op = tool.Generate (monitor, file, result); + aggOp.AddOperation (op); + op.Completed += delegate { + UpdateCompleted (monitor, aggOp, file, genFile, result); + }; } catch (Exception ex) { - broken = true; - (errors ?? (errors = new CompilerErrorCollection ())) .Add ( - new CompilerError (file.Name, 0, 0, "", - string.Format ("The '{0}' code generator crashed: {1}", file.Generator, ex.Message))); - LoggingService.LogError (string.Format ("The '{0}' code generator crashed: {1}", file.Generator), ex); + result.UnhandledException = ex; + UpdateCompleted (monitor, aggOp, file, genFile, result); } - if (string.IsNullOrEmpty (genFileName) || - genFileName.IndexOfAny (new char[] { '/', '\\' }) >= 0 || - !FileService.IsValidFileName (genFileName)) - { - broken = true; - (errors ?? (errors = new CompilerErrorCollection ())).Add ( - new CompilerError (file.Name, 0, 0, "", - string.Format ("The '{0}' code generator output invalid filename '{1}'", file.Generator, genFileName))); + } + + static void UpdateCompleted (IProgressMonitor monitor, AggregatedOperationMonitor aggOp, + ProjectFile file, ProjectFile genFile, SingleFileCustomToolResult result) + { + monitor.EndTask (); + aggOp.Dispose (); + + if (monitor.IsCancelRequested) { + monitor.ReportError ("Cancelled", null); + monitor.Dispose (); + return; } - TaskService.Errors.ClearByOwner (file); - if (errors.Count > 0) { - foreach (CompilerError err in errors) - TaskService.Errors.Add (new Task (file.FilePath, err.ErrorText, err.Column, err.Line, - err.IsWarning? TaskSeverity.Warning : TaskSeverity.Error, - TaskPriority.Normal, file.Project.ParentSolution, file)); + string genFileName; + try { + + bool broken = false; + + if (result.UnhandledException != null) { + broken = true; + result.Errors.Add (new CompilerError (file.Name, 0, 0, "", + string.Format ("The '{0}' code generator crashed: {1}", file.Generator, result.UnhandledException.Message))); + monitor.ReportError (string.Format ("The '{0}' code generator crashed", file.Generator), result.UnhandledException); + } + + genFileName = result.GeneratedFilePath.IsNullOrEmpty? + null : result.GeneratedFilePath.ToRelative (file.FilePath.ParentDirectory); + + bool validName = !string.IsNullOrEmpty (genFileName) + && genFileName.IndexOfAny (new char[] { '/', '\\' }) < 0 + && FileService.IsValidFileName (genFileName); + + if (!validName) { + broken = true; + result.Errors.Add (new CompilerError (file.Name, 0, 0, "", + string.Format ("The '{0}' code generator output invalid filename '{1}'", file.Generator, result.GeneratedFilePath))); + } + + TaskService.Errors.ClearByOwner (file); + if (result.Errors.Count > 0) { + foreach (CompilerError err in result.Errors) + TaskService.Errors.Add (new Task (file.FilePath, err.ErrorText, err.Column, err.Line, + err.IsWarning? TaskSeverity.Warning : TaskSeverity.Error, + TaskPriority.Normal, file.Project.ParentSolution, file)); + } + + if (broken) + return; + + if (result.Success) + monitor.ReportSuccess ("Generated file successfully."); + else + monitor.ReportError ("Failed to generate file. See error pad for details.", null); + + } finally { + monitor.Dispose (); } - if (broken) - return; - if (!genFilePath.IsNullOrEmpty && File.Exists (genFilePath)) { + if (!result.GeneratedFilePath.IsNullOrEmpty && File.Exists (result.GeneratedFilePath)) { if (genFile == null) { - genFile = file.Project.AddFile (genFilePath); - } else if (genFilePath != genFile.FilePath) { - genFile.Name = genFilePath; + genFile = file.Project.AddFile (result.GeneratedFilePath); + } else if (result.GeneratedFilePath != genFile.FilePath) { + genFile.Name = result.GeneratedFilePath; } - file.LastGenOutput = genFileName; + file.LastGenOutput = genFileName; genFile.DependsOn = file.FilePath.FileName; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/CustomToolExtensionNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/CustomToolExtensionNode.cs index b676c4fed3..db1d48958f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/CustomToolExtensionNode.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/CustomToolExtensionNode.cs @@ -39,8 +39,8 @@ namespace MonoDevelop.Ide.Extensions get { return name; } } - public CustomTool Tool { - get { return (CustomTool)base.GetInstance (); } + public ISingleFileCustomTool Tool { + get { return (ISingleFileCustomTool)base.GetInstance (); } } } } |