Merge branch 'master' of github.com:mono/mono
[mono.git] / mcs / class / Microsoft.Build.Utilities / Microsoft.Build.Utilities / ToolTask.cs
index de05e9c71fddcfde086c376ce694ea4a03e7dcb8..c2ccff4c5c6763be8a0dea8a8a06e0043228bbda 100644 (file)
@@ -46,7 +46,6 @@ namespace Microsoft.Build.Utilities
 {
        public abstract class ToolTask : Task
        {
-               SCS.ProcessStringDictionary     environmentOverride;
                int                     exitCode;
                int                     timeout;
                string                  toolPath, toolExe;
@@ -76,7 +75,6 @@ namespace Microsoft.Build.Utilities
                        this.toolPath = MonoLocationHelper.GetBinDir ();
                        this.responseFileEncoding = Encoding.UTF8;
                        this.timeout = Int32.MaxValue;
-                       this.environmentOverride = new SCS.ProcessStringDictionary ();
                }
 
                [MonoTODO]
@@ -111,11 +109,8 @@ namespace Microsoft.Build.Utilities
                        if (pathToTool == null)
                                throw new ArgumentNullException ("pathToTool");
 
-                       string output, error, responseFileName;
-                       StreamWriter outwr, errwr;
-
-                       outwr = errwr = null;
-                       responseFileName = output = error = null;
+                       string responseFileName;
+                       responseFileName = null;
                        toolOutput = new StringBuilder ();
 
                        try {
@@ -129,11 +124,6 @@ namespace Microsoft.Build.Utilities
                                LogToolCommand (String.Format ("Tool {0} execution started with arguments: {1} {2}",
                                                pathToTool, commandLineCommands, responseFileCommands));
 
-                               output = Path.GetTempFileName ();
-                               error = Path.GetTempFileName ();
-                               outwr = new StreamWriter (output);
-                               errwr = new StreamWriter (error);
-
                                ProcessStartInfo pinfo = new ProcessStartInfo (pathToTool, arguments);
                                pinfo.WorkingDirectory = GetWorkingDirectory () ?? Environment.CurrentDirectory;
 
@@ -141,15 +131,27 @@ namespace Microsoft.Build.Utilities
                                pinfo.RedirectStandardOutput = true;
                                pinfo.RedirectStandardError = true;
 
+                               var pendingLineFragmentOutput = new StringBuilder ();
+                               var pendingLineFragmentError = new StringBuilder ();
+                               var environmentOverride = GetAndLogEnvironmentVariables ();
                                try {
-                                       ProcessWrapper pw = ProcessService.StartProcess (pinfo, outwr, errwr, null, environmentOverride);
-                                       pw.OutputStreamChanged += delegate (object o, string msg) { ProcessLine (msg, StandardOutputLoggingImportance); };
-                                       pw.ErrorStreamChanged += delegate (object o, string msg) { ProcessLine (msg, StandardErrorLoggingImportance); };
+                                       // When StartProcess returns, the process has already .Start()'ed
+                                       // If we subscribe to the events after that, then for processes that
+                                       // finish executing before we can subscribe, we won't get the output/err
+                                       // events at all!
+                                       ProcessWrapper pw = ProcessService.StartProcess (pinfo,
+                                                       (_, msg) => ProcessLine (pendingLineFragmentOutput, msg, StandardOutputLoggingImportance),
+                                                       (_, msg) => ProcessLine (pendingLineFragmentError, msg, StandardErrorLoggingImportance),
+                                                       null,
+                                                       environmentOverride);
+
+                                       pw.WaitForOutput (timeout == Int32.MaxValue ? Int32.MaxValue : timeout);
+
+                                       // Process any remaining line
+                                       ProcessLine (pendingLineFragmentOutput, StandardOutputLoggingImportance, true);
+                                       ProcessLine (pendingLineFragmentError, StandardErrorLoggingImportance, true);
 
-                                       pw.WaitForOutput (timeout == Int32.MaxValue ? -1 : timeout);
                                        exitCode = pw.ExitCode;
-                                       outwr.Close();
-                                       errwr.Close();
                                        pw.Dispose ();
                                } catch (System.ComponentModel.Win32Exception e) {
                                        Log.LogError ("Error executing tool '{0}': {1}", pathToTool, e.Message);
@@ -159,17 +161,13 @@ namespace Microsoft.Build.Utilities
                                if (typeLoadException)
                                        ProcessTypeLoadException ();
 
+                               pendingLineFragmentOutput.Length = 0;
+                               pendingLineFragmentError.Length = 0;
+
                                Log.LogMessage (MessageImportance.Low, "Tool {0} execution finished.", pathToTool);
                                return exitCode;
                        } finally {
                                DeleteTempFile (responseFileName);
-                               if (outwr != null)
-                                       outwr.Dispose ();
-                               if (errwr != null)
-                                       errwr.Dispose ();
-
-                               DeleteTempFile (output);
-                               DeleteTempFile (error);
                        }
                }
 
@@ -192,21 +190,54 @@ namespace Microsoft.Build.Utilities
                        Log.LogError (output_str);
                }
 
-               void ProcessLine (string line, MessageImportance importance)
+               void ProcessLine (StringBuilder outputBuilder, MessageImportance importance, bool isLastLine)
+               {
+                       if (outputBuilder.Length == 0)
+                               return;
+
+                       if (isLastLine && !outputBuilder.ToString ().EndsWith (Environment.NewLine))
+                               // last line, but w/o an trailing newline, so add that
+                               outputBuilder.Append (Environment.NewLine);
+
+                       ProcessLine (outputBuilder, null, importance);
+               }
+
+               void ProcessLine (StringBuilder outputBuilder, string line, MessageImportance importance)
                {
-                       toolOutput.AppendLine (line);
+                       // Add to any line fragment from previous call
+                       if (line != null)
+                               outputBuilder.Append (line);
+
+                       // Don't remove empty lines!
+                       var lines = outputBuilder.ToString ().Split (new string [] {Environment.NewLine}, StringSplitOptions.None);
+
+                       // Clear the builder. If any incomplete line is found,
+                       // then it will get added back
+                       outputBuilder.Length = 0;
+                       for (int i = 0; i < lines.Length; i ++) {
+                               string singleLine = lines [i];
+                               if (i == lines.Length - 1 && !singleLine.EndsWith (Environment.NewLine)) {
+                                       // Last line doesn't end in newline, could be part of
+                                       // a bigger line. Save for later processing
+                                       outputBuilder.Append (singleLine);
+                                       continue;
+                               }
 
-                       // in case of typeLoadException, collect all the output
-                       // and then handle in ProcessTypeLoadException
-                       if (!typeLoadException)
-                               LogEventsFromTextOutput (line, importance);
+                               toolOutput.AppendLine (singleLine);
+
+                               // in case of typeLoadException, collect all the output
+                               // and then handle in ProcessTypeLoadException
+                               if (!typeLoadException)
+                                       LogEventsFromTextOutput (singleLine, importance);
+                       }
                }
 
                protected virtual void LogEventsFromTextOutput (string singleLine, MessageImportance importance)
                {
-                       singleLine = singleLine.Trim ();
-                       if (singleLine.Length == 0)
+                       if (singleLine.Length == 0) {
+                               Log.LogMessage (singleLine, importance);
                                return;
+                       }
 
                        if (singleLine.StartsWith ("Unhandled Exception: System.TypeLoadException") ||
                            singleLine.StartsWith ("Unhandled Exception: System.IO.FileNotFoundException")) {
@@ -317,11 +348,50 @@ namespace Microsoft.Build.Utilities
                        }
                }
 
+               // If EnvironmentVariables is defined, then merge EnvironmentOverride
+               // EnvironmentOverride is Obsolete'd in 4.0
+               //
+               // Returns the final set of environment variables and logs them
+               SCS.StringDictionary GetAndLogEnvironmentVariables ()
+               {
+                       var env_vars = GetEnvironmentVariables ();
+                       if (env_vars == null)
+                               return env_vars;
+
+                       Log.LogMessage (MessageImportance.Low, "Environment variables being passed to the tool:");
+                       foreach (DictionaryEntry entry in env_vars)
+                               Log.LogMessage (MessageImportance.Low, "\t{0}={1}", (string)entry.Key, (string)entry.Value);
+
+                       return env_vars;
+               }
+
+               SCS.StringDictionary GetEnvironmentVariables ()
+               {
+                       if (EnvironmentVariables == null || EnvironmentVariables.Length == 0)
+                               return EnvironmentOverride;
+
+                       var env_vars = new SCS.ProcessStringDictionary ();
+                       foreach (string pair in EnvironmentVariables) {
+                               string [] key_value = pair.Split ('=');
+                               if (!String.IsNullOrEmpty (key_value [0]))
+                                       env_vars [key_value [0]] = key_value.Length > 1 ? key_value [1] : String.Empty;
+                       }
+
+                       if (EnvironmentOverride != null)
+                               foreach (DictionaryEntry entry in EnvironmentOverride)
+                                       env_vars [(string)entry.Key] = (string)entry.Value;
+
+                       return env_vars;
+               }
+
                protected virtual StringDictionary EnvironmentOverride
                {
-                       get { return environmentOverride; }
+                       get { return null; }
                }
-               
+
+               // Ignore EnvironmentOverride if this is set
+               public string[] EnvironmentVariables { get; set; }
+
                [Output]
                public int ExitCode {
                        get { return exitCode; }