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;
33 using System.Collections.Specialized;
35 using System.Resources;
37 using System.Text.RegularExpressions;
38 using Microsoft.Build.Framework;
39 using Mono.XBuild.Utilities;
41 namespace Microsoft.Build.Utilities
43 public abstract class ToolTask : Task
45 StringDictionary environmentOverride;
50 Encoding responseFileEncoding;
51 MessageImportance standardErrorLoggingImportance;
52 MessageImportance standardOutputLoggingImportance;
59 this.standardErrorLoggingImportance = MessageImportance.High;
60 this.standardOutputLoggingImportance = MessageImportance.Normal;
63 protected ToolTask (ResourceManager taskResources)
64 : this (taskResources, null)
68 protected ToolTask (ResourceManager taskResources,
69 string helpKeywordPrefix)
71 this.TaskResources = taskResources;
72 this.HelpKeywordPrefix = helpKeywordPrefix;
73 this.toolPath = MonoLocationHelper.GetBinDir ();
74 this.responseFileEncoding = Encoding.UTF8;
81 + @"(((?<ORIGIN>(((\d+>)?[a-zA-Z]?:[^:]*)|([^:]*))):)"
83 + "(?<SUBCATEGORY>(()|([^:]*? )))"
84 + "(?<CATEGORY>(error|warning)) "
87 RegexOptions.IgnoreCase);
91 protected virtual bool CallHostObjectToExecute ()
97 // FIXME: it should write responseFileCommands to temporary response file
98 protected virtual int ExecuteTool (string pathToTool,
99 string responseFileCommands,
100 string commandLineCommands)
102 return RealExecute (pathToTool, responseFileCommands, commandLineCommands) ? 0 : -1;
105 public override bool Execute ()
107 if (SkipTaskExecution ())
110 int result = ExecuteTool (GenerateFullPathToTool (), GenerateResponseFileCommands (),
111 GenerateCommandLineCommands ());
117 protected virtual string GetWorkingDirectory ()
122 private bool RealExecute (string pathToTool,
123 string responseFileCommands,
124 string commandLineCommands)
127 if (pathToTool == null)
128 throw new ArgumentNullException ("pathToTool");
130 if (!File.Exists (pathToTool)) {
131 Log.LogError ("Unable to find tool {0} at '{1}'", ToolName, pathToTool);
135 string responseFileName = Path.GetTempFileName ();
136 File.WriteAllText (responseFileName, responseFileCommands);
138 string arguments = String.Concat (commandLineCommands, " ", GetResponseFileSwitch (responseFileName));
140 Log.LogMessage (MessageImportance.Normal, String.Format ("Tool {0} execution started with arguments: {1} {2}",
141 pathToTool, commandLineCommands, responseFileCommands));
143 string output = Path.GetTempFileName ();
144 string error = Path.GetTempFileName ();
145 StreamWriter outwr = new StreamWriter (output);
146 StreamWriter errwr = new StreamWriter (error);
148 ProcessStartInfo pinfo = new ProcessStartInfo (pathToTool, arguments);
149 pinfo.WorkingDirectory = GetWorkingDirectory () ?? Environment.CurrentDirectory;
151 pinfo.UseShellExecute = false;
152 pinfo.RedirectStandardOutput = true;
153 pinfo.RedirectStandardError = true;
156 ProcessWrapper pw = ProcessService.StartProcess (pinfo, outwr, errwr, null, environmentOverride);
158 exitCode = pw.ExitCode;
162 } catch (System.ComponentModel.Win32Exception e) {
163 Log.LogError ("Error executing tool '{0}': {1}", pathToTool, e.Message);
167 foreach (string s in new string[] { output, error }) {
168 using (StreamReader sr = File.OpenText (s)) {
170 while ((line = sr.ReadLine ()) != null) {
171 LogEventsFromTextOutput (line, MessageImportance.Low);
176 Log.LogMessage (MessageImportance.Low, String.Format ("Tool {0} execution finished.", pathToTool));
178 return !Log.HasLoggedErrors;
183 protected virtual void LogEventsFromTextOutput (string singleLine, MessageImportance importance)
185 if (String.IsNullOrEmpty (singleLine))
188 string filename, origin, category, code, subcategory, text;
189 int lineNumber, columnNumber, endLineNumber, endColumnNumber;
191 Match m = regex.Match (singleLine);
192 origin = m.Groups [regex.GroupNumberFromName ("ORIGIN")].Value;
193 category = m.Groups [regex.GroupNumberFromName ("CATEGORY")].Value;
194 code = m.Groups [regex.GroupNumberFromName ("CODE")].Value;
195 subcategory = m.Groups [regex.GroupNumberFromName ("SUBCATEGORY")].Value;
196 text = m.Groups [regex.GroupNumberFromName ("TEXT")].Value;
198 ParseOrigin (origin, out filename, out lineNumber, out columnNumber, out endLineNumber, out endColumnNumber);
200 if (category == "warning") {
201 Log.LogWarning (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
202 endColumnNumber, text, null);
203 } else if (category == "error") {
204 Log.LogError (subcategory, code, null, filename, lineNumber, columnNumber, endLineNumber,
205 endColumnNumber, text, null);
209 private void ParseOrigin (string origin, out string filename,
210 out int lineNumber, out int columnNumber,
211 out int endLineNumber, out int endColumnNumber)
215 string[] left, right;
217 if (origin.IndexOf ('(') != -1 ) {
218 lParen = origin.IndexOf ('(');
219 filename = origin.Substring (0, lParen);
220 temp = origin.Substring (lParen + 1, origin.Length - lParen - 2).Split (',');
221 if (temp.Length == 1) {
222 left = temp [0].Split ('-');
223 if (left.Length == 1) {
224 lineNumber = Int32.Parse (left [0]);
228 } else if (left.Length == 2) {
229 lineNumber = Int32.Parse (left [0]);
231 endLineNumber = Int32.Parse (left [1]);
234 throw new Exception ("Invalid line/column format.");
235 } else if (temp.Length == 2) {
236 right = temp [1].Split ('-');
237 lineNumber = Int32.Parse (temp [0]);
239 if (right.Length == 1) {
240 columnNumber = Int32.Parse (right [0]);
242 } else if (right.Length == 2) {
243 columnNumber = Int32.Parse (right [0]);
244 endColumnNumber = Int32.Parse (right [0]);
246 throw new Exception ("Invalid line/column format.");
247 } else if (temp.Length == 4) {
248 lineNumber = Int32.Parse (temp [0]);
249 endLineNumber = Int32.Parse (temp [2]);
250 columnNumber = Int32.Parse (temp [1]);
251 endColumnNumber = Int32.Parse (temp [3]);
253 throw new Exception ("Invalid line/column format.");
264 protected virtual string GenerateCommandLineCommands ()
269 protected abstract string GenerateFullPathToTool ();
272 protected virtual string GenerateResponseFileCommands ()
278 protected virtual string GetResponseFileSwitch (string responseFilePath)
280 return String.Format ("@{0}", responseFilePath);
284 protected virtual bool HandleTaskExecutionErrors ()
289 protected virtual HostObjectInitializationStatus InitializeHostObject ()
291 return HostObjectInitializationStatus.NoActionReturnSuccess;
295 protected virtual void LogToolCommand (string message)
300 protected virtual void LogPathToTool (string toolName,
305 protected virtual bool SkipTaskExecution()
310 protected virtual bool ValidateParameters()
315 protected virtual StringDictionary EnvironmentOverride
317 get { return environmentOverride; }
322 public int ExitCode {
323 get { return exitCode; }
326 protected virtual Encoding ResponseFileEncoding
328 get { return responseFileEncoding; }
331 protected virtual Encoding StandardErrorEncoding
333 get { return Console.Error.Encoding; }
336 protected virtual MessageImportance StandardErrorLoggingImportance {
337 get { return standardErrorLoggingImportance; }
340 protected virtual Encoding StandardOutputEncoding
342 get { return Console.Out.Encoding; }
345 protected virtual MessageImportance StandardOutputLoggingImportance {
346 get { return standardOutputLoggingImportance; }
349 public virtual int Timeout
351 get { return timeout; }
352 set { timeout = value; }
355 protected abstract string ToolName
360 public string ToolPath
362 get { return toolPath; }
363 set { toolPath = value; }