2006-02-26 Marek Sieradzki <marek.sieradzki@gmail.com>
[mono.git] / mcs / class / Microsoft.Build.Utilities / Microsoft.Build.Utilities / ToolTask.cs
1 //
2 // ToolTask.cs: Base class for command line tool tasks. 
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 //
7 // (C) 2005 Marek Sieradzki
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 #if NET_2_0
29
30 using System;
31 using System.Diagnostics;
32 using System.Collections.Specialized;
33 using System.IO;
34 using System.Resources;
35 using System.Text;
36 using System.Text.RegularExpressions;
37 using Microsoft.Build.Framework;
38 using Mono.XBuild.Utilities;
39
40 namespace Microsoft.Build.Utilities
41 {
42         public abstract class ToolTask : Task
43         {
44                 StringDictionary        environmentOverride;
45                 int                     exitCode;
46                 int                     timeout;
47                 string                  toolPath;
48                 Process                 process;
49                 MessageImportance       standardErrorLoggingImportance;
50                 MessageImportance       standardOutputLoggingImportance;
51                 
52                 static Regex            regex;
53                 
54                 protected ToolTask ()
55                         : this (null, null)
56                 {
57                         this.standardErrorLoggingImportance = MessageImportance.High;
58                         this.standardOutputLoggingImportance = MessageImportance.Normal;
59                 }
60
61                 protected ToolTask (ResourceManager taskResources)
62                         : this (taskResources, null)
63                 {
64                 }
65
66                 protected ToolTask (ResourceManager taskResources,
67                                    string helpKeywordPrefix)
68                 {
69                         this.TaskResources = taskResources;
70                         this.HelpKeywordPrefix = helpKeywordPrefix;
71                         this.toolPath = MonoLocationHelper.GetBinDir ();
72                 }
73
74                 static ToolTask ()
75                 {
76                         regex = new Regex (
77                                 @"^\s*"
78                                 + @"(((?<ORIGIN>(((\d+>)?[a-zA-Z]?:[^:]*)|([^:]*))):)"
79                                 + "|())"
80                                 + "(?<SUBCATEGORY>(()|([^:]*? )))"
81                                 + "(?<CATEGORY>(error|warning)) "
82                                 + "(?<CODE>[^:]*):"
83                                 + "(?<TEXT>.*)$",
84                                 RegexOptions.IgnoreCase);
85                 }
86
87                 protected virtual bool CallHostObjectToExecute ()
88                 {
89                         return true;
90                 }
91
92                 protected virtual int ExecuteTool (string pathToTool,
93                                                    string responseFileCommands,
94                                                    string commandLineCommands)
95                 {
96                         string arguments;
97                         bool success;
98                         
99                         arguments = String.Concat (commandLineCommands, " ", responseFileCommands);
100                         
101                         success  = RealExecute (pathToTool, arguments);
102                         
103                         if (success)
104                                 return 0;
105                         else
106                                 return -1;
107                 }
108                 
109                 public override bool Execute ()
110                 {
111                         int result;
112                         
113                         result = ExecuteTool (GenerateFullPathToTool (), GenerateResponseFileCommands (),
114                                 GenerateCommandLineCommands ());
115                         
116                         if (result == 0)
117                                 return true;
118                         else
119                                 return false;
120                 }
121                 
122                 protected virtual string GetWorkingDirectory ()
123                 {
124                         return null;
125                 }
126                 
127                 private bool RealExecute (string filename, string arguments)
128                 {
129                         string line;
130                 
131                         if (filename == null)
132                                 throw new ArgumentNullException ("filename");
133                         if (arguments == null)
134                                 throw new ArgumentNullException ("arguments");
135                         
136                         process = new Process ();
137                         process.StartInfo.Arguments = arguments;
138                         process.StartInfo.CreateNoWindow = true;
139                         process.StartInfo.FileName = filename;
140                         process.StartInfo.RedirectStandardError = true;
141                         process.StartInfo.RedirectStandardOutput = true;
142                         process.StartInfo.UseShellExecute = false;
143                         
144                         Log.LogMessage (MessageImportance.Low, String.Format ("Tool {0} execution started with arguments: {1}",
145                                 filename, arguments));
146                         
147                         process.Start ();
148                         process.WaitForExit ();
149                         
150                         while ((line = process.StandardError.ReadLine ()) != null) {
151                                 LogEventsFromTextOutput (line, MessageImportance.Normal);
152                         }
153                         
154                         Log.LogMessage (MessageImportance.Low, String.Format ("Tool {0} execution finished.", filename));
155                         
156                         return !Log.HasLoggedErrors;
157                 }
158                 
159                 
160                 // FIXME: use importance
161                 [MonoTODO]
162                 protected virtual void LogEventsFromTextOutput (string singleLine,
163                                                                 MessageImportance importance)
164                 {
165                         string filename, origin, category, code, subcategory, text;
166                         int lineNumber, columnNumber, endLineNumber, endColumnNumber;
167                 
168                         Match m = regex.Match (singleLine);
169                         origin = m.Groups [regex.GroupNumberFromName ("ORIGIN")].Value;
170                         category = m.Groups [regex.GroupNumberFromName ("CATEGORY")].Value;
171                         code = m.Groups [regex.GroupNumberFromName ("CODE")].Value;
172                         subcategory = m.Groups [regex.GroupNumberFromName ("SUBCATEGORY")].Value;
173                         text = m.Groups [regex.GroupNumberFromName ("TEXT")].Value;
174                         
175                         ParseOrigin (origin, out filename, out lineNumber, out columnNumber, out endLineNumber, out endColumnNumber);
176                         
177                         if (category == "warning") {
178                                 Log.LogWarning (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
179                                         endColumnNumber, text, null);
180                         } else if (category == "error") {
181                                 Log.LogError (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
182                                         endColumnNumber, text, null);
183                         }
184                 }
185                 
186                 private void ParseOrigin (string origin, out string filename,
187                                      out int lineNumber, out int columnNumber,
188                                      out int endLineNumber, out int endColumnNumber)
189                 {
190                         int lParen;
191                         string[] temp;
192                         string[] left, right;
193                         
194                         if (origin.IndexOf ('(') != -1 ) {
195                                 lParen = origin.IndexOf ('(');
196                                 filename = origin.Substring (0, lParen);
197                                 temp = origin.Substring (lParen + 1, origin.Length - lParen - 2).Split (',');
198                                 if (temp.Length == 1) {
199                                         left = temp [0].Split ('-');
200                                         if (left.Length == 1) {
201                                                 lineNumber = Int32.Parse (left [0]);
202                                                 columnNumber = 0;
203                                                 endLineNumber = 0;
204                                                 endColumnNumber = 0;
205                                         } else if (left.Length == 2) {
206                                                 lineNumber = Int32.Parse (left [0]);
207                                                 columnNumber = 0;
208                                                 endLineNumber = Int32.Parse (left [1]);
209                                                 endColumnNumber = 0;
210                                         } else
211                                                 throw new Exception ("Invalid line/column format.");
212                                 } else if (temp.Length == 2) {
213                                         right = temp [1].Split ('-');
214                                         lineNumber = Int32.Parse (temp [0]);
215                                         endLineNumber = 0;
216                                         if (right.Length == 1) {
217                                                 columnNumber = Int32.Parse (right [0]);
218                                                 endColumnNumber = 0;
219                                         } else if (right.Length == 2) {
220                                                 columnNumber = Int32.Parse (right [0]);
221                                                 endColumnNumber = Int32.Parse (right [0]);
222                                         } else
223                                                 throw new Exception ("Invalid line/column format.");
224                                 } else if (temp.Length == 4) {
225                                         lineNumber = Int32.Parse (temp [0]);
226                                         endLineNumber = Int32.Parse (temp [2]);
227                                         columnNumber = Int32.Parse (temp [1]);
228                                         endColumnNumber = Int32.Parse (temp [3]);
229                                 } else
230                                         throw new Exception ("Invalid line/column format.");
231                         } else {
232                                 filename = origin;
233                                 lineNumber = 0;
234                                 columnNumber = 0;
235                                 endLineNumber = 0;
236                                 endColumnNumber = 0;
237                         }
238                 }
239
240                 protected virtual string GenerateCommandLineCommands ()
241                 {
242                         return null;
243                 }
244
245                 protected abstract string GenerateFullPathToTool ();
246
247                 protected virtual string GenerateResponseFileCommands ()
248                 {
249                         return null;
250                 }
251
252                 protected virtual string GetResponseFileSwitch (string responseFilePath)
253                 {
254                         return String.Format ("@{0}", responseFilePath);
255                 }
256
257                 [MonoTODO]
258                 protected virtual bool HandleTaskExecutionErrors ()
259                 {
260                         return true;
261                 }
262
263                 protected virtual HostObjectInitializationStatus InitializeHostObject ()
264                 {
265                         return HostObjectInitializationStatus.NoActionReturnSuccess;
266                 }
267
268                 [MonoTODO]
269                 protected virtual void LogToolCommand (string message)
270                 {
271                 }
272                 
273                 [MonoTODO]
274                 protected virtual void LogPathToTool (string toolName,
275                                                       string pathToTool)
276                 {
277                 }
278
279                 protected virtual bool SkipTaskExecution()
280                 {
281                         return false;
282                 }
283
284                 protected virtual bool ValidateParameters()
285                 {
286                         return true;
287                 }
288
289                 protected virtual StringDictionary EnvironmentOverride
290                 {
291                         get { return environmentOverride; }
292                 }
293                 
294                 [MonoTODO]
295                 [Output]
296                 public int ExitCode {
297                         get { return exitCode; }
298                 }
299
300                 protected virtual Encoding ResponseFileEncoding
301                 {
302                         get { return Encoding.UTF8; }
303                 }
304
305                 protected virtual Encoding StandardErrorEncoding
306                 {
307                         get { return Console.Error.Encoding; }
308                 }
309
310                 protected virtual MessageImportance StandardErrorLoggingImportance {
311                         get { return standardErrorLoggingImportance; }
312                 }
313
314                 protected virtual Encoding StandardOutputEncoding
315                 {
316                         get { return Console.Out.Encoding; }
317                 }
318
319                 protected virtual MessageImportance StandardOutputLoggingImportance {
320                         get { return standardOutputLoggingImportance; }
321                 }
322
323                 public virtual int Timeout
324                 {
325                         get { return timeout; }
326                         set { timeout = value; }
327                 }
328
329                 protected abstract string ToolName
330                 {
331                         get;
332                 }
333
334                 public string ToolPath
335                 {
336                         get { return toolPath; }
337                         set { toolPath  = value; }
338                 }
339         }
340 }
341
342 #endif