2 // ToolTask.cs: Base class for command line tool tasks.
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
7 // (C) 2005 Marek Sieradzki
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
31 using System.Diagnostics;
32 using System.Collections.Specialized;
34 using System.Resources;
36 using System.Text.RegularExpressions;
37 using Microsoft.Build.Framework;
38 using Mono.XBuild.Utilities;
40 namespace Microsoft.Build.Utilities
42 public abstract class ToolTask : Task
44 StringDictionary environmentOverride;
49 Encoding responseFileEncoding;
50 MessageImportance standardErrorLoggingImportance;
51 MessageImportance standardOutputLoggingImportance;
58 this.standardErrorLoggingImportance = MessageImportance.High;
59 this.standardOutputLoggingImportance = MessageImportance.Normal;
62 protected ToolTask (ResourceManager taskResources)
63 : this (taskResources, null)
67 protected ToolTask (ResourceManager taskResources,
68 string helpKeywordPrefix)
70 this.TaskResources = taskResources;
71 this.HelpKeywordPrefix = helpKeywordPrefix;
72 this.toolPath = MonoLocationHelper.GetBinDir ();
73 this.responseFileEncoding = Encoding.UTF8;
80 + @"(((?<ORIGIN>(((\d+>)?[a-zA-Z]?:[^:]*)|([^:]*))):)"
82 + "(?<SUBCATEGORY>(()|([^:]*? )))"
83 + "(?<CATEGORY>(error|warning)) "
86 RegexOptions.IgnoreCase);
90 protected virtual bool CallHostObjectToExecute ()
96 // FIXME: it should write responseFileCommands to temporary response file
97 protected virtual int ExecuteTool (string pathToTool,
98 string responseFileCommands,
99 string commandLineCommands)
104 arguments = String.Concat (commandLineCommands, " ", responseFileCommands);
106 success = RealExecute (pathToTool, arguments);
114 public override bool Execute ()
118 result = ExecuteTool (GenerateFullPathToTool (), GenerateResponseFileCommands (),
119 GenerateCommandLineCommands ());
128 protected virtual string GetWorkingDirectory ()
133 private bool RealExecute (string filename, string arguments)
137 if (filename == null)
138 throw new ArgumentNullException ("filename");
139 if (arguments == null)
140 throw new ArgumentNullException ("arguments");
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;
150 Log.LogMessage (MessageImportance.Low, String.Format ("Tool {0} execution started with arguments: {1}",
151 filename, arguments));
154 process.WaitForExit ();
156 exitCode = process.ExitCode;
158 while ((line = process.StandardError.ReadLine ()) != null) {
159 LogEventsFromTextOutput (line, MessageImportance.Normal);
162 Log.LogMessage (MessageImportance.Low, String.Format ("Tool {0} execution finished.", filename));
164 return !Log.HasLoggedErrors;
168 // FIXME: use importance
170 protected virtual void LogEventsFromTextOutput (string singleLine,
171 MessageImportance importance)
173 string filename, origin, category, code, subcategory, text;
174 int lineNumber, columnNumber, endLineNumber, endColumnNumber;
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;
183 ParseOrigin (origin, out filename, out lineNumber, out columnNumber, out endLineNumber, out endColumnNumber);
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);
194 private void ParseOrigin (string origin, out string filename,
195 out int lineNumber, out int columnNumber,
196 out int endLineNumber, out int endColumnNumber)
200 string[] left, right;
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]);
213 } else if (left.Length == 2) {
214 lineNumber = Int32.Parse (left [0]);
216 endLineNumber = Int32.Parse (left [1]);
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]);
224 if (right.Length == 1) {
225 columnNumber = Int32.Parse (right [0]);
227 } else if (right.Length == 2) {
228 columnNumber = Int32.Parse (right [0]);
229 endColumnNumber = Int32.Parse (right [0]);
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]);
238 throw new Exception ("Invalid line/column format.");
249 protected virtual string GenerateCommandLineCommands ()
254 protected abstract string GenerateFullPathToTool ();
257 protected virtual string GenerateResponseFileCommands ()
263 protected virtual string GetResponseFileSwitch (string responseFilePath)
265 return String.Format ("@{0}", responseFilePath);
269 protected virtual bool HandleTaskExecutionErrors ()
274 protected virtual HostObjectInitializationStatus InitializeHostObject ()
276 return HostObjectInitializationStatus.NoActionReturnSuccess;
280 protected virtual void LogToolCommand (string message)
285 protected virtual void LogPathToTool (string toolName,
290 protected virtual bool SkipTaskExecution()
295 protected virtual bool ValidateParameters()
300 protected virtual StringDictionary EnvironmentOverride
302 get { return environmentOverride; }
307 public int ExitCode {
308 get { return exitCode; }
311 protected virtual Encoding ResponseFileEncoding
313 get { return responseFileEncoding; }
316 protected virtual Encoding StandardErrorEncoding
318 get { return Console.Error.Encoding; }
321 protected virtual MessageImportance StandardErrorLoggingImportance {
322 get { return standardErrorLoggingImportance; }
325 protected virtual Encoding StandardOutputEncoding
327 get { return Console.Out.Encoding; }
330 protected virtual MessageImportance StandardOutputLoggingImportance {
331 get { return standardOutputLoggingImportance; }
334 public virtual int Timeout
336 get { return timeout; }
337 set { timeout = value; }
340 protected abstract string ToolName
345 public string ToolPath
347 get { return toolPath; }
348 set { toolPath = value; }