[xbuild] Exec task - add support for custom error/warning regex.
[mono.git] / mcs / class / Microsoft.Build.Tasks / Microsoft.Build.Tasks / Exec.cs
1 //
2 // Exec.cs: Task that executes commands.
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 //   Ankit Jain (jankit@novell.com)
7 //
8 // (C) 2005 Marek Sieradzki
9 // Copyright 2009 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
30 #if NET_2_0
31
32 using System;
33 using System.Collections;
34 using System.Collections.Specialized;
35 using System.Diagnostics;
36 using System.IO;
37 using System.Text;
38 using System.Text.RegularExpressions;
39 using System.Threading;
40 using Microsoft.Build.Framework;
41 using Microsoft.Build.Utilities;
42
43 namespace Microsoft.Build.Tasks {
44         public class Exec : ToolTaskExtension {
45         
46                 string          command;
47                 bool            ignoreExitCode;
48                 ITaskItem[]     outputs;
49                 string          stdErrEncoding;
50                 string          stdOutEncoding;
51                 string          workingDirectory;
52                 string scriptFile;
53
54 #if NET_4_0
55                 Func<string, bool> errorMatcher, warningMatcher;
56 #endif
57                 
58                 public Exec ()
59                 {
60                         ignoreExitCode = false;
61                 }
62                 
63                 protected internal override void AddCommandLineCommands (CommandLineBuilderExtension commandLine)
64                 {
65                         if (IsRunningOnWindows)
66                                 commandLine.AppendSwitch ("/q /c");
67
68                         if (!String.IsNullOrEmpty (command)) {
69                                 scriptFile = Path.GetTempFileName ();
70                                 if (IsRunningOnWindows)
71                                         scriptFile = scriptFile + ".bat";
72                                 using (StreamWriter sw = new StreamWriter (scriptFile)) {
73                                         sw.Write (command);
74                                 }
75                                 commandLine.AppendFileNameIfNotNull (scriptFile);
76                         }
77                         base.AddCommandLineCommands (commandLine);
78                 }
79
80                 protected override int ExecuteTool (string pathToTool,
81                                                     string responseFileCommands,
82                                                     string commandLineCommands)
83                 {
84                         try {
85 #if NET_4_0
86                                 errorMatcher = GetTryMatchRegexFunc (CustomErrorRegularExpression, true);
87                                 warningMatcher = GetTryMatchRegexFunc (CustomWarningRegularExpression, false);
88 #endif
89                                 return base.ExecuteTool (pathToTool, responseFileCommands, commandLineCommands);
90                         } finally {
91                                 if (scriptFile != null)
92                                         DeleteTempFile (scriptFile);
93                         }
94                 }
95
96                 [MonoTODO]
97                 protected override string GenerateFullPathToTool ()
98                 {
99                         return IsRunningOnWindows ? "cmd.exe" : "sh";
100                 }
101                 
102                 protected override string GetWorkingDirectory ()
103                 {
104                         return workingDirectory;
105                 }
106                 
107                 protected override bool HandleTaskExecutionErrors ()
108                 {
109                         if (ExitCode != 0)
110                                 Log.LogError ("Command '{0}' exited with code: {1}.", Command, ExitCode);
111
112                         return ExitCode == 0 || ignoreExitCode;
113                 }
114                 
115                 [MonoTODO]
116                 protected override void LogPathToTool (string toolName,
117                                                        string pathToTool)
118                 {
119                 }
120                 
121                 [MonoTODO]
122                 protected override void LogToolCommand (string message)
123                 {
124                         Log.LogMessage (MessageImportance.Normal, "Executing: " + command);
125                 }
126                 
127                 protected override void LogEventsFromTextOutput (string singleLine, MessageImportance importance)
128                 {
129 #if NET_4_0
130                         if (IgnoreStandardErrorWarningFormat ||
131                                 (!errorMatcher (singleLine) && !warningMatcher (singleLine)))
132 #endif
133                                 Log.LogMessage (importance, singleLine);
134                 }
135
136 #if NET_4_0
137                 // @is_error_type - log as errors, else warnings
138                 Func<string, bool> GetTryMatchRegexFunc (string regex_str, bool is_error_type)
139                 {
140                         bool is_bad = false;
141                         Regex regex = null;
142                         return (singleLine) => {
143                                 if (String.IsNullOrEmpty (regex_str) || is_bad)
144                                         return false;
145
146                                 try {
147                                         if (regex == null)
148                                                 regex = new Regex (regex_str, RegexOptions.Compiled);
149                                 } catch (ArgumentException ae) {
150                                         Log.LogError ("The regular expression specified for '{0}' is invalid : {1}",
151                                                         is_error_type ? "errors" : "warnings", ae.Message);
152                                         Log.LogMessage (MessageImportance.Low, "The regular expression specified for '{0}' is invalid : {1}",
153                                                         is_error_type ? "errors" : "warnings", ae.ToString ());
154
155                                         is_bad = true;
156                                         return false;
157                                 }
158
159                                 if (!regex.Match (singleLine).Success)
160                                         return false;
161
162                                 if (is_error_type)
163                                         Log.LogError (singleLine);
164                                 else
165                                         Log.LogWarning (singleLine);
166                                 return true;
167                         };
168                 }
169 #endif
170
171                 [MonoTODO]
172                 protected override bool ValidateParameters ()
173                 {
174                         return true;
175                 }
176                 
177                 [Required]
178                 public string Command {
179                         get { return command; }
180                         set {
181                                 command = value;
182                                 if (Path.DirectorySeparatorChar == '/')
183                                         command = command.Replace ("\r\n", "\n");
184                         }
185                 }
186
187                 public bool IgnoreExitCode {
188                         get { return ignoreExitCode; }
189                         set { ignoreExitCode = value; }
190                 }
191
192                 [Output]
193                 public ITaskItem[] Outputs {
194                         get { return outputs; }
195                         set { outputs = value; }
196                 }
197
198                 protected override Encoding StandardErrorEncoding {
199                         get { return base.StandardErrorEncoding; }
200                 }
201                 
202                 protected override MessageImportance StandardErrorLoggingImportance {
203                         get { return base.StandardErrorLoggingImportance; }
204                 }
205
206                 protected override Encoding StandardOutputEncoding {
207                         get { return base.StandardOutputEncoding; }
208                 }
209                 
210                 protected override MessageImportance StandardOutputLoggingImportance {
211                         get { return base.StandardOutputLoggingImportance; }
212                 }
213
214 #if NET_4_0
215                 public bool IgnoreStandardErrorWarningFormat { get; set; }
216
217                 public string CustomErrorRegularExpression { get; set; }
218
219                 public string CustomWarningRegularExpression { get; set; }
220 #endif
221                 
222                 [MonoTODO]
223                 [Output]
224                 public string StdOutEncoding {
225                         get { return stdOutEncoding; }
226                         set { stdOutEncoding = value; }
227                 }
228                 
229                 [MonoTODO]
230                 [Output]
231                 public string StdErrEncoding {
232                         get { return stdErrEncoding; }
233                         set { stdErrEncoding = value; }
234                 }
235                 
236                 [MonoTODO]
237                 protected override string ToolName {
238                         get { return String.Empty; }
239                 }
240
241                 public string WorkingDirectory {
242                         get { return workingDirectory; }
243                         set { workingDirectory = value; }
244                 }
245
246                 static bool IsRunningOnWindows {
247                         get {
248                                 PlatformID pid = Environment.OSVersion.Platform;
249                                 return ((int) pid != 128 && (int) pid != 4 && (int) pid != 6);
250                         }
251                 }
252
253         }
254 }
255
256 #endif