Fix bug #475438.
[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
140                         if (!File.Exists (filename)) {
141                                 Log.LogError ("Unable to find tool {0} at '{1}'", ToolName, filename);
142                                 return false;
143                         }
144
145                         if (arguments == null)
146                                 throw new ArgumentNullException ("arguments");
147                         
148                         process = new Process ();
149                         process.StartInfo.Arguments = arguments;
150                         process.StartInfo.CreateNoWindow = true;
151                         process.StartInfo.FileName = filename;
152                         process.StartInfo.RedirectStandardError = true;
153                         process.StartInfo.RedirectStandardOutput = true;
154                         process.StartInfo.UseShellExecute = false;
155                         
156                         Log.LogMessage (MessageImportance.Normal, String.Format ("Tool {0} execution started with arguments: {1}",
157                                 filename, arguments));
158                         
159                         try {
160                                 process.Start ();
161                                 process.WaitForExit ();
162                         } catch (System.ComponentModel.Win32Exception e) {
163                                 Log.LogError ("Error executing tool '{0}': {1}", filename, e.Message);
164                                 return false;
165                         }
166                         
167                         exitCode = process.ExitCode;
168                         
169                         while ((line = process.StandardError.ReadLine ()) != null) {
170                                 LogEventsFromTextOutput (line, MessageImportance.Low);
171                         }
172                         
173                         Log.LogMessage (MessageImportance.Low, String.Format ("Tool {0} execution finished.", filename));
174                         
175                         return !Log.HasLoggedErrors;
176                 }
177                 
178                 
179                 [MonoTODO]
180                 protected virtual void LogEventsFromTextOutput (string singleLine, MessageImportance importance)
181                 {
182                         string filename, origin, category, code, subcategory, text;
183                         int lineNumber, columnNumber, endLineNumber, endColumnNumber;
184                 
185                         Match m = regex.Match (singleLine);
186                         origin = m.Groups [regex.GroupNumberFromName ("ORIGIN")].Value;
187                         category = m.Groups [regex.GroupNumberFromName ("CATEGORY")].Value;
188                         code = m.Groups [regex.GroupNumberFromName ("CODE")].Value;
189                         subcategory = m.Groups [regex.GroupNumberFromName ("SUBCATEGORY")].Value;
190                         text = m.Groups [regex.GroupNumberFromName ("TEXT")].Value;
191                         
192                         ParseOrigin (origin, out filename, out lineNumber, out columnNumber, out endLineNumber, out endColumnNumber);
193                         
194                         if (category == "warning") {
195                                 Log.LogWarning (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
196                                         endColumnNumber, text, null);
197                         } else if (category == "error") {
198                                 Log.LogError (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
199                                         endColumnNumber, text, null);
200                         }
201                 }
202                 
203                 private void ParseOrigin (string origin, out string filename,
204                                      out int lineNumber, out int columnNumber,
205                                      out int endLineNumber, out int endColumnNumber)
206                 {
207                         int lParen;
208                         string[] temp;
209                         string[] left, right;
210                         
211                         if (origin.IndexOf ('(') != -1 ) {
212                                 lParen = origin.IndexOf ('(');
213                                 filename = origin.Substring (0, lParen);
214                                 temp = origin.Substring (lParen + 1, origin.Length - lParen - 2).Split (',');
215                                 if (temp.Length == 1) {
216                                         left = temp [0].Split ('-');
217                                         if (left.Length == 1) {
218                                                 lineNumber = Int32.Parse (left [0]);
219                                                 columnNumber = 0;
220                                                 endLineNumber = 0;
221                                                 endColumnNumber = 0;
222                                         } else if (left.Length == 2) {
223                                                 lineNumber = Int32.Parse (left [0]);
224                                                 columnNumber = 0;
225                                                 endLineNumber = Int32.Parse (left [1]);
226                                                 endColumnNumber = 0;
227                                         } else
228                                                 throw new Exception ("Invalid line/column format.");
229                                 } else if (temp.Length == 2) {
230                                         right = temp [1].Split ('-');
231                                         lineNumber = Int32.Parse (temp [0]);
232                                         endLineNumber = 0;
233                                         if (right.Length == 1) {
234                                                 columnNumber = Int32.Parse (right [0]);
235                                                 endColumnNumber = 0;
236                                         } else if (right.Length == 2) {
237                                                 columnNumber = Int32.Parse (right [0]);
238                                                 endColumnNumber = Int32.Parse (right [0]);
239                                         } else
240                                                 throw new Exception ("Invalid line/column format.");
241                                 } else if (temp.Length == 4) {
242                                         lineNumber = Int32.Parse (temp [0]);
243                                         endLineNumber = Int32.Parse (temp [2]);
244                                         columnNumber = Int32.Parse (temp [1]);
245                                         endColumnNumber = Int32.Parse (temp [3]);
246                                 } else
247                                         throw new Exception ("Invalid line/column format.");
248                         } else {
249                                 filename = origin;
250                                 lineNumber = 0;
251                                 columnNumber = 0;
252                                 endLineNumber = 0;
253                                 endColumnNumber = 0;
254                         }
255                 }
256
257                 [MonoTODO]
258                 protected virtual string GenerateCommandLineCommands ()
259                 {
260                         return null;
261                 }
262
263                 protected abstract string GenerateFullPathToTool ();
264
265                 [MonoTODO]
266                 protected virtual string GenerateResponseFileCommands ()
267                 {
268                         return null;
269                 }
270
271                 [MonoTODO]
272                 protected virtual string GetResponseFileSwitch (string responseFilePath)
273                 {
274                         return String.Format ("@{0}", responseFilePath);
275                 }
276
277                 [MonoTODO]
278                 protected virtual bool HandleTaskExecutionErrors ()
279                 {
280                         return true;
281                 }
282
283                 protected virtual HostObjectInitializationStatus InitializeHostObject ()
284                 {
285                         return HostObjectInitializationStatus.NoActionReturnSuccess;
286                 }
287
288                 [MonoTODO]
289                 protected virtual void LogToolCommand (string message)
290                 {
291                 }
292                 
293                 [MonoTODO]
294                 protected virtual void LogPathToTool (string toolName,
295                                                       string pathToTool)
296                 {
297                 }
298
299                 protected virtual bool SkipTaskExecution()
300                 {
301                         return false;
302                 }
303
304                 protected virtual bool ValidateParameters()
305                 {
306                         return true;
307                 }
308
309                 protected virtual StringDictionary EnvironmentOverride
310                 {
311                         get { return environmentOverride; }
312                 }
313                 
314                 [MonoTODO]
315                 [Output]
316                 public int ExitCode {
317                         get { return exitCode; }
318                 }
319
320                 protected virtual Encoding ResponseFileEncoding
321                 {
322                         get { return responseFileEncoding; }
323                 }
324
325                 protected virtual Encoding StandardErrorEncoding
326                 {
327                         get { return Console.Error.Encoding; }
328                 }
329
330                 protected virtual MessageImportance StandardErrorLoggingImportance {
331                         get { return standardErrorLoggingImportance; }
332                 }
333
334                 protected virtual Encoding StandardOutputEncoding
335                 {
336                         get { return Console.Out.Encoding; }
337                 }
338
339                 protected virtual MessageImportance StandardOutputLoggingImportance {
340                         get { return standardOutputLoggingImportance; }
341                 }
342
343                 public virtual int Timeout
344                 {
345                         get { return timeout; }
346                         set { timeout = value; }
347                 }
348
349                 protected abstract string ToolName
350                 {
351                         get;
352                 }
353
354                 public string ToolPath
355                 {
356                         get { return toolPath; }
357                         set { toolPath  = value; }
358                 }
359         }
360 }
361
362 #endif