2006-12-29 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                 Encoding                responseFileEncoding;
50                 MessageImportance       standardErrorLoggingImportance;
51                 MessageImportance       standardOutputLoggingImportance;
52                 
53                 static Regex            regex;
54                 
55                 protected ToolTask ()
56                         : this (null, null)
57                 {
58                         this.standardErrorLoggingImportance = MessageImportance.High;
59                         this.standardOutputLoggingImportance = MessageImportance.Normal;
60                 }
61
62                 protected ToolTask (ResourceManager taskResources)
63                         : this (taskResources, null)
64                 {
65                 }
66
67                 protected ToolTask (ResourceManager taskResources,
68                                    string helpKeywordPrefix)
69                 {
70                         this.TaskResources = taskResources;
71                         this.HelpKeywordPrefix = helpKeywordPrefix;
72                         this.toolPath = MonoLocationHelper.GetBinDir ();
73                         this.responseFileEncoding = Encoding.UTF8;
74                 }
75
76                 static ToolTask ()
77                 {
78                         regex = new Regex (
79                                 @"^\s*"
80                                 + @"(((?<ORIGIN>(((\d+>)?[a-zA-Z]?:[^:]*)|([^:]*))):)"
81                                 + "|())"
82                                 + "(?<SUBCATEGORY>(()|([^:]*? )))"
83                                 + "(?<CATEGORY>(error|warning)) "
84                                 + "(?<CODE>[^:]*):"
85                                 + "(?<TEXT>.*)$",
86                                 RegexOptions.IgnoreCase);
87                 }
88
89                 [MonoTODO]
90                 protected virtual bool CallHostObjectToExecute ()
91                 {
92                         return true;
93                 }
94
95                 [MonoTODO]
96                 // FIXME: it should write responseFileCommands to temporary response file
97                 protected virtual int ExecuteTool (string pathToTool,
98                                                    string responseFileCommands,
99                                                    string commandLineCommands)
100                 {
101                         string arguments;
102                         bool success;
103                         
104                         arguments = String.Concat (commandLineCommands, " ", responseFileCommands);
105                         
106                         success  = RealExecute (pathToTool, arguments);
107                         
108                         if (success)
109                                 return 0;
110                         else
111                                 return -1;
112                 }
113                 
114                 public override bool Execute ()
115                 {
116                         int result;
117                         
118                         result = ExecuteTool (GenerateFullPathToTool (), GenerateResponseFileCommands (),
119                                 GenerateCommandLineCommands ());
120                         
121                         if (result == 0)
122                                 return true;
123                         else
124                                 return false;
125                 }
126                 
127                 [MonoTODO]
128                 protected virtual string GetWorkingDirectory ()
129                 {
130                         return null;
131                 }
132                 
133                 private bool RealExecute (string filename, string arguments)
134                 {
135                         string line;
136                 
137                         if (filename == null)
138                                 throw new ArgumentNullException ("filename");
139                         if (arguments == null)
140                                 throw new ArgumentNullException ("arguments");
141                         
142                         process = new Process ();
143                         process.StartInfo.Arguments = arguments;
144                         process.StartInfo.CreateNoWindow = true;
145                         process.StartInfo.FileName = filename;
146                         process.StartInfo.RedirectStandardError = true;
147                         process.StartInfo.RedirectStandardOutput = true;
148                         process.StartInfo.UseShellExecute = false;
149                         
150                         Log.LogMessage (MessageImportance.Normal, String.Format ("Tool {0} execution started with arguments: {1}",
151                                 filename, arguments));
152                         
153                         process.Start ();
154                         process.WaitForExit ();
155                         
156                         exitCode = process.ExitCode;
157                         
158                         while ((line = process.StandardError.ReadLine ()) != null) {
159                                 LogEventsFromTextOutput (line, MessageImportance.Low);
160                         }
161                         
162                         Log.LogMessage (MessageImportance.Low, String.Format ("Tool {0} execution finished.", filename));
163                         
164                         return !Log.HasLoggedErrors;
165                 }
166                 
167                 
168                 [MonoTODO]
169                 protected virtual void LogEventsFromTextOutput (string singleLine, MessageImportance importance)
170                 {
171                         string filename, origin, category, code, subcategory, text;
172                         int lineNumber, columnNumber, endLineNumber, endColumnNumber;
173                 
174                         Match m = regex.Match (singleLine);
175                         origin = m.Groups [regex.GroupNumberFromName ("ORIGIN")].Value;
176                         category = m.Groups [regex.GroupNumberFromName ("CATEGORY")].Value;
177                         code = m.Groups [regex.GroupNumberFromName ("CODE")].Value;
178                         subcategory = m.Groups [regex.GroupNumberFromName ("SUBCATEGORY")].Value;
179                         text = m.Groups [regex.GroupNumberFromName ("TEXT")].Value;
180                         
181                         ParseOrigin (origin, out filename, out lineNumber, out columnNumber, out endLineNumber, out endColumnNumber);
182                         
183                         if (category == "warning") {
184                                 Log.LogWarning (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
185                                         endColumnNumber, text, null);
186                         } else if (category == "error") {
187                                 Log.LogError (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
188                                         endColumnNumber, text, null);
189                         }
190                 }
191                 
192                 private void ParseOrigin (string origin, out string filename,
193                                      out int lineNumber, out int columnNumber,
194                                      out int endLineNumber, out int endColumnNumber)
195                 {
196                         int lParen;
197                         string[] temp;
198                         string[] left, right;
199                         
200                         if (origin.IndexOf ('(') != -1 ) {
201                                 lParen = origin.IndexOf ('(');
202                                 filename = origin.Substring (0, lParen);
203                                 temp = origin.Substring (lParen + 1, origin.Length - lParen - 2).Split (',');
204                                 if (temp.Length == 1) {
205                                         left = temp [0].Split ('-');
206                                         if (left.Length == 1) {
207                                                 lineNumber = Int32.Parse (left [0]);
208                                                 columnNumber = 0;
209                                                 endLineNumber = 0;
210                                                 endColumnNumber = 0;
211                                         } else if (left.Length == 2) {
212                                                 lineNumber = Int32.Parse (left [0]);
213                                                 columnNumber = 0;
214                                                 endLineNumber = Int32.Parse (left [1]);
215                                                 endColumnNumber = 0;
216                                         } else
217                                                 throw new Exception ("Invalid line/column format.");
218                                 } else if (temp.Length == 2) {
219                                         right = temp [1].Split ('-');
220                                         lineNumber = Int32.Parse (temp [0]);
221                                         endLineNumber = 0;
222                                         if (right.Length == 1) {
223                                                 columnNumber = Int32.Parse (right [0]);
224                                                 endColumnNumber = 0;
225                                         } else if (right.Length == 2) {
226                                                 columnNumber = Int32.Parse (right [0]);
227                                                 endColumnNumber = Int32.Parse (right [0]);
228                                         } else
229                                                 throw new Exception ("Invalid line/column format.");
230                                 } else if (temp.Length == 4) {
231                                         lineNumber = Int32.Parse (temp [0]);
232                                         endLineNumber = Int32.Parse (temp [2]);
233                                         columnNumber = Int32.Parse (temp [1]);
234                                         endColumnNumber = Int32.Parse (temp [3]);
235                                 } else
236                                         throw new Exception ("Invalid line/column format.");
237                         } else {
238                                 filename = origin;
239                                 lineNumber = 0;
240                                 columnNumber = 0;
241                                 endLineNumber = 0;
242                                 endColumnNumber = 0;
243                         }
244                 }
245
246                 [MonoTODO]
247                 protected virtual string GenerateCommandLineCommands ()
248                 {
249                         return null;
250                 }
251
252                 protected abstract string GenerateFullPathToTool ();
253
254                 [MonoTODO]
255                 protected virtual string GenerateResponseFileCommands ()
256                 {
257                         return null;
258                 }
259
260                 [MonoTODO]
261                 protected virtual string GetResponseFileSwitch (string responseFilePath)
262                 {
263                         return String.Format ("@{0}", responseFilePath);
264                 }
265
266                 [MonoTODO]
267                 protected virtual bool HandleTaskExecutionErrors ()
268                 {
269                         return true;
270                 }
271
272                 protected virtual HostObjectInitializationStatus InitializeHostObject ()
273                 {
274                         return HostObjectInitializationStatus.NoActionReturnSuccess;
275                 }
276
277                 [MonoTODO]
278                 protected virtual void LogToolCommand (string message)
279                 {
280                 }
281                 
282                 [MonoTODO]
283                 protected virtual void LogPathToTool (string toolName,
284                                                       string pathToTool)
285                 {
286                 }
287
288                 protected virtual bool SkipTaskExecution()
289                 {
290                         return false;
291                 }
292
293                 protected virtual bool ValidateParameters()
294                 {
295                         return true;
296                 }
297
298                 protected virtual StringDictionary EnvironmentOverride
299                 {
300                         get { return environmentOverride; }
301                 }
302                 
303                 [MonoTODO]
304                 [Output]
305                 public int ExitCode {
306                         get { return exitCode; }
307                 }
308
309                 protected virtual Encoding ResponseFileEncoding
310                 {
311                         get { return responseFileEncoding; }
312                 }
313
314                 protected virtual Encoding StandardErrorEncoding
315                 {
316                         get { return Console.Error.Encoding; }
317                 }
318
319                 protected virtual MessageImportance StandardErrorLoggingImportance {
320                         get { return standardErrorLoggingImportance; }
321                 }
322
323                 protected virtual Encoding StandardOutputEncoding
324                 {
325                         get { return Console.Out.Encoding; }
326                 }
327
328                 protected virtual MessageImportance StandardOutputLoggingImportance {
329                         get { return standardOutputLoggingImportance; }
330                 }
331
332                 public virtual int Timeout
333                 {
334                         get { return timeout; }
335                         set { timeout = value; }
336                 }
337
338                 protected abstract string ToolName
339                 {
340                         get;
341                 }
342
343                 public string ToolPath
344                 {
345                         get { return toolPath; }
346                         set { toolPath  = value; }
347                 }
348         }
349 }
350
351 #endif