Merge pull request #1225 from strawd/bug22307
[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 using System;
31 using System.Collections;
32 using System.Collections.Specialized;
33 using System.Diagnostics;
34 using System.IO;
35 using System.Text;
36 using System.Text.RegularExpressions;
37 using System.Threading;
38 using Microsoft.Build.Framework;
39 using Microsoft.Build.Utilities;
40
41 namespace Microsoft.Build.Tasks {
42         public class Exec : ToolTaskExtension {
43         
44                 string          command;
45                 bool            ignoreExitCode;
46                 ITaskItem[]     outputs;
47                 string          stdErrEncoding;
48                 string          stdOutEncoding;
49                 string          workingDirectory;
50                 string scriptFile;
51
52                 Func<string, bool> errorMatcher, warningMatcher;
53                 
54                 public Exec ()
55                 {
56                         ignoreExitCode = false;
57                 }
58                 
59                 protected internal override void AddCommandLineCommands (CommandLineBuilderExtension commandLine)
60                 {
61                         if (IsRunningOnWindows)
62                                 commandLine.AppendSwitch ("/q /c");
63
64                         if (!String.IsNullOrEmpty (command)) {
65                                 scriptFile = Path.GetTempFileName ();
66                                 if (IsRunningOnWindows)
67                                         scriptFile = scriptFile + ".bat";
68                                 using (StreamWriter sw = new StreamWriter (scriptFile)) {
69                                         sw.Write (command);
70                                 }
71                                 commandLine.AppendFileNameIfNotNull (scriptFile);
72                         }
73                         base.AddCommandLineCommands (commandLine);
74                 }
75
76                 protected override int ExecuteTool (string pathToTool,
77                                                     string responseFileCommands,
78                                                     string commandLineCommands)
79                 {
80                         try {
81                                 errorMatcher = GetTryMatchRegexFunc (CustomErrorRegularExpression, true);
82                                 warningMatcher = GetTryMatchRegexFunc (CustomWarningRegularExpression, false);
83                                 return base.ExecuteTool (pathToTool, responseFileCommands, commandLineCommands);
84                         } finally {
85                                 if (scriptFile != null)
86                                         DeleteTempFile (scriptFile);
87                         }
88                 }
89
90                 [MonoTODO]
91                 protected override string GenerateFullPathToTool ()
92                 {
93                         return IsRunningOnWindows ? "cmd.exe" : "sh";
94                 }
95                 
96                 protected override string GetWorkingDirectory ()
97                 {
98                         return workingDirectory;
99                 }
100                 
101                 protected override bool HandleTaskExecutionErrors ()
102                 {
103                         if (ExitCode != 0)
104                                 Log.LogError ("Command '{0}' exited with code: {1}.", Command, ExitCode);
105
106                         return ExitCode == 0 || ignoreExitCode;
107                 }
108                 
109                 [MonoTODO]
110                 protected override void LogPathToTool (string toolName,
111                                                        string pathToTool)
112                 {
113                 }
114                 
115                 [MonoTODO]
116                 protected override void LogToolCommand (string message)
117                 {
118                         Log.LogMessage (MessageImportance.Normal, "Executing: " + command);
119                 }
120                 
121                 protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance)
122                 {
123                         if (IgnoreStandardErrorWarningFormat ||
124                                 (!errorMatcher (singleLine) && !warningMatcher (singleLine)))
125                                 Log.LogMessage (messageImportance, singleLine);
126                 }
127
128                 // @is_error_type - log as errors, else warnings
129                 Func<string, bool> GetTryMatchRegexFunc (string regex_str, bool is_error_type)
130                 {
131                         bool is_bad = false;
132                         Regex regex = null;
133                         return (singleLine) => {
134                                 if (String.IsNullOrEmpty (regex_str) || is_bad)
135                                         return false;
136
137                                 try {
138                                         if (regex == null)
139                                                 regex = new Regex (regex_str, RegexOptions.Compiled);
140                                 } catch (ArgumentException ae) {
141                                         Log.LogError ("The regular expression specified for '{0}' is invalid : {1}",
142                                                         is_error_type ? "errors" : "warnings", ae.Message);
143                                         Log.LogMessage (MessageImportance.Low, "The regular expression specified for '{0}' is invalid : {1}",
144                                                         is_error_type ? "errors" : "warnings", ae.ToString ());
145
146                                         is_bad = true;
147                                         return false;
148                                 }
149
150                                 if (!regex.Match (singleLine).Success)
151                                         return false;
152
153                                 if (is_error_type)
154                                         Log.LogError (singleLine);
155                                 else
156                                         Log.LogWarning (singleLine);
157                                 return true;
158                         };
159                 }
160
161                 [MonoTODO]
162                 protected override bool ValidateParameters ()
163                 {
164                         return true;
165                 }
166                 
167                 [Required]
168                 public string Command {
169                         get { return command; }
170                         set {
171                                 command = value;
172                                 if (Path.DirectorySeparatorChar == '/')
173                                         command = command.Replace ("\r\n", "\n");
174                         }
175                 }
176
177                 public bool IgnoreExitCode {
178                         get { return ignoreExitCode; }
179                         set { ignoreExitCode = value; }
180                 }
181
182                 [Output]
183                 public ITaskItem[] Outputs {
184                         get { return outputs; }
185                         set { outputs = value; }
186                 }
187
188                 protected override Encoding StandardErrorEncoding {
189                         get { return base.StandardErrorEncoding; }
190                 }
191                 
192                 protected override MessageImportance StandardErrorLoggingImportance {
193                         get { return base.StandardErrorLoggingImportance; }
194                 }
195
196                 protected override Encoding StandardOutputEncoding {
197                         get { return base.StandardOutputEncoding; }
198                 }
199                 
200                 protected override MessageImportance StandardOutputLoggingImportance {
201                         get { return base.StandardOutputLoggingImportance; }
202                 }
203
204                 public bool IgnoreStandardErrorWarningFormat { get; set; }
205
206                 public string CustomErrorRegularExpression { get; set; }
207
208                 public string CustomWarningRegularExpression { get; set; }
209                 
210                 [MonoTODO]
211                 [Output]
212                 public string StdOutEncoding {
213                         get { return stdOutEncoding; }
214                         set { stdOutEncoding = value; }
215                 }
216                 
217                 [MonoTODO]
218                 [Output]
219                 public string StdErrEncoding {
220                         get { return stdErrEncoding; }
221                         set { stdErrEncoding = value; }
222                 }
223                 
224                 [MonoTODO]
225                 protected override string ToolName {
226                         get { return String.Empty; }
227                 }
228
229                 public string WorkingDirectory {
230                         get { return workingDirectory; }
231                         set { workingDirectory = value; }
232                 }
233
234                 static bool IsRunningOnWindows {
235                         get {
236                                 PlatformID pid = Environment.OSVersion.Platform;
237                                 return ((int) pid != 128 && (int) pid != 4 && (int) pid != 6);
238                         }
239                 }
240
241         }
242 }