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