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);
89 protected virtual bool CallHostObjectToExecute ()
94 protected virtual int ExecuteTool (string pathToTool,
95 string responseFileCommands,
96 string commandLineCommands)
101 arguments = String.Concat (commandLineCommands, " ", responseFileCommands);
103 success = RealExecute (pathToTool, arguments);
111 public override bool Execute ()
115 result = ExecuteTool (GenerateFullPathToTool (), GenerateResponseFileCommands (),
116 GenerateCommandLineCommands ());
124 protected virtual string GetWorkingDirectory ()
129 private bool RealExecute (string filename, string arguments)
133 if (filename == null)
134 throw new ArgumentNullException ("filename");
135 if (arguments == null)
136 throw new ArgumentNullException ("arguments");
138 process = new Process ();
139 process.StartInfo.Arguments = arguments;
140 process.StartInfo.CreateNoWindow = true;
141 process.StartInfo.FileName = filename;
142 process.StartInfo.RedirectStandardError = true;
143 process.StartInfo.RedirectStandardOutput = true;
144 process.StartInfo.UseShellExecute = false;
146 Log.LogMessage (MessageImportance.Low, String.Format ("Tool {0} execution started with arguments: {1}",
147 filename, arguments));
150 process.WaitForExit ();
152 exitCode = process.ExitCode;
154 while ((line = process.StandardError.ReadLine ()) != null) {
155 LogEventsFromTextOutput (line, MessageImportance.Normal);
158 Log.LogMessage (MessageImportance.Low, String.Format ("Tool {0} execution finished.", filename));
160 return !Log.HasLoggedErrors;
164 // FIXME: use importance
166 protected virtual void LogEventsFromTextOutput (string singleLine,
167 MessageImportance importance)
169 string filename, origin, category, code, subcategory, text;
170 int lineNumber, columnNumber, endLineNumber, endColumnNumber;
172 Match m = regex.Match (singleLine);
173 origin = m.Groups [regex.GroupNumberFromName ("ORIGIN")].Value;
174 category = m.Groups [regex.GroupNumberFromName ("CATEGORY")].Value;
175 code = m.Groups [regex.GroupNumberFromName ("CODE")].Value;
176 subcategory = m.Groups [regex.GroupNumberFromName ("SUBCATEGORY")].Value;
177 text = m.Groups [regex.GroupNumberFromName ("TEXT")].Value;
179 ParseOrigin (origin, out filename, out lineNumber, out columnNumber, out endLineNumber, out endColumnNumber);
181 if (category == "warning") {
182 Log.LogWarning (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
183 endColumnNumber, text, null);
184 } else if (category == "error") {
185 Log.LogError (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
186 endColumnNumber, text, null);
190 private void ParseOrigin (string origin, out string filename,
191 out int lineNumber, out int columnNumber,
192 out int endLineNumber, out int endColumnNumber)
196 string[] left, right;
198 if (origin.IndexOf ('(') != -1 ) {
199 lParen = origin.IndexOf ('(');
200 filename = origin.Substring (0, lParen);
201 temp = origin.Substring (lParen + 1, origin.Length - lParen - 2).Split (',');
202 if (temp.Length == 1) {
203 left = temp [0].Split ('-');
204 if (left.Length == 1) {
205 lineNumber = Int32.Parse (left [0]);
209 } else if (left.Length == 2) {
210 lineNumber = Int32.Parse (left [0]);
212 endLineNumber = Int32.Parse (left [1]);
215 throw new Exception ("Invalid line/column format.");
216 } else if (temp.Length == 2) {
217 right = temp [1].Split ('-');
218 lineNumber = Int32.Parse (temp [0]);
220 if (right.Length == 1) {
221 columnNumber = Int32.Parse (right [0]);
223 } else if (right.Length == 2) {
224 columnNumber = Int32.Parse (right [0]);
225 endColumnNumber = Int32.Parse (right [0]);
227 throw new Exception ("Invalid line/column format.");
228 } else if (temp.Length == 4) {
229 lineNumber = Int32.Parse (temp [0]);
230 endLineNumber = Int32.Parse (temp [2]);
231 columnNumber = Int32.Parse (temp [1]);
232 endColumnNumber = Int32.Parse (temp [3]);
234 throw new Exception ("Invalid line/column format.");
244 protected virtual string GenerateCommandLineCommands ()
249 protected abstract string GenerateFullPathToTool ();
251 protected virtual string GenerateResponseFileCommands ()
256 protected virtual string GetResponseFileSwitch (string responseFilePath)
258 return String.Format ("@{0}", responseFilePath);
262 protected virtual bool HandleTaskExecutionErrors ()
267 protected virtual HostObjectInitializationStatus InitializeHostObject ()
269 return HostObjectInitializationStatus.NoActionReturnSuccess;
273 protected virtual void LogToolCommand (string message)
278 protected virtual void LogPathToTool (string toolName,
283 protected virtual bool SkipTaskExecution()
288 protected virtual bool ValidateParameters()
293 protected virtual StringDictionary EnvironmentOverride
295 get { return environmentOverride; }
300 public int ExitCode {
301 get { return exitCode; }
304 protected virtual Encoding ResponseFileEncoding
306 get { return responseFileEncoding; }
309 protected virtual Encoding StandardErrorEncoding
311 get { return Console.Error.Encoding; }
314 protected virtual MessageImportance StandardErrorLoggingImportance {
315 get { return standardErrorLoggingImportance; }
318 protected virtual Encoding StandardOutputEncoding
320 get { return Console.Out.Encoding; }
323 protected virtual MessageImportance StandardOutputLoggingImportance {
324 get { return standardOutputLoggingImportance; }
327 public virtual int Timeout
329 get { return timeout; }
330 set { timeout = value; }
333 protected abstract string ToolName
338 public string ToolPath
340 get { return toolPath; }
341 set { toolPath = value; }