~ubuntu-branches/ubuntu/saucy/monodevelop/saucy-proposed

« back to all changes in this revision

Viewing changes to src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomToolService.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2010-09-10 16:54:48 UTC
  • mfrom: (19.1.1 experimental)
  • Revision ID: james.westby@ubuntu.com-20100910165448-0rybfk25zd4o9431
Tags: 2.4+dfsg-2
* debian/patches/inject_Mono.Debugger.Soft_source.patch,
  debian/patches/use_system_Mono.Debugger.Soft.patch,
  debian/control:
  + Build against system Soft Debugger, since we now have a new
    enough Mono to match MonoDevelop's required API

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// 
 
2
// CustomToolService.cs
 
3
//  
 
4
// Author:
 
5
//       Michael Hutchinson <mhutchinson@novell.com>
 
6
// 
 
7
// Copyright (c) 2010 Novell, Inc.
 
8
// 
 
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
10
// of this software and associated documentation files (the "Software"), to deal
 
11
// in the Software without restriction, including without limitation the rights
 
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
13
// copies of the Software, and to permit persons to whom the Software is
 
14
// furnished to do so, subject to the following conditions:
 
15
// 
 
16
// The above copyright notice and this permission notice shall be included in
 
17
// all copies or substantial portions of the Software.
 
18
// 
 
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
25
// THE SOFTWARE.
 
26
 
 
27
using System;
 
28
using MonoDevelop.Ide.Extensions;
 
29
using System.Collections.Generic;
 
30
using MonoDevelop.Core;
 
31
using Mono.Addins;
 
32
using MonoDevelop.Projects;
 
33
using System.IO;
 
34
using MonoDevelop.Ide.Tasks;
 
35
using System.CodeDom.Compiler;
 
36
using MonoDevelop.Ide.Gui;
 
37
using MonoDevelop.Core.ProgressMonitoring;
 
38
 
 
39
namespace MonoDevelop.Ide.CustomTools
 
40
{
 
41
        public static class CustomToolService
 
42
        {
 
43
                static Dictionary<string,CustomToolExtensionNode> nodes = new Dictionary<string,CustomToolExtensionNode> ();
 
44
                
 
45
                static Dictionary<string,IAsyncOperation> runningTasks = new Dictionary<string, IAsyncOperation> ();
 
46
                
 
47
                static CustomToolService ()
 
48
                {
 
49
                        AddinManager.AddExtensionNodeHandler ("/MonoDevelop/Ide/CustomTools", delegate(object sender, ExtensionNodeEventArgs args) {
 
50
                                var node = (CustomToolExtensionNode)args.ExtensionNode;
 
51
                                switch (args.Change) {
 
52
                                case ExtensionChange.Add:
 
53
                                        if (nodes.ContainsKey (node.Name))
 
54
                                                LoggingService.LogError ("Duplicate custom tool name '{0}'", node.Name);
 
55
                                        else
 
56
                                                nodes.Add (node.Name, node);
 
57
                                        break;
 
58
                                case ExtensionChange.Remove:
 
59
                                        nodes.Remove (node.Name);
 
60
                                        break;
 
61
                                }
 
62
                        });
 
63
                        IdeApp.Workspace.FileChangedInProject += delegate (object sender, ProjectFileEventArgs e) {
 
64
                                Update (e.ProjectFile, false);
 
65
                        };
 
66
                        IdeApp.Workspace.FilePropertyChangedInProject += delegate (object sender, ProjectFileEventArgs e) {
 
67
                                Update (e.ProjectFile, false);
 
68
                        };
 
69
                        //FIXME: handle the rename
 
70
                        //MonoDevelop.Ide.Gui.IdeApp.Workspace.FileRenamedInProject
 
71
                }
 
72
                
 
73
                internal static void Init ()
 
74
                {
 
75
                        //forces static ctor to run
 
76
                }
 
77
                
 
78
                static ISingleFileCustomTool GetGenerator (ProjectFile file)
 
79
                {
 
80
                        CustomToolExtensionNode node;
 
81
                        if (!string.IsNullOrEmpty (file.Generator) && nodes.TryGetValue (file.Generator, out node))
 
82
                                return node.Tool;
 
83
                        return null;
 
84
                }
 
85
                
 
86
                public static void Update (ProjectFile file, bool force)
 
87
                {
 
88
                        var tool = GetGenerator (file);
 
89
                        if (tool == null)
 
90
                                return;
 
91
                        
 
92
                        ProjectFile genFile = null;
 
93
                        if (!string.IsNullOrEmpty (file.LastGenOutput))
 
94
                                genFile = file.Project.Files.GetFile (file.FilePath.ParentDirectory.Combine (file.LastGenOutput));
 
95
                        
 
96
                        if (!force && genFile != null && File.Exists (genFile.FilePath) && 
 
97
                            File.GetLastWriteTime (file.FilePath) < File.GetLastWriteTime (genFile.FilePath)) {
 
98
                                return;
 
99
                        }
 
100
                        
 
101
                        TaskService.Errors.ClearByOwner (file);
 
102
                        
 
103
                        //if this file is already being run, cancel it
 
104
                        lock (runningTasks) {
 
105
                                IAsyncOperation runningTask;
 
106
                                if (runningTasks.TryGetValue (file.FilePath, out runningTask)) {
 
107
                                        runningTask.Cancel ();
 
108
                                        runningTasks.Remove (file.FilePath);
 
109
                                }
 
110
                        }
 
111
                        
 
112
                        string title = GettextCatalog.GetString ("Custom Tool");
 
113
                        var monitor = IdeApp.Workbench.ProgressMonitors.GetOutputProgressMonitor (title, null, false, true);
 
114
                        var result = new SingleFileCustomToolResult ();
 
115
                        var aggOp = new AggregatedOperationMonitor (monitor);
 
116
                        try {
 
117
                                monitor.BeginTask (GettextCatalog.GetString ("Running generator '{0}' on file '{1}'...", file.Generator, file.Name), 1);
 
118
                                var op = tool.Generate (monitor, file, result);
 
119
                                runningTasks.Add (file.FilePath, op);
 
120
                                aggOp.AddOperation (op);
 
121
                                op.Completed += delegate {
 
122
                                        lock (runningTasks) {
 
123
                                                IAsyncOperation runningTask;
 
124
                                                if (runningTasks.TryGetValue (file.FilePath, out runningTask) && runningTask == op) {
 
125
                                                        runningTasks.Remove (file.FilePath);
 
126
                                                        UpdateCompleted (monitor, aggOp, file, genFile, result);
 
127
                                                } else {
 
128
                                                        //it was cancelled because another was run for the same file, so just clean up
 
129
                                                        aggOp.Dispose ();
 
130
                                                        monitor.EndTask ();
 
131
                                                        monitor.ReportWarning (GettextCatalog.GetString ("Cancelled because generator ran again for the same file"));
 
132
                                                        monitor.Dispose ();
 
133
                                                }
 
134
                                        }
 
135
                                };
 
136
                        } catch (Exception ex) {
 
137
                                result.UnhandledException = ex;
 
138
                                UpdateCompleted (monitor, aggOp, file, genFile, result);
 
139
                        }
 
140
                }
 
141
                
 
142
                static void UpdateCompleted (IProgressMonitor monitor, AggregatedOperationMonitor aggOp,
 
143
                                             ProjectFile file, ProjectFile genFile, SingleFileCustomToolResult result)
 
144
                {
 
145
                        monitor.EndTask ();
 
146
                        aggOp.Dispose ();
 
147
                        
 
148
                        if (monitor.IsCancelRequested) {
 
149
                                monitor.ReportError (GettextCatalog.GetString ("Cancelled"), null);
 
150
                                monitor.Dispose ();
 
151
                                return;
 
152
                        }
 
153
                        
 
154
                        string genFileName;
 
155
                        try {
 
156
                                
 
157
                                bool broken = false;
 
158
                                
 
159
                                if (result.UnhandledException != null) {
 
160
                                        broken = true;
 
161
                                        string msg = GettextCatalog.GetString ("The '{0}' code generator crashed", file.Generator);
 
162
                                        result.Errors.Add (new CompilerError (file.Name, 0, 0, "", msg + ": " + result.UnhandledException.Message));
 
163
                                        monitor.ReportError (msg, result.UnhandledException);
 
164
                                }
 
165
                                
 
166
                                genFileName = result.GeneratedFilePath.IsNullOrEmpty?
 
167
                                        null : result.GeneratedFilePath.ToRelative (file.FilePath.ParentDirectory);
 
168
                                
 
169
                                bool validName = !string.IsNullOrEmpty (genFileName)
 
170
                                        && genFileName.IndexOfAny (new char[] { '/', '\\' }) < 0
 
171
                                        && FileService.IsValidFileName (genFileName);
 
172
                                
 
173
                                if (!validName) {
 
174
                                        broken = true;
 
175
                                        string msg = GettextCatalog.GetString ("The '{0}' code generator output invalid filename '{1}'",
 
176
                                                                               file.Generator, result.GeneratedFilePath);
 
177
                                        result.Errors.Add (new CompilerError (file.Name, 0, 0, "", msg));
 
178
                                        monitor.ReportError (msg, null);
 
179
                                }
 
180
                                
 
181
                                if (result.Errors.Count > 0) {
 
182
                                        foreach (CompilerError err in result.Errors)
 
183
                                                TaskService.Errors.Add (new Task (file.FilePath, err.ErrorText, err.Column, err.Line,
 
184
                                                                                          err.IsWarning? TaskSeverity.Warning : TaskSeverity.Error,
 
185
                                                                                          TaskPriority.Normal, file.Project.ParentSolution, file));
 
186
                                }
 
187
                                
 
188
                                if (broken)
 
189
                                        return;
 
190
                                
 
191
                                if (result.Success)
 
192
                                        monitor.ReportSuccess ("Generated file successfully.");
 
193
                                else
 
194
                                        monitor.ReportError ("Failed to generate file. See error pad for details.", null);
 
195
                                
 
196
                        } finally {
 
197
                                monitor.Dispose ();
 
198
                        }
 
199
                        
 
200
                        if (!result.GeneratedFilePath.IsNullOrEmpty && File.Exists (result.GeneratedFilePath)) {
 
201
                                if (genFile == null) {
 
202
                                        genFile = file.Project.AddFile (result.GeneratedFilePath);
 
203
                                } else if (result.GeneratedFilePath != genFile.FilePath) {
 
204
                                        genFile.Name = result.GeneratedFilePath;
 
205
                                }
 
206
                                file.LastGenOutput = genFileName;
 
207
                                genFile.DependsOn = file.FilePath.FileName; 
 
208
                        }
 
209
                }
 
210
                
 
211
                public static void HandleRename (ProjectFileRenamedEventArgs args)
 
212
                {
 
213
                        var file = args.ProjectFile;
 
214
                        var tool = GetGenerator (file);
 
215
                        if (tool == null)
 
216
                                return;
 
217
                }
 
218
        }
 
219
}
 
220