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