diff options
author | Lluis Sanchez <lluis@xamarin.com> | 2016-03-01 12:06:10 +0300 |
---|---|---|
committer | Lluis Sanchez <lluis@xamarin.com> | 2016-03-01 17:38:07 +0300 |
commit | 2ec4446e7f4491d13723d1648f70e008e858131d (patch) | |
tree | 7b37ac8105207885e041dfc86357f6dbda67f1f1 /main/src | |
parent | 78e0ff336e0c12e00a18182eb461e07d8f10e6b9 (diff) |
Fix serialization issues in BinaryMessage and related classes
Changed the way dictionaries are serialized. Now dictionaries of any
type are supported, not only <string,object>. Also arrays of any type
are supported.
Improved exception catching and logging.
Diffstat (limited to 'main/src')
15 files changed, 1286 insertions, 59 deletions
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/BinaryMessage.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/BinaryMessage.cs index 697dcdcf35..bb83d11c39 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/BinaryMessage.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/BinaryMessage.cs @@ -81,9 +81,7 @@ namespace MonoDevelop.Core.Execution public T GetArgument<T> (string name) { var r = GetArgument (name); - if ((r is IDictionary<string, object>) && IsSerializableType (typeof(T))) - return (T)(object)ReadMessageData (typeof(T), (Dictionary<string, object>)r); - return (T)r; + return (T)ConvertToType (r, typeof (T)); } public List<BinaryMessageArgument> Args { @@ -92,6 +90,7 @@ namespace MonoDevelop.Core.Execution protected BinaryMessage () { + Id = System.Threading.Interlocked.Increment (ref nextId); } public BinaryMessage (string name) @@ -201,22 +200,22 @@ namespace MonoDevelop.Core.Execution } else if (val is TimeSpan) { bw.Write ((byte)TypeCode.TimeSpan); bw.Write (((TimeSpan)val).Ticks); - } else if (val is IDictionary<string, object>) { - bw.Write ((byte)TypeCode.Map); - var dict = (IDictionary<string, object>)val; - bw.Write (dict.Count); - foreach (var e in dict) { - bw.Write (e.Key); - WriteValue (bw, e.Value); - } - } else if (val is IDictionary<string, string>) { + } else if (val.GetType ().IsGenericType && val.GetType ().GetGenericTypeDefinition () == typeof(Dictionary<,>)) { bw.Write ((byte)TypeCode.Map); - var dict = (IDictionary<string, string>)val; - bw.Write (dict.Count); - foreach (var e in dict) { - bw.Write (e.Key); - WriteValue (bw, e.Value); + var t = val.GetType (); + var keyType = t.GetGenericArguments () [0]; + var valueType = t.GetGenericArguments () [1]; + var dict = (IDictionary)val; + var keys = Array.CreateInstance (keyType, dict.Count); + var values = Array.CreateInstance (valueType, dict.Count); + int n = 0; + foreach (DictionaryEntry e in dict) { + keys.SetValue (e.Key, n); + values.SetValue (e.Value, n); + n++; } + WriteArray (bw, keys); + WriteArray (bw, values); } else if (val.GetType ().IsEnum) { WriteValue (bw, Convert.ToInt64(val)); } else { @@ -303,11 +302,8 @@ namespace MonoDevelop.Core.Execution switch ((TypeCode)t) { case TypeCode.Null: return null; - case TypeCode.Array: { - int n = br.ReadInt32 (); - var et = (TypeCode)br.ReadByte (); - return ReadArray (br, et, n); - } + case TypeCode.Array: + return ReadArray (br); case TypeCode.Double: return br.ReadDouble (); case TypeCode.Byte: @@ -329,21 +325,25 @@ namespace MonoDevelop.Core.Execution case TypeCode.TimeSpan: return new TimeSpan (br.ReadInt64 ()); case TypeCode.Map: { - Dictionary<string, object> dict = new Dictionary<string, object> (); - int size = br.ReadInt32 (); - while (size-- > 0) { - string key = br.ReadString (); - object value = ReadValue (br); - dict [key] = value; - } + var keys = (Array)ReadArray (br); + var values = (Array)ReadArray (br); + var dt = typeof (Dictionary<,>); + var dictType = dt.MakeGenericType (keys.GetType ().GetElementType (), values.GetType ().GetElementType ()); + + var dict = (IDictionary)Activator.CreateInstance (dictType); + for (int n = 0; n < keys.Length; n++) + dict [keys.GetValue (n)] = values.GetValue (n); return dict; } } throw new NotSupportedException ("code: " + t); } - static object ReadArray (BinaryReader br, TypeCode type, int count) + static object ReadArray (BinaryReader br) { + int count = br.ReadInt32 (); + var type = (TypeCode)br.ReadByte (); + switch (type) { case TypeCode.Object: { var a = new object [count]; @@ -414,7 +414,7 @@ namespace MonoDevelop.Core.Execution bool IsSerializableType (Type type) { - return Type.GetTypeCode (type) == System.TypeCode.Object && !type.IsArray && !typeof(IDictionary<string, string>).IsAssignableFrom (type); + return Type.GetTypeCode (type) == System.TypeCode.Object && !type.IsArray && !typeof(IDictionary).IsAssignableFrom (type); } public override string ToString () @@ -470,7 +470,7 @@ namespace MonoDevelop.Core.Execution } else { map = new TypeMap (); foreach (var m in type.GetMembers (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { - var at = m.GetCustomAttribute<MessageDataPropertyAttribute> (); + var at = (MessageDataPropertyAttribute)Attribute.GetCustomAttribute (m, typeof(MessageDataPropertyAttribute)); if (at == null || (!(m is PropertyInfo) && !(m is FieldInfo))) continue; map [at.Name ?? m.Name] = m; @@ -492,24 +492,22 @@ namespace MonoDevelop.Core.Execution var m = e.Value; object val = GetArgument (e.Key); if (m is PropertyInfo) { - if (val is IDictionary<string, object> && IsSerializableType (((PropertyInfo)m).PropertyType)) - val = ReadMessageData (((PropertyInfo)m).PropertyType, (IDictionary<string, object>)val); + val = ConvertToType (val, ((PropertyInfo)m).PropertyType); ((PropertyInfo)m).SetValue (this, val, null); } else { - if (val is IDictionary<string, object> && IsSerializableType (((FieldInfo)m).FieldType)) - val = ReadMessageData (((FieldInfo)m).FieldType, (IDictionary<string, object>)val); + val = ConvertToType (val, ((FieldInfo)m).FieldType); ((FieldInfo)m).SetValue (this, val); } } } - object ReadMessageData (Type type, IDictionary<string, object> data) + object ReadMessageData (Type type, Dictionary<string, object> data) { TypeMap map = GetTypeMap (type); if (map == null) throw new InvalidOperationException ("Type '" + type.FullName + "' can't be read from message data. The type must have the [MessageDataType] attribute applied to it"); - var result = Activator.CreateInstance (type); + var result = Activator.CreateInstance (type, true); foreach (var e in map) { var m = e.Value; object val; @@ -530,9 +528,29 @@ namespace MonoDevelop.Core.Execution object ConvertToType (object ob, Type type) { - if (ob is IDictionary<string, object> && IsSerializableType (type)) - return ReadMessageData (type, (IDictionary<string, object>)ob); - + if (ob == null) + return null; + + if (type.IsAssignableFrom (ob.GetType ())) + return ob; + + if (ob is Dictionary<string, object> && IsSerializableType (type)) + return ReadMessageData (type, (Dictionary<string, object>)ob); + + if (ob.GetType ().IsGenericType && ob.GetType().GetGenericTypeDefinition () == typeof(Dictionary<,>)) { + if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof(Dictionary<,>)) { + var targs = type.GetGenericArguments (); + var keyType = targs [0]; + var elemType = targs [1]; + var dict = (IDictionary)Activator.CreateInstance (type); + foreach (DictionaryEntry e in (IDictionary)ob) { + var key = ConvertToType (e.Key, keyType); + var val = ConvertToType (e.Value, elemType); + dict [key] = val; + } + return dict; + } + } if (type.IsEnum && !ob.GetType ().IsEnum) { return Enum.ToObject (type, ob); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs index 5c2fb53d67..6f01b43fd6 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs @@ -375,12 +375,12 @@ namespace MonoDevelop.Core.Execution message.Write (connectionStream); connectionStream.Flush (); - } - catch { + } + catch (Exception ex){ if (connection == null || (!connection.Connected && status == ConnectionStatus.Connected)) { AbortConnection ("Disconnected from remote process due to a communication error", isAsync: true); } else - ProcessResponse (message.CreateErrorResponse ("Connection is closed")); + ProcessResponse (message.CreateErrorResponse (ex.ToString ())); } } @@ -528,19 +528,27 @@ namespace MonoDevelop.Core.Execution MessageRequest req; if (messageWaiters.TryGetValue (msg.Id, out req)) { messageWaiters.Remove (msg.Id); - var rt = req.Request.GetResponseType (); - if (rt != typeof (BinaryMessage)) { - var resp = (BinaryMessage)Activator.CreateInstance (rt); - resp.CopyFrom (msg); - msg = resp; + try { + var rt = req.Request.GetResponseType (); + if (rt != typeof (BinaryMessage)) { + var resp = (BinaryMessage)Activator.CreateInstance (rt); + resp.CopyFrom (msg); + msg = resp; + } + } catch (Exception ex) { + msg = msg.CreateErrorResponse (ex.ToString ()); + } + + if (DebugMode) { + var time = (int)(respTime - req.Request.SentTime).TotalMilliseconds; + LogMessage (MessageType.Response, msg, time); } + if (!req.Request.OneWay) NotifyResponse (req, msg); } - if (DebugMode) { - var time = req != null ? (int)(respTime - req.Request.SentTime).TotalMilliseconds : -1; - LogMessage (MessageType.Response, msg, time); - } + else if (DebugMode) + LogMessage (MessageType.Response, msg, -1); } } @@ -593,15 +601,15 @@ namespace MonoDevelop.Core.Execution Console.Write ("[" + (Environment.TickCount - tickBase) + "] "); if (type == MessageType.Request) - Console.WriteLine ("XS >> RP " + msg); + Console.WriteLine ("[CLIENT] XS >> RP " + msg); else if (type == MessageType.Response) { if (time != -1) - Console.WriteLine ("XS << RP " + time + "ms " + msg); + Console.WriteLine ("[CLIENT] XS << RP " + time + "ms " + msg); else - Console.WriteLine ("XS << RP " + msg); + Console.WriteLine ("[CLIENT] XS << RP " + msg); } else - Console.WriteLine ("XS <- RP " + msg); + Console.WriteLine ("[CLIENT] XS <- RP " + msg); } void PingConnection (object state) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessServer.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessServer.cs index dfe7dc51e7..5a4425ef76 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessServer.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessServer.cs @@ -159,7 +159,7 @@ namespace MonoDevelop.Core.Execution msg = LoadMessageData (msg); if (DebugMode) { String mtype = type == MESSAGE_QUEUE_END ? "[M] " : "[Q] "; - Console.WriteLine ("XS >> RP " + mtype + msg); + Console.WriteLine ("[SERVER] XS >> RP " + mtype + msg); } } catch (Exception e) { Console.WriteLine (e); @@ -208,7 +208,7 @@ namespace MonoDevelop.Core.Execution msg.ReadCustomData (); lock (listeners) { if (DebugMode) - Console.WriteLine ("XS << RP " + type + " [" + msg.ProcessingTime + "ms] " + msg); + Console.WriteLine ("[SERVER] XS << RP " + type + " [" + msg.ProcessingTime + "ms] " + msg); outStream.WriteByte (type); try { msg.Write (outStream); @@ -297,6 +297,7 @@ namespace MonoDevelop.Core.Execution Console.WriteLine (ex); } if (response != null) { + response.Id = msg.Id; response.ProcessingTime = sw.ElapsedMilliseconds; server.SendResponse (response); } else if (!msg.OneWay) diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/BuildEngine.Shared.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/BuildEngine.Shared.cs new file mode 100644 index 0000000000..5a55c5cbb2 --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/BuildEngine.Shared.cs @@ -0,0 +1,320 @@ +// +// ProjectBuilder.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Threading; +using System.Runtime.Remoting; +using System.Collections.Generic; +using System.Globalization; +using System.IO; + +//this is the builder for the deprecated build engine API +using MonoDevelop.Core.Execution; +using System.Net.Configuration; +using System.Diagnostics; +#pragma warning disable 618 + +namespace MonoDevelop.Projects.MSBuild +{ + partial class BuildEngine + { + static readonly AutoResetEvent workDoneEvent = new AutoResetEvent (false); + static ThreadStart workDelegate; + static readonly object workLock = new object (); + static Thread workThread; + static Exception workError; + + static List<int> cancelledTasks = new List<int> (); + static int currentTaskId; + static int projectIdCounter; + Dictionary<int, ProjectBuilder> projects = new Dictionary<int, ProjectBuilder> (); + + readonly ManualResetEvent doneEvent = new ManualResetEvent (false); + + static RemoteProcessServer server; + + internal WaitHandle WaitHandle { + get { return doneEvent; } + } + + public class LogWriter: ILogWriter + { + int id; + + public LogWriter (int loggerId) + { + this.id = loggerId; + } + + public void Write (string text) + { + server.SendMessage (new LogMessage { LoggerId = id, Text = text }); + } + } + + public class NullLogWriter: ILogWriter + { + public void Write (string text) + { + } + } + + void WatchProcess (int procId) + { + var t = new Thread (delegate () { + while (true) { + Thread.Sleep (1000); + try { + // Throws exception if process is not running. + // When watching a .NET process from Mono, GetProcessById may + // return the process with HasExited=true + Process p = Process.GetProcessById (procId); + if (p.HasExited) + break; + } + catch { + break; + } + } + doneEvent.Set (); + }); + t.IsBackground = true; + t.Start (); + } + + public BuildEngine (RemoteProcessServer pserver) + { + server = pserver; + } + + [MessageHandler] + public BinaryMessage Initialize (InitializeRequest msg) + { + WatchProcess (msg.IdeProcessId); + SetCulture (CultureInfo.GetCultureInfo (msg.CultureName)); + SetGlobalProperties (msg.GlobalProperties); + return msg.CreateResponse (); + } + + [MessageHandler] + public LoadProjectResponse LoadProject (LoadProjectRequest msg) + { + var pb = LoadProject (msg.ProjectFile); + lock (projects) { + var id = ++projectIdCounter; + projects [id] = pb; + return new LoadProjectResponse { ProjectId = id }; + } + } + + [MessageHandler] + public BinaryMessage UnloadProject (UnloadProjectRequest msg) + { + ProjectBuilder pb = null; + lock (projects) { + if (projects.TryGetValue (msg.ProjectId, out pb)) + projects.Remove (msg.ProjectId); + } + if (pb != null) + UnloadProject (pb); + return msg.CreateResponse (); + } + + [MessageHandler] + public BinaryMessage Dispose (DisposeRequest msg) + { + doneEvent.Set (); + return msg.CreateResponse (); + } + + [MessageHandler] + public BinaryMessage Ping (PingRequest msg) + { + return msg.CreateResponse (); + } + + [MessageHandler] + public BinaryMessage CancelTask (CancelTaskRequest msg) + { + lock (cancelledTasks) { + if (currentTaskId == msg.TaskId) + AbortCurrentTask (); + else + cancelledTasks.Add (msg.TaskId); + } + return msg.CreateResponse (); + } + + [MessageHandler] + public BinaryMessage SetGlobalProperties (SetGlobalPropertiesRequest msg) + { + SetGlobalProperties (msg.Properties); + return msg.CreateResponse (); + } + + [MessageHandler] + public BinaryMessage DisposeProject (DisposeProjectRequest msg) + { + var pb = GetProject (msg.ProjectId); + if (pb != null) + pb.Dispose (); + return msg.CreateResponse (); + } + + [MessageHandler] + public BinaryMessage RefreshProject (RefreshProjectRequest msg) + { + var pb = GetProject (msg.ProjectId); + if (pb != null) + pb.Refresh (); + return msg.CreateResponse (); + } + + [MessageHandler] + public BinaryMessage RefreshWithContent (RefreshWithContentRequest msg) + { + var pb = GetProject (msg.ProjectId); + if (pb != null) + pb.RefreshWithContent (msg.Content); + return msg.CreateResponse (); + } + + [MessageHandler] + public BinaryMessage RunProject (RunProjectRequest msg) + { + var pb = GetProject (msg.ProjectId); + if (pb != null) { + var logger = msg.LogWriterId != -1 ? (ILogWriter) new LogWriter (msg.LogWriterId) : (ILogWriter) new NullLogWriter (); + var res = pb.Run (msg.Configurations, logger, msg.Verbosity, msg.RunTargets, msg.EvaluateItems, msg.EvaluateProperties, msg.GlobalProperties, msg.TaskId); + return new RunProjectResponse { Result = res }; + } + return msg.CreateResponse (); + } + + ProjectBuilder GetProject (int projectId) + { + lock (projects) { + ProjectBuilder pb; + if (projects.TryGetValue (projectId, out pb)) + return pb; + return null; + } + } + + static bool IsTaskCancelled (int taskId) + { + lock (cancelledTasks) { + return cancelledTasks.Contains (taskId); + } + } + + static bool SetCurrentTask (int taskId) + { + lock (cancelledTasks) { + if (cancelledTasks.Contains (taskId)) + return false; + currentTaskId = taskId; + return true; + } + } + + static void ResetCurrentTask () + { + lock (cancelledTasks) { + currentTaskId = -1; + } + } + + static void AbortCurrentTask () + { + workThread.Abort (); + workThread = null; + workDoneEvent.Set (); + } + + internal static void RunSTA (ThreadStart ts) + { + RunSTA (-1, ts); + } + + internal static void RunSTA (int taskId, ThreadStart ts) + { + lock (workLock) { + if (IsTaskCancelled (taskId)) + return; + lock (threadLock) { + // Last chance to check for canceled task before the thread is started + if (IsTaskCancelled (taskId)) + return; + + workDelegate = ts; + workError = null; + if (workThread == null) { + workThread = new Thread (STARunner); + workThread.SetApartmentState (ApartmentState.STA); + workThread.IsBackground = true; + workThread.CurrentUICulture = uiCulture; + workThread.Start (); + } + else + // Awaken the existing thread + Monitor.Pulse (threadLock); + } + if (!SetCurrentTask (taskId)) { + // The task was aborted after all. Since the thread is already running we need to abort it + AbortCurrentTask (); + return; + } + + workDoneEvent.WaitOne (); + + ResetCurrentTask (); + } + if (workError != null) + throw new Exception ("MSBuild operation failed", workError); + } + + static readonly object threadLock = new object (); + + static void STARunner () + { + lock (threadLock) { + do { + try { + workDelegate (); + } + catch (Exception ex) { + workError = ex; + } + workDoneEvent.Set (); + } + while (Monitor.Wait (threadLock, 60000)); + + workThread = null; + } + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/IBuildEngine.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/IBuildEngine.cs new file mode 100644 index 0000000000..b3ef0b3c21 --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/IBuildEngine.cs @@ -0,0 +1,43 @@ +// +// IProjectBuilder.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Globalization; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace MonoDevelop.Projects.MSBuild +{ + public interface IBuildEngine: IDisposable + { + Task SetCulture (CultureInfo uiCulture); + Task SetGlobalProperties (IDictionary<string, string> properties); + Task<IProjectBuilder> LoadProject (string projectFile); + Task UnloadProject (IProjectBuilder pb); + Task Ping (); + Task CancelTask (int taskId); + } +} diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/ILogWriter.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/ILogWriter.cs new file mode 100644 index 0000000000..6e8ec1225b --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/ILogWriter.cs @@ -0,0 +1,57 @@ +// +// ILogWriter.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.IO; +using System.Runtime.Remoting.Messaging; + +namespace MonoDevelop.Projects.MSBuild +{ + public interface ILogWriter + { + void Write (string text); + } + + public class LogWriter: MarshalByRefObject, ILogWriter + { + TextWriter writer; + + public LogWriter (TextWriter writer) + { + this.writer = writer; + } + + public void Write (string text) + { + writer.Write (text); + } + + public override object InitializeLifetimeService () + { + return null; + } + } +} diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/IProjectBuilder.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/IProjectBuilder.cs new file mode 100644 index 0000000000..8acbb5aa63 --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/IProjectBuilder.cs @@ -0,0 +1,44 @@ +// +// IProjectBuilder.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// Michael Hutchinson <m.j.hutchinson@gmail.com> +// +// Copyright (c) 2009-2011 Novell, Inc (http://www.novell.com) +// Copyright (c) 2011-2015 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace MonoDevelop.Projects.MSBuild +{ + public interface IProjectBuilder : IDisposable + { + Task Refresh (); + Task RefreshWithContent (string projectContent); + Task<MSBuildResult> Run ( + ProjectConfigurationInfo[] configurations, ILogWriter logWriter, MSBuildVerbosity verbosity, + string[] runTargets, string[] evaluateItems, string[] evaluateProperties, Dictionary<string,string> globalProperties, int taskId + ); + } +} diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MSBuildEvaluatedItem.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MSBuildEvaluatedItem.cs new file mode 100644 index 0000000000..f948e28109 --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MSBuildEvaluatedItem.cs @@ -0,0 +1,56 @@ +// +// MSBuildEvaluatedItem.cs +// +// Author: +// Michael Hutchinson <m.j.hutchinson@gmail.com> +// +// Copyright (c) 2014 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using MonoDevelop.Core.Execution; + +namespace MonoDevelop.Projects.MSBuild +{ + [MessageDataTypeAttribute] + class MSBuildEvaluatedItem + { + public MSBuildEvaluatedItem () + { + } + + public MSBuildEvaluatedItem (string name, string itemSpec) + { + Name = name; + ItemSpec = itemSpec; + Metadata = new Dictionary<string, string> (); + } + + [MessageDataProperty] + public Dictionary<string,string> Metadata { get; set; } + + [MessageDataProperty] + public string ItemSpec { get; set; } + + [MessageDataProperty] + public string Name { get; set; } + } +} diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MSBuildResult.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MSBuildResult.cs new file mode 100644 index 0000000000..e68b995cf9 --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MSBuildResult.cs @@ -0,0 +1,69 @@ +// +// MSBuildResult.cs +// +// Author: +// Michael Hutchinson <m.j.hutchinson@gmail.com> +// +// Copyright (c) 2014 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using MonoDevelop.Core.Execution; + +namespace MonoDevelop.Projects.MSBuild +{ + [MessageDataTypeAttribute] + class MSBuildResult + { + [MessageDataProperty] + MSBuildTargetResult[] errors; + + [MessageDataProperty] + Dictionary<string,string> properties; + + [MessageDataProperty] + Dictionary<string,MSBuildEvaluatedItem[]> items; + + internal MSBuildResult () + { + } + + public MSBuildResult (MSBuildTargetResult[] errors) + { + this.errors = errors; + this.properties = new Dictionary<string,string> (); + this.items = new Dictionary<string,MSBuildEvaluatedItem[]> (); + } + + public MSBuildTargetResult[] Errors { + get { return errors; } + } + + public Dictionary<string,MSBuildEvaluatedItem[]> Items { + get { return items; } + } + + public Dictionary<string, string> Properties { + get { return properties; } + } + } + +} diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MSBuildTargetResult.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MSBuildTargetResult.cs new file mode 100644 index 0000000000..df0080b9ac --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MSBuildTargetResult.cs @@ -0,0 +1,140 @@ +// +// MSBuildTargetResult.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Text; +using MonoDevelop.Core.Execution; + +namespace MonoDevelop.Projects.MSBuild +{ + [MessageDataType] + class MSBuildTargetResult + { + public MSBuildTargetResult () + { + } + + public MSBuildTargetResult ( + string projectFile, bool isWarning, string subcategory, string code, string file, + int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string message, string helpKeyword) + { + ProjectFile = projectFile; + IsWarning = isWarning; + Subcategory = subcategory; + Code = code; + File = file; + LineNumber = lineNumber; + ColumnNumber = columnNumber; + EndLineNumber = endLineNumber; + EndColumnNumber = endColumnNumber; + Message = message; + HelpKeyword = helpKeyword; + } + + [MessageDataProperty] + public string ProjectFile { get; set; } + + [MessageDataProperty] + public bool IsWarning { get; set; } + + [MessageDataProperty] + public string Subcategory { get; set; } + + [MessageDataProperty] + public string Code { get; set; } + + [MessageDataProperty] + public string File { get; set; } + + [MessageDataProperty] + public int LineNumber { get; set; } + + [MessageDataProperty] + public int ColumnNumber { get; set; } + + [MessageDataProperty] + public int EndLineNumber { get; set; } + + [MessageDataProperty] + public int EndColumnNumber { get; set; } + + [MessageDataProperty] + public string Message { get; set; } + + [MessageDataProperty] + public string HelpKeyword { get; set; } + + public override string ToString () + { + var sb = new StringBuilder (); + if (!string.IsNullOrEmpty (File)) { + sb.Append (File); + if (LineNumber > 0) { + //(line) + sb.Append ("("); + sb.Append (LineNumber); + if (ColumnNumber > 0) { + //(line,col) + sb.Append (","); + sb.Append (ColumnNumber); + if (EndColumnNumber > 0) { + if (EndLineNumber > 0) { + //(line,col,line,col) + sb.Append (","); + sb.Append (EndLineNumber); + sb.Append (","); + sb.Append (EndColumnNumber); + } else { + //(line,col-col) + sb.Append ("-"); + sb.Append (EndColumnNumber); + } + } + } else if (EndLineNumber > 0) { + //(line-line) + sb.Append ("-"); + sb.Append (EndLineNumber); + } + sb.Append (")"); + } + sb.Append (": "); + } + if (!string.IsNullOrEmpty (Subcategory)) { + sb.Append (Subcategory); + sb.Append (" "); + } + sb.Append (IsWarning ? "warning" : "error"); + if (!string.IsNullOrEmpty (Code)) { + sb.Append (" "); + sb.Append (Code); + } + sb.Append (": "); + sb.Append (Message); + return sb.ToString (); + } + } +} diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/Main.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/Main.cs new file mode 100644 index 0000000000..cfc5126fd2 --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/Main.cs @@ -0,0 +1,50 @@ +// +// Main.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.IO; +using System.Collections; +using System.Runtime.Serialization.Formatters.Binary; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Channels; +using System.Runtime.Remoting.Channels.Tcp; +using System.Threading; +using System.Diagnostics; +using MonoDevelop.Core.Execution; + +namespace MonoDevelop.Projects.MSBuild +{ + class MainClass + { + [STAThread] + public static void Main (string[] args) + { + RemoteProcessServer server = new RemoteProcessServer (); + var builderEngine = new BuildEngine (server); + server.Connect (args, builderEngine); + builderEngine.WaitHandle.WaitOne (); + } + } +} diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MonoDevelop.Projects.MSBuild.Shared.projitems b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MonoDevelop.Projects.MSBuild.Shared.projitems new file mode 100644 index 0000000000..bd0e54a174 --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MonoDevelop.Projects.MSBuild.Shared.projitems @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
+ <HasSharedItems>true</HasSharedItems>
+ <SharedGUID>{DB95FF74-275D-4488-84E9-1060527988BF}</SharedGUID>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration">
+ <Import_RootNamespace>MonoDevelop.Projects.MSBuild.Shared</Import_RootNamespace>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="$(MSBuildThisFileDirectory)..\..\MonoDevelop.Core\MonoDevelop.Core.Execution\BinaryMessage.cs">
+ <Link>BinaryMessage.cs</Link>
+ </Compile>
+ <Compile Include="$(MSBuildThisFileDirectory)BuildEngine.Shared.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)RemoteBuildEngineMessages.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)ProjectBuilder.Shared.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)..\..\MonoDevelop.Core\MonoDevelop.Core.Execution\RemoteProcessServer.cs">
+ <Link>RemoteProcessServer.cs</Link>
+ </Compile>
+ <Compile Include="$(MSBuildThisFileDirectory)ILogWriter.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)MSBuildEvaluatedItem.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)MSBuildResult.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)MSBuildTargetResult.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Main.cs" />
+ </ItemGroup>
+</Project>
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MonoDevelop.Projects.MSBuild.Shared.shproj b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MonoDevelop.Projects.MSBuild.Shared.shproj new file mode 100644 index 0000000000..3538f4c628 --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/MonoDevelop.Projects.MSBuild.Shared.shproj @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <ProductVersion>8.0.30703</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{DB95FF74-275D-4488-84E9-1060527988BF}</ProjectGuid> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" /> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" /> + <Import Project="MonoDevelop.Projects.MSBuild.Shared.projitems" Label="Shared" /> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" /> +</Project>
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/ProjectBuilder.Shared.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/ProjectBuilder.Shared.cs new file mode 100644 index 0000000000..3a5a54fe9e --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/ProjectBuilder.Shared.cs @@ -0,0 +1,186 @@ +//
+// ProjectBuilder.Shared.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+// Michael Hutchinson <m.j.hutchinson@gmail.com>
+//
+// Copyright (c) 2009-2011 Novell, Inc (http://www.novell.com)
+// Copyright (c) 2011-2015 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using Microsoft.Build.Framework;
+using System.Xml;
+using System.IO;
+using System;
+using System.Text;
+using System.Threading;
+using MonoDevelop.Core.Execution;
+
+namespace MonoDevelop.Projects.MSBuild
+{
+ partial class ProjectBuilder
+ {
+ ILogWriter currentLogWriter;
+ StringBuilder log = new StringBuilder ();
+ bool flushingLog;
+ Timer flushTimer;
+ object flushLogLock = new object ();
+ const int LogFlushTimeout = 100;
+
+ public void Dispose ()
+ {
+ buildEngine.UnloadProject (file);
+ }
+
+ public void Refresh ()
+ {
+ buildEngine.UnloadProject (file);
+ }
+
+ public void RefreshWithContent (string projectContent)
+ {
+ buildEngine.UnloadProject (file);
+ buildEngine.SetUnsavedProjectContent (file, projectContent);
+ }
+
+ /// <summary>
+ /// Prepares the logging infrastructure
+ /// </summary>
+ void InitLogger (ILogWriter logWriter)
+ {
+ currentLogWriter = logWriter;
+ if (currentLogWriter != null) { + log.Clear (); + flushingLog = false; + flushTimer = new Timer (o => FlushLog ());
+ }
+ }
+
+ /// <summary>
+ /// Flushes the log that has not yet been sent and disposes the logging infrastructure
+ /// </summary>
+ void DisposeLogger ()
+ {
+ if (currentLogWriter != null) { + flushTimer.Dispose (); + flushTimer = null; + FlushLog (); + currentLogWriter = null;
+ }
+ }
+
+ void LogWriteLine (string txt)
+ {
+ if (currentLogWriter != null) {
+ lock (log) {
+ // Append the line to the log, and schedule the flush of the log, unless it has already been done
+ log.AppendLine (txt);
+ if (!flushingLog) {
+ // Flush the log after 100ms
+ flushingLog = true;
+ flushTimer.Change (LogFlushTimeout, Timeout.Infinite);
+ }
+ }
+ }
+ }
+
+ void FlushLog ()
+ {
+ // We need a lock for the whole method here because it is called from the timer
+ // and from DisposeLogger, and we want to make sure a flush is complete before
+ // trying another one
+
+ lock (flushLogLock) { + string txt; + lock (log) {
+ // Don't flush the log inside the lock since that would prevent LogWriteLine from writing
+ // more log while the current log is being flushed (that would slow down the whole build) + txt = log.ToString (); + log.Clear (); + flushingLog = false; + } + if (txt.Length > 0 && currentLogWriter != null) + currentLogWriter.Write (txt);
+ }
+ }
+
+ LoggerVerbosity GetVerbosity (MSBuildVerbosity verbosity)
+ {
+ switch (verbosity) {
+ case MSBuildVerbosity.Quiet:
+ return LoggerVerbosity.Quiet;
+ case MSBuildVerbosity.Minimal:
+ return LoggerVerbosity.Minimal;
+ default:
+ return LoggerVerbosity.Normal;
+ case MSBuildVerbosity.Detailed:
+ return LoggerVerbosity.Detailed;
+ case MSBuildVerbosity.Diagnostic:
+ return LoggerVerbosity.Diagnostic;
+ }
+ }
+
+ //from MSBuildProjectService
+ static string UnescapeString (string str)
+ {
+ int i = str.IndexOf ('%');
+ while (i != -1 && i < str.Length - 2) {
+ int c;
+ if (int.TryParse (str.Substring (i+1, 2), System.Globalization.NumberStyles.HexNumber, null, out c))
+ str = str.Substring (0, i) + (char) c + str.Substring (i + 3);
+ i = str.IndexOf ('%', i + 1);
+ }
+ return str;
+ }
+
+ string GenerateSolutionConfigurationContents (ProjectConfigurationInfo[] configurations)
+ {
+ // can't use XDocument because of the 2.0 builder
+ // and don't just build a string because things may need escaping
+
+ var doc = new XmlDocument ();
+ var root = doc.CreateElement ("SolutionConfiguration");
+ doc.AppendChild (root);
+ foreach (var config in configurations) {
+ var el = doc.CreateElement ("ProjectConfiguration");
+ root.AppendChild (el);
+ el.SetAttribute ("Project", config.ProjectGuid);
+ el.SetAttribute ("AbsolutePath", config.ProjectFile);
+ el.SetAttribute ("BuildProjectInSolution", config.Enabled ? "True" : "False");
+ el.InnerText = string.Format (config.Configuration + "|" + config.Platform);
+ }
+
+ //match MSBuild formatting
+ var options = new XmlWriterSettings {
+ Indent = true,
+ IndentChars = "",
+ OmitXmlDeclaration = true,
+ };
+ using (var sw = new StringWriter ())
+ using (var xw = XmlWriter.Create (sw, options)) {
+ doc.WriteTo (xw);
+ xw.Flush ();
+ return sw.ToString ();
+ }
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/RemoteBuildEngineMessages.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/RemoteBuildEngineMessages.cs new file mode 100644 index 0000000000..23f22f4b16 --- /dev/null +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/RemoteBuildEngineMessages.cs @@ -0,0 +1,195 @@ +// +// RemoteBuildEngineMessages.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2016 Xamarin, Inc (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using MonoDevelop.Core.Execution; + +namespace MonoDevelop.Projects.MSBuild +{ + [MessageDataTypeAttribute] + class InitializeRequest: BinaryMessage + { + [MessageDataProperty] + public int IdeProcessId { get; set; } + + [MessageDataProperty] + public string CultureName { get; set; } + + [MessageDataProperty] + public Dictionary<string, string> GlobalProperties { get; set; } + } + + [MessageDataTypeAttribute] + class LoadProjectRequest: BinaryMessage<LoadProjectResponse> + { + [MessageDataProperty] + public string ProjectFile { get; set; } + } + + [MessageDataTypeAttribute] + class LoadProjectResponse: BinaryMessage + { + [MessageDataProperty] + public int ProjectId { get; set; } + } + + [MessageDataTypeAttribute] + class UnloadProjectRequest: BinaryMessage + { + [MessageDataProperty] + public int ProjectId { get; set; } + } + + [MessageDataTypeAttribute] + class CancelTaskRequest: BinaryMessage + { + [MessageDataProperty] + public int TaskId { get; set; } + } + + [MessageDataTypeAttribute] + class SetGlobalPropertiesRequest: BinaryMessage + { + [MessageDataProperty] + public Dictionary<string, string> Properties { get; set; } + } + + [MessageDataTypeAttribute] + class PingRequest: BinaryMessage + { + [MessageDataProperty] + public int TaskId = 1; + } + + [MessageDataTypeAttribute] + class DisposeRequest: BinaryMessage + { + } + + [MessageDataTypeAttribute] + class DisposeProjectRequest: BinaryMessage + { + [MessageDataProperty] + public int ProjectId { get; set; } + } + + [MessageDataTypeAttribute] + class RefreshProjectRequest: BinaryMessage + { + [MessageDataProperty] + public int ProjectId { get; set; } + } + + [MessageDataTypeAttribute] + class RefreshWithContentRequest: BinaryMessage + { + [MessageDataProperty] + public int ProjectId { get; set; } + + [MessageDataProperty] + public string Content { get; set; } + } + + [MessageDataTypeAttribute] + class RunProjectRequest: BinaryMessage<RunProjectResponse> + { + [MessageDataProperty] + public int ProjectId { get; set; } + + [MessageDataProperty] + public string Content { get; set; } + + [MessageDataProperty] + public ProjectConfigurationInfo [] Configurations { get; set; } + + [MessageDataProperty] + public int LogWriterId { get; set; } + + [MessageDataProperty] + public MSBuildVerbosity Verbosity { get; set; } + + [MessageDataProperty] + public string [] RunTargets { get; set; } + + [MessageDataProperty] + public string [] EvaluateItems { get; set; } + + [MessageDataProperty] + public string [] EvaluateProperties { get; set; } + + [MessageDataProperty] + public Dictionary<string, string> GlobalProperties { get; set; } + + [MessageDataProperty] + public int TaskId { get; set; } + } + + [MessageDataTypeAttribute] + class RunProjectResponse : BinaryMessage + { + [MessageDataProperty] + public MSBuildResult Result { get; set; } + } + + [MessageDataTypeAttribute] + class LogMessage : BinaryMessage + { + [MessageDataProperty] + public int LoggerId { get; set; } + + [MessageDataProperty] + public string Text { get; set; } + } + + public enum MSBuildVerbosity + { + Quiet, + Minimal, + Normal, + Detailed, + Diagnostic + } + + [MessageDataTypeAttribute] + class ProjectConfigurationInfo + { + [MessageDataProperty] + public string ProjectFile { get; set; } + + [MessageDataProperty] + public string ProjectGuid { get; set; } + + [MessageDataProperty] + public string Configuration { get; set; } + + [MessageDataProperty] + public string Platform { get; set; } + + [MessageDataProperty] + public bool Enabled { get; set; } + } +} + |