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");
140 if (!File.Exists (filename)) {
141 Log.LogError ("Unable to find tool {0} at '{1}'", ToolName, filename);
145 if (arguments == null)
146 throw new ArgumentNullException ("arguments");
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;
156 Log.LogMessage (MessageImportance.Normal, String.Format ("Tool {0} execution started with arguments: {1}",
157 filename, arguments));
161 process.WaitForExit ();
162 } catch (System.ComponentModel.Win32Exception e) {
163 Log.LogError ("Error executing tool '{0}': {1}", filename, e.Message);
167 exitCode = process.ExitCode;
169 while ((line = process.StandardError.ReadLine ()) != null) {
170 LogEventsFromTextOutput (line, MessageImportance.Low);
173 Log.LogMessage (MessageImportance.Low, String.Format ("Tool {0} execution finished.", filename));
175 return !Log.HasLoggedErrors;
180 protected virtual void LogEventsFromTextOutput (string singleLine, MessageImportance importance)
182 string filename, origin, category, code, subcategory, text;
183 int lineNumber, columnNumber, endLineNumber, endColumnNumber;
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;
192 ParseOrigin (origin, out filename, out lineNumber, out columnNumber, out endLineNumber, out endColumnNumber);
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);
203 private void ParseOrigin (string origin, out string filename,
204 out int lineNumber, out int columnNumber,
205 out int endLineNumber, out int endColumnNumber)
209 string[] left, right;
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]);
222 } else if (left.Length == 2) {
223 lineNumber = Int32.Parse (left [0]);
225 endLineNumber = Int32.Parse (left [1]);
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]);
233 if (right.Length == 1) {
234 columnNumber = Int32.Parse (right [0]);
236 } else if (right.Length == 2) {
237 columnNumber = Int32.Parse (right [0]);
238 endColumnNumber = Int32.Parse (right [0]);
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]);
247 throw new Exception ("Invalid line/column format.");
258 protected virtual string GenerateCommandLineCommands ()
263 protected abstract string GenerateFullPathToTool ();
266 protected virtual string GenerateResponseFileCommands ()
272 protected virtual string GetResponseFileSwitch (string responseFilePath)
274 return String.Format ("@{0}", responseFilePath);
278 protected virtual bool HandleTaskExecutionErrors ()
283 protected virtual HostObjectInitializationStatus InitializeHostObject ()
285 return HostObjectInitializationStatus.NoActionReturnSuccess;
289 protected virtual void LogToolCommand (string message)
294 protected virtual void LogPathToTool (string toolName,
299 protected virtual bool SkipTaskExecution()
304 protected virtual bool ValidateParameters()
309 protected virtual StringDictionary EnvironmentOverride
311 get { return environmentOverride; }
316 public int ExitCode {
317 get { return exitCode; }
320 protected virtual Encoding ResponseFileEncoding
322 get { return responseFileEncoding; }
325 protected virtual Encoding StandardErrorEncoding
327 get { return Console.Error.Encoding; }
330 protected virtual MessageImportance StandardErrorLoggingImportance {
331 get { return standardErrorLoggingImportance; }
334 protected virtual Encoding StandardOutputEncoding
336 get { return Console.Out.Encoding; }
339 protected virtual MessageImportance StandardOutputLoggingImportance {
340 get { return standardOutputLoggingImportance; }
343 public virtual int Timeout
345 get { return timeout; }
346 set { timeout = value; }
349 protected abstract string ToolName
354 public string ToolPath
356 get { return toolPath; }
357 set { toolPath = value; }