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.Normal, 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.Low);
162 Log.LogMessage (MessageImportance.Low, String.Format ("Tool {0} execution finished.", filename));
164 return !Log.HasLoggedErrors;
169 protected virtual void LogEventsFromTextOutput (string singleLine, MessageImportance importance)
171 string filename, origin, category, code, subcategory, text;
172 int lineNumber, columnNumber, endLineNumber, endColumnNumber;
174 Match m = regex.Match (singleLine);
175 origin = m.Groups [regex.GroupNumberFromName ("ORIGIN")].Value;
176 category = m.Groups [regex.GroupNumberFromName ("CATEGORY")].Value;
177 code = m.Groups [regex.GroupNumberFromName ("CODE")].Value;
178 subcategory = m.Groups [regex.GroupNumberFromName ("SUBCATEGORY")].Value;
179 text = m.Groups [regex.GroupNumberFromName ("TEXT")].Value;
181 ParseOrigin (origin, out filename, out lineNumber, out columnNumber, out endLineNumber, out endColumnNumber);
183 if (category == "warning") {
184 Log.LogWarning (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
185 endColumnNumber, text, null);
186 } else if (category == "error") {
187 Log.LogError (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
188 endColumnNumber, text, null);
192 private void ParseOrigin (string origin, out string filename,
193 out int lineNumber, out int columnNumber,
194 out int endLineNumber, out int endColumnNumber)
198 string[] left, right;
200 if (origin.IndexOf ('(') != -1 ) {
201 lParen = origin.IndexOf ('(');
202 filename = origin.Substring (0, lParen);
203 temp = origin.Substring (lParen + 1, origin.Length - lParen - 2).Split (',');
204 if (temp.Length == 1) {
205 left = temp [0].Split ('-');
206 if (left.Length == 1) {
207 lineNumber = Int32.Parse (left [0]);
211 } else if (left.Length == 2) {
212 lineNumber = Int32.Parse (left [0]);
214 endLineNumber = Int32.Parse (left [1]);
217 throw new Exception ("Invalid line/column format.");
218 } else if (temp.Length == 2) {
219 right = temp [1].Split ('-');
220 lineNumber = Int32.Parse (temp [0]);
222 if (right.Length == 1) {
223 columnNumber = Int32.Parse (right [0]);
225 } else if (right.Length == 2) {
226 columnNumber = Int32.Parse (right [0]);
227 endColumnNumber = Int32.Parse (right [0]);
229 throw new Exception ("Invalid line/column format.");
230 } else if (temp.Length == 4) {
231 lineNumber = Int32.Parse (temp [0]);
232 endLineNumber = Int32.Parse (temp [2]);
233 columnNumber = Int32.Parse (temp [1]);
234 endColumnNumber = Int32.Parse (temp [3]);
236 throw new Exception ("Invalid line/column format.");
247 protected virtual string GenerateCommandLineCommands ()
252 protected abstract string GenerateFullPathToTool ();
255 protected virtual string GenerateResponseFileCommands ()
261 protected virtual string GetResponseFileSwitch (string responseFilePath)
263 return String.Format ("@{0}", responseFilePath);
267 protected virtual bool HandleTaskExecutionErrors ()
272 protected virtual HostObjectInitializationStatus InitializeHostObject ()
274 return HostObjectInitializationStatus.NoActionReturnSuccess;
278 protected virtual void LogToolCommand (string message)
283 protected virtual void LogPathToTool (string toolName,
288 protected virtual bool SkipTaskExecution()
293 protected virtual bool ValidateParameters()
298 protected virtual StringDictionary EnvironmentOverride
300 get { return environmentOverride; }
305 public int ExitCode {
306 get { return exitCode; }
309 protected virtual Encoding ResponseFileEncoding
311 get { return responseFileEncoding; }
314 protected virtual Encoding StandardErrorEncoding
316 get { return Console.Error.Encoding; }
319 protected virtual MessageImportance StandardErrorLoggingImportance {
320 get { return standardErrorLoggingImportance; }
323 protected virtual Encoding StandardOutputEncoding
325 get { return Console.Out.Encoding; }
328 protected virtual MessageImportance StandardOutputLoggingImportance {
329 get { return standardOutputLoggingImportance; }
332 public virtual int Timeout
334 get { return timeout; }
335 set { timeout = value; }
338 protected abstract string ToolName
343 public string ToolPath
345 get { return toolPath; }
346 set { toolPath = value; }