diff options
Diffstat (limited to 'main/src/core/MonoDevelop.Core')
162 files changed, 12529 insertions, 9904 deletions
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/DefaultExecutionHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/DefaultExecutionHandler.cs index e56f71e714..7cac46b2a8 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/DefaultExecutionHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/DefaultExecutionHandler.cs @@ -37,7 +37,7 @@ namespace MonoDevelop.Core.Execution return Runtime.ProcessService.GetDefaultExecutionHandler (command) != null; } - public IProcessAsyncOperation Execute (ExecutionCommand command, IConsole console) + public ProcessAsyncOperation Execute (ExecutionCommand command, IConsole console) { IExecutionHandler handler = Runtime.ProcessService.GetDefaultExecutionHandler (command); return handler.Execute (command, console); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/DotNetExecutionHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/DotNetExecutionHandler.cs index da3985afa5..c4a2dd0790 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/DotNetExecutionHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/DotNetExecutionHandler.cs @@ -35,7 +35,7 @@ namespace MonoDevelop.Core.Execution return command is DotNetExecutionCommand; } - public IProcessAsyncOperation Execute (ExecutionCommand command, IConsole console) + public ProcessAsyncOperation Execute (ExecutionCommand command, IConsole console) { DotNetExecutionCommand cmd = (DotNetExecutionCommand) command; if (cmd.TargetRuntime == null) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/IExecutionHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/IExecutionHandler.cs index a0a1c7b6d0..fe12bceeb8 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/IExecutionHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/IExecutionHandler.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; +using System.Threading; namespace MonoDevelop.Core.Execution { @@ -56,6 +57,6 @@ namespace MonoDevelop.Core.Execution /// <param name='console'> /// Console where to log the output /// </param> - IProcessAsyncOperation Execute (ExecutionCommand command, IConsole console); + ProcessAsyncOperation Execute (ExecutionCommand command, IConsole console); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/MonoPlatformExecutionHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/MonoPlatformExecutionHandler.cs index a164a13508..42451c68f2 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/MonoPlatformExecutionHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/MonoPlatformExecutionHandler.cs @@ -44,7 +44,7 @@ namespace MonoDevelop.Core.Execution this.monoPath = monoPath; } - public override IProcessAsyncOperation Execute (ExecutionCommand command, IConsole console) + public override ProcessAsyncOperation Execute (ExecutionCommand command, IConsole console) { DotNetExecutionCommand dotcmd = (DotNetExecutionCommand) command; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/NativePlatformExecutionHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/NativePlatformExecutionHandler.cs index 6783653e3a..8c429a80ae 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/NativePlatformExecutionHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/NativePlatformExecutionHandler.cs @@ -45,7 +45,7 @@ namespace MonoDevelop.Core.Execution this.defaultEnvironmentVariables = defaultEnvironmentVariables; } - public virtual IProcessAsyncOperation Execute (ExecutionCommand command, IConsole console) + public virtual ProcessAsyncOperation Execute (ExecutionCommand command, IConsole console) { ProcessExecutionCommand cmd = (ProcessExecutionCommand) command; IDictionary<string, string> vars; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/IProcessAsyncOperation.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessAsyncOperation.cs index 977ea67c7c..1cdeadd47f 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/IProcessAsyncOperation.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessAsyncOperation.cs @@ -29,25 +29,34 @@ using System; using MonoDevelop.Core; using MonoDevelop.Core.ProgressMonitoring; +using System.Threading.Tasks; +using System.Threading; namespace MonoDevelop.Core.Execution { - public interface IProcessAsyncOperation: IAsyncOperation, IDisposable + public class ProcessAsyncOperation: AsyncOperation { - int ExitCode { get; } + protected ProcessAsyncOperation () + { + } + + public ProcessAsyncOperation (Task task, CancellationTokenSource cancellationTokenSource): base (task, cancellationTokenSource) + { + } + + public int ExitCode { get; set; } - int ProcessId { get; } + public int ProcessId { get; set; } } - public class NullProcessAsyncOperation : NullAsyncOperation, IProcessAsyncOperation + public class NullProcessAsyncOperation : ProcessAsyncOperation { - public NullProcessAsyncOperation (bool success) : base (success, false) {} - public int ExitCode { get { return ((IAsyncOperation)this).Success? 0 : 1; } } - public int ProcessId { get { return 0; } } - - void IDisposable.Dispose () {} - - public new static NullProcessAsyncOperation Success = new NullProcessAsyncOperation (true); - public new static NullProcessAsyncOperation Failure = new NullProcessAsyncOperation (false); + public NullProcessAsyncOperation (int exitCode) + { + ExitCode = exitCode; + } + + public static NullProcessAsyncOperation Success = new NullProcessAsyncOperation (0); + public static NullProcessAsyncOperation Failure = new NullProcessAsyncOperation (1); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessHostController.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessHostController.cs index 11be57b373..e22d48cba8 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessHostController.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessHostController.cs @@ -50,7 +50,7 @@ namespace MonoDevelop.Core.Execution DateTime lastReleaseTime; bool starting; bool stopping; - IProcessAsyncOperation process; + ProcessAsyncOperation process; Timer timer; string id; IExecutionHandler executionHandlerFactory; @@ -130,10 +130,10 @@ namespace MonoDevelop.Core.Execution cmd.UserAssemblyPaths = userAssemblyPaths; cmd.DebugMode = isDebugMode; ProcessHostConsole cons = new ProcessHostConsole (); - process = executionHandlerFactory.Execute (cmd, cons); + var p = process = executionHandlerFactory.Execute (cmd, cons); Counters.ExternalHostProcesses++; - process.Completed += ProcessExited; + process.Task.ContinueWith ((t) => ProcessExited (p)); } catch (Exception ex) { if (tmpFile != null) { @@ -155,7 +155,7 @@ namespace MonoDevelop.Core.Execution } } - void ProcessExited (IAsyncOperation oper) + void ProcessExited (ProcessAsyncOperation oper) { lock (this) { @@ -307,7 +307,7 @@ namespace MonoDevelop.Core.Execution void WaitTimeout (object sender, System.Timers.ElapsedEventArgs args) { try { - IProcessAsyncOperation oldProcess; + ProcessAsyncOperation oldProcess; lock (this) { if (references > 0) { @@ -395,75 +395,4 @@ namespace MonoDevelop.Core.Execution { } } - - class InernalProcessHost: Process, IProcessAsyncOperation - { - object doneLock = new object (); - bool finished; - OperationHandler completed; - - public InernalProcessHost () - { - Exited += delegate { - lock (doneLock) { - finished = true; - Monitor.PulseAll (doneLock); - if (completed != null) - completed (this); - } - }; - } - - public int ProcessId { - get { return Id; } - } - - public event OperationHandler Completed { - add { - lock (doneLock) { - completed += value; - if (finished) - value (this); - } - } - remove { - lock (doneLock) { - completed -= value; - } - } - } - - public void Cancel () - { - Kill (); - } - - public void WaitForCompleted () - { - lock (doneLock) { - while (!finished) - Monitor.Wait (doneLock); - } - } - - public bool IsCompleted { - get { - lock (doneLock) { - return finished; - } - } - } - - public bool Success { - get { - return true; - } - } - - public bool SuccessWithWarnings { - get { - return true; - } - } - } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessService.cs index b88803792d..94e679d891 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessService.cs @@ -155,15 +155,9 @@ namespace MonoDevelop.Core.Execution // p.Exited += exited; // p.EnableRaisingEvents = true; - if (exited != null) { - MonoDevelop.Core.OperationHandler handler = null; - handler = delegate (MonoDevelop.Core.IAsyncOperation op) { - op.Completed -= handler; - exited (p, EventArgs.Empty); - }; - ((MonoDevelop.Core.IAsyncOperation)p).Completed += handler; - } - + if (exited != null) + p.Task.ContinueWith (t => exited (p, EventArgs.Empty)); + Counters.ProcessesStarted++; p.Start (); return p; @@ -194,13 +188,13 @@ namespace MonoDevelop.Core.Execution return startInfo; } - public IProcessAsyncOperation StartConsoleProcess (string command, string arguments, string workingDirectory, IConsole console, + public ProcessAsyncOperation StartConsoleProcess (string command, string arguments, string workingDirectory, IConsole console, EventHandler exited) { return StartConsoleProcess (command, arguments, workingDirectory, null, console, exited); } - public IProcessAsyncOperation StartConsoleProcess (string command, string arguments, string workingDirectory, + public ProcessAsyncOperation StartConsoleProcess (string command, string arguments, string workingDirectory, IDictionary<string, string> environmentVariables, IConsole console, EventHandler exited) { if ((console == null || (console is ExternalConsole)) && externalConsoleHandler != null) { @@ -218,11 +212,8 @@ namespace MonoDevelop.Core.Execution console != null ? !console.CloseOnDispose : false); if (p != null) { - if (exited != null) { - p.Completed += delegate { - exited (p, EventArgs.Empty); - }; - } + if (exited != null) + p.Task.ContinueWith (t => exited (p, EventArgs.Empty)); Counters.ProcessesStarted++; return p; } else { @@ -234,8 +225,8 @@ namespace MonoDevelop.Core.Execution foreach (KeyValuePair<string, string> kvp in environmentVariables) psi.EnvironmentVariables [kvp.Key] = kvp.Value; ProcessWrapper pw = StartProcess (psi, console.Out, console.Error, null); - new ProcessMonitor (console, pw, exited); - return pw; + new ProcessMonitor (console, pw.ProcessAsyncOperation, exited); + return pw.ProcessAsyncOperation; } public IExecutionHandler GetDefaultExecutionHandler (ExecutionCommand command) @@ -420,22 +411,22 @@ namespace MonoDevelop.Core.Execution { public IConsole console; EventHandler exited; - IProcessAsyncOperation operation; + ProcessAsyncOperation operation; - public ProcessMonitor (IConsole console, IProcessAsyncOperation operation, EventHandler exited) + public ProcessMonitor (IConsole console, ProcessAsyncOperation operation, EventHandler exited) { this.exited = exited; this.operation = operation; this.console = console; - operation.Completed += new OperationHandler (OnOperationCompleted); - console.CancelRequested += new EventHandler (OnCancelRequest); + operation.Task.ContinueWith (t => OnOperationCompleted ()); + console.CancelRequested += OnCancelRequest; } - public void OnOperationCompleted (IAsyncOperation op) + public void OnOperationCompleted () { try { if (exited != null) - exited (op, null); + exited (operation, EventArgs.Empty); if (!Platform.IsWindows && Mono.Unix.Native.Syscall.WIFSIGNALED (operation.ExitCode)) console.Log.WriteLine (GettextCatalog.GetString ("The application was terminated by a signal: {0}"), Mono.Unix.Native.Syscall.WTERMSIG (operation.ExitCode)); @@ -451,7 +442,7 @@ namespace MonoDevelop.Core.Execution operation.Cancel (); //remove the cancel handler, it will be attached again when StartConsoleProcess is called - console.CancelRequested -= new EventHandler (OnCancelRequest); + console.CancelRequested -= OnCancelRequest; } } @@ -475,5 +466,5 @@ namespace MonoDevelop.Core.Execution } } - public delegate IProcessAsyncOperation ExternalConsoleHandler (string command, string arguments, string workingDirectory, IDictionary<string, string> environmentVariables, string title, bool pauseWhenFinished); + public delegate ProcessAsyncOperation ExternalConsoleHandler (string command, string arguments, string workingDirectory, IDictionary<string, string> environmentVariables, string title, bool pauseWhenFinished); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessWrapper.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessWrapper.cs index a5f121882e..2aa4f7d099 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessWrapper.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessWrapper.cs @@ -2,36 +2,47 @@ using System; using System.Threading; using System.Diagnostics; +using System.Threading.Tasks; namespace MonoDevelop.Core.Execution { public delegate void ProcessEventHandler(object sender, string message); [System.ComponentModel.DesignerCategory ("Code")] - public class ProcessWrapper : Process, IProcessAsyncOperation + public class ProcessWrapper : Process { - private Thread captureOutputThread; private Thread captureErrorThread; ManualResetEvent endEventOut = new ManualResetEvent (false); ManualResetEvent endEventErr = new ManualResetEvent (false); bool done; object lockObj = new object (); + Task task; + ProcessAsyncOperation operation; + IDisposable customCancelToken; public ProcessWrapper () { } - public bool CancelRequested { get; private set; }
- + public bool CancelRequested { get; private set; } + + public Task Task { + get { return task; } + } + + public ProcessAsyncOperation ProcessAsyncOperation { + get { return operation; } + } + public new void Start () { CheckDisposed (); base.Start (); - - captureOutputThread = new Thread (new ThreadStart(CaptureOutput)); - captureOutputThread.Name = "Process output reader"; - captureOutputThread.IsBackground = true; - captureOutputThread.Start (); - + + task = Task.Factory.StartNew (CaptureOutput, TaskCreationOptions.LongRunning); + var cs = new CancellationTokenSource (); + operation = new ProcessAsyncOperation (task, cs); + cs.Token.Register (Cancel); + if (ErrorStreamChanged != null) { captureErrorThread = new Thread (new ThreadStart(CaptureError)); captureErrorThread.Name = "Process error reader"; @@ -40,6 +51,12 @@ namespace MonoDevelop.Core.Execution } else { endEventErr.Set (); } + operation.ProcessId = Id; + } + + public void SetCancellationToken (CancellationToken cancelToken) + { + customCancelToken = cancelToken.Register (Cancel); } public void WaitForOutput (int milliseconds) @@ -56,6 +73,7 @@ namespace MonoDevelop.Core.Execution private void CaptureOutput () { + Thread.CurrentThread.Name = "Process output reader"; try { if (OutputStreamChanged != null) { char[] buffer = new char [1024]; @@ -74,6 +92,9 @@ namespace MonoDevelop.Core.Execution if (endEventErr != null) endEventErr.WaitOne (); + if (HasExited) + operation.ExitCode = ExitCode; + OnExited (this, EventArgs.Empty); lock (lockObj) { @@ -108,9 +129,9 @@ namespace MonoDevelop.Core.Execution return; if (!done) - ((IAsyncOperation)this).Cancel (); + Cancel (); - captureOutputThread = captureErrorThread = null; + captureErrorThread = null; endEventOut.Close (); endEventErr.Close (); endEventOut = endEventErr = null; @@ -132,15 +153,7 @@ namespace MonoDevelop.Core.Execution throw new ObjectDisposedException ("ProcessWrapper"); } - int IProcessAsyncOperation.ExitCode { - get { return ExitCode; } - } - - int IProcessAsyncOperation.ProcessId { - get { return Id; } - } - - void IAsyncOperation.Cancel () + public void Cancel () { try { if (!done) { @@ -156,13 +169,12 @@ namespace MonoDevelop.Core.Execution } } - void IAsyncOperation.WaitForCompleted () - { - WaitForOutput (); - } - void OnExited (object sender, EventArgs args) { + if (customCancelToken != null) { + customCancelToken.Dispose (); + customCancelToken = null; + } try { if (!HasExited) WaitForExit (); @@ -171,49 +183,10 @@ namespace MonoDevelop.Core.Execution } finally { lock (lockObj) { done = true; - try { - if (completedEvent != null) - completedEvent (this); - } catch { - // Ignore - } } } } - event OperationHandler IAsyncOperation.Completed { - add { - bool raiseNow = false; - lock (lockObj) { - if (done) - raiseNow = true; - else - completedEvent += value; - } - if (raiseNow) - value (this); - } - remove { - lock (lockObj) { - completedEvent -= value; - } - } - } - - bool IAsyncOperation.Success { - get { return done ? ExitCode == 0 : false; } - } - - bool IAsyncOperation.SuccessWithWarnings { - get { return false; } - } - - bool IAsyncOperation.IsCompleted { - get { return done; } - } - - event OperationHandler completedEvent; - public event ProcessEventHandler OutputStreamChanged; public event ProcessEventHandler ErrorStreamChanged; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.FileSystem/DefaultFileSystemExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.FileSystem/DefaultFileSystemExtension.cs index c6667cd16b..ac77a0bb22 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.FileSystem/DefaultFileSystemExtension.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.FileSystem/DefaultFileSystemExtension.cs @@ -97,7 +97,7 @@ namespace MonoDevelop.Core.FileSystem public override void MoveDirectory (FilePath source, FilePath dest) { - Directory.Move (source, dest); + FileService.SystemDirectoryRename (source, dest); } public override void DeleteDirectory (FilePath path) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/Counter.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/Counter.cs index 11ce9e69ad..5036c77e7a 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/Counter.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/Counter.cs @@ -35,6 +35,7 @@ namespace MonoDevelop.Core.Instrumentation { internal int count; int totalCount; + int lastStoredCount; string name; bool logMessages; CounterCategory category; @@ -45,8 +46,9 @@ namespace MonoDevelop.Core.Instrumentation bool disposed; bool storeValues; bool enabled; + string id; - List<IInstrumentationConsumer> handlers = new List<IInstrumentationConsumer> (); + List<InstrumentationConsumer> handlers = new List<InstrumentationConsumer> (); public bool StoreValues { get { @@ -56,14 +58,22 @@ namespace MonoDevelop.Core.Instrumentation storeValues = value; } } + + public bool Enabled { + get { return enabled; } + } - internal List<IInstrumentationConsumer> Handlers { - get { return handlers; } + internal List<InstrumentationConsumer> Handlers { + get { + InstrumentationService.InitializeHandlers (); + return handlers; + } } internal void UpdateStatus () { - enabled = InstrumentationService.Enabled || handlers.Count > 0; + InstrumentationService.InitializeHandlers (); + enabled = InstrumentationService.Enabled || Handlers.Count > 0; storeValues = InstrumentationService.Enabled; } @@ -76,6 +86,11 @@ namespace MonoDevelop.Core.Instrumentation public string Name { get { return name; } } + + public string Id { + get { return id ?? Name; } + internal set { id = value; } + } public CounterCategory Category { get { return category; } @@ -182,19 +197,29 @@ namespace MonoDevelop.Core.Instrumentation } } - internal int StoreValue (string message, TimerTraceList traces) + internal int StoreValue (string message, TimeCounter timer) { DateTime now = DateTime.Now; if (resolution.Ticks != 0) { if (now - lastValueTime < resolution) return -1; } - var val = new CounterValue (count, totalCount, now, message, traces); + var val = new CounterValue (count, totalCount, count - lastStoredCount, now, message, timer != null ? timer.TraceList : null); + lastStoredCount = count; + if (storeValues) values.Add (val); - if (handlers.Count > 0) { - foreach (var h in handlers) - h.ConsumeValue (this, val); + if (Handlers.Count > 0) { + if (timer != null) { + foreach (var h in handlers) { + var t = h.BeginTimer ((TimerCounter)this, val); + if (t != null) + timer.AddHandlerTracker (t); + } + } else { + foreach (var h in handlers) + h.ConsumeValue (this, val); + } } return values.Count - 1; } @@ -322,6 +347,7 @@ namespace MonoDevelop.Core.Instrumentation { int value; int totalCount; + int change; DateTime timestamp; string message; TimerTraceList traces; @@ -335,15 +361,17 @@ namespace MonoDevelop.Core.Instrumentation this.message = null; traces = null; threadId = 0; + change = 0; } - internal CounterValue (int value, int totalCount, DateTime timestamp, string message, TimerTraceList traces) + internal CounterValue (int value, int totalCount, int change, DateTime timestamp, string message, TimerTraceList traces) { this.value = value; this.timestamp = timestamp; this.totalCount = totalCount; this.message = message; this.traces = traces; + this.change = change; this.threadId = System.Threading.Thread.CurrentThread.ManagedThreadId; } @@ -358,6 +386,10 @@ namespace MonoDevelop.Core.Instrumentation public int TotalCount { get { return totalCount; } } + + public int ValueChange { + get { return change; } + } public int ThreadId { get { return this.threadId; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/IInstrumentationConsumer.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationConsumer.cs index 9f6af53387..8e0574fe64 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/IInstrumentationConsumer.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationConsumer.cs @@ -28,10 +28,18 @@ using System; namespace MonoDevelop.Core.Instrumentation { [Mono.Addins.TypeExtensionPoint] - public interface IInstrumentationConsumer + public abstract class InstrumentationConsumer { - bool SupportsCounter (Counter counter); - void ConsumeValue (Counter counter, CounterValue value); + public abstract bool SupportsCounter (Counter counter); + + public virtual void ConsumeValue (Counter counter, CounterValue value) + { + } + + public virtual IDisposable BeginTimer (TimerCounter counter, CounterValue value) + { + return null; + } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationService.cs index 0cbe2dccbb..bd9358bbac 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationService.cs @@ -54,7 +54,7 @@ namespace MonoDevelop.Core.Instrumentation static Thread autoSaveThread; static bool stopping; static int autoSaveInterval; - static List<IInstrumentationConsumer> handlers = new List<IInstrumentationConsumer> (); + static List<InstrumentationConsumer> handlers = new List<InstrumentationConsumer> (); static bool handlersLoaded; static InstrumentationService () @@ -64,12 +64,12 @@ namespace MonoDevelop.Core.Instrumentation startTime = DateTime.Now; } - static void InitializeHandlers () + internal static void InitializeHandlers () { if (!handlersLoaded && AddinManager.IsInitialized) { lock (counters) { handlersLoaded = true; - AddinManager.AddExtensionNodeHandler (typeof(IInstrumentationConsumer), HandleInstrumentationHandlerExtension); + AddinManager.AddExtensionNodeHandler (typeof(InstrumentationConsumer), HandleInstrumentationHandlerExtension); } } } @@ -84,26 +84,37 @@ namespace MonoDevelop.Core.Instrumentation static void HandleInstrumentationHandlerExtension (object sender, ExtensionNodeEventArgs args) { - var handler = (IInstrumentationConsumer)args.ExtensionObject; + var handler = (InstrumentationConsumer)args.ExtensionObject; if (args.Change == ExtensionChange.Add) { - handlers.Add (handler); - lock (counters) { - foreach (var c in counters.Values) { - if (handler.SupportsCounter (c)) - c.Handlers.Add (handler); - } - } + RegisterInstrumentationConsumer (handler); } else { - handlers.Remove (handler); - lock (counters) { - foreach (var c in counters.Values) - c.Handlers.Remove (handler); + UnregisterInstrumentationConsumer (handler); + } + } + + public static void RegisterInstrumentationConsumer (InstrumentationConsumer consumer) + { + lock (counters) { + handlers.Add (consumer); + foreach (var c in counters.Values) { + if (consumer.SupportsCounter (c)) + c.Handlers.Add (consumer); } } UpdateCounterStatus (); } + public static void UnregisterInstrumentationConsumer (InstrumentationConsumer consumer) + { + lock (counters) { + handlers.Remove (consumer); + foreach (var c in counters.Values) + c.Handlers.Remove (consumer); + } + UpdateCounterStatus (); + } + public static int PublishService () { RemotingService.RegisterRemotingChannel (); @@ -123,7 +134,7 @@ namespace MonoDevelop.Core.Instrumentation throw new InvalidOperationException ("Service not published"); if (Platform.IsMac) { - var macOSDir = PropertyService.EntryAssemblyPath.ParentDirectory.ParentDirectory.ParentDirectory; + var macOSDir = PropertyService.EntryAssemblyPath.ParentDirectory.ParentDirectory.ParentDirectory.ParentDirectory.Combine ("MacOS"); var app = macOSDir.Combine ("MDMonitor.app"); if (Directory.Exists (app)) { var psi = new ProcessStartInfo ("open", string.Format ("-n '{0}' --args -c localhost:{1} ", app, publicPort)) { @@ -223,10 +234,15 @@ namespace MonoDevelop.Core.Instrumentation public static Counter CreateCounter (string name, string category, bool logMessages) { - return CreateCounter (name, category, logMessages, false); + return CreateCounter (name, category, logMessages, null, false); } - static Counter CreateCounter (string name, string category, bool logMessages, bool isTimer) + public static Counter CreateCounter (string name, string category = null, bool logMessages = false, string id = null) + { + return CreateCounter (name, category, logMessages, id, false); + } + + static Counter CreateCounter (string name, string category, bool logMessages, string id, bool isTimer) { InitializeHandlers (); @@ -241,6 +257,7 @@ namespace MonoDevelop.Core.Instrumentation } Counter c = isTimer ? new TimerCounter (name, cat) : new Counter (name, cat); + c.Id = id; c.LogMessages = logMessages; cat.AddCounter (c); @@ -289,7 +306,12 @@ namespace MonoDevelop.Core.Instrumentation public static TimerCounter CreateTimerCounter (string name, string category, double minSeconds, bool logMessages) { - TimerCounter c = (TimerCounter) CreateCounter (name, category, logMessages, true); + return CreateTimerCounter (name, category, minSeconds, logMessages, null); + } + + public static TimerCounter CreateTimerCounter (string name, string category = null, double minSeconds = 0, bool logMessages = false, string id = null) + { + TimerCounter c = (TimerCounter) CreateCounter (name, category, logMessages, id, true); c.DisplayMode = CounterDisplayMode.Line; c.LogMessages = logMessages; c.MinSeconds = minSeconds; @@ -357,7 +379,7 @@ namespace MonoDevelop.Core.Instrumentation } } - public static IProgressMonitor GetInstrumentedMonitor (IProgressMonitor monitor, TimerCounter counter) + public static ProgressMonitor GetInstrumentedMonitor (ProgressMonitor monitor, TimerCounter counter) { if (enabled) { AggregatedProgressMonitor mon = new AggregatedProgressMonitor (monitor); @@ -368,37 +390,23 @@ namespace MonoDevelop.Core.Instrumentation } } - class IntrumentationMonitor: NullProgressMonitor + class IntrumentationMonitor: ProgressMonitor { TimerCounter counter; Stack<ITimeTracker> timers = new Stack<ITimeTracker> (); - LogTextWriter logger = new LogTextWriter (); - + public IntrumentationMonitor (TimerCounter counter) { this.counter = counter; - logger.TextWritten += HandleLoggerTextWritten; } - void HandleLoggerTextWritten (string writtenText) + protected override void OnWriteLog (string message) { if (timers.Count > 0) - timers.Peek ().Trace (writtenText); - } - - public override void BeginTask (string name, int totalWork) - { - if (!string.IsNullOrEmpty (name)) { - ITimeTracker c = counter.BeginTiming (name); - c.Trace (name); - timers.Push (c); - } else { - timers.Push (null); - } - base.BeginTask (name, totalWork); + timers.Peek ().Trace (message); } - - public override void BeginStepTask (string name, int totalWork, int stepSize) + + protected override void OnBeginTask (string name, int totalWork, int stepWork) { if (!string.IsNullOrEmpty (name)) { ITimeTracker c = counter.BeginTiming (name); @@ -407,23 +415,15 @@ namespace MonoDevelop.Core.Instrumentation } else { timers.Push (null); } - base.BeginStepTask (name, totalWork, stepSize); } - public override void EndTask () + protected override void OnEndTask (string name, int totalWork, int stepWork) { if (timers.Count > 0) { ITimeTracker c = timers.Pop (); if (c != null) c.End (); } - base.EndTask (); - } - - public override System.IO.TextWriter Log { - get { - return logger; - } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimeCounter.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimeCounter.cs index 140f84f899..55227a859f 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimeCounter.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimeCounter.cs @@ -25,6 +25,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; namespace MonoDevelop.Core.Instrumentation { @@ -55,6 +56,7 @@ namespace MonoDevelop.Core.Instrumentation TimerTraceList traceList; TimerTrace lastTrace; TimerCounter counter; + object linkedTrackers; internal TimeCounter (TimerCounter counter) { @@ -62,6 +64,18 @@ namespace MonoDevelop.Core.Instrumentation traceList = new TimerTraceList (); Begin (); } + + public void AddHandlerTracker (IDisposable t) + { + if (linkedTrackers == null) + linkedTrackers = t; + else if (!(linkedTrackers is List<IDisposable>)) { + var list = new List<IDisposable> (); + list.Add ((IDisposable)linkedTrackers); + list.Add (t); + } else + ((List<IDisposable>)linkedTrackers).Add (t); + } internal TimerTraceList TraceList { get { return this.traceList; } @@ -98,6 +112,13 @@ namespace MonoDevelop.Core.Instrumentation else counter.AddTime (traceList.TotalTime); counter = null; + + if (linkedTrackers is List<IDisposable>) { + foreach (var t in (List<IDisposable>)linkedTrackers) + t.Dispose (); + } else if (linkedTrackers != null) + ((IDisposable)linkedTrackers).Dispose (); + } void IDisposable.Dispose () diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimerCounter.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimerCounter.cs index ed2f54cd46..316d54f87e 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimerCounter.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimerCounter.cs @@ -82,7 +82,7 @@ namespace MonoDevelop.Core.Instrumentation public override void Trace (string message) { - if (InstrumentationService.Enabled) { + if (Enabled) { if (lastTimer != null) lastTimer.Trace (message); else { @@ -103,13 +103,14 @@ namespace MonoDevelop.Core.Instrumentation public ITimeTracker BeginTiming (string message) { ITimeTracker timer; - if (!InstrumentationService.Enabled) { + if (!Enabled) { timer = dummyTimer; } else { + var c = new TimeCounter (this); lock (values) { - timer = lastTimer = new TimeCounter (this); + timer = lastTimer = c; count++; - int i = StoreValue (message, lastTimer.TraceList); + int i = StoreValue (message, lastTimer); lastTimer.TraceList.ValueIndex = i; } } @@ -120,7 +121,7 @@ namespace MonoDevelop.Core.Instrumentation public void EndTiming () { - if (InstrumentationService.Enabled && lastTimer != null) + if (Enabled && lastTimer != null) lastTimer.End (); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.LogReporting/CrashReporter.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.LogReporting/CrashReporter.cs new file mode 100644 index 0000000000..96b3c6e313 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.LogReporting/CrashReporter.cs @@ -0,0 +1,39 @@ +// +// CrashReporter.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 Mono.Addins; +using System.Collections.Generic; + +namespace MonoDevelop.Core.LogReporting +{ + [TypeExtensionPoint] + public abstract class CrashReporter + { + public abstract void ReportCrash (Exception ex, bool willShutDown, IEnumerable<string> tags); + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AggregatedAsyncOperation.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AggregatedAsyncOperation.cs deleted file mode 100644 index b4067d2923..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AggregatedAsyncOperation.cs +++ /dev/null @@ -1,112 +0,0 @@ -// -// AggregatedAsyncOperation.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.Collections.Generic; - -namespace MonoDevelop.Core.ProgressMonitoring -{ - public class AggregatedAsyncOperation: IAsyncOperation - { - public event OperationHandler Completed; - - List<IAsyncOperation> operations = new List<IAsyncOperation> (); - bool started; - bool success = true; - bool successWithWarnings = true; - int completed; - - public void Add (IAsyncOperation oper) - { - if (started) - throw new InvalidOperationException ("Can't add more operations after calling StartMonitoring"); - operations.Add (oper); - } - - public void StartMonitoring () - { - started = true; - foreach (IAsyncOperation oper in operations) - oper.Completed += OperCompleted; - } - - void OperCompleted (IAsyncOperation op) - { - bool raiseEvent; - lock (operations) { - completed++; - success = success && op.Success; - successWithWarnings = success && op.SuccessWithWarnings; - raiseEvent = (completed == operations.Count); - } - if (raiseEvent && Completed != null) - Completed (this); - } - - public void Cancel () - { - CheckStarted (); - lock (operations) { - foreach (IAsyncOperation op in operations) - op.Cancel (); - } - } - - public void WaitForCompleted () - { - CheckStarted (); - foreach (IAsyncOperation op in operations) - op.WaitForCompleted (); - } - - public bool IsCompleted { - get { - CheckStarted (); - return completed == operations.Count; - } - } - - public bool Success { - get { - CheckStarted (); - return success; - } - } - - public bool SuccessWithWarnings { - get { - CheckStarted (); - return successWithWarnings; - } - } - - void CheckStarted () - { - if (!started) - throw new InvalidOperationException ("Operation not started"); - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AggregatedOperationMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AggregatedOperationMonitor.cs deleted file mode 100644 index c8ffc6fa50..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AggregatedOperationMonitor.cs +++ /dev/null @@ -1,77 +0,0 @@ -// -// AggregatedOperationMonitor.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (C) 2005 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.Collections.Generic; - -namespace MonoDevelop.Core.ProgressMonitoring -{ - public class AggregatedOperationMonitor: IDisposable - { - List<IAsyncOperation> list = new List<IAsyncOperation> (); - IProgressMonitor monitor; - - public AggregatedOperationMonitor (IProgressMonitor monitor, params IAsyncOperation[] operations) - { - this.monitor = monitor; - - if (operations != null) { - lock (list) { - foreach (IAsyncOperation operation in operations) - AddOperation (operation); - } - } - - monitor.CancelRequested += new MonitorHandler (OnCancel); - } - - public void AddOperation (IAsyncOperation operation) - { - lock (list) { - if (monitor.IsCancelRequested) - operation.Cancel (); - else - list.Add (operation); - } - } - - void OnCancel (IProgressMonitor m) - { - lock (list) { - foreach (IAsyncOperation operation in list) - operation.Cancel (); - } - } - - public void Dispose () - { - monitor.CancelRequested -= new MonitorHandler (OnCancel); - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AggregatedProgressMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AggregatedProgressMonitor.cs index cd8c4aed7b..6bf2638e99 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AggregatedProgressMonitor.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AggregatedProgressMonitor.cs @@ -30,6 +30,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; +using System.Threading; namespace MonoDevelop.Core.ProgressMonitoring { @@ -48,179 +50,145 @@ namespace MonoDevelop.Core.ProgressMonitoring All = 0xff } - public class AggregatedProgressMonitor: IProgressMonitor, IAsyncOperation + public class AggregatedProgressMonitor: ProgressMonitor { - IProgressMonitor masterMonitor; + ProgressMonitor masterMonitor; List<MonitorInfo> monitors = new List<MonitorInfo> (); - LogTextWriter logger; + CancellationTokenSource cancelSource; class MonitorInfo { public MonitorAction ActionMask; - public IProgressMonitor Monitor; + public ProgressMonitor Monitor; + public CancellationTokenRegistration CancellationTokenRegistration; } - public IProgressMonitor MasterMonitor { + public ProgressMonitor MasterMonitor { get { return this.masterMonitor; } } - public AggregatedProgressMonitor (): this (new NullProgressMonitor ()) + public AggregatedProgressMonitor (): this (new ProgressMonitor ()) { } - public AggregatedProgressMonitor (IProgressMonitor masterMonitor, params IProgressMonitor[] slaveMonitors) + public AggregatedProgressMonitor (ProgressMonitor masterMonitor, params ProgressMonitor[] slaveMonitors): this (masterMonitor, null, slaveMonitors) { + } + + internal AggregatedProgressMonitor (ProgressMonitor masterMonitor, CancellationTokenSource cancelSource, params ProgressMonitor[] slaveMonitors) + { + this.cancelSource = cancelSource ?? new CancellationTokenSource (); this.masterMonitor = masterMonitor; AddSlaveMonitor (masterMonitor, MonitorAction.All); - logger = new LogTextWriter (); - logger.TextWritten += new LogTextEventHandler (OnWriteLog); - foreach (IProgressMonitor mon in slaveMonitors) + foreach (ProgressMonitor mon in slaveMonitors) AddSlaveMonitor (mon); } - public void AddSlaveMonitor (IProgressMonitor slaveMonitor) + public new void AddSlaveMonitor (ProgressMonitor slaveMonitor) { AddSlaveMonitor (slaveMonitor, MonitorAction.All); } - public void AddSlaveMonitor (IProgressMonitor slaveMonitor, MonitorAction actionMask) + public void AddSlaveMonitor (ProgressMonitor slaveMonitor, MonitorAction actionMask) { MonitorInfo smon = new MonitorInfo (); smon.ActionMask = actionMask; smon.Monitor = slaveMonitor; monitors.Add (smon); if ((actionMask & MonitorAction.SlaveCancel) != 0) - slaveMonitor.CancelRequested += new MonitorHandler (OnSlaveCancelRequested); + smon.CancellationTokenRegistration = slaveMonitor.CancellationToken.Register (OnSlaveCancelRequested); } - - public void BeginTask (string name, int totalWork) + + protected override void OnBeginTask (string name, int totalWork, int stepWork) { - foreach (MonitorInfo info in monitors) - if ((info.ActionMask & MonitorAction.Tasks) != 0) - info.Monitor.BeginTask (name, totalWork); + if (stepWork == -1) { + foreach (MonitorInfo info in monitors) + if ((info.ActionMask & MonitorAction.Tasks) != 0) + info.Monitor.BeginTask (name, totalWork); + } else { + foreach (MonitorInfo info in monitors) + if ((info.ActionMask & MonitorAction.Tasks) != 0) { + info.Monitor.BeginStep (stepWork); + info.Monitor.BeginTask (name, totalWork); + } + } } - - public void BeginStepTask (string name, int totalWork, int stepSize) + + protected override void OnEndTask (string name, int totalWork, int stepWork) { foreach (MonitorInfo info in monitors) if ((info.ActionMask & MonitorAction.Tasks) != 0) - info.Monitor.BeginStepTask (name, totalWork, stepSize); + info.Monitor.EndTask (); } - - public void EndTask () + + protected override void OnStep (string message, int work) { foreach (MonitorInfo info in monitors) if ((info.ActionMask & MonitorAction.Tasks) != 0) - info.Monitor.EndTask (); + info.Monitor.Step (message, work); } - - public void Step (int work) + + protected override ProgressMonitor CreateAsyncStepMonitor () + { + return new AggregatedProgressMonitor (); + } + + protected override void OnBeginAsyncStep (string message, int work, ProgressMonitor stepMonitor) { + var am = (AggregatedProgressMonitor) stepMonitor; foreach (MonitorInfo info in monitors) if ((info.ActionMask & MonitorAction.Tasks) != 0) - info.Monitor.Step (work); + am.AddSlaveMonitor (info.Monitor.BeginAsyncStep (message, work)); } - - public TextWriter Log + + protected override void OnWriteLog (string message) { - get { return logger; } + foreach (MonitorInfo info in monitors) + if ((info.ActionMask & MonitorAction.WriteLog) != 0) + info.Monitor.Log.Write (message); } - - void OnWriteLog (string text) + + protected override void OnWriteErrorLog (string message) { foreach (MonitorInfo info in monitors) if ((info.ActionMask & MonitorAction.WriteLog) != 0) - info.Monitor.Log.Write (text); + info.Monitor.ErrorLog.Write (message); } - - public void ReportSuccess (string message) + + protected override void OnSuccessReported (string message) { foreach (MonitorInfo info in monitors) if ((info.ActionMask & MonitorAction.ReportSuccess) != 0) info.Monitor.ReportSuccess (message); } - - public void ReportWarning (string message) + + protected override void OnWarningReported (string message) { foreach (MonitorInfo info in monitors) if ((info.ActionMask & MonitorAction.ReportWarning) != 0) info.Monitor.ReportWarning (message); } - - public void ReportError (string message, Exception ex) + + protected override void OnErrorReported (string message, Exception exception) { foreach (MonitorInfo info in monitors) if ((info.ActionMask & MonitorAction.ReportError) != 0) - info.Monitor.ReportError (message, ex); + info.Monitor.ReportError (message, exception); } - - public void Dispose () + + public override void Dispose () { foreach (MonitorInfo info in monitors) { if ((info.ActionMask & MonitorAction.Dispose) != 0) info.Monitor.Dispose (); if ((info.ActionMask & MonitorAction.SlaveCancel) != 0) - info.Monitor.CancelRequested -= new MonitorHandler (OnSlaveCancelRequested); - } - } - - public bool IsCancelRequested - { - get { - foreach (MonitorInfo info in monitors) - if ((info.ActionMask & MonitorAction.SlaveCancel) != 0) { - if (info.Monitor.IsCancelRequested) return true; - } - return false; + info.CancellationTokenRegistration.Dispose (); } } - - public object SyncRoot { - get { return this; } - } - - void OnSlaveCancelRequested (IProgressMonitor sender) - { - AsyncOperation.Cancel (); - } - - public IAsyncOperation AsyncOperation - { - get { return this; } - } - - void IAsyncOperation.Cancel () - { - foreach (MonitorInfo info in monitors) - if ((info.ActionMask & MonitorAction.Cancel) != 0 && !info.Monitor.IsCancelRequested) - info.Monitor.AsyncOperation.Cancel (); - } - - void IAsyncOperation.WaitForCompleted () + + void OnSlaveCancelRequested () { - masterMonitor.AsyncOperation.WaitForCompleted (); - } - - public bool IsCompleted { - get { return masterMonitor.AsyncOperation.IsCompleted; } - } - - bool IAsyncOperation.Success { - get { return masterMonitor.AsyncOperation.Success; } - } - - bool IAsyncOperation.SuccessWithWarnings { - get { return masterMonitor.AsyncOperation.SuccessWithWarnings; } - } - - public event MonitorHandler CancelRequested { - add { masterMonitor.CancelRequested += value; } - remove { masterMonitor.CancelRequested -= value; } - } - - public event OperationHandler Completed { - add { masterMonitor.AsyncOperation.Completed += value; } - remove { masterMonitor.AsyncOperation.Completed -= value; } + cancelSource.Cancel (); } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AsyncOperation.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AsyncOperation.cs deleted file mode 100644 index 59201ae555..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/AsyncOperation.cs +++ /dev/null @@ -1,134 +0,0 @@ -// AsyncOperation.cs -// -// Author: -// Lluis Sanchez Gual <lluis@novell.com> -// -// Copyright (c) 2008 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; - -namespace MonoDevelop.Core -{ - public class AsyncOperation: IAsyncOperation - { - bool canceled; - bool completed; - bool success; - bool successWithWarnings; - IAsyncOperation trackedOperation; - object lck = new object (); - - OperationHandler completedEvent; - - public event OperationHandler Completed { - add { - bool done = false; - lock (lck) { - if (completed) - done = true; - else - completedEvent += value; - } - if (done) - value (this); - } - remove { - lock (lck) { - completedEvent -= value; - } - } - } - - public event OperationHandler CancelRequested; - - public bool Canceled { - get { return canceled; } - } - - public void Cancel () - { - canceled = true; - if (trackedOperation != null) - trackedOperation.Cancel (); - if (CancelRequested != null) - CancelRequested (this); - } - - void IAsyncOperation.WaitForCompleted () - { - lock (lck) { - if (!completed) - Monitor.Wait (lck); - } - } - - public bool IsCompleted { - get { - return completed; - } - } - - public bool Success { - get { - return success; - } - } - - public bool SuccessWithWarnings { - get { - return successWithWarnings; - } - } - - public void TrackOperation (IAsyncOperation oper, bool isFinal) - { - if (trackedOperation != null) - throw new InvalidOperationException ("An operation is already being tracked."); - trackedOperation = oper; - oper.Completed += delegate { - if (!oper.Success || isFinal) - SetCompleted (oper.Success, oper.SuccessWithWarnings); - trackedOperation = null; - }; - } - - public void SetCompleted (bool success) - { - SetCompleted (success, false); - } - - public void SetCompleted (bool success, bool hasWarnings) - { - lock (lck) { - completed = true; - this.success = success; - if (success && hasWarnings) - successWithWarnings = true; - Monitor.PulseAll (lck); - if (completedEvent != null) - completedEvent (this); - } - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ConsoleProgressMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ConsoleProgressMonitor.cs index 82410eae20..fb8fc26d40 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ConsoleProgressMonitor.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ConsoleProgressMonitor.cs @@ -31,7 +31,7 @@ using System.IO; namespace MonoDevelop.Core.ProgressMonitoring { - public class ConsoleProgressMonitor: NullProgressMonitor + public class ConsoleProgressMonitor: ProgressMonitor { int columns = 0; bool leaveOpen; @@ -40,7 +40,6 @@ namespace MonoDevelop.Core.ProgressMonitoring int ilevel = 0; int isize = 3; int col = -1; - LogTextWriter logger; bool ignoreLogMessages; TextWriter writer; @@ -67,8 +66,6 @@ namespace MonoDevelop.Core.ProgressMonitoring { this.writer = writer; this.leaveOpen = leaveOpen; - logger = new LogTextWriter (); - logger.TextWritten += WriteLog; } public ConsoleProgressMonitor (TextWriter writer) : this (writer, false) @@ -77,9 +74,6 @@ namespace MonoDevelop.Core.ProgressMonitoring public override void Dispose () { - logger.TextWritten -= WriteLog; - logger.Dispose (); - if (!leaveOpen) writer.Dispose (); @@ -110,7 +104,7 @@ namespace MonoDevelop.Core.ProgressMonitoring set { indent = value; } } - public override void BeginTask (string name, int totalWork) + protected override void OnBeginTask (string name, int totalWork, int stepWork) { if (!ignoreLogMessages) { WriteText (name); @@ -118,38 +112,29 @@ namespace MonoDevelop.Core.ProgressMonitoring } } - public override void BeginStepTask (string name, int totalWork, int stepSize) - { - BeginTask (name, totalWork); - } - - public override void EndTask () + protected override void OnEndTask (string name, int totalWork, int stepWork) { if (!ignoreLogMessages) Unindent (); } - - void WriteLog (string text) + + protected override void OnWriteLog (string message) { if (!ignoreLogMessages) - WriteText (text); - } - - public override TextWriter Log { - get { return logger; } + WriteText (message); } - - public override void ReportSuccess (string message) + + protected override void OnSuccessReported (string message) { WriteText (message + "\n"); } - - public override void ReportWarning (string message) + + protected override void OnWarningReported (string message) { WriteText ("WARNING: " + message + "\n"); } - - public override void ReportError (string message, Exception ex) + + protected override void OnErrorReported (string message, Exception ex) { if (message == null && ex != null) message = ex.Message; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ConsoleProjectLoadProgressMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ConsoleProjectLoadProgressMonitor.cs index 2447804b9b..22e3dce120 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ConsoleProjectLoadProgressMonitor.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ConsoleProjectLoadProgressMonitor.cs @@ -27,17 +27,14 @@ using System; namespace MonoDevelop.Core.ProgressMonitoring { - public class ConsoleProjectLoadProgressMonitor : WrappedProgressMonitor, IProjectLoadProgressMonitor + public class ConsoleProjectLoadProgressMonitor : ProjectLoadProgressMonitor { - public MonoDevelop.Projects.Solution CurrentSolution { get; set; } - - public ConsoleProjectLoadProgressMonitor (IProgressMonitor monitor) - : base (monitor) + public ConsoleProjectLoadProgressMonitor (ConsoleProgressMonitor monitor) { - + AddSlaveMonitor (monitor); } - public MonoDevelop.Projects.Extensions.MigrationType ShouldMigrateProject () + public override MonoDevelop.Projects.Extensions.MigrationType ShouldMigrateProject () { Console.WriteLine ("Warning: One or more projects in this solution cannot be "); Console.WriteLine ("compiled unless they are migrated to a newer format. Please "); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/FilteredProgressMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/FilteredProgressMonitor.cs index 4c1f97df84..57fae2a68f 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/FilteredProgressMonitor.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/FilteredProgressMonitor.cs @@ -34,12 +34,12 @@ namespace MonoDevelop.Core.ProgressMonitoring // the operation being monitorized provides. public class FilteredProgressMonitor: AggregatedProgressMonitor { - public FilteredProgressMonitor (IProgressMonitor targetMonitor) + public FilteredProgressMonitor (ProgressMonitor targetMonitor) : this (targetMonitor, MonitorAction.WriteLog | MonitorAction.ReportError | MonitorAction.ReportWarning | MonitorAction.ReportSuccess | MonitorAction.Cancel | MonitorAction.SlaveCancel) { } - public FilteredProgressMonitor (IProgressMonitor targetMonitor, MonitorAction actionMask) + public FilteredProgressMonitor (ProgressMonitor targetMonitor, MonitorAction actionMask) { AddSlaveMonitor (targetMonitor, actionMask); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/NullAsyncOperation.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/NullAsyncOperation.cs deleted file mode 100644 index 4262120002..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/NullAsyncOperation.cs +++ /dev/null @@ -1,73 +0,0 @@ -// -// NullAsyncOperation.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (C) 2005 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; - -namespace MonoDevelop.Core.ProgressMonitoring -{ - public class NullAsyncOperation: IAsyncOperation - { - public static NullAsyncOperation Success = new NullAsyncOperation (true, false); - public static NullAsyncOperation Failure = new NullAsyncOperation (false, false); - - bool success; - bool warnings; - - protected NullAsyncOperation (bool success, bool warnings) - { - this.success = success; - this.warnings = warnings; - } - - public void Cancel () - { - } - - public void WaitForCompleted () - { - } - - public bool IsCompleted { - get { return true; } - } - - bool IAsyncOperation.Success { - get { return success; } - } - - bool IAsyncOperation.SuccessWithWarnings { - get { return success && warnings; } - } - - public event OperationHandler Completed { - add { value (this); } - remove {} - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/NullProgressMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/NullProgressMonitor.cs deleted file mode 100644 index d79899c4f0..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/NullProgressMonitor.cs +++ /dev/null @@ -1,242 +0,0 @@ -// -// NullProgressMonitor.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (C) 2005 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.Collections.Generic; -using System.Threading; -using System.IO; - -namespace MonoDevelop.Core.ProgressMonitoring -{ - public class NullProgressMonitor: IProgressMonitor, IAsyncOperation - { - bool done, canceled, error; - ManualResetEvent waitEvent; - List<ProgressError> errors; - List<string> warnings; - List<string> messages; - - public object SyncRoot { - get { return this; } - } - - public string[] Messages { - get { - if (messages != null) - return messages.ToArray (); - return new string [0]; - } - } - - public string[] Warnings { - get { - if (warnings != null) - return warnings.ToArray (); - return new string [0]; - } - } - - public ProgressError[] Errors { - get { - if (errors != null) - return errors.ToArray (); - return new ProgressError [0]; - } - } - - public virtual void BeginTask (string name, int totalWork) - { - } - - public virtual void EndTask () - { - } - - public virtual void BeginStepTask (string name, int totalWork, int stepSize) - { - } - - public virtual void Step (int work) - { - } - - public virtual TextWriter Log { - get { return TextWriter.Null; } - } - - public virtual void ReportSuccess (string message) - { - if (messages == null) - messages = new List<string> (); - messages.Add (message); - } - - public virtual void ReportWarning (string message) - { - if (warnings == null) - warnings = new List<string> (); - warnings.Add (message); - } - - public virtual void ReportError (string message, Exception ex) - { - if (errors == null) - errors = new List<ProgressError> (); - - if (message == null && ex != null) - message = ex.Message; - else if (message != null && ex != null) { - if (!message.EndsWith (".")) message += "."; - message += " " + ex.Message; - } - - errors.Add (new ProgressError (message, ex)); - error = true; - } - - public bool IsCancelRequested { - get { return canceled; } - } - - public virtual void Dispose () - { - lock (this) { - if (done) return; - done = true; - if (waitEvent != null) - waitEvent.Set (); - } - OnCompleted (); - } - - public IAsyncOperation AsyncOperation - { - get { return this; } - } - - void IAsyncOperation.Cancel () - { - OnCancelRequested (); - } - - void IAsyncOperation.WaitForCompleted () - { - lock (this) { - if (done) return; - if (waitEvent == null) - waitEvent = new ManualResetEvent (false); - } - waitEvent.WaitOne (); - } - - bool IAsyncOperation.IsCompleted - { - get { return done; } - } - - bool IAsyncOperation.Success { - get { return !error && !canceled; } - } - - bool IAsyncOperation.SuccessWithWarnings { - get { return !error && warnings != null; } - } - - public event OperationHandler Completed { - add { - bool alreadyCompleted = false; - lock (this) { - completedEvent += value; - alreadyCompleted = done; - } - if (alreadyCompleted) value (this); - } - remove { - lock (this) { - completedEvent -= value; - } - } - } - - public event MonitorHandler CancelRequested { - add { - bool alreadyCanceled = false; - lock (this) { - cancelRequestedEvent += value; - alreadyCanceled = canceled; - } - if (alreadyCanceled) value (this); - } - remove { - lock (this) { - cancelRequestedEvent -= value; - } - } - } - - protected virtual void OnCancelRequested () - { - lock (this) { - if (canceled) return; - canceled = true; - } - if (cancelRequestedEvent != null) - cancelRequestedEvent (this); - } - - protected virtual void OnCompleted () - { - if (completedEvent != null) - completedEvent (AsyncOperation); - } - - event MonitorHandler cancelRequestedEvent; - event OperationHandler completedEvent; - } - - public class ProgressError - { - Exception ex; - string message; - - public ProgressError (string message, Exception ex) - { - this.ex = ex; - this.message = message; - } - - public string Message { - get { return message; } - } - - public Exception Exception { - get { return ex; } - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ProgressStatusMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ProgressStatusMonitor.cs index b35e4043de..6f3c772b21 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ProgressStatusMonitor.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ProgressStatusMonitor.cs @@ -33,15 +33,16 @@ namespace MonoDevelop.Core.ProgressMonitoring { public class ProgressStatusMonitor: MarshalByRefObject, IProgressStatus, IDisposable { - IProgressMonitor monitor; + ProgressMonitor monitor; int step; int logLevel; + bool canceled; - public ProgressStatusMonitor (IProgressMonitor monitor): this (monitor, 1) + public ProgressStatusMonitor (ProgressMonitor monitor): this (monitor, 1) { } - public ProgressStatusMonitor (IProgressMonitor monitor, int logLevel) + public ProgressStatusMonitor (ProgressMonitor monitor, int logLevel) { this.logLevel = logLevel; this.monitor = monitor; @@ -77,7 +78,7 @@ namespace MonoDevelop.Core.ProgressMonitoring } public bool IsCanceled { - get { return monitor.IsCancelRequested; } + get { return monitor.CancellationToken.IsCancellationRequested || canceled; } } public int LogLevel { @@ -87,7 +88,7 @@ namespace MonoDevelop.Core.ProgressMonitoring public void Cancel () { - monitor.AsyncOperation.Cancel (); + canceled = true; } public void Dispose () diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/IProjectLoadProgressMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ProjectLoadProgressMonitor.cs index e6511a2215..e65d489a08 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/IProjectLoadProgressMonitor.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/ProjectLoadProgressMonitor.cs @@ -28,10 +28,10 @@ using MonoDevelop.Projects.Extensions; namespace MonoDevelop.Core { - public interface IProjectLoadProgressMonitor : IProgressMonitor + public abstract class ProjectLoadProgressMonitor : ProgressMonitor { - MonoDevelop.Projects.Solution CurrentSolution { get; set; } - MigrationType ShouldMigrateProject (); + public MonoDevelop.Projects.Solution CurrentSolution { get; set; } + public abstract MigrationType ShouldMigrateProject (); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/SimpleProgressMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/SimpleProgressMonitor.cs deleted file mode 100644 index 13ff23051e..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/SimpleProgressMonitor.cs +++ /dev/null @@ -1,77 +0,0 @@ -// -// SimpleProgressMonitor.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; - -namespace MonoDevelop.Core.ProgressMonitoring -{ - public class SimpleProgressMonitor: NullProgressMonitor - { - ProgressTracker tracker = new ProgressTracker (); - - protected ProgressTracker Tracker { - get { return tracker; } - } - - public SimpleProgressMonitor() - { - } - - public override void BeginTask (string name, int totalWork) - { - tracker.BeginTask (name, totalWork); - OnProgressChanged (); - } - - public override void BeginStepTask (string name, int totalWork, int stepSize) - { - tracker.BeginStepTask (name, totalWork, stepSize); - OnProgressChanged (); - } - - public override void EndTask () - { - tracker.EndTask (); - OnProgressChanged (); - } - - public override void Step (int work) - { - tracker.Step (work); - OnProgressChanged (); - } - - protected override void OnCompleted () - { - base.OnCompleted (); - OnProgressChanged (); - } - - protected virtual void OnProgressChanged () - { - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/SynchronizedProgressMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/SynchronizedProgressMonitor.cs deleted file mode 100644 index 083000735f..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/SynchronizedProgressMonitor.cs +++ /dev/null @@ -1,139 +0,0 @@ -// -// SynchronizedProgressMonitor.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (C) 2005 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.IO; - -namespace MonoDevelop.Core.ProgressMonitoring -{ - public sealed class SynchronizedProgressMonitor: IProgressMonitor - { - IProgressMonitor monitor; - - public SynchronizedProgressMonitor (IProgressMonitor monitor) - { - this.monitor = monitor; - } - - public void BeginTask (string name, int totalWork) - { - lock (monitor.SyncRoot) { - monitor.BeginTask (name, totalWork); - } - } - - public void BeginStepTask (string name, int totalWork, int stepSize) - { - lock (monitor.SyncRoot) { - monitor.BeginStepTask (name, totalWork, stepSize); - } - } - - public void EndTask () - { - lock (monitor.SyncRoot) { - monitor.EndTask (); - } - } - - public void Step (int work) - { - lock (monitor.SyncRoot) { - monitor.Step (work); - } - } - - public TextWriter Log { - get { return monitor.Log; } - } - - public void ReportSuccess (string message) - { - lock (monitor.SyncRoot) { - monitor.ReportSuccess (message); - } - } - - public void ReportWarning (string message) - { - lock (monitor.SyncRoot) { - monitor.ReportWarning (message); - } - } - - public void ReportError (string message, Exception ex) - { - lock (monitor.SyncRoot) { - monitor.ReportError (message, ex); - } - } - - public bool IsCancelRequested { - get { - lock (monitor.SyncRoot) { - return monitor.IsCancelRequested; - } - } - } - - public void Dispose () - { - lock (monitor.SyncRoot) { - monitor.Dispose (); - } - } - - public IAsyncOperation AsyncOperation - { - get { - lock (monitor.SyncRoot) { - return monitor.AsyncOperation; - } - } - } - - public event MonitorHandler CancelRequested { - add { - lock (monitor.SyncRoot) { - monitor.CancelRequested += value; - } - } - remove { - lock (monitor.SyncRoot) { - monitor.CancelRequested -= value; - } - } - } - - public object SyncRoot { - get { return monitor.SyncRoot; } - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/WrappedProgressMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/WrappedProgressMonitor.cs deleted file mode 100644 index 66b961880a..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.ProgressMonitoring/WrappedProgressMonitor.cs +++ /dev/null @@ -1,103 +0,0 @@ -// -// ProjectLoadProgressMonitor.cs -// -// Author: -// Alan McGovern <alan@xamarin.com> -// -// Copyright 2011 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; - -namespace MonoDevelop.Core -{ - public class WrappedProgressMonitor : IProgressMonitor - { - public event MonitorHandler CancelRequested { - add { WrappedMonitor.CancelRequested += value; } - remove { WrappedMonitor.CancelRequested -= value; } - } - - public IAsyncOperation AsyncOperation { - get { return WrappedMonitor.AsyncOperation; } - } - - public bool IsCancelRequested { - get { return WrappedMonitor.IsCancelRequested; } - } - - public System.IO.TextWriter Log { - get { return WrappedMonitor.Log; } - } - - public object SyncRoot { - get { return WrappedMonitor.SyncRoot; } - } - - IProgressMonitor WrappedMonitor { - get; set; - } - - public WrappedProgressMonitor (IProgressMonitor monitor) - { - WrappedMonitor = monitor; - } - - public void BeginStepTask (string name, int totalWork, int stepSize) - { - WrappedMonitor.BeginStepTask (name, totalWork, stepSize); - } - - public void BeginTask (string name, int totalWork) - { - WrappedMonitor.BeginTask (name, totalWork); - } - - public void Dispose () - { - WrappedMonitor.Dispose (); - } - - public void EndTask () - { - WrappedMonitor.EndTask (); - } - - public void ReportError (string message, Exception exception) - { - WrappedMonitor.ReportError (message, exception); - } - - public void ReportSuccess (string message) - { - WrappedMonitor.ReportSuccess (message); - } - - public void ReportWarning (string message) - { - WrappedMonitor.ReportWarning (message); - } - - public void Step (int work) - { - WrappedMonitor.Step (work); - } - } -} - diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Serialization/SerializationContext.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Serialization/SerializationContext.cs index 37df92c37f..d3179c26b8 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Serialization/SerializationContext.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Serialization/SerializationContext.cs @@ -37,7 +37,7 @@ namespace MonoDevelop.Core.Serialization string file; IPropertyFilter propertyFilter; DataSerializer serializer; - IProgressMonitor monitor; + ProgressMonitor monitor; char directorySeparatorChar = System.IO.Path.DirectorySeparatorChar; HashSet<ItemProperty> forcedSerializationProps; @@ -73,7 +73,7 @@ namespace MonoDevelop.Core.Serialization } } - public IProgressMonitor ProgressMonitor { + public ProgressMonitor ProgressMonitor { get { return monitor; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.addin.xml b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.addin.xml index f91a6b60eb..abba41846d 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.addin.xml +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.addin.xml @@ -118,6 +118,23 @@ </ExtensionNode> </ExtensionPoint> + <ExtensionPoint path = "/MonoDevelop/ProjectModel/ProjectModelExtensions" name = "Project model extensions"> + <Description>Project model extensions</Description> + <ExtensionNode name="ObjectExtension" type="MonoDevelop.Projects.Extensions.ProjectModelExtensionNode"> + <Description>A subclass of WorkspaceObjectmExtension.</Description> + </ExtensionNode> + <ExtensionNode name="ProjectFlavor" type="MonoDevelop.Projects.Extensions.SolutionItemExtensionNode"> + <Description>A subclass of ProjectExtension.</Description> + </ExtensionNode> + </ExtensionPoint> + + <ExtensionPoint path = "/MonoDevelop/ProjectModel/MSBuildProjectExtensions" name = "MSBuild project extensions"> + <Description>MSBuild project extensions. Specified classes must be a subclass of MSBuildProjectExtension</Description> + <ExtensionNode name="Class"> + <Description>A subclass of MSBuildProjectExtension.</Description> + </ExtensionNode> + </ExtensionPoint> + <ExtensionPoint path = "/MonoDevelop/ProjectModel/LanguageBindings" name = "Language bindings"> <Description>Language bindings.</Description> <ExtensionNode name="LanguageBinding" type="MonoDevelop.Projects.Extensions.LanguageBindingCodon"/> @@ -125,9 +142,9 @@ <ExtensionPoint path = "/MonoDevelop/ProjectModel/MSBuildItemTypes" name = "MSBuild item types"> <Description>Item types supported by MSBuild</Description> - <ExtensionNode name="SolutionItem" type="MonoDevelop.Projects.Extensions.SolutionItemNode"/> - <ExtensionNode name="DotNetProject" type="MonoDevelop.Projects.Extensions.DotNetProjectNode"/> - <ExtensionNode name="DotNetProjectSubtype" type="MonoDevelop.Projects.Extensions.DotNetProjectSubtypeNode"/> + <ExtensionNode name="SolutionItemType" type="MonoDevelop.Projects.Extensions.SolutionItemTypeNode"/> + <ExtensionNode name="ProjectType" type="MonoDevelop.Projects.Extensions.ProjectTypeNode"/> + <ExtensionNode name="DotNetProjectType" type="MonoDevelop.Projects.Extensions.DotNetProjectTypeNode"/> </ExtensionPoint> <ExtensionPoint path = "/MonoDevelop/ProjectModel/PolicyTypes" name = "Policy types"> @@ -149,10 +166,6 @@ <ExtensionNode objectType="MonoDevelop.Projects.Formats.MSBuild.IMSBuildGlobalPropertyProvider"/> </ExtensionPoint> - <ExtensionPoint path = "/MonoDevelop/ProjectModel/MSBuildExtensions" name = "MSBuild Extensions"> - <ExtensionNode name="Class" objectType="MonoDevelop.Projects.Formats.MSBuild.MSBuildExtension"/> - </ExtensionPoint> - <ExtensionPoint path = "/MonoDevelop/ProjectModel/MonoDocSources" name = "MonoDoc Sources"> <ExtensionNode name="Source" type="MonoDevelop.Projects.Extensions.MonoDocSourceNode" /> </ExtensionPoint> @@ -222,7 +235,6 @@ <Extension path = "/MonoDevelop/ProjectModel/ProjectBindings"> <ProjectBinding id = "DotNet" class = "MonoDevelop.Projects.DotNetProjectBinding" /> <ProjectBinding id = "GenericProject" class = "MonoDevelop.Projects.GenericProjectBinding" /> - <ProjectBinding id = "PortableDotNet" class = "MonoDevelop.Projects.PortableDotNetProjectBinding" /> <ProjectBinding id = "SharedAssetsProject" class = "MonoDevelop.Projects.SharedAssetsProjects.SharedAssetsProjectBinding" /> </Extension> @@ -275,17 +287,16 @@ </Extension> <Extension path = "/MonoDevelop/ProjectModel/MSBuildItemTypes"> - <SolutionItem guid = "{8BC9CEB9-8B4A-11D0-8D11-00A0C91BC942}" + <MSBuildProject guid = "{8BC9CEB9-8B4A-11D0-8D11-00A0C91BC942}" type = "MonoDevelop.Projects.CompiledAssemblyProject" handlerType = "MonoDevelop.Projects.Formats.MSBuild.CompiledAssemblyProjectMSBuildHandler" /> - <SolutionItem guid = "{D954291E-2A0B-460D-934E-DC6B0785DB48}" + <MSBuildProject guid = "{D954291E-2A0B-460D-934E-DC6B0785DB48}" type = "MonoDevelop.Projects.SharedAssetsProjects.SharedAssetsProject" - extension = "shproj" - handlerType = "MonoDevelop.Projects.SharedAssetsProjects.SharedAssetsProjectMSBuildHandler" /> + extension = "shproj" /> </Extension> - <Extension path = "/MonoDevelop/ProjectModel/MSBuildExtensions"> - <Class class="MonoDevelop.Projects.SharedAssetsProjects.SharedAssetsProjectMSBuildExtension"/> + <Extension path = "/MonoDevelop/ProjectModel/ProjectModelExtensions"> + <ProjectExtension class="MonoDevelop.Projects.SharedAssetsProjects.SharedAssetsProjectMSBuildExtension"/> </Extension> diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj index 9d41f0a38d..54929d364b 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj @@ -70,9 +70,6 @@ <NoWarn>1591;1573</NoWarn> <DocumentationFile>..\..\..\build\bin\MonoDevelop.Core.xml</DocumentationFile> </PropertyGroup> - <PropertyGroup Condition="'$(BUILD_REVISION)' != ''"> - <DefineConstants>$(DefineConstants);ENABLE_RAYGUN</DefineConstants> - </PropertyGroup> <ItemGroup> <Reference Include="System" /> <Reference Include="Mono.Posix" /> @@ -88,6 +85,7 @@ <Reference Include="System.Web" /> <Reference Include="System.ServiceModel" /> <Reference Include="Microsoft.CSharp" /> + <Reference Include="Microsoft.Build.Engine" /> </ItemGroup> <ItemGroup> <Compile Include="MonoDevelop.Core\StringParserService.cs" /> @@ -104,21 +102,16 @@ <Compile Include="MonoDevelop.Core.Execution\RemoteProcessObject.cs" /> <Compile Include="MonoDevelop.Core.ProgressMonitoring\AggregatedProgressMonitor.cs" /> <Compile Include="MonoDevelop.Core.ProgressMonitoring\ConsoleProgressMonitor.cs" /> - <Compile Include="MonoDevelop.Core\IAsyncOperation.cs" /> <Compile Include="MonoDevelop.Core\IProgressMonitor.cs" /> <Compile Include="MonoDevelop.Core.ProgressMonitoring\LogTextWriter.cs" /> - <Compile Include="MonoDevelop.Core.ProgressMonitoring\NullAsyncOperation.cs" /> <Compile Include="MonoDevelop.Core.ProgressMonitoring\ProgressTracker.cs" /> <Compile Include="MonoDevelop.Core.Execution\DefaultExecutionHandler.cs" /> <Compile Include="MonoDevelop.Core.Execution\ExternalConsoleFactory.cs" /> <Compile Include="MonoDevelop.Core.Execution\IConsole.cs" /> <Compile Include="MonoDevelop.Core.Execution\IConsoleFactory.cs" /> <Compile Include="MonoDevelop.Core.Execution\IExecutionHandler.cs" /> - <Compile Include="MonoDevelop.Core.Execution\IProcessAsyncOperation.cs" /> <Compile Include="MonoDevelop.Core.Execution\MonoPlatformExecutionHandler.cs" /> <Compile Include="MonoDevelop.Core.Execution\NativePlatformExecutionHandler.cs" /> - <Compile Include="MonoDevelop.Core.ProgressMonitoring\AggregatedOperationMonitor.cs" /> - <Compile Include="MonoDevelop.Core.ProgressMonitoring\SynchronizedProgressMonitor.cs" /> <Compile Include="MonoDevelop.Core\ClrVersion.cs" /> <Compile Include="MonoDevelop.Core\FileService.cs" /> <Compile Include="MonoDevelop.Core\FileEventArgs.cs" /> @@ -178,7 +171,6 @@ <Compile Include="MonoDevelop.Core.Collections\Set.cs" /> <Compile Include="MonoDevelop.Core.Collections\ReadOnlyDictionary.cs" /> <Compile Include="MonoDevelop.Core\Gettext.cs" /> - <Compile Include="MonoDevelop.Core.ProgressMonitoring\AsyncOperation.cs" /> <Compile Include="MonoDevelop.Core.AddIns\PackageInstalledCondition.cs" /> <Compile Include="MonoDevelop.Core\ComponentModelLocalization.cs" /> <Compile Include="MonoDevelop.Core.AddIns\ITargetRuntimeFactory.cs" /> @@ -224,12 +216,9 @@ <Compile Include="MonoDevelop.Core.Logging\RemoteLogger.cs" /> <Compile Include="MonoDevelop.Core.Instrumentation\TimeCounter.cs" /> <Compile Include="MonoDevelop.Core.AddIns\PlatformCondition.cs" /> - <Compile Include="MonoDevelop.Core.ProgressMonitoring\AggregatedAsyncOperation.cs" /> <Compile Include="MonoDevelop.Core.Serialization\BinaryDataSerializer.cs" /> <Compile Include="MonoDevelop.Core.Execution\LocalConsole.cs" /> <Compile Include="MonoDevelop.Core\IconId.cs" /> - <Compile Include="MonoDevelop.Core.ProgressMonitoring\NullProgressMonitor.cs" /> - <Compile Include="MonoDevelop.Core.ProgressMonitoring\SimpleProgressMonitor.cs" /> <Compile Include="Mono.Options.cs" /> <Compile Include="MonoDevelop.Core.Logging\InstrumentationLogger.cs" /> <Compile Include="MonoDevelop.Core.Instrumentation\TimerCounter.cs" /> @@ -237,12 +226,10 @@ <Compile Include="MonoDevelop.Projects\ProjectService.cs" /> <Compile Include="MonoDevelop.Projects\ProjectPathItemPropertyAttribute.cs" /> <Compile Include="MonoDevelop.Projects\SolutionConfiguration.cs" /> - <Compile Include="MonoDevelop.Projects\SolutionEntityItem.cs" /> <Compile Include="MonoDevelop.Projects\SolutionItemEventArgs.cs" /> <Compile Include="MonoDevelop.Projects\CombineEntryRenamedEventArgs.cs" /> <Compile Include="MonoDevelop.Projects\SolutionItemConfiguration.cs" /> <Compile Include="MonoDevelop.Projects\ProjectConfiguration.cs" /> - <Compile Include="MonoDevelop.Projects\DotNetProjectBinding.cs" /> <Compile Include="MonoDevelop.Projects\DotNetProjectConfiguration.cs" /> <Compile Include="MonoDevelop.Projects\Project.cs" /> <Compile Include="MonoDevelop.Projects\ProjectFile.cs" /> @@ -252,7 +239,6 @@ <Compile Include="MonoDevelop.Projects\ProjectFileCollection.cs" /> <Compile Include="MonoDevelop.Projects\ProjectReferenceCollection.cs" /> <Compile Include="MonoDevelop.Projects\ProjectCreateInformation.cs" /> - <Compile Include="MonoDevelop.Projects\IProjectBinding.cs" /> <Compile Include="MonoDevelop.Projects\IDotNetLanguageBinding.cs" /> <Compile Include="MonoDevelop.Projects\SolutionItemConfigurationCollection.cs" /> <Compile Include="MonoDevelop.Projects\ConfigurationEventHandler.cs" /> @@ -264,8 +250,6 @@ <Compile Include="MonoDevelop.Projects\CustomCommand.cs" /> <Compile Include="MonoDevelop.Projects\CustomCommandType.cs" /> <Compile Include="MonoDevelop.Projects\GenericProject.cs" /> - <Compile Include="MonoDevelop.Projects\GenericProjectBinding.cs" /> - <Compile Include="MonoDevelop.Projects\CustomCommandExtension.cs" /> <Compile Include="MonoDevelop.Projects\ProjectConvertTool.cs" /> <Compile Include="MonoDevelop.Projects\ProjectsServices.cs" /> <Compile Include="MonoDevelop.Projects\UnknownSolutionItem.cs" /> @@ -279,13 +263,11 @@ <Compile Include="MonoDevelop.Projects\WorkspaceItemCollection.cs" /> <Compile Include="MonoDevelop.Projects\WorkspaceItemEventArgs.cs" /> <Compile Include="MonoDevelop.Projects\SolutionEventArgs.cs" /> - <Compile Include="MonoDevelop.Projects\IWorkspaceObject.cs" /> <Compile Include="MonoDevelop.Projects\SolutionConfigurationCollection.cs" /> <Compile Include="MonoDevelop.Projects\ItemConfiguration.cs" /> <Compile Include="MonoDevelop.Projects\ItemConfigurationCollection.cs" /> <Compile Include="MonoDevelop.Projects\IConfigurationTarget.cs" /> <Compile Include="MonoDevelop.Projects\ItemCollection.cs" /> - <Compile Include="MonoDevelop.Projects\SolutionItem.cs" /> <Compile Include="MonoDevelop.Projects\SolutionFolderItemCollection.cs" /> <Compile Include="MonoDevelop.Projects\SolutionItemReference.cs" /> <Compile Include="MonoDevelop.Projects\FileFormat.cs" /> @@ -297,14 +279,10 @@ <Compile Include="MonoDevelop.Projects\FileCopySet.cs" /> <Compile Include="MonoDevelop.Projects\ProjectItem.cs" /> <Compile Include="MonoDevelop.Projects\ProjectItemCollection.cs" /> - <Compile Include="MonoDevelop.Projects\SimpleProjectItem.cs" /> <Compile Include="MonoDevelop.Projects\IFolderItem.cs" /> <Compile Include="MonoDevelop.Projects\IFileItem.cs" /> <Compile Include="MonoDevelop.Projects\ProjectParameters.cs" /> - <Compile Include="MonoDevelop.Projects\ConfigurationParameters.cs" /> <Compile Include="MonoDevelop.Projects\CyclicDependencyException.cs" /> - <Compile Include="MonoDevelop.Projects\DotNetAssemblyProject.cs" /> - <Compile Include="MonoDevelop.Projects\DotNetProjectParameters.cs" /> <Compile Include="MonoDevelop.Projects\ItemConfigurationSelector.cs" /> <Compile Include="MonoDevelop.Projects\SolutionConfigurationSelector.cs" /> <Compile Include="MonoDevelop.Projects\DefaultConfigurationSelector.cs" /> @@ -315,41 +293,31 @@ <Compile Include="MonoDevelop.Projects.Extensions\ItemPropertyCodon.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\LanguageBindingCodon.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\DataTypeCodon.cs" /> - <Compile Include="MonoDevelop.Projects.Extensions\ProjectBindingCodon.cs" /> - <Compile Include="MonoDevelop.Projects.Extensions\ItemTypeNode.cs" /> - <Compile Include="MonoDevelop.Projects.Extensions\DotNetProjectNode.cs" /> - <Compile Include="MonoDevelop.Projects.Extensions\SolutionItemNode.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\SerlializationMapNode.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\IFileFormat.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\FileFormatNode.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\ProjectExtensionUtil.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\ISolutionItemHandler.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\SolutionItemHandler.cs" /> - <Compile Include="MonoDevelop.Projects.Extensions\DotNetProjectSubtypeNode.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\PolicySetNode.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\PolicyNode.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\ItemTypeCondition.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\MonoDocSourceNode.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\ProjectLanguageCondition.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\IResourceHandler.cs" /> - <Compile Include="MonoDevelop.Projects.Extensions\IPathHandler.cs" /> <Compile Include="MonoDevelop.Projects.Extensions\IAssemblyReferenceHandler.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MD1\MD1DotNetProjectHandler.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MD1\MD1FileFormat.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MD1\MD1SolutionEntityItemHandler.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MD1\MD1SolutionItemHandler.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildFileFormat.cs" /> - <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildProjectHandler.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild\SlnFileFormat.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildProjectService.cs" /> - <Compile Include="MonoDevelop.Projects.Formats.MSBuild\SlnData.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildProject.cs" /> - <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildHandler.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildImportAttribute.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild\RemoteProjectBuilder.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild\IMSBuildImportProvider.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MergeToProjectAttribute.cs" /> - <Compile Include="MonoDevelop.Projects.Formats.MSBuild\CompiledAssemblyProjectMSBuildHandler.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild.Conditions\ConditionAndExpression.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild.Conditions\ConditionExpression.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild.Conditions\ConditionFactorExpresion.cs" /> @@ -413,8 +381,6 @@ <Compile Include="MonoDevelop.Core\UserProfile.cs" /> <Compile Include="MonoDevelop.Core\BrandingService.cs" /> <Compile Include="MonoDevelop.Projects\CleanEventHandler.cs" /> - <Compile Include="MonoDevelop.Core.ProgressMonitoring\IProjectLoadProgressMonitor.cs" /> - <Compile Include="MonoDevelop.Core.ProgressMonitoring\WrappedProgressMonitor.cs" /> <Compile Include="MonoDevelop.Core.ProgressMonitoring\ConsoleProjectLoadProgressMonitor.cs" /> <Compile Include="MonoDevelop.Core\SystemInformation.cs" /> <Compile Include="MonoDevelop.Core\MacSystemInformation.cs" /> @@ -430,11 +396,8 @@ <Compile Include="MonoDevelop.Core\PasswordService.cs" /> <Compile Include="MonoDevelop.Core\IPasswordProvider.cs" /> <Compile Include="MonoDevelop.Projects.Utility\ByteOrderMark.cs" /> - <Compile Include="MonoDevelop.Core.Instrumentation\IInstrumentationConsumer.cs" /> - <Compile Include="MonoDevelop.Projects\PortableDotNetProject.cs" /> <Compile Include="MonoDevelop.Core.AddIns\FilePathExtensionNode.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild\TargetsAvailableCondition.cs" /> - <Compile Include="MonoDevelop.Projects\PortableDotNetProjectBinding.cs" /> <Compile Include="MonoDevelop.Core.AddIns\AssemblyInstalledCondition.cs" /> <Compile Include="MonoDevelop.Core\WebCertificateService.cs" /> <Compile Include="MonoDevelop.Core\IWebCertificateProvider.cs" /> @@ -448,11 +411,8 @@ <Compile Include="MonoDevelop.Core.Logging\AssertLoggingTraceListener.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MD1\MD1ProjectService.cs" /> <Compile Include="MonoDevelop.Projects\UnknownProject.cs" /> - <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildExtension.cs" /> <Compile Include="MonoDevelop.Projects.SharedAssetsProjects\SharedAssetsProject.cs" /> <Compile Include="MonoDevelop.Projects.SharedAssetsProjects\SharedAssetsProjectMSBuildExtension.cs" /> - <Compile Include="MonoDevelop.Projects.SharedAssetsProjects\SharedAssetsProjectMSBuildHandler.cs" /> - <Compile Include="MonoDevelop.Projects.SharedAssetsProjects\SharedAssetsProjectBinding.cs" /> <Compile Include="MonoDevelop.Core\WebRequestHelper.cs" /> <Compile Include="MonoDevelop.Core.Web\IProxyAuthenticationHandler.cs" /> <Compile Include="MonoDevelop.Core.Web\CredentialStore.cs" /> @@ -473,6 +433,55 @@ <Compile Include="MonoDevelop.Projects\IDotNetFileContainer.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild\IMSBuildGlobalPropertyProvider.cs" /> <Compile Include="MonoDevelop.Projects\DotNetProjectImport.cs" /> + <Compile Include="MonoDevelop.Core.LogReporting\CrashReporter.cs" /> + <Compile Include="MonoDevelop.Core.Instrumentation\InstrumentationConsumer.cs" /> + <Compile Include="MonoDevelop.Projects\DotNetProjectExtension.cs" /> + <Compile Include="MonoDevelop.Projects.Extensions\MSBuildProjectExtensionNode.cs" /> + <Compile Include="MonoDevelop.Projects\ChainedExtension.cs" /> + <Compile Include="MonoDevelop.Projects\ExtensionChain.cs" /> + <Compile Include="MonoDevelop.Projects\SolutionFolderItem.cs" /> + <Compile Include="MonoDevelop.Projects\SolutionItem.cs" /> + <Compile Include="MonoDevelop.Projects\SolutionItemExtension.cs" /> + <Compile Include="MonoDevelop.Projects\WorkspaceItemExtension.cs" /> + <Compile Include="MonoDevelop.Projects\SolutionExtension.cs" /> + <Compile Include="MonoDevelop.Projects.Extensions\ProjectModelExtensionNode.cs" /> + <Compile Include="MonoDevelop.Projects.Extensions\SolutionItemExtensionNode.cs" /> + <Compile Include="MonoDevelop.Projects.Extensions\FlavorTypeCondition.cs" /> + <Compile Include="MonoDevelop.Projects\WorkspaceObject.cs" /> + <Compile Include="MonoDevelop.Projects\WorkspaceObjectExtension.cs" /> + <Compile Include="MonoDevelop.Projects\IProject.cs" /> + <Compile Include="MonoDevelop.Core.Execution\ProcessAsyncOperation.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildProperty.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildPropertyGroupMerged.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildPropertyGroup.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildItem.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildItemGroup.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildEvaluationContext.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildImport.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildObject.cs" /> + <Compile Include="MonoDevelop.Projects\IPropertySet.cs" /> + <Compile Include="MonoDevelop.Projects\IMSBuildDataObject.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\IMSBuildPropertySet.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildPropertyGroupEvaluated.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\SlnFile.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\UnknownSolutionItemTypeException.cs" /> + <Compile Include="MonoDevelop.Projects.Formats.MSBuild\MSBuildProjectHandler.cs" /> + <Compile Include="MonoDevelop.Projects\DotNetCompilerParameters.cs" /> + <Compile Include="MonoDevelop.Projects\PortableDotNetProjectFlavor.cs" /> + <Compile Include="MonoDevelop.Projects\SolutionItemFactory.cs" /> + <Compile Include="MonoDevelop.Projects.Extensions\ProjectMigrationHandler.cs" /> + <Compile Include="MonoDevelop.Projects.Extensions\ProjectMigrationHandlerNode.cs" /> + <Compile Include="MonoDevelop.Projects\RegisterProjectModelExtensionAttribute.cs" /> + <Compile Include="MonoDevelop.Projects\RegisterProjectFlavorAttribute.cs" /> + <Compile Include="MonoDevelop.Projects\RegisterSolutionItemTypeAttribute.cs" /> + <Compile Include="MonoDevelop.Projects\RegisterDotNetProject.cs" /> + <Compile Include="MonoDevelop.Projects\RegisterProjectAttribute.cs" /> + <Compile Include="MonoDevelop.Projects.Extensions\SolutionItemTypeNode.cs" /> + <Compile Include="MonoDevelop.Projects.Extensions\ProjectTypeNode.cs" /> + <Compile Include="MonoDevelop.Projects.Extensions\DotNetProjectTypeNode.cs" /> + <Compile Include="MonoDevelop.Projects\ProjectExtension.cs" /> + <Compile Include="MonoDevelop.Core\ProgressMonitor.cs" /> + <Compile Include="MonoDevelop.Core.ProgressMonitoring\ProjectLoadProgressMonitor.cs" /> </ItemGroup> <ItemGroup> <None Include="Makefile.am" /> @@ -560,10 +569,6 @@ <Project>{2B8F4F83-C2B3-4E84-A27B-8DEE1BE0E006}</Project> <Name>ICSharpCode.NRefactory.Cecil</Name> </ProjectReference> - <ProjectReference Include="..\..\..\external\raygun4net\Mindscape.Raygun4Net\Mindscape.Raygun4Net.csproj"> - <Project>{495E53B3-F3AF-4C4F-BAAF-865EFAA2F4A9}</Project> - <Name>Mindscape.Raygun4Net</Name> - </ProjectReference> </ItemGroup> <ItemGroup> <Content Include="MonoDevelop.Core.dll.config"> diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs index b15350f37b..851b1e289c 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; namespace MonoDevelop.Core { @@ -156,6 +157,11 @@ namespace MonoDevelop.Core return new FilePath (path);
} + public Task DeleteAsync () + { + return Task.Factory.StartNew (Delete); + } + public void Delete () { // Ensure that this file/directory and all children are writable diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs index 044704ec37..575e2fcead 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs @@ -566,6 +566,36 @@ namespace MonoDevelop.Core }
}
}
+
+ /// <summary>
+ /// Renames a directory
+ /// </summary>
+ /// <param name="sourceDir">Source directory</param>
+ /// <param name="destDir">Destination directory</param>
+ /// <remarks>
+ /// It works like Directory.Move, but it supports changing the case of a directory name in case-insensitive file systems
+ /// </remarks>
+ public static void SystemDirectoryRename (string sourceDir, string destDir)
+ {
+ if (Directory.Exists (destDir) && string.Equals (Path.GetFullPath (sourceDir), Path.GetFullPath (destDir), StringComparison.CurrentCultureIgnoreCase)) {
+ // If the destination directory exists but we can't find it with the provided name casing, then it means we are just changing the case
+ var existingDir = Directory.GetDirectories (Path.GetDirectoryName (destDir), Path.GetFileName (destDir)).FirstOrDefault ();
+ if (existingDir == null || (Path.GetFileName (existingDir) == Path.GetFileName (sourceDir))) {
+ var temp = destDir + ".renaming";
+ int n = 0;
+ while (Directory.Exists (temp) || File.Exists (temp))
+ temp = destDir + ".renaming_" + (n++);
+ Directory.Move (sourceDir, temp);
+ try {
+ Directory.Move (temp, destDir);
+ } catch {
+ Directory.Move (temp, sourceDir);
+ }
+ return;
+ }
+ }
+ Directory.Move (sourceDir, destDir);
+ }
/// <summary>
/// Removes the directory if it's empty.
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Gettext.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Gettext.cs index 8bf16f5fc7..8f0327c28e 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Gettext.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Gettext.cs @@ -84,6 +84,8 @@ namespace MonoDevelop.Core // MD is located at $prefix/lib/monodevelop/bin // adding "../../.." should give us $prefix string prefix = Path.Combine (Path.Combine (Path.Combine (location, ".."), ".."), ".."); + if (Platform.IsMac) + prefix = Path.Combine (prefix, "..", "MacOS"); //normalise it prefix = Path.GetFullPath (prefix); //catalogue is installed to "$prefix/share/locale" by default diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/IAsyncOperation.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/IAsyncOperation.cs deleted file mode 100644 index 214e9d7203..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/IAsyncOperation.cs +++ /dev/null @@ -1,46 +0,0 @@ -// -// IAsyncOperation.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (C) 2005 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; - -namespace MonoDevelop.Core -{ - public delegate void OperationHandler (IAsyncOperation op); - - public interface IAsyncOperation - { - void Cancel (); - void WaitForCompleted (); - bool IsCompleted { get; } - bool Success { get; } - bool SuccessWithWarnings { get; } - - event OperationHandler Completed; - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/IProgressMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/IProgressMonitor.cs index f8a056ab73..5e4baec441 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/IProgressMonitor.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/IProgressMonitor.cs @@ -29,36 +29,70 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MonoDevelop.Core.ProgressMonitoring; namespace MonoDevelop.Core { - public delegate void MonitorHandler (IProgressMonitor monitor); + public delegate void MonitorHandler (ProgressMonitor monitor); - public interface IProgressMonitor: IDisposable + public interface IProgressMonitorFactory { - void BeginTask (string name, int totalWork); - void BeginStepTask (string name, int totalWork, int stepSize); - void EndTask (); - void Step (int work); - - TextWriter Log { get; } - - void ReportWarning (string message); - - void ReportSuccess (string message); - void ReportError (string message, Exception exception); - - bool IsCancelRequested { get; } - event MonitorHandler CancelRequested; - - // The returned IAsyncOperation object must be thread safe - IAsyncOperation AsyncOperation { get; } - - object SyncRoot { get; } + ProgressMonitor CreateProgressMonitor (); } - - public interface IProgressMonitorFactory + + public class AsyncOperation + { + public static AsyncOperation CompleteOperation = new AsyncOperation (Task.FromResult(0), null); + + protected AsyncOperation () + { + Task = Task.FromResult (0); + } + + public AsyncOperation (Task task, CancellationTokenSource cancellationTokenSource) + { + Task = task; + this.CancellationTokenSource = cancellationTokenSource; + } + + public Task Task { get; protected set; } + + protected CancellationTokenSource CancellationTokenSource { get; set; } + + public bool IsCompleted { + get { return Task.IsCompleted; } + } + + public void Cancel () + { + if (CancellationTokenSource != null) + CancellationTokenSource.Cancel (); + } + + public void WaitForCompleted () + { + Task.Wait (); + } + } + + public class AsyncOperation<T>: AsyncOperation + { + public AsyncOperation (Task<T> task, CancellationTokenSource cancellationTokenSource): base (task, cancellationTokenSource) + { + } + + public new Task<T> Task { + get { return (Task<T>) base.Task; } + } + } + + public static class ProgressMonitorExtensions { - IProgressMonitor CreateProgressMonitor (); + public static ProgressMonitor WithCancellationSource (this ProgressMonitor monitor, CancellationTokenSource cancellationTokenSource) + { + return new AggregatedProgressMonitor (monitor, cancellationTokenSource); + } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs index 1bcbd46c3e..56e1db31f6 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs @@ -32,12 +32,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Diagnostics; - -#if ENABLE_RAYGUN -using System.Threading; -using Mindscape.Raygun4Net; -#endif - +using Mono.Addins; +using MonoDevelop.Core.LogReporting; using MonoDevelop.Core.Logging; using Mono.Unix.Native; @@ -49,9 +45,6 @@ namespace MonoDevelop.Core const string ReportCrashesKey = "MonoDevelop.LogAgent.ReportCrashes"; const string ReportUsageKey = "MonoDevelop.LogAgent.ReportUsage"; -#if ENABLE_RAYGUN - static RaygunClient raygunClient; -#endif static List<ILogger> loggers = new List<ILogger> (); static RemoteLogger remoteLogger; static DateTime timestamp; @@ -66,6 +59,8 @@ namespace MonoDevelop.Core // Thirdparameter shows if the exception is fatal or not public static Func<bool?, Exception, bool, bool?> UnhandledErrorOccured; + static List<CrashReporter> customCrashReporters = new List<CrashReporter> (); + static LoggingService () { var consoleLogger = new ConsoleLogger (); @@ -102,13 +97,6 @@ namespace MonoDevelop.Core timestamp = DateTime.Now; -#if ENABLE_RAYGUN - string raygunKey = BrandingService.GetString ("RaygunApiKey"); - if (raygunKey != null) { - raygunClient = new RaygunClient (raygunKey); - } -#endif - //remove the default trace listener on .NET, it throws up horrible dialog boxes for asserts Debug.Listeners.Clear (); @@ -186,6 +174,18 @@ namespace MonoDevelop.Core RestoreOutputRedirection (); } + public static void RegisterCrashReporter (CrashReporter reporter) + { + lock (customCrashReporters) + customCrashReporters.Add (reporter); + } + + public static void UnregisterCrashReporter (CrashReporter reporter) + { + lock (customCrashReporters) + customCrashReporters.Remove (reporter); + } + internal static void ReportUnhandledException (Exception ex, bool willShutDown) { ReportUnhandledException (ex, willShutDown, false, null); @@ -215,22 +215,10 @@ namespace MonoDevelop.Core if (ReportCrashes.HasValue && !ReportCrashes.Value) return; - var customData = new Hashtable (); - foreach (var cd in SystemInformation.GetDescription ()) - customData[cd.Title ?? ""] = cd.Description; - -#if ENABLE_RAYGUN - if (raygunClient != null) { - ThreadPool.QueueUserWorkItem (delegate { - try { - raygunClient.Send (ex, tags, customData, Runtime.Version.ToString ()); - } catch { - // If we get here then things have gone really wrong - we can't log anything or - // attempt to report anything. Drop any exception that ends up here. - } - }); + lock (customCrashReporters) { + foreach (var cr in customCrashReporters.Concat (AddinManager.GetExtensionObjects<CrashReporter> (true))) + cr.ReportCrash (ex, willShutDown, tags); } -#endif //ensure we don't lose the setting if (ReportCrashes != oldReportCrashes) { @@ -549,5 +537,4 @@ namespace MonoDevelop.Core #endregion } - } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Platform.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Platform.cs index cbcf9f619b..c973fa6308 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Platform.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Platform.cs @@ -35,11 +35,16 @@ namespace MonoDevelop.Core { public readonly static bool IsWindows; public readonly static bool IsMac; - + public readonly static bool IsLinux; + + public static Version OSVersion { get; private set; } + static Platform () { IsWindows = Path.DirectorySeparatorChar == '\\'; IsMac = !IsWindows && IsRunningOnMac (); + IsLinux = !IsMac && !IsWindows; + OSVersion = Environment.OSVersion.Version; // needed to make sure various p/invokes work if (Platform.IsWindows) { @@ -83,12 +88,31 @@ namespace MonoDevelop.Core static void InitMacFoundation () { dlopen ("/System/Library/Frameworks/Foundation.framework/Foundation", 0x1); + OSVersion = new Version (Gestalt ("sys1"), Gestalt ("sys2"), Gestalt ("sys3")); } [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool SetDllDirectory (string lpPathName); + [System.Runtime.InteropServices.DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int Gestalt (int selector, out int result); + + //TODO: there are other gestalt selectors that return info we might want to display + //mac API for obtaining info about the system + static int Gestalt (string selector) + { + System.Diagnostics.Debug.Assert (selector != null && selector.Length == 4); + int cc = selector[3] | (selector[2] << 8) | (selector[1] << 16) | (selector[0] << 24); + int result; + int ret = Gestalt (cc, out result); + if (ret != 0) { + LoggingService.LogError ("Error reading gestalt for selector '{0}': {1}", selector, ret); + return 0; + } + return result; + } + static void InitWindowsNativeLibs () { string location = null; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ProgressMonitor.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ProgressMonitor.cs new file mode 100644 index 0000000000..66b7c8a6a9 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ProgressMonitor.cs @@ -0,0 +1,673 @@ +// +// ProgressMonitor.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Linq; +using System.IO; +using System.Threading; +using MonoDevelop.Core.ProgressMonitoring; +using System.Collections.Generic; + +namespace MonoDevelop.Core +{ + public class ProgressMonitor: IDisposable + { + ProgressTask currentTask; + ProgressTask parentRootTask; + ProgressTask rootTask; + + LogTextWriter logWriter; + LogTextWriter errorLogWriter; + TextWriter customLogWriter; + TextWriter customErrorLogWriter; + + int openStepWork = -1; + ProgressMonitor parentMonitor; + SynchronizationContext context; + + List<ProgressError> errors = new List<ProgressError> (); + List<string> warnings = new List<string> (); + List<string> messages = new List<string> (); + + List<ProgressMonitor> slaveMonitors; + + public ProgressMonitor (): this (null, null) + { + } + + public ProgressMonitor (SynchronizationContext context): this (context, null) + { + } + + public ProgressMonitor (CancellationTokenSource cancellationTokenSource): this (null, cancellationTokenSource) + { + } + + public ProgressMonitor (SynchronizationContext context, CancellationTokenSource cancellationTokenSource) + { + this.cancellationTokenSource = cancellationTokenSource; + this.context = context; + logWriter = new LogTextWriter (); + logWriter.TextWritten += OnWriteLog; + + errorLogWriter = new LogTextWriter (); + errorLogWriter.TextWritten += OnWriteErrorLog; + } + + void SetParentTask (ProgressMonitor parent, ProgressTask task, int work) + { + parentMonitor = parent; + currentTask = parentRootTask = task; + openStepWork = work; + } + + public virtual void Dispose () + { + var t = parentRootTask; + parentRootTask = null; + while (currentTask != t && currentTask != null) + EndTask (); + + if (context != null) + context.Post ((o) => OnCompleted (), null); + else + OnCompleted (); + + if (slaveMonitors != null) { + foreach (var m in slaveMonitors) + m.Dispose (); + } + } + + protected void AddSlaveMonitor (ProgressMonitor monitor) + { + if (slaveMonitors == null) + slaveMonitors = new List<ProgressMonitor> (); + slaveMonitors.Add (monitor); + logWriter.ChainWriter (monitor.Log); + errorLogWriter.ChainWriter (monitor.ErrorLog); + } + + protected void RemoveSlaveMonitor (ProgressMonitor monitor) + { + if (slaveMonitors == null) + return; + slaveMonitors.Remove (monitor); + logWriter.UnchainWriter (monitor.Log); + errorLogWriter.UnchainWriter (monitor.ErrorLog); + } + + public ProgressTask CurrentTask { + get { + var t = currentTask; + while (t != null && t.Name == null) + t = t.ParentTask; + return t; + } + } + + public string CurrentTaskName { + get { + var t = CurrentTask; + return t != null ? t.Name ?? "" : ""; + } + } + + public IEnumerable<ProgressTask> GetRootTasks () + { + var t = rootTask; + if (t.Name != null) + return new [] {t}; + return t.GetChildrenTasks (); + } + + public IDisposable BeginTask (string name, int totalWork) + { + var t = new ProgressTask (this, name, totalWork); + if (openStepWork != -1) { + t.StepWork = openStepWork; + openStepWork = -1; + } + if (currentTask == null) + rootTask = t; + else + currentTask.AddChild (t); + + currentTask = t; + + if (name != null) { + if (context != null) + context.Post ((o) => OnBeginTask (name, totalWork, t.StepWork), null); + else + OnBeginTask (name, totalWork, t.StepWork); + } + + ReportProgressChanged (); + + if (slaveMonitors != null) { + foreach (var m in slaveMonitors) + m.BeginTask (name, totalWork); + } + return t; + } + + public void BeginTask (int totalWork) + { + BeginTask (null, totalWork); + } + + public void EndTask () + { + if (currentTask != null && currentTask != parentRootTask) { + openStepWork = -1; + var t = currentTask; + currentTask = t.ParentTask; + if (currentTask == null) + rootTask = null; + t.SetComplete (); + if (t.Name != null) { + if (context != null) + context.Post ((o) => OnEndTask (t.Name, t.TotalWork, t.StepWork), null); + else + OnEndTask (t.Name, t.TotalWork, t.StepWork); + } + } else + throw new InvalidOperationException ("Task not started"); + + ReportProgressChanged (); + + if (slaveMonitors != null) { + foreach (var m in slaveMonitors) + m.EndTask (); + } + } + + internal void EndTask (ProgressTask task) + { + while (currentTask != null && currentTask != task) + EndTask (); + EndTask (); + } + + public void Step (int work = 1) + { + Step (null, work); + } + + public void Step (string message, int work = 1) + { + if (currentTask == null) + throw new InvalidOperationException ("Task not started"); + if (work < 0) + throw new ArgumentException ("work can't be negative"); + + ConsumePendingWork (); + currentTask.Step (message, work); + + if (context != null) + context.Post ((o) => { + OnStep (message, work); + ReportProgressChanged (); + }, null); + else { + OnStep (message, work); + ReportProgressChanged (); + } + + if (slaveMonitors != null) { + foreach (var m in slaveMonitors) + m.Step (message, work); + } + } + + public void BeginStep (int work = 1) + { + BeginStep (null, work); + } + + public void BeginStep (string message, int work = 1) + { + if (currentTask == null) + throw new InvalidOperationException ("Task not started in progress monitor"); + if (work < 0) + throw new ArgumentException ("work can't be negative"); + + ConsumePendingWork (); + + openStepWork = work; + if (message != null) + currentTask.Step (message, 0); + + ReportProgressChanged (); + + if (slaveMonitors != null) { + foreach (var m in slaveMonitors) + m.BeginStep (message, work); + } + } + + public void EndStep () + { + ConsumePendingWork (); + + if (slaveMonitors != null) { + foreach (var m in slaveMonitors) + m.EndStep (); + } + } + + void ConsumePendingWork () + { + if (openStepWork != -1) { + currentTask.Step (null, openStepWork); + openStepWork = -1; + } + } + + public ProgressMonitor BeginAsyncStep (int work) + { + return BeginAsyncStep (null, work); + } + + public ProgressMonitor BeginAsyncStep (string message, int work) + { + if (currentTask == null) + throw new InvalidOperationException ("Task not started in progress monitor"); + if (work < 0) + throw new ArgumentException ("work can't be negative"); + + ConsumePendingWork (); + if (message != null) + currentTask.Step (message, 0); + + ProgressMonitor m = null; + if (context != null) + context.Send ((o) => m = CreateAsyncStepMonitor (), null); + else + m = CreateAsyncStepMonitor (); + + m.SetParentTask (this, currentTask, work); + + if (context != null) { + context.Post ((o) => { + OnBeginAsyncStep (message, work, m); + ReportProgressChanged (); + }, null); + } else { + OnBeginAsyncStep (message, work, m); + ReportProgressChanged (); + } + + if (slaveMonitors != null) { + foreach (var sm in slaveMonitors) + m.AddSlaveMonitor (sm.BeginAsyncStep (message, work)); + } + return m; + } + + public void ReportWarning (string message) + { + if (parentMonitor != null) + parentMonitor.ReportWarning (message); + lock (warnings) + warnings.Add (message); + + if (context != null) + context.Post ((o) => OnWarningReported (message), null); + else + OnWarningReported (message); + + if (slaveMonitors != null) { + foreach (var sm in slaveMonitors) + sm.ReportWarning (message); + } + } + + public void ReportSuccess (string message) + { + if (parentMonitor != null) + parentMonitor.ReportSuccess (message); + lock (messages) + messages.Add (message); + + if (context != null) + context.Post ((o) => OnSuccessReported (message), null); + else + OnSuccessReported (message); + + if (slaveMonitors != null) { + foreach (var sm in slaveMonitors) + sm.ReportSuccess (message); + } + } + + public void ReportError (string message, Exception exception = null) + { + if (parentMonitor != null) + parentMonitor.ReportError (message, exception); + + var msg = message; + if (message == null && exception != null) + msg = exception.Message; + + lock (errors) + errors.Add (new ProgressError (msg, exception)); + + if (context != null) + context.Post ((o) => OnErrorReported (msg, exception), null); + else + OnErrorReported (msg, exception); + + if (slaveMonitors != null) { + foreach (var sm in slaveMonitors) + sm.ReportError (message, exception); + } + } + + public bool HasErrors { + get { + lock (errors) + return errors.Count > 0; + } + } + + public bool HasWarnings { + get { + lock (warnings) + return warnings.Count > 0; + } + } + + public string[] SuccessMessages { + get { + lock (messages) + return messages.ToArray (); + } + } + + public string[] Warnings { + get { + lock (warnings) + return warnings.ToArray (); + } + } + + public ProgressError[] Errors { + get { + lock (errors) + return errors.ToArray (); + } + } + + public TextWriter Log { + get { + if (parentMonitor != null) + return parentMonitor.Log; + return logWriter; + } + protected set { + if (parentMonitor != null) + throw new InvalidOperationException ("Log writter can't be modified"); + if (customLogWriter != null) + logWriter.UnchainWriter (customLogWriter); + customLogWriter = value; + logWriter.ChainWriter (customLogWriter); + } + } + + public TextWriter ErrorLog { + get { + if (parentMonitor != null) + return parentMonitor.ErrorLog; + return errorLogWriter ?? Log; + } + protected set { + if (parentMonitor != null) + throw new InvalidOperationException ("Log writter can't be modified"); + if (customErrorLogWriter != null) + errorLogWriter.UnchainWriter (customErrorLogWriter); + customErrorLogWriter = value; + errorLogWriter.ChainWriter (customErrorLogWriter); + } + } + + public CancellationToken CancellationToken { + get { + if (parentMonitor != null) + return parentMonitor.CancellationToken; + else + return CancellationTokenSource.Token; + } + } + + public double Progress { + get { + return rootTask != null ? rootTask.Progress : 0; + } + } + + public bool ProgressIsUnknown { + get { + return rootTask == null; + } + } + + CancellationTokenSource cancellationTokenSource; + + protected CancellationTokenSource CancellationTokenSource { + get { + if (cancellationTokenSource == null) + cancellationTokenSource = new CancellationTokenSource (); + return cancellationTokenSource; + } set { + cancellationTokenSource = value; + } + } + + protected virtual void OnBeginTask (string name, int totalWork, int stepWork) + { + } + + protected virtual void OnEndTask (string name, int totalWork, int stepWork) + { + } + + protected virtual void OnStep (string message, int work) + { + } + + protected virtual void OnBeginAsyncStep (string message, int work, ProgressMonitor stepMonitor) + { + } + + protected virtual ProgressMonitor CreateAsyncStepMonitor () + { + return new ProgressMonitor (); + } + + protected virtual void OnSuccessReported (string message) + { + } + + protected virtual void OnWarningReported (string message) + { + } + + protected virtual void OnErrorReported (string message, Exception exception) + { + } + + protected virtual void OnWriteLog (string message) + { + } + + protected virtual void OnWriteErrorLog (string message) + { + } + + void ReportProgressChanged () + { + OnProgressChanged (); + if (parentMonitor != null) + parentMonitor.ReportProgressChanged (); + } + + protected virtual void OnProgressChanged () + { + } + + protected virtual void OnCompleted () + { + } + } + + public class ProgressTask: IDisposable + { + List<ProgressTask> childrenTasks = new List<ProgressTask> (); + + double currentWork; + double completedChildrenWork; + ProgressMonitor monitor; + + internal ProgressTask (ProgressMonitor monitor, string name, int totalWork) + { + this.monitor = monitor; + Name = name; + TotalWork = totalWork; + StepWork = -1; + } + + public int StepWork { get; set; } + + bool HasStepWork { + get { return StepWork != -1; } + } + + public string Name { get; private set; } + + public double Progress { get; private set; } + + public string StatusMessage { get; internal set; } + + public int TotalWork { get; private set; } + + public ProgressTask ParentTask { get; private set; } + + public ProgressTask[] GetChildrenTasks () + { + List<ProgressTask> children = new List<ProgressTask> (); + lock (childrenTasks) { + foreach (var t in childrenTasks) { + if (t.Name == null) + children.AddRange (t.GetChildrenTasks ()); + else + children.Add (t); + } + } + return children.ToArray (); + } + + internal void AddChild (ProgressTask task) + { + task.ParentTask = this; + lock (childrenTasks) + childrenTasks.Add (task); + } + + internal void Step (string message, double work) + { + if (message != null) + StatusMessage = message; + + IncCurrentWork (work); + } + + internal void SetComplete () + { + lock (childrenTasks) { + currentWork = TotalWork; + completedChildrenWork = 0; + Progress = 1; + if (ParentTask != null) + ParentTask.SetChildComplete (this); + } + } + + void SetChildComplete (ProgressTask child) + { + child.ParentTask = null; + lock (childrenTasks) { + if (child.HasStepWork) + currentWork += child.StepWork; + childrenTasks.Remove (child); + } + UpdateProgressFromChildren (); + } + + void IncCurrentWork (double work) + { + lock (childrenTasks) { + currentWork += work; + Progress = Math.Min (currentWork + completedChildrenWork, (double)TotalWork) / (double)TotalWork; + if (ParentTask != null && HasStepWork) + ParentTask.UpdateProgressFromChildren (); + } + } + + void UpdateProgressFromChildren () + { + lock (childrenTasks) { + completedChildrenWork = childrenTasks.Where (t => t.HasStepWork).Sum (t => t.Progress * (double)t.StepWork); + IncCurrentWork (0); + } + } + + void IDisposable.Dispose () + { + monitor.EndTask (this); + } + } + + public class ProgressError + { + Exception ex; + string message; + + public ProgressError (string message, Exception ex) + { + this.ex = ex; + this.message = message; + } + + public string Message { + get { return message; } + } + + public Exception Exception { + get { return ex; } + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs index 653f215e61..53429cd8a2 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs @@ -54,6 +54,7 @@ namespace MonoDevelop.Core static ApplicationService applicationService; static bool initialized; static SynchronizationContext mainSynchronizationContext; + static SynchronizationContext defaultSynchronizationContext; public static void GetAddinRegistryLocation (out string configDir, out string addinsDir, out string databaseDir) { @@ -80,10 +81,12 @@ namespace MonoDevelop.Core SetupInstrumentation (); Platform.Initialize (); + + defaultSynchronizationContext = new SynchronizationContext (); // Set a default sync context if (SynchronizationContext.Current == null) - SynchronizationContext.SetSynchronizationContext (new SynchronizationContext ()); + SynchronizationContext.SetSynchronizationContext (defaultSynchronizationContext); // Hook up the SSL certificate validation codepath ServicePointManager.ServerCertificateValidationCallback += delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { @@ -281,7 +284,7 @@ namespace MonoDevelop.Core public static SynchronizationContext MainSynchronizationContext { get { - return mainSynchronizationContext ?? SynchronizationContext.Current; + return mainSynchronizationContext ?? defaultSynchronizationContext; } set { if (mainSynchronizationContext != null && value != null) @@ -327,10 +330,10 @@ namespace MonoDevelop.Core internal static class Counters { - public static TimerCounter RuntimeInitialization = InstrumentationService.CreateTimerCounter ("Runtime initialization", "Runtime"); + public static TimerCounter RuntimeInitialization = InstrumentationService.CreateTimerCounter ("Runtime initialization", "Runtime", id:"Core.RuntimeInitialization"); public static TimerCounter PropertyServiceInitialization = InstrumentationService.CreateTimerCounter ("Property Service initialization", "Runtime"); - public static Counter AddinsLoaded = InstrumentationService.CreateCounter ("Add-ins loaded", "Add-in Engine", true); + public static Counter AddinsLoaded = InstrumentationService.CreateCounter ("Add-ins loaded", "Add-in Engine", true, id:"Core.AddinsLoaded"); public static Counter ProcessesStarted = InstrumentationService.CreateCounter ("Processes started", "Process Service"); public static Counter ExternalObjects = InstrumentationService.CreateCounter ("External objects", "Process Service"); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/DotNetProjectNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/DotNetProjectNode.cs deleted file mode 100644 index 1b0759072d..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/DotNetProjectNode.cs +++ /dev/null @@ -1,77 +0,0 @@ -// MSBuildDotNetProjectExtensionNode.cs -// -// Author: -// Lluis Sanchez Gual <lluis@novell.com> -// -// Copyright (c) 2008 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 Mono.Addins; -using MonoDevelop.Core; -using MonoDevelop.Projects.Formats.MSBuild; -using MonoDevelop.Projects.Extensions; - -namespace MonoDevelop.Projects.Extensions -{ - public class DotNetProjectNode: ItemTypeNode - { - [NodeAttribute (Required=true)] - string language = null; - - [NodeAttribute] - string resourceHandler = null; - - public override bool CanHandleItem (SolutionEntityItem item) - { - return (item is DotNetProject) && ((DotNetProject)item).LanguageName == language; - } - - public override bool CanHandleFile (string fileName, string typeGuid) - { - if (base.CanHandleFile (fileName, typeGuid)) - return true; - else if (!string.IsNullOrEmpty (typeGuid) && typeGuid.Contains (Guid)) - { - DotNetProjectSubtypeNode node = MSBuildProjectService.GetDotNetProjectSubtype (typeGuid); - if (node != null && node.CanHandleFile (fileName, typeGuid)) - return true; - } - return false; - } - - public override SolutionEntityItem LoadSolutionItem (IProgressMonitor monitor, string fileName, MSBuildFileFormat expectedFormat, string itemGuid) - { - MSBuildProjectHandler handler = CreateHandler<MSBuildProjectHandler> (fileName, itemGuid); - handler.SetCustomResourceHandler (GetResourceHandler ()); - return handler.Load (monitor, fileName, expectedFormat, language, null); - } - - public IResourceHandler GetResourceHandler () - { - if (!string.IsNullOrEmpty (resourceHandler)) - return (IResourceHandler) Addin.CreateInstance (resourceHandler, true); - else - return new MSBuildResourceHandler (); - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/DotNetProjectSubtypeNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/DotNetProjectSubtypeNode.cs deleted file mode 100644 index 2da66dcfa6..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/DotNetProjectSubtypeNode.cs +++ /dev/null @@ -1,214 +0,0 @@ -// DotNetProjectSubtype.cs -// -// Author: -// Lluis Sanchez Gual <lluis@novell.com> -// -// Copyright (c) 2008 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.Linq; -using Mono.Addins; -using MonoDevelop.Projects.Formats.MSBuild; -using System.Collections.Generic; -using MonoDevelop.Core; - -namespace MonoDevelop.Projects.Extensions -{ - [ExtensionNodeChild (typeof(DotNetProjectSubtypeNodeImport), "AddImport")] - [ExtensionNodeChild (typeof(DotNetProjectSubtypeNodeImport), "RemoveImport")] - public class DotNetProjectSubtypeNode: ExtensionNode - { - #pragma warning disable 649 - - [NodeAttribute] - string guid = null; - - [NodeAttribute] - string type = null; - - [NodeAttribute] - string import = null; - - [NodeAttribute] - string extension = null; - - [NodeAttribute] - string exclude = null; - - [NodeAttribute] - bool useXBuild = false; - - [NodeAttribute] - bool requireXBuild = true; - - [NodeAttribute] - string migrationHandler; - - [NodeAttribute] - bool migrationRequired = true; - - #pragma warning restore 649 - - Type itemType; - - public string Import { - get { - return import; - } - } - - public Type Type { - get { - if (itemType == null) { - itemType = Addin.GetType (type, true); - if (!typeof(MonoDevelop.Projects.DotNetProject).IsAssignableFrom (itemType)) - throw new InvalidOperationException ("Type must be a subclass of DotNetProject"); - } - return itemType; - } - } - - public string Extension { - get { - return extension; - } - } - - public string Exclude { - get { - return exclude; - } - } - - public string Guid { - get { return guid; } - } - - public bool UseXBuild { - get { return useXBuild; } - } - - public bool RequireXBuild { - get { return useXBuild && requireXBuild; } - } - - public bool IsMigration { - get { return migrationHandler != null; } - } - - public bool IsMigrationRequired { - get { return migrationRequired; } - } - - public IDotNetSubtypeMigrationHandler MigrationHandler { - get { return (IDotNetSubtypeMigrationHandler) Addin.CreateInstance (migrationHandler); } - } - - public bool SupportsType (string guid) - { - return string.Compare (this.guid, guid, true) == 0; - } - - public DotNetProject CreateInstance (string language) - { - return (DotNetProject) Activator.CreateInstance (Type, language); - } - - public virtual bool CanHandleItem (SolutionEntityItem item) - { - return !(IsMigration && IsMigrationRequired) && Type.IsAssignableFrom (item.GetType ()); - } - - public virtual bool CanHandleType (Type type) - { - return !(IsMigration && IsMigrationRequired) && Type.IsAssignableFrom (type); - } - - public virtual bool CanHandleFile (string fileName, string typeGuid) - { - if (typeGuid != null && typeGuid.ToLower().Contains(guid.ToLower())) - return true; - if (!string.IsNullOrEmpty (extension) && System.IO.Path.GetExtension (fileName) == "." + extension) - return true; - return false; - } - - public virtual void InitializeHandler (SolutionEntityItem item) - { - MSBuildProjectHandler h = (MSBuildProjectHandler) ProjectExtensionUtil.GetItemHandler (item); - UpdateImports (item, h.TargetImports); - h.SubtypeGuids.Add (guid); - h.UseMSBuildEngineByDefault |= UseXBuild; - h.RequireMSBuildEngine |= RequireXBuild; - } - - public void UpdateImports (SolutionEntityItem item, List<string> imports) - { - DotNetProject p = (DotNetProject) item; - if (!string.IsNullOrEmpty (import)) - imports.AddRange (import.Split (':')); - if (!string.IsNullOrEmpty (exclude)) - exclude.Split (':').ToList ().ForEach (i => imports.Remove (i)); - - foreach (DotNetProjectSubtypeNodeImport iob in ChildNodes) { - if (iob.Language == p.LanguageName) { - if (iob.IsAdd) - imports.AddRange (iob.Projects.Split (':')); - else - iob.Projects.Split (':').ToList ().ForEach (i => imports.Remove (i)); - } - } - } - } - - class DotNetProjectSubtypeNodeImport: ExtensionNode - { - protected override void Read (NodeElement elem) - { - IsAdd = elem.NodeName == "AddImport"; - base.Read (elem); - } - - [NodeAttribute ("language")] - public string Language { get; set; } - - [NodeAttribute ("projects")] - public string Projects { get; set; } - - public bool IsAdd { get; private set; } - } - - public interface IDotNetSubtypeMigrationHandler - { - IEnumerable<string> FilesToBackup (string filename); - Type Migrate (IProjectLoadProgressMonitor monitor, MSBuildProject project, string fileName, string language); - bool CanPromptForMigration { get; } - MigrationType PromptForMigration (IProjectLoadProgressMonitor monitor, MSBuildProject project, string fileName, string language); - } - - public enum MigrationType { - Ignore, - Migrate, - BackupAndMigrate, - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/DotNetProjectTypeNode.cs index 1dc81dafca..2a75bcf347 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemNode.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/DotNetProjectTypeNode.cs @@ -1,4 +1,4 @@ -// SolutionItemNode.cs +// MSBuildDotNetProjectExtensionNode.cs // // Author: // Lluis Sanchez Gual <lluis@novell.com> @@ -26,32 +26,34 @@ // using System; -using System.IO; -using System.Xml; using Mono.Addins; -using MonoDevelop.Projects.Formats.MSBuild; using MonoDevelop.Core; +using MonoDevelop.Projects.Formats.MSBuild; +using MonoDevelop.Projects.Extensions; +using System.Threading.Tasks; +using System.Collections.Generic; namespace MonoDevelop.Projects.Extensions { - public class SolutionItemNode: ItemTypeNode + [ExtensionNode (ExtensionAttributeType=typeof(RegisterDotNetProjectTypeAttribute))] + public class DotNetProjectTypeNode: ProjectTypeNode { [NodeAttribute (Required=true)] - string type = null; + string language = null; - public Type ItemType { - get { return Addin.GetType (type, true); } + public string Language { + get { return language; } } - public override bool CanHandleItem (SolutionEntityItem item) + public DotNetProjectTypeNode () { - return ItemType != null && ItemType.IsAssignableFrom (item.GetType ()); + Alias = "DotNet"; } - - public override SolutionEntityItem LoadSolutionItem (IProgressMonitor monitor, string fileName, MSBuildFileFormat expectedFormat, string itemGuid) + + public override bool CanCreateSolutionItem (string type, ProjectCreateInformation info, System.Xml.XmlElement projectOptions) { - MSBuildProjectHandler handler = CreateHandler<MSBuildProjectHandler> (fileName, itemGuid); - return handler.Load (monitor, fileName, expectedFormat, null, ItemType); + string lang = projectOptions.GetAttribute ("language"); + return base.CanCreateSolutionItem (type, info, projectOptions) && lang == Language; } } -}
\ No newline at end of file +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/IPathHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/FlavorTypeCondition.cs index e987803100..855a09dd15 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/IPathHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/FlavorTypeCondition.cs @@ -1,21 +1,21 @@ -// -// ISolutionItemHandler.cs -// +// +// FlavorCondition.cs +// // Author: -// Lluis Sanchez Gual <lluis@novell.com> -// -// Copyright (c) 2010 Novell, Inc (http://www.novell.com) -// +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 @@ -23,19 +23,24 @@ // 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 MonoDevelop.Core; +using System.Linq; namespace MonoDevelop.Projects.Extensions { - /// <summary> - /// This interface can be implemented by a ISolutionItemHandler class to provide - /// custom rules for encoding and decoding paths - /// </summary> - public interface IPathHandler + public class FlavorTypeCondition: ItemTypeCondition { - string EncodePath (string path, string oldPath); - string DecodePath (string path); + Project project; + + public FlavorTypeCondition (Project project) + { + this.project = project; + } + + protected override System.Collections.Generic.IEnumerable<Type> GetObjectTypes () + { + return project.GetFlavors ().Select (e => e.GetType ()); + } } } + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/IFileFormat.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/IFileFormat.cs index 6c02e7d1f2..2a24523429 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/IFileFormat.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/IFileFormat.cs @@ -30,6 +30,7 @@ using System; using MonoDevelop.Core; using System.Collections.Generic; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Extensions { @@ -48,10 +49,10 @@ namespace MonoDevelop.Projects.Extensions // Makes the required changes in the object to support this file format. // It usually means setting the ISolutionItemHandler of the item. - void ConvertToFormat (object obj);
+ Task ConvertToFormat (object obj);
- void WriteFile (FilePath file, object obj, IProgressMonitor monitor);
- object ReadFile (FilePath file, Type expectedType, IProgressMonitor monitor); + Task WriteFile (FilePath file, object obj, ProgressMonitor monitor);
+ Task<object> ReadFile (FilePath file, Type expectedType, ProgressMonitor monitor); // Returns the list of files where the object is stored
List<FilePath> GetItemFiles (object obj); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ISolutionItemHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ISolutionItemHandler.cs index 83b0603b79..636abf31a4 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ISolutionItemHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ISolutionItemHandler.cs @@ -27,6 +27,7 @@ using System; using MonoDevelop.Core; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Extensions { @@ -50,7 +51,7 @@ namespace MonoDevelop.Projects.Extensions /// <param name='configuration'> /// Selector to be used to get the target configuration /// </param> - BuildResult RunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration); + Task<BuildResult> RunTarget (ProgressMonitor monitor, string target, ConfigurationSelector configuration); /// <summary> /// Saves the solution item @@ -58,7 +59,7 @@ namespace MonoDevelop.Projects.Extensions /// <param name='monitor'> /// A progress monitor /// </param> - void Save (IProgressMonitor monitor); + Task Save (ProgressMonitor monitor); /// <summary> /// Gets a value indicating whether the name of the solution item should be the same as the name of the file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ItemTypeCondition.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ItemTypeCondition.cs index 48824db647..5bd95f10eb 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ItemTypeCondition.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ItemTypeCondition.cs @@ -34,7 +34,7 @@ namespace MonoDevelop.Projects.Extensions public class ItemTypeCondition: ConditionType { Type objType; - List<string> typeNames; + HashSet<string> typeNames; IDictionary<string,string> aliases; public ItemTypeCondition () @@ -87,7 +87,7 @@ namespace MonoDevelop.Projects.Extensions aliases [alias] = fullName; } - bool MatchesType (string type) + protected bool MatchesType (string type) { if (type.IndexOf ('.') == -1) { string res; @@ -106,29 +106,36 @@ namespace MonoDevelop.Projects.Extensions // use of the condition. if (typeNames == null) { - typeNames = new List<string> (); - - typeNames.Add (objType.FullName); - typeNames.Add (objType.AssemblyQualifiedName); + typeNames = new HashSet<string> (); + + foreach (var t in GetObjectTypes ()) { + typeNames.Add (t.FullName); + typeNames.Add (t.AssemblyQualifiedName); - // base class hierarchy + // base class hierarchy - Type baseType = objType.BaseType; - while (baseType != null) { - typeNames.Add (baseType.FullName); - typeNames.Add (baseType.AssemblyQualifiedName); - baseType = baseType.BaseType; - } + Type baseType = t.BaseType; + while (baseType != null) { + typeNames.Add (baseType.FullName); + typeNames.Add (baseType.AssemblyQualifiedName); + baseType = baseType.BaseType; + } - // Implemented interfaces + // Implemented interfaces - Type[] interfaces = objType.GetInterfaces(); - foreach (Type itype in interfaces) { - typeNames.Add (itype.FullName); - typeNames.Add (itype.AssemblyQualifiedName); + Type[] interfaces = t.GetInterfaces (); + foreach (Type itype in interfaces) { + typeNames.Add (itype.FullName); + typeNames.Add (itype.AssemblyQualifiedName); + } } } return typeNames.Contains (type); } + + protected virtual IEnumerable<Type> GetObjectTypes () + { + yield return objType; + } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/MSBuildProjectExtensionNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/MSBuildProjectExtensionNode.cs new file mode 100644 index 0000000000..85daa23c06 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/MSBuildProjectExtensionNode.cs @@ -0,0 +1,46 @@ +// +// MSBuildProjectExtensionNode.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 Mono.Addins; + +namespace MonoDevelop.Projects.Extensions +{ + public class MSBuildProjectExtensionNode: TypeExtensionNode + { + [NodeAttribute (Description = "GUID of the extension. The extension will be loaded if the project has this GUID in the project type GUID list. " + + "If not specified, the SupportsItem method will be called on the extension to determine if it is supported or not.")] + string guid; + + public MSBuildProjectExtensionNode () + { + } + + public string Guid { + get { return guid; } + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectBindingCodon.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectBindingCodon.cs deleted file mode 100644 index c0cda37f63..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectBindingCodon.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// ProjectBindingCodon.cs -// -// Author: -// Lluis Sanchez Gual -// - -// -// Copyright (C) 2005 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.Collections; -using System.ComponentModel; - -using Mono.Addins; -using MonoDevelop.Projects; - -namespace MonoDevelop.Projects.Extensions -{ - [ExtensionNode (Description="A project binding. The specified class must implement MonoDevelop.Projects.IProjectBinding.")] - internal class ProjectBindingCodon : TypeExtensionNode - { - public IProjectBinding ProjectBinding { - get { return (IProjectBinding) base.GetInstance (); } - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectExtensionUtil.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectExtensionUtil.cs index 6004da536e..926dcf07e8 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectExtensionUtil.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectExtensionUtil.cs @@ -30,6 +30,7 @@ using System.IO; using System.Collections.Generic; using System.Threading; using MonoDevelop.Core; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Extensions { @@ -43,28 +44,13 @@ namespace MonoDevelop.Projects.Extensions loadControlSlot = Thread.AllocateDataSlot (); } - public static ISolutionItemHandler GetItemHandler (SolutionItem item) - { - return item.GetItemHandler (); - } - - public static void InstallHandler (ISolutionItemHandler handler, SolutionItem item) - { - item.SetItemHandler (handler); - } - - public static SolutionEntityItem LoadSolutionItem (IProgressMonitor monitor, string fileName, ItemLoadCallback callback) + public async static Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, string fileName, ItemLoadCallback callback) { using (Counters.ReadSolutionItem.BeginTiming ("Read project " + fileName)) { - return Services.ProjectService.GetExtensionChain (null).LoadSolutionItem (monitor, fileName, callback); + return await Services.ProjectService.GetExtensionChain (null).LoadSolutionItem (monitor, fileName, callback); } } - - public static BuildResult Compile (IProgressMonitor monitor, SolutionEntityItem item, BuildData buildData, ItemCompileCallback callback) - { - return Services.ProjectService.GetExtensionChain (item).Compile (monitor, item, buildData, callback); - } - + public static void BeginLoadOperation () { Interlocked.Increment (ref loading); @@ -94,28 +80,6 @@ namespace MonoDevelop.Projects.Extensions if (op != null) op.Add (rc); } - - public static string EncodePath (SolutionEntityItem item, string path, string oldPath) - { - IPathHandler ph = item.GetItemHandler () as IPathHandler; - if (ph != null) - return ph.EncodePath (path, oldPath); - else { - string basePath = Path.GetDirectoryName (item.FileName); - return FileService.RelativeToAbsolutePath (basePath, path); - } - } - - public static string DecodePath (SolutionEntityItem item, string path) - { - IPathHandler ph = item.GetItemHandler () as IPathHandler; - if (ph != null) - return ph.DecodePath (path); - else { - string basePath = Path.GetDirectoryName (item.FileName); - return FileService.AbsoluteToRelativePath (basePath, path); - } - } } class LoadOperation @@ -141,7 +105,7 @@ namespace MonoDevelop.Projects.Extensions - public delegate SolutionEntityItem ItemLoadCallback (IProgressMonitor monitor, string fileName); + public delegate Task<SolutionItem> ItemLoadCallback (ProgressMonitor monitor, string fileName); - public delegate BuildResult ItemCompileCallback (IProgressMonitor monitor, SolutionEntityItem item, BuildData buildData); + public delegate BuildResult ItemCompileCallback (ProgressMonitor monitor, SolutionItem item, BuildData buildData); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IWorkspaceObject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectMigrationHandler.cs index 502337e651..976dc06b42 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IWorkspaceObject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectMigrationHandler.cs @@ -1,4 +1,4 @@ -// IWorkspaceObject.cs +// DotNetProjectSubtype.cs // // Author: // Lluis Sanchez Gual <lluis@novell.com> @@ -26,29 +26,37 @@ // using System; +using System.Linq; +using Mono.Addins; +using MonoDevelop.Projects.Formats.MSBuild; using System.Collections.Generic; using MonoDevelop.Core; -using MonoDevelop.Core.Serialization; +using System.Threading.Tasks; - -namespace MonoDevelop.Projects +namespace MonoDevelop.Projects.Extensions { - public interface IWorkspaceObject: IExtendedDataItem, IFolderItem, IDisposable + public abstract class ProjectMigrationHandler { - string Name { get; set; }
- FilePath ItemDirectory { get; }
- new FilePath BaseDirectory { get; set; } - void Save (IProgressMonitor monitor); + public virtual IEnumerable<string> FilesToBackup (string filename) + { + yield break; + } + + public abstract bool Migrate (ProjectLoadProgressMonitor monitor, MSBuildProject project, string fileName, string language); + + public virtual bool CanPromptForMigration { + get { return false; } + } + + public virtual MigrationType PromptForMigration (ProjectLoadProgressMonitor monitor, MSBuildProject project, string fileName, string language) + { + throw new NotImplementedException (); + } } - public interface IWorkspaceFileObject: IWorkspaceObject, IFileItem - { - FileFormat FileFormat { get; } - void ConvertToFormat (FileFormat format, bool convertChildren); - bool SupportsFormat (FileFormat format);
- List<FilePath> GetItemFiles (bool includeReferencedFiles); - new FilePath FileName { get; set; } - bool NeedsReload { get; set; } - bool ItemFilesChanged { get; } + public enum MigrationType { + Ignore, + Migrate, + BackupAndMigrate, } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectMigrationHandlerNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectMigrationHandlerNode.cs new file mode 100644 index 0000000000..9e61188b88 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectMigrationHandlerNode.cs @@ -0,0 +1,45 @@ +// +// ProjectMigrationHandlerNode.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 Mono.Addins; + +namespace MonoDevelop.Projects.Extensions +{ + public class ProjectMigrationHandlerNode: TypeExtensionNode + { + [NodeAttribute] + bool migrationRequired = true; + + public bool IsMigrationRequired { + get { return migrationRequired; } + } + + public ProjectMigrationHandler MigrationHandler { + get { return (ProjectMigrationHandler) CreateInstance (typeof(ProjectMigrationHandler)); } + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectModelExtensionNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectModelExtensionNode.cs new file mode 100644 index 0000000000..bcd5bf6a81 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectModelExtensionNode.cs @@ -0,0 +1,45 @@ +// +// ProjectModelExtensionNode.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 Mono.Addins; + +namespace MonoDevelop.Projects.Extensions +{ + [ExtensionNode (ExtensionAttributeType = typeof(RegisterProjectModelExtensionAttribute))] + public class ProjectModelExtensionNode: TypeExtensionNode + { + public virtual bool CanHandleObject (object ob) + { + return ob is WorkspaceObject; + } + + public virtual WorkspaceObjectExtension CreateExtension () + { + return (WorkspaceObjectExtension) CreateInstance (typeof(WorkspaceObjectExtension)); + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectTypeNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectTypeNode.cs new file mode 100644 index 0000000000..407a77db63 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectTypeNode.cs @@ -0,0 +1,62 @@ +// +// MSBuildProjectNode.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Threading.Tasks; +using System.Collections.Generic; +using MonoDevelop.Projects.Formats.MSBuild; +using Mono.Addins; +using MonoDevelop.Core; + +namespace MonoDevelop.Projects.Extensions +{ + [ExtensionNode (ExtensionAttributeType=typeof(RegisterProjectTypeAttribute))] + public class ProjectTypeNode: SolutionItemTypeNode + { + public override async Task<SolutionItem> CreateSolutionItem (ProgressMonitor monitor, string fileName, string typeGuid) + { + MSBuildProject p = null; + + if (!string.IsNullOrEmpty (fileName)) { + p = await MSBuildProject.LoadAsync (fileName); +// if (MSBuildProjectService.CanMigrateFlavor (p.ProjectTypeGuids)) +// await MSBuildProjectService.MigrateFlavor (monitor, fileName, typeGuid, this, p); + } + + if (p != null) + Project.CreationContext.LockContext (p, typeGuid); + try { + var project = await base.CreateSolutionItem (monitor, fileName, typeGuid) as Project; + if (project == null) + throw new InvalidOperationException ("Project node type is not a subclass of MonoDevelop.Projects.Project"); + return project; + } finally { + if (p != null) + Project.CreationContext.UnlockContext (); + } + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemExtensionNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemExtensionNode.cs new file mode 100644 index 0000000000..4b64b1f20e --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemExtensionNode.cs @@ -0,0 +1,64 @@ +// +// SolutionItemExtensionNode.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 Mono.Addins; +using System.Linq; + +namespace MonoDevelop.Projects.Extensions +{ + [ExtensionNode (ExtensionAttributeType = typeof(RegisterProjectFlavorAttribute))] + public class SolutionItemExtensionNode: ProjectModelExtensionNode + { + [NodeAttribute (Description = "GUID of the extension. The extension will be loaded if the project has this GUID in the project type GUID list. " + + "If not specified, the extension will be applied to all projects.")] + string guid; + + public string Guid { + get { return guid; } + } + + public override bool CanHandleObject (object ob) + { + SolutionItem p = ob as SolutionItem; + if (p == null) + return false; + + if (guid == null) + return true; + + var typeGuids = p.GetItemTypeGuids (); + return typeGuids.Any (g => string.Equals (g, guid, StringComparison.InvariantCultureIgnoreCase)); + } + + public override WorkspaceObjectExtension CreateExtension () + { + var ext = (ProjectExtension) CreateInstance (typeof(ProjectExtension)); + ext.FlavorGuid = Guid; + return ext; + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemHandler.cs index 835a0c2d92..887af95949 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemHandler.cs @@ -29,14 +29,15 @@ using System; using System.CodeDom.Compiler; using MonoDevelop.Core; using MonoDevelop.Projects.Extensions; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Extensions { public abstract class SolutionItemHandler: ISolutionItemHandler { - SolutionItem item; + SolutionFolderItem item; - public SolutionItemHandler (SolutionItem item) + public SolutionItemHandler (SolutionFolderItem item) { this.item = item; } @@ -45,30 +46,30 @@ namespace MonoDevelop.Projects.Extensions get { return true; } } - public SolutionItem Item { + public SolutionFolderItem Item { get { return item; } } - public virtual BuildResult RunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) + public async virtual Task<BuildResult> RunTarget (ProgressMonitor monitor, string target, ConfigurationSelector configuration) { switch (target) { case "Build": - return OnBuild (monitor, configuration); + return await OnBuild (monitor, configuration); case "Clean": - return OnClean (monitor, configuration); + return await OnClean (monitor, configuration); } return new BuildResult (new CompilerResults (null), ""); } - protected virtual BuildResult OnBuild (IProgressMonitor monitor, ConfigurationSelector configuration) + protected virtual Task<BuildResult> OnBuild (ProgressMonitor monitor, ConfigurationSelector configuration) { - return null; + return Task.FromResult (BuildResult.Success); } - protected virtual BuildResult OnClean (IProgressMonitor monitor, ConfigurationSelector configuration) + protected virtual Task<BuildResult> OnClean (ProgressMonitor monitor, ConfigurationSelector configuration) { - return null; + return Task.FromResult (BuildResult.Success); } public virtual void Dispose () @@ -77,7 +78,7 @@ namespace MonoDevelop.Projects.Extensions public abstract string ItemId { get; } - public abstract void Save (IProgressMonitor monitor); + public abstract Task Save (ProgressMonitor monitor); public virtual void OnModified (string hint) { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ItemTypeNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemTypeNode.cs index 7608071bee..3b9a46a8db 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ItemTypeNode.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemTypeNode.cs @@ -31,10 +31,13 @@ using System.Xml; using Mono.Addins; using MonoDevelop.Projects.Formats.MSBuild; using MonoDevelop.Core; +using System.Threading.Tasks; +using MonoDevelop.Core.ProgressMonitoring; namespace MonoDevelop.Projects.Extensions { - public abstract class ItemTypeNode: ExtensionNode + [ExtensionNode (ExtensionAttributeType=typeof(RegisterSolutionItemTypeAttribute))] + public abstract class SolutionItemTypeNode: ExtensionNode { [NodeAttribute (Required=true)] string guid = null; @@ -46,13 +49,16 @@ namespace MonoDevelop.Projects.Extensions string import = null; [NodeAttribute] - string handlerType = null; - - public ItemTypeNode () + string type = null; + + [NodeAttribute ("alias")] + public string Alias { get; protected set; } + + public SolutionItemTypeNode () { } - public ItemTypeNode (string guid, string extension, string import) + public SolutionItemTypeNode (string guid, string extension, string import) { this.guid = guid; this.extension = extension; @@ -74,14 +80,13 @@ namespace MonoDevelop.Projects.Extensions return import; } } - - public abstract bool CanHandleItem (SolutionEntityItem item); - - public virtual void InitializeHandler (SolutionEntityItem item) - { - MSBuildHandler h = CreateHandler<MSBuildHandler> (null, null); - h.Item = item; - item.SetItemHandler (h); + + internal string ItenTypeName { + get { return type; } + } + + public virtual Type ItemType { + get { return Addin.GetType (type, true); } } public virtual bool CanHandleFile (string fileName, string typeGuid) @@ -92,32 +97,29 @@ namespace MonoDevelop.Projects.Extensions return true; return false; } + + SolutionItemFactory factory; - protected T CreateHandler<T> (string fileName, string itemGuid) where T:MSBuildHandler + public virtual async Task<SolutionItem> CreateSolutionItem (ProgressMonitor monitor, string fileName, string typeGuid) { - MSBuildHandler h = OnCreateHandler (fileName, itemGuid); - if (!(h is T)) - throw new InvalidOperationException ("Error while creating a MSBuildHandler. Expected an object of type '" + typeof(T).FullName + ", found type '" + h.GetType ()); - return (T)h; + if (typeof(SolutionItemFactory).IsAssignableFrom (ItemType)) { + if (factory == null) + factory = (SolutionItemFactory) Activator.CreateInstance (ItemType); + return await factory.CreateItem (fileName, typeGuid); + } + return (SolutionItem) Activator.CreateInstance (ItemType); } - - protected virtual MSBuildHandler OnCreateHandler (string fileName, string itemGuid) + + public virtual bool CanCreateSolutionItem (string type, ProjectCreateInformation info, System.Xml.XmlElement projectOptions) { - MSBuildHandler h; - if (!string.IsNullOrEmpty (handlerType)) { - h = Addin.CreateInstance (handlerType, true) as MSBuildHandler; - if (h == null) - throw new InvalidOperationException ("Type '" + handlerType + "' must be a subclass of 'MonoDevelop.Projects.Formats.MSBuild.MSBuildHandler'"); - if (h is MSBuildProjectHandler) - ((MSBuildProjectHandler)h).Initialize (Guid, Import, itemGuid); - else - h.Initialize (Guid, itemGuid); - } else { - h = new MSBuildProjectHandler (Guid, Import, itemGuid); - } - return h; + return type.Equals (Guid, StringComparison.OrdinalIgnoreCase) || type == Alias; + } + + public virtual SolutionItem CreateSolutionItem (string type, ProjectCreateInformation info, System.Xml.XmlElement projectOptions) + { + var item = CreateSolutionItem (new ProgressMonitor (), null, Guid).Result; + item.InitializeNew (info, projectOptions); + return item; } - - public abstract SolutionEntityItem LoadSolutionItem (IProgressMonitor monitor, string fileName, MSBuildFileFormat expectedFormat, string itemGuid); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1DotNetProjectHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1DotNetProjectHandler.cs index c370403ffa..605464cee2 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1DotNetProjectHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1DotNetProjectHandler.cs @@ -35,6 +35,7 @@ using MonoDevelop.Core; using MonoDevelop.Core.Execution; using MonoDevelop.Projects.Extensions; using Microsoft.Build.BuildEngine; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Formats.MD1 { @@ -48,9 +49,9 @@ namespace MonoDevelop.Projects.Formats.MD1 get { return (DotNetProject) Item; } } - protected override BuildResult OnBuild (IProgressMonitor monitor, ConfigurationSelector configuration) + protected async override Task<BuildResult> OnBuild (ProgressMonitor monitor, ConfigurationSelector configuration) { - if (!Project.InternalCheckNeedsBuild (configuration)) { + if (!Project.OnGetNeedsBuilding (configuration)) { monitor.Log.WriteLine (GettextCatalog.GetString ("Skipping project since output files are up to date")); return new BuildResult (); } @@ -150,25 +151,36 @@ namespace MonoDevelop.Projects.Formats.MD1 buildData.Configuration.SetParentItem (project); buildData.ConfigurationSelector = configuration; - return ProjectExtensionUtil.Compile (monitor, project, buildData, delegate { + return await Task<BuildResult>.Factory.StartNew (delegate { ProjectItemCollection items = buildData.Items; - BuildResult res = BuildResources (buildData.Configuration, ref items, monitor); - if (res != null) - return res; - - res = project.LanguageBinding.Compile (items, buildData.Configuration, buildData.ConfigurationSelector, monitor); + BuildResult br = BuildResources (buildData.Configuration, ref items, monitor); + if (br != null) + return br; + + br = project.LanguageBinding.Compile (items, buildData.Configuration, buildData.ConfigurationSelector, monitor); if (refres != null) { - refres.Append (res); + refres.Append (br); return refres; - } - else - return res; + } else + return br; + }); + + } + + internal static Task<BuildResult> Compile (ProgressMonitor monitor, DotNetProject project, BuildData buildData) + { + return Task<BuildResult>.Factory.StartNew (delegate { + ProjectItemCollection items = buildData.Items; + BuildResult br = BuildResources (buildData.Configuration, ref items, monitor); + if (br != null) + return br; + return project.LanguageBinding.Compile (items, buildData.Configuration, buildData.ConfigurationSelector, monitor); }); - } + } // Builds the EmbedAsResource files. If any localized resources are found then builds the satellite assemblies // and sets @projectItems to a cloned collection minus such resource files. - private BuildResult BuildResources (DotNetProjectConfiguration configuration, ref ProjectItemCollection projectItems, IProgressMonitor monitor) + internal static BuildResult BuildResources (DotNetProjectConfiguration configuration, ref ProjectItemCollection projectItems, ProgressMonitor monitor) { string resgen = configuration.TargetRuntime.GetToolPath (configuration.TargetFramework, "resgen"); ExecutionEnvironment env = configuration.TargetRuntime.GetToolsExecutionEnvironment (configuration.TargetFramework); @@ -220,7 +232,7 @@ namespace MonoDevelop.Projects.Formats.MD1 return null; } - CompilerError GetResourceId (FilePath outputFile, ExecutionEnvironment env, ProjectFile finfo, ref string fname, string resgen, out string resourceId, IProgressMonitor monitor) + static CompilerError GetResourceId (FilePath outputFile, ExecutionEnvironment env, ProjectFile finfo, ref string fname, string resgen, out string resourceId, ProgressMonitor monitor) { resourceId = finfo.ResourceId; if (resourceId == null) { @@ -326,7 +338,7 @@ namespace MonoDevelop.Projects.Formats.MD1 return finfo_first.LastWriteTime > finfo_second.LastWriteTime; } - CompilerError GenerateSatelliteAssemblies (Dictionary<string, string> resourcesByCulture, string outputDir, string al, string defaultns, IProgressMonitor monitor) + static CompilerError GenerateSatelliteAssemblies (Dictionary<string, string> resourcesByCulture, string outputDir, string al, string defaultns, ProgressMonitor monitor) { foreach (KeyValuePair<string, string> pair in resourcesByCulture) { string culture = pair.Key; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1FileFormat.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1FileFormat.cs index 669a0e92c7..a4ec7999ee 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1FileFormat.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1FileFormat.cs @@ -33,6 +33,7 @@ using System.Xml; using MonoDevelop.Core.Serialization; using MonoDevelop.Projects.Extensions; using MonoDevelop.Core; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Formats.MD1 { @@ -65,7 +66,7 @@ namespace MonoDevelop.Projects.Formats.MD1 return new List<FilePath> (); }
- public void WriteFile (FilePath file, object node, IProgressMonitor monitor) + async public Task WriteFile (FilePath file, object node, ProgressMonitor monitor) { string tmpfilename = null; try { @@ -76,9 +77,9 @@ namespace MonoDevelop.Projects.Formats.MD1 } if (tmpfilename == null) { - WriteFileInternal (file, file, node, monitor); + await WriteFileInternal (file, file, node, monitor); } else { - WriteFileInternal (file, tmpfilename, node, monitor); + await WriteFileInternal (file, tmpfilename, node, monitor); File.Delete (file); File.Move (tmpfilename, file); } @@ -89,45 +90,47 @@ namespace MonoDevelop.Projects.Formats.MD1 } }
- void WriteFileInternal (FilePath actualFile, FilePath outFile, object node, IProgressMonitor monitor) + Task WriteFileInternal (FilePath actualFile, FilePath outFile, object node, ProgressMonitor monitor) { - WriteWorkspaceItem (actualFile, outFile, (WorkspaceItem) node, monitor); + return WriteWorkspaceItem (actualFile, outFile, (WorkspaceItem) node, monitor); } - void WriteWorkspaceItem (FilePath actualFile, FilePath outFile, WorkspaceItem item, IProgressMonitor monitor) + async Task WriteWorkspaceItem (FilePath actualFile, FilePath outFile, WorkspaceItem item, ProgressMonitor monitor) { Workspace ws = item as Workspace; if (ws != null) { monitor.BeginTask (null, ws.Items.Count); try { foreach (WorkspaceItem it in ws.Items) { - it.Save (monitor); + await it.SaveAsync (monitor); monitor.Step (1); } } finally { monitor.EndTask (); } } - - StreamWriter sw = new StreamWriter (outFile); - try { - monitor.BeginTask (GettextCatalog.GetString ("Saving item: {0}", actualFile), 1); - XmlTextWriter tw = new XmlTextWriter (sw); - tw.Formatting = Formatting.Indented; - XmlDataSerializer ser = new XmlDataSerializer (MD1ProjectService.DataContext); - ser.SerializationContext.BaseFile = actualFile; - ser.SerializationContext.ProgressMonitor = monitor; - ser.Serialize (sw, item, typeof(WorkspaceItem)); - } catch (Exception ex) { - monitor.ReportError (GettextCatalog.GetString ("Could not save item: {0}", actualFile), ex); - throw; - } finally { - monitor.EndTask (); - sw.Close (); - } + + await Task.Factory.StartNew (delegate { + StreamWriter sw = new StreamWriter (outFile); + try { + monitor.BeginTask (GettextCatalog.GetString ("Saving item: {0}", actualFile), 1); + XmlTextWriter tw = new XmlTextWriter (sw); + tw.Formatting = Formatting.Indented; + XmlDataSerializer ser = new XmlDataSerializer (MD1ProjectService.DataContext); + ser.SerializationContext.BaseFile = actualFile; + ser.SerializationContext.ProgressMonitor = monitor; + ser.Serialize (sw, item, typeof(WorkspaceItem)); + } catch (Exception ex) { + monitor.ReportError (GettextCatalog.GetString ("Could not save item: {0}", actualFile), ex); + throw; + } finally { + monitor.EndTask (); + sw.Close (); + } + }); }
- public object ReadFile (FilePath fileName, Type expectedType, IProgressMonitor monitor) + public async Task<object> ReadFile (FilePath fileName, Type expectedType, ProgressMonitor monitor) { string ext = Path.GetExtension (fileName).ToLower (); if (ext != ".mdw") @@ -137,43 +140,44 @@ namespace MonoDevelop.Projects.Formats.MD1 ProjectExtensionUtil.BeginLoadOperation (); try { - readObject = ReadWorkspaceItemFile (fileName, monitor); + readObject = await ReadWorkspaceItemFile (fileName, monitor); } finally { ProjectExtensionUtil.EndLoadOperation (); } IWorkspaceFileObject fo = readObject as IWorkspaceFileObject; if (fo != null) - fo.ConvertToFormat (MD1ProjectService.FileFormat, false); + await fo.ConvertToFormat (MD1ProjectService.FileFormat, false); return readObject; }
- object ReadWorkspaceItemFile (FilePath fileName, IProgressMonitor monitor) + Task<object> ReadWorkspaceItemFile (FilePath fileName, ProgressMonitor monitor) { - XmlTextReader reader = new XmlTextReader (new StreamReader (fileName)); - try { - monitor.BeginTask (string.Format (GettextCatalog.GetString ("Loading workspace item: {0}"), fileName), 1); - reader.MoveToContent (); - XmlDataSerializer ser = new XmlDataSerializer (MD1ProjectService.DataContext); - ser.SerializationContext.BaseFile = fileName; - ser.SerializationContext.ProgressMonitor = monitor; - WorkspaceItem entry = (WorkspaceItem) ser.Deserialize (reader, typeof(WorkspaceItem)); - entry.ConvertToFormat (MD1ProjectService.FileFormat, false); - entry.FileName = fileName; - return entry; - } - catch (Exception ex) { - monitor.ReportError (string.Format (GettextCatalog.GetString ("Could not load solution item: {0}"), fileName), ex); - throw; - } - finally { - monitor.EndTask (); - reader.Close (); - } + return Task<object>.Factory.StartNew (delegate { + XmlTextReader reader = new XmlTextReader (new StreamReader (fileName)); + try { + monitor.BeginTask (string.Format (GettextCatalog.GetString ("Loading workspace item: {0}"), fileName), 1); + reader.MoveToContent (); + XmlDataSerializer ser = new XmlDataSerializer (MD1ProjectService.DataContext); + ser.SerializationContext.BaseFile = fileName; + ser.SerializationContext.ProgressMonitor = monitor; + WorkspaceItem entry = (WorkspaceItem)ser.Deserialize (reader, typeof(WorkspaceItem)); + entry.ConvertToFormat (MD1ProjectService.FileFormat, false).Wait (); + entry.FileName = fileName; + return entry; + } catch (Exception ex) { + monitor.ReportError (string.Format (GettextCatalog.GetString ("Could not load solution item: {0}"), fileName), ex); + throw; + } finally { + monitor.EndTask (); + reader.Close (); + } + }); } - public void ConvertToFormat (object obj) + public Task ConvertToFormat (object obj) { + return Task.FromResult (0); }
public IEnumerable<string> GetCompatibilityWarnings (object obj) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1SolutionEntityItemHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1SolutionEntityItemHandler.cs index 6034c074c4..7515e2b50a 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1SolutionEntityItemHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1SolutionEntityItemHandler.cs @@ -29,19 +29,20 @@ using System; using System.CodeDom.Compiler; using MonoDevelop.Core; using MonoDevelop.Projects.Extensions; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Formats.MD1 { internal class MD1SolutionEntityItemHandler: MD1SolutionItemHandler { - public MD1SolutionEntityItemHandler (SolutionEntityItem item): base (item) + public MD1SolutionEntityItemHandler (SolutionItem item): base (item) { } - public override void Save (IProgressMonitor monitor) + public override Task Save (ProgressMonitor monitor) { - SolutionEntityItem it = (SolutionEntityItem) Item; - it.FileFormat.Format.WriteFile (it.FileName, it, monitor); + SolutionItem it = (SolutionItem) Item; + return it.FileFormat.Format.WriteFile (it.FileName, it, monitor); } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1SolutionItemHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1SolutionItemHandler.cs index 4d971784bb..544f48a3de 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1SolutionItemHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1SolutionItemHandler.cs @@ -29,16 +29,17 @@ using System; using System.CodeDom.Compiler; using MonoDevelop.Core; using MonoDevelop.Projects.Extensions; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Formats.MD1 { internal class MD1SolutionItemHandler: SolutionItemHandler { - public MD1SolutionItemHandler (SolutionItem item): base (item) + public MD1SolutionItemHandler (SolutionFolderItem item): base (item) { } - public override void Save (IProgressMonitor monitor) + public override Task Save (ProgressMonitor monitor) { throw new NotSupportedException (); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/CompiledAssemblyProjectMSBuildHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/CompiledAssemblyProjectMSBuildHandler.cs deleted file mode 100644 index 513a286921..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/CompiledAssemblyProjectMSBuildHandler.cs +++ /dev/null @@ -1,68 +0,0 @@ -// -// CompiledAssemblyProjectMSBuildHandler.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 MonoDevelop.Core.Serialization; -using MonoDevelop.Core; - -namespace MonoDevelop.Projects.Formats.MSBuild -{ - class CompiledAssemblyProjectMSBuildHandler: MSBuildProjectHandler - { - public override bool HasSlnData { - get { - return true; - } - } - - public override DataItem WriteSlnData () - { - DataSerializer ser = new DataSerializer (MSBuildProjectService.DataContext); - ser.SerializationContext.BaseFile = EntityItem.FileName; - ser.SerializationContext.DirectorySeparatorChar = '\\'; - DataItem data = (DataItem) ser.Serialize (EntityItem, typeof(CompiledAssemblyProject)); - return data; - } - - public override void ReadSlnData (DataItem item) - { - // Remove the default configuration, since new ones will be loaded - CompiledAssemblyProject project = (CompiledAssemblyProject) EntityItem; - project.Configurations.Clear (); - - DataSerializer ser = new DataSerializer (MSBuildProjectService.DataContext); - ser.SerializationContext.BaseFile = EntityItem.FileName; - ser.SerializationContext.DirectorySeparatorChar = '\\'; - ser.Deserialize (project, item); - } - - protected override void SaveItem (IProgressMonitor monitor) - { - // Do nothing, since info is saved to the .sln - } - } -} - diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/IMSBuildImportProvider.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/IMSBuildImportProvider.cs index 397e432eaf..65f43a52b5 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/IMSBuildImportProvider.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/IMSBuildImportProvider.cs @@ -31,6 +31,6 @@ namespace MonoDevelop.Projects.Formats.MSBuild { public interface IMSBuildImportProvider { - void UpdateImports (SolutionEntityItem item, List<string> imports); + void UpdateImports (SolutionItem item, List<string> imports); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/IMSBuildProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/IMSBuildProject.cs new file mode 100644 index 0000000000..a43393337c --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/IMSBuildProject.cs @@ -0,0 +1,519 @@ +// +// IMSBuildProject.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 MonoDevelop.Core; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Linq; +using System.Linq; + +namespace MonoDevelop.Projects.Formats.MSBuildInternal +{ + public abstract class MSBuildProject + { + Dictionary<object,Dictionary<Type,IMSBuildDataObject>> customDataObjects = new Dictionary<object,Dictionary<Type,IMSBuildDataObject>> (); + Dictionary<string, MSBuildItemGroup> bestGroups; + + public abstract FilePath FileName { get; } + + public FilePath BaseDirectory { + get { return FileName.ParentDirectory; } + } + + public abstract void Load (FilePath file); + + public abstract void Save (string fileName); + + public abstract string SaveToString (); + + public MonoDevelop.Projects.Formats.MSBuild.MSBuildFileFormat Format { + get; + set; + } + + public abstract string DefaultTargets { get; set; } + + public abstract string ToolsVersion { get; set; } + + public T GetObject<T> () where T : IMSBuildDataObject, new() + { + return GetObject<T> (this); + } + + public void SetObject<T> (T t) where T : IMSBuildDataObject + { + SetObject<T> (this, t); + } + + internal T GetObject<T> (object owner) where T : IMSBuildDataObject, new() + { + Dictionary<Type,IMSBuildDataObject> col; + if (!customDataObjects.TryGetValue (owner, out col)) + return default(T); + IMSBuildDataObject res; + col.TryGetValue (typeof(T), out res); + return (T)res; + } + + internal void SetObject<T> (object owner, T t) where T : IMSBuildDataObject + { + Dictionary<Type,IMSBuildDataObject> col; + if (!customDataObjects.TryGetValue (owner, out col)) + col = customDataObjects [owner] = new Dictionary<Type, IMSBuildDataObject> (); + col [typeof(T)] = t; + } + + internal IEnumerable<IMSBuildDataObject> GetDataObjects (object owner) + { + Dictionary<Type,IMSBuildDataObject> col; + if (!customDataObjects.TryGetValue (owner, out col)) + return new IMSBuildDataObject[0]; + else + return col.Values; + } + + public abstract void AddNewImport (string name, MSBuildImport beforeImport = null); + + public abstract void RemoveImport (MSBuildImport import); + + public abstract IEnumerable<MSBuildImport> Imports { get; } + + public MSBuildPropertyGroup GetGlobalPropertyGroup () + { + return PropertyGroups.FirstOrDefault (p => string.IsNullOrEmpty (p.Condition)); + } + + public abstract MSBuildPropertyGroup AddNewPropertyGroup (MSBuildPropertyGroup beforeGroup = null); + + public abstract void RemovePropertyGroup (MSBuildPropertyGroup grp); + + public abstract IEnumerable<MSBuildItem> GetAllItems (); + + public abstract IEnumerable<MSBuildItem> GetAllItems (params string[] names); + + public abstract IEnumerable<MSBuildPropertyGroup> PropertyGroups { get; } + + public abstract IEnumerable<MSBuildItemGroup> ItemGroups { get; } + + public abstract MSBuildItemGroup AddNewItemGroup (); + + public abstract MSBuildItem AddNewItem (string name, string include); + + public MSBuildItemGroup FindBestGroupForItem (string itemName) + { + MSBuildItemGroup group; + + if (bestGroups == null) + bestGroups = new Dictionary<string, MSBuildItemGroup> (); + else { + if (bestGroups.TryGetValue (itemName, out group)) + return group; + } + + foreach (MSBuildItemGroup grp in ItemGroups) { + foreach (MSBuildItem it in grp.Items) { + if (it.Name == itemName) { + bestGroups [itemName] = grp; + return grp; + } + } + } + group = AddNewItemGroup (); + bestGroups [itemName] = group; + return group; + } + + public abstract XmlElement GetProjectExtensions (string section); + + public abstract void SetProjectExtensions (string section, string value); + + public abstract void RemoveProjectExtensions (string section); + + public abstract void RemoveItem (MSBuildItem item); + } + + public abstract class MSBuildItemGroup + { + internal MSBuildItemGroup (MSBuildProject parent) + { + Project = parent; + } + + public abstract MSBuildItem AddNewItem (string name, string include); + + public abstract IEnumerable<MSBuildItem> Items { get; } + + public MSBuildProject Project { get; private set; } + } + + public abstract class MSBuildPropertyGroup: MSBuildPropertySet + { + MSBuildProject project; + + internal MSBuildPropertyGroup (MSBuildProject project): base (project) + { + this.project = project; + } + + public abstract string Label { get; set; } + public abstract string Condition { get; set; } + } + + internal interface IPropertySetImpl + { + bool HasProperty (string name); + MSBuildProperty GetProperty (string name, string condition = null); + MSBuildProperty AddProperty (string name, string condition = null); + bool RemoveProperty (MSBuildProperty prop); + bool RemoveProperty (string name); + void RemoveAllProperties (); + IEnumerable<MSBuildProperty> Properties { get; } + } + + public abstract class MSBuildPropertySet: IMSBuildPropertySet + { + MSBuildProject project; + Dictionary<Type,IMSBuildDataObject> customDataObjects = new Dictionary<Type,IMSBuildDataObject> (); + + internal MSBuildPropertySet (MSBuildProject project) + { + this.project = project; + } + + internal abstract IPropertySetImpl PropertySet { get; } + + public T GetObject<T> () where T : IMSBuildDataObject, new() + { + IMSBuildDataObject res; + customDataObjects.TryGetValue (typeof(T), out res); + return (T)res; + } + + public void SetObject<T> (T t) where T : IMSBuildDataObject + { + customDataObjects [typeof(T)] = t; + } + + internal void WriteDataObjects () + { + foreach (IMSBuildDataObject ob in customDataObjects.Values) + ob.Write (this, project.Format); + } + + public bool HasProperty (string name) + { + return PropertySet.HasProperty (name); + } + + public MSBuildProperty GetProperty (string name, string condition = null) + { + return PropertySet.GetProperty (name, condition); + } + + public MSBuildProperty AddProperty (string name, string condition = null) + { + return PropertySet.AddProperty (name, condition); + } + + MSBuildProperty SafeGetProperty (string name, string condition) + { + var prop = GetProperty (name, condition); + if (prop == null) { + prop = GetProperty (name); + if (prop != null) { + prop.Condition = condition; + return prop; + } + } + return AddProperty (name, condition); + } + + public void SetValue (string name, string value, string defaultValue = null, bool preserveExistingCase = false, bool isXmlValue = false, bool mergeToMainGroup = false, string condition = null) + { + var prop = SafeGetProperty (name, condition); + if (value == defaultValue) { + RemoveProperty (prop); + return; + } + prop.SetValue (value, preserveExistingCase, isXmlValue, mergeToMainGroup); + } + + public void SetValue (string name, XmlElement elem, bool mergeToMainGroup = false, string condition = null) + { + SafeGetProperty (name, condition).SetValue (elem, mergeToMainGroup); + } + + public void SetValue (string name, XElement elem, bool mergeToMainGroup = false, string condition = null) + { + SafeGetProperty (name, condition).SetValue (elem, mergeToMainGroup); + } + + public void SetValue (string name, FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath), bool mergeToMainGroup = false, string condition = null) + { + SafeGetProperty (name, condition).SetValue (value, relativeToProject, relativeToPath, mergeToMainGroup); + } + + public void SetValue (string name, bool value, bool? defaultValue = false, bool mergeToMainGroup = false, string condition = null) + { + SafeGetProperty (name, condition).SetValue (value, mergeToMainGroup); + } + + public string GetValue (string name, string defaultValue = null) + { + var prop = GetProperty (name); + if (prop != null) + return prop.GetValue (); + else + return defaultValue; + } + + public XElement GetXmlValue (string name) + { + var prop = GetProperty (name); + if (prop != null) + return XElement.Parse (prop.GetValue ()); + else + return null; + } + + public FilePath GetPathValue (string name, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + var prop = GetProperty (name); + if (prop != null) + return prop.GetPathValue (relativeToProject, relativeToPath); + else + return defaultValue; + } + + public bool TryGetPathValue (string name, out FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + var prop = GetProperty (name); + if (prop != null) + return prop.TryGetPathValue (out value, relativeToProject, relativeToPath); + else { + value = defaultValue; + return value != default(FilePath); + } + } + + public bool GetBoolValue (string name, bool defaultValue = false) + { + var prop = GetProperty (name); + if (prop != null) + return prop.GetBoolValue (); + else + return defaultValue; + } + + public virtual bool RemoveProperty (string name) + { + return PropertySet.RemoveProperty (name); + } + + public bool RemoveProperty (MSBuildProperty prop) + { + return PropertySet.RemoveProperty (prop); + } + + public void RemoveAllProperties () + { + PropertySet.RemoveAllProperties (); + } + + public IEnumerable<MSBuildProperty> Properties { + get { return PropertySet.Properties; } + } + } + + public interface IMSBuildPropertySet + { + T GetObject<T> () where T:IMSBuildDataObject, new(); + void SetObject<T> (T t) where T:IMSBuildDataObject; + + bool HasProperty (string name); + MSBuildProperty GetProperty (string name, string condition = null); + IEnumerable<MSBuildProperty> Properties { get; } + + void SetValue (string name, string value, string defaultValue = null, bool preserveExistingCase = false, bool isXmlValue = false, bool mergeToMainGroup = false, string condition = null); + void SetValue (string name, XmlElement elem, bool mergeToMainGroup = false, string condition = null); + void SetValue (string name, XElement elem, bool mergeToMainGroup = false, string condition = null); + void SetValue (string name, FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath), bool mergeToMainGroup = false, string condition = null); + void SetValue (string name, bool value, bool? defaultValue = false, bool mergeToMainGroup = false, string condition = null); + + string GetValue (string name, string defaultValue = null); + XElement GetXmlValue (string name); + FilePath GetPathValue (string name, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)); + bool TryGetPathValue (string name, out FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)); + bool GetBoolValue (string name, bool defaultValue = false); + + bool RemoveProperty (string name); + void RemoveAllProperties (); + } + + public abstract class MSBuildImport + { + public abstract string Target { get; set; } + + public abstract string Label { get; set; } + + public abstract string Condition { get; set; } + } + + public abstract class MSBuildProperty + { + MSBuildProject project; + + internal MSBuildProperty (MSBuildProject project) + { + this.project = project; + } + + public abstract string Name { + get; + } + + public abstract string Condition { get; set; } + + public void SetValue (string value, bool preserveExistingCase = false, bool isXmlValue = false, bool mergeToMainGroup = false) + { + if (value == null) + value = String.Empty; + + if (preserveExistingCase) { + var current = GetPropertyValue (); + if (current != null) { + if (current.Equals (value, preserveExistingCase ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture)) + return; + } + } + SetPropertyValue (value, isXmlValue); + } + + public void SetValue (XmlElement elem, bool mergeToMainGroup = false) + { + SetPropertyValue (elem.OuterXml, true); + } + + public void SetValue (XElement elem, bool mergeToMainGroup = false) + { + SetPropertyValue (elem.ToString (), true); + } + + public void SetValue (FilePath value, bool relativeToProject = true, FilePath relativeToPath = default(FilePath), bool mergeToMainGroup = false) + { + string baseDir = null; + if (relativeToProject) { + if (relativeToPath != default(FilePath)) + throw new ArgumentException ("relativeToPath argument can't be used together with relativeToProject"); + baseDir = project.BaseDirectory; + } else if (relativeToPath != null) { + baseDir = relativeToPath; + } + SetPropertyValue (MonoDevelop.Projects.Formats.MSBuild.MSBuildProjectService.ToMSBuildPath (baseDir, value), false); + } + + public void SetValue (bool value, bool mergeToMainGroup = false) + { + SetPropertyValue (value ? "True" : "False", false); + } + + public string GetValue () + { + return GetPropertyValue (); + } + + public XElement GetXmlValue () + { + var val = GetPropertyValue (); + if (val == null) + return null; + return XElement.Parse (val); + } + + public FilePath GetPathValue (bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + var val = GetPropertyValue (); + string baseDir = null; + if (relativeToProject) { + if (relativeToPath != default(FilePath)) + throw new ArgumentException ("relativeToPath argument can't be used together with relativeToProject"); + baseDir = project.BaseDirectory; + } else if (relativeToPath != null) { + baseDir = relativeToPath; + } + return MonoDevelop.Projects.Formats.MSBuild.MSBuildProjectService.FromMSBuildPath (baseDir, val); + } + + public bool TryGetPathValue (out FilePath value, bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + var val = GetPropertyValue (); + string baseDir = null; + if (relativeToProject) { + if (relativeToPath != default(FilePath)) + throw new ArgumentException ("relativeToPath argument can't be used together with relativeToProject"); + baseDir = project.BaseDirectory; + } else if (relativeToPath != null) { + baseDir = relativeToPath; + } + string path; + var res = MonoDevelop.Projects.Formats.MSBuild.MSBuildProjectService.FromMSBuildPath (baseDir, val, out path); + value = path; + return res; + } + + public bool GetBoolValue () + { + var val = GetPropertyValue (); + return val.Equals ("true", StringComparison.InvariantCultureIgnoreCase); + } + + protected abstract void SetPropertyValue (string value, bool isXml); + protected abstract string GetPropertyValue (); + } + + public abstract class MSBuildItem: MSBuildPropertySet + { + MSBuildProject project; + + internal MSBuildItem (MSBuildProject project): base (project) + { + this.project = project; + } + + public abstract string Include { get; } + + public abstract string UnevaluatedInclude { get; } + + public abstract string Name { get; } + } + + public interface IMSBuildDataObject + { + void Read (IMSBuildPropertySet pset, MonoDevelop.Projects.Formats.MSBuild.MSBuildFileFormat format); + void Write (IMSBuildPropertySet pset, MonoDevelop.Projects.Formats.MSBuild.MSBuildFileFormat format); + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/IMSBuildPropertySet.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/IMSBuildPropertySet.cs new file mode 100644 index 0000000000..83a96d9f4c --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/IMSBuildPropertySet.cs @@ -0,0 +1,173 @@ +// +// MSBuildPropertySet.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Linq; +using System.IO; +using System.Collections.Generic; +using System.Xml; +using System.Text; + +using Microsoft.Build.BuildEngine; +using MonoDevelop.Core; +using System.Xml.Linq; +using System.Reflection; +using MonoDevelop.Core.Serialization; +using System.Globalization; + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + + public interface IMSBuildPropertySet + { + string Label { get; set; } + T GetObject<T> () where T:IMSBuildDataObject, new(); + void SetObject<T> (T t) where T:IMSBuildDataObject; + + bool HasProperty (string name); + MSBuildProperty GetProperty (string name); + IEnumerable<MSBuildProperty> Properties { get; } + + void SetValue (string name, string value, string defaultValue = null, bool preserveExistingCase = false, bool mergeToMainGroup = false, string condition = null); + void SetValue (string name, FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath), bool mergeToMainGroup = false, string condition = null); + void SetValue (string name, object value, object defaultValue = null, bool mergeToMainGroup = false, string condition = null); + + string GetValue (string name, string defaultValue = null); + FilePath GetPathValue (string name, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)); + bool TryGetPathValue (string name, out FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)); + T GetValue<T> (string name); + T GetValue<T> (string name, T defaultValue); + object GetValue (string name, Type type, object defaultValue); + + bool RemoveProperty (string name); + void RemoveAllProperties (); + + void SetPropertyOrder (params string[] propertyNames); + + MSBuildProject Project { get; } + } + + public static class IMSBuildPropertySetExtensions + { + class PropInfo + { + public MemberInfo Member; + public ItemPropertyAttribute Attribute; + public bool MergeToMainGroup; + + public Type ReturnType { + get { return (Member is FieldInfo) ? ((FieldInfo)Member).FieldType : ((PropertyInfo)Member).PropertyType; } + } + public object GetValue (object instance) + { + if (Member is FieldInfo) + return ((FieldInfo)Member).GetValue (instance); + else + return ((PropertyInfo)Member).GetValue (instance, null); + } + public void SetValue (object instance, object value) + { + if (Member is FieldInfo) + ((FieldInfo)Member).SetValue (instance, value); + else + ((PropertyInfo)Member).SetValue (instance, value, null); + } + public string Name { + get { return Attribute.Name ?? Member.Name; } + } + } + + static Dictionary<Type,PropInfo[]> types = new Dictionary<Type, PropInfo[]> (); + + static PropInfo[] GetMembers (Type type) + { + PropInfo[] pinfo; + if (types.TryGetValue (type, out pinfo)) + return pinfo; + + List<PropInfo> list = new List<PropInfo> (); + + foreach (var m in type.GetMembers (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { + if (m.DeclaringType != type) + continue; + if (!(m is FieldInfo) && !(m is PropertyInfo)) + continue; + var attr = Attribute.GetCustomAttribute (m, typeof(ItemPropertyAttribute)) as ItemPropertyAttribute; + if (attr == null) + continue; + bool merge = Attribute.IsDefined (m, typeof(MergeToProjectAttribute)); + + list.Add (new PropInfo { + Member = m, + Attribute = attr, + MergeToMainGroup = merge + }); + } + return types [type] = list.ToArray (); + } + + public static void WriteObjectProperties (this IMSBuildPropertySet pset, object ob, Type typeToScan) + { + var props = GetMembers (typeToScan); + foreach (var prop in props) { + if (prop.ReturnType == typeof(FilePath)) { + var val = (FilePath)prop.GetValue (ob); + FilePath def = prop.Attribute.DefaultValue != null ? (string)prop.Attribute.DefaultValue : (string)null; + pset.SetValue (prop.Name, val, def, mergeToMainGroup: prop.MergeToMainGroup); + } else if (prop.Attribute is ProjectPathItemProperty && prop.ReturnType == typeof(string)) { + FilePath val = (string)prop.GetValue (ob); + FilePath def = prop.Attribute.DefaultValue != null ? (string)prop.Attribute.DefaultValue : (string)null; + pset.SetValue (prop.Name, val, def, mergeToMainGroup: prop.MergeToMainGroup); + } else if (prop.ReturnType == typeof(string)) { + pset.SetValue (prop.Name, (string)prop.GetValue (ob), (string)prop.Attribute.DefaultValue, prop.MergeToMainGroup); + } else { + pset.SetValue (prop.Name, prop.GetValue (ob), prop.Attribute.DefaultValue, prop.MergeToMainGroup); + } + } + } + + public static void ReadObjectProperties (this IMSBuildPropertySet pset, object ob, Type typeToScan) + { + var props = GetMembers (typeToScan); + foreach (var prop in props) { + object readVal = null; + if (prop.ReturnType == typeof(FilePath)) { + FilePath def = prop.Attribute.DefaultValue != null ? (string)prop.Attribute.DefaultValue : (string)null; + readVal = pset.GetPathValue (prop.Name, def); + } else if (prop.Attribute is ProjectPathItemProperty && prop.ReturnType == typeof(string)) { + FilePath def = prop.Attribute.DefaultValue != null ? (string)prop.Attribute.DefaultValue : (string)null; + readVal = pset.GetPathValue (prop.Name, def); + readVal = readVal.ToString (); + } else if (prop.ReturnType == typeof(string)) { + readVal = pset.GetValue (prop.Name, (string)prop.Attribute.DefaultValue); + } else { + readVal = pset.GetValue (prop.Name, prop.ReturnType, prop.Attribute.DefaultValue); + } + prop.SetValue (ob, readVal); + } + } + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildEvaluationContext.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildEvaluationContext.cs new file mode 100644 index 0000000000..c681f46c94 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildEvaluationContext.cs @@ -0,0 +1,177 @@ +// +// MSBuildEvaluationContext.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Linq; +using System.IO; +using System.Collections.Generic; +using System.Xml; +using System.Text; + +using Microsoft.Build.BuildEngine; + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + + public class MSBuildEvaluationContext: IExpressionContext + { + Dictionary<string,string> properties = new Dictionary<string, string> (); + bool allResolved; + MSBuildProject project; + + public MSBuildEvaluationContext () + { + } + + internal void InitEvaluation (MSBuildProject project) + { + this.project = project; + SetPropertyValue ("MSBuildThisFile", Path.GetFileName (project.FileName)); + SetPropertyValue ("MSBuildThisFileName", Path.GetFileNameWithoutExtension (project.FileName)); + SetPropertyValue ("MSBuildThisFileDirectory", Path.GetDirectoryName (project.FileName) + Path.DirectorySeparatorChar); + SetPropertyValue ("MSBuildThisFileExtension", Path.GetExtension (project.FileName)); + SetPropertyValue ("MSBuildThisFileFullPath", Path.GetFullPath (project.FileName)); + SetPropertyValue ("VisualStudioReferenceAssemblyVersion", project.ToolsVersion + ".0.0"); + } + + public string GetPropertyValue (string name) + { + string val; + if (properties.TryGetValue (name, out val)) + return val; + else + return Environment.GetEnvironmentVariable (name); + } + + public void SetPropertyValue (string name, string value) + { + properties [name] = value; + } + + public void ClearPropertyValue (string name) + { + properties.Remove (name); + } + + public bool Evaluate (XmlElement source, out XmlElement result) + { + allResolved = true; + result = (XmlElement) EvaluateNode (source); + return allResolved; + } + + XmlNode EvaluateNode (XmlNode source) + { + var elemSource = source as XmlElement; + if (elemSource != null) { + var elem = source.OwnerDocument.CreateElement (elemSource.Prefix, elemSource.LocalName, elemSource.NamespaceURI); + foreach (XmlAttribute attr in elemSource.Attributes) + elem.Attributes.Append ((XmlAttribute)EvaluateNode (attr)); + foreach (XmlNode child in elemSource.ChildNodes) + elem.AppendChild (EvaluateNode (child)); + return elem; + } + + var attSource = source as XmlAttribute; + if (attSource != null) { + bool oldResolved = allResolved; + var att = source.OwnerDocument.CreateAttribute (attSource.Prefix, attSource.LocalName, attSource.NamespaceURI); + att.Value = Evaluate (attSource.Value); + + // Condition attributes don't change the resolution status. Conditions are handled in the property and item objects + if (attSource.Name == "Condition") + allResolved = oldResolved; + + return att; + } + var textSource = source as XmlText; + if (textSource != null) { + return source.OwnerDocument.CreateTextNode (Evaluate (textSource.InnerText)); + } + return source.Clone (); + } + + public bool Evaluate (string str, out string result) + { + allResolved = true; + result = Evaluate (str); + return allResolved; + } + + string Evaluate (string str) + { + int i = str.IndexOf ("$("); + if (i == -1) + return str; + + int last = 0; + + StringBuilder sb = new StringBuilder (); + do { + sb.Append (str, last, i - last); + i += 2; + int j = str.IndexOf (")", i); + if (j == -1) { + allResolved = false; + return ""; + } + + string prop = str.Substring (i, j - i); + string val = GetPropertyValue (prop); + if (val == null) { + allResolved = false; + return ""; + } + + sb.Append (val); + last = j + 1; + i = str.IndexOf ("$(", last); + } + while (i != -1); + + sb.Append (str, last, str.Length - last); + return sb.ToString (); + } + + #region IExpressionContext implementation + + public string EvaluateString (string value) + { + if (value.StartsWith ("$(") && value.EndsWith (")")) + return GetPropertyValue (value.Substring (2, value.Length - 3)) ?? value; + else + return value; + } + + public string FullFileName { + get { + return project.FileName; + } + } + + #endregion + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildFileFormat.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildFileFormat.cs index 05314dfdb7..282f7c0b3d 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildFileFormat.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildFileFormat.cs @@ -33,18 +33,24 @@ using System.IO; using MonoDevelop.Core; using MonoDevelop.Core.Assemblies; using MonoDevelop.Projects.Extensions; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Formats.MSBuild { public abstract class MSBuildFileFormat: IFileFormat { - readonly SlnFileFormat slnFileFormat = new SlnFileFormat (); + readonly SlnFileFormat slnFileFormat; + + protected MSBuildFileFormat () + { + slnFileFormat = new SlnFileFormat (this); + } public string Name { get { return "MSBuild"; } } - public SlnFileFormat SlnFileFormat { + internal SlnFileFormat SlnFileFormat { get { return slnFileFormat; } } @@ -70,7 +76,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild if (slnFileFormat.CanWriteFile (obj, this)) return slnFileFormat.GetValidFormatName (obj, fileName, this); else { - string ext = MSBuildProjectService.GetExtensionForItem ((SolutionEntityItem)obj); + string ext = MSBuildProjectService.GetExtensionForItem ((SolutionItem)obj); if (!string.IsNullOrEmpty (ext)) return fileName.ChangeExtension ("." + ext); else @@ -82,7 +88,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild { if (expectedType.IsAssignableFrom (typeof(Solution)) && slnFileFormat.CanReadFile (file, this)) return true; - else if (expectedType.IsAssignableFrom (typeof(SolutionEntityItem))) { + else if (expectedType.IsAssignableFrom (typeof(SolutionItem))) { if (!MSBuildProjectService.CanReadFile (file)) return false; //TODO: check ProductVersion first @@ -95,12 +101,12 @@ namespace MonoDevelop.Projects.Formats.MSBuild { if (slnFileFormat.CanWriteFile (obj, this)) { Solution sol = (Solution) obj; - foreach (SolutionEntityItem si in sol.GetAllSolutionItems<SolutionEntityItem> ()) + foreach (SolutionItem si in sol.GetAllItems<SolutionItem> ()) if (!CanWriteFile (si)) return false; return true; } - else if (obj is SolutionEntityItem) { + else if (obj is SolutionItem) { DotNetProject p = obj as DotNetProject; // Check the framework only if the project is not loading, since otherwise the // project may not yet have the framework info set. @@ -118,7 +124,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild { if (obj is Solution) { List<string> msg = new List<string> (); - foreach (SolutionEntityItem si in ((Solution)obj).GetAllSolutionItems<SolutionEntityItem> ()) { + foreach (SolutionItem si in ((Solution)obj).GetAllItems<SolutionItem> ()) { IEnumerable<string> ws = GetCompatibilityWarnings (si); if (ws != null) msg.AddRange (ws); @@ -135,26 +141,21 @@ namespace MonoDevelop.Projects.Formats.MSBuild return null; } - public void WriteFile (FilePath file, object obj, IProgressMonitor monitor) + public async Task WriteFile (FilePath file, object obj, ProgressMonitor monitor) { if (slnFileFormat.CanWriteFile (obj, this)) { - slnFileFormat.WriteFile (file, obj, this, true, monitor); + await slnFileFormat.WriteFile (file, obj, true, monitor); } else { - SolutionEntityItem item = (SolutionEntityItem) obj; - if (!(item.ItemHandler is MSBuildProjectHandler)) - MSBuildProjectService.InitializeItemHandler (item); - MSBuildProjectHandler handler = (MSBuildProjectHandler) item.ItemHandler; - handler.SetSolutionFormat (this, false); - handler.Save (monitor); + throw new NotSupportedException (); } }
- public object ReadFile (FilePath file, Type expectedType, MonoDevelop.Core.IProgressMonitor monitor) + public async Task<object> ReadFile (FilePath file, Type expectedType, MonoDevelop.Core.ProgressMonitor monitor) { if (slnFileFormat.CanReadFile (file, this)) - return slnFileFormat.ReadFile (file, this, monitor); + return await slnFileFormat.ReadFile (file, monitor); else - return MSBuildProjectService.LoadItem (monitor, file, null, null, null); + return await MSBuildProjectService.LoadItem (monitor, file, null, null, null); }
public List<FilePath> GetItemFiles (object obj) @@ -162,24 +163,17 @@ namespace MonoDevelop.Projects.Formats.MSBuild return new List<FilePath> (); } - public void ConvertToFormat (object obj) + public Task ConvertToFormat (object obj) { if (obj == null) - return; + return Task.FromResult(0); - MSBuildHandler handler; - SolutionItem item = obj as SolutionItem; + SolutionFolderItem item = obj as SolutionFolderItem; if (item != null) { - handler = item.GetItemHandler() as MSBuildHandler; - if (handler != null) { - handler.SetSolutionFormat (this, true); - return; - } + item.SetSolutionFormat (this, true); + return Task.FromResult(0); } - - MSBuildProjectService.InitializeItemHandler (item); - handler = (MSBuildHandler) item.ItemHandler; - handler.SetSolutionFormat (this, true); + return Task.FromResult (0); } public bool SupportsMixedFormats { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildHandler.cs deleted file mode 100644 index 29e862cb26..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildHandler.cs +++ /dev/null @@ -1,172 +0,0 @@ -// MSBuildHandler.cs -// -// Author: -// Lluis Sanchez Gual <lluis@novell.com> -// -// Copyright (c) 2008 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.Collections.Generic; -using System.Reflection; -using MonoDevelop.Core; -using MonoDevelop.Core.Serialization; -using MonoDevelop.Projects; -using MonoDevelop.Projects.Extensions; - -namespace MonoDevelop.Projects.Formats.MSBuild -{ - public class MSBuildHandler: ISolutionItemHandler - { - SolutionItem item; - string typeGuid; - string id; - string[] slnProjectContent; - DataItem customSlnData; - - internal List<string> UnresolvedProjectDependencies { get; set; } - - internal protected MSBuildHandler () - { - } - - public MSBuildHandler (string typeGuid, string itemId) - { - Initialize (typeGuid, itemId); - } - - internal void Initialize (string typeGuid, string itemId) - { - this.typeGuid = typeGuid; - this.id = itemId; - } - - // When set, it means this item is saved as part of a global solution save operation - internal bool SavingSolution { get; set; } - - internal protected SolutionItem Item { - get { return item; } - set { item = value; } - } - - public virtual bool SyncFileName { - get { return true; } - } - - public string TypeGuid { - get { - return typeGuid; - } - } - - internal string[] SlnProjectContent { - get { - return slnProjectContent; - } - set { - slnProjectContent = value; - } - } - - public string ItemId { - get { - if (id == null) - id = String.Format ("{{{0}}}", System.Guid.NewGuid ().ToString ().ToUpper ()); - return id; - } - set { id = value; } - } - - internal MSBuildFileFormat SolutionFormat { get; private set; } - - internal virtual void SetSolutionFormat (MSBuildFileFormat format, bool converting) - { - SolutionFormat = format; - } - - public virtual BuildResult RunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) - { - throw new NotSupportedException (); - } - - public void Save (IProgressMonitor monitor) - { - if (HasSlnData && !SavingSolution && Item.ParentSolution != null) { - // The project has data that has to be saved in the solution, but the solution is not being saved. Do it now. - monitor.BeginTask (null, 2); - SaveItem (monitor); - monitor.Step (1); - Solution sol = Item.ParentSolution; - SolutionFormat.SlnFileFormat.WriteFile (sol.FileName, sol, SolutionFormat, false, monitor); - sol.NeedsReload = false; - monitor.EndTask (); - } else - SaveItem (monitor); - } - - protected virtual void SaveItem (MonoDevelop.Core.IProgressMonitor monitor) - { - throw new NotSupportedException (); - } - - public virtual void OnModified (string hint) - { - } - - public virtual void Dispose () - { - } - - public virtual bool HasSlnData { - get { return false; } - } - - public virtual DataItem WriteSlnData () - { - return customSlnData; - } - - public virtual void ReadSlnData (DataItem item) - { - customSlnData = item; - } - - /// <summary> - /// Gets a service instance of a given type - /// </summary> - /// <returns> - /// The service. - /// </returns> - /// <param name='t'> - /// Type of the service - /// </param> - /// <remarks> - /// This method looks for an imlpementation of a service of the given type. - /// </remarks> - public virtual object GetService (Type t) - { - return null; - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildImport.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildImport.cs new file mode 100644 index 0000000000..0948c2a0b9 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildImport.cs @@ -0,0 +1,56 @@ +// +// MSBuildImport.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Xml; + + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + + public class MSBuildImport: MSBuildObject + { + string evaluatedProjectPath; + + public MSBuildImport (XmlElement elem): base (elem) + { + } + + public string Project { + get { return Element.GetAttribute ("Project"); } + set { Element.SetAttribute ("Project", value); } + } + + public string EvaluatedProject { + get { return evaluatedProjectPath ?? Project; } + } + + internal void SetEvalResult (string evaluatedProjectPath) + { + this.evaluatedProjectPath = evaluatedProjectPath; + } + } + +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildItem.cs new file mode 100644 index 0000000000..9858088f45 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildItem.cs @@ -0,0 +1,152 @@ +// +// MSBuildItem.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Xml; + + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + public class MSBuildItem: MSBuildObject, IMSBuildItemEvaluated + { + MSBuildPropertyGroup metadata; + MSBuildPropertyGroupEvaluated evaluatedMetadata; + MSBuildProject parent; + string evaluatedInclude; + + internal MSBuildItem (MSBuildProject parent, XmlElement elem): base (elem) + { + this.parent = parent; + } + + public string Include { + get { return evaluatedInclude ?? UnevaluatedInclude; } + set { evaluatedInclude = UnevaluatedInclude = value; } + } + + public string UnevaluatedInclude { + get { return Element.GetAttribute ("Include"); } + set { Element.SetAttribute ("Include", value); } + } + + internal void SetEvalResult (string value) + { + this.evaluatedInclude = value; + } + + public bool IsImported { + get; + set; + } + + public string Name { + get { return Element.Name; } + } + + public IMSBuildPropertySet Metadata { + get { + if (metadata == null) { + metadata = new MSBuildPropertyGroup (parent, Element); + metadata.UppercaseBools = true; + } + return metadata; + } + } + + public IMSBuildPropertyGroupEvaluated EvaluatedMetadata { + get { + if (evaluatedMetadata == null) + evaluatedMetadata = new MSBuildPropertyGroupEvaluated (parent); + return evaluatedMetadata; + } + } + + IMSBuildPropertyGroupEvaluated IMSBuildItemEvaluated.Metadata { + get { return EvaluatedMetadata; } + } + + public void WriteDataObjects () + { + metadata.WriteDataObjects (); + if (!Element.HasChildNodes) + Element.IsEmpty = true; + } + } + + class MSBuildItemEvaluated: MSBuildObject, IMSBuildItemEvaluated + { + MSBuildPropertyGroupEvaluated metadata; + MSBuildProject parent; + string evaluatedInclude; + string include; + + internal MSBuildItemEvaluated (MSBuildProject parent, string name, string include, string evaluatedInclude): base (null) + { + this.include = include; + this.evaluatedInclude = evaluatedInclude; + this.parent = parent; + Name = name; + } + + public string Include { + get { return evaluatedInclude; } + } + + public string UnevaluatedInclude { + get { return include; } + } + + public bool IsImported { + get; + internal set; + } + + public string Name { get; private set; } + + public IMSBuildPropertyGroupEvaluated Metadata { + get { + if (metadata == null) + metadata = new MSBuildPropertyGroupEvaluated (parent); + return metadata; + } + } + } + + + public interface IMSBuildItemEvaluated + { + string Include { get; } + + string UnevaluatedInclude { get; } + + string Condition { get; } + + bool IsImported { get; } + + string Name { get; } + + IMSBuildPropertyGroupEvaluated Metadata { get; } + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildItemGroup.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildItemGroup.cs new file mode 100644 index 0000000000..5f1a2eab17 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildItemGroup.cs @@ -0,0 +1,73 @@ +// +// MSBuildItemGroup.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Collections.Generic; +using System.Xml; + + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + + public class MSBuildItemGroup: MSBuildObject + { + MSBuildProject parent; + + internal MSBuildItemGroup (MSBuildProject parent, XmlElement elem): base (elem) + { + this.parent = parent; + } + + public bool IsImported { + get; + set; + } + + public MSBuildItem AddNewItem (string name, string include) + { + XmlElement elem = AddChildElement (name); + MSBuildItem it = parent.GetItem (elem); + it.Include = include; + return it; + } + + public IEnumerable<MSBuildItem> Items { + get { + foreach (XmlNode node in Element.ChildNodes) { + XmlElement elem = node as XmlElement; + if (elem != null) + yield return parent.GetItem (elem); + } + } + } + + internal override void Evaluate (MSBuildEvaluationContext context) + { + foreach (var item in Items) + item.Evaluate (context); + } + } + +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildObject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildObject.cs new file mode 100644 index 0000000000..4c74f54285 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildObject.cs @@ -0,0 +1,75 @@ +// +// MSBuildObject.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Xml; + + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + + public class MSBuildObject + { + XmlElement elem; + + public MSBuildObject (XmlElement elem) + { + this.elem = elem; + } + + public XmlElement Element { + get { return elem; } + } + + protected XmlElement AddChildElement (string name) + { + XmlElement e = elem.OwnerDocument.CreateElement (null, name, MSBuildProject.Schema); + elem.AppendChild (e); + return e; + } + + public string Label { + get { return Element.GetAttribute ("Label"); } + set { Element.SetAttribute ("Label", value); } + } + + public string Condition { + get { + return Element != null ? Element.GetAttribute ("Condition") : null; + } + set { + if (string.IsNullOrEmpty (value)) + Element.RemoveAttribute ("Condition"); + else + Element.SetAttribute ("Condition", value); + } + } + + internal virtual void Evaluate (MSBuildEvaluationContext context) + { + } + } + +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProject.cs index 2976287a16..4674d3f027 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProject.cs @@ -26,33 +26,39 @@ // using System; -using System.Linq; using System.IO; using System.Collections.Generic; using System.Xml; using System.Text; +using MonoDevelop.Core; using MonoDevelop.Projects.Utility; -using MonoDevelop.Projects.Text; using Microsoft.Build.BuildEngine; +using MSProject = Microsoft.Build.BuildEngine.Project; +using System.Linq; +using MonoDevelop.Projects.Text; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Formats.MSBuild { public class MSBuildProject { XmlDocument doc; - string file; + FilePath file; Dictionary<XmlElement,MSBuildObject> elemCache = new Dictionary<XmlElement,MSBuildObject> (); Dictionary<string, MSBuildItemGroup> bestGroups; - + List<MSBuildItemEvaluated> evaluatedItems = new List<MSBuildItemEvaluated> (); + List<MSBuildItemEvaluated> evaluatedItemsIgnoringCondition; + MSBuildEvaluatedPropertyCollection evaluatedProperties; + public const string Schema = "http://schemas.microsoft.com/developer/msbuild/2003"; static XmlNamespaceManager manager; bool endsWithEmptyLine; string newLine = Environment.NewLine; ByteOrderMark bom; - - internal static XmlNamespaceManager XmlNamespaceManager { + + public static XmlNamespaceManager XmlNamespaceManager { get { if (manager == null) { manager = new XmlNamespaceManager (new NameTable ()); @@ -62,9 +68,21 @@ namespace MonoDevelop.Projects.Formats.MSBuild } } - public string FileName { + public FilePath FileName { get { return file; } + set { file = value; } + } + + public FilePath BaseDirectory { + get { return file.ParentDirectory; } } + + public MSBuildFileFormat Format { + get; + set; + } + + public bool IsNewProject { get; private set; } public XmlDocument Document { get { return doc; } @@ -72,14 +90,27 @@ namespace MonoDevelop.Projects.Formats.MSBuild public MSBuildProject () { + evaluatedProperties = new MSBuildEvaluatedPropertyCollection (this); + evaluatedItemsIgnoringCondition = new List<MSBuildItemEvaluated> (); doc = new XmlDocument (); doc.PreserveWhitespace = false; doc.AppendChild (doc.CreateElement (null, "Project", Schema)); + IsNewProject = true; + } + + public static Task<MSBuildProject> LoadAsync (string file) + { + return Task<MSBuildProject>.Factory.StartNew (delegate { + var p = new MSBuildProject (); + p.Load (file); + return p; + }); } public void Load (string file) { this.file = file; + IsNewProject = false; using (FileStream fs = File.OpenRead (file)) { byte[] buf = new byte [1024]; int nread, i; @@ -172,18 +203,87 @@ namespace MonoDevelop.Projects.Formats.MSBuild return content; } + void WriteDataObjects () + { + foreach (var ob in elemCache.Values) { + if (ob is MSBuildPropertyGroup) + ((MSBuildPropertyGroup)ob).WriteDataObjects (); + else if (ob is MSBuildItem) + ((MSBuildItem)ob).WriteDataObjects (); + } + } + public void Evaluate () { - Evaluate (new MSBuildEvaluationContext ()); + try { + Engine e = new Engine (); + MSProject project = new MSProject (e); + project.Load (FileName); + SyncBuildProject (project); + } catch (Exception ex) { + // If the project can't be evaluated don't crash + LoggingService.LogError ("MSBuild project could not be evaluated", ex); + } } - public void Evaluate (MSBuildEvaluationContext context) + void SyncBuildProject (MSProject project) { - context.InitEvaluation (this); - foreach (var pg in PropertyGroups) - pg.Evaluate (context); - foreach (var pg in ItemGroups) - pg.Evaluate (context); + var xmlGroups = PropertyGroups.ToArray (); + var buildGroups = project.PropertyGroups.Cast<BuildPropertyGroup> ().ToArray (); + for (int n=0; n<xmlGroups.Length && n<buildGroups.Length; n++) + SyncBuildPropertyGroup (xmlGroups [n], buildGroups [n]); + + var xmlItems = ItemGroups.ToArray (); + var buildItems = project.ItemGroups.Cast<BuildItemGroup> ().Where (g => !g.IsImported).ToArray (); + for (int n=0; n<xmlItems.Length && n<buildItems.Length; n++) + SyncBuildItemGroup (xmlItems [n], buildItems [n]); + + var xmlImports = Imports.ToArray (); + var buildImports = project.Imports.Cast<Import> ().ToArray (); + for (int n = 0; n < xmlImports.Length && n < buildImports.Length; n++) + xmlImports [n].SetEvalResult (buildImports [n].EvaluatedProjectPath); + + foreach (BuildItem it in project.EvaluatedItems) { + var xit = new MSBuildItemEvaluated (this, it.Name, it.Include, it.FinalItemSpec); + xit.IsImported = it.IsImported; + ((MSBuildPropertyGroupEvaluated)xit.Metadata).Sync (it); + evaluatedItems.Add (xit); + } + + foreach (BuildItem it in project.EvaluatedItemsIgnoringCondition) { + var xit = new MSBuildItemEvaluated (this, it.Name, it.Include, it.FinalItemSpec); + xit.IsImported = it.IsImported; + ((MSBuildPropertyGroupEvaluated)xit.Metadata).Sync (it); + evaluatedItemsIgnoringCondition.Add (xit); + } + + evaluatedProperties.Sync (project.EvaluatedProperties); + } + + void SyncBuildPropertyGroup (MSBuildPropertyGroup xmlGroup, BuildPropertyGroup buildGroup) + { + var xmlProps = xmlGroup.Properties.ToArray (); + var buildProps = buildGroup.Cast<BuildProperty> ().ToArray (); + for (int n = 0; n < xmlProps.Length && n < buildProps.Length; n++) + SyncBuildProperty (xmlProps [n], buildProps [n]); + } + + void SyncBuildProperty (MSBuildProperty xmlProp, BuildProperty buildProp) + { + } + + void SyncBuildItemGroup (MSBuildItemGroup xmlGroup, BuildItemGroup buildGroup) + { + var xmlItems = xmlGroup.Items.ToArray (); + var buildItems = buildGroup.Cast<BuildItem> ().ToArray (); + for (int n = 0; n < xmlItems.Length && n < buildItems.Length; n++) + SyncBuildItem (xmlItems [n], buildItems [n]); + } + + void SyncBuildItem (MSBuildItem xmlItem, BuildItem buildItem) + { + xmlItem.SetEvalResult (buildItem.FinalItemSpec); + ((MSBuildPropertyGroupEvaluated)xmlItem.EvaluatedMetadata).Sync (buildItem); } public string DefaultTargets { @@ -200,11 +300,43 @@ namespace MonoDevelop.Projects.Formats.MSBuild doc.DocumentElement.RemoveAttribute ("ToolsVersion"); } } + + public string[] ProjectTypeGuids { + get { return GetGlobalPropertyGroup ().GetValue ("ProjectTypeGuids", "").Split (new []{';'}, StringSplitOptions.RemoveEmptyEntries).Select (t => t.Trim()).ToArray (); } + set { GetGlobalPropertyGroup ().SetValue ("ProjectTypeGuids", string.Join (";", value), preserveExistingCase:true); } + } + + public bool AddProjectTypeGuid (string guid) + { + var guids = GetGlobalPropertyGroup ().GetValue ("ProjectTypeGuids", "").Trim (); + if (guids.IndexOf (guid, StringComparison.OrdinalIgnoreCase) == -1) { + if (!string.IsNullOrEmpty (guids)) + guids += ";" + guid; + else + guids = guid; + GetGlobalPropertyGroup ().SetValue ("ProjectTypeGuids", guids, preserveExistingCase: true); + return true; + } + return false; + } - public MSBuildImport AddNewImport (string name, MSBuildImport beforeImport = null) + public bool RemoveProjectTypeGuid (string guid) + { + var guids = ProjectTypeGuids; + var newGuids = guids.Where (g => !g.Equals (guid, StringComparison.OrdinalIgnoreCase)).ToArray (); + if (newGuids.Length != guids.Length) { + ProjectTypeGuids = newGuids; + return true; + } else + return false; + } + + public MSBuildImport AddNewImport (string name, string condition = null, MSBuildImport beforeImport = null) { XmlElement elem = doc.CreateElement (null, "Import", MSBuildProject.Schema); elem.SetAttribute ("Project", name); + if (condition != null) + elem.SetAttribute ("Condition", condition); if (beforeImport != null) { doc.DocumentElement.InsertBefore (elem, beforeImport.Element); @@ -215,7 +347,12 @@ namespace MonoDevelop.Projects.Formats.MSBuild else doc.DocumentElement.AppendChild (elem); } - return new MSBuildImport (elem); + return GetImport (elem); + } + + public MSBuildImport GetImport (string name, string condition = null) + { + return Imports.FirstOrDefault (i => i.Project == name && i.Condition == condition); } public void RemoveImport (string name) @@ -228,21 +365,37 @@ namespace MonoDevelop.Projects.Formats.MSBuild Console.WriteLine ("ppnf:"); } + public void RemoveImport (MSBuildImport import) + { + import.Element.ParentNode.RemoveChild (import.Element); + } + public IEnumerable<MSBuildImport> Imports { get { foreach (XmlElement elem in doc.DocumentElement.SelectNodes ("tns:Import", XmlNamespaceManager)) - yield return new MSBuildImport (elem); + yield return GetImport (elem); } } - - public MSBuildPropertySet GetGlobalPropertyGroup () + + public IMSBuildEvaluatedPropertyCollection EvaluatedProperties { + get { return evaluatedProperties; } + } + + public IEnumerable<IMSBuildItemEvaluated> EvaluatedItems { + get { return evaluatedItems; } + } + + public IEnumerable<IMSBuildItemEvaluated> EvaluatedItemsIgnoringCondition { + get { return evaluatedItemsIgnoringCondition; } + } + + public IMSBuildPropertySet GetGlobalPropertyGroup () { - MSBuildPropertyGroupMerged res = new MSBuildPropertyGroupMerged (); foreach (MSBuildPropertyGroup grp in PropertyGroups) { if (grp.Condition.Length == 0) - res.Add (grp); + return grp; } - return res.GroupCount > 0 ? res : null; + return null; } public MSBuildPropertyGroup AddNewPropertyGroup (bool insertAtEnd) @@ -336,31 +489,67 @@ namespace MonoDevelop.Projects.Formats.MSBuild return group; } - public string GetProjectExtensions (string section) + public XmlElement GetProjectExtension (string section) { - XmlElement elem = doc.DocumentElement.SelectSingleNode ("tns:ProjectExtensions/tns:" + section, XmlNamespaceManager) as XmlElement; - if (elem != null) - return elem.InnerXml; - else - return string.Empty; + return doc.DocumentElement.SelectSingleNode ("tns:ProjectExtensions/tns:" + section, XmlNamespaceManager) as XmlElement; } - public void SetProjectExtensions (string section, string value) + public XmlElement GetMonoDevelopProjectExtension (string section) { + return doc.DocumentElement.SelectSingleNode ("tns:ProjectExtensions/tns:MonoDevelop/tns:Properties/tns:" + section, XmlNamespaceManager) as XmlElement; + } + + public void SetProjectExtension (string section, XmlElement value) + { + if (value.OwnerDocument != doc) + value = (XmlElement)doc.ImportNode (value, true); + + XmlElement elem = doc.DocumentElement ["ProjectExtensions", MSBuildProject.Schema]; + if (elem == null) { + elem = doc.CreateElement (null, "ProjectExtensions", MSBuildProject.Schema); + doc.DocumentElement.AppendChild (elem); + } + XmlElement sec = elem [section]; + if (sec == null) + elem.AppendChild (value); + else { + elem.InsertAfter (value, sec); + elem.RemoveChild (sec); + } + } + + public void SetMonoDevelopProjectExtension (string section, XmlElement value) + { + if (value.OwnerDocument != doc) + value = (XmlElement)doc.ImportNode (value, true); + XmlElement elem = doc.DocumentElement ["ProjectExtensions", MSBuildProject.Schema]; if (elem == null) { elem = doc.CreateElement (null, "ProjectExtensions", MSBuildProject.Schema); doc.DocumentElement.AppendChild (elem); } + var parent = elem; + elem = parent ["MonoDevelop", MSBuildProject.Schema]; + if (elem == null) { + elem = doc.CreateElement (null, "MonoDevelop", MSBuildProject.Schema); + parent.AppendChild (elem); + } + parent = elem; + elem = parent ["Properties", MSBuildProject.Schema]; + if (elem == null) { + elem = doc.CreateElement (null, "Properties", MSBuildProject.Schema); + parent.AppendChild (elem); + } XmlElement sec = elem [section]; - if (sec == null) { - sec = doc.CreateElement (null, section, MSBuildProject.Schema); - elem.AppendChild (sec); + if (sec == null) + elem.AppendChild (value); + else { + elem.InsertAfter (value, sec); + elem.RemoveChild (sec); } - sec.InnerXml = value; } - public void RemoveProjectExtensions (string section) + public void RemoveProjectExtension (string section) { XmlElement elem = doc.DocumentElement.SelectSingleNode ("tns:ProjectExtensions/tns:" + section, XmlNamespaceManager) as XmlElement; if (elem != null) { @@ -371,6 +560,19 @@ namespace MonoDevelop.Projects.Formats.MSBuild } } + public void RemoveMonoDevelopProjectExtension (string section) + { + XmlElement elem = doc.DocumentElement.SelectSingleNode ("tns:ProjectExtensions/tns:MonoDevelop/tns:Properties/tns:" + section, XmlNamespaceManager) as XmlElement; + if (elem != null) { + do { + XmlElement parent = (XmlElement) elem.ParentNode; + parent.RemoveChild (elem); + elem = parent; + } + while (!elem.HasChildNodes); + } + } + public void RemoveItem (MSBuildItem item) { elemCache.Remove (item.Element); @@ -383,12 +585,22 @@ namespace MonoDevelop.Projects.Formats.MSBuild } } + internal MSBuildImport GetImport (XmlElement elem) + { + MSBuildObject ob; + if (elemCache.TryGetValue (elem, out ob)) + return (MSBuildImport) ob; + MSBuildImport it = new MSBuildImport (elem); + elemCache [elem] = it; + return it; + } + internal MSBuildItem GetItem (XmlElement elem) { MSBuildObject ob; if (elemCache.TryGetValue (elem, out ob)) return (MSBuildItem) ob; - MSBuildItem it = new MSBuildItem (elem); + MSBuildItem it = new MSBuildItem (this, elem); elemCache [elem] = it; return it; } @@ -420,628 +632,12 @@ namespace MonoDevelop.Projects.Formats.MSBuild } } - public class MSBuildObject - { - XmlElement elem; - XmlElement evaluatedElem; - public MSBuildObject (XmlElement elem) - { - this.elem = elem; - } - - public XmlElement Element { - get { return elem; } - } - - public XmlElement EvaluatedElement { - get { return evaluatedElem ?? elem; } - protected set { evaluatedElem = value; } - } - - public bool IsEvaluated { - get { return evaluatedElem != null; } - } - - protected XmlElement AddChildElement (string name) - { - XmlElement e = elem.OwnerDocument.CreateElement (null, name, MSBuildProject.Schema); - elem.AppendChild (e); - return e; - } - - public string Label { - get { return EvaluatedElement.GetAttribute ("Label"); } - set { Element.SetAttribute ("Label", value); } - } - - public string Condition { - get { - return Element.GetAttribute ("Condition"); - } - set { - if (string.IsNullOrEmpty (value)) - Element.RemoveAttribute ("Condition"); - else - Element.SetAttribute ("Condition", value); - } - } - - internal virtual void Evaluate (MSBuildEvaluationContext context) - { - } - } - - public class MSBuildImport: MSBuildObject - { - public MSBuildImport (XmlElement elem): base (elem) - { - } - - public string Project { - get { return EvaluatedElement.GetAttribute ("Project"); } - set { Element.SetAttribute ("Project", value); } - } - - public string Condition { - get { return EvaluatedElement.GetAttribute ("Condition"); } - set { Element.SetAttribute ("Condition", value); } - } - } - public class MSBuildProperty: MSBuildObject - { - public MSBuildProperty (XmlElement elem): base (elem) - { - } - - public string Name { - get { return Element.Name; } - } - - internal bool Overwritten { get; set; } - - public string GetValue (bool isXml = false) - { - if (isXml) - return EvaluatedElement.InnerXml; - return EvaluatedElement.InnerText; - } - - public void SetValue (string value, bool isXml = false) - { - if (isXml) - Element.InnerXml = value; - else - Element.InnerText = value; - } - - internal override void Evaluate (MSBuildEvaluationContext context) - { - EvaluatedElement = null; - - if (!string.IsNullOrEmpty (Condition)) { - string cond; - if (!context.Evaluate (Condition, out cond)) { - // The value could not be evaluated, so if there is an existing value, it is not valid anymore - context.ClearPropertyValue (Name); - return; - } - if (!ConditionParser.ParseAndEvaluate (cond, context)) - return; - } - - XmlElement elem; - - if (context.Evaluate (Element, out elem)) { - EvaluatedElement = elem; - context.SetPropertyValue (Name, GetValue ()); - } else { - // The value could not be evaluated, so if there is an existing value, it is not valid anymore - context.ClearPropertyValue (Name); - } - } - } - public interface MSBuildPropertySet - { - MSBuildProperty GetProperty (string name); - IEnumerable<MSBuildProperty> Properties { get; } - MSBuildProperty SetPropertyValue (string name, string value, bool preserveExistingCase, bool isXml = false); - string GetPropertyValue (string name, bool isXml = false); - bool RemoveProperty (string name); - void RemoveAllProperties (); - void UnMerge (MSBuildPropertySet baseGrp, ISet<string> propertiesToExclude); - } - class MSBuildPropertyGroupMerged: MSBuildPropertySet - { - List<MSBuildPropertyGroup> groups = new List<MSBuildPropertyGroup> (); - - public void Add (MSBuildPropertyGroup g) - { - groups.Add (g); - } - - public int GroupCount { - get { return groups.Count; } - } - - public MSBuildProperty GetProperty (string name) - { - // Find property in reverse order, since the last set - // value is the good one - for (int n=groups.Count - 1; n >= 0; n--) { - var g = groups [n]; - MSBuildProperty p = g.GetProperty (name); - if (p != null) - return p; - } - return null; - } - - public MSBuildProperty SetPropertyValue (string name, string value, bool preserveExistingCase, bool isXml = false) - { - MSBuildProperty p = GetProperty (name); - if (p != null) { - if (!preserveExistingCase || !string.Equals (value, p.GetValue (isXml), StringComparison.OrdinalIgnoreCase)) { - p.SetValue (value, isXml); - } - return p; - } - return groups [0].SetPropertyValue (name, value, preserveExistingCase, isXml); - } - - public string GetPropertyValue (string name, bool isXml = false) - { - MSBuildProperty prop = GetProperty (name); - return prop != null ? prop.GetValue (isXml) : null; - } - - public bool RemoveProperty (string name) - { - bool found = false; - foreach (var g in groups) { - if (g.RemoveProperty (name)) { - Prune (g); - found = true; - } - } - return found; - } - - public void RemoveAllProperties () - { - foreach (var g in groups) { - g.RemoveAllProperties (); - Prune (g); - } - } - - public void UnMerge (MSBuildPropertySet baseGrp, ISet<string> propertiesToExclude) - { - foreach (var g in groups) { - g.UnMerge (baseGrp, propertiesToExclude); - } - } - - public IEnumerable<MSBuildProperty> Properties { - get { - foreach (var g in groups) { - foreach (var p in g.Properties) - yield return p; - } - } - } - - void Prune (MSBuildPropertyGroup g) - { - if (g != groups [0] && !g.Properties.Any()) { - // Remove this group since it's now empty - g.Parent.RemoveGroup (g); - } - } - } - public class MSBuildPropertyGroup: MSBuildObject, MSBuildPropertySet - { - Dictionary<string,MSBuildProperty> properties = new Dictionary<string,MSBuildProperty> (); - List<MSBuildProperty> propertyList = new List<MSBuildProperty> (); - MSBuildProject parent; - - public MSBuildPropertyGroup (MSBuildProject parent, XmlElement elem): base (elem) - { - this.parent = parent; - - foreach (var pelem in Element.ChildNodes.OfType<XmlElement> ()) { - MSBuildProperty prevSameName; - if (properties.TryGetValue (pelem.Name, out prevSameName)) - prevSameName.Overwritten = true; - - var prop = new MSBuildProperty (pelem); - propertyList.Add (prop); - properties [pelem.Name] = prop; // If a property is defined more than once, we only care about the last registered value - } - } - - public MSBuildProject Parent { - get { - return this.parent; - } - } - - public MSBuildProperty GetProperty (string name) - { - MSBuildProperty prop; - properties.TryGetValue (name, out prop); - return prop; - } - - public IEnumerable<MSBuildProperty> Properties { - get { - return propertyList.Where (p => !p.Overwritten); - } - } - - public MSBuildProperty SetPropertyValue (string name, string value, bool preserveExistingCase, bool isXml = false) - { - MSBuildProperty prop = GetProperty (name); - if (prop == null) { - XmlElement pelem = AddChildElement (name); - prop = new MSBuildProperty (pelem); - properties [name] = prop; - propertyList.Add (prop); - prop.SetValue (value, isXml); - } else if (!preserveExistingCase || !string.Equals (value, prop.GetValue (isXml), StringComparison.OrdinalIgnoreCase)) { - prop.SetValue (value, isXml); - } - return prop; - } - - public string GetPropertyValue (string name, bool isXml = false) - { - MSBuildProperty prop = GetProperty (name); - if (prop == null) - return null; - else - return prop.GetValue (isXml); - } - - public bool RemoveProperty (string name) - { - MSBuildProperty prop = GetProperty (name); - if (prop != null) { - properties.Remove (name); - propertyList.Remove (prop); - Element.RemoveChild (prop.Element); - return true; - } - return false; - } - - public void RemoveAllProperties () - { - List<XmlNode> toDelete = new List<XmlNode> (); - foreach (XmlNode node in Element.ChildNodes) { - if (node is XmlElement) - toDelete.Add (node); - } - foreach (XmlNode node in toDelete) - Element.RemoveChild (node); - properties.Clear (); - propertyList.Clear (); - } - - public void UnMerge (MSBuildPropertySet baseGrp, ISet<string> propsToExclude) - { - foreach (MSBuildProperty prop in baseGrp.Properties) { - if (propsToExclude != null && propsToExclude.Contains (prop.Name)) - continue; - MSBuildProperty thisProp = GetProperty (prop.Name); - if (thisProp != null && prop.GetValue (true).Equals (thisProp.GetValue (true), StringComparison.OrdinalIgnoreCase)) - RemoveProperty (prop.Name); - } - } - - internal override void Evaluate (MSBuildEvaluationContext context) - { - if (!string.IsNullOrEmpty (Condition)) { - string cond; - if (!context.Evaluate (Condition, out cond)) { - // The condition could not be evaluated. Clear all properties that this group defines - // since we don't know if they will have a value or not - foreach (var prop in Properties) - context.ClearPropertyValue (prop.Name); - return; - } - if (!ConditionParser.ParseAndEvaluate (cond, context)) - return; - } - - foreach (var prop in propertyList) - prop.Evaluate (context); - } - - public override string ToString() - { - string s = "[MSBuildPropertyGroup:"; - foreach (MSBuildProperty prop in Properties) - s += " " + prop.Name + "=" + prop.GetValue (true); - return s + "]"; - } - - } - public class MSBuildItem: MSBuildObject - { - public MSBuildItem (XmlElement elem): base (elem) - { - } - - public string Include { - get { return EvaluatedElement.GetAttribute ("Include"); } - set { Element.SetAttribute ("Include", value); } - } - - public string UnevaluatedInclude { - get { return Element.GetAttribute ("Include"); } - set { Element.SetAttribute ("Include", value); } - } - - public string Name { - get { return Element.Name; } - } - - public bool HasMetadata (string name) - { - return EvaluatedElement [name, MSBuildProject.Schema] != null; - } - - public void SetMetadata (string name, bool value) - { - SetMetadata (name, value ? "True" : "False"); - } - - public void SetMetadata (string name, string value, bool isXml = false) - { - // Don't overwrite the metadata value if the new value is the same as the old - // This will keep the old metadata string, which can contain property references - if (GetMetadata (name, isXml) == value) - return; - - XmlElement elem = Element [name, MSBuildProject.Schema]; - if (elem == null) { - elem = AddChildElement (name); - Element.AppendChild (elem); - } - if (isXml) - elem.InnerXml = value; - else - elem.InnerText = value; - } - - public void UnsetMetadata (string name) - { - XmlElement elem = Element [name, MSBuildProject.Schema]; - if (elem != null) { - Element.RemoveChild (elem); - if (!Element.HasChildNodes) - Element.IsEmpty = true; - } - } - - public string GetMetadata (string name, bool isXml = false) - { - XmlElement elem = EvaluatedElement [name, MSBuildProject.Schema]; - if (elem != null) - return isXml ? elem.InnerXml : elem.InnerText; - else - return null; - } - - public bool? GetBoolMetadata (string name) - { - var val = GetMetadata (name); - if (String.Equals (val, "False", StringComparison.OrdinalIgnoreCase)) - return false; - if (String.Equals (val, "True", StringComparison.OrdinalIgnoreCase)) - return true; - return null; - } - - public bool GetMetadataIsFalse (string name) - { - return String.Compare (GetMetadata (name), "False", StringComparison.OrdinalIgnoreCase) == 0; - } - - public void MergeFrom (MSBuildItem other) - { - foreach (XmlNode node in Element.ChildNodes) { - if (node is XmlElement) - SetMetadata (node.LocalName, node.InnerXml, true); - } - } - - internal override void Evaluate (MSBuildEvaluationContext context) - { - XmlElement elem; - if (context.Evaluate (Element, out elem)) - EvaluatedElement = elem; - else - EvaluatedElement = null; - } - } - public class MSBuildItemGroup: MSBuildObject - { - MSBuildProject parent; - - internal MSBuildItemGroup (MSBuildProject parent, XmlElement elem): base (elem) - { - this.parent = parent; - } - - public MSBuildItem AddNewItem (string name, string include) - { - XmlElement elem = AddChildElement (name); - MSBuildItem it = parent.GetItem (elem); - it.Include = include; - return it; - } - - public IEnumerable<MSBuildItem> Items { - get { - foreach (XmlNode node in Element.ChildNodes) { - XmlElement elem = node as XmlElement; - if (elem != null) - yield return parent.GetItem (elem); - } - } - } - - internal override void Evaluate (MSBuildEvaluationContext context) - { - foreach (var item in Items) - item.Evaluate (context); - } - } - public class MSBuildEvaluationContext: IExpressionContext - { - Dictionary<string,string> properties = new Dictionary<string, string> (); - bool allResolved; - MSBuildProject project; - - public MSBuildEvaluationContext () - { - } - - internal void InitEvaluation (MSBuildProject project) - { - this.project = project; - SetPropertyValue ("MSBuildThisFile", Path.GetFileName (project.FileName)); - SetPropertyValue ("MSBuildThisFileName", Path.GetFileNameWithoutExtension (project.FileName)); - SetPropertyValue ("MSBuildThisFileDirectory", Path.GetDirectoryName (project.FileName) + Path.DirectorySeparatorChar); - SetPropertyValue ("MSBuildThisFileExtension", Path.GetExtension (project.FileName)); - SetPropertyValue ("MSBuildThisFileFullPath", Path.GetFullPath (project.FileName)); - SetPropertyValue ("VisualStudioReferenceAssemblyVersion", project.ToolsVersion + ".0.0"); - } - - public string GetPropertyValue (string name) - { - string val; - if (properties.TryGetValue (name, out val)) - return val; - else - return Environment.GetEnvironmentVariable (name); - } - - public void SetPropertyValue (string name, string value) - { - properties [name] = value; - } - - public void ClearPropertyValue (string name) - { - properties.Remove (name); - } - - public bool Evaluate (XmlElement source, out XmlElement result) - { - allResolved = true; - result = (XmlElement) EvaluateNode (source); - return allResolved; - } - - XmlNode EvaluateNode (XmlNode source) - { - var elemSource = source as XmlElement; - if (elemSource != null) { - var elem = source.OwnerDocument.CreateElement (elemSource.Prefix, elemSource.LocalName, elemSource.NamespaceURI); - foreach (XmlAttribute attr in elemSource.Attributes) - elem.Attributes.Append ((XmlAttribute)EvaluateNode (attr)); - foreach (XmlNode child in elemSource.ChildNodes) - elem.AppendChild (EvaluateNode (child)); - return elem; - } - - var attSource = source as XmlAttribute; - if (attSource != null) { - bool oldResolved = allResolved; - var att = source.OwnerDocument.CreateAttribute (attSource.Prefix, attSource.LocalName, attSource.NamespaceURI); - att.Value = Evaluate (attSource.Value); - - // Condition attributes don't change the resolution status. Conditions are handled in the property and item objects - if (attSource.Name == "Condition") - allResolved = oldResolved; - - return att; - } - var textSource = source as XmlText; - if (textSource != null) { - return source.OwnerDocument.CreateTextNode (Evaluate (textSource.InnerText)); - } - return source.Clone (); - } - - public bool Evaluate (string str, out string result) - { - allResolved = true; - result = Evaluate (str); - return allResolved; - } - - string Evaluate (string str) - { - int i = str.IndexOf ("$("); - if (i == -1) - return str; - - int last = 0; - - StringBuilder sb = new StringBuilder (); - do { - sb.Append (str, last, i - last); - i += 2; - int j = str.IndexOf (")", i); - if (j == -1) { - allResolved = false; - return ""; - } - - string prop = str.Substring (i, j - i); - string val = GetPropertyValue (prop); - if (val == null) { - allResolved = false; - return ""; - } - - sb.Append (val); - last = j + 1; - i = str.IndexOf ("$(", last); - } - while (i != -1); - - sb.Append (str, last, str.Length - last); - return sb.ToString (); - } - - #region IExpressionContext implementation - - public string EvaluateString (string value) - { - if (value.StartsWith ("$(") && value.EndsWith (")")) - return GetPropertyValue (value.Substring (2, value.Length - 3)) ?? value; - else - return value; - } - - public string FullFileName { - get { - return project.FileName; - } - } - - #endregion - } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProjectFromFile.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProjectFromFile.cs new file mode 100644 index 0000000000..5dc76c4283 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProjectFromFile.cs @@ -0,0 +1,324 @@ +// +// MSBuildProjectFromFile.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 MSProject = Microsoft.Build.BuildEngine.Project; +using Microsoft.Build.BuildEngine; +using System.Collections.Generic; + +namespace MonoDevelop.Projects.Formats.MSBuildInternal +{ + public class MSBuildProjectFromFile: MSBuildProject + { + MSProject project; + List<ImportData> imports = new List<ImportData> (); + List<MSBuildPropertyGroup> propertyGroups = new List<MSBuildPropertyGroup> (); + + class ImportData: MSBuildImport + { + #region implemented abstract members of MSBuildImport + + public override string Target { get; set; } + + public override string Label { get; set; } + + public override string Condition { get; set; } + + #endregion + } + + class ProperyGroupData: MSBuildPropertyGroup, IPropertySetImpl + { + public List<MSBuildProperty> Properties = new List<MSBuildProperty> (); + + public ProperyGroupData (MSBuildProject project) : base (project) + { + } + + internal override IPropertySetImpl PropertySet { + get { + throw new NotImplementedException (); + } + } + + public override string Label { get; set; } + + public override string Condition { get; set; } + } + + class ItemGroupData: MSBuildItemGroup + { + public List<MSBuildItem> GroupItems = new List<MSBuildItem> (); + + public ItemGroupData (MSBuildProject project): base (project) + { + } + + public override MSBuildItem AddNewItem (string name, string include) + { + var it = new ItemData (Project, name, include, include); + GroupItems.Add (it); + return it; + } + + public override IEnumerable<MSBuildItem> Items { + get { + return GroupItems; + } + } + } + + class ItemData: MSBuildItem + { + string name; + string unevaluatedInclude; + string include; + + public ItemData (MSBuildProject project, string name, string include, string unevaluatedInclude): base (project) + { + this.name = name; + this.include = include; + this.unevaluatedInclude = unevaluatedInclude; + } + + internal override IPropertySetImpl PropertySet { + get { + throw new NotImplementedException (); + } + } + + public override string Include { + get { return include; } + } + + public override string UnevaluatedInclude { + get { + return unevaluatedInclude; + } + } + + public override string Name { + get { + return name; + } + } + } + + class PropertyData: MSBuildProperty + { + public string Value; + public string RawValue; + string name; + + public PropertyData (string name, MSBuildProject project) : base (project) + { + this.name = name; + } + + public override string Name { + get { return name; } + } + + protected override void SetPropertyValue (string value, bool isXml) + { + Value = value; + } + + protected override string GetPropertyValue () + { + return Value; + } + + public override string Condition { get; set; } + } + + public MSBuildProjectFromFile () + { + } + + #region IMSBuildProjectImpl implementation + + public override void Load (MonoDevelop.Core.FilePath file) + { + foreach (Import im in project.Imports) { + imports.Add (new ImportData { + Condition = im.Condition + }); + } + foreach (BuildPropertyGroup pg in project.PropertyGroups) { + var g = new ProperyGroupData (this) { + Condition = pg.Condition + }; + foreach (BuildProperty p in pg) { + var prop = new PropertyData (p.Name, this) { + Value = p.FinalValue, + RawValue = p.Value + }; + g.Properties.Add (prop); + } + propertyGroups.Add (g); + } + foreach (BuildItemGroup ig in project.ItemGroups) { + + } + } + + public override void AddNewImport (string name, MSBuildImport beforeImport = null) + { + var data = new ImportData { + Target = name + }; + if (beforeImport != null) { + var other = (ImportData)beforeImport; + int i = imports.IndexOf (other); + if (i != -1) { + imports.Insert (i, data); + return; + } + } + imports.Add (data); + } + + public override void RemoveImport (MSBuildImport import) + { + var data = (ImportData)import; + imports.Remove (data); + } + + public override void Save (string fileName) + { + throw new NotImplementedException (); + } + + public override string SaveToString () + { + throw new NotImplementedException (); + } + + public override MSBuildPropertyGroup AddNewPropertyGroup (MSBuildPropertyGroup beforeGroup = null) + { + var g = new ProperyGroupData (this); + if (beforeGroup != null) { + var i = propertyGroups.IndexOf (beforeGroup); + if (i != -1) { + propertyGroups.Insert (i, g); + return g; + } + } + propertyGroups.Add (g); + return g; + } + + public override void RemovePropertyGroup (MSBuildPropertyGroup grp) + { + propertyGroups.Remove (grp); + } + + public override IEnumerable<MSBuildItem> GetAllItems () + { + throw new NotImplementedException (); + } + + public override IEnumerable<MSBuildItem> GetAllItems (params string[] names) + { + throw new NotImplementedException (); + } + + public override MSBuildItemGroup AddNewItemGroup () + { + throw new NotImplementedException (); + } + + public override MSBuildItem AddNewItem (string name, string include) + { + throw new NotImplementedException (); + } + + public override System.Xml.XmlElement GetProjectExtensions (string section) + { + throw new NotImplementedException (); + } + + public override void SetProjectExtensions (string section, string value) + { + throw new NotImplementedException (); + } + + public override void RemoveProjectExtensions (string section) + { + throw new NotImplementedException (); + } + + public override void RemoveItem (MSBuildItem item) + { + throw new NotImplementedException (); + } + + public override MonoDevelop.Core.FilePath FileName { + get { + throw new NotImplementedException (); + } + } + + public override string DefaultTargets { + get { + throw new NotImplementedException (); + } + set { + throw new NotImplementedException (); + } + } + + public override string ToolsVersion { + get { + throw new NotImplementedException (); + } + set { + throw new NotImplementedException (); + } + } + + public override IEnumerable<MSBuildImport> Imports { + get { + throw new NotImplementedException (); + } + } + + public override IEnumerable<MSBuildPropertyGroup> PropertyGroups { + get { + throw new NotImplementedException (); + } + } + + public override IEnumerable<MSBuildItemGroup> ItemGroups { + get { + throw new NotImplementedException (); + } + } + + #endregion + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProjectHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProjectHandler.cs index 96e68d4cef..7aded71f36 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProjectHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProjectHandler.cs @@ -41,2004 +41,10 @@ using Mono.Addins; using System.Linq; using MonoDevelop.Core.Instrumentation; using MonoDevelop.Core.ProgressMonitoring; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Formats.MSBuild { - public class MSBuildProjectHandler: MSBuildHandler, IResourceHandler, IPathHandler, IAssemblyReferenceHandler - { - List<string> targetImports = new List<string> (); - IResourceHandler customResourceHandler; - List<string> subtypeGuids = new List<string> (); - const string Unspecified = null; - RemoteProjectBuilder projectBuilder; - ITimeTracker timer; - bool modifiedInMemory; - UnknownProjectTypeNode unknownProjectTypeInfo; - - string lastBuildToolsVersion; - string lastBuildRuntime; - string lastFileName; - string lastSlnFileName; - - struct ItemInfo { - public MSBuildItem Item; - public bool Added; - } - - protected SolutionEntityItem EntityItem { - get { return (SolutionEntityItem) Item; } - } - - public string ToolsVersion { get; private set; } - string productVersion; - string schemaVersion; - - internal bool ProjectTypeIsUnsupported { - get { return unknownProjectTypeInfo != null; } - } - - internal UnknownProjectTypeNode UnknownProjectTypeInfo { - get { return unknownProjectTypeInfo; } - } - - public List<string> TargetImports { - get { return targetImports; } - } - - internal void SetUnsupportedType (UnknownProjectTypeNode typeInfo) - { - unknownProjectTypeInfo = typeInfo; - } - - internal override void SetSolutionFormat (MSBuildFileFormat format, bool converting) - { - // when converting formats, set ToolsVersion, ProductVersion, SchemaVersion to default values written by VS - // this happens on creation too - // else we leave them alone and just roundtrip them - if (converting) { - ToolsVersion = format.DefaultToolsVersion; - productVersion = format.DefaultProductVersion; - schemaVersion = format.DefaultSchemaVersion; - } - - base.SetSolutionFormat (format, converting); - } - - //HACK: the solution's format is irrelevant to MSBuild projects, what matters is the ToolsVersion - // but other parts of the MD API expect a FileFormat - MSBuildFileFormat GetToolsFormat () - { - switch (ToolsVersion) { - case "2.0": - return new MSBuildFileFormatVS05 (); - case "3.5": - return new MSBuildFileFormatVS08 (); - case "4.0": - if (SolutionFormat != null && SolutionFormat.Id == "MSBuild10") - return SolutionFormat; - return new MSBuildFileFormatVS12 (); - case "12.0": - return new MSBuildFileFormatVS12 (); - default: - throw new Exception ("Unknown ToolsVersion '" + ToolsVersion + "'"); - } - } - - public void SetCustomResourceHandler (IResourceHandler value) - { - customResourceHandler = value; - } - - public List<string> SubtypeGuids { - get { - return subtypeGuids; - } - } - - public MSBuildProjectHandler () - { - } - - public MSBuildProjectHandler (string typeGuid, string import, string itemId) - { - Initialize (typeGuid, import, itemId); - } - - internal void Initialize (string typeGuid, string import, string itemId) - { - base.Initialize (typeGuid, itemId); - if (import != null && import.Trim().Length > 0) - this.targetImports.AddRange (import.Split (':')); - - Runtime.SystemAssemblyService.DefaultRuntimeChanged += OnDefaultRuntimeChanged; - } - - public override object GetService (Type t) - { - foreach (var ex in GetMSBuildExtensions ()) { - var s = ex.GetService (t); - if (s != null) - return s; - } - return null; - } - - void OnDefaultRuntimeChanged (object o, EventArgs args) - { - // If the default runtime changes, the project builder for this project may change - // so it has to be created again. - if (projectBuilder != null) { - projectBuilder.Dispose (); - projectBuilder = null; - } - } - - RemoteProjectBuilder GetProjectBuilder () - { - var item = (SolutionEntityItem) Item; - - //FIXME: we can't really have per-project runtimes, has to be per-solution - TargetRuntime runtime = null; - var ap = item as IAssemblyProject; - runtime = ap != null ? ap.TargetRuntime : Runtime.SystemAssemblyService.CurrentRuntime; - - var sln = item.ParentSolution; - var slnFile = sln != null ? sln.FileName : null; - - if (projectBuilder == null || lastBuildToolsVersion != ToolsVersion || lastBuildRuntime != runtime.Id || lastFileName != item.FileName || lastSlnFileName != slnFile) { - if (projectBuilder != null) { - projectBuilder.Dispose (); - projectBuilder = null; - } - projectBuilder = MSBuildProjectService.GetProjectBuilder (runtime, ToolsVersion, item.FileName, slnFile); - lastBuildToolsVersion = ToolsVersion; - lastBuildRuntime = runtime.Id; - lastFileName = item.FileName; - lastSlnFileName = slnFile; - } - else if (modifiedInMemory) { - modifiedInMemory = false; - var p = SaveProject (new NullProgressMonitor ()); - projectBuilder.RefreshWithContent (p.SaveToString ()); - } - return projectBuilder; - } - - void CleanupProjectBuilder () - { - if (projectBuilder != null) { - projectBuilder.Dispose (); - projectBuilder = null; - } - } - - public override void Dispose () - { - base.Dispose (); - CleanupProjectBuilder (); - Runtime.SystemAssemblyService.DefaultRuntimeChanged -= OnDefaultRuntimeChanged; - } - - //for some reason, MD internally handles "AnyCPU" as "", but we need to be explicit when - //passing it to the build engine - static string GetExplicitPlatform (SolutionItemConfiguration configObject) - { - if (string.IsNullOrEmpty (configObject.Platform)) { - return "AnyCPU"; - } - return configObject.Platform; - } - - ProjectConfigurationInfo[] GetConfigurations (SolutionEntityItem item, ConfigurationSelector configuration) - { - // Returns a list of project/configuration information for the provided item and all its references - List<ProjectConfigurationInfo> configs = new List<ProjectConfigurationInfo> (); - var c = item.GetConfiguration (configuration); - configs.Add (new ProjectConfigurationInfo () { - ProjectFile = item.FileName, - Configuration = c.Name, - Platform = GetExplicitPlatform (c) - }); - foreach (var refProject in item.GetReferencedItems (configuration).OfType<Project> ()) { - var refConfig = refProject.GetConfiguration (configuration); - if (refConfig != null) { - configs.Add (new ProjectConfigurationInfo () { - ProjectFile = refProject.FileName, - Configuration = refConfig.Name, - Platform = GetExplicitPlatform (refConfig) - }); - } - } - return configs.ToArray (); - } - - IEnumerable<string> IAssemblyReferenceHandler.GetAssemblyReferences (ConfigurationSelector configuration) - { - if (UseMSBuildEngineForItem (Item, configuration)) { - // Get the references list from the msbuild project - SolutionEntityItem item = (SolutionEntityItem) Item; - RemoteProjectBuilder builder = GetProjectBuilder (); - var configs = GetConfigurations (item, configuration); - - var result = builder.Run ( - configs, null, MSBuildVerbosity.Normal, - new[] { "ResolveAssemblyReferences" }, new [] { "ReferencePath" }, null - ); - - List<MSBuildEvaluatedItem> items; - if (result.Items.TryGetValue ("ReferencePath", out items) && items != null) { - foreach (var i in items) - yield return i.ItemSpec; - } - } - else { - CleanupProjectBuilder (); - DotNetProject item = Item as DotNetProject; - if (item == null) - yield break; - foreach (ProjectReference pref in item.References.Where (pr => pr.ReferenceType != ReferenceType.Project)) { - foreach (string asm in pref.GetReferencedFileNames (configuration)) - yield return asm; - } - } - } - - public override BuildResult RunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) - { - if (UseMSBuildEngineForItem (Item, configuration)) { - SolutionEntityItem item = Item as SolutionEntityItem; - if (item != null) { - LogWriter logWriter = new LogWriter (monitor.Log); - RemoteProjectBuilder builder = GetProjectBuilder (); - var configs = GetConfigurations (item, configuration); - var result = builder.Run (configs, logWriter, MSBuildProjectService.DefaultMSBuildVerbosity, new[] { target }, null, null); - System.Runtime.Remoting.RemotingServices.Disconnect (logWriter); - - var br = new BuildResult (); - foreach (var err in result.Errors) { - FilePath file = null; - if (err.File != null) - file = Path.Combine (Path.GetDirectoryName (err.ProjectFile), err.File); - - if (err.IsWarning) - br.AddWarning (file, err.LineNumber, err.ColumnNumber, err.Code, err.Message); - else - br.AddError (file, err.LineNumber, err.ColumnNumber, err.Code, err.Message); - } - return br; - } - } - else { - CleanupProjectBuilder (); - if (Item is DotNetProject) { - MD1DotNetProjectHandler handler = new MD1DotNetProjectHandler ((DotNetProject)Item); - return handler.RunTarget (monitor, target, configuration); - } - } - return null; - } - - public string GetDefaultResourceId (ProjectFile file) - { - if (customResourceHandler != null) - return customResourceHandler.GetDefaultResourceId (file); - else - return MSBuildResourceHandler.Instance.GetDefaultResourceId (file); - } - - public string EncodePath (string path, string oldPath) - { - string basePath = Path.GetDirectoryName (EntityItem.FileName); - return FileService.RelativeToAbsolutePath (basePath, path); - } - - public string DecodePath (string path) - { - string basePath = Path.GetDirectoryName (EntityItem.FileName); - return FileService.AbsoluteToRelativePath (basePath, path); - } - - public SolutionEntityItem Load (IProgressMonitor monitor, string fileName, MSBuildFileFormat format, string language, Type itemClass) - { - timer = Counters.ReadMSBuildProject.BeginTiming (); - - timer.Trace ("Reading project file"); - MSBuildProject p = new MSBuildProject (); - p.Load (fileName); - - ToolsVersion = p.ToolsVersion; - if (string.IsNullOrEmpty (ToolsVersion)) - ToolsVersion = "2.0"; - - SetSolutionFormat (format ?? new MSBuildFileFormatVS12 (), false); - - timer.Trace ("Read project guids"); - - MSBuildPropertySet globalGroup = p.GetGlobalPropertyGroup (); - - // Avoid crash if there is not global group - if (globalGroup == null) - globalGroup = p.AddNewPropertyGroup (false); - - productVersion = globalGroup.GetPropertyValue ("ProductVersion"); - schemaVersion = globalGroup.GetPropertyValue ("SchemaVersion"); - - string itemGuid = globalGroup.GetPropertyValue ("ProjectGuid"); - if (itemGuid == null) - throw new UserException ("Project file doesn't have a valid ProjectGuid"); - - // Workaround for a VS issue. VS doesn't include the curly braces in the ProjectGuid - // of shared projects. - if (!itemGuid.StartsWith ("{")) - itemGuid = "{" + itemGuid + "}"; - - itemGuid = itemGuid.ToUpper (); - string projectTypeGuids = globalGroup.GetPropertyValue ("ProjectTypeGuids"); - string itemType = globalGroup.GetPropertyValue ("ItemType"); - - subtypeGuids.Clear (); - if (projectTypeGuids != null) { - foreach (string guid in projectTypeGuids.Split (';')) { - string sguid = guid.Trim (); - if (sguid.Length > 0 && string.Compare (sguid, TypeGuid, true) != 0) - subtypeGuids.Add (guid); - } - } - - try { - timer.Trace ("Create item instance"); - ProjectExtensionUtil.BeginLoadOperation (); - Item = CreateSolutionItem (monitor, p, fileName, language, itemType, itemClass); - - if (subtypeGuids.Any ()) { - string gg = string.Join (";", subtypeGuids) + ";" + TypeGuid; - Item.ExtendedProperties ["ProjectTypeGuids"] = gg.ToUpper (); - } - - Item.SetItemHandler (this); - MSBuildProjectService.SetId (Item, itemGuid); - - SolutionEntityItem it = (SolutionEntityItem) Item; - - it.FileName = fileName; - it.Name = System.IO.Path.GetFileNameWithoutExtension (fileName); - - RemoveDuplicateItems (p, fileName); - - LoadProject (monitor, p); - return it; - - } finally { - ProjectExtensionUtil.EndLoadOperation (); - timer.End (); - } - } - - /// <summary>Whether to use the MSBuild engine for the specified item.</summary> - internal bool UseMSBuildEngineForItem (SolutionItem item, ConfigurationSelector sel, bool checkReferences = true) - { - // if the item mandates MSBuild, always use it - if (RequireMSBuildEngine) - return true; - // if the user has set the option, use the setting - if (item.UseMSBuildEngine.HasValue) - return item.UseMSBuildEngine.Value; - - // If the item type defaults to using MSBuild, only use MSBuild if its direct references also use MSBuild. - // This prevents a not-uncommon common error referencing non-MSBuild projects from MSBuild projects - // NOTE: This adds about 11ms to the load/build/etc times of the MonoDevelop solution. Doing it recursively - // adds well over a second. - return UseMSBuildEngineByDefault && ( - !checkReferences || - item.GetReferencedItems (sel).All (i => { - var h = i.ItemHandler as MSBuildProjectHandler; - return h != null && h.UseMSBuildEngineForItem (i, sel, false); - }) - ); - } - - /// <summary>Whether to use the MSBuild engine by default.</summary> - internal bool UseMSBuildEngineByDefault { get; set; } - - /// <summary>Forces the MSBuild engine to be used.</summary> - internal bool RequireMSBuildEngine { get; set; } - - // All of the last 4 parameters are optional, but at least one must be provided. - SolutionItem CreateSolutionItem (IProgressMonitor monitor, MSBuildProject p, string fileName, string language, - string itemType, Type itemClass) - { - if (ProjectTypeIsUnsupported) - return new UnknownProject (fileName, UnknownProjectTypeInfo.GetInstructions ()); - - if (subtypeGuids.Any ()) { - DotNetProjectSubtypeNode st = MSBuildProjectService.GetDotNetProjectSubtype (subtypeGuids); - if (st != null) { - UseMSBuildEngineByDefault = st.UseXBuild; - RequireMSBuildEngine = st.RequireXBuild; - Type migratedType = null; - - if (st.IsMigration && (migratedType = MigrateProject (monitor, st, p, fileName, language)) != null) { - var oldSt = st; - st = MSBuildProjectService.GetItemSubtypeNodes ().Last (t => t.CanHandleType (migratedType)); - - for (int i = 0; i < subtypeGuids.Count; i++) { - if (string.Equals (subtypeGuids [i], oldSt.Guid, StringComparison.OrdinalIgnoreCase)) { - subtypeGuids [i] = st.Guid; - oldSt = null; - break; - } - } - - if (oldSt != null) - throw new Exception ("Unable to correct flavor GUID"); - - var gg = string.Join (";", subtypeGuids) + ";" + TypeGuid; - p.GetGlobalPropertyGroup ().SetPropertyValue ("ProjectTypeGuids", gg.ToUpper (), true); - p.Save (fileName); - } - - var item = st.CreateInstance (language); - st.UpdateImports ((SolutionEntityItem)item, targetImports); - return item; - } else { - var projectInfo = MSBuildProjectService.GetUnknownProjectTypeInfo (subtypeGuids.ToArray (), fileName); - if (projectInfo != null && projectInfo.LoadFiles) { - SetUnsupportedType (projectInfo); - return new UnknownProject (fileName, UnknownProjectTypeInfo.GetInstructions ()); - } - throw new UnknownSolutionItemTypeException (ProjectTypeIsUnsupported ? TypeGuid : string.Join (";", subtypeGuids)); - } - } - - if (itemClass != null) - return (SolutionItem) Activator.CreateInstance (itemClass); - - if (!string.IsNullOrEmpty (language)) { - //enable msbuild by default .NET assembly projects - UseMSBuildEngineByDefault = true; - RequireMSBuildEngine = false; - return new DotNetAssemblyProject (language); - } - - if (string.IsNullOrEmpty (itemType)) - throw new UnknownSolutionItemTypeException (); - - DataType dt = MSBuildProjectService.DataContext.GetConfigurationDataType (itemType); - if (dt == null) - throw new UnknownSolutionItemTypeException (itemType); - - return (SolutionItem) Activator.CreateInstance (dt.ValueType); - } - - Type MigrateProject (IProgressMonitor monitor, DotNetProjectSubtypeNode st, MSBuildProject p, string fileName, string language) - { - var projectLoadMonitor = monitor as IProjectLoadProgressMonitor; - if (projectLoadMonitor == null) { - // projectLoadMonitor will be null when running through md-tool, but - // this is not fatal if migration is not required, so just ignore it. --abock - if (!st.IsMigrationRequired) - return null; - - LoggingService.LogError (Environment.StackTrace); - monitor.ReportError ("Could not open unmigrated project and no migrator was supplied", null); - throw new Exception ("Could not open unmigrated project and no migrator was supplied"); - } - - var migrationType = st.MigrationHandler.CanPromptForMigration - ? st.MigrationHandler.PromptForMigration (projectLoadMonitor, p, fileName, language) - : projectLoadMonitor.ShouldMigrateProject (); - if (migrationType == MigrationType.Ignore) { - if (st.IsMigrationRequired) { - monitor.ReportError (string.Format ("{1} cannot open the project '{0}' unless it is migrated.", Path.GetFileName (fileName), BrandingService.ApplicationName), null); - throw new Exception ("The user choose not to migrate the project"); - } else - return null; - } - - var baseDir = (FilePath) Path.GetDirectoryName (fileName); - if (migrationType == MigrationType.BackupAndMigrate) { - var backupDirFirst = baseDir.Combine ("backup"); - string backupDir = backupDirFirst; - int i = 0; - while (Directory.Exists (backupDir)) { - backupDir = backupDirFirst + "-" + i.ToString (); - if (i++ > 20) { - throw new Exception ("Too many backup directories"); - } - } - Directory.CreateDirectory (backupDir); - foreach (var file in st.MigrationHandler.FilesToBackup (fileName)) - File.Copy (file, Path.Combine (backupDir, Path.GetFileName (file))); - } - - var type = st.MigrationHandler.Migrate (projectLoadMonitor, p, fileName, language); - if (type == null) - throw new Exception ("Could not migrate the project"); - - return type; - } - - FileFormat GetFileFormat (MSBuildFileFormat fmt) - { - return new FileFormat (fmt, fmt.Id, fmt.Name); - } - - void RemoveDuplicateItems (MSBuildProject msproject, string fileName) - { - timer.Trace ("Checking for duplicate items"); - - var uniqueIncludes = new Dictionary<string,object> (); - var toRemove = new List<MSBuildItem> (); - foreach (MSBuildItem bi in msproject.GetAllItems ()) { - object existing; - string key = bi.Name + "<" + bi.Include; - if (!uniqueIncludes.TryGetValue (key, out existing)) { - uniqueIncludes[key] = bi; - continue; - } - var exBi = existing as MSBuildItem; - if (exBi != null) { - if (exBi.Condition != bi.Condition || exBi.Element.InnerXml != bi.Element.InnerXml) { - uniqueIncludes[key] = new List<MSBuildItem> { exBi, bi }; - } else { - toRemove.Add (bi); - } - continue; - } - - var exList = (List<MSBuildItem>)existing; - bool found = false; - foreach (var m in (exList)) { - if (m.Condition == bi.Condition && m.Element.InnerXml == bi.Element.InnerXml) { - found = true; - break; - } - } - if (!found) { - exList.Add (bi); - } else { - toRemove.Add (bi); - } - } - if (toRemove.Count == 0) - return; - - timer.Trace ("Removing duplicate items"); - - foreach (var t in toRemove) - msproject.RemoveItem (t); - - msproject.Save (fileName); - } - - void LoadConfiguration (MSBuildSerializer serializer, List<ConfigData> configData, string conf, string platform) - { - MSBuildPropertySet grp = GetMergedConfiguration (configData, conf, platform, null); - SolutionItemConfiguration config = EntityItem.CreateConfiguration (conf); - - config.Platform = platform; - DataItem data = ReadPropertyGroupMetadata (serializer, grp, config); - serializer.Deserialize (config, data); - EntityItem.Configurations.Add (config); - - if (config is DotNetProjectConfiguration) { - DotNetProjectConfiguration dpc = (DotNetProjectConfiguration) config; - if (dpc.CompilationParameters != null) { - data = ReadPropertyGroupMetadata (serializer, grp, dpc.CompilationParameters); - serializer.Deserialize (dpc.CompilationParameters, data); - } - } - } - - protected virtual void LoadProject (IProgressMonitor monitor, MSBuildProject msproject) - { - timer.Trace ("Initialize serialization"); - - MSBuildSerializer ser = CreateSerializer (); - ser.SerializationContext.BaseFile = EntityItem.FileName; - ser.SerializationContext.ProgressMonitor = monitor; - - MSBuildPropertySet globalGroup = msproject.GetGlobalPropertyGroup (); - - Item.SetItemHandler (this); - - DotNetProject dotNetProject = Item as DotNetProject; - - // Read all items - - timer.Trace ("Read project items"); - - LoadProjectItems (msproject, ser, ProjectItemFlags.None); - - timer.Trace ("Read configurations"); - - TargetFrameworkMoniker targetFx = null; - - if (dotNetProject != null) { - string frameworkIdentifier = globalGroup.GetPropertyValue ("TargetFrameworkIdentifier"); - string frameworkVersion = globalGroup.GetPropertyValue ("TargetFrameworkVersion"); - string frameworkProfile = globalGroup.GetPropertyValue ("TargetFrameworkProfile"); - - //determine the default target framework from the project type's default - //overridden by the components in the project - var def = dotNetProject.GetDefaultTargetFrameworkForFormat (GetFileFormat (GetToolsFormat ())); - targetFx = new TargetFrameworkMoniker ( - string.IsNullOrEmpty (frameworkIdentifier)? def.Identifier : frameworkIdentifier, - string.IsNullOrEmpty (frameworkVersion)? def.Version : frameworkVersion, - string.IsNullOrEmpty (frameworkProfile)? def.Profile : frameworkProfile); - - if (dotNetProject.LanguageParameters != null) { - DataItem data = ReadPropertyGroupMetadata (ser, globalGroup, dotNetProject.LanguageParameters); - ser.Deserialize (dotNetProject.LanguageParameters, data); - } - } - - // Read configurations - - List<ConfigData> configData = GetConfigData (msproject, false); - List<ConfigData> partialConfigurations = new List<ConfigData> (); - HashSet<string> handledConfigurations = new HashSet<string> (); - var configurations = new HashSet<string> (); - var platforms = new HashSet<string> (); - - MSBuildPropertyGroup mergedToProjectProperties = ExtractMergedtoprojectProperties (ser, globalGroup, EntityItem.CreateConfiguration ("Dummy")); - configData.Insert (0, new ConfigData (Unspecified, Unspecified, mergedToProjectProperties)); - - // Load configurations, skipping the dummy config at index 0. - for (int i = 1; i < configData.Count; i++) { - ConfigData cgrp = configData[i]; - string platform = cgrp.Platform; - string conf = cgrp.Config; - - if (platform != Unspecified) - platforms.Add (platform); - - if (conf != Unspecified) - configurations.Add (conf); - - if (conf == Unspecified || platform == Unspecified) { - // skip partial configurations for now... - partialConfigurations.Add (cgrp); - continue; - } - - string key = conf + "|" + platform; - if (handledConfigurations.Contains (key)) - continue; - - LoadConfiguration (ser, configData, conf, platform); - - handledConfigurations.Add (key); - } - - // Now we can load any partial configurations by combining them with known configs or platforms. - if (partialConfigurations.Count > 0) { - if (platforms.Count == 0) - platforms.Add (string.Empty); // AnyCpu - - foreach (ConfigData cgrp in partialConfigurations) { - if (cgrp.Config != Unspecified && cgrp.Platform == Unspecified) { - string conf = cgrp.Config; - - foreach (var platform in platforms) { - string key = conf + "|" + platform; - - if (handledConfigurations.Contains (key)) - continue; - - LoadConfiguration (ser, configData, conf, platform); - - handledConfigurations.Add (key); - } - } else if (cgrp.Config == Unspecified && cgrp.Platform != Unspecified) { - string platform = cgrp.Platform; - - foreach (var conf in configurations) { - string key = conf + "|" + platform; - - if (handledConfigurations.Contains (key)) - continue; - - LoadConfiguration (ser, configData, conf, platform); - - handledConfigurations.Add (key); - } - } - } - } - - // Read extended properties - - timer.Trace ("Read extended properties"); - - DataItem globalData = ReadPropertyGroupMetadata (ser, globalGroup, Item); - - string extendedData = msproject.GetProjectExtensions ("MonoDevelop"); - if (!string.IsNullOrEmpty (extendedData)) { - StringReader sr = new StringReader (extendedData); - DataItem data = (DataItem) XmlConfigurationReader.DefaultReader.Read (new XmlTextReader (sr)); - globalData.ItemData.AddRange (data.ItemData); - } - ser.Deserialize (Item, globalData); - - // Final initializations - - timer.Trace ("Final initializations"); - - //clean up the "InternalTargetFrameworkVersion" hack from MD 2.2, 2.4 - if (dotNetProject != null) { - string fx = Item.ExtendedProperties ["InternalTargetFrameworkVersion"] as string; - if (!string.IsNullOrEmpty (fx)) { - targetFx = TargetFrameworkMoniker.Parse (fx); - Item.ExtendedProperties.Remove ("InternalTargetFrameworkVersion"); - } - - dotNetProject.TargetFramework = Runtime.SystemAssemblyService.GetTargetFramework (targetFx); - } - - LoadFromMSBuildProject (monitor, msproject); - - Item.NeedsReload = false; - } - - internal void LoadProjectItems (MSBuildProject msproject, MSBuildSerializer ser, ProjectItemFlags flags) - { - foreach (MSBuildItem buildItem in msproject.GetAllItems ()) { - ProjectItem it = ReadItem (ser, buildItem); - if (it == null) - continue; - it.Flags = flags; - if (it is ProjectFile) { - var file = (ProjectFile)it; - if (file.Name.IndexOf ('*') > -1) { - // Thanks to IsOriginatedFromWildcard, these expanded items will not be saved back to disk. - foreach (var expandedItem in ResolveWildcardItems (file)) - EntityItem.Items.Add (expandedItem); - // Add to wildcard items (so it can be re-saved) instead of Items (where tools will - // try to compile and display these nonstandard items - EntityItem.WildcardItems.Add (it); - continue; - } - if (ProjectTypeIsUnsupported && !File.Exists (file.FilePath)) - continue; - } - EntityItem.Items.Add (it); - it.ExtendedProperties ["MSBuild.SourceProject"] = msproject.FileName; - } - } - - protected virtual void LoadFromMSBuildProject (IProgressMonitor monitor, MSBuildProject msproject) - { - foreach (var ext in GetMSBuildExtensions ()) - ext.LoadProject (monitor, EntityItem, msproject); - } - - const string RecursiveDirectoryWildcard = "**"; - static readonly char[] directorySeparators = new [] { - Path.DirectorySeparatorChar, - Path.AltDirectorySeparatorChar - }; - - static string GetWildcardDirectoryName (string path) - { - int indexOfLast = path.LastIndexOfAny (directorySeparators); - if (indexOfLast < 0) - return String.Empty; - return path.Substring (0, indexOfLast); - } - - static string GetWildcardFileName (string path) - { - int indexOfLast = path.LastIndexOfAny (directorySeparators); - if (indexOfLast < 0) - return path; - if (indexOfLast == path.Length) - return String.Empty; - return path.Substring (indexOfLast + 1, path.Length - (indexOfLast + 1)); - } - - static IEnumerable<string> ExpandWildcardFilePath (string filePath) - { - if (String.IsNullOrWhiteSpace (filePath)) - throw new ArgumentException ("Not a wildcard path"); - - string dir = GetWildcardDirectoryName (filePath); - string file = GetWildcardFileName (filePath); - - if (String.IsNullOrEmpty (dir) || String.IsNullOrEmpty (file)) - return null; - - SearchOption searchOption = SearchOption.TopDirectoryOnly; - if (dir.EndsWith (RecursiveDirectoryWildcard, StringComparison.Ordinal)) { - dir = dir.Substring (0, dir.Length - RecursiveDirectoryWildcard.Length); - searchOption = SearchOption.AllDirectories; - } - - if (!Directory.Exists (dir)) - return null; - - return Directory.GetFiles (dir, file, searchOption); - } - - static IEnumerable<ProjectFile> ResolveWildcardItems (ProjectFile wildcardFile) - { - var paths = ExpandWildcardFilePath (wildcardFile.Name); - if (paths == null) - yield break; - foreach (var resolvedFilePath in paths) { - var projectFile = (ProjectFile)wildcardFile.Clone (); - projectFile.Name = resolvedFilePath; - projectFile.IsOriginatedFromWildcard = true; - yield return projectFile; - } - } - - MSBuildPropertyGroup ExtractMergedtoprojectProperties (MSBuildSerializer ser, MSBuildPropertySet pgroup, SolutionItemConfiguration ob) - { - XmlDocument doc = new XmlDocument (); - MSBuildPropertyGroup res = new MSBuildPropertyGroup (null, doc.CreateElement ("PropGroup")); - - // When reading a project, all configuration properties specified in the global property group have to - // be merged with all project configurations, no matter if they have the MergeToProject attribute or not - - ClassDataType dt = (ClassDataType) ser.DataContext.GetConfigurationDataType (ob.GetType ()); - foreach (ItemProperty prop in dt.GetProperties (ser.SerializationContext, ob)) { - MSBuildProperty bp = pgroup.GetProperty (prop.Name); - if (bp != null) { - var preserveCase = prop.DataType is MSBuildBoolDataType; - res.SetPropertyValue (bp.Name, bp.Element.InnerXml, preserveCase, true); - } - } - if (ob is DotNetProjectConfiguration) { - object cparams = ((DotNetProjectConfiguration)ob).CompilationParameters; - dt = (ClassDataType) ser.DataContext.GetConfigurationDataType (cparams.GetType ()); - foreach (ItemProperty prop in dt.GetProperties (ser.SerializationContext, cparams)) { - MSBuildProperty bp = pgroup.GetProperty (prop.Name); - if (bp != null) { - var preserveCase = prop.DataType is MSBuildBoolDataType; - res.SetPropertyValue (bp.Name, bp.Element.InnerXml, preserveCase, true); - } - } - } - return res; - } - - IEnumerable<MergedProperty> GetMergeToProjectProperties (MSBuildSerializer ser, object ob) - { - ClassDataType dt = (ClassDataType) ser.DataContext.GetConfigurationDataType (ob.GetType ()); - foreach (ItemProperty prop in dt.GetProperties (ser.SerializationContext, ob)) { - if (IsMergeToProjectProperty (prop)) { - yield return new MergedProperty (prop.Name, prop.DataType is MSBuildBoolDataType); - } - } - } - - struct MergedProperty - { - public readonly string Name; - public readonly bool PreserveExistingCase; - - public MergedProperty (string name, bool preserveExistingCase) - { - this.Name = name; - this.PreserveExistingCase = preserveExistingCase; - } - } - - internal ProjectItem ReadItem (MSBuildSerializer ser, MSBuildItem buildItem) - { - Project project = Item as Project; - DotNetProject dotNetProject = Item as DotNetProject; - - DataType dt = ser.DataContext.GetConfigurationDataType (buildItem.Name); - - if (project != null) { - if (buildItem.Name == "Folder") { - // Read folders - string path = MSBuildProjectService.FromMSBuildPath (project.ItemDirectory, buildItem.Include); - return new ProjectFile () { Name = Path.GetDirectoryName (path), Subtype = Subtype.Directory }; - } - else if (buildItem.Name == "Reference" && dotNetProject != null) { - ProjectReference pref; - if (buildItem.HasMetadata ("HintPath")) { - string hintPath = buildItem.GetMetadata ("HintPath"); - string path; - if (!MSBuildProjectService.FromMSBuildPath (dotNetProject.ItemDirectory, hintPath, out path)) { - pref = new ProjectReference (ReferenceType.Assembly, path); - pref.SetInvalid (GettextCatalog.GetString ("Invalid file path")); - pref.ExtendedProperties ["_OriginalMSBuildReferenceInclude"] = buildItem.Include; - pref.ExtendedProperties ["_OriginalMSBuildReferenceHintPath"] = hintPath; - } else { - var type = File.Exists (path) ? ReferenceType.Assembly : ReferenceType.Package; - pref = new ProjectReference (type, buildItem.Include, path); - pref.ExtendedProperties ["_OriginalMSBuildReferenceHintPath"] = hintPath; - if (MSBuildProjectService.IsAbsoluteMSBuildPath (hintPath)) - pref.ExtendedProperties ["_OriginalMSBuildReferenceIsAbsolute"] = true; - } - } else { - string asm = buildItem.Include; - // This is a workaround for a VS bug. Looks like it is writing this assembly incorrectly - if (asm == "System.configuration") - asm = "System.Configuration"; - else if (asm == "System.XML") - asm = "System.Xml"; - else if (asm == "system") - asm = "System"; - pref = new ProjectReference (ReferenceType.Package, asm); - } - var privateCopy = buildItem.GetBoolMetadata ("Private"); - if (privateCopy != null) - pref.LocalCopy = privateCopy.Value; - - pref.Condition = buildItem.Condition; - string specificVersion = buildItem.GetMetadata ("SpecificVersion"); - if (string.IsNullOrWhiteSpace (specificVersion)) { - // If the SpecificVersion element isn't present, check if the Assembly Reference specifies a Version - pref.SpecificVersion = ReferenceStringHasVersion (buildItem.Include); - } - else { - bool value; - // if we can't parse the value, default to false which is more permissive - pref.SpecificVersion = bool.TryParse (specificVersion, out value) && value; - } - ReadBuildItemMetadata (ser, buildItem, pref, typeof(ProjectReference)); - return pref; - } - else if (buildItem.Name == "ProjectReference" && dotNetProject != null) { - // Get the project name from the path, since the Name attribute may other stuff other than the name - string path = MSBuildProjectService.FromMSBuildPath (project.ItemDirectory, buildItem.Include); - string name = Path.GetFileNameWithoutExtension (path); - ProjectReference pref = new ProjectReference (ReferenceType.Project, name); - pref.Condition = buildItem.Condition; - var privateCopy = buildItem.GetBoolMetadata ("Private"); - if (privateCopy != null) - pref.LocalCopy = privateCopy.Value; - return pref; - } - else if (dt == null && !string.IsNullOrEmpty (buildItem.Include)) { - // Unknown item. Must be a file. - if (!UnsupportedItems.Contains (buildItem.Name) && IsValidFile (buildItem.Include)) - return ReadProjectFile (ser, project, buildItem, typeof(ProjectFile)); - } - } - - // ProjectReference objects only make sense on a DotNetProject, so don't load them - // if that's not the type of the project. - if (dt != null && dt.ValueType == typeof(ProjectReference) && dotNetProject == null) - dt = null; - - if (dt != null && typeof(ProjectItem).IsAssignableFrom (dt.ValueType)) { - ProjectItem obj = (ProjectItem) Activator.CreateInstance (dt.ValueType); - ReadBuildItemMetadata (ser, buildItem, obj, dt.ValueType); - return obj; - } - - UnknownProjectItem uitem = new UnknownProjectItem (buildItem.Name, ""); - ReadBuildItemMetadata (ser, buildItem, uitem, typeof(UnknownProjectItem)); - - return uitem; - } - - bool ReferenceStringHasVersion (string asmName) - { - int commaPos = asmName.IndexOf (','); - return commaPos >= 0 && asmName.IndexOf ("Version", commaPos) >= 0; - } - - bool IsValidFile (string path) - { - // If it is an absolute uri, it's not a valid file - try { - if (Uri.IsWellFormedUriString (path, UriKind.Absolute)) { - var f = new Uri (path); - return f.Scheme == "file"; - } - } catch { - // Old mono versions may crash in IsWellFormedUriString if the path - // is not an uri. - } - return true; - } - - class ConfigData - { - public ConfigData (string conf, string plt, MSBuildPropertyGroup grp) - { - Config = conf; - Platform = plt; - Group = grp; - } - - public bool FullySpecified { - get { return Config != Unspecified && Platform != Unspecified; } - } - - public string Config; - public string Platform; - public MSBuildPropertyGroup Group; - public bool Exists; - public bool IsNew; // The group did not exist in the original file - } - - MSBuildPropertySet GetMergedConfiguration (List<ConfigData> configData, string conf, string platform, MSBuildPropertyGroup propGroupLimit) - { - MSBuildPropertySet merged = null; - - foreach (ConfigData grp in configData) { - if (grp.Group == propGroupLimit) - break; - if ((grp.Config == conf || grp.Config == Unspecified || conf == Unspecified) && (grp.Platform == platform || grp.Platform == Unspecified || platform == Unspecified)) { - if (merged == null) - merged = grp.Group; - else if (merged is MSBuildPropertyGroupMerged) - ((MSBuildPropertyGroupMerged)merged).Add (grp.Group); - else { - MSBuildPropertyGroupMerged m = new MSBuildPropertyGroupMerged (); - m.Add ((MSBuildPropertyGroup)merged); - m.Add (grp.Group); - merged = m; - } - } - } - return merged; - } - - bool ContainsSpecificPlatformConfiguration (List<ConfigData> configData, string conf) - { - foreach (ConfigData grp in configData) { - if (grp.Config == conf && grp.Platform != Unspecified) - return true; - } - return false; - } - - public override void OnModified (string hint) - { - base.OnModified (hint); - modifiedInMemory = true; - } - - protected override void SaveItem (MonoDevelop.Core.IProgressMonitor monitor) - { - modifiedInMemory = false; - - MSBuildProject msproject = SaveProject (monitor); - if (msproject == null) - return; - - // Don't save the file to disk if the content did not change - msproject.Save (EntityItem.FileName); - - if (projectBuilder != null) - projectBuilder.Refresh (); - } - - protected virtual MSBuildProject SaveProject (IProgressMonitor monitor) - { - if (Item is UnknownSolutionItem) - return null; - - var toolsFormat = GetToolsFormat (); - - bool newProject; - SolutionEntityItem eitem = EntityItem; - - MSBuildSerializer ser = CreateSerializer (); - ser.SerializationContext.BaseFile = eitem.FileName; - ser.SerializationContext.ProgressMonitor = monitor; - - DotNetProject dotNetProject = Item as DotNetProject; - - MSBuildProject msproject = new MSBuildProject (); - newProject = EntityItem.FileName == null || !File.Exists (EntityItem.FileName); - if (newProject) { - msproject.DefaultTargets = "Build"; - } else { - msproject.Load (EntityItem.FileName); - } - - // Global properties - - MSBuildPropertySet globalGroup = msproject.GetGlobalPropertyGroup (); - if (globalGroup == null) { - globalGroup = msproject.AddNewPropertyGroup (false); - } - - if (eitem.Configurations.Count > 0) { - ItemConfiguration conf = eitem.Configurations.FirstOrDefault<ItemConfiguration> (c => c.Name == "Debug"); - if (conf == null) conf = eitem.Configurations [0]; - MSBuildProperty bprop = globalGroup.SetPropertyValue ("Configuration", conf.Name, false); - bprop.Condition = " '$(Configuration)' == '' "; - - string platform = conf.Platform.Length == 0 ? "AnyCPU" : conf.Platform; - bprop = globalGroup.SetPropertyValue ("Platform", platform, false); - bprop.Condition = " '$(Platform)' == '' "; - } - - if (TypeGuid == MSBuildProjectService.GenericItemGuid) { - DataType dt = MSBuildProjectService.DataContext.GetConfigurationDataType (Item.GetType ()); - globalGroup.SetPropertyValue ("ItemType", dt.Name, false); - } - - Item.ExtendedProperties ["ProjectGuid"] = Item.ItemId; - if (subtypeGuids.Count > 0) { - string gg = ""; - foreach (string sg in subtypeGuids) { - if (gg.Length > 0) - gg += ";"; - gg += sg; - } - gg += ";" + TypeGuid; - Item.ExtendedProperties ["ProjectTypeGuids"] = gg.ToUpper (); - globalGroup.SetPropertyValue ("ProjectTypeGuids", gg.ToUpper (), true); - } else { - Item.ExtendedProperties.Remove ("ProjectTypeGuids"); - globalGroup.RemoveProperty ("ProjectTypeGuids"); - } - - Item.ExtendedProperties ["ProductVersion"] = productVersion; - Item.ExtendedProperties ["SchemaVersion"] = schemaVersion; - - // having no ToolsVersion is equivalent to 2.0, roundtrip that correctly - if (ToolsVersion != "2.0") - msproject.ToolsVersion = ToolsVersion; - else if (string.IsNullOrEmpty (msproject.ToolsVersion)) - msproject.ToolsVersion = null; - else - msproject.ToolsVersion = "2.0"; - - // This serialize call will write data to ser.InternalItemProperties and ser.ExternalItemProperties - ser.Serialize (Item, Item.GetType ()); - - object langParams = null; - - if (dotNetProject != null && dotNetProject.LanguageParameters != null) { - // Remove all language parameters properties from the data item, since we are going to write them again. - ClassDataType dt = (ClassDataType) ser.DataContext.GetConfigurationDataType (dotNetProject.LanguageParameters.GetType ()); - foreach (ItemProperty prop in dt.GetProperties (ser.SerializationContext, dotNetProject.LanguageParameters)) { - DataNode n = ser.InternalItemProperties.ItemData [prop.Name]; - if (n != null) - ser.InternalItemProperties.ItemData.Remove (n); - } - DataItem ditemComp = (DataItem) ser.Serialize (dotNetProject.LanguageParameters); - ser.InternalItemProperties.ItemData.AddRange (ditemComp.ItemData); - langParams = dotNetProject.LanguageParameters; - } - - if (newProject) - ser.InternalItemProperties.ItemData.Sort (globalConfigOrder); - - WritePropertyGroupMetadata (globalGroup, ser.InternalItemProperties.ItemData, ser, Item, langParams); - - // Convert debug property - - foreach (SolutionItemConfiguration conf in eitem.Configurations) { - if (newProject && conf is DotNetProjectConfiguration) { - conf.ExtendedProperties ["ErrorReport"] = "prompt"; - } - } - - // Configurations - - if (eitem.Configurations.Count > 0) { - List<ConfigData> configData = GetConfigData (msproject, true); - - var mergeToProjectPropertyValues = new Dictionary<string,MergedPropertyValue> (); - var mergeToProjectProperties = new HashSet<MergedProperty> (GetMergeToProjectProperties (ser, eitem.Configurations [0])); - var mergeToProjectPropertyNames = new HashSet<string> (mergeToProjectProperties.Select (p => p.Name)); - - foreach (SolutionItemConfiguration conf in eitem.Configurations) { - bool newConf = false; - ConfigData cdata = FindPropertyGroup (configData, conf); - if (cdata == null) { - MSBuildPropertyGroup pg = msproject.AddNewPropertyGroup (true); - pg.Condition = BuildConfigCondition (conf.Name, conf.Platform); - cdata = new ConfigData (conf.Name, conf.Platform, pg); - cdata.IsNew = true; - configData.Add (cdata); - newConf = true; - } - - MSBuildPropertyGroup propGroup = cdata.Group; - cdata.Exists = true; - - MSBuildPropertySet baseGroup = GetMergedConfiguration (configData, conf.Name, conf.Platform, propGroup); - - // Force the serialization of properties defined in - // the base group, so that they can be later unmerged - if (baseGroup != null) - ForceDefaultValueSerialization (ser, baseGroup, conf); - DataItem ditem = (DataItem) ser.Serialize (conf); - ser.SerializationContext.ResetDefaultValueSerialization (); - - DotNetProjectConfiguration netConfig = conf as DotNetProjectConfiguration; - - if (netConfig != null && netConfig.CompilationParameters != null) { - // Remove all compilation parameters properties from the data item, since we are going to write them again. - ClassDataType dt = (ClassDataType) ser.DataContext.GetConfigurationDataType (netConfig.CompilationParameters.GetType ()); - foreach (ItemProperty prop in dt.GetProperties (ser.SerializationContext, netConfig.CompilationParameters)) { - DataNode n = ditem.ItemData [prop.Name]; - if (n != null) - ditem.ItemData.Remove (n); - } - if (baseGroup != null) - ForceDefaultValueSerialization (ser, baseGroup, netConfig.CompilationParameters); - DataItem ditemComp = (DataItem) ser.Serialize (netConfig.CompilationParameters); - ser.SerializationContext.ResetDefaultValueSerialization (); - ditem.ItemData.AddRange (ditemComp.ItemData); - } - - if (newConf) - ditem.ItemData.Sort (configOrder); - - WritePropertyGroupMetadata (propGroup, ditem.ItemData, ser, conf, netConfig != null ? netConfig.CompilationParameters : null); - - CollectMergetoprojectProperties (propGroup, mergeToProjectProperties, mergeToProjectPropertyValues); - - if (baseGroup != null) - propGroup.UnMerge (baseGroup, mergeToProjectPropertyNames); - } - - // Move properties with common values from configurations to the main - // property group - foreach (KeyValuePair<string,MergedPropertyValue> prop in mergeToProjectPropertyValues) - globalGroup.SetPropertyValue (prop.Key, prop.Value.XmlValue, prop.Value.PreserveExistingCase, true); - foreach (string prop in mergeToProjectPropertyNames) { - if (!mergeToProjectPropertyValues.ContainsKey (prop)) - globalGroup.RemoveProperty (prop); - } - foreach (SolutionItemConfiguration conf in eitem.Configurations) { - MSBuildPropertyGroup propGroup = FindPropertyGroup (configData, conf).Group; - foreach (string mp in mergeToProjectPropertyValues.Keys) - propGroup.RemoveProperty (mp); - } - - // Remove groups corresponding to configurations that have been removed - // or groups which don't have any property and did not already exist - foreach (ConfigData cd in configData) { - if ((!cd.Exists && cd.FullySpecified) || (cd.IsNew && !cd.Group.Properties.Any ())) - msproject.RemoveGroup (cd.Group); - } - } - - SaveProjectItems (monitor, toolsFormat, ser, msproject); - - if (dotNetProject != null) { - var moniker = dotNetProject.TargetFramework.Id; - bool supportsMultipleFrameworks = toolsFormat.SupportsMonikers || toolsFormat.SupportedFrameworks.Length > 0; - var def = dotNetProject.GetDefaultTargetFrameworkForFormat (GetFileFormat (toolsFormat)); - - // If the format only supports one fx version, or the version is the default, there is no need to store it. - // However, is there is already a value set, do not remove it. - if (supportsMultipleFrameworks) { - SetIfPresentOrNotDefaultValue (globalGroup, "TargetFrameworkVersion", "v" + moniker.Version, "v" + def.Version); - } - - if (toolsFormat.SupportsMonikers) { - SetIfPresentOrNotDefaultValue (globalGroup, "TargetFrameworkIdentifier", moniker.Identifier, def.Identifier); - SetIfPresentOrNotDefaultValue (globalGroup, "TargetFrameworkProfile", moniker.Profile, def.Profile); - } - } - - // Impdate the imports section - - List<DotNetProjectImport> currentImports = msproject.Imports.Select (i => new DotNetProjectImport (i.Project)).ToList (); - List<DotNetProjectImport> imports = new List<DotNetProjectImport> (currentImports); - - // If the project is not new, don't add the default project imports, - // just assume that the current imports are correct - UpdateImports (imports, dotNetProject, newProject); - foreach (DotNetProjectImport imp in imports) { - if (!currentImports.Contains (imp)) { - MSBuildImport import = msproject.AddNewImport (imp.Name); - if (imp.HasCondition ()) - import.Condition = imp.Condition; - currentImports.Add (imp); - } - } - foreach (DotNetProjectImport imp in currentImports) { - if (!imports.Contains (imp)) - msproject.RemoveImport (imp.Name); - } - - DataItem extendedData = ser.ExternalItemProperties; - if (extendedData.HasItemData) { - extendedData.Name = "Properties"; - StringWriter sw = new StringWriter (); - XmlConfigurationWriter.DefaultWriter.Write (new XmlTextWriter (sw), extendedData); - msproject.SetProjectExtensions ("MonoDevelop", sw.ToString ()); - } else - msproject.RemoveProjectExtensions ("MonoDevelop"); - - SaveToMSBuildProject (monitor, msproject); - - return msproject; - } - - internal void SaveProjectItems (IProgressMonitor monitor, MSBuildFileFormat toolsFormat, MSBuildSerializer ser, MSBuildProject msproject, string pathPrefix = null) - { - // Remove old items - Dictionary<string, ItemInfo> oldItems = new Dictionary<string, ItemInfo> (); - foreach (MSBuildItem item in msproject.GetAllItems ()) - oldItems [item.Name + "<" + item.UnevaluatedInclude + "<" + item.Condition] = new ItemInfo () { - Item = item - }; - // Add the new items - foreach (object ob in ((SolutionEntityItem)Item).Items.Concat (((SolutionEntityItem)Item).WildcardItems).Where (it => !it.Flags.HasFlag (ProjectItemFlags.DontPersist))) - SaveItem (monitor, toolsFormat, ser, msproject, ob, oldItems, pathPrefix); - foreach (ItemInfo itemInfo in oldItems.Values) { - if (!itemInfo.Added) - msproject.RemoveItem (itemInfo.Item); - } - } - - protected void SaveToMSBuildProject (IProgressMonitor monitor, MSBuildProject msproject) - { - foreach (var ext in GetMSBuildExtensions ()) - ext.SaveProject (monitor, EntityItem, msproject); - } - - void SetIfPresentOrNotDefaultValue (MSBuildPropertySet propGroup, string name, string value, string defaultValue, bool isXml = false) - { - bool hasDefaultValue = string.IsNullOrEmpty (value) || value == defaultValue; - var prop = propGroup.GetProperty (name); - if (prop != null) { - //if the value is default or empty, only remove the element if it was not already the default or empty - //to avoid unnecessary project file churn - if (hasDefaultValue) { - var existing = prop.GetValue (isXml); - bool alreadyHadDefaultValue = string.IsNullOrEmpty (existing) || existing == defaultValue; - if (!alreadyHadDefaultValue) - propGroup.RemoveProperty (name); - } else { - prop.SetValue (value, isXml); - } - } else if (!hasDefaultValue) { - propGroup.SetPropertyValue (name, value, false, isXml); - } - } - - void ForceDefaultValueSerialization (MSBuildSerializer ser, MSBuildPropertySet baseGroup, object ob) - { - ClassDataType dt = (ClassDataType) ser.DataContext.GetConfigurationDataType (ob.GetType()); - foreach (ItemProperty prop in dt.GetProperties (ser.SerializationContext, ob)) { - if (baseGroup.GetProperty (prop.Name) != null) - ser.SerializationContext.ForceDefaultValueSerialization (prop); - } - } - - void CollectMergetoprojectProperties (MSBuildPropertyGroup pgroup, HashSet<MergedProperty> properties, Dictionary<string,MergedPropertyValue> mergeToProjectProperties) - { - // This method checks every property in pgroup which has the MergeToProject flag. - // If the value of this property is the same as the one stored in mergeToProjectProperties - // it means that the property can be merged to the main project property group (so far). - - foreach (var pinfo in new List<MergedProperty> (properties)) { - MSBuildProperty prop = pgroup.GetProperty (pinfo.Name); - - MergedPropertyValue mvalue; - if (!mergeToProjectProperties.TryGetValue (pinfo.Name, out mvalue)) { - if (prop != null) { - // This is the first time the value is checked. Just assign it. - mergeToProjectProperties.Add (pinfo.Name, new MergedPropertyValue (prop.GetValue (true), pinfo.PreserveExistingCase)); - continue; - } - // If there is no value, it can't be merged - } - else if (prop != null && string.Equals (prop.GetValue (true), mvalue.XmlValue, StringComparison.OrdinalIgnoreCase)) - // Same value. It can be merged. - continue; - - // The property can't be merged because different configurations have different - // values for it. Remove it from the list. - properties.Remove (pinfo); - mergeToProjectProperties.Remove (pinfo.Name); - } - } - - struct MergedPropertyValue - { - public readonly string XmlValue; - public readonly bool PreserveExistingCase; - - public MergedPropertyValue (string xmlValue, bool preserveExistingCase) - { - this.XmlValue = xmlValue; - this.PreserveExistingCase = preserveExistingCase; - } - } - - void SaveItem (IProgressMonitor monitor, MSBuildFileFormat fmt, MSBuildSerializer ser, MSBuildProject msproject, object ob, Dictionary<string,ItemInfo> oldItems, string pathPrefix = null) - { - if (ob is ProjectReference) { - SaveReference (monitor, fmt, ser, msproject, (ProjectReference) ob, oldItems); - } - else if (ob is ProjectFile) { - SaveProjectFile (ser, msproject, (ProjectFile) ob, oldItems, pathPrefix); - } - else { - string itemName; - if (ob is UnknownProjectItem) { - var ui = (UnknownProjectItem)ob; - itemName = ui.ItemName; - var buildItem = AddOrGetBuildItem (msproject, oldItems, itemName, ui.Include, ui.Condition); - WriteBuildItemMetadata (ser, buildItem, ob, oldItems); - } - else { - DataType dt = ser.DataContext.GetConfigurationDataType (ob.GetType ()); - var buildItem = msproject.AddNewItem (dt.Name, ""); - WriteBuildItemMetadata (ser, buildItem, ob, oldItems); - } - } - } - - void SaveProjectFile (MSBuildSerializer ser, MSBuildProject msproject, ProjectFile file, Dictionary<string,ItemInfo> oldItems, string pathPrefix = null) - { - if (file.IsOriginatedFromWildcard) return; - - string itemName = (file.Subtype == Subtype.Directory)? "Folder" : file.BuildAction; - - string path = pathPrefix + MSBuildProjectService.ToMSBuildPath (Item.ItemDirectory, file.FilePath); - if (path.Length == 0) - return; - - //directory paths must end with '/' - if ((file.Subtype == Subtype.Directory) && path[path.Length-1] != '\\') - path = path + "\\"; - - MSBuildItem buildItem = AddOrGetBuildItem (msproject, oldItems, itemName, path, file.Condition); - WriteBuildItemMetadata (ser, buildItem, file, oldItems); - - if (!string.IsNullOrEmpty (file.DependsOn)) - buildItem.SetMetadata ("DependentUpon", MSBuildProjectService.ToMSBuildPath (Path.GetDirectoryName (file.FilePath), file.DependsOn)); - else - buildItem.UnsetMetadata ("DependentUpon"); - - if (!string.IsNullOrEmpty (file.ContentType)) - buildItem.SetMetadata ("SubType", file.ContentType); - - if (!string.IsNullOrEmpty (file.Generator)) - buildItem.SetMetadata ("Generator", file.Generator); - else - buildItem.UnsetMetadata ("Generator"); - - if (!string.IsNullOrEmpty (file.CustomToolNamespace)) - buildItem.SetMetadata ("CustomToolNamespace", file.CustomToolNamespace); - else - buildItem.UnsetMetadata ("CustomToolNamespace"); - - if (!string.IsNullOrEmpty (file.LastGenOutput)) - buildItem.SetMetadata ("LastGenOutput", file.LastGenOutput); - else - buildItem.UnsetMetadata ("LastGenOutput"); - - if (!string.IsNullOrEmpty (file.Link)) - buildItem.SetMetadata ("Link", MSBuildProjectService.ToMSBuildPathRelative (Item.ItemDirectory, file.Link)); - else - buildItem.UnsetMetadata ("Link"); - - buildItem.Condition = file.Condition; - - if (file.CopyToOutputDirectory == FileCopyMode.None) { - buildItem.UnsetMetadata ("CopyToOutputDirectory"); - } else { - buildItem.SetMetadata ("CopyToOutputDirectory", file.CopyToOutputDirectory.ToString ()); - } - - if (!file.Visible) { - buildItem.SetMetadata ("Visible", "False"); - } else { - buildItem.UnsetMetadata ("Visible"); - } - - var resId = file.ResourceId; - - //For EmbeddedResource, emit LogicalName only when it does not match the default Id - if (file.BuildAction == BuildAction.EmbeddedResource && GetDefaultResourceId (file) == resId) - resId = null; - - if (!string.IsNullOrEmpty (resId)) { - buildItem.SetMetadata ("LogicalName", resId); - } else { - buildItem.UnsetMetadata ("LogicalName"); - } - } - - void SaveReference (IProgressMonitor monitor, MSBuildFileFormat fmt, MSBuildSerializer ser, MSBuildProject msproject, ProjectReference pref, Dictionary<string,ItemInfo> oldItems) - { - MSBuildItem buildItem; - if (pref.ReferenceType == ReferenceType.Assembly) { - string asm = null; - string hintPath = null; - if (pref.ExtendedProperties.Contains ("_OriginalMSBuildReferenceInclude")) { - asm = (string) pref.ExtendedProperties ["_OriginalMSBuildReferenceInclude"]; - hintPath = (string) pref.ExtendedProperties ["_OriginalMSBuildReferenceHintPath"]; - } - else { - if (File.Exists (pref.HintPath)) { - try { - var aname = AssemblyName.GetAssemblyName (pref.HintPath); - if (pref.SpecificVersion) { - asm = aname.FullName; - } else { - asm = aname.Name; - } - } catch (Exception ex) { - string msg = string.Format ("Could not get full name for assembly '{0}'.", pref.Reference); - monitor.ReportWarning (msg); - LoggingService.LogError (msg, ex); - } - } - string basePath = Item.ItemDirectory; - if (pref.ExtendedProperties.Contains ("_OriginalMSBuildReferenceIsAbsolute")) - basePath = null; - hintPath = MSBuildProjectService.ToMSBuildPath (basePath, pref.HintPath); - } - if (asm == null) - asm = Path.GetFileNameWithoutExtension (pref.Reference); - - buildItem = AddOrGetBuildItem (msproject, oldItems, "Reference", asm, pref.Condition); - - if (!pref.SpecificVersion && ReferenceStringHasVersion (asm)) { - buildItem.SetMetadata ("SpecificVersion", "False"); - } else { - buildItem.UnsetMetadata ("SpecificVersion"); - } - - buildItem.SetMetadata ("HintPath", hintPath); - } - else if (pref.ReferenceType == ReferenceType.Package) { - string include = pref.StoredReference; - SystemPackage pkg = pref.Package; - if (pkg != null && pkg.IsFrameworkPackage) { - int i = include.IndexOf (','); - if (i != -1) - include = include.Substring (0, i).Trim (); - } - buildItem = AddOrGetBuildItem (msproject, oldItems, "Reference", include, pref.Condition); - if (!pref.SpecificVersion && ReferenceStringHasVersion (include)) - buildItem.SetMetadata ("SpecificVersion", "False"); - else - buildItem.UnsetMetadata ("SpecificVersion"); - - //RequiredTargetFramework is undocumented, maybe only a hint for VS. Only seems to be used for .NETFramework - var dnp = pref.OwnerProject as DotNetProject; - IList supportedFrameworks = fmt.SupportedFrameworks; - if (supportedFrameworks != null && dnp != null && pkg != null - && dnp.TargetFramework.Id.Identifier == TargetFrameworkMoniker.ID_NET_FRAMEWORK - && pkg.IsFrameworkPackage && supportedFrameworks.Contains (pkg.TargetFramework) - && pkg.TargetFramework.Version != "2.0" && supportedFrameworks.Count > 1) - { - TargetFramework fx = Runtime.SystemAssemblyService.GetTargetFramework (pkg.TargetFramework); - buildItem.SetMetadata ("RequiredTargetFramework", fx.Id.Version); - } else { - buildItem.UnsetMetadata ("RequiredTargetFramework"); - } - - string hintPath = (string) pref.ExtendedProperties ["_OriginalMSBuildReferenceHintPath"]; - if (hintPath != null) - buildItem.SetMetadata ("HintPath", hintPath); - else - buildItem.UnsetMetadata ("HintPath"); - } - else if (pref.ReferenceType == ReferenceType.Project) { - Project refProj = Item.ParentSolution.FindProjectByName (pref.Reference); - if (refProj != null) { - buildItem = AddOrGetBuildItem (msproject, oldItems, "ProjectReference", MSBuildProjectService.ToMSBuildPath (Item.ItemDirectory, refProj.FileName), pref.Condition); - MSBuildProjectHandler handler = refProj.ItemHandler as MSBuildProjectHandler; - if (handler != null) - buildItem.SetMetadata ("Project", handler.Item.ItemId); - else - buildItem.UnsetMetadata ("Project"); - buildItem.SetMetadata ("Name", refProj.Name); - } else { - monitor.ReportWarning (GettextCatalog.GetString ("Reference to unknown project '{0}' ignored.", pref.Reference)); - return; - } - } - else { - // Custom - DataType dt = ser.DataContext.GetConfigurationDataType (pref.GetType ()); - buildItem = AddOrGetBuildItem (msproject, oldItems, dt.Name, pref.Reference, pref.Condition); - } - - if (pref.LocalCopy != pref.DefaultLocalCopy) - buildItem.SetMetadata ("Private", pref.LocalCopy); - else - buildItem.UnsetMetadata ("Private"); - - WriteBuildItemMetadata (ser, buildItem, pref, oldItems); - buildItem.Condition = pref.Condition; - } - - void UpdateImports (List<DotNetProjectImport> imports, DotNetProject project, bool addItemTypeImports) - { - if (targetImports != null && addItemTypeImports) { - AddMissingImports (imports, targetImports); - } - - List <string> updatedImports = imports.Select (import => import.Name).ToList (); - foreach (IMSBuildImportProvider ip in AddinManager.GetExtensionObjects ("/MonoDevelop/ProjectModel/MSBuildImportProviders")) { - ip.UpdateImports (EntityItem, updatedImports); - } - - UpdateImports (imports, updatedImports); - - if (project != null) { - AddMissingImports (imports, project.ImportsAdded); - RemoveImports (imports, project.ImportsRemoved); - project.ImportsSaved (); - } - } - - void AddMissingImports (List<DotNetProjectImport> existingImports, IEnumerable<string> newImports) - { - AddMissingImports (existingImports, newImports.Select (import => new DotNetProjectImport (import))); - } - - void AddMissingImports (List<DotNetProjectImport> existingImports, IEnumerable<DotNetProjectImport> newImports) - { - foreach (DotNetProjectImport imp in newImports) - if (!existingImports.Contains (imp)) - existingImports.Add (imp); - } - - void UpdateImports (List<DotNetProjectImport> existingImports, List<string> updatedImports) - { - RemoveMissingImports (existingImports, updatedImports); - AddMissingImports (existingImports, updatedImports); - } - - void RemoveMissingImports (List<DotNetProjectImport> existingImports, List<string> updatedImports) - { - List <DotNetProjectImport> importsToRemove = existingImports.Where (import => !updatedImports.Contains (import.Name)).ToList (); - RemoveImports (existingImports, importsToRemove); - } - - void RemoveImports (List<DotNetProjectImport> existingImports, IEnumerable<DotNetProjectImport> importsToRemove) - { - foreach (DotNetProjectImport imp in importsToRemove) - existingImports.Remove (imp); - } - - IEnumerable<MSBuildExtension> GetMSBuildExtensions () - { - foreach (var e in AddinManager.GetExtensionObjects<MSBuildExtension> ("/MonoDevelop/ProjectModel/MSBuildExtensions")) { - e.Handler = this; - yield return e; - } - } - - void ReadBuildItemMetadata (DataSerializer ser, MSBuildItem buildItem, object dataItem, Type extendedType) - { - DataItem ditem = new DataItem (); - foreach (ItemProperty prop in ser.GetProperties (dataItem)) { - string name = ToMsbuildItemName (prop.Name); - if (name == "Include") - ditem.ItemData.Add (new DataValue ("Include", buildItem.Include)); - else if (buildItem.HasMetadata (name)) { - string data = buildItem.GetMetadata (name, !prop.DataType.IsSimpleType); - ditem.ItemData.Add (GetDataNode (prop, data)); - } - } - ConvertFromMsbuildFormat (ditem); - ser.Deserialize (dataItem, ditem); - } - - void WriteBuildItemMetadata (DataSerializer ser, MSBuildItem buildItem, object dataItem, Dictionary<string,ItemInfo> oldItems) - { - var notWrittenProps = new HashSet<string> (); - foreach (ItemProperty prop in ser.GetProperties (dataItem)) - notWrittenProps.Add (prop.Name); - - DataItem ditem = (DataItem) ser.Serialize (dataItem, dataItem.GetType ()); - if (ditem.HasItemData) { - foreach (DataNode node in ditem.ItemData) { - notWrittenProps.Remove (node.Name); - if (node.Name == "Include" && node is DataValue) - buildItem.Include = ((DataValue) node).Value; - else { - ConvertToMsbuildFormat (node); - buildItem.SetMetadata (node.Name, GetXmlString (node), node is DataItem); - } - } - } - foreach (string prop in notWrittenProps) - buildItem.UnsetMetadata (prop); - } - - MSBuildItem AddOrGetBuildItem (MSBuildProject msproject, Dictionary<string,ItemInfo> oldItems, string name, string include, string condition) - { - ItemInfo itemInfo; - string key = name + "<" + include + "<" + condition; - if (oldItems.TryGetValue (key, out itemInfo)) { - if (!itemInfo.Added) { - itemInfo.Added = true; - oldItems [key] = itemInfo; - } - return itemInfo.Item; - } else { - return msproject.AddNewItem (name, include); - } - } - - DataItem ReadPropertyGroupMetadata (DataSerializer ser, MSBuildPropertySet propGroup, object dataItem) - { - DataItem ditem = new DataItem (); - - foreach (MSBuildProperty bprop in propGroup.Properties) { - DataNode node = null; - foreach (XmlNode xnode in bprop.Element.ChildNodes) { - if (xnode is XmlElement) { - node = XmlConfigurationReader.DefaultReader.Read ((XmlElement)xnode); - break; - } - } - if (node == null) { - node = new DataValue (bprop.Name, bprop.GetValue (false)); - } - - ConvertFromMsbuildFormat (node); - ditem.ItemData.Add (node); - } - - return ditem; - } - - void WritePropertyGroupMetadata (MSBuildPropertySet propGroup, DataCollection itemData, MSBuildSerializer ser, params object[] itemsToReplace) - { - var notWrittenProps = new HashSet<string> (); - - foreach (object ob in itemsToReplace) { - if (ob == null) - continue; - ClassDataType dt = (ClassDataType) ser.DataContext.GetConfigurationDataType (ob.GetType ()); - foreach (ItemProperty prop in dt.GetProperties (ser.SerializationContext, ob)) - notWrittenProps.Add (prop.Name); - } - - foreach (DataNode node in itemData) { - notWrittenProps.Remove (node.Name); - ConvertToMsbuildFormat (node); - - // In the other msbuild contexts (metadata, solution properties, etc) we TitleCase by default, so the node.Value is TitleCase. - // However, for property value, we lowercase by default and preserve existing case to reduce noise on VS-created files. - var boolNode = node as MSBuildBoolDataValue; - string value; - bool preserveExistingCase; - if (boolNode != null) { - value = boolNode.RawValue? "true" : "false"; - preserveExistingCase = true; - } else { - value = GetXmlString (node); - preserveExistingCase = false; - } - - propGroup.SetPropertyValue (node.Name, value, preserveExistingCase, node is DataItem); - } - foreach (string prop in notWrittenProps) - propGroup.RemoveProperty (prop); - } - - string ToMsbuildItemName (string name) - { - return name.Replace ('.', '-'); - } - - void ConvertToMsbuildFormat (DataNode node) - { - ReplaceChar (node, true, '.', '-'); - } - - void ConvertFromMsbuildFormat (DataNode node) - { - ReplaceChar (node, true, '-', '.'); - } - - void ReplaceChar (DataNode node, bool force, char oldChar, char newChar) - { - DataItem it = node as DataItem; - if ((force || it != null) && node.Name != null) - node.Name = node.Name.Replace (oldChar, newChar); - if (it != null) { - foreach (DataNode cnode in it.ItemData) - ReplaceChar (cnode, !it.UniqueNames, oldChar, newChar); - } - } - - List<ConfigData> GetConfigData (MSBuildProject msproject, bool includeGlobalGroups) - { - List<ConfigData> configData = new List<ConfigData> (); - foreach (MSBuildPropertyGroup cgrp in msproject.PropertyGroups) { - string conf, platform; - if (ParseConfigCondition (cgrp.Condition, out conf, out platform) || includeGlobalGroups) - configData.Add (new ConfigData (conf, platform, cgrp)); - } - return configData; - } - - ConfigData FindPropertyGroup (List<ConfigData> configData, SolutionItemConfiguration config) - { - foreach (ConfigData data in configData) { - if (data.Config == config.Name && data.Platform == config.Platform) - return data; - } - return null; - } - - ProjectFile ReadProjectFile (DataSerializer ser, Project project, MSBuildItem buildItem, Type type) - { - string path = MSBuildProjectService.FromMSBuildPath (project.ItemDirectory, buildItem.Include); - ProjectFile file = (ProjectFile) Activator.CreateInstance (type); - file.Name = path; - file.BuildAction = buildItem.Name; - - ReadBuildItemMetadata (ser, buildItem, file, type); - - string dependentFile = buildItem.GetMetadata ("DependentUpon"); - if (!string.IsNullOrEmpty (dependentFile)) { - dependentFile = MSBuildProjectService.FromMSBuildPath (Path.GetDirectoryName (path), dependentFile); - file.DependsOn = dependentFile; - } - - string copyToOutputDirectory = buildItem.GetMetadata ("CopyToOutputDirectory"); - if (!string.IsNullOrEmpty (copyToOutputDirectory)) { - switch (copyToOutputDirectory) { - case "None": break; - case "Always": file.CopyToOutputDirectory = FileCopyMode.Always; break; - case "PreserveNewest": file.CopyToOutputDirectory = FileCopyMode.PreserveNewest; break; - default: - MonoDevelop.Core.LoggingService.LogWarning ( - "Unrecognised value {0} for CopyToOutputDirectory MSBuild property", - copyToOutputDirectory); - break; - } - } - - if (buildItem.GetMetadataIsFalse ("Visible")) - file.Visible = false; - - - string resourceId = buildItem.GetMetadata ("LogicalName"); - if (!string.IsNullOrEmpty (resourceId)) - file.ResourceId = resourceId; - - string contentType = buildItem.GetMetadata ("SubType"); - if (!string.IsNullOrEmpty (contentType)) - file.ContentType = contentType; - - string generator = buildItem.GetMetadata ("Generator"); - if (!string.IsNullOrEmpty (generator)) - file.Generator = generator; - - string customToolNamespace = buildItem.GetMetadata ("CustomToolNamespace"); - if (!string.IsNullOrEmpty (customToolNamespace)) - file.CustomToolNamespace = customToolNamespace; - - string lastGenOutput = buildItem.GetMetadata ("LastGenOutput"); - if (!string.IsNullOrEmpty (lastGenOutput)) - file.LastGenOutput = lastGenOutput; - - string link = buildItem.GetMetadata ("Link"); - if (!string.IsNullOrEmpty (link)) { - if (!Platform.IsWindows) - link = MSBuildProjectService.UnescapePath (link); - file.Link = link; - } - - file.Condition = buildItem.Condition; - return file; - } - - bool ParseConfigCondition (string cond, out string config, out string platform) - { - config = platform = Unspecified; - int i = cond.IndexOf ("=="); - if (i == -1) - return false; - if (cond.Substring (0, i).Trim () == "'$(Configuration)|$(Platform)'") { - cond = cond.Substring (i+2).Trim (' ','\''); - i = cond.IndexOf ('|'); - if (i != -1) { - config = cond.Substring (0, i); - platform = cond.Substring (i+1); - } else { - // Invalid configuration - return false; - } - if (platform == "AnyCPU") - platform = string.Empty; - return true; - } - else if (cond.Substring (0, i).Trim () == "'$(Configuration)'") { - config = cond.Substring (i+2).Trim (' ','\''); - platform = Unspecified; - return true; - } - else if (cond.Substring (0, i).Trim () == "'$(Platform)'") { - config = Unspecified; - platform = cond.Substring (i+2).Trim (' ','\''); - if (platform == "AnyCPU") - platform = string.Empty; - return true; - } - return false; - } - - string BuildConfigCondition (string config, string platform) - { - if (platform.Length == 0) - platform = "AnyCPU"; - return " '$(Configuration)|$(Platform)' == '" + config + "|" + platform + "' "; - } - - bool IsMergeToProjectProperty (ItemProperty prop) - { - foreach (object at in prop.CustomAttributes) { - if (at is MergeToProjectAttribute) - return true; - } - return false; - } - - string GetXmlString (DataNode node) - { - if (node is DataValue) - return ((DataValue)node).Value; - else { - StringWriter sw = new StringWriter (); - XmlTextWriter xw = new XmlTextWriter (sw); - XmlConfigurationWriter.DefaultWriter.Write (xw, node); - return sw.ToString (); - } - } - - DataNode GetDataNode (ItemProperty prop, string xmlString) - { - if (prop.DataType.IsSimpleType) - return new DataValue (prop.Name, xmlString); - else { - StringReader sr = new StringReader (xmlString); - return XmlConfigurationReader.DefaultReader.Read (new XmlTextReader (sr)); - } - } - - internal virtual MSBuildSerializer CreateSerializer () - { - return new MSBuildSerializer (EntityItem.FileName); - } - - static readonly MSBuildElementOrder globalConfigOrder = new MSBuildElementOrder ( - "Configuration","Platform","ProductVersion","SchemaVersion","ProjectGuid", "OutputType", - "AppDesignerFolder","RootNamespace","AssemblyName","StartupObject" - ); - static readonly MSBuildElementOrder configOrder = new MSBuildElementOrder ( - "DebugSymbols","DebugType","Optimize","OutputPath","DefineConstants","ErrorReport","WarningLevel", - "TreatWarningsAsErrors","DocumentationFile" - ); - - // Those are properties which are dynamically set by this file format - - internal static readonly ItemMember[] ExtendedMSBuildProperties = new ItemMember [] { - new ItemMember (typeof(SolutionEntityItem), "ProductVersion"), - new ItemMember (typeof(SolutionEntityItem), "SchemaVersion"), - new ItemMember (typeof(SolutionEntityItem), "ProjectGuid"), - new ItemMember (typeof(DotNetProjectConfiguration), "ErrorReport"), - new ItemMember (typeof(DotNetProjectConfiguration), "TargetFrameworkVersion", new object[] { new MergeToProjectAttribute () }), - new ItemMember (typeof(ProjectReference), "RequiredTargetFramework"), - new ItemMember (typeof(Project), "InternalTargetFrameworkVersion", true), - }; - - // Items generated by VS but which MD is not using and should be ignored - - internal static readonly IList<string> UnsupportedItems = new string[] { - "BootstrapperFile", "AppDesigner", "WebReferences", "WebReferenceUrl", "Service", - "ProjectReference", "Reference", // Reference elements are included here because they are special-cased for DotNetProject, and they are unsupported in other types of projects - "InternalsVisibleTo", - "InternalsVisibleToTest" - }; - } - class MSBuildSerializer: DataSerializer { public DataItem InternalItemProperties = new DataItem (); @@ -2061,8 +67,8 @@ namespace MonoDevelop.Projects.Formats.MSBuild if (prop.Name == "References" || prop.Name == "LanguageParameters") return false; } - if (instance is SolutionEntityItem) { - if (prop.IsExtendedProperty (typeof(SolutionEntityItem))) + if (instance is SolutionItem) { + if (prop.IsExtendedProperty (typeof(SolutionItem))) return true; return prop.Name != "name" && prop.Name != "Configurations"; } @@ -2086,7 +92,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild internal protected override DataNode OnSerializeProperty (ItemProperty prop, SerializationContext serCtx, object instance, object value) { DataNode data = base.OnSerializeProperty (prop, serCtx, instance, value); - if (instance is SolutionEntityItem && data != null) { + if (instance is SolutionItem && data != null) { if (prop.IsExternal) ExternalItemProperties.ItemData.Add (data); else @@ -2096,22 +102,6 @@ namespace MonoDevelop.Projects.Formats.MSBuild } } - class UnknownSolutionItemTypeException : InvalidOperationException - { - public UnknownSolutionItemTypeException () - : base ("Unknown solution item type") - { - } - - public UnknownSolutionItemTypeException (string name) - : base ("Unknown solution item type: " + name) - { - this.TypeName = name; - } - - public string TypeName { get; private set; } - } - class MSBuildElementOrder: Dictionary<string, int> { public MSBuildElementOrder (params string[] elements) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProjectService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProjectService.cs index ac766786b6..3059abfd4e 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProjectService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProjectService.cs @@ -41,12 +41,14 @@ using MonoDevelop.Core; using MonoDevelop.Core.Assemblies; using Cecil = Mono.Cecil; using System.Threading; +using System.Threading.Tasks; +using System.Xml; namespace MonoDevelop.Projects.Formats.MSBuild { public static class MSBuildProjectService { - const string ItemTypesExtensionPath = "/MonoDevelop/ProjectModel/MSBuildItemTypes"; + internal const string ItemTypesExtensionPath = "/MonoDevelop/ProjectModel/MSBuildItemTypes"; public const string GenericItemGuid = "{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}"; public const string FolderTypeGuid = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}"; @@ -58,7 +60,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild static IMSBuildGlobalPropertyProvider[] globalPropertyProviders; static Dictionary<string,RemoteBuildEngine> builders = new Dictionary<string, RemoteBuildEngine> (); - static GenericItemTypeNode genericItemTypeNode = new GenericItemTypeNode (); + static Dictionary<string,Type> genericProjectTypes = new Dictionary<string, Type> (); internal static bool ShutDown { get; private set; } @@ -67,13 +69,6 @@ namespace MonoDevelop.Projects.Formats.MSBuild if (dataContext == null) { dataContext = new MSBuildDataContext (); Services.ProjectService.InitializeDataContext (dataContext); - foreach (ItemMember prop in MSBuildProjectHandler.ExtendedMSBuildProperties) { - ItemProperty iprop = new ItemProperty (prop.Name, prop.Type); - iprop.IsExternal = prop.IsExternal; - if (prop.CustomAttributes != null) - iprop.CustomAttributes = prop.CustomAttributes; - dataContext.RegisterProperty (prop.DeclaringType, iprop); - } } return dataContext; } @@ -88,10 +83,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild PropertyService.PropertyChanged += HandlePropertyChanged; DefaultMSBuildVerbosity = PropertyService.Get ("MonoDevelop.Ide.MSBuildVerbosity", MSBuildVerbosity.Normal); - Runtime.ShuttingDown += delegate { - ShutDown = true; - CleanProjectBuilders (); - }; + Runtime.ShuttingDown += (sender, e) => ShutDown = true; const string gppPath = "/MonoDevelop/ProjectModel/MSBuildGlobalPropertyProviders"; globalPropertyProviders = AddinManager.GetExtensionObjects<IMSBuildGlobalPropertyProvider> (gppPath); @@ -118,84 +110,184 @@ namespace MonoDevelop.Projects.Formats.MSBuild internal static MSBuildVerbosity DefaultMSBuildVerbosity { get; private set; } - public static SolutionEntityItem LoadItem (IProgressMonitor monitor, string fileName, MSBuildFileFormat expectedFormat, string typeGuid, string itemGuid) + public async static Task<SolutionItem> LoadItem (ProgressMonitor monitor, string fileName, MSBuildFileFormat expectedFormat, string typeGuid, string itemGuid) { - foreach (ItemTypeNode node in GetItemTypeNodes ()) { + foreach (SolutionItemTypeNode node in GetItemTypeNodes ()) { if (node.CanHandleFile (fileName, typeGuid)) - return node.LoadSolutionItem (monitor, fileName, expectedFormat, itemGuid); + return await LoadProjectAsync (monitor, fileName, expectedFormat, typeGuid, null, node); } - if (string.IsNullOrEmpty (typeGuid) && IsProjectSubtypeFile (fileName)) { - typeGuid = LoadProjectTypeGuids (fileName); - foreach (ItemTypeNode node in GetItemTypeNodes ()) { - if (node.CanHandleFile (fileName, typeGuid)) - return node.LoadSolutionItem (monitor, fileName, expectedFormat, itemGuid); - } - } - // If it is a known unsupported project, load it as UnknownProject var projectInfo = MSBuildProjectService.GetUnknownProjectTypeInfo (typeGuid != null ? new [] { typeGuid } : new string[0], fileName); if (projectInfo != null && projectInfo.LoadFiles) { if (typeGuid == null) typeGuid = projectInfo.Guid; - var h = new MSBuildProjectHandler (typeGuid, "", itemGuid); - h.SetUnsupportedType (projectInfo); - return h.Load (monitor, fileName, expectedFormat, "", null); + var p = (UnknownProject) await LoadProjectAsync (monitor, fileName, expectedFormat, "", typeof(UnknownProject), null); + p.UnsupportedProjectMessage = projectInfo.GetInstructions (); + return p; } - return null; } - - internal static IResourceHandler GetResourceHandlerForItem (DotNetProject project) + + internal static async Task<SolutionItem> LoadProjectAsync (ProgressMonitor monitor, string fileName, MSBuildFileFormat format, string typeGuid, Type itemType, SolutionItemTypeNode node) { - foreach (ItemTypeNode node in GetItemTypeNodes ()) { - DotNetProjectNode pNode = node as DotNetProjectNode; - if (pNode != null && pNode.CanHandleItem (project)) - return pNode.GetResourceHandler (); + try { + ProjectExtensionUtil.BeginLoadOperation (); + var item = await CreateSolutionItem (monitor, fileName, typeGuid, itemType, node); + item.TypeGuid = typeGuid ?? node.Guid; + await item.LoadAsync (monitor, fileName, format); + return item; + } finally { + ProjectExtensionUtil.EndLoadOperation (); } - return new MSBuildResourceHandler (); } - - internal static MSBuildHandler GetItemHandler (SolutionEntityItem item) + + // All of the last 4 parameters are optional, but at least one must be provided. + static async Task<SolutionItem> CreateSolutionItem (ProgressMonitor monitor, string fileName, string typeGuid, Type itemClass, SolutionItemTypeNode node) { - MSBuildHandler handler = item.ItemHandler as MSBuildHandler; - if (handler != null) - return handler; - else - throw new InvalidOperationException ("Not an MSBuild project"); + if (itemClass != null) + return (SolutionItem)Activator.CreateInstance (itemClass); + + return await node.CreateSolutionItem (monitor, fileName, typeGuid ?? node.Guid); } - - internal static void SetId (SolutionItem item, string id) + + internal static bool CanCreateSolutionItem (string type, ProjectCreateInformation info, System.Xml.XmlElement projectOptions) { - MSBuildHandler handler = item.ItemHandler as MSBuildHandler; - if (handler != null) - handler.ItemId = id; - else - throw new InvalidOperationException ("Not an MSBuild project"); + foreach (var node in GetItemTypeNodes ()) { + if (node.CanCreateSolutionItem (type, info, projectOptions)) + return true; + } + return false; } - - internal static void InitializeItemHandler (SolutionItem item) - { - SolutionEntityItem eitem = item as SolutionEntityItem; - if (eitem != null) { - foreach (ItemTypeNode node in GetItemTypeNodes ()) { - if (node.CanHandleItem (eitem)) { - node.InitializeHandler (eitem); - foreach (DotNetProjectSubtypeNode snode in GetItemSubtypeNodes ()) { - if (snode.CanHandleItem (eitem)) - snode.InitializeHandler (eitem); - } - return; + + internal static SolutionItem CreateSolutionItem (string typeGuid) + { + foreach (var node in GetItemTypeNodes ()) { + if (node.Guid.Equals (typeGuid, StringComparison.OrdinalIgnoreCase)) { + return node.CreateSolutionItem (new ProgressMonitor (), null, typeGuid).Result; + } + } + throw new InvalidOperationException ("Unknown project type: " + typeGuid); + } + + internal static SolutionItem CreateSolutionItem (string type, ProjectCreateInformation info, System.Xml.XmlElement projectOptions) + { + foreach (var node in GetItemTypeNodes ()) { + if (node.CanCreateSolutionItem (type, info, projectOptions)) + return node.CreateSolutionItem (type, info, projectOptions); + } + throw new InvalidOperationException ("Unknown project type: " + type); + } + +/* internal static bool CanMigrateFlavor (string[] guids) + { + + } + + internal static async Task<bool> MigrateFlavor (IProgressMonitor monitor, string fileName, string typeGuid, MSBuildProjectNode node, MSBuildProject p) + { + var language = GetLanguageFromGuid (typeGuid); + + if (MigrateProject (monitor, node, p, fileName, language)) { + p.Save (fileName); + return true; + } + + return false; + } + + static async Task<bool> MigrateProject (IProgressMonitor monitor, MSBuildProjectNode st, MSBuildProject p, string fileName, string language) + { + var projectLoadMonitor = monitor as IProjectLoadProgressMonitor; + if (projectLoadMonitor == null) { + // projectLoadMonitor will be null when running through md-tool, but + // this is not fatal if migration is not required, so just ignore it. --abock + if (!st.IsMigrationRequired) + return false; + + LoggingService.LogError (Environment.StackTrace); + monitor.ReportError ("Could not open unmigrated project and no migrator was supplied", null); + throw new Exception ("Could not open unmigrated project and no migrator was supplied"); + } + + var migrationType = st.MigrationHandler.CanPromptForMigration + ? st.MigrationHandler.PromptForMigration (projectLoadMonitor, p, fileName, language) + : projectLoadMonitor.ShouldMigrateProject (); + if (migrationType == MigrationType.Ignore) { + if (st.IsMigrationRequired) { + monitor.ReportError (string.Format ("{1} cannot open the project '{0}' unless it is migrated.", Path.GetFileName (fileName), BrandingService.ApplicationName), null); + throw new Exception ("The user choose not to migrate the project"); + } else + return false; + } + + var baseDir = (FilePath) Path.GetDirectoryName (fileName); + if (migrationType == MigrationType.BackupAndMigrate) { + var backupDirFirst = baseDir.Combine ("backup"); + string backupDir = backupDirFirst; + int i = 0; + while (Directory.Exists (backupDir)) { + backupDir = backupDirFirst + "-" + i.ToString (); + if (i++ > 20) { + throw new Exception ("Too many backup directories"); } } + Directory.CreateDirectory (backupDir); + foreach (var file in st.MigrationHandler.FilesToBackup (fileName)) + File.Copy (file, Path.Combine (backupDir, Path.GetFileName (file))); } - else if (item is SolutionFolder) { - MSBuildHandler h = new MSBuildHandler (FolderTypeGuid, null); - h.Item = item; - item.SetItemHandler (h); + + var res = await st.MigrationHandler.Migrate (projectLoadMonitor, p, fileName, language); + if (!res) + throw new Exception ("Could not migrate the project"); + + return true; + }*/ + + internal static string GetLanguageGuid (string language) + { + foreach (var node in GetItemTypeNodes ().OfType<DotNetProjectTypeNode> ()) { + if (node.Language == language) + return node.Guid; } + throw new InvalidOperationException ("Language not supported: " + language); } - + + internal static string GetLanguageFromGuid (string guid) + { + foreach (var node in GetItemTypeNodes ().OfType<DotNetProjectTypeNode> ()) { + if (node.Guid.Equals (guid, StringComparison.OrdinalIgnoreCase)) + return node.Language; + } + throw new InvalidOperationException ("Language not supported: " + guid); + } + + internal static bool IsKnownTypeGuid (string guid) + { + foreach (var node in GetItemTypeNodes ()) { + if (node.Guid.Equals (guid, StringComparison.OrdinalIgnoreCase)) + return true; + } + return false; + } + + internal static string GetTypeGuidFromAlias (string alias) + { + foreach (var node in GetItemTypeNodes ()) { + if (node.Alias.Equals (alias, StringComparison.OrdinalIgnoreCase)) + return node.Guid; + } + return null; + } + + internal static IEnumerable<string> GetDefaultImports (string typeGuid) + { + foreach (var node in GetItemTypeNodes ()) { + if (node.Guid == typeGuid && !string.IsNullOrEmpty (node.Import)) + yield return node.Import; + } + } + public static bool SupportsProjectType (string projectFile) { if (!string.IsNullOrWhiteSpace (projectFile)) { @@ -212,9 +304,9 @@ namespace MonoDevelop.Projects.Formats.MSBuild return false; } - public static void CheckHandlerUsesMSBuildEngine (SolutionItem item, out bool useByDefault, out bool require) + public static void CheckHandlerUsesMSBuildEngine (SolutionFolderItem item, out bool useByDefault, out bool require) { - var handler = item.ItemHandler as MSBuildProjectHandler; + var handler = item as Project; if (handler == null) { useByDefault = require = false; return; @@ -223,88 +315,92 @@ namespace MonoDevelop.Projects.Formats.MSBuild require = handler.RequireMSBuildEngine; } - internal static DotNetProjectSubtypeNode GetDotNetProjectSubtype (string typeGuids) - { - if (!string.IsNullOrEmpty (typeGuids)) - return GetDotNetProjectSubtype (typeGuids.Split (';').Select (t => t.Trim ())); - else - return null; - } - - internal static DotNetProjectSubtypeNode GetDotNetProjectSubtype (IEnumerable<string> typeGuids) - { - Type ptype = null; - DotNetProjectSubtypeNode foundNode = null; - foreach (string guid in typeGuids) { - foreach (DotNetProjectSubtypeNode st in GetItemSubtypeNodes ()) { - if (st.SupportsType (guid)) { - if (ptype == null || ptype.IsAssignableFrom (st.Type)) { - ptype = st.Type; - foundNode = st; - } - } - } - } - return foundNode; - } - - static IEnumerable<ItemTypeNode> GetItemTypeNodes () - { - foreach (ExtensionNode node in AddinManager.GetExtensionNodes (ItemTypesExtensionPath)) { - if (node is ItemTypeNode) - yield return (ItemTypeNode) node; - } - yield return genericItemTypeNode; - } - - internal static IEnumerable<DotNetProjectSubtypeNode> GetItemSubtypeNodes () + static IEnumerable<SolutionItemTypeNode> GetItemTypeNodes () { foreach (ExtensionNode node in AddinManager.GetExtensionNodes (ItemTypesExtensionPath)) { - if (node is DotNetProjectSubtypeNode) - yield return (DotNetProjectSubtypeNode) node; + if (node is SolutionItemTypeNode) + yield return (SolutionItemTypeNode) node; } } internal static bool CanReadFile (FilePath file) { - foreach (ItemTypeNode node in GetItemTypeNodes ()) { + foreach (SolutionItemTypeNode node in GetItemTypeNodes ()) { if (node.CanHandleFile (file, null)) { return true; } } - if (IsProjectSubtypeFile (file)) { - string typeGuids = LoadProjectTypeGuids (file); - foreach (ItemTypeNode node in GetItemTypeNodes ()) { - if (node.CanHandleFile (file, typeGuids)) { - return true; - } - } - } return GetUnknownProjectTypeInfo (new string[0], file) != null; } - internal static string GetExtensionForItem (SolutionEntityItem item) + internal static string GetExtensionForItem (SolutionItem item) { - foreach (DotNetProjectSubtypeNode node in GetItemSubtypeNodes ()) { - if (!string.IsNullOrEmpty (node.Extension) && node.CanHandleItem (item)) - return node.Extension; - } - foreach (ItemTypeNode node in GetItemTypeNodes ()) { - if (node.CanHandleItem (item)) { + foreach (SolutionItemTypeNode node in GetItemTypeNodes ()) { + if (node.Guid.Equals (item.TypeGuid, StringComparison.OrdinalIgnoreCase)) return node.Extension; - } } // The generic handler should always be found throw new InvalidOperationException (); } - - static bool IsProjectSubtypeFile (FilePath file) + + internal static string GetTypeGuidForItem (SolutionItem item) { - foreach (DotNetProjectSubtypeNode node in GetItemSubtypeNodes ()) { - if (!string.IsNullOrEmpty (node.Extension) && node.CanHandleFile (file, null)) - return true; + var className = item.GetType ().FullName; + foreach (SolutionItemTypeNode node in GetItemTypeNodes ()) { + if (node.ItenTypeName == className) + return node.Guid; + } + return GenericItemGuid; + } + + public static void RegisterGenericProjectType (string projectId, Type type) + { + lock (genericProjectTypes) { + if (!typeof(Project).IsAssignableFrom (type)) + throw new ArgumentException ("Type is not a subclass of MonoDevelop.Projects.Project"); + genericProjectTypes [projectId] = type; + } + } + + internal static Task<SolutionItem> CreateGenericProject (string file) + { + return Task<SolutionItem>.Factory.StartNew (delegate { + var t = ReadGenericProjectType (file); + if (t == null) + throw new UserException ("Unknown project type"); + + Type type; + lock (genericProjectTypes) { + if (!genericProjectTypes.TryGetValue (t, out type)) + throw new UserException ("Unknown project type: " + t); + } + return (SolutionItem)Activator.CreateInstance (type); + }); + } + + static string ReadGenericProjectType (string file) + { + using (XmlTextReader tr = new XmlTextReader (file)) { + tr.MoveToContent (); + if (tr.LocalName != "Project") + return null; + if (tr.IsEmptyElement) + return null; + tr.ReadStartElement (); + tr.MoveToContent (); + if (tr.LocalName != "PropertyGroup") + return null; + if (tr.IsEmptyElement) + return null; + tr.ReadStartElement (); + tr.MoveToContent (); + while (tr.NodeType != XmlNodeType.EndElement) { + if (tr.NodeType == XmlNodeType.Element && !tr.IsEmptyElement && tr.LocalName == "ItemType") + return tr.ReadElementString (); + tr.Skip (); + } + return null; } - return false; } static char[] specialCharacters = new char [] {'%', '$', '@', '(', ')', '\'', ';', '?' }; @@ -342,10 +438,14 @@ namespace MonoDevelop.Projects.Formats.MSBuild return str; } - public static string ToMSBuildPath (string baseDirectory, string absPath) + public static string ToMSBuildPath (string baseDirectory, string absPath, bool normalize = true) { + if (string.IsNullOrEmpty (absPath)) + return absPath; if (baseDirectory != null) { - absPath = FileService.NormalizeRelativePath (FileService.AbsoluteToRelativePath (baseDirectory, absPath)); + absPath = FileService.AbsoluteToRelativePath (baseDirectory, absPath); + if (normalize) + absPath = FileService.NormalizeRelativePath (absPath); } return EscapeString (absPath).Replace ('/', '\\'); } @@ -501,6 +601,8 @@ namespace MonoDevelop.Projects.Formats.MSBuild extn = fname.Substring (last_dot + 1); return true; } + + static bool runLocal = false; internal static RemoteProjectBuilder GetProjectBuilder (TargetRuntime runtime, string minToolsVersion, string file, string solutionFile) { @@ -537,6 +639,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild //always start the remote process explicitly, even if it's using the current runtime and fx //else it won't pick up the assembly redirects from the builder exe var exe = GetExeLocation (runtime, toolsVersion); + MonoDevelop.Core.Execution.RemotingService.RegisterRemotingChannel (); var pinfo = new ProcessStartInfo (exe) { UseShellExecute = false, @@ -547,22 +650,30 @@ namespace MonoDevelop.Projects.Formats.MSBuild runtime.GetToolsExecutionEnvironment ().MergeTo (pinfo); Process p = null; + try { - p = runtime.ExecuteAssembly (pinfo); - p.StandardInput.WriteLine (Process.GetCurrentProcess ().Id.ToString ()); - string responseKey = "[MonoDevelop]"; - string sref; - while (true) { - sref = p.StandardError.ReadLine (); - if (sref.StartsWith (responseKey, StringComparison.Ordinal)) { - sref = sref.Substring (responseKey.Length); - break; + IBuildEngine engine; + if (!runLocal) { + p = runtime.ExecuteAssembly (pinfo); + p.StandardInput.WriteLine (Process.GetCurrentProcess ().Id.ToString ()); + string responseKey = "[MonoDevelop]"; + string sref; + while (true) { + sref = p.StandardError.ReadLine (); + if (sref.StartsWith (responseKey, StringComparison.Ordinal)) { + sref = sref.Substring (responseKey.Length); + break; + } } + byte[] data = Convert.FromBase64String (sref); + MemoryStream ms = new MemoryStream (data); + BinaryFormatter bf = new BinaryFormatter (); + engine = (IBuildEngine)bf.Deserialize (ms); + } else { + var asm = System.Reflection.Assembly.LoadFrom (exe); + var t = asm.GetType ("MonoDevelop.Projects.Formats.MSBuild.BuildEngine"); + engine = (IBuildEngine)Activator.CreateInstance (t); } - byte[] data = Convert.FromBase64String (sref); - MemoryStream ms = new MemoryStream (data); - BinaryFormatter bf = new BinaryFormatter (); - var engine = (IBuildEngine)bf.Deserialize (ms); engine.SetCulture (GettextCatalog.UICulture); engine.SetGlobalProperties (GetCoreGlobalProperties (solutionFile)); foreach (var gpp in globalPropertyProviders) @@ -572,13 +683,19 @@ namespace MonoDevelop.Projects.Formats.MSBuild if (p != null) { try { p.Kill (); - } catch { } + } catch { + } } throw; } - + + builders [builderKey] = builder; builder.ReferenceCount = 1; + builder.Disconnected += delegate { + lock (builders) + builders.Remove (builderKey); + }; return new RemoteProjectBuilder (file, builder); } } @@ -623,45 +740,11 @@ namespace MonoDevelop.Projects.Formats.MSBuild internal static void ReleaseProjectBuilder (RemoteBuildEngine engine) { lock (builders) { - if (engine.ReferenceCount > 0) { - if (--engine.ReferenceCount == 0) { - engine.ReleaseTime = DateTime.Now.AddSeconds (3); - ScheduleProjectBuilderCleanup (engine.ReleaseTime.AddMilliseconds (500)); - } - } - } - } - - static DateTime nextCleanup = DateTime.MinValue; - - static void ScheduleProjectBuilderCleanup (DateTime cleanupTime) - { - lock (builders) { - if (cleanupTime < nextCleanup) + if (--engine.ReferenceCount != 0) return; - nextCleanup = cleanupTime; - System.Threading.ThreadPool.QueueUserWorkItem (delegate { - DateTime tnow = DateTime.Now; - while (tnow < nextCleanup) { - System.Threading.Thread.Sleep ((int)(nextCleanup - tnow).TotalMilliseconds); - CleanProjectBuilders (); - tnow = DateTime.Now; - } - }); - } - } - - static void CleanProjectBuilders () - { - lock (builders) { - DateTime tnow = DateTime.Now; - foreach (var val in new Dictionary<string,RemoteBuildEngine> (builders)) { - if (val.Value.ReferenceCount == 0 && val.Value.ReleaseTime <= tnow) { - builders.Remove (val.Key); - val.Value.Dispose (); - } - } + builders.Remove (builders.First (kvp => kvp.Value == engine).Key); } + engine.Dispose (); } static Dictionary<string, string> cultureNamesTable; @@ -682,11 +765,11 @@ namespace MonoDevelop.Projects.Formats.MSBuild MSBuildProject project = new MSBuildProject (); project.Load (fileName); - MSBuildPropertySet globalGroup = project.GetGlobalPropertyGroup (); + IMSBuildPropertySet globalGroup = project.GetGlobalPropertyGroup (); if (globalGroup == null) return null; - return globalGroup.GetPropertyValue ("ProjectTypeGuids"); + return globalGroup.GetValue ("ProjectTypeGuids"); } internal static UnknownProjectTypeNode GetUnknownProjectTypeInfo (string[] guids, string fileName = null) @@ -696,11 +779,6 @@ namespace MonoDevelop.Projects.Formats.MSBuild .Where (p => guids.Any (p.MatchesGuid) || (ext != null && p.Extension == ext)).ToList (); return nodes.FirstOrDefault (n => !n.IsSolvable) ?? nodes.FirstOrDefault (n => n.IsSolvable); } - - public static MSBuildProjectHandler GetHandler (Project project) - { - return (MSBuildProjectHandler) project.GetItemHandler (); - } } class MSBuildDataContext: DataContext @@ -807,21 +885,12 @@ namespace MonoDevelop.Projects.Formats.MSBuild } } - class GenericItemTypeNode: ItemTypeNode + [RegisterProjectType (MSBuildProjectService.GenericItemGuid, Extension="mdproj")] + class GenericItemFactory: SolutionItemFactory { - public GenericItemTypeNode (): base (MSBuildProjectService.GenericItemGuid, "mdproj", null) - { - } - - public override bool CanHandleItem (SolutionEntityItem item) - { - return true; - } - - public override SolutionEntityItem LoadSolutionItem (IProgressMonitor monitor, string fileName, MSBuildFileFormat expectedFormat, string itemGuid) + public override Task<SolutionItem> CreateItem (string fileName, string typeGuid) { - MSBuildProjectHandler handler = new MSBuildProjectHandler (Guid, Import, itemGuid); - return handler.Load (monitor, fileName, expectedFormat, null, null); + return MSBuildProjectService.CreateGenericProject (fileName); } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProperty.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProperty.cs new file mode 100644 index 0000000000..0f5fa0422f --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildProperty.cs @@ -0,0 +1,259 @@ +// +// MSBuildProperty.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Xml; +using Microsoft.Build.BuildEngine; +using System.Xml.Linq; +using MonoDevelop.Core; +using System.Globalization; + + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + + public sealed class MSBuildProperty: MSBuildPropertyCore + { + bool preserverCase; + string defaultValue; + + internal MSBuildProperty (MSBuildProject project, XmlElement elem): base (project, elem) + { + } + + public string Name { + get { return Element.Name; } + } + + public bool IsImported { + get; + set; + } + + internal bool Overwritten { get; set; } + + internal MSBuildPropertyGroup Owner { get; set; } + + public bool MergeToMainGroup { get; set; } + internal bool HasDefaultValue { get; set; } + + internal MergedProperty CreateMergedProperty () + { + return new MergedProperty (Name, preserverCase, HasDefaultValue); + } + + internal void SetDefaultValue (string value) + { + defaultValue = value; + } + + public void SetValue (string value, bool preserveCase = false, bool mergeToMainGroup = false) + { + MergeToMainGroup = mergeToMainGroup; + this.preserverCase = preserveCase; + + if (value == null) + value = String.Empty; + + if (preserveCase) { + var current = GetPropertyValue (); + if (current != null) { + if (current.Equals (value, preserveCase ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture)) + return; + } + } + SetPropertyValue (value); + } + + public void SetValue (FilePath value, bool relativeToProject = true, FilePath relativeToPath = default(FilePath), bool mergeToMainGroup = false) + { + MergeToMainGroup = mergeToMainGroup; + this.preserverCase = false; + + string baseDir = null; + if (relativeToPath != null) { + baseDir = relativeToPath; + } else if (relativeToProject) { + baseDir = Project.BaseDirectory; + } + SetPropertyValue (MSBuildProjectService.ToMSBuildPath (baseDir, value, false)); + } + + public void SetValue (object value, bool mergeToMainGroup = false) + { + if (value is bool) { + if (Owner != null && Owner.UppercaseBools) + SetValue ((bool)value ? "True" : "False", preserveCase: true, mergeToMainGroup: mergeToMainGroup); + else + SetValue ((bool)value ? "true" : "false", preserveCase: true, mergeToMainGroup: mergeToMainGroup); + } + else + SetValue (Convert.ToString (value, CultureInfo.InvariantCulture), false, mergeToMainGroup); + } + + void SetPropertyValue (string value) + { + Element.InnerText = value; + } + + internal override string GetPropertyValue () + { + return Element.InnerText; + } + + public override string UnevaluatedValue { + get { + return Value; + } + } + } + + class MSBuildPropertyEvaluated: MSBuildPropertyCore + { + string value; + string evaluatedValue; + + internal MSBuildPropertyEvaluated (MSBuildProject project, string name, string value, string evaluatedValue): base (project, null) + { + this.evaluatedValue = evaluatedValue; + this.value = value; + Name = name; + } + + public string Name { get; private set; } + + public bool IsImported { get; set; } + + public override string UnevaluatedValue { + get { return value; } + } + + internal override string GetPropertyValue () + { + return evaluatedValue; + } + } + + public abstract class MSBuildPropertyCore: MSBuildObject, IMSBuildPropertyEvaluated + { + MSBuildProject project; + + internal MSBuildPropertyCore (MSBuildProject project, XmlElement elem): base (elem) + { + this.project = project; + } + + public MSBuildProject Project { + get { return project; } + } + + public string Value { + get { return GetPropertyValue (); } + } + + public T GetValue<T> () + { + var val = GetPropertyValue (); + if (typeof(T) == typeof(bool)) + return (T) (object) val.Equals ("true", StringComparison.InvariantCultureIgnoreCase); + if (typeof(T).IsEnum) + return (T) Enum.Parse (typeof(T), val, true); + return (T) Convert.ChangeType (Value, typeof(T), CultureInfo.InvariantCulture); + } + + public object GetValue (Type t) + { + var val = GetPropertyValue (); + if (t == typeof(bool)) + return (object) val.Equals ("true", StringComparison.InvariantCultureIgnoreCase); + return Convert.ChangeType (Value, t, CultureInfo.InvariantCulture); + } + + public FilePath GetPathValue (bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + var val = GetPropertyValue (); + string baseDir = null; + + if (relativeToPath != null) { + baseDir = relativeToPath; + } else if (relativeToProject) { + baseDir = project.BaseDirectory; + } + return MSBuildProjectService.FromMSBuildPath (baseDir, val); + } + + public bool TryGetPathValue (out FilePath value, bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + var val = GetPropertyValue (); + string baseDir = null; + + if (relativeToPath != null) { + baseDir = relativeToPath; + } else if (relativeToProject) { + baseDir = project.BaseDirectory; + } + string path; + var res = MSBuildProjectService.FromMSBuildPath (baseDir, val, out path); + value = path; + return res; + } + + public abstract string UnevaluatedValue { get; } + + internal abstract string GetPropertyValue (); + } + + public interface IMSBuildPropertyEvaluated + { + MSBuildProject Project { get; } + + string Value { get; } + + string UnevaluatedValue { get; } + + T GetValue<T> (); + + object GetValue (Type t); + + FilePath GetPathValue (bool relativeToProject = true, FilePath relativeToPath = default(FilePath)); + + bool TryGetPathValue (out FilePath value, bool relativeToProject = true, FilePath relativeToPath = default(FilePath)); + } + + class MergedProperty + { + public readonly string Name; + public readonly bool IsDefault; + public readonly bool PreserveExistingCase; + + public MergedProperty (string name, bool preserveExistingCase, bool isDefault) + { + this.Name = name; + IsDefault = isDefault; + this.PreserveExistingCase = preserveExistingCase; + } + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildPropertyGroup.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildPropertyGroup.cs new file mode 100644 index 0000000000..2c7fb09bd5 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildPropertyGroup.cs @@ -0,0 +1,415 @@ +// +// MSBuildPropertyGroup.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Linq; +using System.Collections.Generic; +using System.Xml; + +using MonoDevelop.Core; +using System.Xml.Linq; +using Microsoft.Build.BuildEngine; + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + public class MSBuildPropertyGroup: MSBuildObject, IMSBuildPropertySet, IMSBuildPropertyGroupEvaluated + { + Dictionary<string,MSBuildProperty> properties; + List<MSBuildProperty> propertyList = new List<MSBuildProperty> (); + MSBuildProject parent; + + public MSBuildPropertyGroup (MSBuildProject parent, XmlElement elem): base (elem) + { + this.parent = parent; + } + + void InitProperties () + { + if (properties != null) + return; + + properties = new Dictionary<string,MSBuildProperty> (); + propertyList = new List<MSBuildProperty> (); + + foreach (var pelem in Element.ChildNodes.OfType<XmlElement> ()) { + MSBuildProperty prevSameName; + if (properties.TryGetValue (pelem.Name, out prevSameName)) + prevSameName.Overwritten = true; + + var prop = new MSBuildProperty (parent, pelem); + prop.Owner = this; + propertyList.Add (prop); + properties [pelem.Name] = prop; // If a property is defined more than once, we only care about the last registered value + } + } + + public bool IsImported { + get; + set; + } + + public MSBuildProject Project { + get { + return this.parent; + } + } + + internal bool IgnoreDefaultValues { get; set; } + + internal bool UppercaseBools { get; set; } + + IMSBuildPropertyEvaluated IMSBuildPropertyGroupEvaluated.GetProperty (string name) + { + return GetProperty (name); + } + + public MSBuildProperty GetProperty (string name) + { + return GetProperty (name, null); + } + + public MSBuildProperty GetProperty (string name, string condition) + { + InitProperties (); + MSBuildProperty prop; + properties.TryGetValue (name, out prop); + return prop; + } + + public IEnumerable<MSBuildProperty> Properties { + get { + InitProperties (); + return propertyList.Where (p => !p.Overwritten); + } + } + + public string GetValue (string name, string defaultValue = null) + { + var prop = GetProperty (name); + if (prop != null) + return prop.Value; + else + return defaultValue; + } + + public FilePath GetPathValue (string name, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + var prop = GetProperty (name); + if (prop != null) + return prop.GetPathValue (relativeToProject, relativeToPath); + else + return defaultValue; + } + + public bool TryGetPathValue (string name, out FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + var prop = GetProperty (name); + if (prop != null) + return prop.TryGetPathValue (out value, relativeToProject, relativeToPath); + else { + value = defaultValue; + return value != default(FilePath); + } + } + + public T GetValue<T> (string name) + { + var prop = GetProperty (name); + if (prop != null) + return prop.GetValue<T> (); + else + return default(T); + } + + public T GetValue<T> (string name, T defaultValue) + { + var prop = GetProperty (name); + if (prop != null) + return prop.GetValue<T> (); + else + return defaultValue; + } + + public object GetValue (string name, Type type, object defaultValue) + { + var prop = GetProperty (name); + if (prop != null) + return prop.GetValue (type); + else + return defaultValue; + } + + Dictionary<Type,object> customDataObjects = new Dictionary<Type, object> (); + + public T GetObject<T> () where T:IMSBuildDataObject, new() + { + object ob; + if (!customDataObjects.TryGetValue (typeof(T), out ob)) { + customDataObjects [typeof(T)] = ob = new T (); + ((IMSBuildDataObject)ob).Read (this, parent.Format); + } + return (T)ob; + } + + public void SetObject<T> (T t, bool persist = true) where T:IMSBuildDataObject + { + customDataObjects [typeof(T)] = t; + } + + public void WriteDataObjects () + { + foreach (IMSBuildDataObject ob in customDataObjects.Values) + ob.Write (this, parent.Format); + } + + MSBuildProperty AddProperty (string name, string condition = null) + { + InitProperties (); + int i = propertyOrder.IndexOf (name); + var pelem = Element.OwnerDocument.CreateElement (null, name, MSBuildProject.Schema); + int insertIndex = -1; + if (i != -1) { + var foundProp = FindExistingProperty (i - 1, -1); + if (foundProp != null) { + Element.InsertAfter (pelem, foundProp.Element); + insertIndex = propertyList.IndexOf (foundProp) + 1; + } else { + foundProp = FindExistingProperty (i + 1, 1); + if (foundProp != null) { + Element.InsertBefore (pelem, foundProp.Element); + insertIndex = propertyList.IndexOf (foundProp) - 1; + } + else + Element.AppendChild (pelem); + } + } else + Element.AppendChild (pelem); + + var prop = new MSBuildProperty (parent, pelem); + prop.Owner = this; + properties [name] = prop; + + if (insertIndex != -1) + propertyList.Insert (insertIndex, prop); + else + propertyList.Add (prop); + + if (condition != null) + prop.Condition = condition; + return prop; + } + + MSBuildProperty FindExistingProperty (int index, int inc) + { + while (index >= 0 && index < propertyOrder.Count) { + var ep = GetProperty (propertyOrder[index]); + if (ep != null) + return ep; + index += inc; + } + return null; + } + + MSBuildProperty SafeGetProperty (string name, string condition) + { + var prop = GetProperty (name, condition); + if (prop == null) { + prop = GetProperty (name); + if (prop != null) { + prop.Condition = condition; + return prop; + } + } + return AddProperty (name, condition); + } + + public void SetValue (string name, string value, string defaultValue = null, bool preserveExistingCase = false, bool mergeToMainGroup = false, string condition = null) + { + if (value == null && defaultValue == "") + value = ""; + var prop = GetProperty (name, condition); + var isDefault = value == defaultValue; + if (isDefault && !mergeToMainGroup && !IgnoreDefaultValues) { + // if the value is default, only remove the property if it was not already the default + // to avoid unnecessary project file churn + if (prop != null && (defaultValue == null || !string.Equals (defaultValue, prop.Value, preserveExistingCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))) + RemoveProperty (prop); + return; + } + if (prop == null) + prop = AddProperty (name, condition); + prop.SetValue (value, preserveExistingCase, mergeToMainGroup); + prop.HasDefaultValue = isDefault; + } + + public void SetValue (string name, FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath), bool mergeToMainGroup = false, string condition = null) + { + var prop = GetProperty (name, condition); + var isDefault = value.CanonicalPath == defaultValue.CanonicalPath; + if (isDefault && !mergeToMainGroup && !IgnoreDefaultValues) { + // if the value is default, only remove the property if it was not already the default + // to avoid unnecessary project file churn + if (prop != null && (defaultValue == null || defaultValue != prop.GetPathValue (relativeToProject, relativeToPath))) + RemoveProperty (prop); + return; + } + if (prop == null) + prop = AddProperty (name, condition); + prop.SetValue (value, relativeToProject, relativeToPath, mergeToMainGroup); + prop.HasDefaultValue = isDefault; + } + + public void SetValue (string name, object value, object defaultValue = null, bool mergeToMainGroup = false, string condition = null) + { + var prop = GetProperty (name, condition); + var isDefault = object.Equals (value, defaultValue); + if (isDefault && !mergeToMainGroup && !IgnoreDefaultValues) { + // if the value is default, only remove the property if it was not already the default + // to avoid unnecessary project file churn + if (prop != null && (defaultValue == null || !object.Equals (defaultValue, prop.GetValue (defaultValue.GetType ())))) + RemoveProperty (prop); + return; + } + if (prop == null) + prop = AddProperty (name, condition); + prop.SetValue (value, mergeToMainGroup); + prop.HasDefaultValue = isDefault; + } + + public bool RemoveProperty (string name) + { + MSBuildProperty prop = GetProperty (name); + if (prop != null) { + RemoveProperty (prop); + return true; + } + return false; + } + + public void RemoveProperty (MSBuildProperty prop) + { + InitProperties (); + properties.Remove (prop.Name); + propertyList.Remove (prop); + Element.RemoveChild (prop.Element); + } + + public void RemoveAllProperties () + { + InitProperties (); + List<XmlNode> toDelete = new List<XmlNode> (); + foreach (XmlNode node in Element.ChildNodes) { + if (node is XmlElement) + toDelete.Add (node); + } + foreach (XmlNode node in toDelete) + Element.RemoveChild (node); + properties.Clear (); + propertyList.Clear (); + } + + public void UnMerge (IMSBuildPropertySet baseGrp, ISet<string> propsToExclude) + { + HashSet<string> baseProps = new HashSet<string> (); + foreach (MSBuildProperty prop in baseGrp.Properties) { + if (propsToExclude != null && propsToExclude.Contains (prop.Name)) + continue; + baseProps.Add (prop.Name); + MSBuildProperty thisProp = GetProperty (prop.Name); + if (thisProp != null && prop.Value.Equals (thisProp.Value, StringComparison.OrdinalIgnoreCase)) + RemoveProperty (prop.Name); + } + + // Remove properties which have the default value and which are not defined in the main group + foreach (var p in Properties.ToArray ()) { + if (baseProps.Contains (p.Name)) + continue; + if (p.HasDefaultValue) + RemoveProperty (p); + } + } + + public override string ToString() + { + string s = "[MSBuildPropertyGroup:"; + foreach (MSBuildProperty prop in Properties) + s += " " + prop.Name + "=" + prop.Value; + return s + "]"; + } + + public void SetObject<T> (T t) where T : IMSBuildDataObject + { + throw new NotImplementedException (); + } + + public bool HasProperty (string name) + { + InitProperties (); + return properties.ContainsKey (name); + } + + List<string> propertyOrder = new List<string> (); + + public void SetPropertyOrder (params string[] propertyNames) + { + int i = 0; + foreach (var name in propertyNames) { + if (i < propertyOrder.Count) { + var pos = propertyOrder.IndexOf (name, i); + if (pos != -1) { + i = pos + 1; + continue; + } else { + propertyOrder.Insert (i, name); + i++; + continue; + } + } + propertyOrder.Add (name); + i++; + } + } + } + + public interface IMSBuildPropertyGroupEvaluated + { + bool HasProperty (string name); + + IMSBuildPropertyEvaluated GetProperty (string name); + + string GetValue (string name, string defaultValue = null); + + FilePath GetPathValue (string name, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)); + + bool TryGetPathValue (string name, out FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)); + + T GetValue<T> (string name); + + T GetValue<T> (string name, T defaultValue); + + object GetValue (string name, Type type, object defaultValue); + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildPropertyGroupEvaluated.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildPropertyGroupEvaluated.cs new file mode 100644 index 0000000000..2ffc9d9d1b --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildPropertyGroupEvaluated.cs @@ -0,0 +1,155 @@ +// +// MSBuildPropertyGroupEvaluated.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Xml.Linq; +using MonoDevelop.Core; +using Microsoft.Build.BuildEngine; +using MSProject = Microsoft.Build.BuildEngine.Project; + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + class MSBuildPropertyGroupEvaluated: IMSBuildPropertyGroupEvaluated + { + internal Dictionary<string,MSBuildPropertyEvaluated> properties = new Dictionary<string, MSBuildPropertyEvaluated> (); + internal MSBuildProject parent; + BuildItem sourceItem; + + internal MSBuildPropertyGroupEvaluated (MSBuildProject parent) + { + this.parent = parent; + } + + internal void Sync (BuildItem item) + { + properties.Clear (); + sourceItem = item; + } + + public bool HasProperty (string name) + { + if (!properties.ContainsKey (name)) { + if (sourceItem != null) + return sourceItem.HasMetadata (name); + } + return false; + } + + public IMSBuildPropertyEvaluated GetProperty (string name) + { + MSBuildPropertyEvaluated prop; + if (!properties.TryGetValue (name, out prop)) { + if (sourceItem != null) { + if (sourceItem.HasMetadata (name)) { + prop = new MSBuildPropertyEvaluated (parent, name, sourceItem.GetMetadata (name), sourceItem.GetEvaluatedMetadata (name)); + properties [name] = prop; + } + } + } + return prop; + } + + public string GetValue (string name, string defaultValue = null) + { + var prop = GetProperty (name); + if (prop != null) + return prop.Value; + else + return defaultValue; + } + + public FilePath GetPathValue (string name, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + var prop = GetProperty (name); + if (prop != null) + return prop.GetPathValue (relativeToProject, relativeToPath); + else + return defaultValue; + } + + public bool TryGetPathValue (string name, out FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + var prop = GetProperty (name); + if (prop != null) + return prop.TryGetPathValue (out value, relativeToProject, relativeToPath); + else { + value = defaultValue; + return value != default(FilePath); + } + } + + public T GetValue<T> (string name) + { + var prop = GetProperty (name); + if (prop != null) + return prop.GetValue<T> (); + else + return default(T); + } + + public T GetValue<T> (string name, T defaultValue) + { + var prop = GetProperty (name); + if (prop != null) + return prop.GetValue<T> (); + else + return defaultValue; + } + + public object GetValue (string name, Type type, object defaultValue) + { + var prop = GetProperty (name); + if (prop != null) + return prop.GetValue (type); + else + return defaultValue; + } + } + + class MSBuildEvaluatedPropertyCollection: MSBuildPropertyGroupEvaluated, IMSBuildEvaluatedPropertyCollection + { + public MSBuildEvaluatedPropertyCollection (MSBuildProject parent): base (parent) + { + } + + internal void Sync (BuildPropertyGroup msgroup) + { + properties.Clear (); + foreach (BuildProperty p in msgroup) + properties [p.Name] = new MSBuildPropertyEvaluated (parent, p.Name, p.Value, p.FinalValue); + } + + public IEnumerable<IMSBuildPropertyEvaluated> Properties { + get { return properties.Values; } + } + } + + public interface IMSBuildEvaluatedPropertyCollection: IMSBuildPropertyGroupEvaluated + { + IEnumerable<IMSBuildPropertyEvaluated> Properties { get; } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildPropertyGroupMerged.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildPropertyGroupMerged.cs new file mode 100644 index 0000000000..11117a97e6 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildPropertyGroupMerged.cs @@ -0,0 +1,234 @@ +// +// MSBuildPropertyGroupMerged.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Linq; +using System.Collections.Generic; +using System.Xml; + +using MonoDevelop.Core; +using System.Xml.Linq; + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + + class MSBuildPropertyGroupMerged: IMSBuildPropertySet + { + List<IMSBuildPropertySet> groups = new List<IMSBuildPropertySet> (); + MSBuildFileFormat format; + + public MSBuildPropertyGroupMerged (MSBuildProject project, MSBuildFileFormat format) + { + this.format = format; + Project = project; + } + + public MSBuildProject Project { get; private set; } + + public string Label { get; set; } + + public void Add (IMSBuildPropertySet g) + { + groups.Add (g); + } + + public int GroupCount { + get { return groups.Count; } + } + + public MSBuildProperty GetProperty (string name) + { + // Find property in reverse order, since the last set + // value is the good one + for (int n=groups.Count - 1; n >= 0; n--) { + var g = groups [n]; + MSBuildProperty p = g.GetProperty (name); + if (p != null) + return p; + } + return null; + } + + public IMSBuildPropertySet GetGroupForProperty (string name) + { + // Find property in reverse order, since the last set + // value is the good one + for (int n=groups.Count - 1; n >= 1; n--) { + var g = groups [n]; + MSBuildProperty p = g.GetProperty (name); + if (p != null) + return g; + } + return groups[0]; + } + + Dictionary<Type,object> customDataObjects = new Dictionary<Type, object> (); + + public T GetObject<T> () where T:IMSBuildDataObject, new() + { + object ob; + if (!customDataObjects.TryGetValue (typeof(T), out ob)) { + customDataObjects [typeof(T)] = ob = new T (); + ((IMSBuildDataObject)ob).Read (this, format); + } + return (T)ob; + } + + public void SetObject<T> (T t, bool persist = true) where T:IMSBuildDataObject + { + customDataObjects [typeof(T)] = t; + } + + public void WriteDataObjects () + { + foreach (IMSBuildDataObject ob in customDataObjects.Values) + ob.Write (this, format); + } + + public void SetPropertyValue (string name, string value, bool preserveExistingCase) + { + MSBuildProperty p = GetProperty (name); + if (p != null) { + if (!preserveExistingCase || !string.Equals (value, p.Value, StringComparison.OrdinalIgnoreCase)) { + p.SetValue (value); + } + return; + } + groups [0].SetValue (name, value, preserveExistingCase:preserveExistingCase); + } + + public void SetValue (string name, string value, string defaultValue = null, bool preserveExistingCase = false, bool mergeToMainGroup = false, string condition = null) + { + GetGroupForProperty (name).SetValue (name, value, defaultValue, preserveExistingCase, mergeToMainGroup, condition); + } + + public void SetValue (string name, FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath), bool mergeToMainGroup = false, string condition = null) + { + GetGroupForProperty (name).SetValue (name, value, defaultValue, relativeToProject, relativeToPath, mergeToMainGroup, condition); + } + + public void SetValue (string name, object value, object defaultValue = null, bool mergeToMainGroup = false, string condition = null) + { + GetGroupForProperty (name).SetValue (name, value, defaultValue, mergeToMainGroup, condition); + } + + public string GetValue (string name, string defaultValue = null) + { + return GetGroupForProperty (name).GetValue (name, defaultValue); + } + + public string GetPathValue (string name, FilePath defaultValue = default(FilePath), bool relativeToFile = true) + { + return GetGroupForProperty (name).GetPathValue (name, defaultValue, relativeToFile); + } + + public T GetValue<T> (string name) + { + return GetGroupForProperty (name).GetValue<T> (name); + } + + public T GetValue<T> (string name, T defaultValue) + { + return GetGroupForProperty (name).GetValue<T> (name, defaultValue); + } + + public object GetValue (string name, Type type, object defaultValue) + { + return GetGroupForProperty (name).GetValue (name, type, defaultValue); + } + + public bool RemoveProperty (string name) + { + bool found = false; + foreach (var g in groups) { + if (g.RemoveProperty (name)) { + Prune ((MSBuildPropertyGroup)g); + found = true; + } + } + return found; + } + + public void RemoveAllProperties () + { + foreach (var g in groups) { + g.RemoveAllProperties (); + Prune ((MSBuildPropertyGroup)g); + } + } + + public void UnMerge (IMSBuildPropertySet baseGrp, ISet<string> propertiesToExclude) + { + foreach (var g in groups) { + ((MSBuildPropertyGroup)g).UnMerge (baseGrp, propertiesToExclude); + } + } + + public IEnumerable<MSBuildProperty> Properties { + get { + foreach (var g in groups) { + foreach (var p in g.Properties) + yield return p; + } + } + } + + void Prune (MSBuildPropertyGroup g) + { + if (g != groups [0] && !g.Properties.Any()) { + // Remove this group since it's now empty + g.Project.RemoveGroup (g); + } + } + + public void SetObject<T> (T t) where T : IMSBuildDataObject + { + throw new NotImplementedException (); + } + + public bool HasProperty (string name) + { + throw new NotImplementedException (); + } + + public FilePath GetPathValue (string name, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + return GetGroupForProperty (name).GetPathValue (name, defaultValue, relativeToProject, relativeToPath); + } + + public bool TryGetPathValue (string name, out FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, FilePath relativeToPath = default(FilePath)) + { + return GetGroupForProperty (name).TryGetPathValue (name, out value, defaultValue, relativeToProject, relativeToPath); + } + + public void SetPropertyOrder (params string[] propertyNames) + { + foreach (var g in groups) + g.SetPropertyOrder (propertyNames); + } + } + +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/RemoteProjectBuilder.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/RemoteProjectBuilder.cs index e5d99fb724..d2cefb444b 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/RemoteProjectBuilder.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/RemoteProjectBuilder.cs @@ -30,6 +30,9 @@ using System.Globalization; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MonoDevelop.Core; +using System.IO; +using System.Linq; namespace MonoDevelop.Projects.Formats.MSBuild { @@ -37,6 +40,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild { IBuildEngine engine; Process proc; + bool alive = true; public int ReferenceCount { get; set; } public DateTime ReleaseTime { get; set; } @@ -47,36 +51,92 @@ namespace MonoDevelop.Projects.Formats.MSBuild this.engine = engine; } + public event EventHandler Disconnected; + public IProjectBuilder LoadProject (string projectFile) { - return engine.LoadProject (projectFile); + try { + return engine.LoadProject (projectFile); + } catch { + CheckDisconnected (); + throw; + } } public void UnloadProject (IProjectBuilder pb) { - engine.UnloadProject (pb); + try { + engine.UnloadProject (pb); + } catch (Exception ex) { + LoggingService.LogError ("Project unloading failed", ex); + if (!CheckDisconnected ()) + throw; + } } public void SetCulture (CultureInfo uiCulture) { - engine.SetCulture (uiCulture); + try { + engine.SetCulture (uiCulture); + } catch { + CheckDisconnected (); + throw; + } } public void SetGlobalProperties (IDictionary<string, string> properties) { - engine.SetGlobalProperties (properties); + try { + engine.SetGlobalProperties (properties); + } catch { + CheckDisconnected (); + throw; + } + } + + void IBuildEngine.Ping () + { + engine.Ping (); } - + + bool CheckAlive () + { + if (!alive) + return false; + try { + engine.Ping (); + return true; + } catch { + alive = false; + return false; + } + } + + internal bool CheckDisconnected () + { + if (!CheckAlive ()) { + if (Disconnected != null) + Disconnected (this, EventArgs.Empty); + return true; + } + return false; + } + public void Dispose () { - if (proc != null) { - try { - proc.Kill (); - } catch { + try { + alive = false; + if (proc != null) { + try { + proc.Kill (); + } catch { + } } + else + engine.Dispose (); + } catch { + // Ignore } - else - engine.Dispose (); } } @@ -84,11 +144,25 @@ namespace MonoDevelop.Projects.Formats.MSBuild { RemoteBuildEngine engine; IProjectBuilder builder; - + Dictionary<string,string[]> referenceCache; + string file; + internal RemoteProjectBuilder (string file, RemoteBuildEngine engine) { + this.file = file; this.engine = engine; builder = engine.LoadProject (file); + referenceCache = new Dictionary<string, string[]> (); + } + + public event EventHandler Disconnected; + + void CheckDisconnected () + { + if (engine.CheckDisconnected ()) { + if (Disconnected != null) + Disconnected (this, EventArgs.Empty); + } } public MSBuildResult Run ( @@ -99,17 +173,70 @@ namespace MonoDevelop.Projects.Formats.MSBuild string[] evaluateItems, string[] evaluateProperties) { - return builder.Run (configurations, logWriter, verbosity, runTargets, evaluateItems, evaluateProperties); + try { + return builder.Run (configurations, logWriter, verbosity, runTargets, evaluateItems, evaluateProperties); + } catch (Exception ex) { + CheckDisconnected (); + LoggingService.LogError ("RunTarget failed", ex); + MSBuildTargetResult err = new MSBuildTargetResult (file, false, "", "", file, 1, 1, 1, 1, "Unknown MSBuild failure. Please try building the project again", ""); + MSBuildResult res = new MSBuildResult (new [] { err }); + return res; + } } - + + public string[] ResolveAssemblyReferences (ProjectConfigurationInfo[] configurations) + { + string[] refs = null; + var id = configurations [0].Configuration + "|" + configurations [0].Platform; + + lock (referenceCache) { + if (!referenceCache.TryGetValue (id, out refs)) { + MSBuildResult result; + try { + result = builder.Run ( + configurations, null, MSBuildVerbosity.Normal, + new[] { "ResolveAssemblyReferences" }, new [] { "ReferencePath" }, null + ); + } catch (Exception ex) { + CheckDisconnected (); + LoggingService.LogError ("ResolveAssemblyReferences failed", ex); + return new string[0]; + } + + List<MSBuildEvaluatedItem> items; + if (result.Items.TryGetValue ("ReferencePath", out items) && items != null) { + refs = items.Select (i => i.ItemSpec).ToArray (); + } else + refs = new string[0]; + + referenceCache [id] = refs; + } + } + return refs; + } + public void Refresh () { - builder.Refresh (); + lock (referenceCache) + referenceCache.Clear (); + try { + builder.Refresh (); + } catch (Exception ex) { + LoggingService.LogError ("MSBuild refresh failed", ex); + CheckDisconnected (); + } } public void RefreshWithContent (string projectContent) { - builder.RefreshWithContent (projectContent); + lock (referenceCache) + referenceCache.Clear (); + try { + builder.RefreshWithContent (projectContent); + } catch (Exception ex) { + LoggingService.LogError ("MSBuild refresh failed", ex); + CheckDisconnected (); + } } public void Dispose () diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/SlnData.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/SlnData.cs deleted file mode 100644 index 9f602a5dff..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/SlnData.cs +++ /dev/null @@ -1,141 +0,0 @@ -// -// SlnData.cs -// -// Author: -// Ankit Jain <jankit@novell.com> -// -// Copyright (C) 2006 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 MonoDevelop.Projects; - -using System; -using System.Collections.Generic; - -namespace MonoDevelop.Projects.Formats.MSBuild -{ - class SlnData - { - string headerComment = "# MonoDevelop"; - Dictionary<SolutionConfiguration, string> configStrings; - List<string> globalExtra; // unused GlobalSections - Dictionary<string, List<string>> sectionExtras; - string[] extra; //used by solution folders.. - List<string> unknownProjects; - Dictionary<string, SolutionEntityItem> projectsByGuidTable; - - public void UpdateVersion (MSBuildFileFormat format) - { - VersionString = format.SlnVersion; - headerComment = "# " + format.ProductDescription; - } - - // Eg. "# Visual C# Express 2008" - public string HeaderComment { - get { return headerComment; } - set { headerComment = value; } - } - - // Eg. 9.00 or 10.00 - public string VersionString { get; set; } - - public Version VisualStudioVersion { get; set; } - - public Version MinimumVisualStudioVersion { get; set; } - - public Dictionary<SolutionConfiguration, string> ConfigStrings { - get { - if (configStrings == null) - configStrings = new Dictionary<SolutionConfiguration, string> (); - return configStrings; - } - } - - public List<string> GlobalExtra { - get { return globalExtra; } - set { globalExtra = value; } - } - - public string[] Extra { - get { return extra; } - set { extra = value; } - } - - public List<string> UnknownProjects { - get { - if (unknownProjects == null) - unknownProjects = new List<string> (); - return unknownProjects; - } - } - - //Extra lines per section which need to be preserved - //eg. lines in ProjectConfigurationPlatforms for projects - //that we couldn't load - public Dictionary<string, List<string>> SectionExtras { - get { - if (sectionExtras == null) - sectionExtras = new Dictionary<string, List<string>> (); - return sectionExtras; - } - } - - public Dictionary<string, SolutionEntityItem> ItemsByGuid { - get { - if (projectsByGuidTable == null) - projectsByGuidTable = new Dictionary<string, SolutionEntityItem> (); - return projectsByGuidTable; - } - } - - } - - class ItemSlnData - { - List<string> configLines; - - public List<string> ConfigLines { - get { - if (configLines == null) - configLines = new List<string> (); - return configLines; - } - } - - public static ItemSlnData ForItem (SolutionItem item) - { - ItemSlnData data = (ItemSlnData) item.ExtendedProperties [typeof(ItemSlnData)]; - if (data == null) { - data = new ItemSlnData (); - item.ExtendedProperties [typeof(ItemSlnData)] = data; - } - return data; - } - - public static void TransferData (SolutionItem source, SolutionItem target) - { - ItemSlnData data = (ItemSlnData) source.ExtendedProperties [typeof(ItemSlnData)]; - if (data != null) - target.ExtendedProperties [typeof(ItemSlnData)] = data; - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/SlnFile.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/SlnFile.cs new file mode 100644 index 0000000000..ba9c875b75 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/SlnFile.cs @@ -0,0 +1,581 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.IO; +using System.Collections; +using MonoDevelop.Core; + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + public class SlnFile + { + SlnProjectCollection projects = new SlnProjectCollection (); + SlnSectionCollection sections = new SlnSectionCollection (); + SlnPropertySet metadata = new SlnPropertySet (true); + + public string FormatVersion { get; set; } + public string ProductDescription { get; set; } + + public string VisualStudioVersion { + get { return metadata.GetValue ("VisualStudioVersion"); } + set { metadata.SetValue ("VisualStudioVersion", value); } + } + + public string MinimumVisualStudioVersion { + get { return metadata.GetValue ("MinimumVisualStudioVersion"); } + set { metadata.SetValue ("MinimumVisualStudioVersion", value); } + } + + public SlnFile () + { + } + + /// <summary> + /// The directory to be used as base for converting absolute paths to relative + /// </summary> + public FilePath BaseDirectory { + get; + set; + } + + public SlnPropertySet SolutionConfigurationsSection { + get { return sections.GetOrCreateSection ("SolutionConfigurationPlatforms", "preSolution").Properties; } + } + + public SlnPropertySetCollection ProjectConfigurationsSection { + get { return sections.GetOrCreateSection ("ProjectConfigurationPlatforms", "postSolution").NestedPropertySets; } + } + + public SlnSectionCollection Sections { + get { return sections; } + } + + public SlnProjectCollection Projects { + get { return projects; } + } + + public void Read (string file) + { + using (var sr = new StreamReader (file)) + Read (sr); + } + + public void Read (TextReader reader) + { + string line; + int curLineNum = 0; + bool globalFound = false; + bool productRead = false; + + while ((line = reader.ReadLine ()) != null) { + curLineNum++; + line = line.Trim (); + if (line.StartsWith ("Microsoft Visual Studio Solution File")) { + int i = line.LastIndexOf (' '); + if (i == -1) + throw new InvalidSolutionFormatException (curLineNum); + FormatVersion = line.Substring (i + 1); + } + if (line.StartsWith ("# ")) { + if (!productRead) { + productRead = true; + ProductDescription = line.Substring (2); + } + } else if (line.StartsWith ("Project")) { + SlnProject p = new SlnProject (); + p.Read (reader, line, ref curLineNum); + projects.Add (p); + } else if (line == "Global") { + if (globalFound) + throw new InvalidSolutionFormatException (curLineNum, "Global section specified more than once"); + globalFound = true; + while ((line = reader.ReadLine ()) != null) { + curLineNum++; + line = line.Trim (); + if (line == "EndGlobal") { + break; + } else if (line.StartsWith ("GlobalSection")) { + var sec = new SlnSection (); + sec.Read (reader, line, ref curLineNum); + sections.Add (sec); + } else + throw new InvalidSolutionFormatException (curLineNum); + } + if (line == null) + throw new InvalidSolutionFormatException (curLineNum, "Global section not closed"); + } else if (line.IndexOf ('=') != -1) { + metadata.ReadLine (line, curLineNum); + } + } + if (FormatVersion == null) + throw new InvalidSolutionFormatException (curLineNum, "File header is missing"); + } + + public void Write (string file) + { + using (var sw = new StreamWriter (file)) + Write (sw); + } + + public void Write (TextWriter writer) + { + writer.NewLine = "\r\n"; + writer.WriteLine ("\r\nMicrosoft Visual Studio Solution File, Format Version " + FormatVersion); + writer.WriteLine ("# " + ProductDescription); + + metadata.Write (writer); + + foreach (var p in projects) + p.Write (writer); + + writer.WriteLine ("Global"); + foreach (SlnSection s in sections) + s.Write (writer, "GlobalSection"); + writer.WriteLine ("EndGlobal"); + } + } + + public class SlnProject + { + SlnSectionCollection sections = new SlnSectionCollection (); + + public string Id { get; set; } + public string TypeGuid { get; set; } + public string Name { get; set; } + public string FilePath { get; set; } + public int Line { get; private set; } + internal bool Processed { get; set; } + + public SlnSectionCollection Sections { + get { return sections; } + } + + internal void Read (TextReader reader, string line, ref int curLineNum) + { + Line = curLineNum; + + int n = 0; + FindNext (curLineNum, line, ref n, '('); + n++; + FindNext (curLineNum, line, ref n, '"'); + int n2 = n + 1; + FindNext (curLineNum, line, ref n2, '"'); + TypeGuid = line.Substring (n + 1, n2 - n - 1); + + n = n2 + 1; + FindNext (curLineNum, line, ref n, ')'); + FindNext (curLineNum, line, ref n, '='); + + FindNext (curLineNum, line, ref n, '"'); + n2 = n + 1; + FindNext (curLineNum, line, ref n2, '"'); + Name = line.Substring (n + 1, n2 - n - 1); + + n = n2 + 1; + FindNext (curLineNum, line, ref n, ','); + FindNext (curLineNum, line, ref n, '"'); + n2 = n + 1; + FindNext (curLineNum, line, ref n2, '"'); + FilePath = line.Substring (n + 1, n2 - n - 1); + + n = n2 + 1; + FindNext (curLineNum, line, ref n, ','); + FindNext (curLineNum, line, ref n, '"'); + n2 = n + 1; + FindNext (curLineNum, line, ref n2, '"'); + Id = line.Substring (n + 1, n2 - n - 1); + + while ((line = reader.ReadLine ()) != null) { + curLineNum++; + line = line.Trim (); + if (line == "EndProject") { + return; + } + if (line.StartsWith ("ProjectSection")) { + if (sections == null) + sections = new SlnSectionCollection (); + var sec = new SlnSection (); + sections.Add (sec); + sec.Read (reader, line, ref curLineNum); + } + } + + throw new InvalidSolutionFormatException (curLineNum, "Project section not closed"); + } + + void FindNext (int ln, string line, ref int i, char c) + { + i = line.IndexOf (c, i); + if (i == -1) + throw new InvalidSolutionFormatException (ln); + } + + public void Write (TextWriter writer) + { + writer.Write ("Project(\""); + writer.Write (TypeGuid); + writer.Write ("\") = \""); + writer.Write (Name); + writer.Write ("\", \""); + writer.Write (FilePath); + writer.Write ("\", \""); + writer.Write (Id); + writer.WriteLine ("\""); + if (sections != null) { + foreach (SlnSection s in sections) + s.Write (writer, "ProjectSection"); + } + writer.WriteLine ("EndProject"); + } + } + + public class SlnSection + { + SlnPropertySetCollection nestedPropertySets; + SlnPropertySet properties; + List<string> sectionLines; + int baseIndex; + + public string Id { get; set; } + public int Line { get; private set; } + internal bool Processed { get; set; } + + public SlnPropertySet Properties { + get { + if (properties == null) { + properties = new SlnPropertySet (); + if (sectionLines != null) { + foreach (var line in sectionLines) + properties.ReadLine (line, Line); + sectionLines = null; + } + } + return properties; + } + } + + public SlnPropertySetCollection NestedPropertySets { + get { + if (nestedPropertySets == null) { + nestedPropertySets = new SlnPropertySetCollection (); + if (sectionLines != null) + LoadPropertySets (); + } + return nestedPropertySets; + } + } + + public string SectionType { get; set; } + + internal void Read (TextReader reader, string line, ref int curLineNum) + { + Line = curLineNum; + int k = line.IndexOf ('('); + if (k == -1) + throw new InvalidSolutionFormatException (curLineNum, "Section id missing"); + var tag = line.Substring (0, k).Trim (); + var k2 = line.IndexOf (')', k); + if (k2 == -1) + throw new InvalidSolutionFormatException (curLineNum); + Id = line.Substring (k + 1, k2 - k - 1); + + k = line.IndexOf ('=', k2); + SectionType = line.Substring (k + 1).Trim (); + + var endTag = "End" + tag; + + sectionLines = new List<string> (); + baseIndex = ++curLineNum; + while ((line = reader.ReadLine()) != null) { + curLineNum++; + line = line.Trim (); + if (line == endTag) + break; + sectionLines.Add (line); + } + if (line == null) + throw new InvalidSolutionFormatException (curLineNum, "Closing section tag not found"); + } + + void LoadPropertySets () + { + if (sectionLines != null) { + SlnPropertySet curSet = null; + for (int n = 0; n < sectionLines.Count; n++) { + var line = sectionLines [n]; + var i = line.IndexOf ('.'); + if (i == -1) + throw new InvalidSolutionFormatException (baseIndex + i); + var id = line.Substring (0, i); + if (curSet == null || id != curSet.Id) { + curSet = new SlnPropertySet (id); + nestedPropertySets.Add (curSet); + } + curSet.ReadLine (line.Substring (i + 1), baseIndex + i); + } + sectionLines = null; + } + } + + internal void Write (TextWriter writer, string sectionTag) + { + writer.Write ("\t"); + writer.Write (sectionTag); + writer.Write ('('); + writer.Write (Id); + writer.Write (") = "); + writer.WriteLine (SectionType); + if (sectionLines != null) { + foreach (var l in sectionLines) + writer.WriteLine ("\t\t" + l); + } else if (properties != null) + properties.Write (writer); + else if (nestedPropertySets != null) { + foreach (var ps in nestedPropertySets) + ps.Write (writer); + } + writer.WriteLine ("\tEnd" + sectionTag); + } + } + + public class SlnPropertySet: IDictionary<string,string> + { + OrderedDictionary values = new OrderedDictionary (); + bool isMetadata; + + internal bool Processed { get; set; } + public int Line { get; private set; } + + public SlnPropertySet () + { + } + + public SlnPropertySet (string id) + { + Id = id; + } + + internal SlnPropertySet (bool isMetadata) + { + this.isMetadata = isMetadata; + } + + internal void ReadLine (string line, int currentLine) + { + if (Line == 0) + Line = currentLine; + line = line.Trim (); + int k = line.IndexOf ('='); + if (k != -1) { + var name = line.Substring (0, k).Trim (); + var val = line.Substring (k + 1).Trim (); + values.Add (name, val); + } else { + values.Add (line, null); + } + } + + internal void Write (TextWriter writer) + { + foreach (DictionaryEntry e in values) { + if (!isMetadata) + writer.Write ("\t\t"); + if (Id != null) + writer.Write (Id + "."); + writer.WriteLine (e.Key + " = " + e.Value); + } + } + + public string Id { get; private set; } + + public string GetValue (string key) + { + return (string) values [key]; + } + + public void SetValue (string key, string value) + { + values [key] = value; + } + + public void Add (string key, string value) + { + SetValue (key, value); + } + + public bool ContainsKey (string key) + { + return values.Contains (key); + } + + public bool Remove (string key) + { + var wasThere = values.Contains (key); + values.Remove (key); + return wasThere; + } + + public bool TryGetValue (string key, out string value) + { + value = (string) values [key]; + return value != null; + } + + public string this [string index] { + get { + return (string) values [index]; + } + set { + values [index] = value; + } + } + + public ICollection<string> Values { + get { + return values.Values.Cast<string>().ToList (); + } + } + + public ICollection<string> Keys { + get { return values.Keys.Cast<string> ().ToList (); } + } + + public void Add (KeyValuePair<string, string> item) + { + SetValue (item.Key, item.Value); + } + + public void Clear () + { + values.Clear (); + } + + internal void ClearExcept (HashSet<string> keys) + { + foreach (var k in values.Keys.Cast<string>().Except (keys).ToArray ()) + values.Remove (k); + } + + public bool Contains (KeyValuePair<string, string> item) + { + var val = GetValue (item.Key); + return val == item.Value; + } + + public void CopyTo (KeyValuePair<string, string>[] array, int arrayIndex) + { + foreach (DictionaryEntry de in values) + array [arrayIndex++] = new KeyValuePair<string, string> ((string)de.Key, (string)de.Value); + } + + public bool Remove (KeyValuePair<string, string> item) + { + if (Contains (item)) { + Remove (item.Key); + return true; + } else + return false; + } + + public int Count { + get { + return values.Count; + } + } + + public bool IsReadOnly { + get { + return false; + } + } + + public IEnumerator<KeyValuePair<string, string>> GetEnumerator () + { + foreach (DictionaryEntry de in values) + yield return new KeyValuePair<string,string> ((string)de.Key, (string)de.Value); + } + + IEnumerator IEnumerable.GetEnumerator () + { + foreach (DictionaryEntry de in values) + yield return new KeyValuePair<string,string> ((string)de.Key, (string)de.Value); + } + } + + public class SlnProjectCollection: Collection<SlnProject> + { + public SlnProject GetProject (string id) + { + return this.FirstOrDefault (s => s.Id == id); + } + + public SlnProject GetOrCreateProject (string id) + { + var p = this.FirstOrDefault (s => s.Id.Equals (id, StringComparison.OrdinalIgnoreCase)); + if (p == null) { + p = new SlnProject { Id = id }; + Add (p); + } + return p; + } + } + + public class SlnSectionCollection: Collection<SlnSection> + { + public SlnSection GetSection (string id) + { + return this.FirstOrDefault (s => s.Id == id); + } + + public SlnSection GetOrCreateSection (string id, string sectionType) + { + var sec = this.FirstOrDefault (s => s.Id == id); + if (sec == null) { + sec = new SlnSection { Id = id }; + sec.SectionType = sectionType; + Add (sec); + } + return sec; + } + + public void RemoveSection (string id) + { + var s = GetSection (id); + if (s != null) + Remove (s); + } + } + + public class SlnPropertySetCollection: Collection<SlnPropertySet> + { + public SlnPropertySet GetPropertySet (string id, bool ignoreCase = false) + { + var sc = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + return this.FirstOrDefault (s => s.Id.Equals (id, sc)); + } + + public SlnPropertySet GetOrCreatePropertySet (string id, bool ignoreCase = false) + { + var ps = GetPropertySet (id, ignoreCase); + if (ps == null) { + ps = new SlnPropertySet (id); + Add (ps); + } + return ps; + } + } + + class InvalidSolutionFormatException: Exception + { + public InvalidSolutionFormatException (int line): base ("Invalid format in line " + line) + { + } + + public InvalidSolutionFormatException (int line, string msg): base ("Invalid format in line " + line + ": " + msg) + { + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/SlnFileFormat.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/SlnFileFormat.cs index 27baad33c5..f1f4fb5522 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/SlnFileFormat.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/SlnFileFormat.cs @@ -27,10 +27,7 @@ // using System; -using System.Collections; using System.Collections.Generic; -using System.Collections.Specialized; -using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; @@ -41,11 +38,19 @@ using MonoDevelop.Projects.Extensions; using MonoDevelop.Core; using System.Reflection; using System.Linq; +using System.Threading.Tasks; namespace MonoDevelop.Projects.Formats.MSBuild { - public class SlnFileFormat + class SlnFileFormat { + MSBuildFileFormat format; + + public SlnFileFormat (MSBuildFileFormat format) + { + this.format = format; + } + public string GetValidFormatName (object obj, string fileName, MSBuildFileFormat format) { return Path.ChangeExtension (fileName, ".sln"); @@ -71,372 +76,299 @@ namespace MonoDevelop.Projects.Formats.MSBuild return null; } - public void WriteFile (string file, object obj, MSBuildFileFormat format, bool saveProjects, IProgressMonitor monitor) + public Task WriteFile (string file, object obj, bool saveProjects, ProgressMonitor monitor) { - Solution sol = (Solution) obj; + return Task.Factory.StartNew (delegate { + Solution sol = (Solution)obj; - string tmpfilename = String.Empty; - try { - monitor.BeginTask (GettextCatalog.GetString ("Saving solution: {0}", file), 1); + string tmpfilename = String.Empty; try { - if (File.Exists (file)) - tmpfilename = Path.GetTempFileName (); - } catch (IOException) { - } + monitor.BeginTask (GettextCatalog.GetString ("Saving solution: {0}", file), 1); + try { + if (File.Exists (file)) + tmpfilename = Path.GetTempFileName (); + } catch (IOException) { + } - string baseDir = Path.GetDirectoryName (file); - if (tmpfilename == String.Empty) { - WriteFileInternal (file, sol, baseDir, format, saveProjects, monitor); - } else { - WriteFileInternal (tmpfilename, sol, baseDir, format, saveProjects, monitor); - File.Delete (file); - File.Move (tmpfilename, file); + if (tmpfilename == String.Empty) { + WriteFileInternal (file, file, sol, saveProjects, monitor); + } else { + WriteFileInternal (tmpfilename, file, sol, saveProjects, monitor); + File.Delete (file); + File.Move (tmpfilename, file); + } + } catch (Exception ex) { + monitor.ReportError (GettextCatalog.GetString ("Could not save solution: {0}", file), ex); + LoggingService.LogError (GettextCatalog.GetString ("Could not save solution: {0}", file), ex); + + if (!String.IsNullOrEmpty (tmpfilename)) + File.Delete (tmpfilename); + throw; + } finally { + monitor.EndTask (); } - } catch (Exception ex) { - monitor.ReportError (GettextCatalog.GetString ("Could not save solution: {0}", file), ex); - LoggingService.LogError (GettextCatalog.GetString ("Could not save solution: {0}", file), ex); - - if (!String.IsNullOrEmpty (tmpfilename)) - File.Delete (tmpfilename); - throw; - } finally { - monitor.EndTask (); - } + }); } - void WriteFileInternal (string file, Solution solution, string baseDir, MSBuildFileFormat format, bool saveProjects, IProgressMonitor monitor) + void WriteFileInternal (string file, string sourceFile, Solution solution, bool saveProjects, ProgressMonitor monitor) { - SolutionFolder c = solution.RootFolder; - - using (StreamWriter sw = new StreamWriter (file, false, Encoding.UTF8)) { - sw.NewLine = "\r\n"; - - SlnData slnData = GetSlnData (c); - if (slnData == null) { - // If a non-msbuild project is being converted by just - // changing the fileformat, then create the SlnData for it - slnData = new SlnData (); - c.ExtendedProperties [typeof (SlnFileFormat)] = slnData; + string baseDir = Path.GetDirectoryName (sourceFile); + + if (saveProjects) { + var items = solution.GetAllSolutionItems ().ToArray (); + monitor.BeginTask (items.Length + 1); + foreach (var item in items) { + try { + monitor.BeginStep (); + item.SavingSolution = true; + item.Save (monitor); + } finally { + item.SavingSolution = false; + } } + } else { + monitor.BeginTask (1); + monitor.BeginStep (); + } - slnData.UpdateVersion (format); - - sw.WriteLine (); - - //Write Header - sw.WriteLine ("Microsoft Visual Studio Solution File, Format Version " + slnData.VersionString); - sw.WriteLine (slnData.HeaderComment); - if (slnData.VisualStudioVersion != null) - sw.WriteLine ("VisualStudioVersion = {0}", slnData.VisualStudioVersion); - if (slnData.MinimumVisualStudioVersion != null) - sw.WriteLine ("MinimumVisualStudioVersion = {0}", slnData.MinimumVisualStudioVersion); - - //Write the projects - monitor.BeginTask (GettextCatalog.GetString ("Saving projects"), 1); - WriteProjects (c, baseDir, sw, saveProjects, monitor); - monitor.EndTask (); + SlnFile sln = new SlnFile (); + sln.BaseDirectory = baseDir; + if (File.Exists (sourceFile)) { + try { + sln.Read (sourceFile); + } catch (Exception ex) { + LoggingService.LogError ("Existing solution can't be updated since it can't be read", ex); + } + } - //Write the lines for unknownProjects - foreach (string l in slnData.UnknownProjects) - sw.WriteLine (l); + sln.FormatVersion = format.SlnVersion; + sln.ProductDescription = format.ProductDescription; - //Write the Globals - sw.WriteLine ("Global"); + solution.WriteSolution (monitor, sln); - //Write SolutionConfigurationPlatforms - //FIXME: SolutionConfigurations? - sw.WriteLine ("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"); + sln.Write (file); + monitor.EndTask (); + } - foreach (SolutionConfiguration config in solution.Configurations) - sw.WriteLine ("\t\t{0} = {0}", ToSlnConfigurationId (config)); + internal void WriteFileInternal (SlnFile sln, Solution solution, ProgressMonitor monitor) + { + SolutionFolder c = solution.RootFolder; - sw.WriteLine ("\tEndGlobalSection"); + // Delete data for projects that have been removed from the solution + + var currentProjects = new HashSet<string> (solution.GetAllItems<SolutionFolderItem> ().Select (it => it.ItemId)); + var removedProjects = new HashSet<string> (); + if (solution.LoadedProjects != null) + removedProjects.UnionWith (solution.LoadedProjects.Except (currentProjects)); + var unknownProjects = new HashSet<string> (sln.Projects.Select (p => p.Id).Except (removedProjects).Except (currentProjects)); + + foreach (var p in removedProjects) { + var ps = sln.Projects.GetProject (p); + if (ps != null) + sln.Projects.Remove (ps); + var pc = sln.ProjectConfigurationsSection.GetPropertySet (p, true); + if (pc != null) + sln.ProjectConfigurationsSection.Remove (pc); + } + var secNested = sln.Sections.GetSection ("NestedProjects"); + if (secNested != null) { + foreach (var np in secNested.Properties.ToArray ()) { + if (removedProjects.Contains (np.Key) || removedProjects.Contains (np.Value)) + secNested.Properties.Remove (np.Key); + } + } + solution.LoadedProjects = currentProjects; - //Write ProjectConfigurationPlatforms - sw.WriteLine ("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"); + //Write the projects + using (monitor.BeginTask (GettextCatalog.GetString ("Saving projects"), 1)) { + monitor.BeginStep (); + WriteProjects (c, sln, monitor, unknownProjects); + } - List<string> list = new List<string> (); - WriteProjectConfigurations (solution, list); + //FIXME: SolutionConfigurations? - list.Sort (StringComparer.Create (CultureInfo.InvariantCulture, true)); - foreach (string s in list) - sw.WriteLine (s); + var pset = sln.SolutionConfigurationsSection; + foreach (SolutionConfiguration config in solution.Configurations) { + var cid = ToSlnConfigurationId (config); + pset.SetValue (cid, cid); + } - //Write lines for projects we couldn't load - if (slnData.SectionExtras.ContainsKey ("ProjectConfigurationPlatforms")) { - foreach (string s in slnData.SectionExtras ["ProjectConfigurationPlatforms"]) - sw.WriteLine ("\t\t{0}", s); - } + WriteProjectConfigurations (solution, sln); - sw.WriteLine ("\tEndGlobalSection"); - - //Write Nested Projects - ICollection<SolutionFolder> folders = solution.RootFolder.GetAllItems<SolutionFolder> (); - if (folders.Count > 1) { - // If folders ==1, that's the root folder - sw.WriteLine ("\tGlobalSection(NestedProjects) = preSolution"); - foreach (SolutionFolder folder in folders) { - if (folder.IsRoot) - continue; - WriteNestedProjects (folder, solution.RootFolder, sw); - } - sw.WriteLine ("\tEndGlobalSection"); + //Write Nested Projects + ICollection<SolutionFolder> folders = solution.RootFolder.GetAllItems<SolutionFolder> ().ToList (); + if (folders.Count > 1) { + // If folders ==1, that's the root folder + var sec = sln.Sections.GetOrCreateSection ("NestedProjects", "preSolution"); + foreach (SolutionFolder folder in folders) { + if (folder.IsRoot) + continue; + WriteNestedProjects (folder, solution.RootFolder, sec); } - - //Write custom properties - MSBuildSerializer ser = new MSBuildSerializer (solution.FileName); - DataItem data = (DataItem) ser.Serialize (solution, typeof(Solution)); + // Remove items which don't have a parent folder + var toRemove = solution.GetAllItems<SolutionFolderItem> ().Where (it => it.ParentFolder == solution.RootFolder); + foreach (var it in toRemove) + sec.Properties.Remove (it.ItemId); + } + + //Write custom properties + MSBuildSerializer ser = new MSBuildSerializer (solution.FileName); + DataItem data = (DataItem) ser.Serialize (solution, typeof(Solution)); + if (data.HasItemData) { + var sec = sln.Sections.GetOrCreateSection ("MonoDevelopProperties", "preSolution"); + WriteDataItem (sec.Properties, data); + } else + sln.Sections.RemoveSection ("MonoDevelopProperties"); + + // Write custom properties for configurations + foreach (SolutionConfiguration conf in solution.Configurations) { + data = (DataItem) ser.Serialize (conf); + string secId = "MonoDevelopProperties." + conf.Id; if (data.HasItemData) { - sw.WriteLine ("\tGlobalSection(MonoDevelopProperties) = preSolution"); - WriteDataItem (sw, data); - sw.WriteLine ("\tEndGlobalSection"); - } - - // Write custom properties for configurations - foreach (SolutionConfiguration conf in solution.Configurations) { - data = (DataItem) ser.Serialize (conf); - if (data.HasItemData) { - sw.WriteLine ("\tGlobalSection(MonoDevelopProperties." + conf.Id + ") = preSolution"); - WriteDataItem (sw, data); - sw.WriteLine ("\tEndGlobalSection"); - } - } - - //Write 'others' - if (slnData.GlobalExtra != null) { - foreach (string s in slnData.GlobalExtra) - sw.WriteLine (s); + var sec = sln.Sections.GetOrCreateSection (secId, "preSolution"); + WriteDataItem (sec.Properties, data); + } else { + sln.Sections.RemoveSection (secId); } - - sw.WriteLine ("EndGlobal"); } } - void WriteProjects (SolutionFolder folder, string baseDirectory, StreamWriter writer, bool saveProjects, IProgressMonitor monitor) + void WriteProjects (SolutionFolder folder, SlnFile sln, ProgressMonitor monitor, HashSet<string> unknownProjects) { - monitor.BeginStepTask (GettextCatalog.GetString ("Saving projects"), folder.Items.Count, 1); - foreach (SolutionItem ce in folder.Items) + monitor.BeginTask (folder.Items.Count); + foreach (SolutionFolderItem ce in folder.Items) { - string[] l = null; - if (ce is SolutionEntityItem) { + monitor.BeginStep (); + if (ce is SolutionItem) { - SolutionEntityItem item = (SolutionEntityItem) ce; - MSBuildHandler handler = MSBuildProjectService.GetItemHandler (item); - - if (saveProjects) { - try { - handler.SavingSolution = true; - item.Save (monitor); - } finally { - handler.SavingSolution = false; - } - } + SolutionItem item = (SolutionItem) ce; - l = handler.SlnProjectContent; + var proj = sln.Projects.GetOrCreateProject (ce.ItemId); + proj.TypeGuid = item.TypeGuid; + proj.Name = item.Name; + proj.FilePath = FileService.NormalizeRelativePath (FileService.AbsoluteToRelativePath (sln.BaseDirectory, item.FileName)).Replace ('/', '\\'); - writer.WriteLine (@"Project(""{0}"") = ""{1}"", ""{2}"", ""{3}""", - handler.TypeGuid, - item.Name, - FileService.NormalizeRelativePath (FileService.AbsoluteToRelativePath ( - baseDirectory, item.FileName)).Replace ('/', '\\'), - ce.ItemId); - DataItem data = handler.WriteSlnData (); + DataItem data = item.WriteSlnData (); if (data != null && data.HasItemData) { - writer.WriteLine ("\tProjectSection(MonoDevelopProperties) = preProject"); - WriteDataItem (writer, data); - writer.WriteLine ("\tEndProjectSection"); - } + var sec = proj.Sections.GetOrCreateSection ("MonoDevelopProperties", "preProject"); + WriteDataItem (sec.Properties, data); + } else + proj.Sections.RemoveSection ("MonoDevelopProperties"); + if (item.ItemDependencies.Count > 0) { - writer.WriteLine ("\tProjectSection(ProjectDependencies) = postProject"); + var sec = proj.Sections.GetOrCreateSection ("ProjectDependencies", "postProject"); + sec.Properties.ClearExcept (unknownProjects); foreach (var dep in item.ItemDependencies) - writer.WriteLine ("\t\t{0} = {0}", dep.ItemId); - if (handler.UnresolvedProjectDependencies != null) { - foreach (var dep in handler.UnresolvedProjectDependencies) - writer.WriteLine ("\t\t{0} = {0}", dep); - } - writer.WriteLine ("\tEndProjectSection"); - } + sec.Properties.SetValue (dep.ItemId, dep.ItemId); + } else + proj.Sections.RemoveSection ("ProjectDependencies"); } else if (ce is SolutionFolder) { - //Solution - SlnData slnData = GetSlnData (ce); - if (slnData == null) { - // Solution folder - slnData = new SlnData (); - ce.ExtendedProperties [typeof (SlnFileFormat)] = slnData; - } + var proj = sln.Projects.GetOrCreateProject (ce.ItemId); + proj.TypeGuid = MSBuildProjectService.FolderTypeGuid; + proj.Name = ce.Name; + proj.FilePath = ce.Name; - l = slnData.Extra; - - writer.WriteLine (@"Project(""{0}"") = ""{1}"", ""{2}"", ""{3}""", - MSBuildProjectService.FolderTypeGuid, - ce.Name, - ce.Name, - ce.ItemId); - // Folder files - WriteFolderFiles (writer, (SolutionFolder) ce); + WriteFolderFiles (proj, (SolutionFolder) ce); //Write custom properties MSBuildSerializer ser = new MSBuildSerializer (folder.ParentSolution.FileName); DataItem data = (DataItem) ser.Serialize (ce, typeof(SolutionFolder)); if (data.HasItemData) { - writer.WriteLine ("\tProjectSection(MonoDevelopProperties) = preProject"); - WriteDataItem (writer, data); - writer.WriteLine ("\tEndProjectSection"); + var sec = proj.Sections.GetOrCreateSection ("MonoDevelopProperties", "preProject"); + WriteDataItem (sec.Properties, data); } } - - if (l != null) { - foreach (string s in l) - writer.WriteLine (s); - } - - writer.WriteLine ("EndProject"); if (ce is SolutionFolder) - WriteProjects (ce as SolutionFolder, baseDirectory, writer, saveProjects, monitor); - monitor.Step (1); + WriteProjects (ce as SolutionFolder, sln, monitor, unknownProjects); } monitor.EndTask (); } - void WriteFolderFiles (StreamWriter writer, SolutionFolder folder) + void WriteFolderFiles (SlnProject proj, SolutionFolder folder) { if (folder.Files.Count > 0) { - writer.WriteLine ("\tProjectSection(SolutionItems) = preProject"); + var sec = proj.Sections.GetOrCreateSection ("SolutionItems", "preProject"); + sec.Properties.Clear (); foreach (FilePath f in folder.Files) { string relFile = MSBuildProjectService.ToMSBuildPathRelative (folder.ParentSolution.ItemDirectory, f); - writer.WriteLine ("\t\t" + relFile + " = " + relFile); + sec.Properties.SetValue (relFile, relFile); } - writer.WriteLine ("\tEndProjectSection"); - } + } else + proj.Sections.RemoveSection ("SolutionItems"); } - void WriteProjectConfigurations (Solution sol, List<string> list) + void WriteProjectConfigurations (Solution sol, SlnFile sln) { - foreach (SolutionConfiguration cc in sol.Configurations) { + var col = sln.ProjectConfigurationsSection; + + foreach (var item in sol.GetAllSolutionItems ()) { + // Don't save configurations for shared projects + if (!item.SupportsConfigurations ()) + continue; - foreach (SolutionConfigurationEntry cce in cc.Configurations) { - SolutionEntityItem p = cce.Item; + // <ProjectGuid>...</ProjectGuid> in some Visual Studio generated F# project files + // are missing "{"..."}" in their guid. This is not generally a problem since it + // is a valid GUID format. However the solution file format requires that these are present. + string itemGuid = item.ItemId; + if (!itemGuid.StartsWith ("{") && !itemGuid.EndsWith ("}")) + itemGuid = "{" + itemGuid + "}"; - // Don't save configurations for shared projects - if (!p.SupportsBuild () && !(p is UnloadedSolutionItem)) + var pset = col.GetOrCreatePropertySet (itemGuid, ignoreCase:true); + pset.Clear (); + + foreach (SolutionConfiguration cc in sol.Configurations) { + var cce = cc.GetEntryForItem (item); + if (cce == null) continue; - - // <ProjectGuid>...</ProjectGuid> in some Visual Studio generated F# project files - // are missing "{"..."}" in their guid. This is not generally a problem since it - // is a valid GUID format. However the solution file format requires that these are present. - string itemGuid = p.ItemId; - if (!itemGuid.StartsWith("{") && !itemGuid.EndsWith("}")) - itemGuid = "{" + itemGuid + "}"; + var configId = ToSlnConfigurationId (cc); + var itemConfigId = ToSlnConfigurationId (cce.ItemConfiguration); - list.Add (String.Format ( - "\t\t{0}.{1}.ActiveCfg = {2}", itemGuid, ToSlnConfigurationId (cc), ToSlnConfigurationId (cce.ItemConfiguration))); + pset.SetValue (configId + ".ActiveCfg", itemConfigId); if (cce.Build) - list.Add (String.Format ( - "\t\t{0}.{1}.Build.0 = {2}", itemGuid, ToSlnConfigurationId (cc), ToSlnConfigurationId (cce.ItemConfiguration))); - + pset.SetValue (configId + ".Build.0", itemConfigId); + if (cce.Deploy) - list.Add (String.Format ( - "\t\t{0}.{1}.Deploy.0 = {2}", itemGuid, ToSlnConfigurationId (cc), ToSlnConfigurationId (cce.ItemConfiguration))); + pset.SetValue (configId + ".Deploy.0", itemConfigId); } } - } - void WriteNestedProjects (SolutionFolder folder, SolutionFolder root, StreamWriter writer) + void WriteNestedProjects (SolutionFolder folder, SolutionFolder root, SlnSection sec) { - foreach (SolutionItem ce in folder.Items) - writer.WriteLine (@"{0}{1} = {2}", "\t\t", ce.ItemId, folder.ItemId); + foreach (SolutionFolderItem ce in folder.Items) + sec.Properties.SetValue (ce.ItemId, folder.ItemId); } - DataItem GetSolutionItemData (List<string> lines) + List<string> ReadSolutionItemDependencies (SlnProject proj) { - // Find a project section of type MonoDevelopProperties - int start, end; - if (!FindSection (lines, "MonoDevelopProperties", true, out start, out end)) - return null; - - // Deserialize the object - DataItem it = ReadDataItem (start, end - start + 1, lines); - - // Remove the lines, since they have already been preocessed - lines.RemoveRange (start, end - start + 1); - return it; - } - - List<string> ReadSolutionItemDependencies (List<string> lines) - { - // Find a project section of type MonoDevelopProperties - int start, end; - if (!FindSection (lines, "ProjectDependencies", false, out start, out end)) + // Find a project section of type ProjectDependencies + var sec = proj.Sections.GetSection ("ProjectDependencies"); + if (sec == null) return null; - var ids = new List<string> (); - for (int n=start + 1; n < end; n++) { - string line = lines [n]; - int i = line.IndexOf ('='); - if (i != -1) - ids.Add (line.Substring (0, i).Trim ()); - } - - // Remove the lines, since they have already been preocessed - lines.RemoveRange (start, end - start + 1); - return ids; + return sec.Properties.Keys.ToList (); } - List<string> ReadFolderFiles (List<string> lines) + IEnumerable<string> ReadFolderFiles (SlnProject proj) { // Find a solution item section of type SolutionItems + var sec = proj.Sections.GetSection ("SolutionItems"); + if (sec == null) + return new string[0]; - List<string> list = new List<string> (); - int start, end; - if (!FindSection (lines, "SolutionItems", true, out start, out end)) - return list; - - for (int n=start + 1; n < end; n++) { - string file = lines [n]; - int i = file.IndexOf ('='); - if (i == -1) - continue; - file = file.Substring (0, i).Trim (' ','\t'); - if (file.Length > 0) - list.Add (file); - } - - // Remove the lines, since they have already been preocessed - lines.RemoveRange (start, end - start + 1); - return list; + return sec.Properties.Keys.ToList (); } - - bool FindSection (List<string> lines, string name, bool preProject, out int start, out int end) - { - start = -1; - end = -1; - string prePost = preProject ? "preProject" : "postProject"; - var sectionLine = "ProjectSection(" + name + ")=" + prePost; - - for (int n=0; n<lines.Count && start == -1; n++) { - string line = lines [n].Replace ("\t","").Replace (" ", ""); - if (line == sectionLine) - start = n; - } - if (start == -1) - return false; - - for (int n=start+1; n<lines.Count && end == -1; n++) { - string line = lines [n].Replace ("\t","").Replace (" ", ""); - if (line == "EndProjectSection") - end = n; - } - return end != -1; - } - - void DeserializeSolutionItem (Solution sln, SolutionItem item, List<string> lines) + void DeserializeSolutionItem (Solution sln, SolutionFolderItem item, SlnProject proj) { // Deserialize the object - DataItem it = GetSolutionItemData (lines); + var sec = proj.Sections.GetSection ("MonoDevelopProperties"); + if (sec == null) + return; + + DataItem it = ReadDataItem (sec); if (it == null) return; @@ -445,14 +377,15 @@ namespace MonoDevelop.Projects.Formats.MSBuild ser.Deserialize (item, it); } - void WriteDataItem (StreamWriter sw, DataItem item) + void WriteDataItem (SlnPropertySet pset, DataItem item) { + pset.Clear (); int id = 0; foreach (DataNode val in item.ItemData) - WriteDataNode (sw, "", val, ref id); + WriteDataNode (pset, "", val, ref id); } - void WriteDataNode (StreamWriter sw, string prefix, DataNode node, ref int id) + void WriteDataNode (SlnPropertySet pset, string prefix, DataNode node, ref int id) { string name = node.Name; string newPrefix = prefix.Length > 0 ? prefix + "." + name: name; @@ -460,15 +393,15 @@ namespace MonoDevelop.Projects.Formats.MSBuild if (node is DataValue) { DataValue val = (DataValue) node; string value = EncodeString (val.Value); - sw.WriteLine ("\t\t" + newPrefix + " = " + value); + pset.SetValue (newPrefix, value); } else { DataItem it = (DataItem) node; - sw.WriteLine ("\t\t" + newPrefix + " = $" + id); + pset.SetValue (newPrefix, "$" + id); newPrefix = "$" + id; id ++; foreach (DataNode cn in it.ItemData) - WriteDataNode (sw, newPrefix, cn, ref id); + WriteDataNode (pset, newPrefix, cn, ref id); } } @@ -533,16 +466,14 @@ namespace MonoDevelop.Projects.Formats.MSBuild return val; } - DataItem ReadDataItem (Section sec, List<string> lines) - { - return ReadDataItem (sec.Start, sec.Count, lines); - } - - DataItem ReadDataItem (int start, int count, List<string> lines) + DataItem ReadDataItem (SlnSection sec) { DataItem it = new DataItem (); - int lineNum = start + 1; - int lastLine = start + count - 2; + + var lines = sec.Properties.ToArray (); + + int lineNum = 0; + int lastLine = lines.Length - 1; while (lineNum <= lastLine) { if (!ReadDataNode (it, lines, lastLine, "", ref lineNum)) lineNum++; @@ -550,38 +481,26 @@ namespace MonoDevelop.Projects.Formats.MSBuild return it; } - bool ReadDataNode (DataItem item, List<string> lines, int lastLine, string prefix, ref int lineNum) + bool ReadDataNode (DataItem item, KeyValuePair<string,string>[] lines, int lastLine, string prefix, ref int lineNum) { - string s = lines [lineNum].Trim (' ','\t'); - - if (s.Length == 0) { - lineNum++; - return true; - } + var s = lines [lineNum]; // Check if the line belongs to the current item if (prefix.Length > 0) { - if (!s.StartsWith (prefix + ".")) + if (!s.Key.StartsWith (prefix + ".")) return false; - s = s.Substring (prefix.Length + 1); } else { - if (s.StartsWith ("$")) + if (s.Key.StartsWith ("$")) return false; } - int i = s.IndexOf ('='); - if (i == -1) { - lineNum++; - return true; - } - - string name = s.Substring (0, i).Trim (' ','\t'); + string name = s.Key; if (name.Length == 0) { lineNum++; return true; } - string value = s.Substring (i+1).Trim (' ','\t'); + string value = s.Value; if (value.StartsWith ("$")) { // New item DataItem child = new DataItem (); @@ -629,7 +548,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild } //Reader - public object ReadFile (string fileName, MSBuildFileFormat format, IProgressMonitor monitor) + public async Task<object> ReadFile (string fileName, ProgressMonitor monitor) { if (fileName == null || monitor == null) return null; @@ -639,10 +558,10 @@ namespace MonoDevelop.Projects.Formats.MSBuild ProjectExtensionUtil.BeginLoadOperation (); sol = new Solution (); monitor.BeginTask (string.Format (GettextCatalog.GetString ("Loading solution: {0}"), fileName), 1); - var projectLoadMonitor = monitor as IProjectLoadProgressMonitor; + var projectLoadMonitor = monitor as ProjectLoadProgressMonitor; if (projectLoadMonitor != null) projectLoadMonitor.CurrentSolution = sol; - LoadSolution (sol, fileName, format, monitor); + await Task.Factory.StartNew (() => LoadSolution (sol, fileName, monitor)); } catch (Exception ex) { monitor.ReportError (GettextCatalog.GetString ("Could not load solution: {0}", fileName), ex); throw; @@ -653,129 +572,63 @@ namespace MonoDevelop.Projects.Formats.MSBuild return sol; } - //ExtendedProperties - // Per config - // Platform : Eg. Any CPU - // SolutionConfigurationPlatforms - // - SolutionFolder LoadSolution (Solution sol, string fileName, MSBuildFileFormat format, IProgressMonitor monitor) + SolutionFolder LoadSolution (Solution sol, string fileName, ProgressMonitor monitor) { - string headerComment; - string version = GetSlnFileVersion (fileName, out headerComment); - - ListDictionary globals = null; - SolutionFolder folder = null; - SlnData data = null; - List<Section> projectSections = null; - List<string> lines = null; - FileFormat projectFormat = Services.ProjectService.FileFormats.GetFileFormat (format); monitor.BeginTask (GettextCatalog.GetString ("Loading solution: {0}", fileName), 1); - //Parse the .sln file - using (StreamReader reader = new StreamReader(fileName)) { - sol.FileName = fileName; - sol.ConvertToFormat (projectFormat, false); - folder = sol.RootFolder; - sol.Version = "0.1"; //FIXME: - data = new SlnData (); - folder.ExtendedProperties [typeof (SlnFileFormat)] = data; - data.VersionString = version; - data.HeaderComment = headerComment; - - string s = null; - projectSections = new List<Section> (); - lines = new List<string> (); - globals = new ListDictionary (); - //Parse - while (reader.Peek () >= 0) { - s = GetNextLine (reader, lines).Trim (); - - if (String.Compare (s, "Global", StringComparison.OrdinalIgnoreCase) == 0) { - ParseGlobal (reader, lines, globals); - continue; - } - - if (s.StartsWith ("Project", StringComparison.Ordinal)) { - Section sec = new Section (); - projectSections.Add (sec); - sec.Start = lines.Count - 1; + var sln = new SlnFile (); + sln.Read (fileName); - int e = ReadUntil ("EndProject", reader, lines); - sec.Count = (e < 0) ? 1 : (e - sec.Start + 1); + sol.FileName = fileName; + sol.ConvertToFormat (projectFormat, false); - continue; - } + sol.ReadSolution (monitor, sln); + return sol.RootFolder; + } - if (s.StartsWith ("VisualStudioVersion = ", StringComparison.Ordinal)) { - Version v; - if (Version.TryParse (s.Substring ("VisualStudioVersion = ".Length), out v)) - data.VisualStudioVersion = v; - else - monitor.Log.WriteLine ("Ignoring unparseable VisualStudioVersion value in sln file"); - } + internal void LoadSolution (Solution sol, SlnFile sln, ProgressMonitor monitor) + { + var version = sln.FormatVersion; - if (s.StartsWith ("MinimumVisualStudioVersion = ", StringComparison.Ordinal)) { - Version v; - if (Version.TryParse (s.Substring ("MinimumVisualStudioVersion = ".Length), out v)) - data.MinimumVisualStudioVersion = v; - else - monitor.Log.WriteLine ("Ignoring unparseable MinimumVisualStudioVersion value in sln file"); - } - } - } + //Parse the .sln file + var folder = sol.RootFolder; + sol.Version = "0.1"; //FIXME: - monitor.BeginTask("Loading projects ..", projectSections.Count + 1); - Dictionary<string, SolutionItem> items = new Dictionary<string, SolutionItem> (); - List<SolutionItem> sortedList = new List<SolutionItem> (); - foreach (Section sec in projectSections) { + monitor.BeginTask("Loading projects ..", sln.Projects.Count + 1); + Dictionary<string, SolutionFolderItem> items = new Dictionary<string, SolutionFolderItem> (); + List<SolutionFolderItem> sortedList = new List<SolutionFolderItem> (); + foreach (SlnProject sec in sln.Projects) { monitor.Step (1); - Match match = ProjectRegex.Match (lines [sec.Start]); - if (!match.Success) { - LoggingService.LogDebug (GettextCatalog.GetString ( - "Invalid Project definition on line number #{0} in file '{1}'. Ignoring.", - sec.Start + 1, - fileName)); - - continue; - } - try { // Valid guid? - new Guid (match.Groups [1].Value); + new Guid (sec.TypeGuid); } catch (FormatException) { //Use default guid as projectGuid LoggingService.LogDebug (GettextCatalog.GetString ( "Invalid Project type guid '{0}' on line #{1}. Ignoring.", - match.Groups [1].Value, - sec.Start + 1)); + sec.Id, + sec.Line)); continue; } - string projTypeGuid = match.Groups [1].Value.ToUpper (); - string projectName = match.Groups [2].Value; - string projectPath = match.Groups [3].Value; - string projectGuid = match.Groups [4].Value.ToUpper (); - List<string> projLines; + string projTypeGuid = sec.TypeGuid.ToUpper (); + string projectName = sec.Name; + string projectPath = sec.FilePath; + string projectGuid = sec.Id; if (projTypeGuid == MSBuildProjectService.FolderTypeGuid) { //Solution folder SolutionFolder sfolder = new SolutionFolder (); sfolder.Name = projectName; - MSBuildProjectService.InitializeItemHandler (sfolder); - MSBuildProjectService.SetId (sfolder, projectGuid); + sfolder.ItemId = projectGuid; - projLines = lines.GetRange (sec.Start + 1, sec.Count - 2); - DeserializeSolutionItem (sol, sfolder, projLines); + DeserializeSolutionItem (sol, sfolder, sec); - foreach (string f in ReadFolderFiles (projLines)) - sfolder.Files.Add (MSBuildProjectService.FromMSBuildPath (Path.GetDirectoryName (fileName), f)); + foreach (string f in ReadFolderFiles (sec)) + sfolder.Files.Add (MSBuildProjectService.FromMSBuildPath (Path.GetDirectoryName (sol.FileName), f)); - SlnData slnData = new SlnData (); - slnData.Extra = projLines.ToArray (); - sfolder.ExtendedProperties [typeof (SlnFileFormat)] = slnData; - items.Add (projectGuid, sfolder); sortedList.Add (sfolder); @@ -785,42 +638,38 @@ namespace MonoDevelop.Projects.Formats.MSBuild if (projectPath.StartsWith("http://")) { monitor.ReportWarning (GettextCatalog.GetString ( "{0}({1}): Projects with non-local source (http://...) not supported. '{2}'.", - fileName, sec.Start + 1, projectPath)); - data.UnknownProjects.AddRange (lines.GetRange (sec.Start, sec.Count)); + sol.FileName, sec.Line, projectPath)); continue; } - string path = MSBuildProjectService.FromMSBuildPath (Path.GetDirectoryName (fileName), projectPath); + string path = MSBuildProjectService.FromMSBuildPath (Path.GetDirectoryName (sol.FileName), projectPath); if (String.IsNullOrEmpty (path)) { monitor.ReportWarning (GettextCatalog.GetString ( - "Invalid project path found in {0} : {1}", fileName, projectPath)); + "Invalid project path found in {0} : {1}", sol.FileName, projectPath)); LoggingService.LogWarning (GettextCatalog.GetString ( - "Invalid project path found in {0} : {1}", fileName, projectPath)); + "Invalid project path found in {0} : {1}", sol.FileName, projectPath)); continue; } projectPath = Path.GetFullPath (path); - SolutionEntityItem item = null; + SolutionItem item = null; try { if (sol.IsSolutionItemEnabled (projectPath)) { - item = ProjectExtensionUtil.LoadSolutionItem (monitor, projectPath, delegate { + var t = ProjectExtensionUtil.LoadSolutionItem (monitor, projectPath, delegate { return MSBuildProjectService.LoadItem (monitor, projectPath, format, projTypeGuid, projectGuid); }); + t.Wait (); + item = t.Result; if (item == null) { throw new UnknownSolutionItemTypeException (projTypeGuid); } } else { - var uitem = new UnloadedSolutionItem () { + item = new UnloadedSolutionItem () { FileName = projectPath }; - var h = new MSBuildHandler (projTypeGuid, projectGuid) { - Item = uitem, - }; - uitem.SetItemHandler (h); - item = uitem; } } catch (Exception e) { @@ -837,7 +686,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild var relPath = new FilePath (path).ToRelative (sol.BaseDirectory); if (!string.IsNullOrEmpty (name)) { var guids = name.Split (';'); - var projectInfo = MSBuildProjectService.GetUnknownProjectTypeInfo (guids, fileName); + var projectInfo = MSBuildProjectService.GetUnknownProjectTypeInfo (guids, sol.FileName); if (projectInfo != null) { loadAsProject = projectInfo.LoadFiles; LoggingService.LogWarning (string.Format ("Could not load {0} project '{1}'. {2}", projectInfo.Name, relPath, projectInfo.GetInstructions ())); @@ -861,218 +710,143 @@ namespace MonoDevelop.Projects.Formats.MSBuild "Error while trying to load the project '{0}': {1}", projectPath, e.Message)); } - SolutionEntityItem uitem; + SolutionItem uitem; if (loadAsProject) { uitem = new UnknownProject () { FileName = projectPath, - LoadError = e.Message, + UnsupportedProjectMessage = e.Message, }; } else { uitem = new UnknownSolutionItem () { FileName = projectPath, - LoadError = e.Message, + UnsupportedProjectMessage = e.Message, }; } - - var h = new MSBuildHandler (projTypeGuid, projectGuid) { - Item = uitem, - }; - uitem.SetItemHandler (h); item = uitem; } - MSBuildHandler handler = (MSBuildHandler) item.ItemHandler; - projLines = lines.GetRange (sec.Start + 1, sec.Count - 2); - DataItem it = GetSolutionItemData (projLines); + item.UnresolvedProjectDependencies = ReadSolutionItemDependencies (sec); - handler.UnresolvedProjectDependencies = ReadSolutionItemDependencies (projLines); - handler.SlnProjectContent = projLines.ToArray (); - handler.ReadSlnData (it); + // Deserialize the object + var ssec = sec.Sections.GetSection ("MonoDevelopProperties"); + if (ssec != null) { + DataItem it = ReadDataItem (ssec); + item.ReadSlnData (it); + } if (!items.ContainsKey (projectGuid)) { items.Add (projectGuid, item); sortedList.Add (item); - data.ItemsByGuid [projectGuid] = item; } else { monitor.ReportError (GettextCatalog.GetString ("Invalid solution file. There are two projects with the same GUID. The project {0} will be ignored.", projectPath), null); } } monitor.EndTask (); - if (globals != null && globals.Contains ("NestedProjects")) { - LoadNestedProjects (globals ["NestedProjects"] as Section, lines, items, monitor); - globals.Remove ("NestedProjects"); - } + sol.LoadedProjects = new HashSet<string> (items.Keys); + + var nested = sln.Sections.GetSection ("NestedProjects"); + if (nested != null) + LoadNestedProjects (nested, items, monitor); // Resolve project dependencies - foreach (var it in items.OfType<SolutionEntityItem> ()) { - MSBuildHandler handler = (MSBuildHandler) it.ItemHandler; - if (handler.UnresolvedProjectDependencies != null) { - foreach (var id in handler.UnresolvedProjectDependencies.ToArray ()) { - SolutionItem dep; - if (items.TryGetValue (id, out dep) && dep is SolutionEntityItem) { - handler.UnresolvedProjectDependencies.Remove (id); - it.ItemDependencies.Add ((SolutionEntityItem)dep); + foreach (var it in items.Values.OfType<SolutionItem> ()) { + if (it.UnresolvedProjectDependencies != null) { + foreach (var id in it.UnresolvedProjectDependencies.ToArray ()) { + SolutionFolderItem dep; + if (items.TryGetValue (id, out dep) && dep is SolutionItem) { + it.UnresolvedProjectDependencies.Remove (id); + it.ItemDependencies.Add ((SolutionItem)dep); } } - if (handler.UnresolvedProjectDependencies.Count == 0) - handler.UnresolvedProjectDependencies = null; + if (it.UnresolvedProjectDependencies.Count == 0) + it.UnresolvedProjectDependencies = null; } } //Add top level folders and projects to the main folder - foreach (SolutionItem ce in sortedList) { + foreach (SolutionFolderItem ce in sortedList) { if (ce.ParentFolder == null) folder.Items.Add (ce); } //FIXME: This can be just SolutionConfiguration also! - if (globals != null) { - if (globals.Contains ("SolutionConfigurationPlatforms")) { - LoadSolutionConfigurations (globals ["SolutionConfigurationPlatforms"] as Section, lines, - sol, monitor); - globals.Remove ("SolutionConfigurationPlatforms"); - } + LoadSolutionConfigurations (sln.SolutionConfigurationsSection, sol, monitor); - if (globals.Contains ("ProjectConfigurationPlatforms")) { - LoadProjectConfigurationMappings (globals ["ProjectConfigurationPlatforms"] as Section, lines, - sol, monitor); - globals.Remove ("ProjectConfigurationPlatforms"); - } + LoadProjectConfigurationMappings (sln.ProjectConfigurationsSection, sol, items, monitor); - if (globals.Contains ("MonoDevelopProperties")) { - LoadMonoDevelopProperties (globals ["MonoDevelopProperties"] as Section, lines, sol, monitor); - globals.Remove ("MonoDevelopProperties"); - } - - ArrayList toRemove = new ArrayList (); - foreach (DictionaryEntry e in globals) { - string name = (string) e.Key; - if (name.StartsWith ("MonoDevelopProperties.")) { - int i = name.IndexOf ('.'); - LoadMonoDevelopConfigurationProperties (name.Substring (i+1), (Section)e.Value, lines, sol, monitor); - toRemove.Add (e.Key); - } + LoadMonoDevelopProperties (sln.Sections.GetSection ("MonoDevelopProperties"), sol, monitor); + + foreach (var e in sln.Sections) { + string name = e.Id; + if (name.StartsWith ("MonoDevelopProperties.")) { + int i = name.IndexOf ('.'); + LoadMonoDevelopConfigurationProperties (name.Substring (i+1), e, sol, monitor); } - foreach (object key in toRemove) - globals.Remove (key); } - //Save the global sections that we dont use - List<string> globalLines = new List<string> (); - foreach (Section sec in globals.Values) - globalLines.InsertRange (globalLines.Count, lines.GetRange (sec.Start, sec.Count)); - - data.GlobalExtra = globalLines; monitor.EndTask (); - - // When reloading a project, keep the solution data and item id - sol.SolutionItemAdded += delegate(object sender, SolutionItemChangeEventArgs e) { - if (e.Reloading) { - ItemSlnData.TransferData (e.ReplacedItem, e.SolutionItem); - var ih = e.SolutionItem.ItemHandler as MSBuildHandler; - if (ih != null) - ih.ItemId = e.ReplacedItem.ItemId; - } - }; - - return folder; } - void ParseGlobal (StreamReader reader, List<string> lines, ListDictionary dict) + void LoadProjectConfigurationMappings (SlnPropertySetCollection sets, Solution sln, Dictionary<string, SolutionFolderItem> items, ProgressMonitor monitor) { - //Process GlobalSection-s - while (reader.Peek () >= 0) { - string s = GetNextLine (reader, lines).Trim (); - if (s.Length == 0) - //Skip blank lines - continue; - - Match m = GlobalSectionRegex.Match (s); - if (!m.Success) { - if (String.Compare (s, "EndGlobal", true) == 0) - return; - - continue; - } - - Section sec = new Section (m.Groups [1].Value, m.Groups [2].Value, lines.Count - 1, 1); - dict [sec.Key] = sec; - - sec.Count = ReadUntil ("EndGlobalSection", reader, lines) - sec.Start + 1; - //FIXME: sec.Count == -1 : No EndGlobalSection found, ignore entry? - } - } - - void LoadProjectConfigurationMappings (Section sec, List<string> lines, Solution sln, IProgressMonitor monitor) - { - if (sec == null || String.Compare (sec.Val, "postSolution", true) != 0) + if (sets == null) return; Dictionary<string, SolutionConfigurationEntry> cache = new Dictionary<string, SolutionConfigurationEntry> (); Dictionary<string, string> ignoredProjects = new Dictionary<string, string> (); - SlnData slnData = GetSlnData (sln.RootFolder); - - List<string> extras = new List<string> (); - for (int i = 0; i < sec.Count - 2; i ++) { - int lineNum = i + sec.Start + 1; - string s = lines [lineNum].Trim (); - extras.Add (s); - - //Format: - // {projectGuid}.SolutionConfigName|SolutionPlatform.ActiveCfg = ProjConfigName|ProjPlatform - // {projectGuid}.SolutionConfigName|SolutionPlatform.Build.0 = ProjConfigName|ProjPlatform - // {projectGuid}.SolutionConfigName|SolutionPlatform.Deploy.0 = ProjConfigName|ProjPlatform + foreach (var pset in sets) { + + var projGuid = pset.Id; + + if (!items.ContainsKey (projGuid)) { + if (ignoredProjects.ContainsKey (projGuid)) + // already warned + continue; - string [] parts = s.Split (new char [] {'='}, 2); - if (parts.Length < 2) { - LoggingService.LogDebug ("{0} ({1}) : Invalid format. Ignoring", sln.FileName, lineNum + 1); + LoggingService.LogWarning (GettextCatalog.GetString ("{0} ({1}) : Project with guid = '{2}' not found or not loaded. Ignoring", + sln.FileName, pset.Line + 1, projGuid)); + ignoredProjects [projGuid] = projGuid; continue; } - string action; - string projConfig = parts [1].Trim (); - - string left = parts [0].Trim (); - if (left.EndsWith (".ActiveCfg")) { - action = "ActiveCfg"; - left = left.Substring (0, left.Length - 10); - } else if (left.EndsWith (".Build.0")) { - action = "Build.0"; - left = left.Substring (0, left.Length - 8); - } else if (left.EndsWith (".Deploy.0")) { - action = "Deploy.0"; - left = left.Substring (0, left.Length - 9); - } else { - LoggingService.LogWarning (GettextCatalog.GetString ("{0} ({1}) : Unknown action. Only ActiveCfg, Build.0 and Deploy.0 supported.", - sln.FileName, lineNum + 1)); + SolutionFolderItem it; + if (!items.TryGetValue (projGuid, out it)) continue; - } - string [] t = left.Split (new char [] {'.'}, 2); - if (t.Length < 2) { - LoggingService.LogDebug ("{0} ({1}) : Invalid format of the left side. Ignoring", - sln.FileName, lineNum + 1); + SolutionItem item = it as SolutionItem; + + if (item == null || !item.SupportsConfigurations ()) continue; - } - string projGuid = t [0].ToUpper (); - string slnConfig = t [1]; + //Format: + // {projectGuid}.SolutionConfigName|SolutionPlatform.ActiveCfg = ProjConfigName|ProjPlatform + // {projectGuid}.SolutionConfigName|SolutionPlatform.Build.0 = ProjConfigName|ProjPlatform + // {projectGuid}.SolutionConfigName|SolutionPlatform.Deploy.0 = ProjConfigName|ProjPlatform - if (!slnData.ItemsByGuid.ContainsKey (projGuid)) { - if (ignoredProjects.ContainsKey (projGuid)) - // already warned + foreach (var prop in pset) { + string action; + string projConfig = prop.Value; + + string left = prop.Key; + if (left.EndsWith (".ActiveCfg")) { + action = "ActiveCfg"; + left = left.Substring (0, left.Length - 10); + } else if (left.EndsWith (".Build.0")) { + action = "Build.0"; + left = left.Substring (0, left.Length - 8); + } else if (left.EndsWith (".Deploy.0")) { + action = "Deploy.0"; + left = left.Substring (0, left.Length - 9); + } else { + LoggingService.LogWarning (GettextCatalog.GetString ("{0} ({1}) : Unknown action. Only ActiveCfg, Build.0 and Deploy.0 supported.", + sln.FileName, pset.Line)); continue; + } - LoggingService.LogWarning (GettextCatalog.GetString ("{0} ({1}) : Project with guid = '{2}' not found or not loaded. Ignoring", - sln.FileName, lineNum + 1, projGuid)); - ignoredProjects [projGuid] = projGuid; - continue; - } + string slnConfig = left; - SolutionEntityItem item; - if (slnData.ItemsByGuid.TryGetValue (projGuid, out item) && (item.SupportsBuild () || item is UnloadedSolutionItem)) { string key = projGuid + "." + slnConfig; SolutionConfigurationEntry combineConfigEntry = null; if (cache.ContainsKey (key)) { @@ -1092,6 +866,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild * if Build (true/false) for the project will * will depend on presence/absence of Build.0 entry */ + if (action == "ActiveCfg") { combineConfigEntry.ItemConfiguration = FromSlnConfigurationId (projConfig); } else if (action == "Build.0") { @@ -1100,15 +875,12 @@ namespace MonoDevelop.Projects.Formats.MSBuild combineConfigEntry.Deploy = true; } } - extras.RemoveAt (extras.Count - 1); } - - slnData.SectionExtras ["ProjectConfigurationPlatforms"] = extras; } /* Gets the CombineConfigurationEntry corresponding to the @entry in its parentCombine's * CombineConfiguration. Creates the required bits if not present */ - SolutionConfigurationEntry GetConfigEntry (Solution sol, SolutionEntityItem item, string configName) + SolutionConfigurationEntry GetConfigEntry (Solution sol, SolutionItem item, string configName) { configName = FromSlnConfigurationId (configName); @@ -1124,21 +896,13 @@ namespace MonoDevelop.Projects.Formats.MSBuild return solutionConfig.AddItem (item); } - void LoadSolutionConfigurations (Section sec, List<string> lines, Solution solution, IProgressMonitor monitor) + void LoadSolutionConfigurations (SlnPropertySet sec, Solution solution, ProgressMonitor monitor) { - if (sec == null || String.Compare (sec.Val, "preSolution", true) != 0) + if (sec == null) return; - for (int i = 0; i < sec.Count - 2; i ++) { - //FIXME: expects both key and val to be on the same line - int lineNum = i + sec.Start + 1; - string s = lines [lineNum].Trim (); - if (s.Length == 0) - //Skip blank lines - continue; + foreach (var pair in sec) { - KeyValuePair<string, string> pair = SplitKeyValue (s); - string configId = FromSlnConfigurationId (pair.Key); SolutionConfiguration config = solution.Configurations [configId]; @@ -1154,37 +918,37 @@ namespace MonoDevelop.Projects.Formats.MSBuild return new SolutionConfiguration (fullId); } - void LoadMonoDevelopProperties (Section sec, List<string> lines, Solution sln, IProgressMonitor monitor) + void LoadMonoDevelopProperties (SlnSection sec, Solution sln, ProgressMonitor monitor) { - DataItem it = ReadDataItem (sec, lines); + if (sec == null) + return; + DataItem it = ReadDataItem (sec); MSBuildSerializer ser = new MSBuildSerializer (sln.FileName); ser.SerializationContext.BaseFile = sln.FileName; ser.Deserialize (sln, it); } - void LoadMonoDevelopConfigurationProperties (string configName, Section sec, List<string> lines, Solution sln, IProgressMonitor monitor) + void LoadMonoDevelopConfigurationProperties (string configName, SlnSection sec, Solution sln, ProgressMonitor monitor) { SolutionConfiguration config = sln.Configurations [configName]; if (config == null) return; - DataItem it = ReadDataItem (sec, lines); + DataItem it = ReadDataItem (sec); MSBuildSerializer ser = new MSBuildSerializer (sln.FileName); ser.Deserialize (config, it); } - void LoadNestedProjects (Section sec, List<string> lines, - IDictionary<string, SolutionItem> entries, IProgressMonitor monitor) + void LoadNestedProjects (SlnSection sec, IDictionary<string, SolutionFolderItem> entries, ProgressMonitor monitor) { - if (sec == null || String.Compare (sec.Val, "preSolution", true) != 0) + if (sec == null || String.Compare (sec.SectionType, "preSolution", StringComparison.OrdinalIgnoreCase) != 0) return; - for (int i = 0; i < sec.Count - 2; i ++) { + foreach (var kvp in sec.Properties) { // Guids should be upper case for VS compatibility - KeyValuePair<string, string> pair = SplitKeyValue (lines [i + sec.Start + 1].Trim ()); - pair = new KeyValuePair<string, string> (pair.Key.ToUpper (), pair.Value.ToUpper ()); + var pair = new KeyValuePair<string, string> (kvp.Key.ToUpper (), kvp.Value.ToUpper ()); - SolutionItem folderItem; - SolutionItem item; + SolutionFolderItem folderItem; + SolutionFolderItem item; if (!entries.TryGetValue (pair.Value, out folderItem)) { //Container not found @@ -1232,17 +996,6 @@ namespace MonoDevelop.Projects.Formats.MSBuild } - KeyValuePair<string, string> SplitKeyValue (string s) - { - string [] pair = s.Split (new char [] {'='}, 2); - string key = pair [0].Trim (); - string val = String.Empty; - if (pair.Length == 2) - val = pair [1].Trim (); - - return new KeyValuePair<string, string> (key, val); - } - // Utility function to determine the sln file version string GetSlnFileVersion(string strInSlnFile, out string headerComment) { @@ -1276,32 +1029,6 @@ namespace MonoDevelop.Projects.Formats.MSBuild return strVersion; } - static SlnData GetSlnData (SolutionItem c) - { - if (c.ExtendedProperties.Contains (typeof (SlnFileFormat))) - return c.ExtendedProperties [typeof (SlnFileFormat)] as SlnData; - return null; - } - - // static regexes - static Regex projectRegex = null; - internal static Regex ProjectRegex { - get { - if (projectRegex == null) - projectRegex = new Regex(@"Project\(""(\{[^}]*\})""\) = ""(.*)"", ""(.*)"", ""(\{[^{]*\})"""); - return projectRegex; - } - } - - static Regex globalSectionRegex = null; - static Regex GlobalSectionRegex { - get { - if (globalSectionRegex == null) - globalSectionRegex = new Regex (@"GlobalSection\s*\(([^)]*)\)\s*=\s*(\w*)"); - return globalSectionRegex; - } - } - static Regex slnVersionRegex = null; internal static Regex SlnVersionRegex { get { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/UnknownSolutionItemTypeException.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/UnknownSolutionItemTypeException.cs new file mode 100644 index 0000000000..ce2af4ae34 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/UnknownSolutionItemTypeException.cs @@ -0,0 +1,64 @@ +// +// UnknownSolutionItemTypeException.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Xml; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using MonoDevelop.Core; +using MonoDevelop.Projects; +using MonoDevelop.Core.Serialization; +using MonoDevelop.Core.Assemblies; +using MonoDevelop.Projects.Formats.MD1; +using MonoDevelop.Projects.Extensions; +using Mono.Addins; +using System.Linq; +using MonoDevelop.Core.Instrumentation; +using MonoDevelop.Core.ProgressMonitoring; +using System.Threading.Tasks; + +namespace MonoDevelop.Projects.Formats.MSBuild +{ + + class UnknownSolutionItemTypeException : InvalidOperationException + { + public UnknownSolutionItemTypeException () + : base ("Unknown solution item type") + { + } + + public UnknownSolutionItemTypeException (string name) + : base ("Unknown solution item type: " + name) + { + this.TypeName = name; + } + + public string TypeName { get; private set; } + } + +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/DotNetNamingPolicy.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/DotNetNamingPolicy.cs index 882004de7c..045e042aa3 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/DotNetNamingPolicy.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/DotNetNamingPolicy.cs @@ -62,10 +62,10 @@ namespace MonoDevelop.Projects.Policies internal static ResourceNamePolicy GetDefaultResourceNamePolicy (object ob) { FileFormat format = null; - if (ob is SolutionEntityItem) - format = ((SolutionEntityItem)ob).FileFormat; - else if (ob is SolutionItem) - format = ((SolutionItem)ob).ParentSolution.FileFormat; + if (ob is SolutionItem) + format = ((SolutionItem)ob).FileFormat; + else if (ob is SolutionFolderItem) + format = ((SolutionFolderItem)ob).ParentSolution.FileFormat; else if (ob is Solution) format = ((Solution)ob).FileFormat; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyBag.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyBag.cs index 074bcc8d0d..4d8ce0d6be 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyBag.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyBag.cs @@ -42,7 +42,7 @@ namespace MonoDevelop.Projects.Policies [DataItem ("Policies")] public class PolicyBag: PolicyContainer, ICustomDataItem { - public PolicyBag (SolutionItem owner) + public PolicyBag (SolutionFolderItem owner) { this.Owner = owner; } @@ -51,7 +51,7 @@ namespace MonoDevelop.Projects.Policies { } - public SolutionItem Owner { get; internal set; } + public SolutionFolderItem Owner { get; internal set; } public override bool IsRoot { get { return Owner == null || Owner.ParentFolder == null; } @@ -107,7 +107,7 @@ namespace MonoDevelop.Projects.Policies { SolutionFolder solFol = Owner as SolutionFolder; if (solFol != null) - foreach (SolutionItem item in solFol.Items) + foreach (SolutionFolderItem item in solFol.Items) if (!item.Policies.DirectHas (args.PolicyType, args.Scope)) item.Policies.OnPolicyChanged (args.PolicyType, args.Scope); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProject.cs index 2a98dae3cb..f3fd6487ff 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProject.cs @@ -1,4 +1,4 @@ -// +// // SharedProject.cs // // Author: @@ -31,35 +31,130 @@ using MonoDevelop.Core; using System.IO; using System.Xml; using MonoDevelop.Projects.Policies; +using System.Threading.Tasks; +using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects.SharedAssetsProjects { + [RegisterProjectType ("{D954291E-2A0B-460D-934E-DC6B0785DB48}", Extension="shproj", Alias="SharedAssetsProject")] public class SharedAssetsProject: Project, IDotNetFileContainer { Solution currentSolution; IDotNetLanguageBinding languageBinding; string languageName; + string projitemsFile; public SharedAssetsProject () { + Initialize (this); } - public SharedAssetsProject (string language) + public SharedAssetsProject (string language): this () { languageName = language; } - public SharedAssetsProject (ProjectCreateInformation projectCreateInfo, XmlElement projectOptions) + public SharedAssetsProject (ProjectCreateInformation projectCreateInfo, XmlElement projectOptions): this () { languageName = projectOptions.GetAttribute ("language"); DefaultNamespace = projectCreateInfo.ProjectName; } - internal protected override List<FilePath> OnGetItemFiles (bool includeReferencedFiles) + protected override void OnReadProject (ProgressMonitor monitor, MSBuildProject msproject) + { + base.OnReadProject (monitor, msproject); + + var doc = msproject.Document; + projitemsFile = null; + foreach (var no in doc.DocumentElement.ChildNodes) { + var im = no as XmlElement; + if (im != null && im.LocalName == "Import" && im.GetAttribute ("Label") == "Shared") { + projitemsFile = im.GetAttribute ("Project"); + break; + } + } + if (projitemsFile == null) + return; + + // TODO: load the type from msbuild + LanguageName = "C#"; + + projitemsFile = Path.Combine (Path.GetDirectoryName (msproject.FileName), projitemsFile); + + MSBuildProject p = new MSBuildProject (); + p.Load (projitemsFile); + + var cp = p.PropertyGroups.FirstOrDefault (g => g.Label == "Configuration"); + if (cp != null) + DefaultNamespace = cp.GetValue ("Import_RootNamespace"); + + LoadProjectItems (p, ProjectItemFlags.None); + } + + protected override void OnWriteProject (ProgressMonitor monitor, MonoDevelop.Projects.Formats.MSBuild.MSBuildProject msproject) + { + base.OnWriteProject (monitor, msproject); + + MSBuildProject projitemsProject = new MSBuildProject (); + + var newProject = FileName == null || !File.Exists (FileName); + if (newProject) { + var grp = msproject.GetGlobalPropertyGroup (); + if (grp == null) + grp = msproject.AddNewPropertyGroup (false); + grp.SetValue ("ProjectGuid", ItemId, preserveExistingCase:true); + var import = msproject.AddNewImport (@"$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"); + import.Condition = @"Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"; + msproject.AddNewImport (@"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props"); + msproject.AddNewImport (@"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props"); + import = msproject.AddNewImport (Path.ChangeExtension (FileName.FileName, ".projitems")); + import.Label = "Shared"; + msproject.AddNewImport (@"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets"); + } else { + msproject.Load (FileName); + } + + // having no ToolsVersion is equivalent to 2.0, roundtrip that correctly + if (ToolsVersion != "2.0") + msproject.ToolsVersion = ToolsVersion; + else if (string.IsNullOrEmpty (msproject.ToolsVersion)) + msproject.ToolsVersion = null; + else + msproject.ToolsVersion = "2.0"; + + if (projitemsFile == null) + projitemsFile = Path.ChangeExtension (FileName, ".projitems"); + if (File.Exists (projitemsFile)) { + projitemsProject.Load (projitemsFile); + } else { + IMSBuildPropertySet grp = projitemsProject.AddNewPropertyGroup (true); + grp.SetValue ("MSBuildAllProjects", "$(MSBuildAllProjects);$(MSBuildThisFileFullPath)"); + grp.SetValue ("HasSharedItems", true); + grp.SetValue ("SharedGUID", ItemId, preserveExistingCase:true); + } + + IMSBuildPropertySet configGrp = projitemsProject.PropertyGroups.FirstOrDefault (g => g.Label == "Configuration"); + if (configGrp == null) { + configGrp = projitemsProject.AddNewPropertyGroup (true); + configGrp.Label = "Configuration"; + } + configGrp.SetValue ("Import_RootNamespace", DefaultNamespace); + + SaveProjectItems (monitor, new MSBuildFileFormatVS12 (), projitemsProject, "$(MSBuildThisFileDirectory)"); + + // Remove all items of this project, since items are saved in the projitems file + + foreach (var it in msproject.GetAllItems ().ToArray ()) + msproject.RemoveItem (it); + + projitemsProject.Save (projitemsFile); + } + + protected override IEnumerable<FilePath> OnGetItemFiles (bool includeReferencedFiles) { - var list = base.OnGetItemFiles (includeReferencedFiles); + var list = base.OnGetItemFiles (includeReferencedFiles).ToList (); if (!string.IsNullOrEmpty (FileName)) - list.Add (FileName.ChangeExtension (".projitems")); + list.Add (ProjItemsPath); return list; } @@ -70,10 +165,19 @@ namespace MonoDevelop.Projects.SharedAssetsProjects public string DefaultNamespace { get; set; } - public override IEnumerable<string> GetProjectTypes () + public FilePath ProjItemsPath { + get { + return projitemsFile != null ? (FilePath) projitemsFile : FileName.ChangeExtension (".projitems"); + } + set { + projitemsFile = value; + } + } + + protected override void OnGetProjectTypes (HashSet<string> types) { - yield return "SharedAssets"; - yield return "DotNet"; + types.Add ("SharedAssets"); + types.Add ("DotNet"); } public override string[] SupportedLanguages { @@ -95,17 +199,22 @@ namespace MonoDevelop.Projects.SharedAssetsProjects return LanguageBinding.IsSourceCodeFile (fileName); } - protected override BuildResult OnBuild (MonoDevelop.Core.IProgressMonitor monitor, ConfigurationSelector configuration) + protected override Task<BuildResult> OnBuild (MonoDevelop.Core.ProgressMonitor monitor, ConfigurationSelector configuration) + { + return Task.FromResult (BuildResult.Success); + } + + protected override bool OnGetSupportsTarget (string target) { - return new BuildResult (); + return false; } - internal protected override bool OnGetSupportsTarget (string target) + protected override bool OnGetSupportsExecute () { return false; } - internal protected override bool OnGetSupportsExecute () + protected override bool OnGetSupportsBuild () { return false; } @@ -143,7 +252,7 @@ namespace MonoDevelop.Projects.SharedAssetsProjects // Maybe there is a project that is already referencing this one. It may happen when creating a solution // from a template - foreach (var p in ParentSolution.GetAllSolutionItems<DotNetProject> ()) + foreach (var p in ParentSolution.GetAllItems<DotNetProject> ()) ProcessProject (p); } @@ -191,6 +300,14 @@ namespace MonoDevelop.Projects.SharedAssetsProjects void ProcessProject (DotNetProject p) { + // When the projitems file name doesn't match the shproj file name, the reference we add to the referencing projects + // uses the projitems name, not the shproj name. Here we detect such case and re-add the references using the correct name + var referencesToFix = p.References.Where (r => r.GetItemsProjectPath () == ProjItemsPath && r.Reference != Name).ToList (); + foreach (var r in referencesToFix) { + p.References.Remove (r); + p.References.Add (new ProjectReference (this)); + } + foreach (var pref in p.References.Where (r => r.ReferenceType == ReferenceType.Project && r.Reference == Name)) ProcessNewReference (pref); } @@ -198,7 +315,7 @@ namespace MonoDevelop.Projects.SharedAssetsProjects void ProcessNewReference (ProjectReference pref) { pref.Flags = ProjectItemFlags.DontPersist; - pref.SetItemsProjectPath (Path.ChangeExtension (FileName, ".projitems")); + pref.SetItemsProjectPath (ProjItemsPath); foreach (var f in Files) { if (pref.OwnerProject.Files.GetFile (f.FilePath) == null) { var cf = (ProjectFile)f.Clone (); @@ -268,7 +385,7 @@ namespace MonoDevelop.Projects.SharedAssetsProjects if (ParentSolution == null) return new DotNetProject[0]; - return ParentSolution.GetAllSolutionItems<DotNetProject> ().Where (p => p.References.Any (r => r.GetItemsProjectPath () != null)); + return ParentSolution.GetAllItems<DotNetProject> ().Where (p => p.References.Any (r => r.GetItemsProjectPath () != null)); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProjectMSBuildExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProjectMSBuildExtension.cs index 1da660c7a1..645d7b282a 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProjectMSBuildExtension.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProjectMSBuildExtension.cs @@ -32,53 +32,45 @@ using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects.SharedAssetsProjects { - class SharedAssetsProjectMSBuildExtension: MSBuildExtension + [RegisterProjectModelExtension] + class SharedAssetsProjectMSBuildExtension: DotNetProjectExtension { - public override void LoadProject (IProgressMonitor monitor, SolutionEntityItem item, MSBuildProject msproject) + internal protected override void OnReadProject (ProgressMonitor monitor, MSBuildProject msproject) { - base.LoadProject (monitor, item, msproject); - - var dnp = item as DotNetProject; - if (dnp == null) - return; + base.OnReadProject (monitor, msproject); // Convert .projitems imports into project references foreach (var sp in msproject.Imports.Where (im => im.Label == "Shared" && im.Project.EndsWith (".projitems"))) { var projitemsFile = sp.Project; if (!string.IsNullOrEmpty (projitemsFile)) { - projitemsFile = MSBuildProjectService.FromMSBuildPath (item.ItemDirectory, projitemsFile); + projitemsFile = MSBuildProjectService.FromMSBuildPath (Project.ItemDirectory, projitemsFile); projitemsFile = Path.Combine (Path.GetDirectoryName (msproject.FileName), projitemsFile); if (File.Exists (projitemsFile)) { - MSBuildSerializer iser = Handler.CreateSerializer (); - iser.SerializationContext.BaseFile = projitemsFile; - iser.SerializationContext.ProgressMonitor = monitor; MSBuildProject p = new MSBuildProject (); p.Load (projitemsFile); - Handler.LoadProjectItems (p, iser, ProjectItemFlags.Hidden | ProjectItemFlags.DontPersist); + Project.LoadProjectItems (p, ProjectItemFlags.Hidden | ProjectItemFlags.DontPersist); var r = new ProjectReference (ReferenceType.Project, Path.GetFileNameWithoutExtension (projitemsFile)); r.Flags = ProjectItemFlags.DontPersist; r.SetItemsProjectPath (projitemsFile); - dnp.References.Add (r); + Project.References.Add (r); } } } } - public override void SaveProject (IProgressMonitor monitor, SolutionEntityItem item, MSBuildProject project) + internal protected override void OnWriteProject (ProgressMonitor monitor, MSBuildProject project) { - base.SaveProject (monitor, item, project); - var dnp = item as DotNetProject; - if (dnp == null) - return; + base.OnWriteProject (monitor, project); + HashSet<string> validProjitems = new HashSet<string> (); - foreach (var r in dnp.References.Where (rp => rp.ReferenceType == ReferenceType.Project)) { + foreach (var r in Project.References.Where (rp => rp.ReferenceType == ReferenceType.Project)) { var ip = r.GetItemsProjectPath (); if (!string.IsNullOrEmpty (ip)) { - ip = MSBuildProjectService.ToMSBuildPath (item.ItemDirectory, ip); + ip = MSBuildProjectService.ToMSBuildPath (Project.ItemDirectory, ip); validProjitems.Add (ip); if (!project.Imports.Any (im => im.Project == ip)) { - var im = project.AddNewImport (ip, project.Imports.FirstOrDefault (i => i.Label != "Shared")); + var im = project.AddNewImport (ip, beforeImport:project.Imports.FirstOrDefault (i => i.Label != "Shared")); im.Label = "Shared"; im.Condition = "Exists('" + ip + "')"; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProjectMSBuildHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProjectMSBuildHandler.cs deleted file mode 100644 index d64df4f8e0..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProjectMSBuildHandler.cs +++ /dev/null @@ -1,139 +0,0 @@ -// -// SharedProjectMSBuildHandler.cs -// -// Author: -// Lluis Sanchez <lluis@xamarin.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.Linq; -using System.Xml; -using System.IO; -using MonoDevelop.Core; -using System.Collections.Generic; -using MonoDevelop.Projects.Formats.MSBuild; - -namespace MonoDevelop.Projects.SharedAssetsProjects -{ - class SharedAssetsProjectMSBuildHandler: MSBuildProjectHandler - { - string projitemsFile; - - public SharedAssetsProjectMSBuildHandler () - { - } - - protected override void LoadProject (IProgressMonitor monitor, MSBuildProject msproject) - { - var doc = msproject.Document; - projitemsFile = null; - foreach (var no in doc.DocumentElement.ChildNodes) { - var im = no as XmlElement; - if (im != null && im.LocalName == "Import" && im.GetAttribute ("Label") == "Shared") { - projitemsFile = im.GetAttribute ("Project"); - break; - } - } - if (projitemsFile == null) - return; - - // TODO: load the type from msbuild - ((SharedAssetsProject)EntityItem).LanguageName = "C#"; - - projitemsFile = Path.Combine (Path.GetDirectoryName (msproject.FileName), projitemsFile); - - MSBuildProject p = new MSBuildProject (); - p.Load (projitemsFile); - - MSBuildSerializer ser = CreateSerializer (); - ser.SerializationContext.BaseFile = EntityItem.FileName; - ser.SerializationContext.ProgressMonitor = monitor; - - Item.SetItemHandler (this); - - var cp = p.PropertyGroups.FirstOrDefault (g => g.Label == "Configuration"); - if (cp != null) - ((SharedAssetsProject)EntityItem).DefaultNamespace = cp.GetPropertyValue ("Import_RootNamespace"); - - LoadProjectItems (p, ser, ProjectItemFlags.None); - } - - protected override MSBuildProject SaveProject (IProgressMonitor monitor) - { - MSBuildSerializer ser = CreateSerializer (); - ser.SerializationContext.BaseFile = EntityItem.FileName; - ser.SerializationContext.ProgressMonitor = monitor; - - MSBuildProject projitemsProject = new MSBuildProject (); - MSBuildProject msproject = new MSBuildProject (); - - var newProject = EntityItem.FileName == null || !File.Exists (EntityItem.FileName); - if (newProject) { - var grp = msproject.GetGlobalPropertyGroup (); - if (grp == null) - grp = msproject.AddNewPropertyGroup (false); - grp.SetPropertyValue ("ProjectGuid", EntityItem.ItemId, false); - var import = msproject.AddNewImport (@"$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"); - import.Condition = @"Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"; - msproject.AddNewImport (@"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props"); - msproject.AddNewImport (@"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props"); - import = msproject.AddNewImport (Path.ChangeExtension (EntityItem.FileName.FileName, ".projitems")); - import.Label = "Shared"; - msproject.AddNewImport (@"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets"); - } else { - msproject.Load (EntityItem.FileName); - } - - // having no ToolsVersion is equivalent to 2.0, roundtrip that correctly - if (ToolsVersion != "2.0") - msproject.ToolsVersion = ToolsVersion; - else if (string.IsNullOrEmpty (msproject.ToolsVersion)) - msproject.ToolsVersion = null; - else - msproject.ToolsVersion = "2.0"; - - if (projitemsFile == null) - projitemsFile = Path.ChangeExtension (EntityItem.FileName, ".projitems"); - if (File.Exists (projitemsFile)) { - projitemsProject.Load (projitemsFile); - } else { - var grp = projitemsProject.AddNewPropertyGroup (true); - grp.SetPropertyValue ("MSBuildAllProjects", "$(MSBuildAllProjects);$(MSBuildThisFileFullPath)", false); - grp.SetPropertyValue ("HasSharedItems", "true", false); - grp.SetPropertyValue ("SharedGUID", EntityItem.ItemId, false); - } - - var configGrp = projitemsProject.PropertyGroups.FirstOrDefault (g => g.Label == "Configuration"); - if (configGrp == null) { - configGrp = projitemsProject.AddNewPropertyGroup (true); - configGrp.Label = "Configuration"; - } - configGrp.SetPropertyValue ("Import_RootNamespace", ((SharedAssetsProject)EntityItem).DefaultNamespace, false); - - SaveProjectItems (monitor, new MSBuildFileFormatVS12 (), ser, projitemsProject, "$(MSBuildThisFileDirectory)"); - - projitemsProject.Save (projitemsFile); - - return msproject; - } - } -} - diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextEncoding.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextEncoding.cs index b1126af426..464366de6a 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextEncoding.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextEncoding.cs @@ -121,7 +121,7 @@ namespace MonoDevelop.Projects.Text PropertyService.SaveProperties (); } } - + public static TextEncoding GetEncoding (string id) { foreach (TextEncoding e in SupportedEncodings) { @@ -130,6 +130,15 @@ namespace MonoDevelop.Projects.Text } return null; } + + public static TextEncoding GetEncoding (int codePage) + { + foreach (TextEncoding e in SupportedEncodings) { + if (e.CodePage == codePage) + return e; + } + return null; + } public static string DefaultEncoding { get { return "UTF-8"; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildEventHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildEventHandler.cs index ef76135af7..596d7ee072 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildEventHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildEventHandler.cs @@ -35,10 +35,10 @@ namespace MonoDevelop.Projects public class BuildEventArgs: EventArgs { - IProgressMonitor monitor; + ProgressMonitor monitor; bool success; - public BuildEventArgs (IProgressMonitor monitor, bool success) + public BuildEventArgs (ProgressMonitor monitor, bool success) { this.monitor = monitor; this.success = success; @@ -48,7 +48,7 @@ namespace MonoDevelop.Projects this.FailedBuildCount = -1; } - public IProgressMonitor ProgressMonitor { + public ProgressMonitor ProgressMonitor { get { return monitor; } } @@ -72,7 +72,7 @@ namespace MonoDevelop.Projects get; set; } - public SolutionItem SolutionItem { + public SolutionFolderItem SolutionItem { get; set; } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildResult.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildResult.cs index 543ec390c1..9bfa798cba 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildResult.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildResult.cs @@ -43,6 +43,7 @@ namespace MonoDevelop.Projects string compilerOutput; List<BuildError> errors = new List<BuildError> (); IBuildTarget sourceTarget; + static BuildResult success = new BuildResult (); public BuildResult() { @@ -65,6 +66,18 @@ namespace MonoDevelop.Projects } } } + + public bool HasErrors { + get { return ErrorCount > 0; } + } + + public bool HasWarnings { + get { return WarningCount > 0; } + } + + public static BuildResult Success { + get { return success; } + } public ReadOnlyCollection<BuildError> Errors { get { return errors.AsReadOnly (); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildTool.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildTool.cs index 43b3b5cfd6..46479efbc9 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildTool.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/BuildTool.cs @@ -34,6 +34,7 @@ using Mono.Addins; using MonoDevelop.Core.ProgressMonitoring; using MonoDevelop.Core; using MonoDevelop.Core.Assemblies; +using System.Threading.Tasks; namespace MonoDevelop.Projects { @@ -48,6 +49,13 @@ namespace MonoDevelop.Projects public int Run (string[] arguments) { + var t = RunAsync (arguments); + t.Wait (); + return t.Result; + } + + public async Task<int> RunAsync (string[] arguments) + { Console.WriteLine (BrandingService.BrandApplicationName ("MonoDevelop Build Tool")); foreach (string s in arguments) ReadArgument (s); @@ -93,7 +101,7 @@ namespace MonoDevelop.Projects } } - IProgressMonitor monitor = new ConsoleProjectLoadProgressMonitor (new ConsoleProgressMonitor ()); + ProgressMonitor monitor = new ConsoleProjectLoadProgressMonitor (new ConsoleProgressMonitor ()); TargetRuntime targetRuntime = null; TargetRuntime defaultRuntime = Runtime.SystemAssemblyService.DefaultRuntime; @@ -106,11 +114,16 @@ namespace MonoDevelop.Projects IBuildTarget item; if (solFile != null) - item = Services.ProjectService.ReadWorkspaceItem (monitor, solFile); + item = await Services.ProjectService.ReadWorkspaceItem (monitor, solFile) as IBuildTarget; else - item = Services.ProjectService.ReadSolutionItem (monitor, itemFile); + item = await Services.ProjectService.ReadSolutionItem (monitor, itemFile); - using (var readItem = item) { + if (item == null) { + Console.WriteLine ("The file '" + file + "' can't be built"); + return 1; + } + + using (var readItem = (WorkspaceObject)item) { if (project != null) { Solution solution = item as Solution; item = null; @@ -130,21 +143,25 @@ namespace MonoDevelop.Projects monitor = new ConsoleProgressMonitor (); BuildResult res = null; - if (item is SolutionEntityItem && ((SolutionEntityItem)item).ParentSolution == null) { + if (item is SolutionItem && ((SolutionItem)item).ParentSolution == null) { ConfigurationSelector configuration = new ItemConfigurationSelector (config); - res = item.RunTarget (monitor, command, configuration); + if (command == ProjectService.BuildTarget) + res = await item.Build (monitor, configuration); + else if (command == ProjectService.CleanTarget) + res = await item.Clean (monitor, configuration); } else { ConfigurationSelector configuration = new SolutionConfigurationSelector (config); - SolutionEntityItem solutionEntityItem = item as SolutionEntityItem; + SolutionItem solutionEntityItem = item as SolutionItem; if (solutionEntityItem != null) { if (command == ProjectService.BuildTarget) - res = solutionEntityItem.Build (monitor, configuration, true); + res = await solutionEntityItem.Build (monitor, configuration, true); else if (command == ProjectService.CleanTarget) - solutionEntityItem.Clean (monitor, configuration); - else - res = item.RunTarget (monitor, command, configuration); + await solutionEntityItem.Clean (monitor, configuration); + else if (solutionEntityItem is Project) + res = await ((Project)item).RunTarget (monitor, command, configuration); } else { - res = item.RunTarget (monitor, command, configuration); + Console.WriteLine ("The project '" + project + "' can't be built"); + return 1; } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ChainedExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ChainedExtension.cs new file mode 100644 index 0000000000..48af259aba --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ChainedExtension.cs @@ -0,0 +1,94 @@ +// +// ChainedExtension.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Reflection; +using System.Linq; +using System.Collections.Generic; + +namespace MonoDevelop.Projects +{ + public class ChainedExtension: IDisposable + { + ChainedExtension nextInChain; + + internal protected static T FindNextImplementation<T> (ChainedExtension next) where T:class + { + if (next == null) + return null; + + var nextT = next as T; + if (nextT == null) + return FindNextImplementation<T> (next.nextInChain); + + foreach (var m in typeof(T).GetMembers (BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { + MethodInfo method = m as MethodInfo; + if (method == null) { + var prop = m as PropertyInfo; + if (prop != null) { + method = prop.GetGetMethod (); + if (method == null) + method = prop.GetSetMethod (); + } + } + if (method != null && method.IsVirtual && method.Name != "InitializeChain") { + var tm = next.GetType ().GetMethod (method.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, method.GetParameters ().Select (p=>p.ParameterType).ToArray (), null); + if (tm == null) + continue; + if (tm.DeclaringType != typeof(T)) + return nextT; + } + } + + return FindNextImplementation<T> (next.nextInChain); + } + + internal void Init (ChainedExtension next) + { + nextInChain = next; + InitializeChain (next); + } + + internal protected virtual void InitializeChain (ChainedExtension next) + { + } + + internal ChainedExtension Next { + get { return nextInChain; } + } + + internal void DisposeChain () + { + Dispose (); + if (nextInChain != null) + nextInChain.DisposeChain (); + } + + public virtual void Dispose () + { + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CleanEventHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CleanEventHandler.cs index 5d0dc54264..430a43b3fe 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CleanEventHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CleanEventHandler.cs @@ -33,14 +33,14 @@ namespace MonoDevelop.Projects public class CleanEventArgs : EventArgs { - IProgressMonitor monitor; + ProgressMonitor monitor; - public CleanEventArgs (IProgressMonitor monitor) + public CleanEventArgs (ProgressMonitor monitor) { this.monitor = monitor; } - public IProgressMonitor Monitor { + public ProgressMonitor Monitor { get { return monitor; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CombineEntryRenamedEventArgs.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CombineEntryRenamedEventArgs.cs index 41f95e07ad..c7efbcef93 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CombineEntryRenamedEventArgs.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CombineEntryRenamedEventArgs.cs @@ -37,7 +37,7 @@ namespace MonoDevelop.Projects string oldName; string newName; - public SolutionItemRenamedEventArgs (SolutionItem node, string oldName, string newName) + public SolutionItemRenamedEventArgs (SolutionFolderItem node, string oldName, string newName) : base (node) { this.oldName = oldName; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CompiledAssemblyProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CompiledAssemblyProject.cs index 481bcd9ecf..bbaa65182f 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CompiledAssemblyProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CompiledAssemblyProject.cs @@ -34,6 +34,9 @@ using MonoDevelop.Core.ProgressMonitoring; using System.Collections.Generic; using Mono.Cecil.Mdb; using Mono.Cecil.Cil; +using System.Threading.Tasks; +using MonoDevelop.Core.Serialization; +using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects { @@ -46,9 +49,9 @@ namespace MonoDevelop.Projects AddNewConfiguration ("Default"); } - public override IEnumerable<string> GetProjectTypes () + protected override void OnGetProjectTypes (HashSet<string> types) { - yield return "CompiledAssembly"; + types.Add ("CompiledAssembly"); } public override IconId StockIcon { @@ -137,17 +140,12 @@ namespace MonoDevelop.Projects return string.Join (Path.DirectorySeparatorChar.ToString (), s1, 0, n); } - protected override BuildResult OnBuild (IProgressMonitor monitor, ConfigurationSelector configuration) + protected override Task<BuildResult> OnBuild (ProgressMonitor monitor, ConfigurationSelector configuration) { - return new BuildResult (); + return Task.FromResult (BuildResult.Success); } - internal protected override bool OnGetNeedsBuilding (ConfigurationSelector configuration) - { - return false; - } - - internal protected override void OnExecute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + protected async override Task OnExecute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) { ProjectConfiguration conf = (ProjectConfiguration) GetConfiguration (configuration); monitor.Log.WriteLine (GettextCatalog.GetString ("Running {0} ...", FileName)); @@ -156,8 +154,6 @@ namespace MonoDevelop.Projects ? context.ExternalConsoleFactory.CreateConsole (!conf.PauseConsoleOutput) : context.ConsoleFactory.CreateConsole (!conf.PauseConsoleOutput); - AggregatedOperationMonitor aggregatedOperationMonitor = new AggregatedOperationMonitor (monitor); - try { try { ExecutionCommand executionCommand = CreateExecutionCommand (configuration, conf); @@ -167,14 +163,16 @@ namespace MonoDevelop.Projects return; } - IProcessAsyncOperation asyncOp = context.ExecutionHandler.Execute (executionCommand, console); - aggregatedOperationMonitor.AddOperation (asyncOp); - asyncOp.WaitForCompleted (); + ProcessAsyncOperation asyncOp = context.ExecutionHandler.Execute (executionCommand, console); + var stopper = monitor.CancellationToken.Register (asyncOp.Cancel); + + await asyncOp.Task; + + stopper.Dispose (); monitor.Log.WriteLine (GettextCatalog.GetString ("The application exited with code: {0}", asyncOp.ExitCode)); } finally { console.Dispose (); - aggregatedOperationMonitor.Dispose (); } } catch (Exception ex) { LoggingService.LogError (string.Format ("Cannot execute \"{0}\"", FileName), ex); @@ -182,7 +180,7 @@ namespace MonoDevelop.Projects } } - internal protected override bool OnGetCanExecute (ExecutionContext context, ConfigurationSelector configuration) + protected override bool OnGetCanExecute (ExecutionContext context, ConfigurationSelector configuration) { ProjectConfiguration config = (ProjectConfiguration) GetConfiguration (configuration); if (config == null) @@ -201,6 +199,32 @@ namespace MonoDevelop.Projects cmd.EnvironmentVariables = new Dictionary<string, string> (configuration.EnvironmentVariables); return cmd; } + + public override bool HasSlnData { + get { + return true; + } + } + + public override DataItem WriteSlnData () + { + DataSerializer ser = new DataSerializer (MSBuildProjectService.DataContext); + ser.SerializationContext.BaseFile = FileName; + ser.SerializationContext.DirectorySeparatorChar = '\\'; + DataItem data = (DataItem) ser.Serialize (this, typeof(CompiledAssemblyProject)); + return data; + } + + public override void ReadSlnData (DataItem item) + { + // Remove the default configuration, since new ones will be loaded + Configurations.Clear (); + + DataSerializer ser = new DataSerializer (MSBuildProjectService.DataContext); + ser.SerializationContext.BaseFile = FileName; + ser.SerializationContext.DirectorySeparatorChar = '\\'; + ser.Deserialize (this, item); + } } public class CompiledAssemblyExtension: ProjectServiceExtension @@ -212,22 +236,17 @@ namespace MonoDevelop.Projects return base.IsSolutionItemFile (fileName); } - protected override SolutionEntityItem LoadSolutionItem (IProgressMonitor monitor, string fileName) + protected override Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, string fileName) { if (fileName.ToLower().EndsWith (".exe") || fileName.ToLower().EndsWith (".dll")) { - CompiledAssemblyProject p = new CompiledAssemblyProject (); - p.LoadFrom (fileName); - return p; + return Task<SolutionItem>.Factory.StartNew (delegate { + CompiledAssemblyProject p = new CompiledAssemblyProject (); + p.LoadFrom (fileName); + return p; + }); } return base.LoadSolutionItem (monitor, fileName); } - - public override void Save (IProgressMonitor monitor, SolutionEntityItem item) - { -// if (item is CompiledAssemblyProject) -// return; - base.Save (monitor, item); - } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ConfigurationEventHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ConfigurationEventHandler.cs index fdec84f720..61d537dd5f 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ConfigurationEventHandler.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ConfigurationEventHandler.cs @@ -38,7 +38,7 @@ namespace MonoDevelop.Projects { ItemConfiguration configuration; - public ConfigurationEventArgs (SolutionEntityItem entry, ItemConfiguration configuration): base (entry) + public ConfigurationEventArgs (SolutionItem entry, ItemConfiguration configuration): base (entry) { this.configuration = configuration; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CustomCommand.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CustomCommand.cs index 115edb369f..f74b6f51e8 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CustomCommand.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CustomCommand.cs @@ -35,6 +35,8 @@ using MonoDevelop.Core.Execution; using MonoDevelop.Core.StringParsing; using System.Collections.Generic; using MonoDevelop.Core.ProgressMonitoring; +using System.Threading.Tasks; +using System.Threading; namespace MonoDevelop.Projects { @@ -98,7 +100,7 @@ namespace MonoDevelop.Projects set { pauseExternalConsole = value; } } - public string GetCommandFile (IWorkspaceObject entry, ConfigurationSelector configuration) + public string GetCommandFile (WorkspaceObject entry, ConfigurationSelector configuration) { string exe, args; StringTagModel tagSource = GetTagModel (entry, configuration); @@ -106,7 +108,7 @@ namespace MonoDevelop.Projects return exe; } - public string GetCommandArgs (IWorkspaceObject entry, ConfigurationSelector configuration) + public string GetCommandArgs (WorkspaceObject entry, ConfigurationSelector configuration) { string exe, args; StringTagModel tagSource = GetTagModel (entry, configuration); @@ -114,7 +116,7 @@ namespace MonoDevelop.Projects return args; } - public FilePath GetCommandWorkingDir (IWorkspaceObject entry, ConfigurationSelector configuration) + public FilePath GetCommandWorkingDir (WorkspaceObject entry, ConfigurationSelector configuration) { StringTagModel tagSource = GetTagModel (entry, configuration); if (string.IsNullOrEmpty (workingdir)) @@ -135,10 +137,10 @@ namespace MonoDevelop.Projects return cmd; } - StringTagModel GetTagModel (IWorkspaceObject entry, ConfigurationSelector configuration) + StringTagModel GetTagModel (WorkspaceObject entry, ConfigurationSelector configuration) { - if (entry is SolutionItem) - return ((SolutionItem)entry).GetStringTagModel (configuration); + if (entry is SolutionFolderItem) + return ((SolutionFolderItem)entry).GetStringTagModel (configuration); else if (entry is WorkspaceItem) return ((WorkspaceItem)entry).GetStringTagModel (); else @@ -172,7 +174,7 @@ namespace MonoDevelop.Projects args = StringParserService.Parse (args, tagSource); } - public ProcessExecutionCommand CreateExecutionCommand (IWorkspaceObject entry, ConfigurationSelector configuration) + public ProcessExecutionCommand CreateExecutionCommand (WorkspaceObject entry, ConfigurationSelector configuration) { string exe, args; StringTagModel tagSource = GetTagModel (entry, configuration); @@ -207,12 +209,12 @@ namespace MonoDevelop.Projects return cmd; } - public void Execute (IProgressMonitor monitor, IWorkspaceObject entry, ConfigurationSelector configuration) + public Task<bool> Execute (ProgressMonitor monitor, WorkspaceObject entry, ConfigurationSelector configuration) { - Execute (monitor, entry, null, configuration); + return Execute (monitor, entry, null, configuration); } - public bool CanExecute (IWorkspaceObject entry, ExecutionContext context, ConfigurationSelector configuration) + public bool CanExecute (WorkspaceObject entry, ExecutionContext context, ConfigurationSelector configuration) { if (string.IsNullOrEmpty (command)) return false; @@ -224,7 +226,7 @@ namespace MonoDevelop.Projects return context.ExecutionHandler.CanExecute (cmd); } - public void Execute (IProgressMonitor monitor, IWorkspaceObject entry, ExecutionContext context, + public async Task<bool> Execute (ProgressMonitor monitor, WorkspaceObject entry, ExecutionContext context, ConfigurationSelector configuration) { ProcessExecutionCommand cmd = CreateExecutionCommand (entry, configuration); @@ -233,12 +235,12 @@ namespace MonoDevelop.Projects if (!Directory.Exists (cmd.WorkingDirectory)) { monitor.ReportError (GettextCatalog.GetString ("Custom command working directory does not exist"), null); - return; + return false; } - AggregatedOperationMonitor aggMon = null; - IProcessAsyncOperation oper = null; + ProcessAsyncOperation oper = null; IConsole console = null; + var result = true; try { if (context != null) { @@ -254,35 +256,33 @@ namespace MonoDevelop.Projects cmd.WorkingDirectory, console, null); } else { oper = Runtime.ProcessService.StartProcess (cmd.Command, cmd.Arguments, - cmd.WorkingDirectory, monitor.Log, monitor.Log, null, false); + cmd.WorkingDirectory, monitor.Log, monitor.Log, null, false).ProcessAsyncOperation; } } - aggMon = new AggregatedOperationMonitor (monitor, oper); - oper.WaitForCompleted (); - if (!oper.Success) { + + var stopper = monitor.CancellationToken.Register (oper.Cancel); + + await oper.Task; + + stopper.Dispose (); + + if (oper.ExitCode != 0) { monitor.ReportError ("Custom command failed (exit code: " + oper.ExitCode + ")", null); } } catch (Win32Exception w32ex) { monitor.ReportError (GettextCatalog.GetString ("Failed to execute custom command '{0}': {1}", cmd.Command, w32ex.Message), null); - return; + return false; } catch (Exception ex) { LoggingService.LogError ("Command execution failed", ex); throw new UserException (GettextCatalog.GetString ("Command execution failed: {0}", ex.Message)); } finally { - if (oper == null || !oper.Success) { - monitor.AsyncOperation.Cancel (); - } - if (oper != null) { - oper.Dispose (); - } + result = oper != null && oper.ExitCode == 0; if (console != null) { console.Dispose (); } - if (aggMon != null) { - aggMon.Dispose (); - } } + return result; } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CustomCommandCollection.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CustomCommandCollection.cs index 797ee6da64..ee37994054 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CustomCommandCollection.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CustomCommandCollection.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using MonoDevelop.Core; +using System.Threading.Tasks; namespace MonoDevelop.Projects { @@ -48,19 +49,20 @@ namespace MonoDevelop.Projects Add (cmd.Clone ()); } - public void ExecuteCommand (IProgressMonitor monitor, IWorkspaceObject entry, CustomCommandType type, ConfigurationSelector configuration) + public Task<bool> ExecuteCommand (ProgressMonitor monitor, WorkspaceObject entry, CustomCommandType type, ConfigurationSelector configuration) { - ExecuteCommand (monitor, entry, type, null, configuration); + return ExecuteCommand (monitor, entry, type, null, configuration); } - public void ExecuteCommand (IProgressMonitor monitor, IWorkspaceObject entry, CustomCommandType type, ExecutionContext context, ConfigurationSelector configuration) + public async Task<bool> ExecuteCommand (ProgressMonitor monitor, WorkspaceObject entry, CustomCommandType type, ExecutionContext context, ConfigurationSelector configuration) { foreach (CustomCommand cmd in this) { - if (cmd.Type == type) - cmd.Execute (monitor, entry, context, configuration); - if (monitor.IsCancelRequested) - break; + if (cmd.Type == type) { + if (!await cmd.Execute (monitor, entry, context, configuration)) + return false; + } } + return true; } public bool HasCommands (CustomCommandType type) @@ -71,7 +73,7 @@ namespace MonoDevelop.Projects return false; } - public bool CanExecute (IWorkspaceObject entry, CustomCommandType type, ExecutionContext context, ConfigurationSelector configuration) + public bool CanExecute (WorkspaceObject entry, CustomCommandType type, ExecutionContext context, ConfigurationSelector configuration) { // Note: if this gets changed to return true if *any* of the commands can execute, then // ExecuteCommand() needs to be fixed to only execute commands that can be executed. diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CustomCommandExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CustomCommandExtension.cs deleted file mode 100644 index fc0a9bf77b..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CustomCommandExtension.cs +++ /dev/null @@ -1,101 +0,0 @@ -// CustomCommandExtension.cs -// -// Author: -// Lluis Sanchez Gual <lluis@novell.com> -// -// Copyright (c) 2007 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 MonoDevelop.Core; -using System.CodeDom.Compiler; -using MonoDevelop.Core.Execution; - -namespace MonoDevelop.Projects -{ - internal class CustomCommandExtension: ProjectServiceExtension - { - protected override BuildResult Build (IProgressMonitor monitor, SolutionEntityItem entry, ConfigurationSelector configuration) - { - SolutionItemConfiguration conf = entry.GetConfiguration (configuration) as SolutionItemConfiguration; - if (conf != null) { - if (conf.CustomCommands.CanExecute (entry, CustomCommandType.BeforeBuild, null, configuration)) - conf.CustomCommands.ExecuteCommand (monitor, entry, CustomCommandType.BeforeBuild, configuration); - - if (monitor.IsCancelRequested) - return new BuildResult (new CompilerResults (null), ""); - } - - BuildResult res = base.Build (monitor, entry, configuration); - - if (conf != null && !monitor.IsCancelRequested && !res.Failed) { - if (conf.CustomCommands.CanExecute (entry, CustomCommandType.AfterBuild, null, configuration)) - conf.CustomCommands.ExecuteCommand (monitor, entry, CustomCommandType.AfterBuild, configuration); - } - - return res; - } - - protected override void Clean (IProgressMonitor monitor, SolutionEntityItem entry, ConfigurationSelector configuration) - { - SolutionItemConfiguration conf = entry.GetConfiguration (configuration) as SolutionItemConfiguration; - if (conf != null) { - if (conf.CustomCommands.CanExecute (entry, CustomCommandType.BeforeClean, null, configuration)) - conf.CustomCommands.ExecuteCommand (monitor, entry, CustomCommandType.BeforeClean, configuration); - - if (monitor.IsCancelRequested) - return; - } - - base.Clean (monitor, entry, configuration); - - if (conf != null && !monitor.IsCancelRequested) { - if (conf.CustomCommands.CanExecute (entry, CustomCommandType.AfterClean, null, configuration)) - conf.CustomCommands.ExecuteCommand (monitor, entry, CustomCommandType.AfterClean, configuration); - } - } - - protected override void Execute (IProgressMonitor monitor, SolutionEntityItem entry, ExecutionContext context, ConfigurationSelector configuration) - { - SolutionItemConfiguration conf = entry.GetConfiguration (configuration) as SolutionItemConfiguration; - if (conf != null) { - ExecutionContext localContext = new ExecutionContext (Runtime.ProcessService.DefaultExecutionHandler, context.ConsoleFactory, context.ExecutionTarget); - - if (conf.CustomCommands.CanExecute (entry, CustomCommandType.BeforeExecute, localContext, configuration)) - conf.CustomCommands.ExecuteCommand (monitor, entry, CustomCommandType.BeforeExecute, localContext, configuration); - - if (monitor.IsCancelRequested) - return; - } - - base.Execute (monitor, entry, context, configuration); - - if (conf != null && !monitor.IsCancelRequested) { - ExecutionContext localContext = new ExecutionContext (Runtime.ProcessService.DefaultExecutionHandler, context.ConsoleFactory, context.ExecutionTarget); - - if (conf.CustomCommands.CanExecute (entry, CustomCommandType.AfterExecute, localContext, configuration)) - conf.CustomCommands.ExecuteCommand (monitor, entry, CustomCommandType.AfterExecute, localContext, configuration); - } - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetAssemblyProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetAssemblyProject.cs deleted file mode 100644 index cba01e80c1..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetAssemblyProject.cs +++ /dev/null @@ -1,108 +0,0 @@ -// -// DotNetAssemblyProject.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.Xml; -using MonoDevelop.Core; -using MonoDevelop.Core.Assemblies; - -namespace MonoDevelop.Projects -{ - [ProjectModelDataItem ("DotNetProject")] - public class DotNetAssemblyProject: DotNetProject - { - public DotNetAssemblyProject () - { - } - - public DotNetAssemblyProject (string languageName) : base (languageName) - { - } - - public DotNetAssemblyProject (string languageName, ProjectCreateInformation projectCreateInfo, XmlElement projectOptions): - base (languageName, projectCreateInfo, projectOptions) - { - } - - public override System.Collections.Generic.IEnumerable<string> GetProjectTypes () - { - yield return "DotNetAssembly"; - foreach (var pt in base.GetProjectTypes ()) - yield return pt; - } - - public override bool SupportsFramework (TargetFramework framework) - { - // DotNetAssemblyProject can only generate assemblies for the regular framework. - // Special frameworks such as Moonlight or MonoTouch must subclass DotNetProject directly. - if (!framework.CanReferenceAssembliesTargetingFramework (TargetFrameworkMoniker.NET_1_1)) - return false; - - return base.SupportsFramework (framework); - } - - public override TargetFrameworkMoniker GetDefaultTargetFrameworkForFormat (FileFormat format) - { - switch (format.Id) { - case "MSBuild05": - return TargetFrameworkMoniker.NET_2_0; - case "MSBuild08": - return TargetFrameworkMoniker.NET_2_0; - case "MSBuild10": - case "MSBuild12": - return TargetFrameworkMoniker.NET_4_0; - } - return Services.ProjectService.DefaultTargetFramework.Id; - } - - protected override string GetDefaultTargetPlatform (ProjectCreateInformation projectCreateInfo) - { - if (CompileTarget == CompileTarget.Library) - return string.Empty; - - // Guess a good default platform for the project - if (projectCreateInfo.ParentFolder != null && projectCreateInfo.ParentFolder.ParentSolution != null) { - ItemConfiguration conf = projectCreateInfo.ParentFolder.ParentSolution.GetConfiguration (projectCreateInfo.ActiveConfiguration); - if (conf != null) - return conf.Platform; - else { - string curName, curPlatform, bestPlatform = null; - string sconf = projectCreateInfo.ActiveConfiguration.ToString (); - ItemConfiguration.ParseConfigurationId (sconf, out curName, out curPlatform); - foreach (ItemConfiguration ic in projectCreateInfo.ParentFolder.ParentSolution.Configurations) { - if (ic.Platform == curPlatform) - return curPlatform; - if (ic.Name == curName) - bestPlatform = ic.Platform; - } - if (bestPlatform != null) - return bestPlatform; - } - } - return Services.ProjectService.DefaultPlatformTarget; - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ConfigurationParameters.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetCompilerParameters.cs index 9ff5f46356..c45f57b51c 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ConfigurationParameters.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetCompilerParameters.cs @@ -31,9 +31,7 @@ using System.Linq; namespace MonoDevelop.Projects { - ///<summary>This should really be called DotNetCompilerParameters</summary> - [DataItem (FallbackType=typeof(UnknownCompilationParameters))] - public abstract class ConfigurationParameters: ProjectParameters + public abstract class DotNetCompilerParameters: ProjectParameters { DotNetProjectConfiguration configuration; @@ -58,9 +56,9 @@ namespace MonoDevelop.Projects return GetDefineSymbols ().Any (s => s == symbol); } - public new ConfigurationParameters Clone () + public new DotNetCompilerParameters Clone () { - return (ConfigurationParameters) base.Clone (); + return (DotNetCompilerParameters) base.Clone (); } public DotNetProjectConfiguration ParentConfiguration { @@ -71,11 +69,7 @@ namespace MonoDevelop.Projects ParentProject = configuration.ParentItem; } } - } - public abstract class DotNetConfigurationParameters : ConfigurationParameters - { - public abstract bool NoStdLib { get; set; } + public virtual bool NoStdLib { get; set; } } - } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs index d6a7a8c7f0..e2148a39ac 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs @@ -2,7 +2,7 @@ // // 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 @@ -44,53 +44,67 @@ using MonoDevelop.Projects.Extensions; using MonoDevelop.Projects.Formats.MSBuild; using MonoDevelop.Core.Assemblies; using System.Globalization; +using System.Threading.Tasks; +using Mono.Addins; namespace MonoDevelop.Projects { - [DataInclude(typeof(DotNetProjectConfiguration))] - [ProjectModelDataItem ("AbstractDotNetProject")] - public abstract class DotNetProject : Project, IAssemblyProject, IDotNetFileContainer + public class DotNetProject : Project, IAssemblyProject, IDotNetFileContainer { bool usePartialTypes = true; - ProjectParameters languageParameters; DirectoryAssemblyContext privateAssemblyContext; ComposedAssemblyContext composedAssemblyContext; IAssemblyContext currentRuntimeContext; - [ItemProperty("OutputType")] CompileTarget compileTarget; IDotNetLanguageBinding languageBinding; protected ProjectReferenceCollection projectReferences; - [ItemProperty("RootNamespace", DefaultValue = "")] protected string defaultNamespace = String.Empty; - - public DotNetProject () + + protected DotNetProject () { - Runtime.SystemAssemblyService.DefaultRuntimeChanged += RuntimeSystemAssemblyServiceDefaultRuntimeChanged; + Initialize (this); + } + + protected DotNetProject (string languageName, params string[] flavorIds): base (flavorIds) + { + this.languageName = languageName; + Initialize (this); + } + + protected override void OnInitialize () + { + UseMSBuildEngineByDefault = true; + RequireMSBuildEngine = false; projectReferences = new ProjectReferenceCollection (); Items.Bind (projectReferences); - if (IsLibraryBasedProjectType) - CompileTarget = CompileTarget.Library; FileService.FileRemoved += OnFileRemoved; + Runtime.SystemAssemblyService.DefaultRuntimeChanged += RuntimeSystemAssemblyServiceDefaultRuntimeChanged; + + base.OnInitialize (); + + if (languageName == null) + languageName = MSBuildProjectService.GetLanguageFromGuid (TypeGuid); } - public DotNetProject (string languageName) : this() + protected override void OnExtensionChainInitialized () { - // Language name must be set before the item handler is assigned - this.languageName = languageName; - this.languageBinding = FindLanguage (languageName); - - if (this.languageBinding != null) - this.StockIcon = this.languageBinding.ProjectStockIcon; + base.OnExtensionChainInitialized (); this.usePartialTypes = SupportsPartialTypes; + + if (LanguageBinding != null) + this.StockIcon = LanguageBinding.ProjectStockIcon; + + if (IsLibraryBasedProjectType) + CompileTarget = CompileTarget.Library; } - public DotNetProject (string languageName, ProjectCreateInformation projectCreateInfo, XmlElement projectOptions) : this(languageName) + public override void InitializeNew (ProjectCreateInformation projectCreateInfo, XmlElement projectOptions) { if ((projectOptions != null) && (projectOptions.Attributes ["Target"] != null)) CompileTarget = (CompileTarget)Enum.Parse (typeof(CompileTarget), projectOptions.Attributes ["Target"].Value); @@ -98,8 +112,7 @@ namespace MonoDevelop.Projects CompileTarget = CompileTarget.Library; if (this.LanguageBinding != null) { - LanguageParameters = languageBinding.CreateProjectParameters (projectOptions); - + bool externalConsole = false; string platform = null; @@ -117,22 +130,22 @@ namespace MonoDevelop.Projects } string platformSuffix = string.IsNullOrEmpty (platform) ? string.Empty : "|" + platform; DotNetProjectConfiguration configDebug = CreateConfiguration ("Debug" + platformSuffix) as DotNetProjectConfiguration; - configDebug.CompilationParameters = languageBinding.CreateCompilationParameters (projectOptions); + configDebug.CompilationParameters = LanguageBinding.CreateCompilationParameters (projectOptions); configDebug.DebugMode = true; configDebug.ExternalConsole = externalConsole; configDebug.PauseConsoleOutput = externalConsole; Configurations.Add (configDebug); DotNetProjectConfiguration configRelease = CreateConfiguration ("Release" + platformSuffix) as DotNetProjectConfiguration; - + if (projectOptions != null) { XmlElement releaseProjectOptions = (XmlElement)projectOptions.CloneNode (true); releaseProjectOptions.SetAttribute ("Release", "True"); - configRelease.CompilationParameters = languageBinding.CreateCompilationParameters (releaseProjectOptions); + configRelease.CompilationParameters = LanguageBinding.CreateCompilationParameters (releaseProjectOptions); } else { - configRelease.CompilationParameters = languageBinding.CreateCompilationParameters (null); + configRelease.CompilationParameters = LanguageBinding.CreateCompilationParameters (null); } - + configRelease.CompilationParameters.RemoveDefineSymbol ("DEBUG"); configRelease.DebugMode = false; configRelease.ExternalConsole = externalConsole; @@ -184,9 +197,32 @@ namespace MonoDevelop.Projects return Runtime.SystemAssemblyService.GetTargetFramework (moniker); } - public override IEnumerable<string> GetProjectTypes () + protected override void OnGetProjectTypes (HashSet<string> types) + { + types.Add ("DotNet"); + types.Add ("DotNetAssembly"); + } + + DotNetProjectExtension projectExtension; + DotNetProjectExtension ProjectExtension { + get { + if (projectExtension == null) + projectExtension = ExtensionChain.GetExtension<DotNetProjectExtension> (); + return projectExtension; + } + } + + protected override IEnumerable<WorkspaceObjectExtension> CreateDefaultExtensions () + { + return base.CreateDefaultExtensions ().Concat (Enumerable.Repeat (new DefaultDotNetProjectExtension (), 1)); + } + + protected override ProjectItem OnCreateProjectItem (IMSBuildItemEvaluated item) { - yield return "DotNet"; + if (item.Name == "Reference" || item.Name == "ProjectReference") + return new ProjectReference (); + + return base.OnCreateProjectItem (item); } private string languageName; @@ -195,22 +231,53 @@ namespace MonoDevelop.Projects } public override string[] SupportedLanguages { - get { return new string[] {"",languageName}; } + get { return ProjectExtension.SupportedLanguages; } } public virtual bool IsLibraryBasedProjectType { - get { return false; } + get { return ProjectExtension.IsLibraryBasedProjectType; } + } + + public bool IsPortableLibrary { + get { return GetService<PortableDotNetProjectFlavor> () != null; } } public virtual bool GeneratesDebugInfoFile { - get { return true; } + get { return ProjectExtension.GeneratesDebugInfoFile; } } - - protected virtual string GetDefaultTargetPlatform (ProjectCreateInformation projectCreateInfo) + + protected string GetDefaultTargetPlatform (ProjectCreateInformation projectCreateInfo) { - return string.Empty; + return ProjectExtension.OnGetDefaultTargetPlatform (projectCreateInfo); } - + + protected virtual string OnGetDefaultTargetPlatform (ProjectCreateInformation projectCreateInfo) + { + if (CompileTarget == CompileTarget.Library) + return string.Empty; + + // Guess a good default platform for the project + if (projectCreateInfo.ParentFolder != null && projectCreateInfo.ParentFolder.ParentSolution != null) { + ItemConfiguration conf = projectCreateInfo.ParentFolder.ParentSolution.GetConfiguration (projectCreateInfo.ActiveConfiguration); + if (conf != null) + return conf.Platform; + else { + string curName, curPlatform, bestPlatform = null; + string sconf = projectCreateInfo.ActiveConfiguration.ToString (); + ItemConfiguration.ParseConfigurationId (sconf, out curName, out curPlatform); + foreach (ItemConfiguration ic in projectCreateInfo.ParentFolder.ParentSolution.Configurations) { + if (ic.Platform == curPlatform) + return curPlatform; + if (ic.Name == curName) + bestPlatform = ic.Platform; + } + if (bestPlatform != null) + return bestPlatform; + } + } + return Services.ProjectService.DefaultPlatformTarget; + } + public ProjectReferenceCollection References { get { return projectReferences; } } @@ -229,6 +296,10 @@ namespace MonoDevelop.Projects public virtual bool CanReferenceProject (DotNetProject targetProject, out string reason) { + return ProjectExtension.OnGetCanReferenceProject (targetProject, out reason); + } + bool CheckCanReferenceProject (DotNetProject targetProject, out string reason) + { if (!TargetFramework.CanReferenceAssembliesTargetingFramework (targetProject.TargetFramework)) { reason = GettextCatalog.GetString ("Incompatible target framework: {0}", targetProject.TargetFramework.Id); return false; @@ -264,20 +335,6 @@ namespace MonoDevelop.Projects } } - [ItemProperty("LanguageParameters")] - public ProjectParameters LanguageParameters { - get { - if (languageParameters == null && LanguageBinding != null) - LanguageParameters = LanguageBinding.CreateProjectParameters (null); - return languageParameters; - } - internal set { - languageParameters = value; - if (languageParameters != null) - languageParameters.ParentProject = this; - } - } - /// <summary> /// Default namespace setting. May be empty, use GetDefaultNamespace to get a usable value. /// </summary> @@ -288,7 +345,7 @@ namespace MonoDevelop.Projects NotifyModified ("DefaultNamespace"); } } - + /// <summary> /// Given a namespace, removes from it the implicit namespace of the project, /// if there is one. This depends on the target language. For example, in VB.NET @@ -296,7 +353,7 @@ namespace MonoDevelop.Projects /// </summary> public string StripImplicitNamespace (string ns) { - if ((LanguageParameters is DotNetProjectParameters) && ((DotNetProjectParameters)LanguageParameters).DefaultNamespaceIsImplicit) { + if (DefaultNamespaceIsImplicit) { if (DefaultNamespace.Length > 0 && ns.StartsWith (DefaultNamespace + ".")) return ns.Substring (DefaultNamespace.Length + 1); else if (DefaultNamespace == ns) @@ -305,23 +362,26 @@ namespace MonoDevelop.Projects return ns; } + public bool DefaultNamespaceIsImplicit { get; set; } + IResourceHandler resourceHandler; public IResourceHandler ResourceHandler { get { if (resourceHandler == null) { DotNetNamingPolicy pol = Policies.Get<DotNetNamingPolicy> (); - if (pol.ResourceNamePolicy == ResourceNamePolicy.FileFormatDefault) - resourceHandler = ItemHandler as IResourceHandler; - else if (pol.ResourceNamePolicy == ResourceNamePolicy.MSBuild) - resourceHandler = MSBuildProjectService.GetResourceHandlerForItem (this); - if (resourceHandler == null) + if (pol.ResourceNamePolicy == ResourceNamePolicy.FileFormatDefault || pol.ResourceNamePolicy == ResourceNamePolicy.MSBuild) { + resourceHandler = GetService<IResourceHandler> (); + if (resourceHandler == null) + resourceHandler = MSBuildResourceHandler.Instance; + } + else resourceHandler = DefaultResourceHandler.Instance; } return resourceHandler; } } - + TargetFramework targetFramework; public TargetFramework TargetFramework { @@ -350,18 +410,23 @@ namespace MonoDevelop.Projects public TargetRuntime TargetRuntime { get { return Runtime.SystemAssemblyService.DefaultRuntime; } } - + /// <summary> /// Gets the target framework for new projects /// </summary> /// <returns> /// The default target framework identifier. /// </returns> - public virtual TargetFrameworkMoniker GetDefaultTargetFrameworkId () + public TargetFrameworkMoniker GetDefaultTargetFrameworkId () + { + return ProjectExtension.OnGetDefaultTargetFrameworkId (); + } + + protected virtual TargetFrameworkMoniker OnGetDefaultTargetFrameworkId () { return Services.ProjectService.DefaultTargetFramework.Id; } - + /// <summary> /// Returns the default framework for a given format /// </summary> @@ -375,11 +440,25 @@ namespace MonoDevelop.Projects /// This method is used to determine what's the correct target framework for a project /// deserialized using a specific format. /// </remarks> - public virtual TargetFrameworkMoniker GetDefaultTargetFrameworkForFormat (FileFormat format) + public TargetFrameworkMoniker GetDefaultTargetFrameworkForFormat (FileFormat format) + { + return ProjectExtension.OnGetDefaultTargetFrameworkForFormat (format); + } + + protected virtual TargetFrameworkMoniker OnGetDefaultTargetFrameworkForFormat (FileFormat format) { + switch (format.Id) { + case "MSBuild05": + return TargetFrameworkMoniker.NET_2_0; + case "MSBuild08": + return TargetFrameworkMoniker.NET_2_0; + case "MSBuild10": + case "MSBuild12": + return TargetFrameworkMoniker.NET_4_0; + } return GetDefaultTargetFrameworkId (); } - + public IAssemblyContext AssemblyContext { get { if (composedAssemblyContext == null) { @@ -400,8 +479,17 @@ namespace MonoDevelop.Projects } } - public virtual bool SupportsFramework (TargetFramework framework) + public bool SupportsFramework (TargetFramework framework) + { + return ProjectExtension.OnGetSupportsFramework (framework); + } + + protected virtual bool OnSupportsFramework (TargetFramework framework) { + // DotNetAssemblyProject can only generate assemblies for the regular framework. + // Special frameworks such as Moonlight or MonoTouch must override SupportsFramework. + if (!framework.CanReferenceAssembliesTargetingFramework (TargetFrameworkMoniker.NET_1_1)) + return false; if (LanguageBinding == null) return false; ClrVersion[] versions = LanguageBinding.GetSupportedClrVersions (); @@ -414,7 +502,6 @@ namespace MonoDevelop.Projects return false; } - [ItemProperty(DefaultValue = true)] public bool UsePartialTypes { get { return usePartialTypes; } set { usePartialTypes = value; } @@ -426,16 +513,16 @@ namespace MonoDevelop.Projects composedAssemblyContext.Dispose (); // composedAssemblyContext = null; } - + // languageParameters = null; // privateAssemblyContext = null; // currentRuntimeContext = null; // languageBinding = null; // projectReferences = null; - + Runtime.SystemAssemblyService.DefaultRuntimeChanged -= RuntimeSystemAssemblyServiceDefaultRuntimeChanged; FileService.FileRemoved -= OnFileRemoved; - + base.Dispose (); } @@ -477,7 +564,7 @@ namespace MonoDevelop.Projects // cached and won't take into account unsubscriptions until the next dispatch if (Disposed) return; - + base.OnFileChanged (source, e); foreach (FileEventInfo ei in e) CheckReferenceChange (ei.FileName); @@ -501,24 +588,24 @@ namespace MonoDevelop.Projects this.References.Add (prNew); } } - + internal protected override void PopulateOutputFileList (List<FilePath> list, ConfigurationSelector configuration) { base.PopulateOutputFileList (list, configuration); DotNetProjectConfiguration conf = GetConfiguration (configuration) as DotNetProjectConfiguration; - + // Debug info file - + if (conf.DebugMode) { string mdbFile = TargetRuntime.GetAssemblyDebugInfoFile (conf.CompiledOutputName); list.Add (mdbFile); } - + // Generated satellite resource files - + FilePath outputDir = conf.OutputDirectory; string satelliteAsmName = Path.GetFileNameWithoutExtension (conf.CompiledOutputName) + ".resources.dll"; - + HashSet<string> cultures = new HashSet<string> (); foreach (ProjectFile finfo in Files) { if (finfo.Subtype == Subtype.Directory || finfo.BuildAction != BuildAction.EmbeddedResource) @@ -532,7 +619,7 @@ namespace MonoDevelop.Projects } } } - + [ThreadStatic] static int supportReferDistance; [ThreadStatic] @@ -561,13 +648,13 @@ namespace MonoDevelop.Projects //rename the app.config file list.Remove ("app.config"); list.Remove ("App.config"); - + ProjectFile appConfig = Files.FirstOrDefault (f => f.FilePath.FileName.Equals ("app.config", StringComparison.CurrentCultureIgnoreCase)); if (appConfig != null) { string output = GetOutputFileName (configuration).FileName; list.Add (appConfig.FilePath, true, output + ".config"); } - + //collect all the "local copy" references and their attendant files foreach (ProjectReference projectReference in References) { if (!projectReference.LocalCopy || !projectReference.CanSetLocalCopy) @@ -614,7 +701,7 @@ namespace MonoDevelop.Projects } } } - + //Given a filename like foo.it.resx, get 'it', if its //a valid culture //Note: hand-written as this can get called lotsa times @@ -664,14 +751,14 @@ namespace MonoDevelop.Projects return cultureNamesTable; } } - + IEnumerable<string> GetAssemblyRefsRec (string fileName, HashSet<string> visited) { // Recursivelly finds assemblies referenced by the given assembly - + if (!visited.Add (fileName)) yield break; - + if (!File.Exists (fileName)) { string ext = Path.GetExtension (fileName).ToLower (); if (ext == ".dll" || ext == ".exe") @@ -683,7 +770,7 @@ namespace MonoDevelop.Projects else yield break; } - + yield return fileName; foreach (var reference in SystemAssemblyService.GetAssemblyReferences (fileName)) { @@ -705,9 +792,9 @@ namespace MonoDevelop.Projects return newReferenceInformation; } - public override IEnumerable<SolutionItem> GetReferencedItems (ConfigurationSelector configuration) + protected override IEnumerable<SolutionItem> OnGetReferencedItems (ConfigurationSelector configuration) { - List<SolutionItem> items = new List<SolutionItem> (base.GetReferencedItems (configuration)); + var items = new List<SolutionItem> (base.OnGetReferencedItems (configuration)); if (ParentSolution == null) return items; @@ -745,23 +832,24 @@ namespace MonoDevelop.Projects /// </param> public IEnumerable<string> GetReferencedAssemblies (ConfigurationSelector configuration, bool includeProjectReferences) { - return Services.ProjectService.GetExtensionChain (this).GetReferencedAssemblies (this, configuration, includeProjectReferences); + return ProjectExtension.OnGetReferencedAssemblies (configuration, includeProjectReferences); } internal protected virtual IEnumerable<string> OnGetReferencedAssemblies (ConfigurationSelector configuration, bool includeProjectReferences) { - IAssemblyReferenceHandler handler = this.ItemHandler as IAssemblyReferenceHandler; - if (handler != null) { + if (CheckUseMSBuildEngine (configuration)) { if (includeProjectReferences) { foreach (ProjectReference pref in References.Where (pr => pr.ReferenceType == ReferenceType.Project)) { foreach (string asm in pref.GetReferencedFileNames (configuration)) yield return asm; } } - foreach (string file in handler.GetAssemblyReferences (configuration)) - yield return file; - } - else { + // Get the references list from the msbuild project + RemoteProjectBuilder builder = GetProjectBuilder (); + var configs = GetConfigurations (configuration); + foreach (var r in builder.ResolveAssemblyReferences (configs)) + yield return r; + } else { foreach (ProjectReference pref in References) { if (includeProjectReferences || pref.ReferenceType != ReferenceType.Project) { foreach (string asm in pref.GetReferencedFileNames (configuration)) @@ -772,12 +860,8 @@ namespace MonoDevelop.Projects var config = (DotNetProjectConfiguration)GetConfiguration (configuration); bool noStdLib = false; - if (config != null) { - var parameters = config.CompilationParameters as DotNetConfigurationParameters; - if (parameters != null) { - noStdLib = parameters.NoStdLib; - } - } + if (config != null) + noStdLib = config.CompilationParameters.NoStdLib; // System.Core is an implicit reference if (!noStdLib) { @@ -787,13 +871,13 @@ namespace MonoDevelop.Projects } } - protected internal override void OnSave (IProgressMonitor monitor) + protected internal override Task OnSave (ProgressMonitor monitor) { // Make sure the fx version is sorted out before saving - // to avoid changes in project references while saving + // to avoid changes in project references while saving if (targetFramework == null) targetFramework = Runtime.SystemAssemblyService.GetTargetFramework (GetDefaultTargetFrameworkForFormat (FileFormat)); - base.OnSave (monitor); + return base.OnSave (monitor); } IDotNetLanguageBinding FindLanguage (string name) @@ -839,7 +923,7 @@ namespace MonoDevelop.Projects { if (base.CheckNeedsBuild (configuration)) return true; - + // base.CheckNeedsBuild() checks Project references, but not Assembly, Package, or Custom. DateTime mtime = GetLastBuildTime (configuration); foreach (ProjectReference pref in References) { @@ -875,11 +959,11 @@ namespace MonoDevelop.Projects && String.Compare (Path.GetExtension (file.FilePath), ".resx", StringComparison.OrdinalIgnoreCase) == 0 && MD1DotNetProjectHandler.IsResgenRequired (file.FilePath, config.IntermediateOutputDirectory.Combine (file.ResourceId))); } - + protected internal override DateTime OnGetLastBuildTime (ConfigurationSelector configuration) { var outputBuildTime = base.OnGetLastBuildTime (configuration); - + //if the debug file is newer than the output file, use that as the build time var conf = (DotNetProjectConfiguration) GetConfiguration (configuration); if (GeneratesDebugInfoFile && conf != null && conf.DebugMode) { @@ -896,7 +980,7 @@ namespace MonoDevelop.Projects } return outputBuildTime; } - + public IList<string> GetUserAssemblyPaths (ConfigurationSelector configuration) { if (ParentSolution == null) @@ -908,7 +992,12 @@ namespace MonoDevelop.Projects .Where (d => !string.IsNullOrEmpty (d)).ToList (); } - protected virtual ExecutionCommand CreateExecutionCommand (ConfigurationSelector configSel, DotNetProjectConfiguration configuration) + public ExecutionCommand CreateExecutionCommand (ConfigurationSelector configSel, DotNetProjectConfiguration configuration) + { + return ProjectExtension.OnCreateExecutionCommand (configSel, configuration); + } + + internal protected virtual ExecutionCommand OnCreateExecutionCommand (ConfigurationSelector configSel, DotNetProjectConfiguration configuration) { DotNetExecutionCommand cmd = new DotNetExecutionCommand (configuration.CompiledOutputName); cmd.Arguments = configuration.CommandLineParameters; @@ -919,7 +1008,7 @@ namespace MonoDevelop.Projects return cmd; } - protected internal override bool OnGetCanExecute (ExecutionContext context, ConfigurationSelector configuration) + protected override bool OnGetCanExecute (ExecutionContext context, ConfigurationSelector configuration) { DotNetProjectConfiguration config = (DotNetProjectConfiguration) GetConfiguration (configuration); if (config == null) @@ -931,10 +1020,11 @@ namespace MonoDevelop.Projects return (compileTarget == CompileTarget.Exe || compileTarget == CompileTarget.WinExe) && context.ExecutionHandler.CanExecute (cmd); } - protected internal override List<FilePath> OnGetItemFiles (bool includeReferencedFiles) + protected override IEnumerable<FilePath> OnGetItemFiles (bool includeReferencedFiles) { - List<FilePath> col = base.OnGetItemFiles (includeReferencedFiles); + var baseFiles = base.OnGetItemFiles (includeReferencedFiles); if (includeReferencedFiles) { + List<FilePath> col = new List<FilePath> (); foreach (ProjectReference pref in References) { if (pref.ReferenceType == ReferenceType.Assembly) { foreach (var f in pref.GetReferencedFileNames (DefaultConfiguration.Selector)) @@ -945,10 +1035,20 @@ namespace MonoDevelop.Projects if (c.SignAssembly) col.Add (c.AssemblyKeyFile); } + baseFiles = baseFiles.Concat (col); } - return col; + return baseFiles; + } + + internal Task<BuildResult> Compile (ProgressMonitor monitor, BuildData buildData) + { + return ProjectExtension.OnCompile (monitor, buildData); } + protected virtual Task<BuildResult> OnCompile (ProgressMonitor monitor, BuildData buildData) + { + return MD1DotNetProjectHandler.Compile (monitor, this, buildData); + } public override bool IsCompileable (string fileName) { @@ -979,7 +1079,7 @@ namespace MonoDevelop.Projects string defaultNmspc = !string.IsNullOrEmpty (defaultNamespace) ? defaultNamespace : SanitisePotentialNamespace (project.Name) ?? "Application"; - + if (string.IsNullOrEmpty (fileName)) { return defaultNmspc; } @@ -1088,22 +1188,6 @@ namespace MonoDevelop.Projects return BuildAction.DotNetCommonActions; } - internal override void SetItemHandler (ISolutionItemHandler handler) - { - if (ProjectExtensionUtil.GetItemHandler (this) == null) { - // Initial assignment of the item handler - base.SetItemHandler (handler); - return; - } - IResourceHandler rh = ResourceHandler; - - base.SetItemHandler (handler); - resourceHandler = null; - // A change in the file format may imply a change in the resource naming policy. - // Make sure that the resource Id don't change. - MigrateResourceIds (rh, ResourceHandler); - } - protected override void OnEndLoad () { // The resource handler policy may have changed after loading, so reset any @@ -1112,7 +1196,7 @@ namespace MonoDevelop.Projects // Just after loading, the resource Ids are using the file format's policy. // They have to be converted to the new policy - IResourceHandler handler = ItemHandler as IResourceHandler; + IResourceHandler handler = GetService<IResourceHandler> (); if (handler != null) MigrateResourceIds (handler, ResourceHandler); @@ -1172,13 +1256,13 @@ namespace MonoDevelop.Projects internal void NotifyReferenceRemovedFromProject (ProjectReference reference) { NotifyModified ("References"); - OnReferenceRemovedFromProject (new ProjectReferenceEventArgs (this, reference)); + ProjectExtension.OnReferenceRemovedFromProject (new ProjectReferenceEventArgs (this, reference)); } internal void NotifyReferenceAddedToProject (ProjectReference reference) { NotifyModified ("References"); - OnReferenceAddedToProject (new ProjectReferenceEventArgs (this, reference)); + ProjectExtension.OnReferenceAddedToProject (new ProjectReferenceEventArgs (this, reference)); } protected virtual void OnReferenceRemovedFromProject (ProjectReferenceEventArgs e) @@ -1205,7 +1289,7 @@ namespace MonoDevelop.Projects CheckReferenceChange (ei.FileName); } - protected override void DoExecute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + protected async override Task DoExecute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) { DotNetProjectConfiguration dotNetProjectConfig = GetConfiguration (configuration) as DotNetProjectConfiguration; monitor.Log.WriteLine (GettextCatalog.GetString ("Running {0} ...", dotNetProjectConfig.CompiledOutputName)); @@ -1213,8 +1297,6 @@ namespace MonoDevelop.Projects IConsole console = dotNetProjectConfig.ExternalConsole ? context.ExternalConsoleFactory.CreateConsole (!dotNetProjectConfig.PauseConsoleOutput) : context.ConsoleFactory.CreateConsole (!dotNetProjectConfig.PauseConsoleOutput); - - AggregatedOperationMonitor aggregatedOperationMonitor = new AggregatedOperationMonitor (monitor); try { try { @@ -1227,14 +1309,16 @@ namespace MonoDevelop.Projects return; } - IProcessAsyncOperation asyncOp = context.ExecutionHandler.Execute (executionCommand, console); - aggregatedOperationMonitor.AddOperation (asyncOp); - asyncOp.WaitForCompleted (); + ProcessAsyncOperation asyncOp = context.ExecutionHandler.Execute (executionCommand, console); + var stopper = monitor.CancellationToken.Register (asyncOp.Cancel); + + await asyncOp.Task; + + stopper.Dispose (); monitor.Log.WriteLine (GettextCatalog.GetString ("The application exited with code: {0}", asyncOp.ExitCode)); } finally { console.Dispose (); - aggregatedOperationMonitor.Dispose (); } } catch (Exception ex) { LoggingService.LogError (string.Format ("Cannot execute \"{0}\"", dotNetProjectConfig.CompiledOutputName), ex); @@ -1242,32 +1326,158 @@ namespace MonoDevelop.Projects } } - public void AddImportIfMissing (string name, string condition) + + protected override void OnReadProjectHeader (ProgressMonitor monitor, MSBuildProject msproject) { - importsAdded.Add (new DotNetProjectImport (name, condition)); + base.OnReadProjectHeader (monitor, msproject); + + compileTarget = msproject.EvaluatedProperties.GetValue<CompileTarget> ("OutputType"); + defaultNamespace = msproject.EvaluatedProperties.GetValue ("RootNamespace", string.Empty); + usePartialTypes = msproject.EvaluatedProperties.GetValue ("UsePartialTypes", true); + + string frameworkIdentifier = msproject.EvaluatedProperties.GetValue ("TargetFrameworkIdentifier"); + string frameworkVersion = msproject.EvaluatedProperties.GetValue ("TargetFrameworkVersion"); + string frameworkProfile = msproject.EvaluatedProperties.GetValue ("TargetFrameworkProfile"); + + //determine the default target framework from the project type's default + //overridden by the components in the project + var def = GetDefaultTargetFrameworkForFormat (GetFileFormat (GetToolsFormat ())); + var targetFx = new TargetFrameworkMoniker ( + string.IsNullOrEmpty (frameworkIdentifier)? def.Identifier : frameworkIdentifier, + string.IsNullOrEmpty (frameworkVersion)? def.Version : frameworkVersion, + string.IsNullOrEmpty (frameworkProfile)? def.Profile : frameworkProfile); + + + string fx = ExtendedProperties ["InternalTargetFrameworkVersion"] as string; + if (!string.IsNullOrEmpty (fx)) { + targetFx = TargetFrameworkMoniker.Parse (fx); + ExtendedProperties.Remove ("InternalTargetFrameworkVersion"); + } + + TargetFramework = Runtime.SystemAssemblyService.GetTargetFramework (targetFx); } - public void RemoveImport (string name) + protected override void OnWriteProjectHeader (ProgressMonitor monitor, MSBuildProject msproject) { - importsRemoved.Add (new DotNetProjectImport (name)); + base.OnWriteProjectHeader (monitor, msproject); + + IMSBuildPropertySet globalGroup = msproject.GetGlobalPropertyGroup (); + + globalGroup.SetValue ("OutputType", compileTarget); + globalGroup.SetValue ("RootNamespace", defaultNamespace, string.Empty); + globalGroup.SetValue ("UsePartialTypes", usePartialTypes, true); } - List <DotNetProjectImport> importsAdded = new List<DotNetProjectImport> (); + protected override void OnWriteProject (ProgressMonitor monitor, MSBuildProject msproject) + { + base.OnWriteProject (monitor, msproject); + + var toolsFormat = GetToolsFormat (); + var moniker = TargetFramework.Id; + bool supportsMultipleFrameworks = toolsFormat.SupportsMonikers || toolsFormat.SupportedFrameworks.Length > 0; + var def = GetDefaultTargetFrameworkForFormat (GetFileFormat (toolsFormat)); + + IMSBuildPropertySet globalGroup = msproject.GetGlobalPropertyGroup (); - internal IList<DotNetProjectImport> ImportsAdded { - get { return importsAdded; } + // If the format only supports one fx version, or the version is the default, there is no need to store it. + // However, is there is already a value set, do not remove it. + if (supportsMultipleFrameworks) { + globalGroup.SetValue ("TargetFrameworkVersion", "v" + moniker.Version, "v" + def.Version, true); + } + + if (toolsFormat.SupportsMonikers) { + globalGroup.SetValue ("TargetFrameworkIdentifier", moniker.Identifier, def.Identifier, true); + globalGroup.SetValue ("TargetFrameworkProfile", moniker.Profile, def.Profile, true); + } } - List <DotNetProjectImport> importsRemoved = new List<DotNetProjectImport> (); + protected override void OnWriteConfiguration (ProgressMonitor monitor, ProjectConfiguration config, IMSBuildPropertySet pset) + { + base.OnWriteConfiguration (monitor, config, pset); + if (pset.Project.IsNewProject) + pset.SetValue ("ErrorReport", "prompt"); + + } - internal IList<DotNetProjectImport> ImportsRemoved { - get { return importsRemoved; } + FileFormat GetFileFormat (MSBuildFileFormat fmt) + { + return new FileFormat (fmt, fmt.Id, fmt.Name); } - public void ImportsSaved () + internal class DefaultDotNetProjectExtension: DotNetProjectExtension { - importsAdded.Clear (); - importsRemoved.Clear (); + internal protected override bool IsLibraryBasedProjectType { + get { + return false; + } + } + + internal protected override bool GeneratesDebugInfoFile { + get { + return true; + } + } + + internal protected override bool SupportsPartialTypes { + get { + return Project.SupportsPartialTypes; + } + } + + + internal protected override bool OnGetCanReferenceProject (DotNetProject targetProject, out string reason) + { + return Project.CheckCanReferenceProject (targetProject, out reason); + } + + internal protected override string OnGetDefaultTargetPlatform (ProjectCreateInformation projectCreateInfo) + { + return Project.OnGetDefaultTargetPlatform (projectCreateInfo); + } + + internal protected override IEnumerable<string> OnGetReferencedAssemblies (ConfigurationSelector configuration, bool includeProjectReferences) + { + return Project.OnGetReferencedAssemblies (configuration, includeProjectReferences); + } + + internal protected override ExecutionCommand OnCreateExecutionCommand (ConfigurationSelector configSel, DotNetProjectConfiguration configuration) + { + return Project.OnCreateExecutionCommand (configSel, configuration); + } + + internal protected override void OnReferenceRemovedFromProject (ProjectReferenceEventArgs e) + { + Project.OnReferenceRemovedFromProject (e); + } + + internal protected override void OnReferenceAddedToProject (ProjectReferenceEventArgs e) + { + Project.OnReferenceAddedToProject (e); + } + + internal protected override Task<BuildResult> OnCompile (ProgressMonitor monitor, BuildData buildData) + { + return Project.OnCompile (monitor, buildData); + } + + #region Framework management + + internal protected override TargetFrameworkMoniker OnGetDefaultTargetFrameworkId () + { + return Project.OnGetDefaultTargetFrameworkId (); + } + + internal protected override TargetFrameworkMoniker OnGetDefaultTargetFrameworkForFormat (FileFormat format) + { + return Project.OnGetDefaultTargetFrameworkForFormat (format); + } + + internal protected override bool OnGetSupportsFramework (TargetFramework framework) + { + return Project.OnSupportsFramework (framework); + } + + #endregion } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectBinding.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectBinding.cs deleted file mode 100644 index e2e26591cb..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectBinding.cs +++ /dev/null @@ -1,76 +0,0 @@ -// -// DotNetProjectBinding.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (C) 2005 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.Xml; -using MonoDevelop.Core.Serialization; -using MonoDevelop.Core; - -namespace MonoDevelop.Projects -{ - public class DotNetProjectBinding : IProjectBinding - { - public virtual string Name { - get { return "DotNet"; } - } - - public Project CreateProject (ProjectCreateInformation info, XmlElement projectOptions) - { - string lang = projectOptions.GetAttribute ("language"); - return CreateProject (lang, info, projectOptions); - } - - protected virtual DotNetProject CreateProject (string languageName, ProjectCreateInformation info, XmlElement projectOptions) - { - return new DotNetAssemblyProject (languageName, info, projectOptions); - } - - public Project CreateSingleFileProject (string file) - { - IDotNetLanguageBinding binding = LanguageBindingService.GetBindingPerFileName (file) as IDotNetLanguageBinding; - if (binding != null) { - ProjectCreateInformation info = new ProjectCreateInformation (); - info.ProjectName = Path.GetFileNameWithoutExtension (file); - info.SolutionPath = Path.GetDirectoryName (file); - info.ProjectBasePath = Path.GetDirectoryName (file); - Project project = CreateProject (binding.Language, info, null); - project.Files.Add (new ProjectFile (file)); - return project; - } - return null; - } - - public bool CanCreateSingleFileProject (string file) - { - IDotNetLanguageBinding binding = LanguageBindingService.GetBindingPerFileName (file) as IDotNetLanguageBinding; - return binding != null; - } - - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectConfiguration.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectConfiguration.cs index ed6bb65fa5..5d359650c8 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectConfiguration.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectConfiguration.cs @@ -35,6 +35,7 @@ using MonoDevelop.Core.Serialization; using MonoDevelop.Core.Assemblies; using MonoDevelop.Core.StringParsing; using System.Collections.Generic; +using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects { @@ -47,14 +48,10 @@ namespace MonoDevelop.Projects public class DotNetProjectConfiguration: ProjectConfiguration { - [MonoDevelop.Projects.Formats.MSBuild.MergeToProject] - [ItemProperty ("AssemblyName")] string assembly; - - ConfigurationParameters compilationParameters; - string sourcePath; - + DotNetCompilerParameters compilationParameters; + public DotNetProjectConfiguration () { } @@ -63,32 +60,49 @@ namespace MonoDevelop.Projects { } - [MonoDevelop.Projects.Formats.MSBuild.MergeToProject] - [ItemProperty("SignAssembly", DefaultValue = false)] + internal protected override void Read (IMSBuildPropertySet pset, MSBuildFileFormat format) + { + base.Read (pset, format); + + assembly = pset.GetValue ("AssemblyName"); + signAssembly = pset.GetValue<bool> ("SignAssembly"); + delaySign = pset.GetValue<bool> ("DelaySign"); + assemblyKeyFile = pset.GetPathValue ("AssemblyOriginatorKeyFile"); + if (string.IsNullOrEmpty (assemblyKeyFile)) + assemblyKeyFile = pset.GetPathValue ("AssemblyKeyFile"); + if (compilationParameters != null) + ((IMSBuildDataObject)compilationParameters).Read (pset, format); + } + + internal protected override void Write (IMSBuildPropertySet pset, MSBuildFileFormat format) + { + base.Write (pset, format); + pset.SetValue ("AssemblyName", assembly, mergeToMainGroup: true); + pset.SetValue ("SignAssembly", signAssembly, defaultValue:false, mergeToMainGroup: true); + pset.SetValue ("DelaySign", delaySign, defaultValue:false, mergeToMainGroup:true); + pset.SetValue ("AssemblyOriginatorKeyFile", assemblyKeyFile, mergeToMainGroup:true); + if (compilationParameters != null) + ((IMSBuildDataObject)compilationParameters).Write (pset, format); + } + private bool signAssembly = false; public bool SignAssembly { get { return signAssembly; } set { signAssembly = value; } } - [MonoDevelop.Projects.Formats.MSBuild.MergeToProject] - [ItemProperty("DelaySign", DefaultValue = false)] private bool delaySign = false; public bool DelaySign { get { return delaySign; } set { delaySign = value; } } - [MonoDevelop.Projects.Formats.MSBuild.MergeToProject] - [ProjectPathItemProperty("AssemblyKeyFile", ReadOnly=true)] internal string OldAssemblyKeyFile { set { assemblyKeyFile = value; } } - [MonoDevelop.Projects.Formats.MSBuild.MergeToProject] - [ProjectPathItemProperty("AssemblyOriginatorKeyFile", DefaultValue = "")] - private string assemblyKeyFile = ""; - public string AssemblyKeyFile { + private FilePath assemblyKeyFile = FilePath.Empty; + public FilePath AssemblyKeyFile { get { return assemblyKeyFile; } set { assemblyKeyFile = value; } } @@ -149,8 +163,7 @@ namespace MonoDevelop.Projects } } - [ItemProperty ("CodeGeneration")] - public ConfigurationParameters CompilationParameters { + public DotNetCompilerParameters CompilationParameters { get { return compilationParameters; } set { compilationParameters = value; @@ -159,16 +172,6 @@ namespace MonoDevelop.Projects } } - public ProjectParameters ProjectParameters { - get { - DotNetProject dnp = ParentItem as DotNetProject; - if (dnp != null) - return dnp.LanguageParameters; - else - return null; - } - } - public FilePath CompiledOutputName { get { FilePath fullPath = OutputDirectory.Combine (OutputAssembly); @@ -206,7 +209,7 @@ namespace MonoDevelop.Projects } } - public class UnknownCompilationParameters: ConfigurationParameters, IExtendedDataItem + public class UnknownCompilationParameters: DotNetCompilerParameters, IExtendedDataItem { readonly Hashtable table = new Hashtable (); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectExtension.cs new file mode 100644 index 0000000000..09f2a6b871 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectExtension.cs @@ -0,0 +1,132 @@ +// +// DotNetProjectExtension.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Assemblies; +using MonoDevelop.Core.Execution; +using MonoDevelop.Core; +using System.Threading.Tasks; + +namespace MonoDevelop.Projects +{ + public class DotNetProjectExtension: ProjectExtension + { + #region Project properties + + DotNetProjectExtension next; + + internal protected override void InitializeChain (ChainedExtension next) + { + base.InitializeChain (next); + this.next = FindNextImplementation<DotNetProjectExtension> (next); + } + + internal protected override bool SupportsObject (WorkspaceObject item) + { + return base.SupportsObject (item) && (item is DotNetProject); + } + + new public DotNetProject Project { + get { return (DotNetProject)base.Item; } + } + + + internal protected virtual bool IsLibraryBasedProjectType { + get { + return next.IsLibraryBasedProjectType; + } + } + + internal protected virtual bool GeneratesDebugInfoFile { + get { + return next.GeneratesDebugInfoFile; + } + } + + internal protected virtual bool SupportsPartialTypes { + get { + return next.SupportsPartialTypes; + } + } + + #endregion + + internal protected virtual bool OnGetCanReferenceProject (DotNetProject targetProject, out string reason) + { + return next.OnGetCanReferenceProject (targetProject, out reason); + } + + internal protected virtual string OnGetDefaultTargetPlatform (ProjectCreateInformation projectCreateInfo) + { + return next.OnGetDefaultTargetPlatform (projectCreateInfo); + } + + internal protected virtual IEnumerable<string> OnGetReferencedAssemblies (ConfigurationSelector configuration, bool includeProjectReferences) + { + return next.OnGetReferencedAssemblies (configuration, includeProjectReferences); + } + + internal protected virtual ExecutionCommand OnCreateExecutionCommand (ConfigurationSelector configSel, DotNetProjectConfiguration configuration) + { + return next.OnCreateExecutionCommand (configSel, configuration); + } + + internal protected virtual void OnReferenceRemovedFromProject (ProjectReferenceEventArgs e) + { + next.OnReferenceRemovedFromProject (e); + } + + internal protected virtual void OnReferenceAddedToProject (ProjectReferenceEventArgs e) + { + next.OnReferenceAddedToProject (e); + } + + #region Framework management + + internal protected virtual TargetFrameworkMoniker OnGetDefaultTargetFrameworkId () + { + return next.OnGetDefaultTargetFrameworkId (); + } + + internal protected virtual TargetFrameworkMoniker OnGetDefaultTargetFrameworkForFormat (FileFormat format) + { + return next.OnGetDefaultTargetFrameworkForFormat (format); + } + + internal protected virtual bool OnGetSupportsFramework (TargetFramework framework) + { + return next.OnGetSupportsFramework (framework); + } + + internal protected virtual Task<BuildResult> OnCompile (ProgressMonitor monitor, BuildData buildData) + { + return next.OnCompile (monitor, buildData); + } + + #endregion + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ExtensionChain.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ExtensionChain.cs new file mode 100644 index 0000000000..9916adc7bd --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ExtensionChain.cs @@ -0,0 +1,69 @@ +// +// ExtensionChain.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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; + +namespace MonoDevelop.Projects +{ + public class ExtensionChain + { + ChainedExtension first; + Dictionary<Type,ChainedExtension> chains = new Dictionary<Type, ChainedExtension> (); + + public static ExtensionChain Create<T> (T[] extensions) where T:ChainedExtension + { + for (int n = extensions.Length - 2; n >= 0; n--) + extensions [n].Init (extensions [n + 1]); + return new ExtensionChain { + first = extensions[0] + }; + } + + public T GetExtension<T> () where T:ChainedExtension + { + ChainedExtension e; + if (!chains.TryGetValue (typeof(T), out e)) { + chains [typeof(T)] = e = ChainedExtension.FindNextImplementation<T> (first); + } + return (T)e; + } + + public IEnumerable<ChainedExtension> GetAllExtensions () + { + var e = first; + while (e != null) { + yield return e; + e = e.Next; + } + } + + public void Dispose () + { + first.DisposeChain (); + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/GenericProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/GenericProject.cs index 9e14d17bb6..7e324914a0 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/GenericProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/GenericProject.cs @@ -49,9 +49,9 @@ namespace MonoDevelop.Projects return conf; } - public override IEnumerable<string> GetProjectTypes () + protected override void OnGetProjectTypes (HashSet<string> types) { - yield return "GenericProject"; + types.Add ("GenericProject"); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/GenericProjectBinding.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/GenericProjectBinding.cs deleted file mode 100644 index a0adbabee8..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/GenericProjectBinding.cs +++ /dev/null @@ -1,59 +0,0 @@ -// -// MakefileProjectBinding.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (C) 2007 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.Xml; -using MonoDevelop.Core.Serialization; -using MonoDevelop.Core; -using MonoDevelop.Projects; - -namespace MonoDevelop.Projects -{ - public class GenericProjectBinding : IProjectBinding - { - public virtual string Name { - get { return "GenericProject"; } - } - - public Project CreateProject (ProjectCreateInformation info, XmlElement projectOptions) - { - return new GenericProject (info, projectOptions); - } - - public Project CreateSingleFileProject (string file) - { - return null; - } - - public bool CanCreateSingleFileProject (string file) - { - return false; - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IBuildTarget.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IBuildTarget.cs index 46f53edd81..bc882c60d3 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IBuildTarget.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IBuildTarget.cs @@ -29,19 +29,17 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using MonoDevelop.Core; +using System.Threading.Tasks; namespace MonoDevelop.Projects { - public interface IBuildTarget: IWorkspaceObject + public interface IBuildTarget { - BuildResult RunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration); - bool SupportsTarget (string target); - void Execute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration); + Task<BuildResult> Build (ProgressMonitor monitor, ConfigurationSelector configuration, bool buildReferencedTargets = false); + Task<BuildResult> Clean (ProgressMonitor monitor, ConfigurationSelector configuration); + Task Execute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration); bool CanExecute (ExecutionContext context, ConfigurationSelector configuration); bool NeedsBuilding (ConfigurationSelector configuration); - void SetNeedsBuilding (bool needsBuilding, ConfigurationSelector configuration); - -// ReadOnlyCollection<string> GetConfigurations (); -// event EventHandler ConfigurationsChanged; + string Name { get; } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IDotNetLanguageBinding.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IDotNetLanguageBinding.cs index 618219d4a3..9b30606afb 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IDotNetLanguageBinding.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IDotNetLanguageBinding.cs @@ -38,10 +38,9 @@ namespace MonoDevelop.Projects get; } - ConfigurationParameters CreateCompilationParameters (XmlElement projectOptions); - ProjectParameters CreateProjectParameters (XmlElement projectOptions); + DotNetCompilerParameters CreateCompilationParameters (XmlElement projectOptions); - BuildResult Compile (ProjectItemCollection items, DotNetProjectConfiguration configuration, ConfigurationSelector configSelector, IProgressMonitor monitor); + BuildResult Compile (ProjectItemCollection items, DotNetProjectConfiguration configuration, ConfigurationSelector configSelector, ProgressMonitor monitor); ClrVersion[] GetSupportedClrVersions (); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectParameters.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IMSBuildDataObject.cs index 013b99f441..2c94b3c395 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProjectParameters.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IMSBuildDataObject.cs @@ -1,21 +1,21 @@ -// -// DotNetProjectParameters.cs -// +// +// IProjectConfigurationData.cs +// // Author: -// Lluis Sanchez Gual <lluis@novell.com> -// -// Copyright (c) 2009 Novell, Inc (http://www.novell.com) -// +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 @@ -23,18 +23,15 @@ // 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 MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects { - /// <summary> - /// This is the base class for project parameter classes. - /// </summary> - public class DotNetProjectParameters: ProjectParameters + public interface IMSBuildDataObject { - public virtual bool DefaultNamespaceIsImplicit { - get { return false; } - } + void Read (IMSBuildPropertySet pset, MSBuildFileFormat format); + void Write (IMSBuildPropertySet pset, MSBuildFileFormat format); } } + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SimpleProjectItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IProject.cs index 633e82f83d..286b4abe95 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SimpleProjectItem.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IProject.cs @@ -1,21 +1,21 @@ -// -// SimpleProjectItem.cs -// +// +// IProject.cs +// // Author: -// Lluis Sanchez Gual <lluis@novell.com> -// -// Copyright (c) 2009 Novell, Inc (http://www.novell.com) -// +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 @@ -23,15 +23,12 @@ // 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 MonoDevelop.Core.Serialization; namespace MonoDevelop.Projects { - public class SimpleProjectItem: ProjectItem + public interface IProject2 { - [ItemProperty ("Include")] - public string Include { get; set; } } } + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IProjectBinding.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IProjectBinding.cs deleted file mode 100644 index 5c0ce972f9..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IProjectBinding.cs +++ /dev/null @@ -1,59 +0,0 @@ -// -// IProjectBinding.cs -// -// Author: -// Lluis Sanchez Gual - -// -// Copyright (C) 2005 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.Collections; -using System.Xml; - -namespace MonoDevelop.Projects -{ - public interface IProjectBinding - { - /// <remarks> - /// Returns the project type name - /// </remarks> - string Name { get; } - - /// <remarks> - /// Creates a Project out of the given ProjetCreateInformation object. - /// Each project binding must provide a representation of the project - /// it 'controls'. - /// </remarks> - Project CreateProject (ProjectCreateInformation info, XmlElement projectOptions); - - /// <remarks> - /// Creates a Project for a single source file. If the file is not - /// valid for this project type, it must return null. - /// </remarks> - Project CreateSingleFileProject (string sourceFile); - - bool CanCreateSingleFileProject (string sourceFile); - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IPropertySet.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IPropertySet.cs new file mode 100644 index 0000000000..56ccd1f3c8 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/IPropertySet.cs @@ -0,0 +1,49 @@ +// +// IPropertySet.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Xml; +using MonoDevelop.Core; +using System.Xml.Linq; + +namespace MonoDevelop.Projects +{ + public interface IPropertySet + { + + void SetValue (string name, string value, string defaultValue = null, bool preserveExistingCase = false, bool mergeToMainGroup = false, string condition = null, bool persist = true); + void SetValue (string name, XmlElement elem, bool mergeToMainGroup = false, string condition = null, bool persist = true); + void SetValue (string name, XElement elem, bool mergeToMainGroup = false, string condition = null, bool persist = true); + void SetValue (string name, FilePath value, FilePath defaultValue = default(FilePath), bool relativeToProject = true, bool mergeToMainGroup = false, string condition = null, bool persist = true); + void SetValue (string name, bool value, bool? defaultValue = false, bool mergeToMainGroup = false, string condition = null, bool persist = true); + + string GetValue (string name, string defaultValue = null); + XElement GetXmlValue (string name); + FilePath GetPathValue (string name, FilePath defaultValue = default(FilePath), bool relativeToProject = true); + bool GetBoolValue (string name, bool defaultValue = false); + + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ItemConfiguration.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ItemConfiguration.cs index 32e1e1316e..8956f4feef 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ItemConfiguration.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ItemConfiguration.cs @@ -32,6 +32,7 @@ using System.Collections.ObjectModel; using MonoDevelop.Projects; using MonoDevelop.Core.Serialization; using MonoDevelop.Core; +using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects { @@ -107,7 +108,7 @@ namespace MonoDevelop.Projects public CustomCommandCollection CustomCommands { get { return customCommands; } } - + public object Clone() { ItemConfiguration conf = (ItemConfiguration) Activator.CreateInstance (GetType ()); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/LanguageBindingService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/LanguageBindingService.cs index f2823eeeb3..61b04d8b89 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/LanguageBindingService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/LanguageBindingService.cs @@ -48,9 +48,6 @@ namespace MonoDevelop.Projects object par = dotNetBinding.CreateCompilationParameters (null); if (par != null) Services.ProjectService.DataContext.IncludeType (par.GetType ()); - par = dotNetBinding.CreateProjectParameters (null); - if (par != null) - Services.ProjectService.DataContext.IncludeType (par.GetType ()); } break; case ExtensionChange.Remove: diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/PortableDotNetProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/PortableDotNetProjectFlavor.cs index 7903a6152a..4a30eee279 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/PortableDotNetProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/PortableDotNetProjectFlavor.cs @@ -1,20 +1,21 @@ -// -// PortableDotNetProject.cs -// -// Author: Jeffrey Stedfast <jeff@xamarin.com> -// -// Copyright (c) 2012 Xamarin Inc. -// +// +// PortableDotNetProjectFlavor.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 @@ -22,57 +23,39 @@ // 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.Xml; -using MonoDevelop.Core; +using System.Collections.Generic; using MonoDevelop.Core.Assemblies; using System.Linq; -using System.Collections.Generic; namespace MonoDevelop.Projects { - public class PortableDotNetProject : DotNetProject + public class PortableDotNetProjectFlavor: DotNetProjectExtension { - public PortableDotNetProject () - { - } - - public PortableDotNetProject (string languageName) : base (languageName) - { - } - - public PortableDotNetProject (string languageName, ProjectCreateInformation projectCreateInfo, XmlElement projectOptions) - : base (languageName, projectCreateInfo, projectOptions) + internal protected override void OnGetProjectTypes (HashSet<string> types) { + types.Add ("PortableDotNet"); } - public override IEnumerable<string> GetProjectTypes () - { - yield return "PortableDotNet"; - foreach (var t in base.GetProjectTypes ()) - yield return t; - } - - public override bool SupportsFormat (FileFormat format) + internal protected override bool OnGetSupportsFormat (FileFormat format) { int version; - + if (!format.Id.StartsWith ("MSBuild", StringComparison.Ordinal)) return false; - + if (!int.TryParse (format.Id.Substring ("MSBuild".Length), out version)) return false; - + return version >= 10; } - - public override bool SupportsFramework (TargetFramework framework) + + internal protected override bool OnGetSupportsFramework (TargetFramework framework) { return framework.Id.Identifier == TargetFrameworkMoniker.ID_PORTABLE; } - - public override TargetFrameworkMoniker GetDefaultTargetFrameworkForFormat (FileFormat format) + + internal protected override TargetFrameworkMoniker OnGetDefaultTargetFrameworkForFormat (FileFormat format) { // Note: This value is used only when serializing the TargetFramework to the .csproj file. // Any component of the TargetFramework that is different from this base TargetFramework @@ -82,19 +65,20 @@ namespace MonoDevelop.Projects // TargetFrameworkVersion and TargetFrameworkProfile values will be serialized. return new TargetFrameworkMoniker (".NETPortable", "1.0"); } - - public override TargetFrameworkMoniker GetDefaultTargetFrameworkId () + + internal protected override TargetFrameworkMoniker OnGetDefaultTargetFrameworkId () { // Profile78 includes .NET 4.5+, Windows Phone 8, and Xamarin.iOS/Android, so make that our default. // Note: see also: PortableLibrary.xpt.xml return new TargetFrameworkMoniker (".NETPortable", "4.5", "Profile78"); } - protected internal override IEnumerable<string> OnGetReferencedAssemblies (ConfigurationSelector configuration, bool includeProjectReferences) + internal protected override IEnumerable<string> OnGetReferencedAssemblies (ConfigurationSelector configuration, bool includeProjectReferences) { var res = base.OnGetReferencedAssemblies (configuration, includeProjectReferences); - var asms = TargetRuntime.AssemblyContext.GetAssemblies (TargetFramework).Where (a => a.Package.IsFrameworkPackage).Select (a => a.Location); + var asms = Project.TargetRuntime.AssemblyContext.GetAssemblies (Project.TargetFramework).Where (a => a.Package.IsFrameworkPackage).Select (a => a.Location); return res.Concat (asms).Distinct (); } } } + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs index b3bf2233e3..ce145e9c78 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs @@ -37,6 +37,14 @@ using MonoDevelop; using MonoDevelop.Core; using MonoDevelop.Core.Serialization; using MonoDevelop.Projects; +using System.Threading.Tasks; +using MonoDevelop.Projects.Formats.MSBuild; +using System.Xml; +using MonoDevelop.Core.Instrumentation; +using MonoDevelop.Core.Assemblies; +using MonoDevelop.Projects.Extensions; +using System.Threading; +using Mono.Addins; namespace MonoDevelop.Projects @@ -48,27 +56,200 @@ namespace MonoDevelop.Projects /// This is the base class for MonoDevelop projects. A project is a solution item which has a list of /// source code files and which can be built to generate an output. /// </remarks> - [DataInclude(typeof(ProjectFile))] - [ProjectModelDataItem(FallbackType = typeof(UnknownProject))] - public abstract class Project : SolutionEntityItem + public class Project : SolutionItem { + string[] flavorGuids = new string[0]; string[] buildActions; + MSBuildProject sourceProject; + + string productVersion; + string schemaVersion; + bool modifiedInMemory; + + List<string> defaultImports = new List<string> (); protected Project () { FileService.FileChanged += OnFileChanged; + Runtime.SystemAssemblyService.DefaultRuntimeChanged += OnDefaultRuntimeChanged; files = new ProjectFileCollection (); Items.Bind (files); DependencyResolutionEnabled = true; } - + + protected Project (params string[] flavorGuids): this() + { + this.flavorGuids = flavorGuids; + } + + protected Project (string[] flavorIds, MSBuildProject sourceProject): this(flavorIds) + { + } + + protected Project (ProjectCreateInformation projectCreateInfo, XmlElement projectOptions): this() + { + var ids = projectOptions != null ? projectOptions.GetAttribute ("flavorIds") : null; + if (!string.IsNullOrEmpty (ids)) { + this.flavorGuids = ids.Split (new [] {';'}, StringSplitOptions.RemoveEmptyEntries); + } + } + + internal class CreationContext + { + static object theLock = new object (); + public MSBuildProject Project { get; set; } + public string TypeGuid { get; set; } + + internal static void LockContext (MSBuildProject p, string typeGuid) + { + Monitor.Enter (theLock); + Current = new CreationContext (); + Current.Project = p; + Current.TypeGuid = typeGuid; + } + + internal static void UnlockContext () + { + Current = null; + Monitor.Exit (theLock); + } + + public static CreationContext Current { get; private set; } + } + + protected override void OnInitialize () + { + base.OnInitialize (); + + if (CreationContext.Current != null) { + + if (IsExtensionChainCreated) + throw new InvalidOperationException ("Extension chain already created for this object"); + + TypeGuid = CreationContext.Current.TypeGuid; + this.sourceProject = CreationContext.Current.Project; + + IMSBuildPropertySet globalGroup = sourceProject.GetGlobalPropertyGroup (); + string projectTypeGuids = globalGroup.GetValue ("ProjectTypeGuids"); + + if (projectTypeGuids != null) { + var subtypeGuids = new List<string> (); + foreach (string guid in projectTypeGuids.Split (';')) { + string sguid = guid.Trim (); + if (sguid.Length > 0 && string.Compare (sguid, CreationContext.Current.TypeGuid, StringComparison.OrdinalIgnoreCase) != 0) + subtypeGuids.Add (guid); + } + flavorGuids = subtypeGuids.ToArray (); + } + } + } + + protected override void OnExtensionChainInitialized () + { + base.OnExtensionChainInitialized (); + if (CreationContext.Current != null) + FileName = CreationContext.Current.Project.FileName; + } + + void OnDefaultRuntimeChanged (object o, EventArgs args) + { + // If the default runtime changes, the project builder for this project may change + // so it has to be created again. + CleanupProjectBuilder (); + } + + public IEnumerable<string> FlavorGuids { + get { return flavorGuids; } + } + + public List<string> DefaultImports { + get { return defaultImports; } + } + + public string ToolsVersion { get; private set; } + + internal bool CheckAllFlavorsSupported () + { + return FlavorGuids.All (g => ProjectExtension.SupportsFlavor (g)); + } + + ProjectExtension projectExtension; + ProjectExtension ProjectExtension { + get { + if (projectExtension == null) + projectExtension = ExtensionChain.GetExtension<ProjectExtension> (); + return projectExtension; + } + } + + /// <summary>Whether to use the MSBuild engine by default.</summary> + internal bool UseMSBuildEngineByDefault { get; set; } + + /// <summary>Forces the MSBuild engine to be used.</summary> + internal bool RequireMSBuildEngine { get; set; } + + protected override void OnModified (SolutionItemModifiedEventArgs args) + { + if (!Loading) + modifiedInMemory = true; + base.OnModified (args); + } + + protected override Task OnLoad (ProgressMonitor monitor) + { + MSBuildProject p = sourceProject; + sourceProject = null; + + return Task.Factory.StartNew (delegate { + if (p == null) + p = MSBuildProject.LoadAsync (FileName).Result; + IMSBuildPropertySet globalGroup = p.GetGlobalPropertyGroup (); + // Avoid crash if there is not global group + if (globalGroup == null) + p.AddNewPropertyGroup (false); + + try { + ProjectExtensionUtil.BeginLoadOperation (); + ReadProject (monitor, p); + } finally { + ProjectExtensionUtil.EndLoadOperation (); + } + }); + } + + internal protected override Task OnSave (ProgressMonitor monitor) + { + modifiedInMemory = false; + + return Task.Factory.StartNew (delegate { + var msproject = WriteProject (monitor); + if (msproject == null) + return; + + // Don't save the file to disk if the content did not change + msproject.Save (FileName); + + if (projectBuilder != null) + projectBuilder.Refresh (); + }); + } + + protected override IEnumerable<WorkspaceObjectExtension> CreateDefaultExtensions () + { + return base.CreateDefaultExtensions ().Concat (Enumerable.Repeat (new DefaultMSBuildProjectExtension (), 1)); + } + + internal protected override IEnumerable<string> GetItemTypeGuids () + { + return base.GetItemTypeGuids ().Concat (flavorGuids); + } + /// <summary> /// Description of the project. /// </summary> - [ItemProperty("Description", DefaultValue = "")] private string description = ""; public string Description { - get { return description; } + get { return description ?? ""; } set { description = value; NotifyModified ("Description"); @@ -86,7 +267,7 @@ namespace MonoDevelop.Projects /// </param> public virtual bool IsCompileable (string fileName) { - return false; + return ProjectExtension.OnGetIsCompileable (fileName); } /// <summary> @@ -97,7 +278,6 @@ namespace MonoDevelop.Projects } private ProjectFileCollection files; - [ProjectPathItemProperty ("BaseIntermediateOutputPath")] FilePath baseIntermediateOutputPath; public virtual FilePath BaseIntermediateOutputPath { @@ -129,7 +309,31 @@ namespace MonoDevelop.Projects /// <summary> /// Gets the project type and its base types. /// </summary> - public abstract IEnumerable<string> GetProjectTypes (); + public IEnumerable<string> GetProjectTypes () + { + var types = new HashSet<string> (); + ProjectExtension.OnGetProjectTypes (types); + return types; + } + + protected virtual void OnGetProjectTypes (HashSet<string> types) + { + } + + public bool HasFlavor<T> () + { + return GetService (typeof(T)) != null; + } + + public T GetFlavor<T> () where T:ProjectExtension + { + return (T) GetService (typeof(T)); + } + + internal IEnumerable<ProjectExtension> GetFlavors () + { + return ExtensionChain.GetAllExtensions ().OfType<ProjectExtension> (); + } /// <summary> /// Gets or sets the icon of the project. @@ -138,10 +342,15 @@ namespace MonoDevelop.Projects /// The stock icon. /// </value> public virtual IconId StockIcon { - get { return stockIcon; } + get { + if (stockIcon != null) + return stockIcon.Value; + else + return ProjectExtension.StockIcon; + } set { this.stockIcon = value; NotifyModified ("StockIcon"); } } - IconId stockIcon = "md-project"; + IconId? stockIcon; /// <summary> /// List of languages that this project supports @@ -150,7 +359,7 @@ namespace MonoDevelop.Projects /// The identifiers of the supported languages. /// </value> public virtual string[] SupportedLanguages { - get { return new String[] { "" }; } + get { return ProjectExtension.SupportedLanguages; } } /// <summary> @@ -164,9 +373,60 @@ namespace MonoDevelop.Projects /// </param> public virtual string GetDefaultBuildAction (string fileName) { - return IsCompileable (fileName) ? BuildAction.Compile : BuildAction.None; + return ProjectExtension.OnGetDefaultBuildAction (fileName); } - + + public string GetDefaultResourceId (ProjectFile projectFile) + { + return ProjectExtension.OnGetDefaultResourceId (projectFile); + } + + protected virtual string OnGetDefaultResourceId (ProjectFile projectFile) + { + return MSBuildResourceHandler.Instance.GetDefaultResourceId (projectFile); + } + + internal ProjectItem CreateProjectItem (IMSBuildItemEvaluated item) + { + return ProjectExtension.OnCreateProjectItem (item); + } + + protected virtual ProjectItem OnCreateProjectItem (IMSBuildItemEvaluated item) + { + if (item.Name == "Folder") + return new ProjectFile (); + + // Unknown item. Must be a file. + if (!string.IsNullOrEmpty (item.Include) && !UnsupportedItems.Contains (item.Name) && IsValidFile (item.Include)) + return new ProjectFile (); + + return new UnknownProjectItem (item.Name, item.Include); + } + + bool IsValidFile (string path) + { + // If it is an absolute uri, it's not a valid file + try { + if (Uri.IsWellFormedUriString (path, UriKind.Absolute)) { + var f = new Uri (path); + return f.Scheme == "file"; + } + } catch { + // Old mono versions may crash in IsWellFormedUriString if the path + // is not an uri. + } + return true; + } + + // Items generated by VS but which MD is not using and should be ignored + + internal static readonly IList<string> UnsupportedItems = new string[] { + "BootstrapperFile", "AppDesigner", "WebReferences", "WebReferenceUrl", "Service", + "ProjectReference", "Reference", // Reference elements are included here because they are special-cased for DotNetProject, and they are unsupported in other types of projects + "InternalsVisibleTo", + "InternalsVisibleToTest" + }; + /// <summary> /// Gets a project file. /// </summary> @@ -255,7 +515,7 @@ namespace MonoDevelop.Projects /// </summary> protected virtual IEnumerable<string> GetStandardBuildActions () { - return BuildAction.StandardActions; + return ProjectExtension.OnGetStandardBuildActions (); } /// <summary> @@ -263,25 +523,244 @@ namespace MonoDevelop.Projects /// </summary> protected virtual IList<string> GetCommonBuildActions () { - return BuildAction.StandardActions; + return ProjectExtension.OnGetCommonBuildActions (); } - public static Project LoadProject (string filename, IProgressMonitor monitor) + public override void Dispose () { - Project prj = Services.ProjectService.ReadSolutionItem (monitor, filename) as Project; - if (prj == null) - throw new InvalidOperationException ("Invalid project file: " + filename); + FileService.FileChanged -= OnFileChanged; + Runtime.SystemAssemblyService.DefaultRuntimeChanged -= OnDefaultRuntimeChanged; + CleanupProjectBuilder (); + base.Dispose (); + } - return prj; + /// <summary> + /// Runs a build or execution target. + /// </summary> + /// <returns> + /// The result of the operation + /// </returns> + /// <param name='monitor'> + /// A progress monitor + /// </param> + /// <param name='target'> + /// Name of the target + /// </param> + /// <param name='configuration'> + /// Configuration to use to run the target + /// </param> + public Task<BuildResult> RunTarget (ProgressMonitor monitor, string target, ConfigurationSelector configuration) + { + return ProjectExtension.OnRunTarget (monitor, target, configuration); } + public bool SupportsTarget (string target) + { + return !IsUnsupportedProject && ProjectExtension.OnGetSupportsTarget (target); + } - public override void Dispose () + protected virtual bool OnGetSupportsTarget (string target) { - FileService.FileChanged -= OnFileChanged; - base.Dispose (); + return target == "Build" || target == "Clean"; } - + + /// <summary> + /// Runs a build or execution target. + /// </summary> + /// <returns> + /// The result of the operation + /// </returns> + /// <param name='monitor'> + /// A progress monitor + /// </param> + /// <param name='target'> + /// Name of the target + /// </param> + /// <param name='configuration'> + /// Configuration to use to run the target + /// </param> + /// <remarks> + /// Subclasses can override this method to provide a custom implementation of project operations such as + /// build or clean. The default implementation delegates the execution to the more specific OnBuild + /// and OnClean methods, or to the item handler for other targets. + /// </remarks> + internal async protected virtual Task<BuildResult> OnRunTarget (ProgressMonitor monitor, string target, ConfigurationSelector configuration) + { + if (target == ProjectService.BuildTarget) + return await RunBuildTarget (monitor, configuration); + else if (target == ProjectService.CleanTarget) + return await RunCleanTarget (monitor, configuration); + return await RunMSBuildTarget (monitor, target, configuration) ?? new BuildResult (); + } + + + async Task<BuildResult> DoRunTarget (ProgressMonitor monitor, string target, ConfigurationSelector configuration) + { + if (target == ProjectService.BuildTarget) { + SolutionItemConfiguration conf = GetConfiguration (configuration); + if (conf != null && conf.CustomCommands.HasCommands (CustomCommandType.Build)) { + if (!await conf.CustomCommands.ExecuteCommand (monitor, this, CustomCommandType.Build, configuration)) { + var r = new BuildResult (); + r.AddError (GettextCatalog.GetString ("Custom command execution failed")); + return r; + } + return BuildResult.Success; + } + } else if (target == ProjectService.CleanTarget) { + SolutionItemConfiguration config = GetConfiguration (configuration); + if (config != null && config.CustomCommands.HasCommands (CustomCommandType.Clean)) { + if (!await config.CustomCommands.ExecuteCommand (monitor, this, CustomCommandType.Clean, configuration)) { + var r = new BuildResult (); + r.AddError (GettextCatalog.GetString ("Custom command execution failed")); + return r; + } + return BuildResult.Success; + } + } + return await OnRunTarget (monitor, target, configuration); + } + + async Task<BuildResult> RunMSBuildTarget (ProgressMonitor monitor, string target, ConfigurationSelector configuration) + { + if (CheckUseMSBuildEngine (configuration)) { + LogWriter logWriter = new LogWriter (monitor.Log); + RemoteProjectBuilder builder = GetProjectBuilder (); + var configs = GetConfigurations (configuration); + + MSBuildResult result = null; + await Task.Factory.StartNew (delegate { + result = builder.Run (configs, logWriter, MSBuildProjectService.DefaultMSBuildVerbosity, new[] { target }, null, null); + System.Runtime.Remoting.RemotingServices.Disconnect (logWriter); + }); + + var br = new BuildResult (); + foreach (var err in result.Errors) { + FilePath file = null; + if (err.File != null) + file = Path.Combine (Path.GetDirectoryName (err.ProjectFile), err.File); + + if (err.IsWarning) + br.AddWarning (file, err.LineNumber, err.ColumnNumber, err.Code, err.Message); + else + br.AddError (file, err.LineNumber, err.ColumnNumber, err.Code, err.Message); + } + return br; + } + else { + CleanupProjectBuilder (); + if (this is DotNetProject) { + var handler = new MonoDevelop.Projects.Formats.MD1.MD1DotNetProjectHandler ((DotNetProject)this); + return await handler.RunTarget (monitor, target, configuration); + } + } + return null; + } + + internal ProjectConfigurationInfo[] GetConfigurations (ConfigurationSelector configuration) + { + // Returns a list of project/configuration information for the provided item and all its references + List<ProjectConfigurationInfo> configs = new List<ProjectConfigurationInfo> (); + var c = GetConfiguration (configuration); + configs.Add (new ProjectConfigurationInfo () { + ProjectFile = FileName, + Configuration = c.Name, + Platform = GetExplicitPlatform (c) + }); + foreach (var refProject in GetReferencedItems (configuration).OfType<Project> ()) { + var refConfig = refProject.GetConfiguration (configuration); + if (refConfig != null) { + configs.Add (new ProjectConfigurationInfo () { + ProjectFile = refProject.FileName, + Configuration = refConfig.Name, + Platform = GetExplicitPlatform (refConfig) + }); + } + } + return configs.ToArray (); + } + + //for some reason, MD internally handles "AnyCPU" as "", but we need to be explicit when + //passing it to the build engine + static string GetExplicitPlatform (SolutionItemConfiguration configObject) + { + if (string.IsNullOrEmpty (configObject.Platform)) { + return "AnyCPU"; + } + return configObject.Platform; + } + + #region Project builder management + + RemoteProjectBuilder projectBuilder; + string lastBuildToolsVersion; + string lastBuildRuntime; + string lastFileName; + string lastSlnFileName; + object builderLock = new object (); + + internal RemoteProjectBuilder GetProjectBuilder () + { + //FIXME: we can't really have per-project runtimes, has to be per-solution + TargetRuntime runtime = null; + var ap = this as IAssemblyProject; + runtime = ap != null ? ap.TargetRuntime : Runtime.SystemAssemblyService.CurrentRuntime; + + var sln = ParentSolution; + var slnFile = sln != null ? sln.FileName : null; + + lock (builderLock) { + if (projectBuilder == null || lastBuildToolsVersion != ToolsVersion || lastBuildRuntime != runtime.Id || lastFileName != FileName || lastSlnFileName != slnFile) { + if (projectBuilder != null) { + projectBuilder.Dispose (); + projectBuilder = null; + } + projectBuilder = MSBuildProjectService.GetProjectBuilder (runtime, ToolsVersion, FileName, slnFile); + projectBuilder.Disconnected += delegate { + CleanupProjectBuilder (); + }; + lastBuildToolsVersion = ToolsVersion; + lastBuildRuntime = runtime.Id; + lastFileName = FileName; + lastSlnFileName = slnFile; + } else if (modifiedInMemory) { + modifiedInMemory = false; + // TODO NPM +// var p = SaveProject (new NullProgressMonitor ()); +// projectBuilder.RefreshWithContent (p.SaveToString ()); + } + } + return projectBuilder; + } + + void CleanupProjectBuilder () + { + if (projectBuilder != null) { + projectBuilder.Dispose (); + projectBuilder = null; + } + } + + #endregion + + /// <summary>Whether to use the MSBuild engine for the specified item.</summary> + internal bool CheckUseMSBuildEngine (ConfigurationSelector sel, bool checkReferences = true) + { + // if the item mandates MSBuild, always use it + if (RequireMSBuildEngine) + return true; + // if the user has set the option, use the setting + if (UseMSBuildEngine.HasValue) + return UseMSBuildEngine.Value; + + // If the item type defaults to using MSBuild, only use MSBuild if its direct references also use MSBuild. + // This prevents a not-uncommon common error referencing non-MSBuild projects from MSBuild projects + // NOTE: This adds about 11ms to the load/build/etc times of the MonoDevelop solution. Doing it recursively + // adds well over a second. + return UseMSBuildEngineByDefault && ( + !checkReferences || GetReferencedItems (sel).OfType<Project>().All (i => i.CheckUseMSBuildEngine (sel, false)) + ); + } + /// <summary> /// Adds a file to the project /// </summary> @@ -394,11 +873,15 @@ namespace MonoDevelop.Projects //so in order to avoid doing them twice when using the msbuild engine, we special-case them bool UsingMSBuildEngine (ConfigurationSelector sel) { - var msbuildHandler = ItemHandler as MonoDevelop.Projects.Formats.MSBuild.MSBuildProjectHandler; - return msbuildHandler != null && msbuildHandler.UseMSBuildEngineForItem (this, sel); + return CheckUseMSBuildEngine (sel); } - protected override BuildResult OnBuild (IProgressMonitor monitor, ConfigurationSelector configuration) + protected override Task<BuildResult> OnBuild (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return RunTarget (monitor, "Build", configuration); + } + + async Task<BuildResult> RunBuildTarget (ProgressMonitor monitor, ConfigurationSelector configuration) { // create output directory, if not exists ProjectConfiguration conf = GetConfiguration (configuration) as ProjectConfiguration; @@ -411,7 +894,7 @@ namespace MonoDevelop.Projects StringParserService.Properties["Project"] = Name; if (UsingMSBuildEngine (configuration)) { - return DoBuild (monitor, configuration); + return await DoBuild (monitor, configuration); } string outputDir = conf.OutputDirectory; @@ -429,7 +912,7 @@ namespace MonoDevelop.Projects monitor.Log.WriteLine ("Performing main compilation..."); - BuildResult res = DoBuild (monitor, configuration); + BuildResult res = await DoBuild (monitor, configuration); if (res != null) { string errorString = GettextCatalog.GetPluralString ("{0} error", "{0} errors", res.ErrorCount, res.ErrorCount); @@ -454,7 +937,7 @@ namespace MonoDevelop.Projects /// Copies all support files to the output directory of the given configuration. Support files /// include: assembly references with the Local Copy flag, data files with the Copy to Output option, etc. /// </remarks> - public void CopySupportFiles (IProgressMonitor monitor, ConfigurationSelector configuration) + public void CopySupportFiles (ProgressMonitor monitor, ConfigurationSelector configuration) { ProjectConfiguration config = (ProjectConfiguration) GetConfiguration (configuration); @@ -505,7 +988,7 @@ namespace MonoDevelop.Projects /// Deletes all support files from the output directory of the given configuration. Support files /// include: assembly references with the Local Copy flag, data files with the Copy to Output option, etc. /// </remarks> - public void DeleteSupportFiles (IProgressMonitor monitor, ConfigurationSelector configuration) + public async Task DeleteSupportFiles (ProgressMonitor monitor, ConfigurationSelector configuration) { ProjectConfiguration config = (ProjectConfiguration) GetConfiguration (configuration); @@ -517,7 +1000,7 @@ namespace MonoDevelop.Projects continue; try { - dest.Delete (); + await dest.DeleteAsync (); } catch (IOException ex) { monitor.ReportError (GettextCatalog.GetString ("Error deleting support file '{0}'.", dest), ex); } @@ -559,13 +1042,17 @@ namespace MonoDevelop.Projects /// </remarks> internal protected virtual void PopulateSupportFileList (FileCopySet list, ConfigurationSelector configuration) { + ProjectExtension.OnPopulateSupportFileList (list, configuration); + } + void DoPopulateSupportFileList (FileCopySet list, ConfigurationSelector configuration) + { foreach (ProjectFile pf in Files) { if (pf.CopyToOutputDirectory == FileCopyMode.None) continue; list.Add (pf.FilePath, pf.CopyToOutputDirectory == FileCopyMode.PreserveNewest, pf.ProjectVirtualPath); } } - + /// <summary> /// Gets a list of files generated when building this project /// </summary> @@ -601,6 +1088,10 @@ namespace MonoDevelop.Projects /// </remarks> internal protected virtual void PopulateOutputFileList (List<FilePath> list, ConfigurationSelector configuration) { + ProjectExtension.OnPopulateOutputFileList (list, configuration); + } + void DoPopulateOutputFileList (List<FilePath> list, ConfigurationSelector configuration) + { string file = GetOutputFileName (configuration); if (file != null) list.Add (file); @@ -622,55 +1113,64 @@ namespace MonoDevelop.Projects /// This method is invoked to build the project. Support files such as files with the Copy to Output flag will /// be copied before calling this method. /// </remarks> - protected virtual BuildResult DoBuild (IProgressMonitor monitor, ConfigurationSelector configuration) + protected async virtual Task<BuildResult> DoBuild (ProgressMonitor monitor, ConfigurationSelector configuration) { - BuildResult res = ItemHandler.RunTarget (monitor, "Build", configuration); + BuildResult res = await RunMSBuildTarget (monitor, "Build", configuration); return res ?? new BuildResult (); } - protected override void OnClean (IProgressMonitor monitor, ConfigurationSelector configuration) + protected override Task<BuildResult> OnClean (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return RunTarget (monitor, "Clean", configuration); + } + + async Task<BuildResult> RunCleanTarget (ProgressMonitor monitor, ConfigurationSelector configuration) { ProjectConfiguration config = GetConfiguration (configuration) as ProjectConfiguration; if (config == null) { monitor.ReportError (GettextCatalog.GetString ("Configuration '{0}' not found in project '{1}'", configuration, Name), null); - return; + return BuildResult.Success; } if (UsingMSBuildEngine (configuration)) { - DoClean (monitor, config.Selector); - return; + return await DoClean (monitor, config.Selector); } monitor.Log.WriteLine ("Removing output files..."); - - // Delete generated files - foreach (FilePath file in GetOutputFiles (configuration)) { - if (File.Exists (file)) { - file.Delete (); - if (file.ParentDirectory.CanonicalPath != config.OutputDirectory.CanonicalPath && Directory.GetFiles (file.ParentDirectory).Length == 0) - file.ParentDirectory.Delete (); + + var filesToDelete = GetOutputFiles (configuration).ToArray (); + + await Task.Factory.StartNew (delegate { + // Delete generated files + foreach (FilePath file in filesToDelete) { + if (File.Exists (file)) { + file.Delete (); + if (file.ParentDirectory.CanonicalPath != config.OutputDirectory.CanonicalPath && Directory.GetFiles (file.ParentDirectory).Length == 0) + file.ParentDirectory.Delete (); + } } - } + }); - DeleteSupportFiles (monitor, configuration); + await DeleteSupportFiles (monitor, configuration); - DoClean (monitor, config.Selector); + var res = await DoClean (monitor, config.Selector); monitor.Log.WriteLine (GettextCatalog.GetString ("Clean complete")); + return res; } - protected virtual void DoClean (IProgressMonitor monitor, ConfigurationSelector configuration) + protected virtual Task<BuildResult> DoClean (ProgressMonitor monitor, ConfigurationSelector configuration) { - ItemHandler.RunTarget (monitor, "Clean", configuration); + return RunMSBuildTarget (monitor, "Clean", configuration); } - protected internal override void OnExecute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + protected async override Task OnExecute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) { ProjectConfiguration config = GetConfiguration (configuration) as ProjectConfiguration; if (config == null) { monitor.ReportError (GettextCatalog.GetString ("Configuration '{0}' not found in project '{1}'", configuration, Name), null); return; } - DoExecute (monitor, context, configuration); + await DoExecute (monitor, context, configuration); } /// <summary> @@ -685,8 +1185,9 @@ namespace MonoDevelop.Projects /// <param name='configuration'> /// Configuration to execute. /// </param> - protected virtual void DoExecute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + protected virtual Task DoExecute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) { + return new Task (delegate {}); } /// <summary> @@ -700,14 +1201,21 @@ namespace MonoDevelop.Projects /// </param> public virtual FilePath GetOutputFileName (ConfigurationSelector configuration) { - return FilePath.Null; + return ProjectExtension.OnGetOutputFileName (configuration); } - protected internal override bool OnGetNeedsBuilding (ConfigurationSelector configuration) + internal protected override bool OnGetNeedsBuilding (ConfigurationSelector configuration) { return CheckNeedsBuild (configuration); } + protected override void OnSetNeedsBuilding (ConfigurationSelector configuration) + { + var of = GetOutputFileName (configuration); + if (File.Exists (of)) + File.Delete (of); + } + /// <summary> /// Checks if the project needs to be built /// </summary> @@ -734,7 +1242,7 @@ namespace MonoDevelop.Projects } } - foreach (SolutionItem pref in GetReferencedItems (configuration)) { + foreach (SolutionFolderItem pref in GetReferencedItems (configuration)) { if (pref.GetLastBuildTime (configuration) > tim) return true; } @@ -778,16 +1286,19 @@ namespace MonoDevelop.Projects } } - protected internal override List<FilePath> OnGetItemFiles (bool includeReferencedFiles) + protected override IEnumerable<FilePath> OnGetItemFiles (bool includeReferencedFiles) { - List<FilePath> col = base.OnGetItemFiles (includeReferencedFiles); + var baseFiles = base.OnGetItemFiles (includeReferencedFiles); + if (includeReferencedFiles) { + List<FilePath> col = new List<FilePath> (); foreach (ProjectFile pf in Files) { if (pf.Subtype != Subtype.Directory) col.Add (pf.FilePath); } + baseFiles = baseFiles.Concat (col); } - return col; + return baseFiles; } protected internal override void OnItemsAdded (IEnumerable<ProjectItem> objs) @@ -908,6 +1419,775 @@ namespace MonoDevelop.Projects } } + internal void ReadProject (ProgressMonitor monitor, MSBuildProject msproject) + { + OnReadProjectHeader (monitor, msproject); + ProjectExtension.OnReadProject (monitor, msproject); + NeedsReload = false; + } + + bool newProject; + + internal MSBuildProject WriteProject (ProgressMonitor monitor) + { + MSBuildProject msproject = new MSBuildProject (); + newProject = FileName == null || !File.Exists (FileName); + if (newProject) { + if (SupportsBuild ()) + msproject.DefaultTargets = "Build"; + msproject.FileName = FileName; + } else { + msproject.Load (FileName); + } + + OnWriteProjectHeader (monitor, msproject); + ProjectExtension.OnWriteProject (monitor, msproject); + + return msproject; + } + + class ConfigData + { + public ConfigData (string conf, string plt, IMSBuildPropertySet grp) + { + Config = conf; + Platform = plt; + Group = grp; + } + + public bool FullySpecified { + get { return Config != Unspecified && Platform != Unspecified; } + } + + public string Config; + public string Platform; + public IMSBuildPropertySet Group; + public bool Exists; + public bool IsNew; // The group did not exist in the original file + } + + const string Unspecified = null; + ITimeTracker timer; + + protected virtual void OnReadProjectHeader (ProgressMonitor monitor, MSBuildProject msproject) + { + timer = Counters.ReadMSBuildProject.BeginTiming (); + + ToolsVersion = msproject.ToolsVersion; + if (string.IsNullOrEmpty (ToolsVersion)) + ToolsVersion = "2.0"; + + IMSBuildPropertySet globalGroup = msproject.GetGlobalPropertyGroup (); + // Avoid crash if there is not global group + if (globalGroup == null) + globalGroup = msproject.AddNewPropertyGroup (false); + + productVersion = globalGroup.GetValue ("ProductVersion"); + schemaVersion = globalGroup.GetValue ("SchemaVersion"); + + // Get the project ID + + string itemGuid = globalGroup.GetValue ("ProjectGuid"); + if (itemGuid == null) + throw new UserException ("Project file doesn't have a valid ProjectGuid"); + + // Workaround for a VS issue. VS doesn't include the curly braces in the ProjectGuid + // of shared projects. + if (!itemGuid.StartsWith ("{", StringComparison.Ordinal)) + itemGuid = "{" + itemGuid + "}"; + + ItemId = itemGuid.ToUpper (); + + // Get the project GUIDs + + string projectTypeGuids = globalGroup.GetValue ("ProjectTypeGuids"); + + var subtypeGuids = new List<string> (); + if (projectTypeGuids != null) { + foreach (string guid in projectTypeGuids.Split (';')) { + string sguid = guid.Trim (); + if (sguid.Length > 0 && string.Compare (sguid, TypeGuid, StringComparison.OrdinalIgnoreCase) != 0) + subtypeGuids.Add (guid); + } + } + flavorGuids = subtypeGuids.ToArray (); + + if (!CheckAllFlavorsSupported ()) { + var guids = new [] { TypeGuid }; + var projectInfo = MSBuildProjectService.GetUnknownProjectTypeInfo (guids.Concat (flavorGuids).ToArray (), FileName); + IsUnsupportedProject = true; + if (projectInfo != null) + UnsupportedProjectMessage = projectInfo.GetInstructions (); + } + + // Common properties + + Description = msproject.EvaluatedProperties.GetValue ("Description", ""); + baseIntermediateOutputPath = msproject.EvaluatedProperties.GetPathValue ("BaseIntermediateOutputPath", defaultValue:BaseDirectory.Combine ("obj"), relativeToProject:true); + + RemoveDuplicateItems (msproject); + } + + protected virtual void OnReadProject (ProgressMonitor monitor, MSBuildProject msproject) + { + timer.Trace ("Read project items"); + LoadProjectItems (msproject, ProjectItemFlags.None); + + timer.Trace ("Read configurations"); + + List<ConfigData> configData = GetConfigData (msproject, false); + List<ConfigData> partialConfigurations = new List<ConfigData> (); + HashSet<string> handledConfigurations = new HashSet<string> (); + var configurations = new HashSet<string> (); + var platforms = new HashSet<string> (); + + IMSBuildPropertySet globalGroup = msproject.GetGlobalPropertyGroup (); + configData.Insert (0, new ConfigData (Unspecified, Unspecified, globalGroup)); + + // Load configurations, skipping the dummy config at index 0. + for (int i = 1; i < configData.Count; i++) { + ConfigData cgrp = configData[i]; + string platform = cgrp.Platform; + string conf = cgrp.Config; + + if (platform != Unspecified) + platforms.Add (platform); + + if (conf != Unspecified) + configurations.Add (conf); + + if (conf == Unspecified || platform == Unspecified) { + // skip partial configurations for now... + partialConfigurations.Add (cgrp); + continue; + } + + string key = conf + "|" + platform; + if (handledConfigurations.Contains (key)) + continue; + + LoadConfiguration (monitor, configData, conf, platform); + + handledConfigurations.Add (key); + } + + // Now we can load any partial configurations by combining them with known configs or platforms. + if (partialConfigurations.Count > 0) { + if (platforms.Count == 0) + platforms.Add (string.Empty); // AnyCpu + + foreach (ConfigData cgrp in partialConfigurations) { + if (cgrp.Config != Unspecified && cgrp.Platform == Unspecified) { + string conf = cgrp.Config; + + foreach (var platform in platforms) { + string key = conf + "|" + platform; + + if (handledConfigurations.Contains (key)) + continue; + + LoadConfiguration (monitor, configData, conf, platform); + + handledConfigurations.Add (key); + } + } else if (cgrp.Config == Unspecified && cgrp.Platform != Unspecified) { + string platform = cgrp.Platform; + + foreach (var conf in configurations) { + string key = conf + "|" + platform; + + if (handledConfigurations.Contains (key)) + continue; + + LoadConfiguration (monitor, configData, conf, platform); + + handledConfigurations.Add (key); + } + } + } + } + + // Read extended properties + + timer.Trace ("Read extended properties"); + } + + List<ConfigData> GetConfigData (MSBuildProject msproject, bool includeGlobalGroups) + { + List<ConfigData> configData = new List<ConfigData> (); + foreach (MSBuildPropertyGroup cgrp in msproject.PropertyGroups) { + string conf, platform; + if (ParseConfigCondition (cgrp.Condition, out conf, out platform) || includeGlobalGroups) + configData.Add (new ConfigData (conf, platform, cgrp)); + } + return configData; + } + + bool ParseConfigCondition (string cond, out string config, out string platform) + { + config = platform = Unspecified; + int i = cond.IndexOf ("==", StringComparison.Ordinal); + if (i == -1) + return false; + if (cond.Substring (0, i).Trim () == "'$(Configuration)|$(Platform)'") { + if (!ExtractConfigName (cond.Substring (i + 2), out cond)) + return false; + i = cond.IndexOf ('|'); + if (i != -1) { + config = cond.Substring (0, i); + platform = cond.Substring (i+1); + } else { + // Invalid configuration + return false; + } + if (platform == "AnyCPU") + platform = string.Empty; + return true; + } + else if (cond.Substring (0, i).Trim () == "'$(Configuration)'") { + if (!ExtractConfigName (cond.Substring (i + 2), out config)) + return false; + platform = Unspecified; + return true; + } + else if (cond.Substring (0, i).Trim () == "'$(Platform)'") { + config = Unspecified; + if (!ExtractConfigName (cond.Substring (i + 2), out platform)) + return false; + if (platform == "AnyCPU") + platform = string.Empty; + return true; + } + return false; + } + + bool ExtractConfigName (string name, out string config) + { + config = name.Trim (' '); + if (config.Length <= 2) + return false; + if (config [0] != '\'' || config [config.Length - 1] != '\'') + return false; + config = config.Substring (1, config.Length - 2); + return config.IndexOf ('\'') == -1; + } + + void LoadConfiguration (ProgressMonitor monitor, List<ConfigData> configData, string conf, string platform) + { + IMSBuildPropertySet grp = GetMergedConfiguration (configData, conf, platform, null); + ProjectConfiguration config = (ProjectConfiguration) CreateConfiguration (conf); + + config.Platform = platform; + projectExtension.OnReadConfiguration (monitor, config, grp); + Configurations.Add (config); + } + + protected virtual void OnReadConfiguration (ProgressMonitor monitor, ProjectConfiguration config, IMSBuildPropertySet grp) + { + config.Read (grp, GetToolsFormat ()); + } + + void RemoveDuplicateItems (MSBuildProject msproject) + { + timer.Trace ("Checking for duplicate items"); + + var uniqueIncludes = new Dictionary<string,object> (); + var toRemove = new List<MSBuildItem> (); + foreach (MSBuildItem bi in msproject.GetAllItems ()) { + object existing; + string key = bi.Name + "<" + bi.Include; + if (!uniqueIncludes.TryGetValue (key, out existing)) { + uniqueIncludes[key] = bi; + continue; + } + var exBi = existing as MSBuildItem; + if (exBi != null) { + if (exBi.Condition != bi.Condition || exBi.Element.InnerXml != bi.Element.InnerXml) { + uniqueIncludes[key] = new List<MSBuildItem> { exBi, bi }; + } else { + toRemove.Add (bi); + } + continue; + } + + var exList = (List<MSBuildItem>)existing; + bool found = false; + foreach (var m in (exList)) { + if (m.Condition == bi.Condition && m.Element.InnerXml == bi.Element.InnerXml) { + found = true; + break; + } + } + if (!found) { + exList.Add (bi); + } else { + toRemove.Add (bi); + } + } + if (toRemove.Count == 0) + return; + + timer.Trace ("Removing duplicate items"); + + foreach (var t in toRemove) + msproject.RemoveItem (t); + } + + IMSBuildPropertySet GetMergedConfiguration (List<ConfigData> configData, string conf, string platform, IMSBuildPropertySet propGroupLimit) + { + IMSBuildPropertySet merged = null; + + foreach (ConfigData grp in configData) { + if (grp.Group == propGroupLimit) + break; + if ((grp.Config == conf || grp.Config == Unspecified || conf == Unspecified) && (grp.Platform == platform || grp.Platform == Unspecified || platform == Unspecified)) { + if (merged == null) + merged = grp.Group; + else if (merged is MSBuildPropertyGroupMerged) + ((MSBuildPropertyGroupMerged)merged).Add (grp.Group); + else { + MSBuildPropertyGroupMerged m = new MSBuildPropertyGroupMerged (merged.Project, GetToolsFormat()); + m.Add (merged); + m.Add (grp.Group); + merged = m; + } + } + } + return merged; + } + + //HACK: the solution's format is irrelevant to MSBuild projects, what matters is the ToolsVersion + // but other parts of the MD API expect a FileFormat + internal MSBuildFileFormat GetToolsFormat () + { + switch (ToolsVersion) { + case "2.0": + return new MSBuildFileFormatVS05 (); + case "3.5": + return new MSBuildFileFormatVS08 (); + case "4.0": + if (SolutionFormat != null && SolutionFormat.Id == "MSBuild10") + return SolutionFormat; + return new MSBuildFileFormatVS12 (); + case "12.0": + return new MSBuildFileFormatVS12 (); + default: + throw new Exception ("Unknown ToolsVersion '" + ToolsVersion + "'"); + } + } + + internal void LoadProjectItems (MSBuildProject msproject, ProjectItemFlags flags) + { + foreach (var buildItem in msproject.GetAllItems ()) { + ProjectItem it = ReadItem (buildItem); + if (it == null) + continue; + it.Flags = flags; + if (it is ProjectFile) { + var file = (ProjectFile)it; + if (file.Name.IndexOf ('*') > -1) { + // Thanks to IsOriginatedFromWildcard, these expanded items will not be saved back to disk. + foreach (var expandedItem in ResolveWildcardItems (file)) + Items.Add (expandedItem); + // Add to wildcard items (so it can be re-saved) instead of Items (where tools will + // try to compile and display these nonstandard items + WildcardItems.Add (it); + continue; + } + } + Items.Add (it); + } + } + + internal override void SetSolutionFormat (MSBuildFileFormat format, bool converting) + { + base.SetSolutionFormat (format, converting); + + // when converting formats, set ToolsVersion, ProductVersion, SchemaVersion to default values written by VS + // this happens on creation too + // else we leave them alone and just roundtrip them + if (converting) { + ToolsVersion = format.DefaultToolsVersion; + productVersion = format.DefaultProductVersion; + schemaVersion = format.DefaultSchemaVersion; + } + } + + internal ProjectItem ReadItem (IMSBuildItemEvaluated buildItem) + { + var item = CreateProjectItem (buildItem); + item.Read (this, buildItem); + return item; + } + + const string RecursiveDirectoryWildcard = "**"; + static readonly char[] directorySeparators = new [] { + Path.DirectorySeparatorChar, + Path.AltDirectorySeparatorChar + }; + + static string GetWildcardDirectoryName (string path) + { + int indexOfLast = path.LastIndexOfAny (directorySeparators); + if (indexOfLast < 0) + return String.Empty; + return path.Substring (0, indexOfLast); + } + + static string GetWildcardFileName (string path) + { + int indexOfLast = path.LastIndexOfAny (directorySeparators); + if (indexOfLast < 0) + return path; + if (indexOfLast == path.Length) + return String.Empty; + return path.Substring (indexOfLast + 1, path.Length - (indexOfLast + 1)); + } + + + static IEnumerable<string> ExpandWildcardFilePath (string filePath) + { + if (String.IsNullOrWhiteSpace (filePath)) + throw new ArgumentException ("Not a wildcard path"); + + string dir = GetWildcardDirectoryName (filePath); + string file = GetWildcardFileName (filePath); + + if (String.IsNullOrEmpty (dir) || String.IsNullOrEmpty (file)) + return null; + + SearchOption searchOption = SearchOption.TopDirectoryOnly; + if (dir.EndsWith (RecursiveDirectoryWildcard, StringComparison.Ordinal)) { + dir = dir.Substring (0, dir.Length - RecursiveDirectoryWildcard.Length); + searchOption = SearchOption.AllDirectories; + } + + if (!Directory.Exists (dir)) + return null; + + return Directory.GetFiles (dir, file, searchOption); + } + + static IEnumerable<ProjectFile> ResolveWildcardItems (ProjectFile wildcardFile) + { + var paths = ExpandWildcardFilePath (wildcardFile.Name); + if (paths == null) + yield break; + foreach (var resolvedFilePath in paths) { + var projectFile = (ProjectFile)wildcardFile.Clone (); + projectFile.Name = resolvedFilePath; + projectFile.Flags |= ProjectItemFlags.DontPersist; + yield return projectFile; + } + } + + struct MergedPropertyValue + { + public readonly string XmlValue; + public readonly bool PreserveExistingCase; + public readonly bool IsDefault; + + public MergedPropertyValue (string xmlValue, bool preserveExistingCase, bool isDefault) + { + this.XmlValue = xmlValue; + this.PreserveExistingCase = preserveExistingCase; + this.IsDefault = isDefault; + } + } + + protected virtual void OnWriteProjectHeader (ProgressMonitor monitor, MSBuildProject msproject) + { + IMSBuildPropertySet globalGroup = msproject.GetGlobalPropertyGroup (); + if (globalGroup == null) + globalGroup = msproject.AddNewPropertyGroup (false); + + if (Configurations.Count > 0) { + ItemConfiguration conf = Configurations.FirstOrDefault<ItemConfiguration> (c => c.Name == "Debug"); + if (conf == null) conf = Configurations [0]; + globalGroup.SetValue ("Configuration", conf.Name, condition:" '$(Configuration)' == '' "); + + string platform = conf.Platform.Length == 0 ? "AnyCPU" : conf.Platform; + globalGroup.SetValue ("Platform", platform, condition:" '$(Platform)' == '' "); + } + + if (TypeGuid == MSBuildProjectService.GenericItemGuid) { + DataType dt = MSBuildProjectService.DataContext.GetConfigurationDataType (GetType ()); + globalGroup.SetValue ("ItemType", dt.Name); + } + + globalGroup.SetValue ("ProductVersion", productVersion); + globalGroup.SetValue ("SchemaVersion", schemaVersion); + + globalGroup.SetValue ("ProjectGuid", ItemId); + + if (flavorGuids.Length > 0) { + string gg = string.Join (";", flavorGuids); + gg += ";" + TypeGuid; + globalGroup.SetValue ("ProjectTypeGuids", gg.ToUpper (), preserveExistingCase:true); + } else { + globalGroup.RemoveProperty ("ProjectTypeGuids"); + } + + // having no ToolsVersion is equivalent to 2.0, roundtrip that correctly + if (ToolsVersion != "2.0") + msproject.ToolsVersion = ToolsVersion; + else if (string.IsNullOrEmpty (msproject.ToolsVersion)) + msproject.ToolsVersion = null; + else + msproject.ToolsVersion = "2.0"; + + msproject.GetGlobalPropertyGroup ().SetValue ("Description", Description, ""); + msproject.GetGlobalPropertyGroup ().SetValue ("BaseIntermediateOutputPath", BaseIntermediateOutputPath, defaultValue:BaseDirectory.Combine ("obj"), relativeToProject:true); + } + + protected virtual void OnWriteProject (ProgressMonitor monitor, MSBuildProject msproject) + { + var toolsFormat = GetToolsFormat (); + + IMSBuildPropertySet globalGroup = msproject.GetGlobalPropertyGroup (); + + // Configurations + + if (Configurations.Count > 0) { + List<ConfigData> configData = GetConfigData (msproject, true); + + // Write configuration data, creating new property groups if necessary + + foreach (ProjectConfiguration conf in Configurations) { + ConfigData cdata = FindPropertyGroup (configData, conf); + if (cdata == null) { + MSBuildPropertyGroup pg = msproject.AddNewPropertyGroup (true); + pg.IgnoreDefaultValues = true; + pg.Condition = BuildConfigCondition (conf.Name, conf.Platform); + cdata = new ConfigData (conf.Name, conf.Platform, pg); + cdata.IsNew = true; + configData.Add (cdata); + } + ((MSBuildPropertyGroup)cdata.Group).IgnoreDefaultValues = true; + cdata.Exists = true; + ProjectExtension.OnWriteConfiguration (monitor, conf, cdata.Group); + } + + // Find the properties in all configurations that have the MergeToProject flag set + var mergeToProjectProperties = new HashSet<MergedProperty> (GetMergeToProjectProperties (configData)); + var mergeToProjectPropertyNames = new HashSet<string> (mergeToProjectProperties.Select (p => p.Name)); + var mergeToProjectPropertyValues = new Dictionary<string,MergedPropertyValue> (); + + foreach (ProjectConfiguration conf in Configurations) { + ConfigData cdata = FindPropertyGroup (configData, conf); + var propGroup = (MSBuildPropertyGroup) cdata.Group; + + IMSBuildPropertySet baseGroup = GetMergedConfiguration (configData, conf.Name, conf.Platform, propGroup); + + CollectMergetoprojectProperties (propGroup, mergeToProjectProperties, mergeToProjectPropertyValues); + + propGroup.UnMerge (baseGroup, mergeToProjectPropertyNames); + propGroup.IgnoreDefaultValues = false; + } + + // Move properties with common values from configurations to the main + // property group + foreach (KeyValuePair<string,MergedPropertyValue> prop in mergeToProjectPropertyValues) { + if (!prop.Value.IsDefault) + globalGroup.SetValue (prop.Key, prop.Value.XmlValue, preserveExistingCase: prop.Value.PreserveExistingCase); + else + globalGroup.RemoveProperty (prop.Key); + } + foreach (string prop in mergeToProjectPropertyNames) { + if (!mergeToProjectPropertyValues.ContainsKey (prop)) + globalGroup.RemoveProperty (prop); + } + foreach (SolutionItemConfiguration conf in Configurations) { + var propGroup = FindPropertyGroup (configData, conf).Group; + foreach (string mp in mergeToProjectPropertyValues.Keys) + propGroup.RemoveProperty (mp); + } + + // Remove groups corresponding to configurations that have been removed + // or groups which don't have any property and did not already exist + foreach (ConfigData cd in configData) { + if ((!cd.Exists && cd.FullySpecified) || (cd.IsNew && !cd.Group.Properties.Any ())) + msproject.RemoveGroup ((MSBuildPropertyGroup)cd.Group); + } + } + SaveProjectItems (monitor, toolsFormat, msproject); + + if (msproject.IsNewProject) { + foreach (var im in DefaultImports) + msproject.AddNewImport (im); + } + + foreach (var im in importsAdded) { + if (msproject.GetImport (im.Name, im.Condition) == null) + msproject.AddNewImport (im.Name, im.Condition); + } + foreach (var im in importsRemoved) { + var i = msproject.GetImport (im.Name, im.Condition); + if (i != null) + msproject.RemoveImport (i); + } + } + + void WriteConfiguration (ProgressMonitor monitor, List<ConfigData> configData, string conf, string platform) + { + IMSBuildPropertySet grp = GetMergedConfiguration (configData, conf, platform, null); + ProjectConfiguration config = (ProjectConfiguration) CreateConfiguration (conf); + + config.Platform = platform; + config.Read (grp, GetToolsFormat ()); + Configurations.Add (config); + projectExtension.OnReadConfiguration (monitor, config, grp); + } + + protected virtual void OnWriteConfiguration (ProgressMonitor monitor, ProjectConfiguration config, IMSBuildPropertySet pset) + { + config.Write (pset, SolutionFormat); + } + + IEnumerable<MergedProperty> GetMergeToProjectProperties (List<ConfigData> configData) + { + Dictionary<string,MergedProperty> mergeProps = new Dictionary<string, MergedProperty> (); + foreach (var cd in configData.Where (d => d.FullySpecified)) { + foreach (var prop in cd.Group.Properties) { + if (!prop.MergeToMainGroup) { + mergeProps [prop.Name] = null; + } else if (!mergeProps.ContainsKey (prop.Name)) + mergeProps [prop.Name] = prop.CreateMergedProperty (); + } + } + return mergeProps.Values.Where (p => p != null); + } + + void CollectMergetoprojectProperties (IMSBuildPropertySet pgroup, HashSet<MergedProperty> properties, Dictionary<string,MergedPropertyValue> mergeToProjectProperties) + { + // This method checks every property in pgroup which has the MergeToProject flag. + // If the value of this property is the same as the one stored in mergeToProjectProperties + // it means that the property can be merged to the main project property group (so far). + + foreach (var pinfo in new List<MergedProperty> (properties)) { + MSBuildProperty prop = pgroup.GetProperty (pinfo.Name); + + MergedPropertyValue mvalue; + if (!mergeToProjectProperties.TryGetValue (pinfo.Name, out mvalue)) { + if (prop != null) { + // This is the first time the value is checked. Just assign it. + mergeToProjectProperties.Add (pinfo.Name, new MergedPropertyValue (prop.Value, pinfo.PreserveExistingCase, pinfo.IsDefault)); + continue; + } + // If there is no value, it can't be merged + } + else if (prop != null && string.Equals (prop.Value, mvalue.XmlValue, mvalue.PreserveExistingCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) + // Same value. It can be merged. + continue; + + // The property can't be merged because different configurations have different + // values for it. Remove it from the list. + properties.Remove (pinfo); + mergeToProjectProperties.Remove (pinfo.Name); + } + } + + struct ItemInfo { + public MSBuildItem Item; + public bool Added; + } + + internal void SaveProjectItems (ProgressMonitor monitor, MSBuildFileFormat toolsFormat, MSBuildProject msproject, string pathPrefix = null) + { + // Remove old items + Dictionary<string, ItemInfo> oldItems = new Dictionary<string, ItemInfo> (); + foreach (MSBuildItem item in msproject.GetAllItems ()) + oldItems [item.Name + "<" + item.UnevaluatedInclude + "<" + item.Condition] = new ItemInfo () { + Item = item + }; + // Add the new items + foreach (ProjectItem ob in Items.Concat (WildcardItems).Where (it => !it.Flags.HasFlag (ProjectItemFlags.DontPersist))) + SaveProjectItem (monitor, toolsFormat, msproject, ob, oldItems, pathPrefix); + foreach (ItemInfo itemInfo in oldItems.Values) { + if (!itemInfo.Added) + msproject.RemoveItem (itemInfo.Item); + } + } + + void SaveProjectItem (ProgressMonitor monitor, MSBuildFileFormat fmt, MSBuildProject msproject, ProjectItem item, Dictionary<string,ItemInfo> oldItems, string pathPrefix = null) + { + var include = item.UnevaluatedInclude ?? item.Include; + if (pathPrefix != null && !include.StartsWith (pathPrefix)) + include = pathPrefix + include; + + MSBuildItem buildItem = AddOrGetBuildItem (msproject, oldItems, item.ItemName, include, item.Condition); + item.Write (fmt, buildItem); + if (pathPrefix != null) + buildItem.Include = include; + } + + MSBuildItem AddOrGetBuildItem (MSBuildProject msproject, Dictionary<string,ItemInfo> oldItems, string name, string include, string condition) + { + ItemInfo itemInfo; + string key = name + "<" + include + "<" + condition; + if (oldItems.TryGetValue (key, out itemInfo)) { + if (!itemInfo.Added) { + itemInfo.Added = true; + oldItems [key] = itemInfo; + } + return itemInfo.Item; + } else { + return msproject.AddNewItem (name, include); + } + } + + ConfigData FindPropertyGroup (List<ConfigData> configData, SolutionItemConfiguration config) + { + foreach (ConfigData data in configData) { + if (data.Config == config.Name && data.Platform == config.Platform) + return data; + } + return null; + } + + string BuildConfigCondition (string config, string platform) + { + if (platform.Length == 0) + platform = "AnyCPU"; + return " '$(Configuration)|$(Platform)' == '" + config + "|" + platform + "' "; + } + + bool IsMergeToProjectProperty (ItemProperty prop) + { + foreach (object at in prop.CustomAttributes) { + if (at is MergeToProjectAttribute) + return true; + } + return false; + } + + public void AddImportIfMissing (string name, string condition) + { + importsAdded.Add (new DotNetProjectImport (name, condition)); + } + + public void RemoveImport (string name) + { + importsRemoved.Add (new DotNetProjectImport (name)); + } + + List <DotNetProjectImport> importsAdded = new List<DotNetProjectImport> (); + + internal IList<DotNetProjectImport> ImportsAdded { + get { return importsAdded; } + } + + List <DotNetProjectImport> importsRemoved = new List<DotNetProjectImport> (); + + internal IList<DotNetProjectImport> ImportsRemoved { + get { return importsRemoved; } + } + + void ImportsSaved () + { + importsAdded.Clear (); + importsRemoved.Clear (); + } internal void NotifyFileRenamedInProject (ProjectFileRenamedEventArgs args) { NotifyModified ("Files"); @@ -919,6 +2199,10 @@ namespace MonoDevelop.Projects /// </summary> protected virtual void OnFileRemovedFromProject (ProjectFileEventArgs e) { + ProjectExtension.OnFileRemovedFromProject (e); + } + void DoOnFileRemovedFromProject (ProjectFileEventArgs e) + { buildActions = null; if (FileRemovedFromProject != null) { FileRemovedFromProject (this, e); @@ -930,38 +2214,54 @@ namespace MonoDevelop.Projects /// </summary> protected virtual void OnFileAddedToProject (ProjectFileEventArgs e) { + ProjectExtension.OnFileAddedToProject (e); + } + void DoOnFileAddedToProject (ProjectFileEventArgs e) + { buildActions = null; if (FileAddedToProject != null) { FileAddedToProject (this, e); } } - + /// <summary> /// Raises the FileChangedInProject event. /// </summary> protected virtual void OnFileChangedInProject (ProjectFileEventArgs e) { + ProjectExtension.OnFileChangedInProject (e); + } + void DoOnFileChangedInProject (ProjectFileEventArgs e) + { if (FileChangedInProject != null) { FileChangedInProject (this, e); } } - + /// <summary> /// Raises the FilePropertyChangedInProject event. /// </summary> protected virtual void OnFilePropertyChangedInProject (ProjectFileEventArgs e) { + ProjectExtension.OnFilePropertyChangedInProject (e); + } + void DoOnFilePropertyChangedInProject (ProjectFileEventArgs e) + { buildActions = null; if (FilePropertyChangedInProject != null) { FilePropertyChangedInProject (this, e); } } - + /// <summary> /// Raises the FileRenamedInProject event. /// </summary> protected virtual void OnFileRenamedInProject (ProjectFileRenamedEventArgs e) { + ProjectExtension.OnFileRenamedInProject (e); + } + void DoOnFileRenamedInProject (ProjectFileRenamedEventArgs e) + { if (FileRenamedInProject != null) { FileRenamedInProject (this, e); } @@ -991,6 +2291,126 @@ namespace MonoDevelop.Projects /// Occurs when a file of this project has been renamed /// </summary> public event ProjectFileRenamedEventHandler FileRenamedInProject; + + + class DefaultMSBuildProjectExtension: ProjectExtension + { + internal protected override bool SupportsFlavor (string guid) + { + return false; + } + + internal protected override bool OnGetIsCompileable (string fileName) + { + return false; + } + + internal protected override void OnGetProjectTypes (HashSet<string> types) + { + Project.OnGetProjectTypes (types); + } + + internal protected override Task<BuildResult> OnRunTarget (ProgressMonitor monitor, string target, ConfigurationSelector configuration) + { + return Project.DoRunTarget (monitor, target, configuration); + } + + internal protected override bool OnGetSupportsTarget (string target) + { + return Project.OnGetSupportsTarget (target); + } + + internal protected override string OnGetDefaultBuildAction (string fileName) + { + return Project.IsCompileable (fileName) ? BuildAction.Compile : BuildAction.None; + } + + internal protected override string OnGetDefaultResourceId (ProjectFile projectFile) + { + return Project.OnGetDefaultResourceId (projectFile); + } + + internal protected override IEnumerable<string> OnGetStandardBuildActions () + { + return BuildAction.StandardActions; + } + + internal protected override IList<string> OnGetCommonBuildActions () + { + return BuildAction.StandardActions; + } + + internal protected override ProjectItem OnCreateProjectItem (IMSBuildItemEvaluated item) + { + return Project.OnCreateProjectItem (item); + } + + internal protected override void OnPopulateSupportFileList (FileCopySet list, ConfigurationSelector configuration) + { + Project.DoPopulateSupportFileList (list, configuration); + } + + internal protected override void OnPopulateOutputFileList (List<FilePath> list, ConfigurationSelector configuration) + { + Project.DoPopulateOutputFileList (list, configuration); + } + + internal protected override FilePath OnGetOutputFileName (ConfigurationSelector configuration) + { + return FilePath.Null; + } + + internal protected override string[] SupportedLanguages { + get { + return new String[] { "" }; + } + } + + internal protected override void OnFileRemovedFromProject (ProjectFileEventArgs e) + { + Project.DoOnFileRemovedFromProject (e); + } + + internal protected override void OnFileAddedToProject (ProjectFileEventArgs e) + { + Project.DoOnFileAddedToProject (e); + } + + internal protected override void OnFileChangedInProject (ProjectFileEventArgs e) + { + Project.DoOnFileChangedInProject (e); + } + + internal protected override void OnFilePropertyChangedInProject (ProjectFileEventArgs e) + { + Project.DoOnFilePropertyChangedInProject (e); + } + + internal protected override void OnFileRenamedInProject (ProjectFileRenamedEventArgs e) + { + Project.DoOnFileRenamedInProject (e); + } + + internal protected override void OnReadProject (ProgressMonitor monitor, MSBuildProject msproject) + { + Project.OnReadProject (monitor, msproject); + } + + internal protected override void OnWriteProject (ProgressMonitor monitor, MSBuildProject msproject) + { + Project.OnWriteProject (monitor, msproject); + } + + internal protected override void OnReadConfiguration (ProgressMonitor monitor, ProjectConfiguration config, IMSBuildPropertySet grp) + { + Project.OnReadConfiguration (monitor, config, grp); + } + + internal protected override void OnWriteConfiguration (ProgressMonitor monitor, ProjectConfiguration config, IMSBuildPropertySet grp) + { + Project.OnWriteConfiguration (monitor, config, grp); + } + } } public delegate void ProjectEventHandler (Object sender, ProjectEventArgs e); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectConfiguration.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectConfiguration.cs index 1db4404892..7a4420c57c 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectConfiguration.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectConfiguration.cs @@ -32,10 +32,12 @@ using System.IO; using MonoDevelop.Core.Serialization; using System.Collections.Generic; using MonoDevelop.Core.StringParsing; +using System.Xml.Linq; +using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects { - public class ProjectConfiguration : SolutionItemConfiguration + public class ProjectConfiguration : SolutionItemConfiguration, IMSBuildDataObject { public ProjectConfiguration () @@ -46,8 +48,64 @@ namespace MonoDevelop.Projects { } - [ProjectPathItemProperty("IntermediateOutputPath")] - private FilePath intermediateOutputDirectory; + #region IProjectConfigurationData implementation + + void IMSBuildDataObject.Read (IMSBuildPropertySet pset, MSBuildFileFormat format) + { + Read (pset, format); + } + + void IMSBuildDataObject.Write (IMSBuildPropertySet pset, MSBuildFileFormat format) + { + Write (pset, format); + } + + #endregion + + internal protected virtual void Read (IMSBuildPropertySet pset, MSBuildFileFormat format) + { + intermediateOutputDirectory = pset.GetPathValue ("IntermediateOutputPath"); + outputDirectory = pset.GetPathValue ("OutputPath", defaultValue:"." + Path.DirectorySeparatorChar); + debugMode = pset.GetValue<bool> ("DebugSymbols"); + pauseConsoleOutput = pset.GetValue ("ConsolePause", true); + externalConsole = pset.GetValue<bool> ("ExternalConsole"); + commandLineParameters = pset.GetValue ("Commandlineparameters", ""); + runWithWarnings = pset.GetValue ("RunWithWarnings", true); + + var svars = pset.GetValue ("EnvironmentVariables"); + if (svars != null) { + XElement vars = XElement.Parse (svars); + foreach (var val in vars.Elements ("Variable")) { + var name = (string)val.Attribute ("name"); + if (name != null) + environmentVariables [name] = (string)val.Attribute ("value"); + } + } + } + + internal protected virtual void Write (IMSBuildPropertySet pset, MSBuildFileFormat format) + { + pset.SetValue ("IntermediateOutputPath", intermediateOutputDirectory, defaultValue:FilePath.Null); + pset.SetValue ("DebugSymbols", debugMode, false); + pset.SetValue ("OutputPath", outputDirectory); + pset.SetValue ("ConsolePause", pauseConsoleOutput, true); + pset.SetValue ("ExternalConsole", externalConsole, false); + pset.SetValue ("Commandlineparameters", commandLineParameters, ""); + pset.SetValue ("RunWithWarnings", runWithWarnings, true); + + if (environmentVariables.Count > 0) { + XElement e = new XElement ("EnvironmentVariables"); + foreach (var v in environmentVariables) { + var val = new XElement ("Variable"); + val.SetAttributeValue ("name", v.Key); + val.SetAttributeValue ("value", v.Value); + e.Add (val); + } + pset.SetValue ("EnvironmentVariables", e.ToString ()); + } + } + + FilePath intermediateOutputDirectory = FilePath.Empty; public virtual FilePath IntermediateOutputDirectory { get { @@ -66,46 +124,37 @@ namespace MonoDevelop.Projects } } - [ProjectPathItemProperty("OutputPath")] - private FilePath outputDirectory = "." + Path.DirectorySeparatorChar; + FilePath outputDirectory = "." + Path.DirectorySeparatorChar; public virtual FilePath OutputDirectory { get { return outputDirectory; } set { outputDirectory = value; } } - [ItemProperty("DebugSymbols", DefaultValue = false)] - private bool debugMode = false; + bool debugMode = false; public bool DebugMode { get { return debugMode; } set { debugMode = value; } } - [ItemProperty("ConsolePause", DefaultValue = true)] - private bool pauseConsoleOutput = true; + bool pauseConsoleOutput = true; public bool PauseConsoleOutput { get { return pauseConsoleOutput; } set { pauseConsoleOutput = value; } } - [ItemProperty("Externalconsole", DefaultValue = false)] - private bool externalConsole = false; + bool externalConsole = false; public bool ExternalConsole { get { return externalConsole; } set { externalConsole = value; } } - [ItemProperty("Commandlineparameters", DefaultValue = "")] - private string commandLineParameters = ""; + string commandLineParameters = ""; public string CommandLineParameters { get { return commandLineParameters; } set { commandLineParameters = value; } } - [ItemProperty("EnvironmentVariables", SkipEmpty = true)] - [ItemProperty("Variable", Scope = "item")] - [ItemProperty("name", Scope = "key")] - [ItemProperty("value", Scope = "value")] - private Dictionary<string, string> environmentVariables = new Dictionary<string, string> (); + Dictionary<string, string> environmentVariables = new Dictionary<string, string> (); public Dictionary<string, string> EnvironmentVariables { get { return environmentVariables; } } @@ -122,8 +171,7 @@ namespace MonoDevelop.Projects return vars; } - [ItemProperty("RunWithWarnings", DefaultValue = true)] - private bool runWithWarnings = true; + bool runWithWarnings = true; public virtual bool RunWithWarnings { get { return runWithWarnings; } set { runWithWarnings = value; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectConvertTool.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectConvertTool.cs index 6bedef9308..c849d3b8d0 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectConvertTool.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectConvertTool.cs @@ -177,7 +177,10 @@ namespace MonoDevelop.Projects destPath = Path.GetDirectoryName (projectFile); destPath = FileService.GetFullPath (destPath); - string ofile = Services.ProjectService.Export (monitor, projectFile, itemsToExport, destPath, format); + var t = Services.ProjectService.Export (monitor, projectFile, itemsToExport, destPath, format); + t.Wait (); + string ofile = t.Result; + if (ofile != null) { Console.WriteLine ("Saved file: " + ofile); return 0; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectExtension.cs new file mode 100644 index 0000000000..a1dd21715c --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectExtension.cs @@ -0,0 +1,182 @@ +// +// MSBuildProjectExtension.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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; +using System.Threading.Tasks; +using MonoDevelop.Projects.Formats.MSBuild; +using Mono.Addins; +using System.Linq; + +namespace MonoDevelop.Projects +{ + public class ProjectExtension: SolutionItemExtension + { + ProjectExtension next; + + public Project Project { + get { return (Project) base.Item; } + } + + internal protected override void InitializeChain (ChainedExtension next) + { + base.InitializeChain (next); + this.next = FindNextImplementation<ProjectExtension> (next); + } + + internal protected override bool SupportsObject (WorkspaceObject item) + { + return item is Project && base.SupportsObject (item); + } + + internal protected virtual bool SupportsFlavor (string guid) + { + if (FlavorGuid != null && guid.Equals (FlavorGuid, StringComparison.OrdinalIgnoreCase)) + return true; + return next.SupportsFlavor (guid); + } + + internal protected virtual Task<BuildResult> OnRunTarget (ProgressMonitor monitor, string target, ConfigurationSelector configuration) + { + return next.OnRunTarget (monitor, target, configuration); + } + + internal protected virtual bool OnGetSupportsTarget (string target) + { + return next.OnGetSupportsTarget (target); + } + + internal protected virtual void OnGetProjectTypes (HashSet<string> types) + { + next.OnGetProjectTypes (types); + } + + internal protected virtual void OnReadProject (ProgressMonitor monitor, MSBuildProject msproject) + { + next.OnReadProject (monitor, msproject); + } + + internal protected virtual void OnReadConfiguration (ProgressMonitor monitor, ProjectConfiguration config, IMSBuildPropertySet pset) + { + next.OnReadConfiguration (monitor, config, pset); + } + + internal protected virtual void OnWriteProject (ProgressMonitor monitor, MSBuildProject msproject) + { + next.OnWriteProject (monitor, msproject); + } + + internal protected virtual void OnWriteConfiguration (ProgressMonitor monitor, ProjectConfiguration config, IMSBuildPropertySet pset) + { + next.OnWriteConfiguration (monitor, config, pset); + } + + #region Building + + internal protected virtual bool OnGetIsCompileable (string fileName) + { + return next.OnGetIsCompileable (fileName); + } + + internal protected virtual string OnGetDefaultBuildAction (string fileName) + { + return next.OnGetDefaultBuildAction (fileName); + } + + internal protected virtual string OnGetDefaultResourceId (ProjectFile projectFile) + { + return next.OnGetDefaultResourceId (projectFile); + } + + internal protected virtual IEnumerable<string> OnGetStandardBuildActions () + { + return next.OnGetStandardBuildActions (); + } + + internal protected virtual IList<string> OnGetCommonBuildActions () + { + return next.OnGetCommonBuildActions (); + } + + internal protected virtual ProjectItem OnCreateProjectItem (IMSBuildItemEvaluated item) + { + return next.OnCreateProjectItem (item); + } + + internal protected virtual void OnPopulateSupportFileList (FileCopySet list, ConfigurationSelector configuration) + { + next.OnPopulateSupportFileList (list, configuration); + } + + internal protected virtual void OnPopulateOutputFileList (List<FilePath> list, ConfigurationSelector configuration) + { + next.OnPopulateOutputFileList (list, configuration); + } + + internal protected virtual FilePath OnGetOutputFileName (ConfigurationSelector configuration) + { + return next.OnGetOutputFileName (configuration); + } + + internal protected virtual string[] SupportedLanguages { + get { + return next.SupportedLanguages; + } + } + + #endregion + + #region Events + + internal protected virtual void OnFileRemovedFromProject (ProjectFileEventArgs e) + { + next.OnFileRemovedFromProject (e); + } + + internal protected virtual void OnFileAddedToProject (ProjectFileEventArgs e) + { + next.OnFileAddedToProject (e); + } + + internal protected virtual void OnFileChangedInProject (ProjectFileEventArgs e) + { + next.OnFileChangedInProject (e); + } + + internal protected virtual void OnFilePropertyChangedInProject (ProjectFileEventArgs e) + { + next.OnFilePropertyChangedInProject (e); + } + + internal protected virtual void OnFileRenamedInProject (ProjectFileRenamedEventArgs e) + { + next.OnFileRenamedInProject (e); + } + + #endregion + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectFile.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectFile.cs index 77d9f63534..ad7d99b972 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectFile.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectFile.cs @@ -37,6 +37,7 @@ using MonoDevelop.Core; using MonoDevelop.Core.Serialization; using MonoDevelop.Projects; using MonoDevelop.Projects.Extensions; +using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects { @@ -55,21 +56,98 @@ namespace MonoDevelop.Projects { } - public ProjectFile (string filename) + public ProjectFile (string filename): this (filename, MonoDevelop.Projects.BuildAction.Compile) { - this.filename = FileService.GetFullPath (filename); - subtype = Subtype.Code; - buildaction = MonoDevelop.Projects.BuildAction.Compile; } public ProjectFile (string filename, string buildAction) { this.filename = FileService.GetFullPath (filename); subtype = Subtype.Code; - buildaction = buildAction; + ItemName = buildaction = buildAction; + } + + public override string Include { + get { + if (project != null) { + string path = MSBuildProjectService.ToMSBuildPath (project.ItemDirectory, FilePath); + if (path.Length > 0) { + //directory paths must end with '/' + if ((Subtype == Subtype.Directory) && path [path.Length - 1] != '\\') + path = path + "\\"; + return path; + } + } + return base.Include; + } + protected set { + base.Include = value; + } + } + + internal protected override void Read (Project project, IMSBuildItemEvaluated buildItem) + { + base.Read (project, buildItem); + + if (buildItem.Name == "Folder") { + // Read folders + string path = MSBuildProjectService.FromMSBuildPath (project.ItemDirectory, buildItem.Include); + Name = Path.GetDirectoryName (path); + Subtype = Subtype.Directory; + return; + } + + Name = MSBuildProjectService.FromMSBuildPath (project.ItemDirectory, buildItem.Include); + BuildAction = buildItem.Name; + + DependsOn = buildItem.Metadata.GetPathValue ("DependentUpon", relativeToPath:FilePath.ParentDirectory); + + string copy = buildItem.Metadata.GetValue ("CopyToOutputDirectory"); + if (!string.IsNullOrEmpty (copy)) { + switch (copy) { + case "None": break; + case "Always": CopyToOutputDirectory = FileCopyMode.Always; break; + case "PreserveNewest": CopyToOutputDirectory = FileCopyMode.PreserveNewest; break; + default: + LoggingService.LogWarning ( + "Unrecognised value {0} for CopyToOutputDirectory MSBuild property", + copy); + break; + } + } + + Visible = buildItem.Metadata.GetValue ("Visible", true); + resourceId = buildItem.Metadata.GetValue ("LogicalName"); + contentType = buildItem.Metadata.GetValue ("SubType"); + generator = buildItem.Metadata.GetValue ("Generator"); + customToolNamespace = buildItem.Metadata.GetValue ("CustomToolNamespace"); + lastGenOutput = buildItem.Metadata.GetValue ("LastGenOutput"); + Link = buildItem.Metadata.GetPathValue ("Link", relativeToProject:false); } - [ItemProperty("subtype")] + internal protected override void Write (MSBuildFileFormat fmt, MSBuildItem buildItem) + { + base.Write (fmt, buildItem); + + buildItem.Metadata.SetValue ("DependentUpon", DependsOn, FilePath.Empty, relativeToPath:FilePath.ParentDirectory); + buildItem.Metadata.SetValue ("SubType", ContentType, ""); + buildItem.Metadata.SetValue ("Generator", Generator, ""); + buildItem.Metadata.SetValue ("CustomToolNamespace", CustomToolNamespace, ""); + buildItem.Metadata.SetValue ("LastGenOutput", LastGenOutput, ""); + buildItem.Metadata.SetValue ("Link", Link, FilePath.Empty, relativeToProject:false); + buildItem.Metadata.SetValue ("CopyToOutputDirectory", CopyToOutputDirectory.ToString (), "None"); + buildItem.Metadata.SetValue ("Visible", Visible, true); + + var resId = ResourceId; + + //For EmbeddedResource, emit LogicalName only when it does not match the default Id + if (BuildAction == MonoDevelop.Projects.BuildAction.EmbeddedResource && project.GetDefaultResourceId (this) == resId) + resId = ""; + + buildItem.Metadata.SetValue ("LogicalName", resId, ""); + } + + Subtype subtype; public Subtype Subtype { get { return subtype; } @@ -79,17 +157,6 @@ namespace MonoDevelop.Projects } } - [ItemProperty("data", DefaultValue = "")] - string data = ""; - public string Data { - get { return data; } - set { - data = value; - OnChanged ("Data"); - } - } - - public string Name { get { return filename; } @@ -117,8 +184,6 @@ namespace MonoDevelop.Projects } } - - [ItemProperty("buildaction")] string buildaction = MonoDevelop.Projects.BuildAction.None; public string BuildAction { get { return buildaction; } @@ -128,7 +193,6 @@ namespace MonoDevelop.Projects } } - [ItemProperty("resource_id", DefaultValue = "")] string resourceId = String.Empty; internal string GetResourceId (IResourceHandler resourceHandler) @@ -148,15 +212,6 @@ namespace MonoDevelop.Projects } /// <summary> - /// Set to true if this ProjectFile was created at load time by - /// a ProjectFile containing wildcards. If true, this instance - /// should not be saved to a csproj file. - /// </summary> - internal bool IsOriginatedFromWildcard { - get; set; - } - - /// <summary> /// The file should be treated as effectively having this relative path within the project. If the file is /// a link or outside the project root, this will not be the same as the physical file. /// </summary> @@ -179,17 +234,15 @@ namespace MonoDevelop.Projects get { return project; } } - [ItemProperty("SubType")] - string contentType = String.Empty; + string contentType; public string ContentType { - get { return contentType; } + get { return contentType ?? ""; } set { contentType = value; OnChanged ("ContentType"); } } - [ItemProperty("Visible", DefaultValue = true)] bool visible = true; /// <summary> @@ -205,14 +258,13 @@ namespace MonoDevelop.Projects } } - [ItemProperty("Generator", DefaultValue = "")] string generator; /// <summary> /// The ID of a custom code generator. /// </summary> public string Generator { - get { return generator; } + get { return generator ?? ""; } set { if (generator != value) { generator = value; @@ -221,14 +273,13 @@ namespace MonoDevelop.Projects } } - [ItemProperty("CustomToolNamespace", DefaultValue = "")] string customToolNamespace; /// <summary> /// Overrides the namespace in which the custom code generator should generate code. /// </summary> public string CustomToolNamespace { - get { return customToolNamespace; } + get { return customToolNamespace ?? ""; } set { if (customToolNamespace != value) { customToolNamespace = value; @@ -238,14 +289,13 @@ namespace MonoDevelop.Projects } - [ItemProperty("LastGenOutput", DefaultValue = "")] string lastGenOutput; /// <summary> /// The file most recently generated by the custom tool. Relative to this file's parent directory. /// </summary> public string LastGenOutput { - get { return lastGenOutput; } + get { return lastGenOutput ?? ""; } set { if (lastGenOutput != value) { lastGenOutput = value; @@ -254,8 +304,6 @@ namespace MonoDevelop.Projects } } - - [RelativeProjectPathItemProperty("Link", DefaultValue = "")] string link; /// <summary> @@ -263,7 +311,7 @@ namespace MonoDevelop.Projects /// within the project root. Use ProjectVirtualPath to read the effective virtual path for any file. /// </summary> public FilePath Link { - get { return link; } + get { return link ?? ""; } set { if (link != value) { if (value.IsAbsolute || value.ToString ().StartsWith ("..", StringComparison.Ordinal)) @@ -296,8 +344,7 @@ namespace MonoDevelop.Projects } } - [ItemProperty("copyToOutputDirectory", DefaultValue = FileCopyMode.None)] - FileCopyMode copyToOutputDirectory; + FileCopyMode copyToOutputDirectory = FileCopyMode.None; public FileCopyMode CopyToOutputDirectory { get { return copyToOutputDirectory; } set { @@ -311,7 +358,7 @@ namespace MonoDevelop.Projects #region File grouping string dependsOn; public string DependsOn { - get { return dependsOn; } + get { return dependsOn ?? ""; } set { if (dependsOn != value) { @@ -406,15 +453,18 @@ namespace MonoDevelop.Projects return resourceId; } set { - resourceId = value; - OnChanged ("ResourceId"); + if (resourceId != value) { + var oldVal = ResourceId; + resourceId = value; + if (ResourceId != oldVal) + OnChanged ("ResourceId"); + } } } internal void SetProject (Project project) { this.project = project; - if (project != null) OnVirtualPathChanged (FilePath.Null, ProjectVirtualPath); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItem.cs index 8c5a7303fa..cb9934cd82 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItem.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItem.cs @@ -26,14 +26,16 @@ using System; using System.Collections; +using System.Collections.Generic; using MonoDevelop.Core.Serialization; +using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects { public class ProjectItem: IExtendedDataItem { Hashtable extendedProperties; - + public IDictionary ExtendedProperties { get { if (extendedProperties == null) @@ -41,19 +43,42 @@ namespace MonoDevelop.Projects return extendedProperties; } } + + public void SetMetadata (string name, string value, bool isXml = false) + { + } + + public string GetMetadata (string name, string defaultValue = null) + { + return null; + } internal string Condition { get; set; } + public string ItemName { get; protected set; } + + public virtual string Include { get; protected set; } + + public string UnevaluatedInclude { get; protected set; } + public ProjectItemFlags Flags { get; set; } + + internal protected virtual void Read (Project project, IMSBuildItemEvaluated buildItem) + { + ItemName = buildItem.Name; + Include = buildItem.Include; + UnevaluatedInclude = buildItem.UnevaluatedInclude; + Condition = buildItem.Condition; + } + + internal protected virtual void Write (MSBuildFileFormat fmt, MSBuildItem buildItem) + { + buildItem.Condition = Condition; + } } public class UnknownProjectItem: ProjectItem { - public string ItemName { get; private set; } - - [ItemProperty ("Include")] - public string Include { get; private set; } - public UnknownProjectItem (string name, string include) { this.ItemName = name; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItemCollection.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItemCollection.cs index cfdbd848dc..4a2badcff1 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItemCollection.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItemCollection.cs @@ -37,7 +37,7 @@ namespace MonoDevelop.Projects { } - internal ProjectItemCollection (SolutionEntityItem parent): base (parent) + internal ProjectItemCollection (SolutionItem parent): base (parent) { } } @@ -51,11 +51,11 @@ namespace MonoDevelop.Projects public class ProjectItemCollection<T>: ItemCollection<T>, IItemListHandler where T: ProjectItem { - SolutionEntityItem parent; + SolutionItem parent; IItemListHandler parentCollection; List<IItemListHandler> subCollections; - internal ProjectItemCollection (SolutionEntityItem parent) + internal ProjectItemCollection (SolutionItem parent) { this.parent = parent; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItemEventArgs.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItemEventArgs.cs index 50cbb2dd8e..806c633362 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItemEventArgs.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItemEventArgs.cs @@ -34,7 +34,7 @@ namespace MonoDevelop.Projects { } - public ProjectItemEventArgs (SolutionEntityItem solutionItem, ProjectItem item) + public ProjectItemEventArgs (SolutionItem solutionItem, ProjectItem item) { Add (new ProjectItemEventInfo (solutionItem, item)); } @@ -43,15 +43,15 @@ namespace MonoDevelop.Projects public class ProjectItemEventInfo { ProjectItem item; - SolutionEntityItem solutionItem; + SolutionItem solutionItem; - public ProjectItemEventInfo (SolutionEntityItem solutionItem, ProjectItem item) + public ProjectItemEventInfo (SolutionItem solutionItem, ProjectItem item) { this.item = item; this.solutionItem = solutionItem; } - public SolutionEntityItem SolutionItem { + public SolutionItem SolutionItem { get { return solutionItem; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectParameters.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectParameters.cs index 457111b18d..f78e021bc7 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectParameters.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectParameters.cs @@ -27,11 +27,12 @@ using System; using MonoDevelop.Core.Serialization; using MonoDevelop.Projects.Extensions; +using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects { [DataItem (FallbackType=typeof(UnknownProjectParameters))] - public class ProjectParameters: ILoadController + public class ProjectParameters: ILoadController, IMSBuildDataObject { DotNetProject parentProject; @@ -69,5 +70,23 @@ namespace MonoDevelop.Projects protected virtual void OnEndLoad () { } + + void IMSBuildDataObject.Read (IMSBuildPropertySet pset, MSBuildFileFormat format) + { + Read (pset, format); + } + + void IMSBuildDataObject.Write (IMSBuildPropertySet pset, MSBuildFileFormat format) + { + Write (pset, format); + } + + protected virtual void Read (IMSBuildPropertySet pset, MSBuildFileFormat format) + { + } + + protected virtual void Write (IMSBuildPropertySet pset, MSBuildFileFormat format) + { + } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectReference.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectReference.cs index 74e56794d8..a30115c441 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectReference.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectReference.cs @@ -37,6 +37,7 @@ using System.ComponentModel; using MonoDevelop.Projects; using MonoDevelop.Core.Serialization; using MonoDevelop.Core.Assemblies; +using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects { @@ -73,8 +74,11 @@ namespace MonoDevelop.Projects string package; SystemPackage cachedPackage; string customError; - string hintPath; - + FilePath hintPath; + + string originalMSBuildReferenceInclude; + string originalMSBuildReferenceHintPath; + public event EventHandler StatusChanged; [ItemProperty ("Package", DefaultValue="")] @@ -100,6 +104,19 @@ namespace MonoDevelop.Projects ownerProject = project; UpdatePackageReference (); } + + public sealed override string Include { + get { + if (referenceType == ReferenceType.Project && base.Include == null) { + Project refProj = OwnerProject != null && OwnerProject.ParentSolution != null ? OwnerProject.ParentSolution.FindProjectByName (Reference) : null; + if (refProj != null) + base.Include = MSBuildProjectService.ToMSBuildPath (OwnerProject.ItemDirectory, refProj.FileName); + else + base.Include = Reference; + } + return base.Include; + } + } public ProjectReference (ReferenceType referenceType, string reference): this (referenceType, reference, null) { @@ -107,20 +124,66 @@ namespace MonoDevelop.Projects public ProjectReference (ReferenceType referenceType, string reference, string hintPath) { + Init (referenceType, reference, hintPath); + } + + void Init (ReferenceType referenceType, string reference, string hintPath) + { if (referenceType == ReferenceType.Assembly) { specificVersion = false; if (hintPath == null) { hintPath = reference; reference = Path.GetFileNameWithoutExtension (reference); } + + if (Include == null) { + if (File.Exists (HintPath)) { + try { + var aname = System.Reflection.AssemblyName.GetAssemblyName (HintPath); + if (SpecificVersion) { + Include = aname.FullName; + } else { + Include = aname.Name; + } + } catch (Exception ex) { + string msg = string.Format ("Could not get full name for assembly '{0}'.", Reference); + LoggingService.LogError (msg, ex); + } + } + if (Include == null) + Include = Path.GetFileNameWithoutExtension (hintPath); + } + } + + switch (referenceType) { + case ReferenceType.Package: + case ReferenceType.Assembly: + ItemName = "Reference"; + break; + case ReferenceType.Project: + ItemName = "ProjectReference"; + break; } this.referenceType = referenceType; this.reference = reference; this.hintPath = hintPath; UpdatePackageReference (); + + if (referenceType == ReferenceType.Package && Include == null) { + Include = StoredReference; + SystemPackage pkg = Package; + if (pkg != null && pkg.IsFrameworkPackage) { + int i = Include.IndexOf (','); + if (i != -1) + Include = Include.Substring (0, i).Trim (); + } + } + + if (Include == null) + Include = reference; } - + public ProjectReference (Project referencedProject) { referenceType = ReferenceType.Project; @@ -138,7 +201,120 @@ namespace MonoDevelop.Projects package = asm.Package.Name; UpdatePackageReference (); } - + + internal protected override void Read (Project project, IMSBuildItemEvaluated buildItem) + { + base.Read (project, buildItem); + + if (buildItem.Name == "Reference") + ReadReference (project, buildItem); + else if (buildItem.Name == "ProjectReference") + ReadProjectReference (project, buildItem); + + LocalCopy = buildItem.Metadata.GetValue ("Private", DefaultLocalCopy); + } + + void ReadReference (Project project, IMSBuildItemEvaluated buildItem) + { + if (buildItem.Metadata.HasProperty ("HintPath")) { + FilePath path; + var p = buildItem.Metadata.GetProperty ("HintPath"); + if (p != null) + originalMSBuildReferenceHintPath = p.UnevaluatedValue; + if (!buildItem.Metadata.TryGetPathValue ("HintPath", out path)) { + var hp = buildItem.Metadata.GetValue ("HintPath"); + Init (ReferenceType.Assembly, hp, null); + SetInvalid (GettextCatalog.GetString ("Invalid file path")); + originalMSBuildReferenceInclude = buildItem.Include; + } else { + var type = File.Exists (path) ? ReferenceType.Assembly : ReferenceType.Package; + Init (type, buildItem.Include, path); + } + } else { + string asm = buildItem.Include; + // This is a workaround for a VS bug. Looks like it is writing this assembly incorrectly + if (asm == "System.configuration") + asm = "System.Configuration"; + else if (asm == "System.XML") + asm = "System.Xml"; + else if (asm == "system") + asm = "System"; + Init (ReferenceType.Package, asm, null); + } + + string specificVersion = buildItem.Metadata.GetValue ("SpecificVersion"); + if (string.IsNullOrWhiteSpace (specificVersion)) { + // If the SpecificVersion element isn't present, check if the Assembly Reference specifies a Version + SpecificVersion = ReferenceStringHasVersion (buildItem.Include); + } + else { + bool value; + // if we can't parse the value, default to false which is more permissive + SpecificVersion = bool.TryParse (specificVersion, out value) && value; + } + } + + void ReadProjectReference (Project project, IMSBuildItemEvaluated buildItem) + { + // Get the project name from the path, since the Name attribute may other stuff other than the name + string path = MSBuildProjectService.FromMSBuildPath (project.ItemDirectory, buildItem.Include); + string name = Path.GetFileNameWithoutExtension (path); + Init (ReferenceType.Project, name, null); + } + + internal protected override void Write (MSBuildFileFormat fmt, MSBuildItem buildItem) + { + // If the project is not supported, don't try to update any metadata of the property, + // just leave what was read + if (OwnerProject.IsUnsupportedProject) + return; + + base.Write (fmt, buildItem); + + if (ReferenceType == ReferenceType.Assembly) { + if (originalMSBuildReferenceHintPath != null) + buildItem.Metadata.SetValue ("HintPath", originalMSBuildReferenceHintPath, ""); + else + buildItem.Metadata.SetValue ("HintPath", HintPath); + + buildItem.Metadata.SetValue ("SpecificVersion", SpecificVersion || !ReferenceStringHasVersion (Include), true); + } + else if (ReferenceType == ReferenceType.Package) { + buildItem.Metadata.SetValue ("SpecificVersion", SpecificVersion || !ReferenceStringHasVersion (Include), true); + + //RequiredTargetFramework is undocumented, maybe only a hint for VS. Only seems to be used for .NETFramework + var dnp = OwnerProject as DotNetProject; + IList supportedFrameworks = fmt.SupportedFrameworks; + if (supportedFrameworks != null && dnp != null && Package != null + && dnp.TargetFramework.Id.Identifier == TargetFrameworkMoniker.ID_NET_FRAMEWORK + && Package.IsFrameworkPackage && supportedFrameworks.Contains (Package.TargetFramework) + && Package.TargetFramework.Version != "2.0" && supportedFrameworks.Count > 1) + { + TargetFramework fx = Runtime.SystemAssemblyService.GetTargetFramework (Package.TargetFramework); + buildItem.Metadata.SetValue ("RequiredTargetFramework", fx.Id.Version); + } else { + buildItem.Metadata.RemoveProperty ("RequiredTargetFramework"); + } + + buildItem.Metadata.SetValue ("HintPath", originalMSBuildReferenceHintPath, ""); + } + else if (ReferenceType == ReferenceType.Project) { + Project refProj = OwnerProject.ParentSolution.FindProjectByName (Reference); + if (refProj != null) { + buildItem.Metadata.SetValue ("Project", refProj.ItemId); + buildItem.Metadata.SetValue ("Name", refProj.Name); + } + } + + buildItem.Metadata.SetValue ("Private", LocalCopy, DefaultLocalCopy); + } + + bool ReferenceStringHasVersion (string asmName) + { + int commaPos = asmName.IndexOf (','); + return commaPos >= 0 && asmName.IndexOf ("Version", commaPos) >= 0; + } + protected void InitCustomReference (string reference) { Reference = reference; @@ -256,8 +432,7 @@ namespace MonoDevelop.Projects if (notFound) { if (ownerProject != null) { bool isDefaultRuntime = Runtime.SystemAssemblyService.DefaultRuntime == TargetRuntime; - var hintPath = ExtendedProperties ["_OriginalMSBuildReferenceHintPath"] as string; - bool probablyFrameworkAssembly = string.IsNullOrEmpty (hintPath); + bool probablyFrameworkAssembly = string.IsNullOrEmpty (originalMSBuildReferenceHintPath); if (TargetRuntime.IsInstalled (TargetFramework) || !probablyFrameworkAssembly) { if (isDefaultRuntime) @@ -307,7 +482,7 @@ namespace MonoDevelop.Projects } } - public string HintPath { + public FilePath HintPath { get { return hintPath; } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs index 64d8bc6e76..fa493fb8f7 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs @@ -26,28 +26,22 @@ using System; using System.Collections; -using System.Collections.Specialized; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Xml; -using System.CodeDom.Compiler; -using System.Threading; using MonoDevelop.Projects; using MonoDevelop.Core.Serialization; using MonoDevelop.Core; using Mono.Addins; -using MonoDevelop.Core.ProgressMonitoring; -using MonoDevelop.Core.Execution; using MonoDevelop.Core.Assemblies; using MonoDevelop.Core.Instrumentation; using MonoDevelop.Projects.Extensions; using Mono.Unix; -using MonoDevelop.Core.StringParsing; using System.Linq; using MonoDevelop.Projects.Formats.MSBuild; +using System.Threading.Tasks; namespace MonoDevelop.Projects { @@ -63,7 +57,7 @@ namespace MonoDevelop.Projects TargetFramework defaultTargetFramework; string defaultPlatformTarget = "x86"; - static readonly TargetFrameworkMoniker DefaultTargetFrameworkId = TargetFrameworkMoniker.NET_4_0; + static readonly TargetFrameworkMoniker DefaultTargetFrameworkId = TargetFrameworkMoniker.NET_4_5; public const string BuildTarget = "Build"; public const string CleanTarget = "Clean"; @@ -72,7 +66,9 @@ namespace MonoDevelop.Projects const string SerializableClassesExtensionPath = "/MonoDevelop/ProjectModel/SerializableClasses"; const string ExtendedPropertiesExtensionPath = "/MonoDevelop/ProjectModel/ExtendedProperties"; const string ProjectBindingsExtensionPath = "/MonoDevelop/ProjectModel/ProjectBindings"; - + + internal const string ProjectModelExtensionsPath = "/MonoDevelop/ProjectModel/ProjectModelExtensions"; + internal event EventHandler DataContextChanged; class ExtensionChainInfo @@ -101,7 +97,7 @@ namespace MonoDevelop.Projects get { return formatManager; } } - internal ProjectServiceExtension GetExtensionChain (IBuildTarget target) + internal ProjectServiceExtension GetExtensionChain (WorkspaceObject target) { ProjectServiceExtension chain; if (target != null) { @@ -147,18 +143,14 @@ namespace MonoDevelop.Projects ProjectServiceExtension CreateExtensionChain (ProjectServiceExtension[] extensions) { - var first = new CustomCommandExtension (); - - for (int n=0; n<extensions.Length - 1; n++) - extensions [n].Next = extensions [n + 1]; - if (extensions.Length > 0) { + for (int n=0; n<extensions.Length - 1; n++) + extensions [n].Next = extensions [n + 1]; extensions [extensions.Length - 1].Next = extensionChainTerminator; - first.Next = extensions [0]; + return extensions [0]; } else { - first.Next = extensionChainTerminator; + return extensionChainTerminator; } - return first; } public string DefaultPlatformTarget { @@ -191,16 +183,16 @@ namespace MonoDevelop.Projects return formats [0]; } - public SolutionEntityItem ReadSolutionItem (IProgressMonitor monitor, string file) + public async Task<SolutionItem> ReadSolutionItem (ProgressMonitor monitor, string file) { file = Path.GetFullPath (file); using (Counters.ReadSolutionItem.BeginTiming ("Read project " + file)) { file = GetTargetFile (file); - SolutionEntityItem loadedItem = GetExtensionChain (null).LoadSolutionItem (monitor, file, delegate { - FileFormat format; - SolutionEntityItem item = ReadFile (monitor, file, typeof(SolutionEntityItem), out format) as SolutionEntityItem; + SolutionItem loadedItem = await GetExtensionChain (null).LoadSolutionItem (monitor, file, async delegate { + var res = await ReadFile (monitor, file, typeof(SolutionItem)); + SolutionItem item = res.Item1 as SolutionItem; if (item != null) - item.FileFormat = format; + item.FileFormat = res.Item2; else throw new InvalidOperationException ("Invalid file format: " + file); return item; @@ -210,23 +202,23 @@ namespace MonoDevelop.Projects } } - public SolutionItem ReadSolutionItem (IProgressMonitor monitor, SolutionItemReference reference, params WorkspaceItem[] workspaces) + public async Task<SolutionFolderItem> ReadSolutionItem (ProgressMonitor monitor, SolutionItemReference reference, params WorkspaceItem[] workspaces) { if (reference.Id == null) { FilePath file = reference.Path.FullPath; foreach (WorkspaceItem workspace in workspaces) { - foreach (SolutionEntityItem eitem in workspace.GetAllSolutionItems<SolutionEntityItem> ()) + foreach (SolutionItem eitem in workspace.GetAllItems<Solution>().SelectMany (s => s.GetAllSolutionItems ())) if (file == eitem.FileName) return eitem; } - return ReadSolutionItem (monitor, reference.Path); + return await ReadSolutionItem (monitor, reference.Path); } else { Solution sol = null; if (workspaces.Length > 0) { FilePath file = reference.Path.FullPath; foreach (WorkspaceItem workspace in workspaces) { - foreach (Solution item in workspace.GetAllSolutions ()) { + foreach (Solution item in workspace.GetAllItems<Solution>()) { if (item.FileName.FullPath == file) { sol = item; break; @@ -237,7 +229,7 @@ namespace MonoDevelop.Projects } } if (sol == null) - sol = ReadWorkspaceItem (monitor, reference.Path) as Solution; + sol = await ReadWorkspaceItem (monitor, reference.Path) as Solution; if (reference.Id == ":root:") return sol.RootFolder; @@ -246,12 +238,12 @@ namespace MonoDevelop.Projects } } - public WorkspaceItem ReadWorkspaceItem (IProgressMonitor monitor, string file) + public async Task<WorkspaceItem> ReadWorkspaceItem (ProgressMonitor monitor, string file) { file = Path.GetFullPath (file); using (Counters.ReadWorkspaceItem.BeginTiming ("Read solution " + file)) { file = GetTargetFile (file); - WorkspaceItem item = GetExtensionChain (null).LoadWorkspaceItem (monitor, file) as WorkspaceItem; + WorkspaceItem item = await GetExtensionChain (null).LoadWorkspaceItem (monitor, file) as WorkspaceItem; if (item != null) item.NeedsReload = false; else @@ -260,54 +252,45 @@ namespace MonoDevelop.Projects } } - internal void InternalWriteSolutionItem (IProgressMonitor monitor, FilePath file, SolutionEntityItem item) - { - var newFile = WriteFile (monitor, file, item, null); - if (newFile != null) - item.FileName = newFile; - else - throw new InvalidOperationException ("FileFormat not provided for solution item '" + item.Name + "'"); - } - - internal WorkspaceItem InternalReadWorkspaceItem (string file, IProgressMonitor monitor) + internal async Task<WorkspaceItem> InternalReadWorkspaceItem (string file, ProgressMonitor monitor) { - FileFormat format; - WorkspaceItem item = ReadFile (monitor, file, typeof(WorkspaceItem), out format) as WorkspaceItem; + var res = await ReadFile (monitor, file, typeof(WorkspaceItem)); + WorkspaceItem item = res.Item1 as WorkspaceItem; if (item == null) throw new InvalidOperationException ("Invalid file format: " + file); if (!item.FormatSet) - item.ConvertToFormat (format, false); + await item.ConvertToFormat (res.Item2, false); return item; } - internal void InternalWriteWorkspaceItem (IProgressMonitor monitor, FilePath file, WorkspaceItem item) + internal async Task InternalWriteWorkspaceItem (ProgressMonitor monitor, FilePath file, WorkspaceItem item) { - var newFile = WriteFile (monitor, file, item, item.FileFormat); + var newFile = await WriteFile (monitor, file, item, item.FileFormat); if (newFile != null) item.FileName = newFile; else throw new InvalidOperationException ("FileFormat not provided for workspace item '" + item.Name + "'"); } - object ReadFile (IProgressMonitor monitor, string file, Type expectedType, out FileFormat format) + async Task<Tuple<object,FileFormat>> ReadFile (ProgressMonitor monitor, string file, Type expectedType) { FileFormat[] formats = formatManager.GetFileFormats (file, expectedType); if (formats.Length == 0) throw new InvalidOperationException ("Unknown file format: " + file); - format = formats [0]; - object obj = format.Format.ReadFile (file, expectedType, monitor); + var format = formats [0]; + object obj = await format.Format.ReadFile (file, expectedType, monitor); if (obj == null) throw new InvalidOperationException ("Invalid file format: " + file); - return obj; + return new Tuple<object,FileFormat> (obj, format); } - FilePath WriteFile (IProgressMonitor monitor, FilePath file, object item, FileFormat format) + async Task<FilePath> WriteFile (ProgressMonitor monitor, FilePath file, object item, FileFormat format) { if (format == null) { if (defaultFormat.CanWrite (item)) @@ -325,33 +308,33 @@ namespace MonoDevelop.Projects FileService.RequestFileEdit (file); - format.Format.WriteFile (file, item, monitor); + await format.Format.WriteFile (file, item, monitor); return file; } - public string Export (IProgressMonitor monitor, string rootSourceFile, string targetPath, FileFormat format) + public Task<string> Export (ProgressMonitor monitor, string rootSourceFile, string targetPath, FileFormat format) { rootSourceFile = GetTargetFile (rootSourceFile); return Export (monitor, rootSourceFile, null, targetPath, format); } - public string Export (IProgressMonitor monitor, string rootSourceFile, string[] includedChildIds, string targetPath, FileFormat format) + public async Task<string> Export (ProgressMonitor monitor, string rootSourceFile, string[] includedChildIds, string targetPath, FileFormat format) { IWorkspaceFileObject obj; if (IsWorkspaceItemFile (rootSourceFile)) { - obj = ReadWorkspaceItem (monitor, rootSourceFile) as Solution; + obj = await ReadWorkspaceItem (monitor, rootSourceFile) as Solution; } else { - obj = ReadSolutionItem (monitor, rootSourceFile); + obj = await ReadSolutionItem (monitor, rootSourceFile); if (obj == null) throw new InvalidOperationException ("File is not a solution or project."); } using (obj) { - return Export (monitor, obj, includedChildIds, targetPath, format); + return await Export (monitor, obj, includedChildIds, targetPath, format); } } - string Export (IProgressMonitor monitor, IWorkspaceFileObject obj, string[] includedChildIds, string targetPath, FileFormat format) + async Task<string> Export (ProgressMonitor monitor, IWorkspaceFileObject obj, string[] includedChildIds, string targetPath, FileFormat format) { string rootSourceFile = obj.FileName; string sourcePath = Path.GetFullPath (Path.GetDirectoryName (rootSourceFile)); @@ -363,17 +346,17 @@ namespace MonoDevelop.Projects string newFile = Path.Combine (targetPath, Path.GetFileName (rootSourceFile)); if (IsWorkspaceItemFile (rootSourceFile)) - obj = ReadWorkspaceItem (monitor, newFile); + obj = await ReadWorkspaceItem (monitor, newFile); else - obj = (SolutionEntityItem) ReadSolutionItem (monitor, newFile); + obj = (SolutionItem) await ReadSolutionItem (monitor, newFile); using (obj) { - List<FilePath> oldFiles = obj.GetItemFiles (true); + var oldFiles = obj.GetItemFiles (true).ToList (); ExcludeEntries (obj, includedChildIds); if (format != null) - obj.ConvertToFormat (format, true); - obj.Save (monitor);
- List<FilePath> newFiles = obj.GetItemFiles (true); + await obj.ConvertToFormat (format, true); + await obj.SaveAsync (monitor);
+ var newFiles = obj.GetItemFiles (true); foreach (FilePath f in newFiles) { if (!f.IsChildPathOf (targetPath)) { @@ -408,8 +391,8 @@ namespace MonoDevelop.Projects using (obj) { ExcludeEntries (obj, includedChildIds); if (format != null) - obj.ConvertToFormat (format, true); - obj.Save (monitor); + await obj.ConvertToFormat (format, true); + await obj.SaveAsync (monitor); return obj.FileName; } } @@ -425,14 +408,14 @@ namespace MonoDevelop.Projects foreach (string it in includedChildIds) childIds [it] = it; - foreach (SolutionItem item in sol.GetAllSolutionItems<SolutionItem> ()) { + foreach (SolutionFolderItem item in sol.GetAllItems<SolutionFolderItem> ()) { if (!childIds.ContainsKey (item.ItemId) && item.ParentFolder != null) item.ParentFolder.Items.Remove (item); } } }
- bool CopyFiles (IProgressMonitor monitor, IWorkspaceFileObject obj, IEnumerable<FilePath> files, FilePath targetBasePath, bool ignoreExternalFiles) + bool CopyFiles (ProgressMonitor monitor, IWorkspaceFileObject obj, IEnumerable<FilePath> files, FilePath targetBasePath, bool ignoreExternalFiles) {
FilePath baseDir = obj.BaseDirectory.FullPath;
foreach (FilePath file in files) { @@ -465,51 +448,28 @@ namespace MonoDevelop.Projects return true; } - public bool CanCreateSingleFileProject (string file) + public DotNetProject CreateDotNetProject (string language) { - foreach (ProjectBindingCodon projectBinding in projectBindings) { - if (projectBinding.ProjectBinding.CanCreateSingleFileProject (file)) - return true; - } - return false; + string typeGuid = MSBuildProjectService.GetLanguageGuid (language); + return (DotNetProject) MSBuildProjectService.CreateSolutionItem (typeGuid); } - - public Project CreateSingleFileProject (string file) - { - foreach (ProjectBindingCodon projectBinding in projectBindings) { - if (projectBinding.ProjectBinding.CanCreateSingleFileProject (file)) { - return projectBinding.ProjectBinding.CreateSingleFileProject (file); - } - } - return null; - } - - public Project CreateProject (string type, ProjectCreateInformation info, XmlElement projectOptions) + + public Project CreateProject (string typeAlias, ProjectCreateInformation info, XmlElement projectOptions) { - foreach (ProjectBindingCodon projectBinding in projectBindings) { - if (projectBinding.ProjectBinding.Name == type) { - Project project = projectBinding.ProjectBinding.CreateProject (info, projectOptions); - return project; - } - } - throw new InvalidOperationException ("Project type '" + type + "' not found"); + return MSBuildProjectService.CreateSolutionItem (typeAlias, info, projectOptions) as Project; } - public bool CanCreateProject (string type) + public bool CanCreateProject (string typeAlias, ProjectCreateInformation info, XmlElement projectOptions) { - foreach (ProjectBindingCodon projectBinding in projectBindings) { - if (projectBinding.ProjectBinding.Name == type) - return true; - } - return false; + return MSBuildProjectService.CanCreateSolutionItem (typeAlias, info, projectOptions); } //TODO: find solution that contains the project if possible - public Solution GetWrapperSolution (IProgressMonitor monitor, string filename) + public async Task<Solution> GetWrapperSolution (ProgressMonitor monitor, string filename) { // First of all, check if a solution with the same name already exists - FileFormat[] formats = Services.ProjectService.FileFormats.GetFileFormats (filename, typeof(SolutionEntityItem)); + FileFormat[] formats = Services.ProjectService.FileFormats.GetFileFormats (filename, typeof(SolutionItem)); if (formats.Length == 0) formats = new [] { DefaultFileFormat }; @@ -520,16 +480,16 @@ namespace MonoDevelop.Projects string solFileName = solutionFileFormat.GetValidFileName (tempSolution, filename); if (File.Exists (solFileName)) { - return (Solution) Services.ProjectService.ReadWorkspaceItem (monitor, solFileName); + return (Solution) await Services.ProjectService.ReadWorkspaceItem (monitor, solFileName); } else { // Create a temporary solution and add the project to the solution tempSolution.SetLocation (Path.GetDirectoryName (filename), Path.GetFileNameWithoutExtension (filename)); - SolutionEntityItem sitem = Services.ProjectService.ReadSolutionItem (monitor, filename); - tempSolution.ConvertToFormat (solutionFileFormat, false); + SolutionItem sitem = await Services.ProjectService.ReadSolutionItem (monitor, filename); + await tempSolution.ConvertToFormat (solutionFileFormat, false); tempSolution.RootFolder.Items.Add (sitem); tempSolution.CreateDefaultConfigurations (); - tempSolution.Save (monitor); + await tempSolution.SaveAsync (monitor); return tempSolution; } } @@ -552,7 +512,7 @@ namespace MonoDevelop.Projects internal bool IsSolutionItemFileInternal (string filename) { - return formatManager.GetFileFormats (filename, typeof(SolutionItem)).Length > 0; + return formatManager.GetFileFormats (filename, typeof(SolutionFolderItem)).Length > 0; } internal bool IsWorkspaceItemFileInternal (string filename) @@ -634,34 +594,6 @@ namespace MonoDevelop.Projects internal class DefaultProjectServiceExtension: ProjectServiceExtension { - Dictionary <SolutionItem,bool> needsBuildingCache; - - public override object GetService (SolutionItem item, Type type) - { - return item.OnGetService (type); - } - - public override object GetService (WorkspaceItem item, Type type) - { - return item.OnGetService (type); - } - - public override void Save (IProgressMonitor monitor, SolutionEntityItem entry) - { - FileService.RequestFileEdit (entry.GetItemFiles (false)); - entry.OnSave (monitor); - } - - public override void Save (IProgressMonitor monitor, WorkspaceItem entry) - { - entry.OnSave (monitor); - }
-
- public override List<FilePath> GetItemFiles (SolutionEntityItem entry, bool includeReferencedFiles) - { - return entry.OnGetItemFiles (includeReferencedFiles); - } - public override bool IsSolutionItemFile (string filename) { return Services.ProjectService.IsSolutionItemFileInternal (filename); @@ -672,152 +604,15 @@ namespace MonoDevelop.Projects return Services.ProjectService.IsWorkspaceItemFileInternal (filename); } - internal override SolutionEntityItem LoadSolutionItem (IProgressMonitor monitor, string fileName, ItemLoadCallback callback) + internal override Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, string fileName, ItemLoadCallback callback) { return callback (monitor, fileName); } - public override WorkspaceItem LoadWorkspaceItem (IProgressMonitor monitor, string fileName) + public override Task<WorkspaceItem> LoadWorkspaceItem (ProgressMonitor monitor, string fileName) { return Services.ProjectService.InternalReadWorkspaceItem (fileName, monitor); } - - public override BuildResult RunTarget (IProgressMonitor monitor, IBuildTarget item, string target, ConfigurationSelector configuration) - { - BuildResult res; - if (item is WorkspaceItem) { - res = ((WorkspaceItem)item).OnRunTarget (monitor, target, configuration); - } - else if (item is SolutionItem) - res = ((SolutionItem)item).OnRunTarget (monitor, target, configuration); - else - throw new InvalidOperationException ("Unknown item type: " + item); - - if (res != null) - res.SourceTarget = item; - return res; - } - - public override bool SupportsTarget (IBuildTarget item, string target) - { - if (item is WorkspaceItem) - return ((WorkspaceItem)item).OnGetSupportsTarget (target); - else if (item is SolutionItem) - return ((SolutionItem)item).OnGetSupportsTarget (target); - else - throw new InvalidOperationException ("Unknown item type: " + item); - } - - public override bool SupportsExecute (IBuildTarget item) - { - if (item is WorkspaceItem) - return ((WorkspaceItem)item).OnGetSupportsExecute (); - else if (item is SolutionItem) - return ((SolutionItem)item).OnGetSupportsExecute (); - else - throw new InvalidOperationException ("Unknown item type: " + item); - } - - public override void Execute (IProgressMonitor monitor, IBuildTarget item, ExecutionContext context, ConfigurationSelector configuration) - { - if (item is SolutionEntityItem) { - SolutionEntityItem entry = (SolutionEntityItem) item; - SolutionItemConfiguration conf = entry.GetConfiguration (configuration) as SolutionItemConfiguration; - if (conf != null && conf.CustomCommands.HasCommands (CustomCommandType.Execute)) { - conf.CustomCommands.ExecuteCommand (monitor, entry, CustomCommandType.Execute, context, configuration); - return; - } - entry.OnExecute (monitor, context, configuration); - } - else if (item is WorkspaceItem) { - ((WorkspaceItem)item).OnExecute (monitor, context, configuration); - } - else if (item is SolutionItem) - ((SolutionItem)item).OnExecute (monitor, context, configuration); - else - throw new InvalidOperationException ("Unknown item type: " + item); - } - - public override bool CanExecute (IBuildTarget item, ExecutionContext context, ConfigurationSelector configuration) - { - if (item is SolutionEntityItem) { - SolutionEntityItem entry = (SolutionEntityItem) item; - SolutionItemConfiguration conf = entry.GetConfiguration (configuration) as SolutionItemConfiguration; - if (conf != null && conf.CustomCommands.HasCommands (CustomCommandType.Execute)) - return conf.CustomCommands.CanExecute (entry, CustomCommandType.Execute, context, configuration); - return entry.OnGetCanExecute (context, configuration); - } - else if (item is WorkspaceItem) { - return ((WorkspaceItem)item).OnGetCanExecute (context, configuration); - } - else if (item is SolutionItem) - return ((SolutionItem)item).OnGetCanExecute (context, configuration); - else - throw new InvalidOperationException ("Unknown item type: " + item); - } - - public override IEnumerable<ExecutionTarget> GetExecutionTargets (IBuildTarget item, ConfigurationSelector configuration) - { - if (item is WorkspaceItem) { - return ((WorkspaceItem)item).OnGetExecutionTargets (configuration); - } - else if (item is SolutionItem) - return ((SolutionItem)item).OnGetExecutionTargets (configuration); - else - throw new InvalidOperationException ("Unknown item type: " + item); - } - - public override bool GetNeedsBuilding (IBuildTarget item, ConfigurationSelector configuration) - { - if (item is SolutionItem) { - SolutionItem entry = (SolutionItem) item; - // This is a cache to avoid unneeded recursive calls to GetNeedsBuilding. - bool cleanCache = false; - if (needsBuildingCache == null) { - needsBuildingCache = new Dictionary <SolutionItem,bool> (); - cleanCache = true; - } else { - bool res; - if (needsBuildingCache.TryGetValue (entry, out res)) - return res; - } - - bool nb = entry.OnGetNeedsBuilding (configuration); - - needsBuildingCache [entry] = nb; - if (cleanCache) - needsBuildingCache = null; - return nb; - } - else if (item is WorkspaceItem) { - return ((WorkspaceItem)item).OnGetNeedsBuilding (configuration); - } - else - throw new InvalidOperationException ("Unknown item type: " + item); - } - - public override void SetNeedsBuilding (IBuildTarget item, bool val, ConfigurationSelector configuration) - { - if (item is SolutionItem) { - SolutionItem entry = (SolutionItem) item; - entry.OnSetNeedsBuilding (val, configuration); - } - else if (item is WorkspaceItem) { - ((WorkspaceItem)item).OnSetNeedsBuilding (val, configuration); - } - else - throw new InvalidOperationException ("Unknown item type: " + item); - } - - internal override BuildResult Compile(IProgressMonitor monitor, SolutionEntityItem item, BuildData buildData, ItemCompileCallback callback) - { - return callback (monitor, item, buildData); - } - - public override IEnumerable<string> GetReferencedAssemblies (DotNetProject project, ConfigurationSelector configuration, bool includeProjectReferences) - { - return project.OnGetReferencedAssemblies (configuration, includeProjectReferences); - } } internal static class Counters @@ -826,7 +621,7 @@ namespace MonoDevelop.Projects public static Counter ItemsLoaded = InstrumentationService.CreateCounter ("Projects loaded", "Project Model"); public static Counter SolutionsInMemory = InstrumentationService.CreateCounter ("Solutions in memory", "Project Model"); public static Counter SolutionsLoaded = InstrumentationService.CreateCounter ("Solutions loaded", "Project Model"); - public static TimerCounter ReadWorkspaceItem = InstrumentationService.CreateTimerCounter ("Workspace item read", "Project Model"); + public static TimerCounter ReadWorkspaceItem = InstrumentationService.CreateTimerCounter ("Workspace item read", "Project Model", id:"Core.ReadWorkspaceItem"); public static TimerCounter ReadSolutionItem = InstrumentationService.CreateTimerCounter ("Solution item read", "Project Model"); public static TimerCounter ReadMSBuildProject = InstrumentationService.CreateTimerCounter ("MSBuild project read", "Project Model"); public static TimerCounter WriteMSBuildProject = InstrumentationService.CreateTimerCounter ("MSBuild project written", "Project Model"); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectServiceExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectServiceExtension.cs index 476081baf6..08d368c82e 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectServiceExtension.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectServiceExtension.cs @@ -31,6 +31,7 @@ using System.Collections.Generic; using MonoDevelop.Core; using MonoDevelop.Projects.Extensions; using MonoDevelop.Core.Execution; +using System.Threading.Tasks; namespace MonoDevelop.Projects { @@ -39,9 +40,8 @@ namespace MonoDevelop.Projects internal ProjectServiceExtension Next; Stack<ItemLoadCallback> loadCallbackStack = new Stack<ItemLoadCallback> (); - Stack<ItemCompileCallback> compileCallbackStack = new Stack<ItemCompileCallback> (); - - internal ProjectServiceExtension GetNext (IBuildTarget item) + + internal ProjectServiceExtension GetNext (WorkspaceObject item) { if (Next.SupportsItem (item)) return Next; @@ -49,42 +49,11 @@ namespace MonoDevelop.Projects return Next.GetNext (item); } - public virtual bool SupportsItem (IBuildTarget item) + public virtual bool SupportsItem (WorkspaceObject item) { return true; } - public virtual object GetService (SolutionItem item, Type type) - { - if (type.IsInstanceOfType (this)) - return this; - else - return GetNext (item).GetService (item, type); - } - - public virtual object GetService (WorkspaceItem item, Type type) - { - if (type.IsInstanceOfType (this)) - return this; - else - return GetNext (item).GetService (item, type); - } - - public virtual void Save (IProgressMonitor monitor, SolutionEntityItem item) - { - GetNext (item).Save (monitor, item); - } - - public virtual void Save (IProgressMonitor monitor, WorkspaceItem item) - { - GetNext (item).Save (monitor, item); - }
-
- public virtual List<FilePath> GetItemFiles (SolutionEntityItem item, bool includeReferencedFiles) - { - return GetNext (item).GetItemFiles (item, includeReferencedFiles); - } - public virtual bool IsSolutionItemFile (string fileName) { return GetNext (UnknownItem.Instance).IsSolutionItemFile (fileName); @@ -95,307 +64,25 @@ namespace MonoDevelop.Projects return GetNext (UnknownItem.Instance).IsWorkspaceItemFile (fileName); } - internal virtual SolutionEntityItem LoadSolutionItem (IProgressMonitor monitor, string fileName, ItemLoadCallback callback) + internal async virtual Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, string fileName, ItemLoadCallback callback) { loadCallbackStack.Push (callback); try { - SolutionEntityItem res = LoadSolutionItem (monitor, fileName); - return res; + return await LoadSolutionItem (monitor, fileName); } finally { loadCallbackStack.Pop (); } } - protected virtual SolutionEntityItem LoadSolutionItem (IProgressMonitor monitor, string fileName) + protected virtual Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, string fileName) { return GetNext (UnknownItem.Instance).LoadSolutionItem (monitor, fileName, loadCallbackStack.Peek ()); } - public virtual WorkspaceItem LoadWorkspaceItem (IProgressMonitor monitor, string fileName) + public virtual Task<WorkspaceItem> LoadWorkspaceItem (ProgressMonitor monitor, string fileName) { return GetNext (UnknownItem.Instance).LoadWorkspaceItem (monitor, fileName); } - - public virtual BuildResult RunTarget (IProgressMonitor monitor, IBuildTarget item, string target, ConfigurationSelector configuration) - { - if (target == ProjectService.BuildTarget) - return Build (monitor, item, configuration); - else if (target == ProjectService.CleanTarget) { - Clean (monitor, item, configuration); - return null; - } - else - return GetNext (item).RunTarget (monitor, item, target, configuration); - } - - public virtual bool SupportsTarget (IBuildTarget item, string target) - { - if (item is SolutionEntityItem) - return SupportsTarget ((SolutionEntityItem)item, target); - else if (item is WorkspaceItem) - return SupportsTarget ((WorkspaceItem) item, target); - else - return GetNext (item).SupportsTarget (item, target); - } - - protected virtual bool SupportsTarget (SolutionEntityItem item, string target) - { - return GetNext (item).SupportsTarget ((IBuildTarget) item, target); - } - - protected virtual bool SupportsTarget (Solution solution, string target) - { - return GetNext (solution).SupportsTarget ((IBuildTarget) solution, target); - } - - protected virtual bool SupportsTarget (WorkspaceItem item, string target) - { - if (item is Solution) - return SupportsTarget ((Solution) item, target); - else - return GetNext (item).SupportsTarget ((IBuildTarget) item, target); - } - - public virtual bool SupportsExecute (IBuildTarget item) - { - if (item is SolutionEntityItem) - return SupportsExecute ((SolutionEntityItem)item); - else if (item is WorkspaceItem) - return SupportsExecute ((WorkspaceItem) item); - else - return GetNext (item).SupportsExecute (item); - } - - protected virtual bool SupportsExecute (SolutionEntityItem item) - { - return GetNext (item).SupportsExecute ((IBuildTarget) item); - } - - protected virtual bool SupportsExecute (Solution solution) - { - return GetNext (solution).SupportsExecute ((IBuildTarget) solution); - } - - protected virtual bool SupportsExecute (WorkspaceItem item) - { - if (item is Solution) - return SupportsExecute ((Solution) item); - else - return GetNext (item).SupportsExecute ((IBuildTarget) item); - } - - protected virtual void Clean (IProgressMonitor monitor, IBuildTarget item, ConfigurationSelector configuration) - { - if (item is SolutionEntityItem) - Clean (monitor, (SolutionEntityItem) item, configuration); - else if (item is WorkspaceItem) - Clean (monitor, (WorkspaceItem) item, configuration); - else - GetNext (item).RunTarget (monitor, item, ProjectService.CleanTarget, configuration); - } - - protected virtual void Clean (IProgressMonitor monitor, SolutionEntityItem item, ConfigurationSelector configuration) - { - GetNext (item).RunTarget (monitor, item, ProjectService.CleanTarget, configuration); - } - - protected virtual void Clean (IProgressMonitor monitor, Solution item, ConfigurationSelector configuration) - { - GetNext (item).RunTarget (monitor, item, ProjectService.CleanTarget, configuration); - } - - protected virtual void Clean (IProgressMonitor monitor, WorkspaceItem item, ConfigurationSelector configuration) - { - if (item is Solution) - Clean (monitor, (Solution) item, configuration); - else - GetNext (item).RunTarget (monitor, item, ProjectService.CleanTarget, configuration); - } - - protected virtual BuildResult Build (IProgressMonitor monitor, IBuildTarget item, ConfigurationSelector configuration) - { - if (item is SolutionEntityItem) - return Build (monitor, (SolutionEntityItem) item, configuration); - if (item is WorkspaceItem) - return Build (monitor, (WorkspaceItem) item, configuration); - return GetNext (item).RunTarget (monitor, item, ProjectService.BuildTarget, configuration); - } - - protected virtual BuildResult Build (IProgressMonitor monitor, SolutionEntityItem item, ConfigurationSelector configuration) - { - return GetNext (item).RunTarget (monitor, item, ProjectService.BuildTarget, configuration); - } - - protected virtual BuildResult Build (IProgressMonitor monitor, WorkspaceItem item, ConfigurationSelector configuration) - { - if (item is Solution) - return Build (monitor, (Solution) item, configuration); - return GetNext (item).RunTarget (monitor, item, ProjectService.BuildTarget, configuration); - } - - protected virtual BuildResult Build (IProgressMonitor monitor, Solution solution, ConfigurationSelector configuration) - { - return GetNext (solution).RunTarget (monitor, solution, ProjectService.BuildTarget, configuration); - } - - public virtual void Execute (IProgressMonitor monitor, IBuildTarget item, ExecutionContext context, ConfigurationSelector configuration) - { - if (item is SolutionEntityItem) - Execute (monitor, (SolutionEntityItem)item, context, configuration); - else if (item is WorkspaceItem) - Execute (monitor, (WorkspaceItem) item, context, configuration); - else - GetNext (item).Execute (monitor, item, context, configuration); - } - - protected virtual void Execute (IProgressMonitor monitor, SolutionEntityItem item, ExecutionContext context, ConfigurationSelector configuration) - { - GetNext (item).Execute (monitor, (IBuildTarget) item, context, configuration); - } - - protected virtual void Execute (IProgressMonitor monitor, Solution solution, ExecutionContext context, ConfigurationSelector configuration) - { - GetNext (solution).Execute (monitor, (IBuildTarget) solution, context, configuration); - } - - protected virtual void Execute (IProgressMonitor monitor, WorkspaceItem item, ExecutionContext context, ConfigurationSelector configuration) - { - if (item is Solution) - Execute (monitor, (Solution) item, context, configuration); - else - GetNext (item).Execute (monitor, (IBuildTarget) item, context, configuration); - } - - public virtual bool CanExecute (IBuildTarget item, ExecutionContext context, ConfigurationSelector configuration) - { - if (item is SolutionEntityItem) - return CanExecute ((SolutionEntityItem)item, context, configuration); - else if (item is WorkspaceItem) - return CanExecute ((WorkspaceItem) item, context, configuration); - else - return GetNext (item).CanExecute (item, context, configuration); - } - - protected virtual bool CanExecute (SolutionEntityItem item, ExecutionContext context, ConfigurationSelector configuration) - { - return GetNext (item).CanExecute ((IBuildTarget) item, context, configuration); - } - - protected virtual bool CanExecute (Solution solution, ExecutionContext context, ConfigurationSelector configuration) - { - return GetNext (solution).CanExecute ((IBuildTarget) solution, context, configuration); - } - - protected virtual bool CanExecute (WorkspaceItem item, ExecutionContext context, ConfigurationSelector configuration) - { - if (item is Solution) - return CanExecute ((Solution) item, context, configuration); - else - return GetNext (item).CanExecute ((IBuildTarget) item, context, configuration); - } - - public virtual IEnumerable<ExecutionTarget> GetExecutionTargets (IBuildTarget item, ConfigurationSelector configuration) - { - if (item is SolutionEntityItem) - return GetExecutionTargets ((SolutionEntityItem)item, configuration); - else if (item is WorkspaceItem) - return GetExecutionTargets ((WorkspaceItem) item, configuration); - else - return GetNext (item).GetExecutionTargets (item, configuration); - } - - protected virtual IEnumerable<ExecutionTarget> GetExecutionTargets (SolutionEntityItem item, ConfigurationSelector configuration) - { - return GetNext (item).GetExecutionTargets ((IBuildTarget) item, configuration); - } - - protected virtual IEnumerable<ExecutionTarget> GetExecutionTargets (Solution solution, ConfigurationSelector configuration) - { - return GetNext (solution).GetExecutionTargets ((IBuildTarget) solution, configuration); - } - - protected virtual IEnumerable<ExecutionTarget> GetExecutionTargets (WorkspaceItem item, ConfigurationSelector configuration) - { - if (item is Solution) - return GetExecutionTargets ((Solution) item, configuration); - else - return GetNext (item).GetExecutionTargets ((IBuildTarget) item, configuration); - } - - public virtual bool GetNeedsBuilding (IBuildTarget item, ConfigurationSelector configuration) - { - if (item is SolutionEntityItem) - return GetNeedsBuilding ((SolutionEntityItem) item, configuration); - if (item is WorkspaceItem) - return GetNeedsBuilding ((WorkspaceItem) item, configuration); - return GetNext (item).GetNeedsBuilding (item, configuration); - } - - protected virtual bool GetNeedsBuilding (SolutionEntityItem item, ConfigurationSelector configuration) - { - return GetNext (item).GetNeedsBuilding ((IBuildTarget) item, configuration); - } - - protected virtual bool GetNeedsBuilding (Solution item, ConfigurationSelector configuration) - { - return GetNext (item).GetNeedsBuilding ((IBuildTarget) item, configuration); - } - - protected virtual bool GetNeedsBuilding (WorkspaceItem item, ConfigurationSelector configuration) - { - if (item is Solution) - return GetNeedsBuilding ((Solution) item, configuration); - return GetNext (item).GetNeedsBuilding ((IBuildTarget) item, configuration); - } - - public virtual void SetNeedsBuilding (IBuildTarget item, bool val, ConfigurationSelector configuration) - { - if (item is SolutionEntityItem) - SetNeedsBuilding ((SolutionEntityItem) item, val, configuration); - else if (item is WorkspaceItem) - SetNeedsBuilding ((WorkspaceItem) item, val, configuration); - else - GetNext (item).SetNeedsBuilding (item, val, configuration); - } - - protected virtual void SetNeedsBuilding (SolutionEntityItem item, bool val, ConfigurationSelector configuration) - { - GetNext (item).SetNeedsBuilding ((IBuildTarget) item, val, configuration); - } - - protected virtual void SetNeedsBuilding (Solution item, bool val, ConfigurationSelector configuration) - { - GetNext (item).SetNeedsBuilding ((IBuildTarget) item, val, configuration); - } - - protected virtual void SetNeedsBuilding (WorkspaceItem item, bool val, ConfigurationSelector configuration) - { - if (item is Solution) - SetNeedsBuilding ((Solution) item, val, configuration); - else - GetNext (item).SetNeedsBuilding ((IBuildTarget) item, val, configuration); - } - - internal virtual BuildResult Compile (IProgressMonitor monitor, SolutionEntityItem item, BuildData buildData, ItemCompileCallback callback) - { - compileCallbackStack.Push (callback); - try { - BuildResult res = Compile (monitor, item, buildData); - return res; - } finally { - compileCallbackStack.Pop (); - } - } - - protected virtual BuildResult Compile (IProgressMonitor monitor, SolutionEntityItem item, BuildData buildData) - { - return GetNext (item).Compile (monitor, item, buildData, compileCallbackStack.Peek ()); - } - - public virtual IEnumerable<string> GetReferencedAssemblies (DotNetProject project, ConfigurationSelector configuration, bool includeProjectReferences) - { - return GetNext (project).GetReferencedAssemblies (project, configuration, includeProjectReferences); - } } public class BuildData @@ -405,16 +92,21 @@ namespace MonoDevelop.Projects public ConfigurationSelector ConfigurationSelector { get; internal set; } } - class UnknownItem: IBuildTarget + class UnknownItem: WorkspaceObject, IBuildTarget { public static UnknownItem Instance = new UnknownItem (); - - public BuildResult RunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) + + public Task<BuildResult> Build (ProgressMonitor monitor, ConfigurationSelector configuration, bool buildReferencedTargets = false) { - return new BuildResult (); + return Task.FromResult (BuildResult.Success); } - public void Execute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + public Task<BuildResult> Clean (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return Task.FromResult (BuildResult.Success); + } + + public Task Execute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) { throw new System.NotImplementedException(); } @@ -424,45 +116,24 @@ namespace MonoDevelop.Projects return false; } - public bool SupportsTarget (string target) - { - return false; - } - public bool NeedsBuilding (ConfigurationSelector configuration) { return false; } - - public void SetNeedsBuilding (bool needsBuilding, ConfigurationSelector configuration) + + protected override string OnGetName () { + return "Unknown"; } - - public void Save (IProgressMonitor monitor) + + protected override string OnGetBaseDirectory () { + return FilePath.Empty; } - - public string Name { - get { return "Unknown"; } - set { } - } - - - public FilePath ItemDirectory { - get { return FilePath.Empty; } - } - - public FilePath BaseDirectory { - get { return FilePath.Empty; } - set { } - } - - public void Dispose () + + protected override string OnGetItemDirectory () { - } - - public System.Collections.IDictionary ExtendedProperties { - get { return null; } + return FilePath.Empty; } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProjectBinding.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterDotNetProject.cs index ca7f7527a1..1d237ac31f 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.SharedAssetsProjects/SharedAssetsProjectBinding.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterDotNetProject.cs @@ -1,5 +1,5 @@ // -// SharedAssetsProjectBinding.cs +// RegisterDotNetProject.cs // // Author: // Lluis Sanchez Gual <lluis@xamarin.com> @@ -24,35 +24,22 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; +using Mono.Addins; -namespace MonoDevelop.Projects.SharedAssetsProjects +namespace MonoDevelop.Projects { - public class SharedAssetsProjectBinding: IProjectBinding + public class RegisterDotNetProjectTypeAttribute: RegisterProjectTypeAttribute { - #region IProjectBinding implementation - - public Project CreateProject (ProjectCreateInformation info, System.Xml.XmlElement projectOptions) - { - return new SharedAssetsProject (info, projectOptions); - } - - public Project CreateSingleFileProject (string sourceFile) + protected RegisterDotNetProjectTypeAttribute () { - throw new NotImplementedException (); } - public bool CanCreateSingleFileProject (string sourceFile) + public RegisterDotNetProjectTypeAttribute ([NodeAttribute ("guid")] string guid, [NodeAttribute("language")] string language): base (guid) { - return false; - } - - public string Name { - get { - return "SharedAssetsProject"; - } } - #endregion + [NodeAttribute ("language", Required=true)] + public string Language { get; set; } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterProjectAttribute.cs index adb9f0a0c4..e582055a24 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MSBuild/MSBuildExtension.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterProjectAttribute.cs @@ -1,5 +1,5 @@ // -// MSBuildExtension.cs +// MSBuildProjectTypeAttribute.cs // // Author: // Lluis Sanchez Gual <lluis@xamarin.com> @@ -25,24 +25,20 @@ // THE SOFTWARE. using System; using MonoDevelop.Core; +using System.Threading.Tasks; +using MonoDevelop.Projects.Formats.MSBuild; +using Mono.Addins; -namespace MonoDevelop.Projects.Formats.MSBuild +namespace MonoDevelop.Projects { - public class MSBuildExtension + public class RegisterProjectTypeAttribute: RegisterSolutionItemTypeAttribute { - public MSBuildProjectHandler Handler { get; set; } - - public virtual void LoadProject (IProgressMonitor monitor, SolutionEntityItem item, MSBuildProject project) - { - } - - public virtual void SaveProject (IProgressMonitor monitor, SolutionEntityItem item, MSBuildProject project) + protected RegisterProjectTypeAttribute () { } - public virtual object GetService (Type t) + public RegisterProjectTypeAttribute ([NodeAttribute ("guid")] string guid): base (guid) { - return null; } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterProjectFlavorAttribute.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterProjectFlavorAttribute.cs new file mode 100644 index 0000000000..023723f1db --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterProjectFlavorAttribute.cs @@ -0,0 +1,48 @@ +// +// ProjectFlavorTypeAttribute.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Linq; +using Mono.Addins; + +namespace MonoDevelop.Projects +{ + public class RegisterProjectFlavorAttribute: RegisterProjectModelExtensionAttribute + { + internal RegisterProjectFlavorAttribute () + { + } + + public RegisterProjectFlavorAttribute ([NodeAttribute ("guid")] string guid) + { + Guid = guid; + } + + [NodeAttribute ("guid", Description = "GUID of the extension. The extension will be loaded if the project has this GUID in the project type GUID list. " + + "If not specified, the extension will be applied to all projects.")] + public string Guid { get; set; } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterProjectModelExtensionAttribute.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterProjectModelExtensionAttribute.cs new file mode 100644 index 0000000000..9c47679650 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterProjectModelExtensionAttribute.cs @@ -0,0 +1,36 @@ +// +// RegisterProjectModelExtensionAttribute.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 Mono.Addins; + +namespace MonoDevelop.Projects +{ + public class RegisterProjectModelExtensionAttribute: CustomExtensionAttribute + { + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterSolutionItemTypeAttribute.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterSolutionItemTypeAttribute.cs new file mode 100644 index 0000000000..ccdc33ab8b --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/RegisterSolutionItemTypeAttribute.cs @@ -0,0 +1,62 @@ +// +// SolutionItemTypeAttribute.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Threading.Tasks; +using MonoDevelop.Core; +using Mono.Addins; +using MonoDevelop.Core.ProgressMonitoring; + +namespace MonoDevelop.Projects +{ + public class RegisterSolutionItemTypeAttribute: CustomExtensionAttribute + { + [NodeAttribute ("guid", Required=true)] + public string Guid { get; set; } + + [NodeAttribute ("extension")] + public string Extension { get; set; } + + [NodeAttribute ("import")] + public string Import { get; set; } + + [NodeAttribute ("alias")] + public string Alias { get; set; } + + public string TypeName { + get { return ((TypeExtensionNode)ExtensionNode).TypeName; } + } + + protected RegisterSolutionItemTypeAttribute () + { + } + + public RegisterSolutionItemTypeAttribute ([NodeAttribute ("guid")] string guid) + { + Guid = guid; + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Solution.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Solution.cs index f0b7f171f2..97534bbc16 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Solution.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Solution.cs @@ -36,18 +36,21 @@ using MonoDevelop.Core; using MonoDevelop.Core.ProgressMonitoring; using MonoDevelop.Core.StringParsing; using MonoDevelop.Projects.Policies; +using MonoDevelop.Core.Execution; +using System.Threading.Tasks; +using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects { [ProjectModelDataItem] - public class Solution: WorkspaceItem, IConfigurationTarget, IPolicyProvider + public class Solution: WorkspaceItem, IConfigurationTarget, IPolicyProvider, IBuildTarget { internal object MemoryProbe = Counters.SolutionsInMemory.CreateMemoryProbe (); SolutionFolder rootFolder; string defaultConfiguration; - SolutionEntityItem startupItem; - List<SolutionEntityItem> startupItems; + SolutionItem startupItem; + List<SolutionItem> startupItems; bool singleStartup = true; // Used for serialization only @@ -70,8 +73,24 @@ namespace MonoDevelop.Projects { Counters.SolutionsLoaded++; configurations = new SolutionConfigurationCollection (this); + Initialize (this); } - + + internal HashSet<string> LoadedProjects { + get; + set; + } + + SolutionExtension itemExtension; + + SolutionExtension SolutionExtension { + get { + if (itemExtension == null) + itemExtension = ExtensionChain.GetExtension<SolutionExtension> (); + return itemExtension; + } + } + public SolutionFolder RootFolder { get { if (rootFolder == null) { @@ -103,18 +122,13 @@ namespace MonoDevelop.Projects // Does not include solution folders public ReadOnlyCollection<SolutionItem> Items { get { - if (solutionItems == null) { - List<SolutionItem> list = new List<SolutionItem> (); - foreach (SolutionItem item in GetAllSolutionItems ()) - if (!(item is SolutionFolder)) - list.Add (item); - solutionItems = list.AsReadOnly (); - } + if (solutionItems == null) + solutionItems = GetAllSolutionItems ().ToList().AsReadOnly (); return solutionItems; } } - public SolutionEntityItem StartupItem { + public SolutionItem StartupItem { get { if (startItemFileName != null) { startupItem = FindSolutionItem (startItemFileName); @@ -122,8 +136,8 @@ namespace MonoDevelop.Projects singleStartup = true; } if (startupItem == null && singleStartup) { - ReadOnlyCollection<SolutionEntityItem> its = GetAllSolutionItems<SolutionEntityItem> (); - if (its.Count > 0) + var its = GetAllItems<SolutionItem> (); + if (its.Any ()) startupItem = its.FirstOrDefault (it => it.SupportsExecute ()); } return startupItem; @@ -161,12 +175,12 @@ namespace MonoDevelop.Projects } } - public List<SolutionEntityItem> MultiStartupItems { + public List<SolutionItem> MultiStartupItems { get { if (multiStartupItems != null) { - startupItems = new List<SolutionEntityItem> (); + startupItems = new List<SolutionItem> (); foreach (string file in multiStartupItems) { - SolutionEntityItem it = FindSolutionItem (file); + SolutionItem it = FindSolutionItem (file); if (it != null) startupItems.Add (it); } @@ -174,7 +188,7 @@ namespace MonoDevelop.Projects singleStartup = false; } else if (startupItems == null) - startupItems = new List<SolutionEntityItem> (); + startupItems = new List<SolutionItem> (); return startupItems; } } @@ -200,7 +214,7 @@ namespace MonoDevelop.Projects if (multiStartupItems != null) return multiStartupItems; List<string> files = new List<string> (); - foreach (SolutionEntityItem item in MultiStartupItems) + foreach (SolutionItem item in MultiStartupItems) files.Add (item.FileName); return files; } @@ -248,30 +262,30 @@ namespace MonoDevelop.Projects CleanItemProperties (UserProperties, RootFolder, "MonoDevelop.Ide.ItemProperties"); } - void CollectItemProperties (PropertyBag props, SolutionItem item, string path) + void CollectItemProperties (PropertyBag props, SolutionFolderItem item, string path) { if (!item.UserProperties.IsEmpty && item.ParentFolder != null) props.SetValue (path, item.UserProperties); SolutionFolder sf = item as SolutionFolder; if (sf != null) { - foreach (SolutionItem ci in sf.Items) + foreach (SolutionFolderItem ci in sf.Items) CollectItemProperties (props, ci, path + "." + ci.Name); } } - void CleanItemProperties (PropertyBag props, SolutionItem item, string path) + void CleanItemProperties (PropertyBag props, SolutionFolderItem item, string path) { props.RemoveValue (path); SolutionFolder sf = item as SolutionFolder; if (sf != null) { - foreach (SolutionItem ci in sf.Items) + foreach (SolutionFolderItem ci in sf.Items) CleanItemProperties (props, ci, path + "." + ci.Name); } } - void LoadItemProperties (PropertyBag props, SolutionItem item, string path) + void LoadItemProperties (PropertyBag props, SolutionFolderItem item, string path) { PropertyBag info = props.GetValue<PropertyBag> (path); if (info != null) { @@ -281,14 +295,14 @@ namespace MonoDevelop.Projects SolutionFolder sf = item as SolutionFolder; if (sf != null) { - foreach (SolutionItem ci in sf.Items) + foreach (SolutionFolderItem ci in sf.Items) LoadItemProperties (props, ci, path + "." + ci.Name); } } public void CreateDefaultConfigurations () { - foreach (SolutionEntityItem item in Items.Where (it => it.SupportsBuild ())) { + foreach (SolutionItem item in Items.Where (it => it.SupportsBuild ())) { foreach (ItemConfiguration conf in item.Configurations) { SolutionConfiguration sc = Configurations [conf.Id]; if (sc == null) { @@ -308,7 +322,7 @@ namespace MonoDevelop.Projects public SolutionConfiguration AddConfiguration (string name, bool createConfigForItems) { SolutionConfiguration conf = new SolutionConfiguration (name); - foreach (SolutionEntityItem item in Items.Where (it => it.SupportsBuild())) { + foreach (SolutionItem item in Items.Where (it => it.SupportsBuild())) { if (createConfigForItems && item.GetConfiguration (new ItemConfigurationSelector (name)) == null) { SolutionItemConfiguration newc = item.CreateConfiguration (name); if (item.DefaultConfiguration != null) @@ -334,15 +348,15 @@ namespace MonoDevelop.Projects return (SolutionConfiguration) configuration.GetConfiguration (this) ?? DefaultConfiguration; } - public SolutionItem GetSolutionItem (string itemId) + public SolutionFolderItem GetSolutionItem (string itemId) { - foreach (SolutionItem item in Items) + foreach (SolutionFolderItem item in Items) if (item.ItemId == itemId) return item; return null; } - public override SolutionEntityItem FindSolutionItem (string fileName) + public SolutionItem FindSolutionItem (string fileName) { return RootFolder.FindSolutionItem (fileName); } @@ -352,11 +366,16 @@ namespace MonoDevelop.Projects return RootFolder.FindProjectByName (name); } - public override ReadOnlyCollection<T> GetAllSolutionItems<T> () + public IEnumerable<SolutionItem> GetAllSolutionItems () { - return RootFolder.GetAllItems<T> (); + return GetAllItems<SolutionItem> (); } - + + public IEnumerable<Project> GetAllProjects () + { + return GetAllItems<Project> (); + } + public ReadOnlyCollection<T> GetAllSolutionItemsWithTopologicalSort<T> (ConfigurationSelector configuration) where T: SolutionItem { return RootFolder.GetAllItemsWithTopologicalSort<T> (configuration); @@ -378,17 +397,22 @@ namespace MonoDevelop.Projects return RootFolder.GetProjectsContainingFile (fileName); } - public override bool ContainsItem (IWorkspaceObject obj) + public override bool ContainsItem (WorkspaceObject obj) { if (base.ContainsItem (obj)) return true; - foreach (SolutionItem it in GetAllSolutionItems<SolutionItem> ()) { + foreach (SolutionFolderItem it in GetAllItems<SolutionFolderItem> ()) { if (it == obj) return true; } return false; } + + protected override IEnumerable<WorkspaceObject> OnGetChildren () + { + yield return RootFolder; + } public string Description { get { @@ -494,7 +518,7 @@ namespace MonoDevelop.Projects } set { version = value; - foreach (SolutionEntityItem item in GetAllSolutionItems<SolutionEntityItem> ()) { + foreach (SolutionItem item in GetAllItems<SolutionItem> ()) { if (item.SyncVersionWithSolution) item.Version = value; } @@ -515,7 +539,7 @@ namespace MonoDevelop.Projects return list == null || !list.Contains (solutionItemPath); } - public void SetSolutionItemEnabled (string solutionItemPath, bool enabled) + internal void SetSolutionItemEnabled (string solutionItemPath, bool enabled) { solutionItemPath = GetRelativeChildPath (Path.GetFullPath (solutionItemPath)); var list = UserProperties.GetValue<List<string>> ("DisabledProjects"); @@ -543,25 +567,91 @@ namespace MonoDevelop.Projects } } } - - protected override BuildResult OnBuild (IProgressMonitor monitor, ConfigurationSelector configuration) + + + public Task<BuildResult> Clean (ProgressMonitor monitor, string configuration) + { + return Clean (monitor, (SolutionConfigurationSelector) configuration); + } + + public Task<BuildResult> Clean (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return SolutionExtension.Clean (monitor, configuration); + } + + public Task<BuildResult> Build (ProgressMonitor monitor, string configuration) + { + return SolutionExtension.Build (monitor, (SolutionConfigurationSelector) configuration); + } + + Task<BuildResult> IBuildTarget.Build (ProgressMonitor monitor, ConfigurationSelector configuration, bool buildReferencedTargets) + { + return Build (monitor, configuration); + } + + public Task<BuildResult> Build (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return SolutionExtension.Build (monitor, configuration); + } + + public bool NeedsBuilding (ConfigurationSelector configuration) + { + return SolutionExtension.NeedsBuilding (configuration); + } + + public Task Execute (ProgressMonitor monitor, ExecutionContext context, string configuration) + { + return Execute (monitor, context, (SolutionConfigurationSelector) configuration); + } + + public Task Execute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + { + return SolutionExtension.Execute (monitor, context, configuration); + } + + public bool CanExecute (ExecutionContext context, string configuration) + { + return CanExecute (context, (SolutionConfigurationSelector) configuration); + } + + public bool CanExecute (ExecutionContext context, ConfigurationSelector configuration) + { + return SolutionExtension.CanExecute (context, configuration); + } + + public IEnumerable<ExecutionTarget> GetExecutionTargets (string configuration) + { + return GetExecutionTargets ((SolutionConfigurationSelector) configuration); + } + + public IEnumerable<ExecutionTarget> GetExecutionTargets (ConfigurationSelector configuration) + { + return SolutionExtension.GetExecutionTargets (this, configuration); + } + + protected virtual Task<BuildResult> OnBuild (ProgressMonitor monitor, ConfigurationSelector configuration) { return RootFolder.Build (monitor, configuration); } - - protected override void OnClean (IProgressMonitor monitor, ConfigurationSelector configuration) + + protected virtual bool OnGetNeedsBuilding (ConfigurationSelector configuration) + { + return RootFolder.NeedsBuilding (configuration); + } + + protected virtual Task<BuildResult> OnClean (ProgressMonitor monitor, ConfigurationSelector configuration) { - RootFolder.Clean (monitor, configuration); + return RootFolder.Clean (monitor, configuration); } - protected internal override bool OnGetCanExecute(ExecutionContext context, ConfigurationSelector configuration) + protected virtual bool OnGetCanExecute(ExecutionContext context, ConfigurationSelector configuration) { if (SingleStartup) { if (StartupItem == null) return false; return StartupItem.CanExecute (context, configuration); } else { - foreach (SolutionEntityItem it in MultiStartupItems) { + foreach (SolutionItem it in MultiStartupItems) { if (it.CanExecute (context, configuration)) return true; } @@ -569,44 +659,36 @@ namespace MonoDevelop.Projects } } - protected internal override void OnExecute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + protected async virtual Task OnExecute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) { if (SingleStartup) { if (StartupItem == null) { monitor.ReportError (GettextCatalog.GetString ("Startup item not set"), null); return; } - StartupItem.Execute (monitor, context, configuration); + await StartupItem.Execute (monitor, context, configuration); } else { - List<IAsyncOperation> list = new List<IAsyncOperation> (); + var tasks = new List<Task> (); + var monitors = new List<AggregatedProgressMonitor> (); monitor.BeginTask ("Executing projects", 1); - SynchronizedProgressMonitor syncMonitor = new SynchronizedProgressMonitor (monitor); - - foreach (SolutionEntityItem it in MultiStartupItems) { + foreach (SolutionItem it in MultiStartupItems) { if (!it.CanExecute (context, configuration)) continue; AggregatedProgressMonitor mon = new AggregatedProgressMonitor (); - mon.AddSlaveMonitor (syncMonitor, MonitorAction.ReportError | MonitorAction.ReportWarning | MonitorAction.SlaveCancel); - list.Add (mon.AsyncOperation); - SolutionEntityItem cit = it; - - Thread t = new Thread (delegate () { - try { - using (mon) { - cit.Execute (mon, context, configuration); - } - } catch (Exception ex) { - LoggingService.LogError ("Project execution failed", ex); - } - }); - t.Name = "Project execution"; - t.IsBackground = true; - t.Start (); + mon.AddSlaveMonitor (monitor, MonitorAction.ReportError | MonitorAction.ReportWarning | MonitorAction.SlaveCancel); + monitors.Add (mon); + tasks.Add (it.Execute (mon, context, configuration)); } - foreach (IAsyncOperation op in list) - op.WaitForCompleted (); - + try { + await Task.WhenAll (tasks); + } catch (Exception ex) { + LoggingService.LogError ("Project execution failed", ex); + } finally { + foreach (var m in monitors) + m.Dispose (); + } + monitor.EndTask (); } } @@ -617,25 +699,25 @@ namespace MonoDevelop.Projects StartupItemChanged (this, e); } - public override void ConvertToFormat (FileFormat format, bool convertChildren) + public async override Task ConvertToFormat (FileFormat format, bool convertChildren) { - base.ConvertToFormat (format, convertChildren); - foreach (SolutionItem item in GetAllSolutionItems<SolutionItem> ()) - ConvertToSolutionFormat (item, convertChildren); + await base.ConvertToFormat (format, convertChildren); + foreach (SolutionFolderItem item in GetAllItems<SolutionFolderItem> ()) + await ConvertToSolutionFormat (item, convertChildren); } public override bool SupportsFormat (FileFormat format) { if (!base.SupportsFormat (format)) return false; - return GetAllSolutionItems<SolutionEntityItem> ().All (p => p.SupportsFormat (format)); + return GetAllItems<SolutionItem> ().All (p => p.SupportsFormat (format)); }
- public override List<FilePath> GetItemFiles (bool includeReferencedFiles) + protected override IEnumerable<FilePath> OnGetItemFiles (bool includeReferencedFiles) {
- List<FilePath> files = base.GetItemFiles (includeReferencedFiles); + List<FilePath> files = base.OnGetItemFiles (includeReferencedFiles).ToList (); if (includeReferencedFiles) { - foreach (SolutionEntityItem item in GetAllSolutionItems<SolutionEntityItem> ()) + foreach (SolutionItem item in GetAllItems<SolutionItem> ()) files.AddRange (item.GetItemFiles (true)); } return files; @@ -649,7 +731,7 @@ namespace MonoDevelop.Projects SolutionFolder sf = args.SolutionItem as SolutionFolder; if (sf != null) { - foreach (SolutionItem eitem in sf.GetAllItems<SolutionItem> ()) + foreach (SolutionFolderItem eitem in sf.GetAllItems<SolutionFolderItem> ()) SetupNewItem (eitem, null); } else { @@ -660,14 +742,14 @@ namespace MonoDevelop.Projects SolutionItemAdded (this, args); } - void SetupNewItem (SolutionItem item, SolutionItem replacedItem) + void SetupNewItem (SolutionFolderItem item, SolutionFolderItem replacedItem) { - ConvertToSolutionFormat (item, false); + ConvertToSolutionFormat (item, false).Wait (); - SolutionEntityItem eitem = item as SolutionEntityItem; + SolutionItem eitem = item as SolutionItem; if (eitem != null) { eitem.NeedsReload = false; - if (eitem.SupportsBuild () || replacedItem != null) { + if (eitem.SupportsConfigurations () || replacedItem != null) { if (replacedItem == null) { // Register the new entry in every solution configuration foreach (SolutionConfiguration conf in Configurations) @@ -678,11 +760,11 @@ namespace MonoDevelop.Projects } else { // Reuse the configuration information of the replaced item foreach (SolutionConfiguration conf in Configurations) - conf.ReplaceItem ((SolutionEntityItem)replacedItem, eitem); + conf.ReplaceItem ((SolutionItem)replacedItem, eitem); if (StartupItem == replacedItem) StartupItem = eitem; else { - int i = MultiStartupItems.IndexOf ((SolutionEntityItem)replacedItem); + int i = MultiStartupItems.IndexOf ((SolutionItem)replacedItem); if (i != -1) MultiStartupItems [i] = eitem; } @@ -691,11 +773,11 @@ namespace MonoDevelop.Projects } } - void ConvertToSolutionFormat (SolutionItem item, bool force) + async Task ConvertToSolutionFormat (SolutionFolderItem item, bool force) { - SolutionEntityItem eitem = item as SolutionEntityItem; + SolutionItem eitem = item as SolutionItem; if (force || !FileFormat.Format.SupportsMixedFormats || eitem == null || !eitem.IsSaved) { - this.FileFormat.Format.ConvertToFormat (item); + await this.FileFormat.Format.ConvertToFormat (item); if (eitem != null) eitem.InstallFormat (this.FileFormat); } @@ -707,11 +789,11 @@ namespace MonoDevelop.Projects SolutionFolder sf = args.SolutionItem as SolutionFolder; if (sf != null) { - foreach (SolutionEntityItem eitem in sf.GetAllItems<SolutionEntityItem> ()) + foreach (SolutionItem eitem in sf.GetAllItems<SolutionItem> ()) DetachItem (eitem, args.Reloading); } else { - SolutionEntityItem item = args.SolutionItem as SolutionEntityItem; + SolutionItem item = args.SolutionItem as SolutionItem; if (item != null) DetachItem (item, args.Reloading); } @@ -720,7 +802,7 @@ namespace MonoDevelop.Projects SolutionItemRemoved (this, args); } - void DetachItem (SolutionEntityItem item, bool reloading) + void DetachItem (SolutionItem item, bool reloading) { item.NeedsReload = false; if (!reloading) { @@ -744,7 +826,7 @@ namespace MonoDevelop.Projects if (projectToRemove == null) return; - foreach (DotNetProject project in GetAllSolutionItems <DotNetProject>()) { + foreach (DotNetProject project in GetAllItems <DotNetProject>()) { if (project == projectToRemove) continue; @@ -760,7 +842,27 @@ namespace MonoDevelop.Projects } } } + + internal void ReadSolution (ProgressMonitor monitor, SlnFile file) + { + SolutionExtension.OnReadSolution (monitor, file); + } + + protected virtual void OnReadSolution (ProgressMonitor monitor, SlnFile file) + { + ((MSBuildFileFormat)FileFormat.Format).SlnFileFormat.LoadSolution (this, file, monitor); + } + + internal void WriteSolution (ProgressMonitor monitor, SlnFile file) + { + SolutionExtension.OnWriteSolution (monitor, file); + } + protected virtual void OnWriteSolution (ProgressMonitor monitor, SlnFile file) + { + ((MSBuildFileFormat)FileFormat.Format).SlnFileFormat.WriteFileInternal (file, this, monitor); + } + internal void NotifyConfigurationsChanged () { OnConfigurationsChanged (); @@ -843,6 +945,54 @@ namespace MonoDevelop.Projects public event SolutionItemModifiedEventHandler EntryModified; public event SolutionItemEventHandler EntrySaved; public event EventHandler<SolutionItemEventArgs> ItemReloadRequired; + + protected override IEnumerable<WorkspaceObjectExtension> CreateDefaultExtensions () + { + return base.CreateDefaultExtensions ().Concat (Enumerable.Repeat (new DefaultSolutionExtension (), 1)); + } + + internal class DefaultSolutionExtension: SolutionExtension + { + internal protected override IEnumerable<FilePath> GetItemFiles (bool includeReferencedFiles) + { + return Solution.OnGetItemFiles (includeReferencedFiles); + } + + internal protected override Task<BuildResult> Build (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return Solution.OnBuild (monitor, configuration); + } + + internal protected override bool NeedsBuilding (ConfigurationSelector configuration) + { + return Solution.OnGetNeedsBuilding (configuration); + } + + internal protected override Task<BuildResult> Clean (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return Solution.OnClean (monitor, configuration); + } + + internal protected override Task Execute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + { + return Solution.OnExecute (monitor, context, configuration); + } + + internal protected override bool CanExecute (ExecutionContext context, ConfigurationSelector configuration) + { + return Solution.OnGetCanExecute (context, configuration); + } + + internal protected override void OnReadSolution (ProgressMonitor monitor, SlnFile file) + { + Solution.OnReadSolution (monitor, file); + } + + internal protected override void OnWriteSolution (ProgressMonitor monitor, SlnFile file) + { + Solution.OnWriteSolution (monitor, file); + } + } } [Mono.Addins.Extension] diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionConfiguration.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionConfiguration.cs index dfa4bc6832..717edf5f9b 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionConfiguration.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionConfiguration.cs @@ -73,7 +73,7 @@ namespace MonoDevelop.Projects get { return configurations.AsReadOnly (); } } - public bool BuildEnabledForItem (SolutionEntityItem item) + public bool BuildEnabledForItem (SolutionItem item) { foreach (SolutionConfigurationEntry entry in configurations) { if (entry.Item == item) @@ -82,7 +82,7 @@ namespace MonoDevelop.Projects return false; } - public string GetMappedConfiguration (SolutionEntityItem item) + public string GetMappedConfiguration (SolutionItem item) { foreach (SolutionConfigurationEntry entry in configurations) { if (entry.Item == item) @@ -91,7 +91,7 @@ namespace MonoDevelop.Projects return null; } - public SolutionConfigurationEntry GetEntryForItem (SolutionEntityItem item) + public SolutionConfigurationEntry GetEntryForItem (SolutionItem item) { foreach (SolutionConfigurationEntry entry in configurations) { if (entry.Item == item) @@ -100,13 +100,13 @@ namespace MonoDevelop.Projects return null; } - public SolutionConfigurationEntry AddItem (SolutionEntityItem item) + public SolutionConfigurationEntry AddItem (SolutionItem item) { string conf = FindMatchingConfiguration (item); return AddItem (item, conf != null, conf); } - string FindMatchingConfiguration (SolutionEntityItem item) + string FindMatchingConfiguration (SolutionItem item) { SolutionItemConfiguration startupConfiguration = null; @@ -153,7 +153,7 @@ namespace MonoDevelop.Projects return item.Configurations [0].Id; } - public SolutionConfigurationEntry AddItem (SolutionEntityItem item, bool build, string itemConfiguration) + public SolutionConfigurationEntry AddItem (SolutionItem item, bool build, string itemConfiguration) { if (itemConfiguration == null) itemConfiguration = Name; @@ -166,7 +166,7 @@ namespace MonoDevelop.Projects return conf; } - public void RemoveItem (SolutionEntityItem item) + public void RemoveItem (SolutionItem item) { for (int n=0; n<configurations.Count; n++) { if (configurations [n].Item == item) { @@ -176,7 +176,7 @@ namespace MonoDevelop.Projects } } - internal void ReplaceItem (SolutionEntityItem oldItem, SolutionEntityItem newItem) + internal void ReplaceItem (SolutionItem oldItem, SolutionItem newItem) { foreach (var e in configurations.Where (ce => ce.Item == oldItem)) e.Item = newItem; @@ -203,7 +203,7 @@ namespace MonoDevelop.Projects public class SolutionConfigurationEntry { - SolutionEntityItem item; + SolutionItem item; SolutionConfiguration parentConfig; [ItemProperty ("name")] @@ -227,7 +227,7 @@ namespace MonoDevelop.Projects this.deploy = other.deploy; } - internal SolutionConfigurationEntry (SolutionConfiguration parentConfig, SolutionEntityItem item) + internal SolutionConfigurationEntry (SolutionConfiguration parentConfig, SolutionItem item) { this.parentConfig = parentConfig; this.item = item; @@ -258,12 +258,12 @@ namespace MonoDevelop.Projects set { deploy = value; } } - public SolutionEntityItem Item { + public SolutionItem Item { get { if (item == null && parentConfig != null) { Solution sol = parentConfig.ParentSolution; if (sol != null) - item = sol.GetSolutionItem (itemId) as SolutionEntityItem; + item = sol.GetSolutionItem (itemId) as SolutionItem; } return item; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionConfigurationSelector.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionConfigurationSelector.cs index 04beb91589..531bd74947 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionConfigurationSelector.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionConfigurationSelector.cs @@ -48,9 +48,9 @@ namespace MonoDevelop.Projects public override ItemConfiguration GetConfiguration (IConfigurationTarget target) { ItemConfiguration sconf; - if (target is SolutionEntityItem) { + if (target is SolutionItem) { // Get the mapped configuration - SolutionEntityItem item = (SolutionEntityItem) target; + SolutionItem item = (SolutionItem) target; if (item.ParentSolution != null) { SolutionConfiguration config = item.ParentSolution.Configurations [Id]; if (config != null) { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionEntityItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionEntityItem.cs deleted file mode 100644 index 0698a3fcf3..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionEntityItem.cs +++ /dev/null @@ -1,584 +0,0 @@ -// SolutionEntityItem.cs -// -// Author: -// Lluis Sanchez Gual <lluis@novell.com> -// -// Copyright (c) 2008 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.Linq; -using System.Xml; -using System.IO; -using System.Collections; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Collections.Generic; -using System.Reflection; -using System.Diagnostics; -using System.CodeDom.Compiler; - -using MonoDevelop.Core; -using MonoDevelop.Projects; -using MonoDevelop.Core.Serialization; -using MonoDevelop.Projects.Extensions; -using MonoDevelop.Core.StringParsing; - -namespace MonoDevelop.Projects -{ - [ProjectModelDataItem (FallbackType = typeof(UnknownSolutionItem))] - public abstract class SolutionEntityItem : SolutionItem, IConfigurationTarget, IWorkspaceFileObject - { - internal object MemoryProbe = Counters.ItemsInMemory.CreateMemoryProbe (); - - ProjectItemCollection items; - ProjectItemCollection wildcardItems; - ItemCollection<SolutionEntityItem> dependencies = new ItemCollection<SolutionEntityItem> (); - - SolutionItemEventArgs thisItemArgs; - - FileStatusTracker<SolutionItemEventArgs> fileStatusTracker; - - FilePath fileName; - string name; - - FileFormat fileFormat; - - SolutionItemConfiguration activeConfiguration; - SolutionItemConfigurationCollection configurations; - - public event EventHandler ConfigurationsChanged; - public event ConfigurationEventHandler DefaultConfigurationChanged; - public event ConfigurationEventHandler ConfigurationAdded; - public event ConfigurationEventHandler ConfigurationRemoved; - public event EventHandler<ProjectItemEventArgs> ProjectItemAdded; - public event EventHandler<ProjectItemEventArgs> ProjectItemRemoved; - - public SolutionEntityItem () - { - items = new ProjectItemCollection (this); - wildcardItems = new ProjectItemCollection (this); - thisItemArgs = new SolutionItemEventArgs (this); - configurations = new SolutionItemConfigurationCollection (this); - configurations.ConfigurationAdded += new ConfigurationEventHandler (OnConfigurationAddedToCollection); - configurations.ConfigurationRemoved += new ConfigurationEventHandler (OnConfigurationRemovedFromCollection); - Counters.ItemsLoaded++; - fileStatusTracker = new FileStatusTracker<SolutionItemEventArgs> (this, OnReloadRequired, new SolutionItemEventArgs (this)); - } - - public override void Dispose () - { - if (Disposed) - return; - - Counters.ItemsLoaded--; - - foreach (var item in items.Concat (wildcardItems)) { - IDisposable disp = item as IDisposable; - if (disp != null) - disp.Dispose (); - } - - // items = null; - // wildcardItems = null; - // thisItemArgs = null; - // fileStatusTracker = null; - // fileFormat = null; - // activeConfiguration = null; - // configurations = null; - - base.Dispose (); - } - - - internal override void SetItemHandler (ISolutionItemHandler handler) - { - string oldName = Name; - string oldFile = FileName; - - base.SetItemHandler (handler); - - // This will update the name if needed, when SyncFileName is true - Name = oldName; - if (!string.IsNullOrEmpty (oldFile)) - FileName = oldFile; - } - - [ItemProperty ("ReleaseVersion", DefaultValue="0.1")] - string releaseVersion = "0.1"; - - [ItemProperty ("SynchReleaseVersion", DefaultValue = true)] - bool syncReleaseVersion = true; - - public string Version { - get { - // If syncReleaseVersion is set, releaseVersion will already contain the solution's version - // That's because the version must be up to date even when loading the project individually - return releaseVersion; - } - set { - releaseVersion = value; - NotifyModified ("Version"); - } - } - - public bool SyncVersionWithSolution { - get { - return syncReleaseVersion; - } - set { - syncReleaseVersion = value; - if (syncReleaseVersion && ParentSolution != null) - Version = ParentSolution.Version; - NotifyModified ("SyncVersionWithSolution"); - } - } - - [ItemProperty ("name")] - public override string Name { - get { - return name ?? string.Empty; - } - set { - if (name == value) - return; - string oldName = name; - name = value; - if (!Loading && ItemHandler.SyncFileName) { - if (string.IsNullOrEmpty (fileName)) - FileName = value; - else { - string ext = fileName.Extension; - FileName = fileName.ParentDirectory.Combine (value) + ext; - } - } - OnNameChanged (new SolutionItemRenamedEventArgs (this, oldName, name)); - } - } - - public virtual FilePath FileName { - get { - return fileName; - } - set { - fileName = value; - if (FileFormat != null) - fileName = FileFormat.GetValidFileName (this, fileName); - if (ItemHandler.SyncFileName) - Name = fileName.FileNameWithoutExtension; - NotifyModified ("FileName"); - } - } - - public bool Enabled { - get { return ParentSolution != null ? ParentSolution.IsSolutionItemEnabled (FileName) : true; } - set { - if (ParentSolution != null) - ParentSolution.SetSolutionItemEnabled (FileName, value); - } - } - - public FileFormat FileFormat { - get { - if (ParentSolution != null) { - if (ParentSolution.FileFormat.Format.SupportsMixedFormats && fileFormat != null) - return fileFormat; - return ParentSolution.FileFormat; - } - if (fileFormat == null) - fileFormat = Services.ProjectService.GetDefaultFormat (this); - return fileFormat; - } - set { - if (ParentSolution != null && !ParentSolution.FileFormat.Format.SupportsMixedFormats) - throw new InvalidOperationException ("The file format can't be changed when the item belongs to a solution."); - InstallFormat (value); - fileFormat.Format.ConvertToFormat (this); - NeedsReload = false; - NotifyModified ("FileFormat"); - } - } - - public ProjectItemCollection Items { - get { return items; } - } - - internal ProjectItemCollection WildcardItems { - get { return wildcardItems; } - } - - /// <summary> - /// Projects that need to be built before building this one - /// </summary> - /// <value>The dependencies.</value> - public ItemCollection<SolutionEntityItem> ItemDependencies { - get { return dependencies; } - } - - public override IEnumerable<SolutionItem> GetReferencedItems (ConfigurationSelector configuration) - { - return base.GetReferencedItems (configuration).Concat (dependencies); - } - - void IWorkspaceFileObject.ConvertToFormat (FileFormat format, bool convertChildren) - { - this.FileFormat = format; - } - - public virtual bool SupportsFormat (FileFormat format) - { - return true; - } - - internal void InstallFormat (FileFormat format) - { - fileFormat = format; - if (fileName != FilePath.Null) - fileName = fileFormat.GetValidFileName (this, fileName); - } - - protected override void InitializeItemHandler () - { - Services.ProjectService.GetDefaultFormat (this).Format.ConvertToFormat (this); - } - - protected override FilePath GetDefaultBaseDirectory ( ) - { - return FileName.IsNullOrEmpty ? FilePath.Empty : FileName.ParentDirectory; - } - - public void Save (FilePath fileName, IProgressMonitor monitor) - { - FileName = fileName; - Save (monitor); - } - - public override void Save (IProgressMonitor monitor) - { - if (string.IsNullOrEmpty (FileName)) - throw new InvalidOperationException ("Project does not have a file name"); - - try { - fileStatusTracker.BeginSave (); - Services.ProjectService.GetExtensionChain (this).Save (monitor, this); - OnSaved (thisItemArgs); - } finally { - fileStatusTracker.EndSave (); - } - FileService.NotifyFileChanged (FileName); - } - - protected override void OnEndLoad () - { - base.OnEndLoad (); - fileStatusTracker.ResetLoadTimes (); - - if (syncReleaseVersion && ParentSolution != null) - releaseVersion = ParentSolution.Version; - } - - - internal bool IsSaved { - get { - return !string.IsNullOrEmpty (FileName) && File.Exists (FileName); - } - } - - public override bool NeedsReload { - get { return fileStatusTracker.NeedsReload; } - set { fileStatusTracker.NeedsReload = value; } - } - - public virtual bool ItemFilesChanged { - get { return fileStatusTracker.ItemFilesChanged; } - } - - internal protected override BuildResult OnRunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) - { - if (target == ProjectService.BuildTarget) { - SolutionItemConfiguration conf = GetConfiguration (configuration) as SolutionItemConfiguration; - if (conf != null && conf.CustomCommands.HasCommands (CustomCommandType.Build)) { - conf.CustomCommands.ExecuteCommand (monitor, this, CustomCommandType.Build, configuration); - return new BuildResult (); - } - } else if (target == ProjectService.CleanTarget) { - SolutionItemConfiguration config = GetConfiguration (configuration) as SolutionItemConfiguration; - if (config != null && config.CustomCommands.HasCommands (CustomCommandType.Clean)) { - config.CustomCommands.ExecuteCommand (monitor, this, CustomCommandType.Clean, configuration); - return new BuildResult (); - } - } - return base.OnRunTarget (monitor, target, configuration); - } - - protected internal virtual void OnSave (IProgressMonitor monitor) - { - ItemHandler.Save (monitor); - } - - [Obsolete ("This method will be removed in future releases")] - public void SetNeedsBuilding (bool value) - { - // Nothing to be done - } - - public FilePath GetAbsoluteChildPath (FilePath relPath) - { - return relPath.ToAbsolute (BaseDirectory); - } - - public FilePath GetRelativeChildPath (FilePath absPath) - { - return absPath.ToRelative (BaseDirectory); - } - - public List<FilePath> GetItemFiles (bool includeReferencedFiles) - { - return Services.ProjectService.GetExtensionChain (this).GetItemFiles (this, includeReferencedFiles); - } - - internal protected virtual List<FilePath> OnGetItemFiles (bool includeReferencedFiles) - { - List<FilePath> col = FileFormat.Format.GetItemFiles (this); - if (!string.IsNullOrEmpty (FileName) && !col.Contains (FileName)) - col.Add (FileName); - return col; - } - - protected override void OnNameChanged (SolutionItemRenamedEventArgs e) - { - Solution solution = this.ParentSolution; - - if (solution != null) { - foreach (DotNetProject project in solution.GetAllSolutionItems<DotNetProject>()) { - if (project == this) - continue; - - project.RenameReferences (e.OldName, e.NewName); - } - } - fileStatusTracker.ResetLoadTimes (); - base.OnNameChanged (e); - } - - protected virtual void OnSaved (SolutionItemEventArgs args) - { - if (Saved != null) - Saved (this, args); - } - - public virtual string[] SupportedPlatforms { - get { - return new string [0]; - } - } - - public virtual SolutionItemConfiguration GetConfiguration (ConfigurationSelector configuration) - { - return (SolutionItemConfiguration) configuration.GetConfiguration (this) ?? DefaultConfiguration; - } - - ItemConfiguration IConfigurationTarget.DefaultConfiguration { - get { return DefaultConfiguration; } - set { DefaultConfiguration = (SolutionItemConfiguration) value; } - } - - public SolutionItemConfiguration DefaultConfiguration { - get { - if (activeConfiguration == null && configurations.Count > 0) { - return configurations[0]; - } - return activeConfiguration; - } - set { - if (activeConfiguration != value) { - activeConfiguration = value; - NotifyModified ("DefaultConfiguration"); - OnDefaultConfigurationChanged (new ConfigurationEventArgs (this, value)); - } - } - } - - public string DefaultConfigurationId { - get { - if (DefaultConfiguration != null) - return DefaultConfiguration.Id; - else - return null; - } - set { - DefaultConfiguration = GetConfiguration (new ItemConfigurationSelector (value)); - } - } - - public virtual ReadOnlyCollection<string> GetConfigurations () - { - List<string> configs = new List<string> (); - foreach (SolutionItemConfiguration conf in Configurations) - configs.Add (conf.Id); - return configs.AsReadOnly (); - } - - [ItemProperty ("Configurations")] - [ItemProperty ("Configuration", ValueType=typeof(SolutionItemConfiguration), Scope="*")] - public SolutionItemConfigurationCollection Configurations { - get { - return configurations; - } - } - - IItemConfigurationCollection IConfigurationTarget.Configurations { - get { - return Configurations; - } - } - - public SolutionItemConfiguration AddNewConfiguration (string name) - { - SolutionItemConfiguration config = CreateConfiguration (name); - Configurations.Add (config); - return config; - } - - ItemConfiguration IConfigurationTarget.CreateConfiguration (string name) - { - return CreateConfiguration (name); - } - - public virtual SolutionItemConfiguration CreateConfiguration (string name) - { - return new SolutionItemConfiguration (name); - } - - void OnConfigurationAddedToCollection (object ob, ConfigurationEventArgs args) - { - NotifyModified ("Configurations"); - OnConfigurationAdded (new ConfigurationEventArgs (this, args.Configuration)); - if (ConfigurationsChanged != null) - ConfigurationsChanged (this, EventArgs.Empty); - if (activeConfiguration == null) - DefaultConfigurationId = args.Configuration.Id; - } - - void OnConfigurationRemovedFromCollection (object ob, ConfigurationEventArgs args) - { - if (activeConfiguration == args.Configuration) { - if (Configurations.Count > 0) - DefaultConfiguration = Configurations [0]; - else - DefaultConfiguration = null; - } - NotifyModified ("Configurations"); - OnConfigurationRemoved (new ConfigurationEventArgs (this, args.Configuration)); - if (ConfigurationsChanged != null) - ConfigurationsChanged (this, EventArgs.Empty); - } - - public override StringTagModelDescription GetStringTagModelDescription (ConfigurationSelector conf) - { - StringTagModelDescription model = base.GetStringTagModelDescription (conf); - SolutionItemConfiguration config = GetConfiguration (conf); - if (config != null) - model.Add (config.GetType ()); - else - model.Add (typeof(SolutionItemConfiguration)); - return model; - } - - public override StringTagModel GetStringTagModel (ConfigurationSelector conf) - { - StringTagModel source = base.GetStringTagModel (conf); - SolutionItemConfiguration config = GetConfiguration (conf); - if (config != null) - source.Add (config); - return source; - } - - internal protected virtual void OnItemsAdded (IEnumerable<ProjectItem> objs) - { - NotifyModified ("Items"); - var args = new ProjectItemEventArgs (); - args.AddRange (objs.Select (pi => new ProjectItemEventInfo (this, pi))); - if (ProjectItemAdded != null) - ProjectItemAdded (this, args); - } - - internal protected virtual void OnItemsRemoved (IEnumerable<ProjectItem> objs) - { - NotifyModified ("Items"); - var args = new ProjectItemEventArgs (); - args.AddRange (objs.Select (pi => new ProjectItemEventInfo (this, pi))); - if (ProjectItemRemoved != null) - ProjectItemRemoved (this, args); - } - - protected virtual void OnDefaultConfigurationChanged (ConfigurationEventArgs args) - { - if (DefaultConfigurationChanged != null) - DefaultConfigurationChanged (this, args); - } - - protected virtual void OnConfigurationAdded (ConfigurationEventArgs args) - { - if (ConfigurationAdded != null) - ConfigurationAdded (this, args); - } - - protected virtual void OnConfigurationRemoved (ConfigurationEventArgs args) - { - if (ConfigurationRemoved != null) - ConfigurationRemoved (this, args); - } - - protected virtual void OnReloadRequired (SolutionItemEventArgs args) - { - fileStatusTracker.FireReloadRequired (args); - } - - public event SolutionItemEventHandler Saved; - -/* public event EventHandler<SolutionItemEventArgs> ReloadRequired { - add { fileStatusTracker.ReloadRequired += value; } - remove { fileStatusTracker.ReloadRequired -= value; } - } -*/ } - - [Mono.Addins.Extension] - class SolutionEntityItemTagProvider: StringTagProvider<SolutionEntityItem>, IStringTagProvider - { - public override IEnumerable<StringTagDescription> GetTags () - { - yield return new StringTagDescription ("ProjectFile", "Project File"); - } - - public override object GetTagValue (SolutionEntityItem item, string tag) - { - switch (tag) { - case "ITEMFILE": - case "PROJECTFILE": - case "PROJECTFILENAME": - return item.FileName; - } - throw new NotSupportedException (); - } - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionExtension.cs new file mode 100644 index 0000000000..4da2bdf7d0 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionExtension.cs @@ -0,0 +1,89 @@ +// +// SolutionExtension.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 MonoDevelop.Core; +using MonoDevelop.Core.Execution; +using System.Collections.Generic; +using System.Threading.Tasks; +using MonoDevelop.Projects.Formats.MSBuild; + +namespace MonoDevelop.Projects +{ + public class SolutionExtension: WorkspaceItemExtension + { + SolutionExtension next; + + protected Solution Solution { + get { return (Solution) base.Item; } + } + + internal protected override bool SupportsItem (WorkspaceItem item) + { + return item is Solution; + } + + internal protected virtual Task<BuildResult> Build (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return next.Build (monitor, configuration); + } + + internal protected virtual Task<BuildResult> Clean (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return next.Clean (monitor, configuration); + } + + internal protected virtual Task Execute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + { + return next.Execute (monitor, context, configuration); + } + + internal protected virtual bool CanExecute (ExecutionContext context, ConfigurationSelector configuration) + { + return next.CanExecute (context, configuration); + } + + internal protected virtual IEnumerable<ExecutionTarget> GetExecutionTargets (Solution solution, ConfigurationSelector configuration) + { + return next.GetExecutionTargets (solution, configuration); + } + + internal protected virtual bool NeedsBuilding (ConfigurationSelector configuration) + { + return next.NeedsBuilding (configuration); + } + + internal protected virtual void OnReadSolution (ProgressMonitor monitor, SlnFile file) + { + next.OnReadSolution (monitor, file); + } + + internal protected virtual void OnWriteSolution (ProgressMonitor monitor, SlnFile file) + { + next.OnWriteSolution (monitor, file); + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionFolder.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionFolder.cs index d4f9cb7e91..15942ba39c 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionFolder.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionFolder.cs @@ -45,11 +45,12 @@ using MonoDevelop.Core.ProgressMonitoring; using MonoDevelop.Projects; using MonoDevelop.Projects.Extensions; using MonoDevelop.Core.Serialization; +using System.Threading.Tasks; namespace MonoDevelop.Projects { [DataInclude (typeof(SolutionConfiguration))] - public class SolutionFolder : SolutionItem + public class SolutionFolder : SolutionFolderItem { SolutionFolderItemCollection items; SolutionFolderFileCollection files; @@ -67,6 +68,11 @@ namespace MonoDevelop.Projects } } + protected override IEnumerable<WorkspaceObject> OnGetChildren () + { + return Items; + } + internal SolutionFolderItemCollection GetItemsWithoutCreating () { return items; @@ -86,20 +92,17 @@ namespace MonoDevelop.Projects get { return ParentFolder == null; } } - public override string Name { - get { - if (ParentFolder == null && ParentSolution != null) - return ParentSolution.Name; - else - return name; - } - set { - if (value != name) { - string oldName = name; - name = value; - OnNameChanged (new SolutionItemRenamedEventArgs (this, oldName, name)); - } - } + protected override string OnGetName () + { + if (ParentFolder == null && ParentSolution != null) + return ParentSolution.Name; + else + return name; + } + + protected override void OnSetName (string value) + { + name = value; }
protected override FilePath GetDefaultBaseDirectory ( ) @@ -136,7 +139,7 @@ namespace MonoDevelop.Projects {
FilePath path = null; - foreach (SolutionItem it in Items) {
+ foreach (SolutionFolderItem it in Items) {
FilePath subdir; if (it is SolutionFolder) { SolutionFolder sf = (SolutionFolder) it; @@ -184,25 +187,19 @@ namespace MonoDevelop.Projects else return null; } - - internal override IDictionary InternalGetExtendedProperties { - get { - if (ParentSolution != null && ParentFolder == null) - return ParentSolution.ExtendedProperties; - else - return base.InternalGetExtendedProperties; - } - } - - protected override void InitializeItemHandler () + + protected override IDictionary OnGetExtendedProperties () { - SetItemHandler (new DummySolutionFolderHandler (this)); + if (ParentSolution != null && ParentFolder == null) + return ParentSolution.ExtendedProperties; + else + return base.OnGetExtendedProperties (); } - + public override void Dispose() { if (items != null) { - foreach (SolutionItem e in items) + foreach (SolutionFolderItem e in items) e.Dispose (); items = null; } @@ -210,30 +207,25 @@ namespace MonoDevelop.Projects base.Dispose (); } - public SolutionItem ReloadItem (IProgressMonitor monitor, SolutionItem sitem) + public async Task<SolutionFolderItem> ReloadItem (ProgressMonitor monitor, SolutionFolderItem sitem) { if (Items.IndexOf (sitem) == -1) throw new InvalidOperationException ("Solution item '" + sitem.Name + "' does not belong to folder '" + Name + "'"); - SolutionEntityItem item = sitem as SolutionEntityItem; + SolutionItem item = sitem as SolutionItem; if (item != null) { // Load the new item - SolutionEntityItem newItem; + SolutionItem newItem; try { if (ParentSolution.IsSolutionItemEnabled (item.FileName)) - newItem = Services.ProjectService.ReadSolutionItem (monitor, item.FileName); + newItem = await Services.ProjectService.ReadSolutionItem (monitor, item.FileName); else { UnknownSolutionItem e = new UnloadedSolutionItem () { FileName = item.FileName }; - var ch = item.GetItemHandler () as MonoDevelop.Projects.Formats.MSBuild.MSBuildHandler; - if (ch != null) { - var h = new MonoDevelop.Projects.Formats.MSBuild.MSBuildHandler (ch.TypeGuid, ch.ItemId) { - Item = e, - }; - e.SetItemHandler (h); - } + e.ItemId = item.ItemId; + e.TypeGuid = item.TypeGuid; newItem = e; } } catch (Exception ex) { @@ -249,7 +241,7 @@ namespace MonoDevelop.Projects newItem.Dispose (); // Find the replacement if it exists - return Items.OfType<SolutionEntityItem> ().FirstOrDefault (it => it.FileName == item.FileName); + return Items.OfType<SolutionItem> ().FirstOrDefault (it => it.FileName == item.FileName); } // Replace in the file list @@ -269,7 +261,7 @@ namespace MonoDevelop.Projects return sitem; } - internal void NotifyItemAdded (SolutionItem item, bool newToSolution) + internal void NotifyItemAdded (SolutionFolderItem item, bool newToSolution) { ConnectChildEntryEvents (item); @@ -277,7 +269,7 @@ namespace MonoDevelop.Projects OnItemAdded (new SolutionItemChangeEventArgs (item, ParentSolution, false), newToSolution); } - void ConnectChildEntryEvents (SolutionItem item) + void ConnectChildEntryEvents (SolutionFolderItem item) { if (item is Project) { Project project = item as Project; @@ -303,42 +295,36 @@ namespace MonoDevelop.Projects folder.ReferenceAddedToProject += NotifyReferenceAddedToProject; } - if (item is SolutionEntityItem) { - ((SolutionEntityItem)item).Saved += NotifyItemSaved; + if (item is SolutionItem) { + ((SolutionItem)item).Saved += NotifyItemSaved; // ((SolutionEntityItem)item).ReloadRequired += NotifyItemReloadRequired; } item.Modified += NotifyItemModified; } - public override void Save (IProgressMonitor monitor) - { - foreach (SolutionItem item in Items) - item.Save (monitor); - } - - public SolutionEntityItem AddItem (IProgressMonitor monitor, string filename) + public Task<SolutionItem> AddItem (ProgressMonitor monitor, string filename) { return AddItem (monitor, filename, false); } - public SolutionEntityItem AddItem (IProgressMonitor monitor, string filename, bool createSolutionConfigurations) + public async Task<SolutionItem> AddItem (ProgressMonitor monitor, string filename, bool createSolutionConfigurations) { - if (monitor == null) monitor = new NullProgressMonitor (); - SolutionEntityItem entry = Services.ProjectService.ReadSolutionItem (monitor, filename); + if (monitor == null) monitor = new ProgressMonitor (); + SolutionItem entry = await Services.ProjectService.ReadSolutionItem (monitor, filename); AddItem (entry, createSolutionConfigurations); return entry; } - public void AddItem (SolutionItem item) + public void AddItem (SolutionFolderItem item) { AddItem (item, false); } - public void AddItem (SolutionItem item, bool createSolutionConfigurations) + public void AddItem (SolutionFolderItem item, bool createSolutionConfigurations) { Items.Add (item); - SolutionEntityItem eitem = item as SolutionEntityItem; + SolutionItem eitem = item as SolutionItem; if (eitem != null && createSolutionConfigurations && eitem.SupportsBuild ()) { // Create new solution configurations for item configurations foreach (ItemConfiguration iconf in eitem.Configurations) { @@ -352,7 +338,7 @@ namespace MonoDevelop.Projects if (!found) { SolutionConfiguration sconf = new SolutionConfiguration (iconf.Id); // Add all items to the new configuration - foreach (var it in ParentSolution.GetAllSolutionItems<SolutionEntityItem> ()) + foreach (var it in ParentSolution.GetAllItems<SolutionItem> ()) sconf.AddItem (it); ParentSolution.Configurations.Add (sconf); } @@ -360,14 +346,14 @@ namespace MonoDevelop.Projects } } - internal void NotifyItemRemoved (SolutionItem item, bool removedFromSolution) + internal void NotifyItemRemoved (SolutionFolderItem item, bool removedFromSolution) { DisconnectChildEntryEvents (item); NotifyModified ("Items"); OnItemRemoved (new SolutionItemChangeEventArgs (item, ParentSolution, false), removedFromSolution); } - void DisconnectChildEntryEvents (SolutionItem entry) + void DisconnectChildEntryEvents (SolutionFolderItem entry) { if (entry is Project) { Project pce = entry as Project; @@ -393,35 +379,29 @@ namespace MonoDevelop.Projects cce.ReferenceAddedToProject -= NotifyReferenceAddedToProject; } - if (entry is SolutionEntityItem) { - ((SolutionEntityItem)entry).Saved -= NotifyItemSaved; + if (entry is SolutionItem) { + ((SolutionItem)entry).Saved -= NotifyItemSaved; // ((SolutionEntityItem)entry).ReloadRequired -= NotifyItemReloadRequired; } entry.Modified -= NotifyItemModified; } - protected internal override void OnExecute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + public void Execute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) { } - - /// <remarks> - /// Returns a collection containing all entries in this folder and - /// undercombines - /// </remarks> - public ReadOnlyCollection<SolutionItem> GetAllItems () + + public bool CanExecute (ExecutionContext context, ConfigurationSelector configuration) { - return GetAllItems<SolutionItem> (); + return false; } - + /// <remarks> - /// Returns a collection containing all entries of the given type in this folder and + /// Returns a collection containing all entries in this folder and /// undercombines /// </remarks> - public ReadOnlyCollection<T> GetAllItems<T> () where T: SolutionItem + public IEnumerable<SolutionFolderItem> GetAllItems () { - List<T> list = new List<T> (); - GetAllItems<T> (list, this); - return list.AsReadOnly (); + return GetAllItems<SolutionFolderItem> (); } public ReadOnlyCollection<T> GetAllItemsWithTopologicalSort<T> (ConfigurationSelector configuration) where T: SolutionItem @@ -447,24 +427,24 @@ namespace MonoDevelop.Projects return SolutionItem.TopologicalSort<Project> (list, configuration); } - void GetAllItems<T> (List<T> list, SolutionItem item) where T: SolutionItem + void GetAllItems<T> (List<T> list, SolutionFolderItem item) where T: SolutionFolderItem { if (item is T) { list.Add ((T)item); } if (item is SolutionFolder) { - foreach (SolutionItem ce in ((SolutionFolder)item).Items) + foreach (SolutionFolderItem ce in ((SolutionFolder)item).Items) GetAllItems<T> (list, ce); } } public ReadOnlyCollection<SolutionItem> GetAllBuildableEntries (ConfigurationSelector configuration, bool topologicalSort, bool includeExternalReferences) { - List<SolutionItem> list = new List<SolutionItem> (); + var list = new List<SolutionItem> (); GetAllBuildableEntries (list, configuration, includeExternalReferences); if (topologicalSort) - return TopologicalSort<SolutionItem> (list, configuration); + return SolutionItem.TopologicalSort<SolutionItem> (list, configuration); else return list.AsReadOnly (); } @@ -482,22 +462,25 @@ namespace MonoDevelop.Projects if (conf == null) return; - foreach (SolutionItem item in Items) { + foreach (SolutionFolderItem item in Items) { if (item is SolutionFolder) ((SolutionFolder)item).GetAllBuildableEntries (list, configuration, includeExternalReferences); - else if ((item is SolutionEntityItem) && conf.BuildEnabledForItem ((SolutionEntityItem) item) && item.SupportsBuild ()) - GetAllBuildableReferences (list, item, configuration, includeExternalReferences); + else if ((item is SolutionItem) && conf.BuildEnabledForItem ((SolutionItem) item) && ((SolutionItem)item).SupportsBuild ()) + GetAllBuildableReferences (list, (SolutionItem)item, configuration, includeExternalReferences, false); } } - void GetAllBuildableReferences (List<SolutionItem> list, SolutionItem item, ConfigurationSelector configuration, bool includeExternalReferences) + void GetAllBuildableReferences (List<SolutionItem> list, SolutionItem item, ConfigurationSelector configuration, bool includeExternalReferences, bool isDirectReference) { if (list.Contains (item)) return; + // Skip unsupported projects which are not directly referenced by other (supported) projects + if (!isDirectReference && item.IsUnsupportedProject) + return; list.Add (item); if (includeExternalReferences) { - foreach (SolutionItem it in item.GetReferencedItems (configuration)) - GetAllBuildableReferences (list, it, configuration, includeExternalReferences); + foreach (var it in item.GetReferencedItems (configuration)) + GetAllBuildableReferences (list, it, configuration, includeExternalReferences, true); } } @@ -538,19 +521,19 @@ namespace MonoDevelop.Projects } } - public SolutionEntityItem FindSolutionItem (string fileName) + public SolutionItem FindSolutionItem (string fileName) { string path = Path.GetFullPath (fileName); - foreach (SolutionItem it in Items) { + foreach (SolutionFolderItem it in Items) { if (it is SolutionFolder) { - SolutionEntityItem r = ((SolutionFolder)it).FindSolutionItem (fileName); + SolutionItem r = ((SolutionFolder)it).FindSolutionItem (fileName); if (r != null) return r; } - else if (it is SolutionEntityItem) { - SolutionEntityItem se = (SolutionEntityItem) it; + else if (it is SolutionItem) { + SolutionItem se = (SolutionItem) it; if (!string.IsNullOrEmpty (se.FileName) && path == Path.GetFullPath (se.FileName)) - return (SolutionEntityItem) it; + return (SolutionItem) it; } } return null; @@ -558,7 +541,7 @@ namespace MonoDevelop.Projects public Project FindProjectByName (string name) { - foreach (SolutionItem it in Items) { + foreach (SolutionFolderItem it in Items) { if (it is SolutionFolder) { Project r = ((SolutionFolder)it).FindProjectByName (name); if (r != null) @@ -572,85 +555,42 @@ namespace MonoDevelop.Projects return null; } - protected internal override BuildResult OnRunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) + public async Task<BuildResult> Clean (ProgressMonitor monitor, ConfigurationSelector configuration) { - if (target == ProjectService.BuildTarget) - return OnBuild (monitor, configuration); - else if (target == ProjectService.CleanTarget) { - OnClean (monitor, configuration); + if (ParentSolution == null) return new BuildResult (); - } - + SolutionConfiguration conf = ParentSolution.GetConfiguration (configuration); + if (conf == null) + return new BuildResult (); + + var res = new BuildResult { BuildCount = 0 }; + ReadOnlyCollection<SolutionItem> allProjects; - try { allProjects = GetAllBuildableEntries (configuration, true, true); } catch (CyclicDependencyException) { monitor.ReportError (GettextCatalog.GetString ("Cyclic dependencies are not supported."), null); return new BuildResult ("", 1, 1); } - - try { - monitor.BeginTask (GettextCatalog.GetString ("Building Solution: {0} ({1})", Name, configuration.ToString ()), allProjects.Count); - - BuildResult cres = new BuildResult (); - cres.BuildCount = 0; - HashSet<SolutionItem> failedItems = new HashSet<SolutionItem> (); - - foreach (SolutionItem item in allProjects) { - if (monitor.IsCancelRequested) - break; - if (!item.ContainsReferences (failedItems, configuration)) { - BuildResult res = item.RunTarget (monitor, target, configuration); - if (res != null) { - cres.Append (res); - if (res.ErrorCount > 0) - failedItems.Add (item); - } - } else - failedItems.Add (item); - monitor.Step (1); - } - return cres; - } finally { - monitor.EndTask (); - } - } - - protected override void OnClean (IProgressMonitor monitor, ConfigurationSelector configuration) - { - if (ParentSolution == null) - return; - SolutionConfiguration conf = ParentSolution.GetConfiguration (configuration); - if (conf == null) - return; - + monitor.BeginTask (GettextCatalog.GetString ("Cleaning Solution: {0} ({1})", Name, configuration.ToString ()), allProjects.Count); try { - monitor.BeginTask (GettextCatalog.GetString ("Cleaning Solution: {0} ({1})", Name, configuration.ToString ()), Items.Count); - - foreach (SolutionItem item in Items) { - if (item is SolutionFolder) - item.Clean (monitor, configuration); - else if (item is SolutionEntityItem) { - SolutionEntityItem si = (SolutionEntityItem) item; - // ce can be null if you add items to the root solution folder which - // causes them to be placed in an autogenerated 'Project Items' folder - SolutionConfigurationEntry ce = conf.GetEntryForItem (si); - if (ce != null && ce.Build) - si.Clean (monitor, ce.ItemConfigurationSelector); - } else { - item.Clean (monitor, configuration); - } + foreach (var si in allProjects) { + // ce can be null if you add items to the root solution folder which + // causes them to be placed in an autogenerated 'Project Items' folder + SolutionConfigurationEntry ce = conf.GetEntryForItem (si); + if (ce != null && ce.Build) + res.Append (await si.Clean (monitor, ce.ItemConfigurationSelector)); monitor.Step (1); } } finally { monitor.EndTask (); } + return res; } - protected override BuildResult OnBuild (IProgressMonitor monitor, ConfigurationSelector configuration) + public async Task<BuildResult> Build (ProgressMonitor monitor, ConfigurationSelector configuration, bool buildReferencedTargets = false) { ReadOnlyCollection<SolutionItem> allProjects; @@ -671,11 +611,11 @@ namespace MonoDevelop.Projects HashSet<SolutionItem> failedItems = new HashSet<SolutionItem> (); foreach (SolutionItem item in toBuild) { - if (monitor.IsCancelRequested) + if (monitor.CancellationToken.IsCancellationRequested) break; if (!item.ContainsReferences (failedItems, configuration)) { - BuildResult res = item.Build (monitor, configuration, false); + BuildResult res = await item.Build (monitor, configuration, false); if (res != null) { cres.Append (res); if (res.ErrorCount > 0) @@ -691,12 +631,17 @@ namespace MonoDevelop.Projects } } + public bool NeedsBuilding (ConfigurationSelector configuration) + { + return Items.OfType<IBuildTarget>().Any (t => t.NeedsBuilding (configuration)); + } + protected internal override DateTime OnGetLastBuildTime (ConfigurationSelector configuration) { // Return the min value, since that the last time all items in the // folder were built DateTime tim = DateTime.MaxValue; - foreach (SolutionItem it in Items) { + foreach (SolutionFolderItem it in Items) { DateTime t = it.GetLastBuildTime (configuration); if (t < tim) tim = t; @@ -1020,12 +965,12 @@ namespace MonoDevelop.Projects get { return folder.Name; } } - public BuildResult RunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) + public Task<BuildResult> RunTarget (ProgressMonitor monitor, string target, ConfigurationSelector configuration) { throw new NotImplementedException (); } - public void Save (IProgressMonitor monitor) + public Task Save (ProgressMonitor monitor) { throw new NotImplementedException (); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionFolderItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionFolderItem.cs new file mode 100644 index 0000000000..9dd7b25a7d --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionFolderItem.cs @@ -0,0 +1,549 @@ +// SolutionItem.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2008 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.Collections; +using System.Xml; +using MonoDevelop.Core; +using MonoDevelop.Core.Serialization; +using MonoDevelop.Projects.Extensions;
+using MonoDevelop.Core.StringParsing; +using MonoDevelop.Projects.Policies; +using System.Collections.Generic; +using MonoDevelop.Projects.Formats.MSBuild; + +namespace MonoDevelop.Projects +{ + public abstract class SolutionFolderItem: WorkspaceObject, IExtendedDataItem, IPolicyProvider + { + SolutionFolder parentFolder; + Solution parentSolution; + SolutionFolder internalChildren; + + [ProjectPathItemProperty ("BaseDirectory", DefaultValue=null)] + string baseDirectory; + + [ItemProperty ("Policies", IsExternal = true, SkipEmpty = true)] + PolicyBag policies; + + [ItemProperty ("UseMSBuildEngine")] + public bool? UseMSBuildEngine { get; set; } + + PropertyBag userProperties; + + internal List<string> UnresolvedProjectDependencies { get; set; } + + [ItemProperty ("name")] + public new string Name { + get { + return base.Name; + } + set { + if (value != Name) { + var oldName = Name; + OnSetName (value); + OnNameChanged (new SolutionItemRenamedEventArgs (this, oldName, Name)); + } + } + } + + public string TypeGuid { + get; + set; + } + + protected abstract void OnSetName (string value); + + /// <summary> + /// Gets the solution to which this item belongs + /// </summary> + public Solution ParentSolution { + get { + if (parentFolder != null) + return parentFolder.ParentSolution; + return parentSolution; + } + internal set { + if (parentSolution != null && parentSolution != value) + NotifyUnboundFromSolution (true); + parentSolution = value; + NotifyBoundToSolution (true); + } + } + + /// <summary> + /// Gets or sets the base directory of this solution item + /// </summary> + /// <value> + /// The base directory. + /// </value> + /// <remarks> + /// The base directory is the directory where files belonging to this project + /// are placed. Notice that this directory may be different than the directory + /// where the project file is placed. + /// </remarks> + public new FilePath BaseDirectory { + get { + if (baseDirectory == null) {
+ FilePath dir = GetDefaultBaseDirectory (); + if (dir.IsNullOrEmpty) + dir = "."; + return dir.FullPath; + } + else + return baseDirectory; + } + set {
+ FilePath def = GetDefaultBaseDirectory (); + if (value != FilePath.Null && def != FilePath.Null && value.FullPath == def.FullPath) + baseDirectory = null; + else if (string.IsNullOrEmpty (value)) + baseDirectory = null; + else + baseDirectory = value.FullPath; + NotifyModified ("BaseDirectory"); + } + } + + protected override string OnGetBaseDirectory () + { + return BaseDirectory; + } + + protected override string OnGetItemDirectory () + { + FilePath dir = GetDefaultBaseDirectory (); + if (string.IsNullOrEmpty (dir)) + dir = "."; + return dir.FullPath; + } + + internal bool HasCustomBaseDirectory { + get { return baseDirectory != null; } + }
+ + /// <summary> + /// Gets the default base directory. + /// </summary> + /// <remarks> + /// The base directory is the directory where files belonging to this project + /// are placed. Notice that this directory may be different than the directory + /// where the project file is placed. + /// </remarks> + protected virtual FilePath GetDefaultBaseDirectory ( ) + { + return ParentSolution.BaseDirectory; + } + + /// <summary> + /// Gets the identifier of this solution item + /// </summary> + /// <remarks> + /// The identifier is unique inside the solution + /// </remarks> + public string ItemId { + get { + if (itemId == null) + itemId = "{" + Guid.NewGuid ().ToString ().ToUpper () + "}"; + return itemId; + } + set { + itemId = value; + } + } + + string itemId; + + /// <summary> + /// Gets policies. + /// </summary> + /// <remarks> + /// Returns a policy container which can be used to query policies specific for this + /// solution item. If a policy is not defined for this item, the inherited value will be returned. + /// </remarks> + public PolicyBag Policies { + get { + //newly created (i.e. not deserialised) SolutionItems may have a null PolicyBag + if (policies == null) + policies = new MonoDevelop.Projects.Policies.PolicyBag (); + //this is the easiest reliable place to associate a deserialised Policybag with its owner + policies.Owner = this; + return policies; + } + //setter so that a solution can deserialise the PropertyBag on its RootFolder + internal set { + policies = value; + } + } + + PolicyContainer IPolicyProvider.Policies { + get { + return Policies; + } + } + + /// <summary> + /// Gets solution item properties specific to the current user + /// </summary> + /// <remarks> + /// These properties are not stored in the project file, but in a separate file which is not to be shared + /// with other users. + /// User properties are only loaded when the project is loaded inside the IDE. + /// </remarks> + public PropertyBag UserProperties { + get { + if (userProperties == null) + userProperties = new PropertyBag (); + return userProperties; + } + } + + /// <summary> + /// Initializes the user properties of the item + /// </summary> + /// <param name='properties'> + /// Properties to be set + /// </param> + /// <exception cref='InvalidOperationException'> + /// The user properties have already been set + /// </exception> + /// <remarks> + /// This method is used by the IDE to initialize the user properties when a project is loaded. + /// </remarks> + public void LoadUserProperties (PropertyBag properties) + { + if (userProperties != null) + throw new InvalidOperationException ("User properties already loaded."); + userProperties = properties; + } + + /// <summary> + /// Gets the parent solution folder. + /// </summary> + public SolutionFolder ParentFolder { + get { + return parentFolder; + } + internal set { + if (parentFolder != null && parentFolder.ParentSolution != null && (value == null || value.ParentSolution != parentFolder.ParentSolution)) + NotifyUnboundFromSolution (false); + + parentFolder = value; + if (internalChildren != null) { + internalChildren.ParentFolder = value; + } + if (value != null && value.ParentSolution != null) { + NotifyBoundToSolution (false); + } + } + } + + // Normally, the ParentFolder setter fires OnBoundToSolution. However, when deserializing, child + // ParentFolder hierarchies can become connected before the ParentSolution becomes set. This method + // enables us to recursively fire the OnBoundToSolution call in those cases. + void NotifyBoundToSolution (bool includeInternalChildren) + { + var folder = this as SolutionFolder; + if (folder != null) { + var items = folder.GetItemsWithoutCreating (); + if (items != null) { + foreach (var item in items) { + item.NotifyBoundToSolution (true); + } + } + } + if (includeInternalChildren && internalChildren != null) { + internalChildren.NotifyBoundToSolution (true); + } + OnBoundToSolution (); + } + + void NotifyUnboundFromSolution (bool includeInternalChildren) + { + var folder = this as SolutionFolder; + if (folder != null) { + var items = folder.GetItemsWithoutCreating (); + if (items != null) { + foreach (var item in items) { + item.NotifyUnboundFromSolution (true); + } + } + } + if (includeInternalChildren && internalChildren != null) { + internalChildren.NotifyUnboundFromSolution (true); + } + OnUnboundFromSolution (); + } + + /// <summary> + /// Releases all resource used by the <see cref="MonoDevelop.Projects.SolutionItem"/> object. + /// </summary> + /// <remarks> + /// Call <see cref="Dispose"/> when you are finished using the <see cref="MonoDevelop.Projects.SolutionItem"/>. The + /// <see cref="Dispose"/> method leaves the <see cref="MonoDevelop.Projects.SolutionItem"/> in an unusable state. + /// After calling <see cref="Dispose"/>, you must release all references to the + /// <see cref="MonoDevelop.Projects.SolutionItem"/> so the garbage collector can reclaim the memory that the + /// <see cref="MonoDevelop.Projects.SolutionItem"/> was occupying. + /// </remarks> + public override void Dispose () + { + base.Dispose (); + + if (userProperties != null) { + ((IDisposable)userProperties).Dispose (); + userProperties = null; + } + + // parentFolder = null; + // parentSolution = null; + // internalChildren = null; + // policies = null; + } + + /// <summary> + /// Gets the time of the last build + /// </summary> + /// <returns> + /// The last build time. + /// </returns> + /// <param name='configuration'> + /// Configuration for which to get the last build time. + /// </param> + public DateTime GetLastBuildTime (ConfigurationSelector configuration) + { + return OnGetLastBuildTime (configuration); + } + + /// <summary> + /// Gets or sets a value indicating whether this <see cref="MonoDevelop.Projects.SolutionItem"/> needs to be reload due to changes in project or solution file + /// </summary> + /// <value> + /// <c>true</c> if needs reload; otherwise, <c>false</c>. + /// </value> + public virtual bool NeedsReload { + get { + if (ParentSolution != null) + return ParentSolution.NeedsReload; + else + return false; + } + set { + } + } + + /// <summary> + /// Registers an internal child item. + /// </summary> + /// <param name='item'> + /// An item + /// </param> + /// <remarks> + /// Some kind of projects may be composed of several child projects. + /// By registering those child projects using this method, the child + /// projects will be plugged into the parent solution infrastructure + /// (so for example, the ParentSolution property for those projects + /// will return the correct value) + /// </remarks> + protected void RegisterInternalChild (SolutionFolderItem item) + { + if (internalChildren == null) { + internalChildren = new SolutionFolder (); + internalChildren.ParentFolder = parentFolder; + } + internalChildren.Items.Add (item); + } + + /// <summary> + /// Unregisters an internal child item. + /// </summary> + /// <param name='item'> + /// The item + /// </param> + protected void UnregisterInternalChild (SolutionFolderItem item) + { + if (internalChildren != null) + internalChildren.Items.Remove (item); + } + + /// <summary> + /// Gets the string tag model description for this solution item + /// </summary> + /// <returns> + /// The string tag model description + /// </returns> + /// <param name='conf'> + /// Configuration for which to get the string tag model description + /// </param> + public virtual StringTagModelDescription GetStringTagModelDescription (ConfigurationSelector conf) + { + StringTagModelDescription model = new StringTagModelDescription (); + model.Add (GetType ()); + model.Add (typeof(Solution)); + return model; + } + + /// <summary> + /// Gets the string tag model for this solution item + /// </summary> + /// <returns> + /// The string tag model + /// </returns> + /// <param name='conf'> + /// Configuration for which to get the string tag model + /// </param> + public virtual StringTagModel GetStringTagModel (ConfigurationSelector conf) + { + StringTagModel source = new StringTagModel (); + source.Add (this); + if (ParentSolution != null) + source.Add (ParentSolution.GetStringTagModel ()); + return source; + } + + /// <summary> + /// Gets the author information for this solution item, inherited from the solution and global settings. + /// </summary> + public AuthorInformation AuthorInformation { + get { + if (ParentSolution != null) + return ParentSolution.AuthorInformation; + else + return AuthorInformation.Default; + } + } + + internal MSBuildFileFormat SolutionFormat { get; private set; } + + internal virtual void SetSolutionFormat (MSBuildFileFormat format, bool converting) + { + this.SolutionFormat = format; + } + + public virtual bool HasSlnData { + get { return false; } + } + + DataItem customSlnData; + + public virtual DataItem WriteSlnData () + { + return customSlnData; + } + + public virtual void ReadSlnData (DataItem item) + { + customSlnData = item; + } + + /// <summary> + /// Notifies that this solution item has been modified + /// </summary> + /// <param name='hint'> + /// Hint about which part of the solution item has been modified. This will typically be the property name. + /// </param> + public void NotifyModified (string hint) + { + OnModified (new SolutionItemModifiedEventArgs (this, hint)); + } + + /// <summary> + /// Raises the modified event. + /// </summary> + /// <param name='args'> + /// Arguments. + /// </param> + protected virtual void OnModified (SolutionItemModifiedEventArgs args) + { + if (Modified != null && !Disposed) + Modified (this, args); + } + + /// <summary> + /// Raises the name changed event. + /// </summary> + /// <param name='e'> + /// Arguments. + /// </param> + protected virtual void OnNameChanged (SolutionItemRenamedEventArgs e) + { + NotifyModified ("Name"); + if (NameChanged != null && !Disposed) + NameChanged (this, e); + } + + /// <summary> + /// Initializes the item handler. + /// </summary> + /// <remarks> + /// This method is called the first time an item handler is requested. + /// Subclasses should override this method use SetItemHandler to + /// assign a handler to this item. + /// </remarks> + protected virtual void InitializeItemHandler () + { + } + + /// <summary> + /// Gets the time of the last build + /// </summary> + /// <returns> + /// The last build time. + /// </returns> + /// <param name='configuration'> + /// Configuration for which to get the last build time. + /// </param> + internal protected virtual DateTime OnGetLastBuildTime (ConfigurationSelector configuration) + { + return DateTime.MinValue; + } + + /// <summary> + /// Called just after this item is bound to a solution + /// </summary> + protected virtual void OnBoundToSolution () + { + } + + /// <summary> + /// Called just before this item is removed from a solution (ParentSolution is still valid when this method is called) + /// </summary> + protected virtual void OnUnboundFromSolution () + { + } + + /// <summary> + /// Occurs when the name of the item changes + /// </summary> + public event SolutionItemRenamedEventHandler NameChanged; + + /// <summary> + /// Occurs when the item is modified. + /// </summary> + public event SolutionItemModifiedEventHandler Modified; + } + +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionFolderItemCollection.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionFolderItemCollection.cs index e6704c7a57..81b85305e1 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionFolderItemCollection.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionFolderItemCollection.cs @@ -30,7 +30,7 @@ using System.Collections.ObjectModel; namespace MonoDevelop.Projects { - public class SolutionFolderItemCollection: ItemCollection<SolutionItem> + public class SolutionFolderItemCollection: ItemCollection<SolutionFolderItem> { SolutionFolder parentFolder; @@ -43,14 +43,14 @@ namespace MonoDevelop.Projects this.parentFolder = parentFolder; } - internal void Replace (SolutionItem item, SolutionItem newItem) + internal void Replace (SolutionFolderItem item, SolutionFolderItem newItem) { int i = IndexOf (item); Items [i] = newItem; newItem.ParentFolder = parentFolder; } - protected override void OnItemAdded (SolutionItem item) + protected override void OnItemAdded (SolutionFolderItem item) { if (parentFolder != null) { // If the item belongs to another solution, remove it from there. @@ -74,7 +74,7 @@ namespace MonoDevelop.Projects } } - protected override void OnItemRemoved (SolutionItem item) + protected override void OnItemRemoved (SolutionFolderItem item) { if (parentFolder != null) { item.ParentFolder = null; @@ -82,12 +82,12 @@ namespace MonoDevelop.Projects } } - internal void InternalAdd (SolutionItem item) + internal void InternalAdd (SolutionFolderItem item) { Items.Add (item); } - internal void InternalRemove (SolutionItem item) + internal void InternalRemove (SolutionFolderItem item) { Items.Remove (item); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItem.cs index 3dd753efab..4cf410e674 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItem.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItem.cs @@ -1,4 +1,4 @@ -// SolutionItem.cs +// SolutionEntityItem.cs // // Author: // Lluis Sanchez Gual <lluis@novell.com> @@ -26,419 +26,301 @@ // using System; +using System.Linq; +using System.Xml; +using System.IO; using System.Collections; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Xml; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Reflection; +using System.Diagnostics; using System.CodeDom.Compiler; + using MonoDevelop.Core; +using MonoDevelop.Projects; using MonoDevelop.Core.Serialization; -using MonoDevelop.Projects.Extensions;
-using MonoDevelop.Core.Collections; +using MonoDevelop.Projects.Extensions; using MonoDevelop.Core.StringParsing; -using MonoDevelop.Core.Instrumentation; -using MonoDevelop.Projects.Policies; using MonoDevelop.Core.Execution; +using Mono.Addins; +using MonoDevelop.Core.Instrumentation; +using MonoDevelop.Core.Collections; +using System.Threading.Tasks; +using MonoDevelop.Projects.Formats.MSBuild; namespace MonoDevelop.Projects { - public abstract class SolutionItem: IExtendedDataItem, IBuildTarget, ILoadController, IPolicyProvider + public abstract class SolutionItem : SolutionFolderItem, IWorkspaceFileObject, IConfigurationTarget, ILoadController, IBuildTarget { - SolutionFolder parentFolder; - Solution parentSolution; - ISolutionItemHandler handler; + internal object MemoryProbe = Counters.ItemsInMemory.CreateMemoryProbe (); + int loading; - SolutionFolder internalChildren; + ProjectItemCollection items; + ProjectItemCollection wildcardItems; + ItemCollection<SolutionItem> dependencies = new ItemCollection<SolutionItem> (); + + SolutionItemEventArgs thisItemArgs; - [ProjectPathItemProperty ("BaseDirectory", DefaultValue=null)] - string baseDirectory; + FileStatusTracker<SolutionItemEventArgs> fileStatusTracker; + + FilePath fileName; + string name; - Hashtable extendedProperties; + FileFormat fileFormat; - [ItemProperty ("Policies", IsExternal = true, SkipEmpty = true)] - MonoDevelop.Projects.Policies.PolicyBag policies; + SolutionItemConfiguration activeConfiguration; + SolutionItemConfigurationCollection configurations; - [ItemProperty ("UseMSBuildEngine")] - public bool? UseMSBuildEngine { get; set; } - - PropertyBag userProperties; + public event EventHandler ConfigurationsChanged; + public event ConfigurationEventHandler DefaultConfigurationChanged; + public event ConfigurationEventHandler ConfigurationAdded; + public event ConfigurationEventHandler ConfigurationRemoved; + public event EventHandler<ProjectItemEventArgs> ProjectItemAdded; + public event EventHandler<ProjectItemEventArgs> ProjectItemRemoved; + + // When set, it means this item is saved as part of a global solution save operation + internal bool SavingSolution { get; set; } - /// <summary> - /// Initializes a new instance of the <see cref="MonoDevelop.Projects.SolutionItem"/> class. - /// </summary> - public SolutionItem() + public SolutionItem () { + var fmt = Services.ProjectService.FileFormats.GetFileFormat (MSBuildProjectService.DefaultFormat); + TypeGuid = MSBuildProjectService.GetTypeGuidForItem (this); + + SetSolutionFormat ((MSBuildFileFormat)fmt.Format, true); ProjectExtensionUtil.LoadControl (this); + items = new ProjectItemCollection (this); + wildcardItems = new ProjectItemCollection (this); + thisItemArgs = new SolutionItemEventArgs (this); + configurations = new SolutionItemConfigurationCollection (this); + configurations.ConfigurationAdded += OnConfigurationAddedToCollection; + configurations.ConfigurationRemoved += OnConfigurationRemovedFromCollection; + Counters.ItemsLoaded++; + fileStatusTracker = new FileStatusTracker<SolutionItemEventArgs> (this, OnReloadRequired, new SolutionItemEventArgs (this)); } - - /// <summary> - /// Initializes a new instance of this item, using an xml element as template - /// </summary> - /// <param name='template'> - /// The template - /// </param> - public virtual void InitializeFromTemplate (XmlElement template) - { - } - - /// <summary> - /// Gets the handler for this solution item - /// </summary> - /// <value> - /// The solution item handler. - /// </value> - /// <exception cref='InvalidOperationException'> - /// Is thrown if there isn't a ISolutionItemHandler for this solution item - /// </exception> - protected internal ISolutionItemHandler ItemHandler { + + SolutionItemExtension itemExtension; + + SolutionItemExtension ItemExtension { get { - if (handler == null) { - InitializeItemHandler (); - if (handler == null) - throw new InvalidOperationException ("No handler found for solution item of type: " + GetType ()); - } - return handler; + if (itemExtension == null) + itemExtension = ExtensionChain.GetExtension<SolutionItemExtension> (); + return itemExtension; } } - - /// <summary> - /// Sets the handler for this solution item - /// </summary> - /// <param name='handler'> - /// A handler. - /// </param> - internal virtual void SetItemHandler (ISolutionItemHandler handler) + + protected override IEnumerable<WorkspaceObjectExtension> CreateDefaultExtensions () { - if (this.handler != null) - this.handler.Dispose (); - this.handler = handler; + foreach (var e in base.CreateDefaultExtensions ()) + yield return e; + yield return new DefaultMSBuildItemExtension (); } - - internal ISolutionItemHandler GetItemHandler () + + internal protected virtual IEnumerable<string> GetItemTypeGuids () { - // Used to get the handler without lazy loading it - return this.handler; + yield return TypeGuid; } - - /// <summary> - /// Gets the author information for this solution item, inherited from the solution and global settings. - /// </summary> - public AuthorInformation AuthorInformation { - get { - if (ParentSolution != null) - return ParentSolution.AuthorInformation; - else - return AuthorInformation.Default; - } - } - - /// <summary> - /// Gets a service instance of a given type - /// </summary> - /// <returns> - /// The service. - /// </returns> - /// <typeparam name='T'> - /// Type of the service - /// </typeparam> - /// <remarks> - /// This method looks for an imlpementation of a service of the given type. - /// </remarks> - public T GetService<T> () where T: class + + public override void Dispose () { - return (T) GetService (typeof(T)); + base.Dispose (); + Counters.ItemsLoaded--; + + foreach (var item in items.Concat (wildcardItems)) { + IDisposable disp = item as IDisposable; + if (disp != null) + disp.Dispose (); + } + + // items = null; + // wildcardItems = null; + // thisItemArgs = null; + // fileStatusTracker = null; + // fileFormat = null; + // activeConfiguration = null; + // configurations = null; } - /// <summary> - /// Gets a service instance of a given type - /// </summary> - /// <returns> - /// The service. - /// </returns> - /// <param name='t'> - /// Type of the service - /// </param> - /// <remarks> - /// This method looks for an imlpementation of a service of the given type. - /// </remarks> - public virtual object GetService (Type t) + void HandleSolutionItemAdded (object sender, SolutionItemChangeEventArgs e) { - return Services.ProjectService.GetExtensionChain (this).GetService (this, t); - } - - /// <summary> - /// Gets the solution to which this item belongs - /// </summary> - public Solution ParentSolution { - get { - if (parentFolder != null) - return parentFolder.ParentSolution; - return parentSolution; - } - internal set { - parentSolution = value; - NotifyBoundToSolution (true); + if (e.Reloading && dependencies.Count > 0 && (e.SolutionItem is SolutionItem) && (e.ReplacedItem is SolutionItem)) { + int i = dependencies.IndexOf ((SolutionItem)e.ReplacedItem); + if (i != -1) + dependencies [i] = (SolutionItem) e.SolutionItem; } } - /// <summary> - /// Gets a value indicating whether this item is currently being loaded from a file - /// </summary> - /// <remarks> - /// While an item is loading, some events such as project file change events may be fired. - /// This flag can be used to check if change events are caused by data being loaded. - /// </remarks> - public bool Loading { - get { return loading > 0; } - } - - /// <summary> - /// Saves the solution item - /// </summary> - /// <param name='monitor'> - /// A progress monitor. - /// </param> - public abstract void Save (IProgressMonitor monitor); - - /// <summary> - /// Name of the solution item - /// </summary> - public abstract string Name { get; set; } - - /// <summary> - /// Gets or sets the base directory of this solution item - /// </summary> - /// <value> - /// The base directory. - /// </value> - /// <remarks> - /// The base directory is the directory where files belonging to this project - /// are placed. Notice that this directory may be different than the directory - /// where the project file is placed. - /// </remarks> - public FilePath BaseDirectory { - get { - if (baseDirectory == null) {
- FilePath dir = GetDefaultBaseDirectory (); - if (dir.IsNullOrEmpty) - dir = "."; - return dir.FullPath; - } - else - return baseDirectory; - } - set {
- FilePath def = GetDefaultBaseDirectory (); - if (value != FilePath.Null && def != FilePath.Null && value.FullPath == def.FullPath) - baseDirectory = null; - else if (string.IsNullOrEmpty (value)) - baseDirectory = null; - else - baseDirectory = value.FullPath; - NotifyModified ("BaseDirectory"); - } + void HandleSolutionItemRemoved (object sender, SolutionItemChangeEventArgs e) + { + if (!e.Reloading && (e.SolutionItem is SolutionItem)) + dependencies.Remove ((SolutionItem)e.SolutionItem); } - - /// <summary> - /// Gets the directory where this solution item is placed - /// </summary> - public FilePath ItemDirectory { - get {
- FilePath dir = GetDefaultBaseDirectory (); - if (string.IsNullOrEmpty (dir)) - dir = "."; - return dir.FullPath; - } + + void ILoadController.BeginLoad () + { + loading++; + OnBeginLoad (); } - - internal bool HasCustomBaseDirectory { - get { return baseDirectory != null; } - }
- - /// <summary> - /// Gets the default base directory. - /// </summary> - /// <remarks> - /// The base directory is the directory where files belonging to this project - /// are placed. Notice that this directory may be different than the directory - /// where the project file is placed. - /// </remarks> - protected virtual FilePath GetDefaultBaseDirectory ( ) + + void ILoadController.EndLoad () { - return ParentSolution.BaseDirectory; + loading--; + OnEndLoad (); } /// <summary> - /// Gets the identifier of this solution item + /// Called when a load operation for this solution item has started /// </summary> - /// <remarks> - /// The identifier is unique inside the solution - /// </remarks> - public string ItemId { - get { return ItemHandler.ItemId; } + protected virtual void OnBeginLoad () + { } - + /// <summary> - /// Gets extended properties. + /// Called when a load operation for this solution item has finished /// </summary> - /// <remarks> - /// This dictionary can be used by add-ins to store arbitrary information about this solution item. - /// Keys and values can be of any type. - /// If a value implements IDisposable, the value will be disposed when this solution item is disposed. - /// Values in this dictionary won't be serialized, unless they are registered as serializable using - /// the /MonoDevelop/ProjectModel/ExtendedProperties extension point. - /// </remarks> - public IDictionary ExtendedProperties { - get { return InternalGetExtendedProperties; } + protected virtual void OnEndLoad () + { + fileStatusTracker.ResetLoadTimes (); + + if (syncReleaseVersion && ParentSolution != null) + releaseVersion = ParentSolution.Version; } + + [ItemProperty ("ReleaseVersion", DefaultValue="0.1")] + string releaseVersion = "0.1"; - /// <summary> - /// Gets policies. - /// </summary> - /// <remarks> - /// Returns a policy container which can be used to query policies specific for this - /// solution item. If a policy is not defined for this item, the inherited value will be returned. - /// </remarks> - public MonoDevelop.Projects.Policies.PolicyBag Policies { + [ItemProperty ("SynchReleaseVersion", DefaultValue = true)] + bool syncReleaseVersion = true; + + public string Version { get { - //newly created (i.e. not deserialised) SolutionItems may have a null PolicyBag - if (policies == null) - policies = new MonoDevelop.Projects.Policies.PolicyBag (); - //this is the easiest reliable place to associate a deserialised Policybag with its owner - policies.Owner = this; - return policies; + // If syncReleaseVersion is set, releaseVersion will already contain the solution's version + // That's because the version must be up to date even when loading the project individually + return releaseVersion; } - //setter so that a solution can deserialise the PropertyBag on its RootFolder - internal set { - policies = value; + set { + releaseVersion = value; + NotifyModified ("Version"); } } - PolicyContainer IPolicyProvider.Policies { + public bool SyncVersionWithSolution { get { - return Policies; + return syncReleaseVersion; + } + set { + syncReleaseVersion = value; + if (syncReleaseVersion && ParentSolution != null) + Version = ParentSolution.Version; + NotifyModified ("SyncVersionWithSolution"); } } - /// <summary> - /// Gets solution item properties specific to the current user - /// </summary> - /// <remarks> - /// These properties are not stored in the project file, but in a separate file which is not to be shared - /// with other users. - /// User properties are only loaded when the project is loaded inside the IDE. - /// </remarks> - public PropertyBag UserProperties { - get { - if (userProperties == null) - userProperties = new PropertyBag (); - return userProperties; + protected override string OnGetName () + { + return name ?? string.Empty; + } + + protected override void OnSetName (string value) + { + name = value; + if (!Loading && SyncFileName) { + if (string.IsNullOrEmpty (fileName)) + FileName = value; + else { + string ext = fileName.Extension; + FileName = fileName.ParentDirectory.Combine (value) + ext; + } } } - + /// <summary> - /// Initializes the user properties of the item + /// Returns a value indicating whether the name of the solution item should be the same as the name of the file /// </summary> - /// <param name='properties'> - /// Properties to be set - /// </param> - /// <exception cref='InvalidOperationException'> - /// The user properties have already been set - /// </exception> - /// <remarks> - /// This method is used by the IDE to initialize the user properties when a project is loaded. - /// </remarks> - public void LoadUserProperties (PropertyBag properties) - { - if (userProperties != null) - throw new InvalidOperationException ("User properties already loaded."); - userProperties = properties; + /// <value> + /// <c>true</c> if the file name must be in sync with the solution item name; otherwise, <c>false</c>. + /// </value> + protected virtual bool SyncFileName { + get { return true; } } - /// <summary> - /// Gets the parent solution folder. - /// </summary> - public SolutionFolder ParentFolder { + public virtual FilePath FileName { get { - return parentFolder; + return fileName; } - internal set { - parentFolder = value; - if (internalChildren != null) { - internalChildren.ParentFolder = value; - } - if (value != null && value.ParentSolution != null) { - NotifyBoundToSolution (false); + set { + if (FileFormat != null) + value = FileFormat.GetValidFileName (this, value); + if (value != fileName) { + fileName = value; + if (SyncFileName) + Name = fileName.FileNameWithoutExtension; + NotifyModified ("FileName"); } } } - // Normally, the ParentFolder setter fires OnBoundToSolution. However, when deserializing, child - // ParentFolder hierarchies can become connected before the ParentSolution becomes set. This method - // enables us to recursively fire the OnBoundToSolution call in those cases. - void NotifyBoundToSolution (bool includeInternalChildren) - { - var folder = this as SolutionFolder; - if (folder != null) { - var items = folder.GetItemsWithoutCreating (); - if (items != null) { - foreach (var item in items) { - item.NotifyBoundToSolution (includeInternalChildren); - } + public bool Enabled { + get { return ParentSolution != null ? ParentSolution.IsSolutionItemEnabled (FileName) : true; } + set { + if (ParentSolution != null) + ParentSolution.SetSolutionItemEnabled (FileName, value); + } + } + + public FileFormat FileFormat { + get { + if (ParentSolution != null) { + if (ParentSolution.FileFormat.Format.SupportsMixedFormats && fileFormat != null) + return fileFormat; + return ParentSolution.FileFormat; } + if (fileFormat == null) + fileFormat = Services.ProjectService.GetDefaultFormat (this); + return fileFormat; } - if (includeInternalChildren && internalChildren != null) { - internalChildren.NotifyBoundToSolution (includeInternalChildren); + set { + if (ParentSolution != null && !ParentSolution.FileFormat.Format.SupportsMixedFormats) + throw new InvalidOperationException ("The file format can't be changed when the item belongs to a solution."); + InstallFormat (value); + fileFormat.Format.ConvertToFormat (this); + NeedsReload = false; + NotifyModified ("FileFormat"); } - OnBoundToSolution (); + } + + protected override object OnGetService (Type t) + { + return null; } + public ProjectItemCollection Items { + get { return items; } + } + internal ProjectItemCollection WildcardItems { + get { return wildcardItems; } + } + /// <summary> - /// Gets a value indicating whether this <see cref="MonoDevelop.Projects.SolutionItem"/> has been disposed. + /// Projects that need to be built before building this one /// </summary> - /// <value> - /// <c>true</c> if disposed; otherwise, <c>false</c>. - /// </value> - internal protected bool Disposed { get; private set; } + /// <value>The dependencies.</value> + public ItemCollection<SolutionItem> ItemDependencies { + get { return dependencies; } + } /// <summary> - /// Releases all resource used by the <see cref="MonoDevelop.Projects.SolutionItem"/> object. + /// Gets a value indicating whether this item is currently being loaded from a file /// </summary> /// <remarks> - /// Call <see cref="Dispose"/> when you are finished using the <see cref="MonoDevelop.Projects.SolutionItem"/>. The - /// <see cref="Dispose"/> method leaves the <see cref="MonoDevelop.Projects.SolutionItem"/> in an unusable state. - /// After calling <see cref="Dispose"/>, you must release all references to the - /// <see cref="MonoDevelop.Projects.SolutionItem"/> so the garbage collector can reclaim the memory that the - /// <see cref="MonoDevelop.Projects.SolutionItem"/> was occupying. + /// While an item is loading, some events such as project file change events may be fired. + /// This flag can be used to check if change events are caused by data being loaded. /// </remarks> - public virtual void Dispose () - { - Disposed = true; - - if (extendedProperties != null) { - foreach (object ob in extendedProperties.Values) { - IDisposable disp = ob as IDisposable; - if (disp != null) - disp.Dispose (); - } - extendedProperties = null; - } - if (handler != null) { - handler.Dispose (); - // handler = null; - } - if (userProperties != null) { - ((IDisposable)userProperties).Dispose (); - userProperties = null; - } - - // parentFolder = null; - // parentSolution = null; - // internalChildren = null; - // policies = null; + public bool Loading { + get { return loading > 0; } } - + /// <summary> /// Gets solution items referenced by this instance (items on which this item depends) /// </summary> @@ -448,94 +330,215 @@ namespace MonoDevelop.Projects /// <param name='configuration'> /// Configuration for which to get the referenced items /// </param> - public virtual IEnumerable<SolutionItem> GetReferencedItems (ConfigurationSelector configuration) + public IEnumerable<SolutionItem> GetReferencedItems (ConfigurationSelector configuration) + { + return ItemExtension.OnGetReferencedItems (configuration); + } + + protected virtual IEnumerable<SolutionItem> OnGetReferencedItems (ConfigurationSelector configuration) + { + return dependencies; + } + + Task IWorkspaceFileObject.ConvertToFormat (FileFormat format, bool convertChildren) + { + this.FileFormat = format; + return Task.FromResult (0); + } + + public bool SupportsFormat (FileFormat format) + { + return ItemExtension.OnGetSupportsFormat (format); + } + + protected virtual bool OnGetSupportsFormat (FileFormat format) + { + return true; + } + + internal void InstallFormat (FileFormat format) { - return new SolutionItem [0]; + fileFormat = format; + if (fileName != FilePath.Null) + fileName = fileFormat.GetValidFileName (this, fileName); } /// <summary> - /// Runs a build or execution target. + /// Initializes a new instance of this item, using an xml element as template /// </summary> - /// <returns> - /// The result of the operation - /// </returns> - /// <param name='monitor'> - /// A progress monitor - /// </param> - /// <param name='target'> - /// Name of the target - /// </param> - /// <param name='configuration'> - /// Configuration to use to run the target + /// <param name='template'> + /// The template /// </param> - public BuildResult RunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) + public void InitializeFromTemplate (XmlElement template) { - return Services.ProjectService.GetExtensionChain (this).RunTarget (monitor, this, target, configuration); + ItemExtension.OnInitializeFromTemplate (template); } - - public bool SupportsTarget (string target) + + protected virtual void OnInitializeFromTemplate (XmlElement template) { - return Services.ProjectService.GetExtensionChain (this).SupportsTarget (this, target); } - public bool SupportsBuild () + public virtual void InitializeNew (ProjectCreateInformation projectCreateInfo, XmlElement projectOptions) { - return SupportsTarget (ProjectService.BuildTarget); } - public bool SupportsExecute () + protected override FilePath GetDefaultBaseDirectory ( ) { - return Services.ProjectService.GetExtensionChain (this).SupportsExecute (this); + return ItemExtension.OnGetDefaultBaseDirectory (); } + internal Task LoadAsync (ProgressMonitor monitor, FilePath fileName, MSBuildFileFormat format) + { + FileName = fileName; + Name = Path.GetFileNameWithoutExtension (fileName); + SetSolutionFormat (format ?? new MSBuildFileFormatVS12 (), false); + return ItemExtension.OnLoad (monitor); + } + + public void Save (ProgressMonitor monitor, FilePath fileName) + { + SaveAsync (monitor, fileName).Wait (); + } + + public Task SaveAsync (ProgressMonitor monitor, FilePath fileName) + { + FileName = fileName; + return SaveAsync (monitor); + } + /// <summary> - /// Cleans the files produced by this solution item + /// Saves the solution item /// </summary> /// <param name='monitor'> - /// A progress monitor - /// </param> - /// <param name='configuration'> - /// Configuration to use to clean the project + /// A progress monitor. /// </param> - public void Clean (IProgressMonitor monitor, ConfigurationSelector configuration) + public void Save (ProgressMonitor monitor) { - ITimeTracker tt = Counters.BuildProjectTimer.BeginTiming ("Cleaning " + Name); + ItemExtension.OnSave (monitor).Wait (); + } + + + public async Task SaveAsync (ProgressMonitor monitor) + { + await ItemExtension.OnSave (monitor); + + if (HasSlnData && !SavingSolution && ParentSolution != null) { + // The project has data that has to be saved in the solution, but the solution is not being saved. Do it now. + await SolutionFormat.SlnFileFormat.WriteFile (ParentSolution.FileName, ParentSolution, false, monitor); + ParentSolution.NeedsReload = false; + } + } + + async Task DoSave (ProgressMonitor monitor) + { + if (string.IsNullOrEmpty (FileName)) + throw new InvalidOperationException ("Project does not have a file name"); + try { - //SolutionFolder handles the begin/end task itself, don't duplicate - if (this is SolutionFolder) { - RunTarget (monitor, ProjectService.CleanTarget, configuration); - return; - } - - try { - SolutionEntityItem it = this as SolutionEntityItem; - SolutionItemConfiguration iconf = it != null ? it.GetConfiguration (configuration) : null; - string confName = iconf != null ? iconf.Id : configuration.ToString (); - monitor.BeginTask (GettextCatalog.GetString ("Cleaning: {0} ({1})", Name, confName), 1); - RunTarget (monitor, ProjectService.CleanTarget, configuration); - } finally { - monitor.EndTask (); - } + fileStatusTracker.BeginSave (); + await OnSave (monitor); + OnSaved (thisItemArgs); + } finally { + fileStatusTracker.EndSave (); } - finally { - tt.End (); + FileService.NotifyFileChanged (FileName); + } + + internal bool IsSaved { + get { + return !string.IsNullOrEmpty (FileName) && File.Exists (FileName); } } + public override bool NeedsReload { + get { return fileStatusTracker.NeedsReload; } + set { fileStatusTracker.NeedsReload = value; } + } + + public virtual bool ItemFilesChanged { + get { return ItemExtension.ItemFilesChanged; } + } + + bool BaseItemFilesChanged { + get { return fileStatusTracker.ItemFilesChanged; } + } + + public bool SupportsBuild () + { + return ItemExtension.OnSupportsBuild (); + } + + protected virtual bool OnGetSupportsBuild () + { + return true; + } + + public bool SupportsExecute () + { + return ItemExtension.OnSupportsExecute (); + } + + protected virtual bool OnGetSupportsExecute () + { + return true; + } + + public virtual bool SupportsConfigurations () + { + // TODO NPM: -> extension chain + return SupportsBuild (); + } + + /// <summary> + /// Gets a value indicating whether this project is supported. + /// </summary> + /// <remarks> + /// Unsupported projects are shown in the solution pad, but operations such as building on executing won't be available. + /// </remarks> + public bool IsUnsupportedProject { get; protected set; } + + /// <summary> + /// Gets a message that explain why the project is not supported (when IsUnsupportedProject returns true) + /// </summary> + public string UnsupportedProjectMessage { + get { return IsUnsupportedProject ? (loadError ?? GettextCatalog.GetString ("Unknown project type")) : ""; } + set { loadError = value; } + } + string loadError; + + public bool NeedsBuilding (ConfigurationSelector configuration) + { + return ItemExtension.OnNeedsBuilding (configuration); + } + + internal protected virtual bool OnGetNeedsBuilding (ConfigurationSelector configuration) + { + return false; + } + + public void SetNeedsBuilding (ConfigurationSelector configuration) + { + OnSetNeedsBuilding (configuration); + } + + protected virtual void OnSetNeedsBuilding (ConfigurationSelector configuration) + { + } + /// <summary> /// Builds the solution item /// </summary> /// <param name='monitor'> /// A progress monitor /// </param> - /// <param name='configuration'> + /// <param name='solutionConfiguration'> /// Configuration to use to build the project /// </param> - public BuildResult Build (IProgressMonitor monitor, ConfigurationSelector configuration) + public Task<BuildResult> Build (ProgressMonitor monitor, ConfigurationSelector solutionConfiguration) { - return Build (monitor, configuration, false); + return Build (monitor, solutionConfiguration, false); } - + /// <summary> /// Builds the solution item /// </summary> @@ -548,54 +551,47 @@ namespace MonoDevelop.Projects /// <param name='buildReferences'> /// When set to <c>true</c>, the referenced items will be built before building this item /// </param> - public BuildResult Build (IProgressMonitor monitor, ConfigurationSelector solutionConfiguration, bool buildReferences) + public async Task<BuildResult> Build (ProgressMonitor monitor, ConfigurationSelector solutionConfiguration, bool buildReferences) { ITimeTracker tt = Counters.BuildProjectTimer.BeginTiming ("Building " + Name); try { if (!buildReferences) { - //SolutionFolder's OnRunTarget handles the begin/end task itself, don't duplicate - if (this is SolutionFolder) { - return RunTarget (monitor, ProjectService.BuildTarget, solutionConfiguration); - } - try { - SolutionEntityItem it = this as SolutionEntityItem; - SolutionItemConfiguration iconf = it != null ? it.GetConfiguration (solutionConfiguration) : null; + SolutionItemConfiguration iconf = GetConfiguration (solutionConfiguration); string confName = iconf != null ? iconf.Id : solutionConfiguration.ToString (); monitor.BeginTask (GettextCatalog.GetString ("Building: {0} ({1})", Name, confName), 1); - - // This will end calling OnBuild () - return RunTarget (monitor, ProjectService.BuildTarget, solutionConfiguration); - + + return await InternalBuild (monitor, solutionConfiguration); + } finally { monitor.EndTask (); } } - + // Get a list of all items that need to be built (including this), // and build them in the correct order - - List<SolutionItem> referenced = new List<SolutionItem> ();
- Set<SolutionItem> visited = new Set<SolutionItem> ();
+ + var referenced = new List<SolutionItem> (); + var visited = new Set<SolutionItem> (); GetBuildableReferencedItems (visited, referenced, this, solutionConfiguration); - - ReadOnlyCollection<SolutionItem> sortedReferenced = SolutionFolder.TopologicalSort (referenced, solutionConfiguration); - + + var sortedReferenced = TopologicalSort (referenced, solutionConfiguration); + BuildResult cres = new BuildResult (); cres.BuildCount = 0; - HashSet<SolutionItem> failedItems = new HashSet<SolutionItem> (); - + var failedItems = new HashSet<SolutionItem> (); + monitor.BeginTask (null, sortedReferenced.Count); - foreach (SolutionItem p in sortedReferenced) { + foreach (var p in sortedReferenced) { if (!p.ContainsReferences (failedItems, solutionConfiguration)) { - BuildResult res = p.Build (monitor, solutionConfiguration, false); + BuildResult res = await p.Build (monitor, solutionConfiguration, false); cres.Append (res); if (res.ErrorCount > 0) failedItems.Add (p); } else failedItems.Add (p); monitor.Step (1); - if (monitor.IsCancelRequested) + if (monitor.CancellationToken.IsCancellationRequested) break; } monitor.EndTask (); @@ -604,40 +600,258 @@ namespace MonoDevelop.Projects tt.End (); } } - + + async Task<BuildResult> InternalBuild (ProgressMonitor monitor, ConfigurationSelector configuration) + { + if (IsUnsupportedProject) { + var r = new BuildResult (); + r.AddError (UnsupportedProjectMessage); + return r; + } + + SolutionItemConfiguration conf = GetConfiguration (configuration) as SolutionItemConfiguration; + if (conf != null) { + if (conf.CustomCommands.CanExecute (this, CustomCommandType.BeforeBuild, null, configuration)) { + if (!await conf.CustomCommands.ExecuteCommand (monitor, this, CustomCommandType.BeforeBuild, configuration)) { + var r = new BuildResult (); + r.AddError (GettextCatalog.GetString ("Custom command execution failed")); + return r; + } + } + } + + if (monitor.CancellationToken.IsCancellationRequested) + return new BuildResult (new CompilerResults (null), ""); + + BuildResult res = await ItemExtension.OnBuild (monitor, configuration); + + if (conf != null && !monitor.CancellationToken.IsCancellationRequested && !res.Failed) { + if (conf.CustomCommands.CanExecute (this, CustomCommandType.AfterBuild, null, configuration)) { + if (!await conf.CustomCommands.ExecuteCommand (monitor, this, CustomCommandType.AfterBuild, configuration)) + res.AddError (GettextCatalog.GetString ("Custom command execution failed")); + } + } + + return res; + } + + /// <summary> + /// Builds the solution item + /// </summary> + /// <param name='monitor'> + /// A progress monitor + /// </param> + /// <param name='configuration'> + /// Configuration to use to build the project + /// </param> + protected virtual Task<BuildResult> OnBuild (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return Task.FromResult (BuildResult.Success); + } + + void GetBuildableReferencedItems (Set<SolutionItem> visited, List<SolutionItem> referenced, SolutionItem item, ConfigurationSelector configuration) + { + if (!visited.Add(item)) + return; + + referenced.Add (item); + + foreach (var ritem in item.GetReferencedItems (configuration)) + GetBuildableReferencedItems (visited, referenced, ritem, configuration); + } + internal bool ContainsReferences (HashSet<SolutionItem> items, ConfigurationSelector conf) { - foreach (SolutionItem it in GetReferencedItems (conf)) + foreach (var it in GetReferencedItems (conf)) if (items.Contains (it)) return true; return false; } /// <summary> - /// Gets the time of the last build + /// Cleans the files produced by this solution item + /// </summary> + /// <param name='monitor'> + /// A progress monitor + /// </param> + /// <param name='configuration'> + /// Configuration to use to clean the project + /// </param> + public async Task<BuildResult> Clean (ProgressMonitor monitor, ConfigurationSelector configuration) + { + ITimeTracker tt = Counters.BuildProjectTimer.BeginTiming ("Cleaning " + Name); + try { + try { + SolutionItemConfiguration iconf = GetConfiguration (configuration); + string confName = iconf != null ? iconf.Id : configuration.ToString (); + monitor.BeginTask (GettextCatalog.GetString ("Cleaning: {0} ({1})", Name, confName), 1); + + SolutionItemConfiguration conf = GetConfiguration (configuration); + if (conf != null) { + if (conf.CustomCommands.CanExecute (this, CustomCommandType.BeforeClean, null, configuration)) { + if (!await conf.CustomCommands.ExecuteCommand (monitor, this, CustomCommandType.BeforeClean, configuration)) { + var r = new BuildResult (); + r.AddError (GettextCatalog.GetString ("Custom command execution failed")); + return r; + } + } + } + + if (monitor.CancellationToken.IsCancellationRequested) + return BuildResult.Success; + + var res = await ItemExtension.OnClean (monitor, configuration); + + if (conf != null && !monitor.CancellationToken.IsCancellationRequested) { + if (conf.CustomCommands.CanExecute (this, CustomCommandType.AfterClean, null, configuration)) { + if (!await conf.CustomCommands.ExecuteCommand (monitor, this, CustomCommandType.AfterClean, configuration)) + res.AddError (GettextCatalog.GetString ("Custom command execution failed")); + } + } + return res; + + } finally { + monitor.EndTask (); + } + } + finally { + tt.End (); + } + } + + /// <summary> + /// Cleans the files produced by this solution item + /// </summary> + /// <param name='monitor'> + /// A progress monitor + /// </param> + /// <param name='configuration'> + /// Configuration to use to clean the project + /// </param> + protected virtual Task<BuildResult> OnClean (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return Task.FromResult (BuildResult.Success); + } + + /// <summary> + /// Sorts a collection of solution items, taking into account the dependencies between them /// </summary> /// <returns> - /// The last build time. + /// The sorted collection of items /// </returns> + /// <param name='items'> + /// Items to sort + /// </param> /// <param name='configuration'> - /// Configuration for which to get the last build time. + /// A configuration /// </param> - public DateTime GetLastBuildTime (ConfigurationSelector configuration) + /// <remarks> + /// This methods sorts a collection of items, ensuring that every item is placed after all the items + /// on which it depends. + /// </remarks> + public static ReadOnlyCollection<T> TopologicalSort<T> (IEnumerable<T> items, ConfigurationSelector configuration) where T: SolutionItem + { + IList<T> allItems; + allItems = items as IList<T>; + if (allItems == null) + allItems = new List<T> (items); + + List<T> sortedEntries = new List<T> (); + bool[] inserted = new bool[allItems.Count]; + bool[] triedToInsert = new bool[allItems.Count]; + for (int i = 0; i < allItems.Count; ++i) { + if (!inserted[i]) + Insert<T> (i, allItems, sortedEntries, inserted, triedToInsert, configuration); + } + return sortedEntries.AsReadOnly (); + } + + static void Insert<T> (int index, IList<T> allItems, List<T> sortedItems, bool[] inserted, bool[] triedToInsert, ConfigurationSelector solutionConfiguration) where T: SolutionItem { - return OnGetLastBuildTime (configuration); + if (triedToInsert[index]) { + throw new CyclicDependencyException (); + } + triedToInsert[index] = true; + var insertItem = allItems[index]; + + foreach (var reference in insertItem.GetReferencedItems (solutionConfiguration)) { + for (int j=0; j < allItems.Count; ++j) { + SolutionFolderItem checkItem = allItems[j]; + if (reference == checkItem) { + if (!inserted[j]) + Insert (j, allItems, sortedItems, inserted, triedToInsert, solutionConfiguration); + break; + } + } + } + sortedItems.Add (insertItem); + inserted[index] = true; } - - void GetBuildableReferencedItems (Set<SolutionItem> visited, List<SolutionItem> referenced, SolutionItem item, ConfigurationSelector configuration) - {
- if (!visited.Add(item)) + + /// <summary> + /// Executes this solution item + /// </summary> + /// <param name='monitor'> + /// A progress monitor + /// </param> + /// <param name='context'> + /// An execution context + /// </param> + /// <param name='configuration'> + /// Configuration to use to execute the item + /// </param> + public async Task Execute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + { + SolutionItemConfiguration conf = GetConfiguration (configuration) as SolutionItemConfiguration; + if (conf != null) { + ExecutionContext localContext = new ExecutionContext (Runtime.ProcessService.DefaultExecutionHandler, context.ConsoleFactory, context.ExecutionTarget); + + if (conf.CustomCommands.CanExecute (this, CustomCommandType.BeforeExecute, localContext, configuration)) { + if (!await conf.CustomCommands.ExecuteCommand (monitor, this, CustomCommandType.BeforeExecute, localContext, configuration)) + return; + } + } + + if (monitor.CancellationToken.IsCancellationRequested) return; - - referenced.Add (item); - foreach (SolutionItem ritem in item.GetReferencedItems (configuration)) - GetBuildableReferencedItems (visited, referenced, ritem, configuration); + await ItemExtension.OnExecute (monitor, context, configuration); + + if (conf != null && !monitor.CancellationToken.IsCancellationRequested) { + ExecutionContext localContext = new ExecutionContext (Runtime.ProcessService.DefaultExecutionHandler, context.ConsoleFactory, context.ExecutionTarget); + + if (conf.CustomCommands.CanExecute (this, CustomCommandType.AfterExecute, localContext, configuration)) + await conf.CustomCommands.ExecuteCommand (monitor, this, CustomCommandType.AfterExecute, localContext, configuration); + } } - + + /// <summary> + /// Determines whether this solution item can be executed using the specified context and configuration. + /// </summary> + /// <returns> + /// <c>true</c> if this instance can be executed; otherwise, <c>false</c>. + /// </returns> + /// <param name='context'> + /// An execution context + /// </param> + /// <param name='configuration'> + /// Configuration to use to execute the item + /// </param> + public bool CanExecute (ExecutionContext context, ConfigurationSelector configuration) + { + return !IsUnsupportedProject && ItemExtension.OnGetCanExecute (context, configuration); + } + + async Task DoExecute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + { + SolutionItemConfiguration conf = GetConfiguration (configuration) as SolutionItemConfiguration; + if (conf != null && conf.CustomCommands.HasCommands (CustomCommandType.Execute)) { + await conf.CustomCommands.ExecuteCommand (monitor, this, CustomCommandType.Execute, context, configuration); + return; + } + await OnExecute (monitor, context, configuration); + } + /// <summary> /// Executes this solution item /// </summary> @@ -650,11 +864,19 @@ namespace MonoDevelop.Projects /// <param name='configuration'> /// Configuration to use to execute the item /// </param> - public void Execute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + protected virtual Task OnExecute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) { - Services.ProjectService.GetExtensionChain (this).Execute (monitor, this, context, configuration); + return Task.FromResult (0); } - + + bool DoGetCanExecute (ExecutionContext context, ConfigurationSelector configuration) + { + SolutionItemConfiguration conf = GetConfiguration (configuration) as SolutionItemConfiguration; + if (conf != null && conf.CustomCommands.HasCommands (CustomCommandType.Execute)) + return conf.CustomCommands.CanExecute (this, CustomCommandType.Execute, context, configuration); + return OnGetCanExecute (context, configuration); + } + /// <summary> /// Determines whether this solution item can be executed using the specified context and configuration. /// </summary> @@ -667,11 +889,9 @@ namespace MonoDevelop.Projects /// <param name='configuration'> /// Configuration to use to execute the item /// </param> - public bool CanExecute (ExecutionContext context, ConfigurationSelector configuration) + protected virtual bool OnGetCanExecute (ExecutionContext context, ConfigurationSelector configuration) { - if (!SupportsExecute ()) - return false; - return Services.ProjectService.GetExtensionChain (this).CanExecute (this, context, configuration); + return ItemExtension.OnGetCanExecute (context, configuration); } /// <summary> @@ -681,7 +901,12 @@ namespace MonoDevelop.Projects /// <param name="configuration">The configuration.</param> public IEnumerable<ExecutionTarget> GetExecutionTargets (ConfigurationSelector configuration) { - return Services.ProjectService.GetExtensionChain (this).GetExecutionTargets (this, configuration); + return ItemExtension.OnGetExecutionTargets (configuration); + } + + protected void NotifyExecutionTargetsChanged () + { + ItemExtension.OnExecutionTargetsChanged (); } public event EventHandler ExecutionTargetsChanged; @@ -691,429 +916,480 @@ namespace MonoDevelop.Projects if (ExecutionTargetsChanged != null) ExecutionTargetsChanged (this, EventArgs.Empty); } - - /// <summary> - /// Checks if this solution item has modified files and has to be built - /// </summary> - /// <returns> - /// <c>true</c> if the solution item has to be built - /// </returns> - /// <param name='configuration'> - /// Configuration for which to do the check - /// </param> - [Obsolete ("This method will be removed in future releases")] - public bool NeedsBuilding (ConfigurationSelector configuration) + + protected virtual Task OnLoad (ProgressMonitor monitor) { - return true; + return Task.FromResult (0); } - internal bool InternalCheckNeedsBuild (ConfigurationSelector configuration) + protected internal virtual Task OnSave (ProgressMonitor monitor) { - using (Counters.NeedsBuildingTimer.BeginTiming ("NeedsBuilding check for " + Name)) { - return Services.ProjectService.GetExtensionChain (this).GetNeedsBuilding (this, configuration); - } + return Task.FromResult (0); } - /// <summary> - /// States whether this solution item needs to be built or not - /// </summary> - /// <param name='value'> - /// Whether this solution item needs to be built or not - /// </param> - /// <param name='configuration'> - /// Configuration for which to set the flag - /// </param> - [Obsolete ("This method will be removed in future releases")] - public void SetNeedsBuilding (bool value, ConfigurationSelector configuration) + public FilePath GetAbsoluteChildPath (FilePath relPath) { - // Nothing to be done. + return relPath.ToAbsolute (BaseDirectory); } - - /// <summary> - /// Gets or sets a value indicating whether this <see cref="MonoDevelop.Projects.SolutionItem"/> needs to be reload due to changes in project or solution file - /// </summary> - /// <value> - /// <c>true</c> if needs reload; otherwise, <c>false</c>. - /// </value> - public virtual bool NeedsReload { - get { - if (ParentSolution != null) - return ParentSolution.NeedsReload; - else - return false; - } - set { - } + + public FilePath GetRelativeChildPath (FilePath absPath) + { + return absPath.ToRelative (BaseDirectory); } - - /// <summary> - /// Registers an internal child item. - /// </summary> - /// <param name='item'> - /// An item - /// </param> - /// <remarks> - /// Some kind of projects may be composed of several child projects. - /// By registering those child projects using this method, the child - /// projects will be plugged into the parent solution infrastructure - /// (so for example, the ParentSolution property for those projects - /// will return the correct value) - /// </remarks> - protected void RegisterInternalChild (SolutionItem item) + + public IEnumerable<FilePath> GetItemFiles (bool includeReferencedFiles) { - if (internalChildren == null) { - internalChildren = new SolutionFolder (); - internalChildren.ParentFolder = parentFolder; - } - internalChildren.Items.Add (item); + return ItemExtension.OnGetItemFiles (includeReferencedFiles); } - - /// <summary> - /// Unregisters an internal child item. - /// </summary> - /// <param name='item'> - /// The item - /// </param> - protected void UnregisterInternalChild (SolutionItem item) + + protected virtual IEnumerable<FilePath> OnGetItemFiles (bool includeReferencedFiles) { - if (internalChildren != null) - internalChildren.Items.Remove (item); + List<FilePath> col = FileFormat.Format.GetItemFiles (this); + if (!string.IsNullOrEmpty (FileName) && !col.Contains (FileName)) + col.Add (FileName); + return col; } - - /// <summary> - /// Gets the string tag model description for this solution item - /// </summary> - /// <returns> - /// The string tag model description - /// </returns> - /// <param name='conf'> - /// Configuration for which to get the string tag model description - /// </param> - public virtual StringTagModelDescription GetStringTagModelDescription (ConfigurationSelector conf) + + protected override void OnNameChanged (SolutionItemRenamedEventArgs e) { - StringTagModelDescription model = new StringTagModelDescription (); - model.Add (GetType ()); - model.Add (typeof(Solution)); - return model; + Solution solution = this.ParentSolution; + + if (solution != null) { + foreach (DotNetProject project in solution.GetAllItems<DotNetProject>()) { + if (project == this) + continue; + + project.RenameReferences (e.OldName, e.NewName); + } + } + fileStatusTracker.ResetLoadTimes (); + base.OnNameChanged (e); } - /// <summary> - /// Gets the string tag model for this solution item - /// </summary> - /// <returns> - /// The string tag model - /// </returns> - /// <param name='conf'> - /// Configuration for which to get the string tag model - /// </param> - public virtual StringTagModel GetStringTagModel (ConfigurationSelector conf) + protected virtual void OnSaved (SolutionItemEventArgs args) { - StringTagModel source = new StringTagModel (); - source.Add (this); - if (ParentSolution != null) - source.Add (ParentSolution.GetStringTagModel ()); - return source; + if (Saved != null) + Saved (this, args); } - /// <summary> - /// Sorts a collection of solution items, taking into account the dependencies between them - /// </summary> - /// <returns> - /// The sorted collection of items - /// </returns> - /// <param name='items'> - /// Items to sort - /// </param> - /// <param name='configuration'> - /// A configuration - /// </param> - /// <remarks> - /// This methods sorts a collection of items, ensuring that every item is placed after all the items - /// on which it depends. - /// </remarks> - public static ReadOnlyCollection<T> TopologicalSort<T> (IEnumerable<T> items, ConfigurationSelector configuration) where T: SolutionItem - { - IList<T> allItems; - allItems = items as IList<T>; - if (allItems == null) - allItems = new List<T> (items); - - List<T> sortedEntries = new List<T> (); - bool[] inserted = new bool[allItems.Count]; - bool[] triedToInsert = new bool[allItems.Count]; - for (int i = 0; i < allItems.Count; ++i) { - if (!inserted[i]) - Insert<T> (i, allItems, sortedEntries, inserted, triedToInsert, configuration); + public virtual string[] SupportedPlatforms { + get { + return new string [0]; } - return sortedEntries.AsReadOnly (); } - static void Insert<T> (int index, IList<T> allItems, List<T> sortedItems, bool[] inserted, bool[] triedToInsert, ConfigurationSelector solutionConfiguration) where T: SolutionItem + public virtual SolutionItemConfiguration GetConfiguration (ConfigurationSelector configuration) { - if (triedToInsert[index]) { - throw new CyclicDependencyException (); + return (SolutionItemConfiguration) configuration.GetConfiguration (this) ?? DefaultConfiguration; + } + + ItemConfiguration IConfigurationTarget.DefaultConfiguration { + get { return DefaultConfiguration; } + set { DefaultConfiguration = (SolutionItemConfiguration) value; } + } + + public SolutionItemConfiguration DefaultConfiguration { + get { + if (activeConfiguration == null && configurations.Count > 0) { + return configurations[0]; + } + return activeConfiguration; } - triedToInsert[index] = true; - SolutionItem insertItem = allItems[index]; - - foreach (SolutionItem reference in insertItem.GetReferencedItems (solutionConfiguration)) { - for (int j=0; j < allItems.Count; ++j) { - SolutionItem checkItem = allItems[j]; - if (reference == checkItem) { - if (!inserted[j]) - Insert (j, allItems, sortedItems, inserted, triedToInsert, solutionConfiguration); - break; - } + set { + if (activeConfiguration != value) { + activeConfiguration = value; + NotifyModified ("DefaultConfiguration"); + OnDefaultConfigurationChanged (new ConfigurationEventArgs (this, value)); } } - sortedItems.Add ((T)insertItem); - inserted[index] = true; } - internal virtual IDictionary InternalGetExtendedProperties { + public string DefaultConfigurationId { get { - if (extendedProperties == null) - extendedProperties = new Hashtable (); - return extendedProperties; + if (DefaultConfiguration != null) + return DefaultConfiguration.Id; + else + return null; + } + set { + DefaultConfiguration = GetConfiguration (new ItemConfigurationSelector (value)); } } - void ILoadController.BeginLoad () + public virtual ReadOnlyCollection<string> GetConfigurations () { - loading++; - OnBeginLoad (); + List<string> configs = new List<string> (); + foreach (SolutionItemConfiguration conf in Configurations) + configs.Add (conf.Id); + return configs.AsReadOnly (); } - void ILoadController.EndLoad () - { - loading--; - OnEndLoad (); + [ItemProperty ("Configurations")] + [ItemProperty ("Configuration", ValueType=typeof(SolutionItemConfiguration), Scope="*")] + public SolutionItemConfigurationCollection Configurations { + get { + return configurations; + } } - /// <summary> - /// Called when a load operation for this solution item has started - /// </summary> - protected virtual void OnBeginLoad () - { + IItemConfigurationCollection IConfigurationTarget.Configurations { + get { + return Configurations; + } } - /// <summary> - /// Called when a load operation for this solution item has finished - /// </summary> - protected virtual void OnEndLoad () + public SolutionItemConfiguration AddNewConfiguration (string name) { + SolutionItemConfiguration config = CreateConfiguration (name); + Configurations.Add (config); + return config; } - /// <summary> - /// Notifies that this solution item has been modified - /// </summary> - /// <param name='hint'> - /// Hint about which part of the solution item has been modified. This will typically be the property name. - /// </param> - internal protected void NotifyModified (string hint) + ItemConfiguration IConfigurationTarget.CreateConfiguration (string name) { - if (!Loading) - ItemHandler.OnModified (hint); - OnModified (new SolutionItemModifiedEventArgs (this, hint)); + return CreateConfiguration (name); } - - /// <summary> - /// Raises the modified event. - /// </summary> - /// <param name='args'> - /// Arguments. - /// </param> - protected virtual void OnModified (SolutionItemModifiedEventArgs args) + + public virtual SolutionItemConfiguration CreateConfiguration (string name) { - if (Modified != null && !Disposed) - Modified (this, args); + return ItemExtension.OnCreateConfiguration (name); } - /// <summary> - /// Raises the name changed event. - /// </summary> - /// <param name='e'> - /// Arguments. - /// </param> - protected virtual void OnNameChanged (SolutionItemRenamedEventArgs e) + void OnConfigurationAddedToCollection (object ob, ConfigurationEventArgs args) { - NotifyModified ("Name"); - if (NameChanged != null && !Disposed) - NameChanged (this, e); + NotifyModified ("Configurations"); + OnConfigurationAdded (new ConfigurationEventArgs (this, args.Configuration)); + if (ConfigurationsChanged != null) + ConfigurationsChanged (this, EventArgs.Empty); + if (activeConfiguration == null) + DefaultConfigurationId = args.Configuration.Id; } - /// <summary> - /// Initializes the item handler. - /// </summary> - /// <remarks> - /// This method is called the first time an item handler is requested. - /// Subclasses should override this method use SetItemHandler to - /// assign a handler to this item. - /// </remarks> - protected virtual void InitializeItemHandler () + void OnConfigurationRemovedFromCollection (object ob, ConfigurationEventArgs args) { + if (activeConfiguration == args.Configuration) { + if (Configurations.Count > 0) + DefaultConfiguration = Configurations [0]; + else + DefaultConfiguration = null; + } + NotifyModified ("Configurations"); + OnConfigurationRemoved (new ConfigurationEventArgs (this, args.Configuration)); + if (ConfigurationsChanged != null) + ConfigurationsChanged (this, EventArgs.Empty); } - /// <summary> - /// Runs a build or execution target. - /// </summary> - /// <returns> - /// The result of the operation - /// </returns> - /// <param name='monitor'> - /// A progress monitor - /// </param> - /// <param name='target'> - /// Name of the target - /// </param> - /// <param name='configuration'> - /// Configuration to use to run the target - /// </param> - /// <remarks> - /// Subclasses can override this method to provide a custom implementation of project operations such as - /// build or clean. The default implementation delegates the execution to the more specific OnBuild - /// and OnClean methods, or to the item handler for other targets. - /// </remarks> - internal protected virtual BuildResult OnRunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) + public override StringTagModelDescription GetStringTagModelDescription (ConfigurationSelector conf) { - if (target == ProjectService.BuildTarget) - return OnBuild (monitor, configuration); - else if (target == ProjectService.CleanTarget) { - OnClean (monitor, configuration); - return new BuildResult (); - } - return ItemHandler.RunTarget (monitor, target, configuration) ?? new BuildResult (); + return ItemExtension.OnGetStringTagModelDescription (conf); } - /// <summary> - /// Cleans the files produced by this solution item - /// </summary> - /// <param name='monitor'> - /// A progress monitor - /// </param> - /// <param name='configuration'> - /// Configuration to use to clean the project - /// </param> - protected abstract void OnClean (IProgressMonitor monitor, ConfigurationSelector configuration); + StringTagModelDescription DoGetStringTagModelDescription (ConfigurationSelector conf) + { + StringTagModelDescription model = base.GetStringTagModelDescription (conf); + SolutionItemConfiguration config = GetConfiguration (conf); + if (config != null) + model.Add (config.GetType ()); + else + model.Add (typeof(SolutionItemConfiguration)); + return model; + } + + public override StringTagModel GetStringTagModel (ConfigurationSelector conf) + { + return ItemExtension.OnGetStringTagModel (conf); + } - /// <summary> - /// Builds the solution item - /// </summary> - /// <param name='monitor'> - /// A progress monitor - /// </param> - /// <param name='configuration'> - /// Configuration to use to build the project - /// </param> - protected abstract BuildResult OnBuild (IProgressMonitor monitor, ConfigurationSelector configuration); + StringTagModel DoGetStringTagModel (ConfigurationSelector conf) + { + StringTagModel source = base.GetStringTagModel (conf); + SolutionItemConfiguration config = GetConfiguration (conf); + if (config != null) + source.Add (config); + return source; + } + + internal protected override DateTime OnGetLastBuildTime (ConfigurationSelector configuration) + { + return ItemExtension.OnGetLastBuildTime (configuration); + } + + DateTime DoGetLastBuildTime (ConfigurationSelector configuration) + { + return base.OnGetLastBuildTime (configuration); + } + + internal protected virtual void OnItemsAdded (IEnumerable<ProjectItem> objs) + { + ItemExtension.OnItemsAdded (objs); + } - /// <summary> - /// Executes this solution item - /// </summary> - /// <param name='monitor'> - /// A progress monitor - /// </param> - /// <param name='context'> - /// An execution context - /// </param> - /// <param name='configuration'> - /// Configuration to use to execute the item - /// </param> - internal protected abstract void OnExecute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration); + void DoOnItemsAdded (IEnumerable<ProjectItem> objs) + { + NotifyModified ("Items"); + var args = new ProjectItemEventArgs (); + args.AddRange (objs.Select (pi => new ProjectItemEventInfo (this, pi))); + if (ProjectItemAdded != null) + ProjectItemAdded (this, args); + } + + internal protected virtual void OnItemsRemoved (IEnumerable<ProjectItem> objs) + { + ItemExtension.OnItemsRemoved (objs); + } - /// <summary> - /// Checks if this solution item has modified files and has to be built - /// </summary> - /// <returns> - /// <c>true</c> if the solution item has to be built - /// </returns> - /// <param name='configuration'> - /// Configuration for which to do the check - /// </param> - internal protected virtual bool OnGetNeedsBuilding (ConfigurationSelector configuration) + void DoOnItemsRemoved (IEnumerable<ProjectItem> objs) { - return true; + NotifyModified ("Items"); + var args = new ProjectItemEventArgs (); + args.AddRange (objs.Select (pi => new ProjectItemEventInfo (this, pi))); + if (ProjectItemRemoved != null) + ProjectItemRemoved (this, args); + } + + protected virtual void OnDefaultConfigurationChanged (ConfigurationEventArgs args) + { + ItemExtension.OnDefaultConfigurationChanged (args); } - /// <summary> - /// States whether this solution item needs to be built or not - /// </summary> - /// <param name='val'> - /// Whether this solution item needs to be built or not - /// </param> - /// <param name='configuration'> - /// Configuration for which to set the flag - /// </param> - internal protected virtual void OnSetNeedsBuilding (bool val, ConfigurationSelector configuration) + void DoOnDefaultConfigurationChanged (ConfigurationEventArgs args) + { + if (DefaultConfigurationChanged != null) + DefaultConfigurationChanged (this, args); + } + + protected virtual void OnConfigurationAdded (ConfigurationEventArgs args) { + ItemExtension.OnConfigurationAdded (args); } - /// <summary> - /// Gets the time of the last build - /// </summary> - /// <returns> - /// The last build time. - /// </returns> - /// <param name='configuration'> - /// Configuration for which to get the last build time. - /// </param> - internal protected virtual DateTime OnGetLastBuildTime (ConfigurationSelector configuration) + void DoOnConfigurationAdded (ConfigurationEventArgs args) { - return DateTime.MinValue; + if (ConfigurationAdded != null) + ConfigurationAdded (this, args); + } + + protected virtual void OnConfigurationRemoved (ConfigurationEventArgs args) + { + ItemExtension.OnConfigurationRemoved (args); } - internal protected virtual bool OnGetSupportsTarget (string target) + void DoOnConfigurationRemoved (ConfigurationEventArgs args) { - return true; + if (ConfigurationRemoved != null) + ConfigurationRemoved (this, args); } - internal protected virtual bool OnGetSupportsExecute () + protected virtual void OnReloadRequired (SolutionItemEventArgs args) { - return true; + ItemExtension.OnReloadRequired (args); + } + + void DoOnReloadRequired (SolutionItemEventArgs args) + { + fileStatusTracker.FireReloadRequired (args); } - /// <summary> - /// Determines whether this solution item can be executed using the specified context and configuration. - /// </summary> - /// <returns> - /// <c>true</c> if this instance can be executed; otherwise, <c>false</c>. - /// </returns> - /// <param name='context'> - /// An execution context - /// </param> - /// <param name='configuration'> - /// Configuration to use to execute the item - /// </param> - internal protected virtual bool OnGetCanExecute (ExecutionContext context, ConfigurationSelector configuration) + protected override void OnBoundToSolution () { - return false; + ParentSolution.SolutionItemRemoved += HandleSolutionItemRemoved; + ParentSolution.SolutionItemAdded += HandleSolutionItemAdded; + ItemExtension.OnBoundToSolution (); } - internal protected virtual IEnumerable<ExecutionTarget> OnGetExecutionTargets (ConfigurationSelector configuration) + void DoOnBoundToSolution () { - yield break; + base.OnBoundToSolution (); } - protected virtual void OnBoundToSolution () + protected override void OnUnboundFromSolution () { + ParentSolution.SolutionItemAdded -= HandleSolutionItemAdded; + ParentSolution.SolutionItemRemoved -= HandleSolutionItemRemoved; + ItemExtension.OnUnboundFromSolution (); } - internal protected virtual object OnGetService (Type t) + void DoOnUnboundFromSolution () { - return ItemHandler.GetService (t); + base.OnUnboundFromSolution (); } - /// <summary> - /// Occurs when the name of the item changes - /// </summary> - public event SolutionItemRenamedEventHandler NameChanged; + + public event SolutionItemEventHandler Saved; - /// <summary> - /// Occurs when the item is modified. - /// </summary> - public event SolutionItemModifiedEventHandler Modified; - } + class DefaultMSBuildItemExtension: SolutionItemExtension + { + internal protected override void OnInitializeFromTemplate (XmlElement template) + { + Item.OnInitializeFromTemplate (template); + } + + internal protected override FilePath OnGetDefaultBaseDirectory () + { + return Item.FileName.IsNullOrEmpty ? FilePath.Empty : Item.FileName.ParentDirectory; + } + + internal protected override IEnumerable<SolutionItem> OnGetReferencedItems (ConfigurationSelector configuration) + { + return Item.OnGetReferencedItems (configuration); + } + + internal protected override StringTagModelDescription OnGetStringTagModelDescription (ConfigurationSelector conf) + { + return Item.DoGetStringTagModelDescription (conf); + } + + internal protected override StringTagModel OnGetStringTagModel (ConfigurationSelector conf) + { + return Item.DoGetStringTagModel (conf); + } + + internal protected override bool OnGetSupportsFormat (FileFormat format) + { + return Item.OnGetSupportsFormat (format); + } + + internal protected override IEnumerable<FilePath> OnGetItemFiles (bool includeReferencedFiles) + { + return Item.OnGetItemFiles (includeReferencedFiles); + } + + internal protected override SolutionItemConfiguration OnCreateConfiguration (string name) + { + return new SolutionItemConfiguration (name); + } + + internal protected override string[] SupportedPlatforms { + get { + return new string [0]; + } + } + + internal protected override DateTime OnGetLastBuildTime (ConfigurationSelector configuration) + { + return Item.DoGetLastBuildTime (configuration); + } + + internal protected override Task OnLoad (ProgressMonitor monitor) + { + return Item.OnLoad (monitor); + } + + internal protected override Task OnSave (ProgressMonitor monitor) + { + return Item.DoSave (monitor); + } + + internal protected override bool OnSupportsBuild () + { + return Item.OnGetSupportsBuild (); + } + + internal protected override bool OnSupportsExecute () + { + return Item.OnGetSupportsExecute (); + } + + internal protected override Task OnExecute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + { + return Item.DoExecute (monitor, context, configuration); + } + + internal protected override bool OnGetCanExecute (ExecutionContext context, ConfigurationSelector configuration) + { + return Item.DoGetCanExecute (context, configuration); + } + + internal protected override IEnumerable<ExecutionTarget> OnGetExecutionTargets (ConfigurationSelector configuration) + { + yield break; + } + + internal protected override void OnExecutionTargetsChanged () + { + Item.OnExecutionTargetsChanged (); + } + + internal protected override void OnReloadRequired (SolutionItemEventArgs args) + { + Item.DoOnReloadRequired (args); + } + + internal protected override void OnItemsAdded (IEnumerable<ProjectItem> objs) + { + Item.DoOnItemsAdded (objs); + } + + internal protected override void OnItemsRemoved (IEnumerable<ProjectItem> objs) + { + Item.DoOnItemsRemoved (objs); + } + + internal protected override void OnDefaultConfigurationChanged (ConfigurationEventArgs args) + { + Item.DoOnDefaultConfigurationChanged (args); + } + + internal protected override void OnBoundToSolution () + { + Item.DoOnBoundToSolution (); + } + + internal protected override void OnUnboundFromSolution () + { + Item.DoOnUnboundFromSolution (); + } + + internal protected override void OnConfigurationAdded (ConfigurationEventArgs args) + { + Item.DoOnConfigurationAdded (args); + } + + internal protected override void OnConfigurationRemoved (ConfigurationEventArgs args) + { + Item.DoOnConfigurationRemoved (args); + } + + internal protected override void OnModified (SolutionItemModifiedEventArgs args) + { + Item.OnModified (args); + } + + internal protected override void OnNameChanged (SolutionItemRenamedEventArgs e) + { + Item.OnNameChanged (e); + } + + internal protected override IconId StockIcon { + get { + return "md-project"; + } + } + + internal protected override bool ItemFilesChanged { + get { + return Item.BaseItemFilesChanged; + } + } + + internal protected override Task<BuildResult> OnBuild (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return Item.OnBuild (monitor, configuration); + } + + internal protected override Task<BuildResult> OnClean (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return Item.OnClean (monitor, configuration); + } + + internal protected override bool OnNeedsBuilding (ConfigurationSelector configuration) + { + return Item.OnGetNeedsBuilding (configuration); + } + } + } + [Mono.Addins.Extension] class SolutionItemTagProvider: StringTagProvider<SolutionItem>, IStringTagProvider { @@ -1126,32 +1402,37 @@ namespace MonoDevelop.Projects yield return new StringTagDescription ("AuthorCopyright", "Project Author Copyright"); yield return new StringTagDescription ("AuthorCompany", "Project Author Company"); yield return new StringTagDescription ("AuthorTrademark", "Project Trademark"); + yield return new StringTagDescription ("ProjectFile", "Project File"); } - + public override object GetTagValue (SolutionItem item, string tag) { switch (tag) { - case "ITEMNAME": - case "PROJECTNAME": - return item.Name; - case "AUTHORCOPYRIGHT": - AuthorInformation authorInfo = item.AuthorInformation ?? AuthorInformation.Default; - return authorInfo.Copyright; - case "AUTHORCOMPANY": - authorInfo = item.AuthorInformation ?? AuthorInformation.Default; - return authorInfo.Company; - case "AUTHORTRADEMARK": - authorInfo = item.AuthorInformation ?? AuthorInformation.Default; - return authorInfo.Trademark; - case "AUTHOREMAIL": - authorInfo = item.AuthorInformation ?? AuthorInformation.Default; - return authorInfo.Email; - case "AUTHORNAME": - authorInfo = item.AuthorInformation ?? AuthorInformation.Default; - return authorInfo.Name; - case "ITEMDIR": - case "PROJECTDIR": - return item.BaseDirectory; + case "ITEMNAME": + case "PROJECTNAME": + return item.Name; + case "AUTHORCOPYRIGHT": + AuthorInformation authorInfo = item.AuthorInformation ?? AuthorInformation.Default; + return authorInfo.Copyright; + case "AUTHORCOMPANY": + authorInfo = item.AuthorInformation ?? AuthorInformation.Default; + return authorInfo.Company; + case "AUTHORTRADEMARK": + authorInfo = item.AuthorInformation ?? AuthorInformation.Default; + return authorInfo.Trademark; + case "AUTHOREMAIL": + authorInfo = item.AuthorInformation ?? AuthorInformation.Default; + return authorInfo.Email; + case "AUTHORNAME": + authorInfo = item.AuthorInformation ?? AuthorInformation.Default; + return authorInfo.Name; + case "ITEMDIR": + case "PROJECTDIR": + return item.BaseDirectory; + case "ITEMFILE": + case "PROJECTFILE": + case "PROJECTFILENAME": + return item.FileName; } throw new NotSupportedException (); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemConfiguration.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemConfiguration.cs index 8ec16e80e2..49bdaaa7a0 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemConfiguration.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemConfiguration.cs @@ -37,7 +37,7 @@ namespace MonoDevelop.Projects [DataItem (FallbackType=typeof(UnknownConfiguration))] public class SolutionItemConfiguration : ItemConfiguration { - SolutionEntityItem parentItem; + SolutionItem parentItem; public SolutionItemConfiguration () { @@ -47,7 +47,7 @@ namespace MonoDevelop.Projects { } - public SolutionEntityItem ParentItem { + public SolutionItem ParentItem { get { return parentItem; } } @@ -58,7 +58,7 @@ namespace MonoDevelop.Projects ?? configs.FirstOrDefault (c => Name == c.Name && (c.Platform == "" || c.Platform == "Any CPU")); } - internal void SetParentItem (SolutionEntityItem item) + internal void SetParentItem (SolutionItem item) { parentItem = item; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemConfigurationCollection.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemConfigurationCollection.cs index a470671b40..0e0a37dd06 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemConfigurationCollection.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemConfigurationCollection.cs @@ -38,13 +38,13 @@ namespace MonoDevelop.Projects { public class SolutionItemConfigurationCollection : ItemConfigurationCollection<SolutionItemConfiguration> { - SolutionEntityItem parentItem; + SolutionItem parentItem; public SolutionItemConfigurationCollection () { } - internal SolutionItemConfigurationCollection (SolutionEntityItem parentItem) + internal SolutionItemConfigurationCollection (SolutionItem parentItem) { this.parentItem = parentItem; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemEventArgs.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemEventArgs.cs index d9b34ca114..0ca212bfdd 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemEventArgs.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemEventArgs.cs @@ -36,10 +36,10 @@ namespace MonoDevelop.Projects public class SolutionItemEventArgs : EventArgs { - SolutionItem entry; + SolutionFolderItem entry; Solution solution; - public SolutionItem SolutionItem { + public SolutionFolderItem SolutionItem { get { return entry; } @@ -51,12 +51,12 @@ namespace MonoDevelop.Projects } } - public SolutionItemEventArgs (SolutionItem entry) + public SolutionItemEventArgs (SolutionFolderItem entry) { this.entry = entry; } - public SolutionItemEventArgs (SolutionItem entry, Solution solution) + public SolutionItemEventArgs (SolutionFolderItem entry, Solution solution) { this.solution = solution; this.entry = entry; @@ -69,7 +69,7 @@ namespace MonoDevelop.Projects { bool reloading; - public SolutionItemChangeEventArgs (SolutionItem item, Solution parentSolution, bool reloading): base (item, parentSolution) + public SolutionItemChangeEventArgs (SolutionFolderItem item, Solution parentSolution, bool reloading): base (item, parentSolution) { this.reloading = reloading; } @@ -82,14 +82,14 @@ namespace MonoDevelop.Projects /// When Reloading is true, it returns the original solution item that is being reloaded /// </summary> /// <value>The replaced item.</value> - public SolutionItem ReplacedItem { get; internal set; } + public SolutionFolderItem ReplacedItem { get; internal set; } } public delegate void SolutionItemModifiedEventHandler (object sender, SolutionItemModifiedEventArgs e); public class SolutionItemModifiedEventArgs: EventArgsChain<SolutionItemModifiedEventInfo> { - public SolutionItemModifiedEventArgs (SolutionItem item, string hint) + public SolutionItemModifiedEventArgs (SolutionFolderItem item, string hint) { Add (new SolutionItemModifiedEventInfo (item, hint)); } @@ -99,7 +99,7 @@ namespace MonoDevelop.Projects { string hint; - public SolutionItemModifiedEventInfo (SolutionItem item, string hint): base (item) + public SolutionItemModifiedEventInfo (SolutionFolderItem item, string hint): base (item) { this.hint = hint; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemExtension.cs new file mode 100644 index 0000000000..cda42c19c3 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemExtension.cs @@ -0,0 +1,289 @@ +// +// SolutionItemExtension.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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; +using MonoDevelop.Core.StringParsing; +using MonoDevelop.Core.Execution; +using System.Xml; +using MonoDevelop.Projects.Formats.MSBuild; +using MonoDevelop.Projects.Extensions; +using System.Threading.Tasks; +using System.Linq; + +namespace MonoDevelop.Projects +{ + public class SolutionItemExtension: WorkspaceObjectExtension, ILoadController + { + SolutionItemExtension next; + + internal string FlavorGuid { get; set; } + + internal protected override void InitializeChain (ChainedExtension next) + { + base.InitializeChain (next); + this.next = FindNextImplementation<SolutionItemExtension> (next); + } + + internal protected override bool SupportsObject (WorkspaceObject item) + { + var p = item as SolutionItem; + if (p == null) + return false; + + return FlavorGuid == null || p.GetItemTypeGuids ().Any (id => id.Equals (FlavorGuid, StringComparison.OrdinalIgnoreCase)); + } + + #region ILoadController implementation + + void ILoadController.BeginLoad () + { + OnBeginLoad (); + } + + void ILoadController.EndLoad () + { + OnEndLoad (); + } + + #endregion + + public SolutionItem Item { + get { return (SolutionItem) Owner; } + } + + internal protected override void Initialize () + { + base.Initialize (); + ProjectExtensionUtil.LoadControl (this); + } + + internal protected virtual void OnInitializeNew (string languageName, ProjectCreateInformation info, XmlElement projectOptions) + { + next.OnInitializeNew (languageName, info, projectOptions); + } + + internal protected virtual void OnInitializeFromTemplate (XmlElement template) + { + next.OnInitializeFromTemplate (template); + } + + #region Project properties + + internal protected virtual IconId StockIcon { + get { + return next.StockIcon; + } + } + #endregion + + #region Project model + + internal protected virtual FilePath OnGetDefaultBaseDirectory () + { + return next.OnGetDefaultBaseDirectory (); + } + + internal protected virtual IEnumerable<SolutionItem> OnGetReferencedItems (ConfigurationSelector configuration) + { + return next.OnGetReferencedItems (configuration); + } + + internal protected virtual StringTagModelDescription OnGetStringTagModelDescription (ConfigurationSelector conf) + { + return next.OnGetStringTagModelDescription (conf); + } + + internal protected virtual StringTagModel OnGetStringTagModel (ConfigurationSelector conf) + { + return next.OnGetStringTagModel (conf); + } + + internal protected virtual bool OnGetSupportsFormat (FileFormat format) + { + return next.OnGetSupportsFormat (format); + } + + internal protected virtual IEnumerable<FilePath> OnGetItemFiles (bool includeReferencedFiles) + { + return next.OnGetItemFiles (includeReferencedFiles); + } + + internal protected virtual bool ItemFilesChanged { + get { + return next.ItemFilesChanged; + } + } + + internal protected virtual SolutionItemConfiguration OnCreateConfiguration (string name) + { + return next.OnCreateConfiguration (name); + } + + internal protected virtual string[] SupportedPlatforms { + get { + return next.SupportedPlatforms; + } + } + + #endregion + + #region Building + + internal protected virtual bool OnSupportsBuild () + { + return next.OnSupportsBuild (); + } + + internal protected virtual bool OnSupportsExecute () + { + return next.OnSupportsExecute (); + } + + internal protected virtual Task<BuildResult> OnClean (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return next.OnClean (monitor, configuration); + } + + internal protected virtual bool OnNeedsBuilding (ConfigurationSelector configuration) + { + return next.OnNeedsBuilding (configuration); + } + + internal protected virtual Task<BuildResult> OnBuild (ProgressMonitor monitor, ConfigurationSelector configuration) + { + return next.OnBuild (monitor, configuration); + } + + internal protected virtual DateTime OnGetLastBuildTime (ConfigurationSelector configuration) + { + return next.OnGetLastBuildTime (configuration); + } + + #endregion + + #region Load / Save + + internal protected virtual Task OnLoad (ProgressMonitor monitor) + { + return next.OnLoad (monitor); + } + + internal protected virtual Task OnSave (ProgressMonitor monitor) + { + return next.OnSave (monitor); + } + + protected virtual void OnBeginLoad () + { + } + + protected virtual void OnEndLoad () + { + } + + #endregion + + #region Execution + + internal protected virtual Task OnExecute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + { + return next.OnExecute (monitor, context, configuration); + } + + internal protected virtual bool OnGetCanExecute (ExecutionContext context, ConfigurationSelector configuration) + { + return next.OnGetCanExecute (context, configuration); + } + + internal protected virtual IEnumerable<ExecutionTarget> OnGetExecutionTargets (ConfigurationSelector configuration) + { + return next.OnGetExecutionTargets (configuration); + } + + internal protected virtual void OnExecutionTargetsChanged () + { + next.OnExecutionTargetsChanged (); + } + + #endregion + + #region Events + + internal protected virtual void OnReloadRequired (SolutionItemEventArgs args) + { + next.OnReloadRequired (args); + } + + internal protected virtual void OnItemsAdded (IEnumerable<ProjectItem> objs) + { + next.OnItemsAdded (objs); + } + + internal protected virtual void OnItemsRemoved (IEnumerable<ProjectItem> objs) + { + next.OnItemsRemoved (objs); + } + + internal protected virtual void OnDefaultConfigurationChanged (ConfigurationEventArgs args) + { + next.OnDefaultConfigurationChanged (args); + } + + internal protected virtual void OnBoundToSolution () + { + next.OnBoundToSolution (); + } + + internal protected virtual void OnUnboundFromSolution () + { + next.OnUnboundFromSolution (); + } + + internal protected virtual void OnConfigurationAdded (ConfigurationEventArgs args) + { + next.OnConfigurationAdded (args); + } + + internal protected virtual void OnConfigurationRemoved (ConfigurationEventArgs args) + { + next.OnConfigurationRemoved (args); + } + + internal protected virtual void OnModified (SolutionItemModifiedEventArgs args) + { + next.OnModified (args); + } + + internal protected virtual void OnNameChanged (SolutionItemRenamedEventArgs e) + { + next.OnNameChanged (e); + } + + #endregion + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemFactory.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemFactory.cs new file mode 100644 index 0000000000..9237ae7de1 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemFactory.cs @@ -0,0 +1,36 @@ +// +// SolutionItemFactory.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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.Threading.Tasks; + +namespace MonoDevelop.Projects +{ + public abstract class SolutionItemFactory + { + public abstract Task<SolutionItem> CreateItem (string fileName, string typeGuid); + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemReference.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemReference.cs index d50f388d81..7abc72f987 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemReference.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItemReference.cs @@ -43,10 +43,10 @@ namespace MonoDevelop.Projects { } - public SolutionItemReference (SolutionItem item) + public SolutionItemReference (SolutionFolderItem item) { - if (item is SolutionEntityItem) { - path = ((SolutionEntityItem)item).FileName; + if (item is SolutionItem) { + path = ((SolutionItem)item).FileName; } else { path = item.ParentSolution.FileName; if ((item is SolutionFolder) && ((SolutionFolder)item).IsRoot) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownProject.cs index 581d8a1b6a..e2142229bd 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownProject.cs @@ -1,4 +1,4 @@ -// +// // UnknownProject.cs // // Author: @@ -26,33 +26,31 @@ using System; using MonoDevelop.Core; using System.Collections.Generic; +using System.Threading.Tasks; namespace MonoDevelop.Projects { public class UnknownProject: Project { - string loadError = string.Empty; - bool unloaded; - // Store the file name locally to avoid the file format to change it FilePath fileName; public UnknownProject () { + IsUnsupportedProject = true; NeedsReload = false; - loadError = GettextCatalog.GetString ("Unknown project type"); } public UnknownProject (FilePath file, string loadError): this () { NeedsReload = false; FileName = file; - this.loadError = loadError; + UnsupportedProjectMessage = loadError; } - public override IEnumerable<string> GetProjectTypes () + public override bool SupportsConfigurations () { - yield return ""; + return true; } public override FilePath FileName { @@ -67,55 +65,36 @@ namespace MonoDevelop.Projects } } - - public string LoadError { - get { return unloaded ? GettextCatalog.GetString ("Unavailable") : loadError; } - set { loadError = value; } - } - - public bool UnloadedEntry { - get { return unloaded; } - set { unloaded = value; } - } - - public override string Name { - get { - if (!FileName.IsNullOrEmpty) - return FileName.FileNameWithoutExtension; - else - return GettextCatalog.GetString ("Unknown entry"); - } - set { } + protected override string OnGetName () + { + if (!FileName.IsNullOrEmpty) + return FileName.FileNameWithoutExtension; + else + return GettextCatalog.GetString ("Unknown entry"); } - internal protected override bool OnGetSupportsTarget (string target) + protected override bool OnGetSupportsTarget (string target) { // We can't do anything with unsupported projects, other than display them in the solution pad return false; } - protected override void OnClean (IProgressMonitor monitor, ConfigurationSelector configuration) + protected override Task<BuildResult> OnClean (ProgressMonitor monitor, ConfigurationSelector configuration) { + return Task.FromResult (BuildResult.Success); } - protected override BuildResult OnBuild (IProgressMonitor monitor, ConfigurationSelector configuration) + protected override Task<BuildResult> OnBuild (ProgressMonitor monitor, ConfigurationSelector configuration) { var r = new BuildResult (); - r.AddError (loadError); - return r; - } - - protected internal override void OnExecute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) - { - } - - protected internal override bool OnGetNeedsBuilding (ConfigurationSelector configuration) - { - return false; + r.AddError (UnsupportedProjectMessage); + return Task.FromResult (r); } - protected internal override void OnSetNeedsBuilding (bool value, ConfigurationSelector configuration) + protected override Task OnExecute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) { + return new Task (delegate { + }); } public override SolutionItemConfiguration CreateConfiguration (string name) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownSolutionItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownSolutionItem.cs index 08d8697279..9ab6a3102b 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownSolutionItem.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownSolutionItem.cs @@ -28,10 +28,11 @@ using System; using MonoDevelop.Core; +using System.Threading.Tasks; namespace MonoDevelop.Projects { - public class UnknownSolutionItem: SolutionEntityItem + public class UnknownSolutionItem: SolutionItem { string loadError = string.Empty; bool unloaded; @@ -41,7 +42,15 @@ namespace MonoDevelop.Projects public UnknownSolutionItem () { + Initialize (this); NeedsReload = false; + IsUnsupportedProject = true; + } + + public override bool SupportsConfigurations () + { + // The item is unknown, but we still want to read/write its configurations + return true; } public override FilePath FileName { @@ -70,48 +79,30 @@ namespace MonoDevelop.Projects loadError = GettextCatalog.GetString ("Unavailable"); } } - - public override string Name { - get { - if (!FileName.IsNullOrEmpty) - return FileName.FileNameWithoutExtension; - else - return GettextCatalog.GetString ("Unknown entry"); - } - set { } - } - internal protected override bool OnGetSupportsTarget (string target) + protected override string OnGetName () { - return false; + if (!FileName.IsNullOrEmpty) + return FileName.FileNameWithoutExtension; + else + return GettextCatalog.GetString ("Unknown entry"); } - - protected override void OnClean (IProgressMonitor monitor, ConfigurationSelector configuration) + + protected override Task<BuildResult> OnClean (ProgressMonitor monitor, ConfigurationSelector configuration) { + return Task.FromResult (BuildResult.Success); } - protected override BuildResult OnBuild (IProgressMonitor monitor, ConfigurationSelector configuration) + protected override Task<BuildResult> OnBuild (ProgressMonitor monitor, ConfigurationSelector configuration) { var r = new BuildResult (); r.AddError ("Project unavailable"); - return r; - } - - protected internal override void OnExecute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) - { - } - - protected internal override bool OnGetNeedsBuilding (ConfigurationSelector configuration) - { - return false; - } - - protected internal override void OnSetNeedsBuilding (bool value, ConfigurationSelector configuration) - { + return Task.FromResult (r); } - protected internal override void OnSave (IProgressMonitor monitor) + protected internal override Task OnSave (ProgressMonitor monitor) { + return Task.FromResult (0); } } @@ -119,6 +110,7 @@ namespace MonoDevelop.Projects { public UnloadedSolutionItem () { + Initialize (this); UnloadedEntry = true; } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownWorkspaceItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownWorkspaceItem.cs index a80b990f9c..3c598966a1 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownWorkspaceItem.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownWorkspaceItem.cs @@ -27,6 +27,7 @@ using System; using MonoDevelop.Core; +using System.Threading.Tasks; namespace MonoDevelop.Projects { @@ -40,6 +41,7 @@ namespace MonoDevelop.Projects public UnknownWorkspaceItem () { NeedsReload = false; + Initialize (this); } public string LoadError { @@ -52,19 +54,17 @@ namespace MonoDevelop.Projects set { unloaded = value; } } - protected internal override void OnSave (IProgressMonitor monitor) + protected internal override Task OnSave (ProgressMonitor monitor) { - Services.ProjectService.InternalWriteWorkspaceItem (monitor, FileName, this); + return Services.ProjectService.InternalWriteWorkspaceItem (monitor, FileName, this); } - public override string Name { - get { - if (!FileName.IsNullOrEmpty) - return FileName.FileNameWithoutExtension; - else - return GettextCatalog.GetString ("Unknown entry"); - } - set { } + protected override string OnGetName () + { + if (!FileName.IsNullOrEmpty) + return FileName.FileNameWithoutExtension; + else + return GettextCatalog.GetString ("Unknown entry"); } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Workspace.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Workspace.cs index a59cdab116..c75837c221 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Workspace.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Workspace.cs @@ -33,13 +33,20 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using MonoDevelop.Core; using MonoDevelop.Core.Serialization; +using System.Linq; +using System.Threading.Tasks; namespace MonoDevelop.Projects { [ProjectModelDataItem] - public class Workspace: WorkspaceItem, ICustomDataItem + public class Workspace: WorkspaceItem, ICustomDataItem, IBuildTarget { WorkspaceItemCollection items; + + public Workspace () + { + Initialize (this); + } public override void Dispose () { @@ -47,6 +54,37 @@ namespace MonoDevelop.Projects foreach (WorkspaceItem it in Items) it.Dispose (); } + + public async Task<BuildResult> Build (ProgressMonitor monitor, ConfigurationSelector configuration, bool buildReferencedTargets = false) + { + var res = new BuildResult { BuildCount = 0 }; + foreach (var bt in Items.OfType<IBuildTarget> ()) + res.Append (await bt.Build (monitor, configuration)); + return res; + } + + public async Task<BuildResult> Clean (ProgressMonitor monitor, ConfigurationSelector configuration) + { + var res = new BuildResult { BuildCount = 0 }; + foreach (var bt in Items.OfType<IBuildTarget> ()) + res.Append (await bt.Clean (monitor, configuration)); + return res; + } + + public Task Execute (ProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) + { + throw new NotSupportedException (); + } + + public bool CanExecute (ExecutionContext context, ConfigurationSelector configuration) + { + return false; + } + + public bool NeedsBuilding (ConfigurationSelector configuration) + { + return Items.OfType<IBuildTarget> ().Any (t => t.NeedsBuilding (configuration)); + } public override ReadOnlyCollection<string> GetConfigurations () { @@ -67,35 +105,12 @@ namespace MonoDevelop.Projects return items; } } - - public override ReadOnlyCollection<T> GetAllItems<T> () - { - List<T> list = new List<T> (); - GetAllItems<T> (list, this); - return list.AsReadOnly (); - } - - void GetAllItems<T> (List<T> list, WorkspaceItem item) where T: WorkspaceItem + + protected override IEnumerable<WorkspaceObject> OnGetChildren () { - if (item is T) - list.Add ((T) item); - - if (item is Workspace) { - foreach (WorkspaceItem citem in ((Workspace)item).Items) - GetAllItems<T> (list, citem); - } + return Items; } - public override SolutionEntityItem FindSolutionItem (string fileName) - { - foreach (WorkspaceItem it in Items) { - SolutionEntityItem si = it.FindSolutionItem (fileName); - if (si != null) - return si; - } - return null; - }
- [Obsolete("Use GetProjectsContainingFile() (plural) instead")]
public override Project GetProjectContainingFile (FilePath fileName) { @@ -116,7 +131,7 @@ namespace MonoDevelop.Projects } } - public override bool ContainsItem (IWorkspaceObject obj) + public override bool ContainsItem (WorkspaceObject obj) { if (base.ContainsItem (obj)) return true; @@ -129,53 +144,16 @@ namespace MonoDevelop.Projects } - public override ReadOnlyCollection<T> GetAllSolutionItems<T> () - { - List<T> list = new List<T> (); - foreach (WorkspaceItem it in Items) { - list.AddRange (it.GetAllSolutionItems<T> ()); - } - return list.AsReadOnly (); - } - - public override void ConvertToFormat (FileFormat format, bool convertChildren) + public async override Task ConvertToFormat (FileFormat format, bool convertChildren) { - base.ConvertToFormat (format, convertChildren); + await base.ConvertToFormat (format, convertChildren); if (convertChildren) { foreach (WorkspaceItem it in Items) - it.ConvertToFormat (format, true); + await it.ConvertToFormat (format, true); } } - - internal protected override BuildResult OnRunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) - { - BuildResult result = null; - monitor.BeginTask (null, Items.Count); - try { - foreach (WorkspaceItem it in Items) { - BuildResult res = it.RunTarget (monitor, target, configuration); - if (res != null) { - if (result == null) { - result = new BuildResult (); - result.BuildCount = 0; - } - result.Append (res); - } - monitor.Step (1); - } - } finally { - monitor.EndTask (); - } - return result; - } - - protected internal override void OnExecute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) - { - throw new NotImplementedException (); - } - - public WorkspaceItem ReloadItem (IProgressMonitor monitor, WorkspaceItem item) + public async Task<WorkspaceItem> ReloadItem (ProgressMonitor monitor, WorkspaceItem item) { if (Items.IndexOf (item) == -1) throw new InvalidOperationException ("Item '" + item.Name + "' does not belong to workspace '" + Name + "'"); @@ -184,7 +162,7 @@ namespace MonoDevelop.Projects WorkspaceItem newItem; try { - newItem = Services.ProjectService.ReadWorkspaceItem (monitor, item.FileName); + newItem = await Services.ProjectService.ReadWorkspaceItem (monitor, item.FileName); } catch (Exception ex) { UnknownWorkspaceItem e = new UnknownWorkspaceItem (); e.LoadError = ex.Message; @@ -203,9 +181,9 @@ namespace MonoDevelop.Projects return newItem; }
- public override List<FilePath> GetItemFiles (bool includeReferencedFiles) + protected override IEnumerable<FilePath> OnGetItemFiles (bool includeReferencedFiles) {
- List<FilePath> list = base.GetItemFiles (includeReferencedFiles); + List<FilePath> list = base.OnGetItemFiles (includeReferencedFiles).ToList (); if (includeReferencedFiles) { foreach (WorkspaceItem it in Items) list.AddRange (it.GetItemFiles (true)); @@ -271,20 +249,20 @@ namespace MonoDevelop.Projects return data; } - void ICustomDataItem.Deserialize (ITypeSerializer handler, DataCollection data) + async void ICustomDataItem.Deserialize (ITypeSerializer handler, DataCollection data) { DataItem items = (DataItem) data.Extract ("Items"); handler.Deserialize (this, data); - IProgressMonitor monitor = handler.SerializationContext.ProgressMonitor; + ProgressMonitor monitor = handler.SerializationContext.ProgressMonitor; if (monitor == null) - monitor = new MonoDevelop.Core.ProgressMonitoring.NullProgressMonitor (); + monitor = new ProgressMonitor (); if (items != null) { string baseDir = Path.GetDirectoryName (handler.SerializationContext.BaseFile); monitor.BeginTask (null, items.ItemData.Count); try { foreach (DataValue item in items.ItemData) { string file = Path.Combine (baseDir, item.Value); - WorkspaceItem it = Services.ProjectService.ReadWorkspaceItem (monitor, file); + WorkspaceItem it = await Services.ProjectService.ReadWorkspaceItem (monitor, file); if (it != null) Items.Add (it); monitor.Step (1); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceItem.cs index 8455a52481..d570d0792e 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceItem.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceItem.cs @@ -41,15 +41,17 @@ using MonoDevelop.Core.Serialization; using MonoDevelop.Core.StringParsing; using MonoDevelop.Core.Execution; using MonoDevelop.Projects.Extensions; +using Mono.Addins; +using System.Linq; +using System.Threading.Tasks; namespace MonoDevelop.Projects { - public abstract class WorkspaceItem : IBuildTarget, IWorkspaceFileObject, ILoadController + public abstract class WorkspaceItem : WorkspaceObject, IWorkspaceFileObject, ILoadController { Workspace parentWorkspace; FileFormat format; internal bool FormatSet; - Hashtable extendedProperties;
FilePath fileName; int loading; PropertyBag userProperties; @@ -57,17 +59,12 @@ namespace MonoDevelop.Projects [ProjectPathItemProperty ("BaseDirectory", DefaultValue=null)]
FilePath baseDirectory; - + public Workspace ParentWorkspace { get { return parentWorkspace; } - internal set { parentWorkspace = value; } - } - - public IDictionary ExtendedProperties { - get { - if (extendedProperties == null) - extendedProperties = new Hashtable (); - return extendedProperties; + internal set { + parentWorkspace = value; + ParentObject = value; } } @@ -80,12 +77,9 @@ namespace MonoDevelop.Projects } } - public virtual string Name { - get {
- if (fileName.IsNullOrEmpty)
- return string.Empty;
- else
- return fileName.FileNameWithoutExtension; + public new string Name { + get { + return base.Name;
} set { if (fileName.IsNullOrEmpty)
@@ -97,6 +91,14 @@ namespace MonoDevelop.Projects } } } + + protected override string OnGetName () + { + if (fileName.IsNullOrEmpty) + return string.Empty; + else + return fileName.FileNameWithoutExtension; + } public virtual FilePath FileName { get { @@ -124,12 +126,9 @@ namespace MonoDevelop.Projects FileName = baseDirectory.Combine (name) + ".x"; } - public FilePath BaseDirectory { + public new FilePath BaseDirectory { get { - if (baseDirectory.IsNull) - return FileName.ParentDirectory.FullPath; - else - return baseDirectory; + return base.BaseDirectory; } set { if (!value.IsNull && !FileName.IsNull && FileName.ParentDirectory.FullPath == value.FullPath) @@ -141,82 +140,64 @@ namespace MonoDevelop.Projects NotifyModified (); } } + + protected override string OnGetBaseDirectory () + { + if (baseDirectory.IsNull) + return FileName.ParentDirectory.FullPath; + else + return baseDirectory; + } - public FilePath ItemDirectory { - get { return FileName.ParentDirectory.FullPath; } + protected override string OnGetItemDirectory () + { + return FileName.ParentDirectory.FullPath; } protected bool Loading { get { return loading > 0; } } - public WorkspaceItem () + protected WorkspaceItem () { - MonoDevelop.Projects.Extensions.ProjectExtensionUtil.LoadControl (this); + ProjectExtensionUtil.LoadControl (this); fileStatusTracker = new FileStatusTracker<WorkspaceItemEventArgs> (this, OnReloadRequired, new WorkspaceItemEventArgs (this)); + Initialize (this); }
- public T GetService<T> () where T: class + WorkspaceItemExtension itemExtension; + + WorkspaceItemExtension ItemExtension { + get { + if (itemExtension == null) + itemExtension = ExtensionChain.GetExtension<WorkspaceItemExtension> (); + return itemExtension; + } + } + + protected override IEnumerable<WorkspaceObjectExtension> CreateDefaultExtensions () { - return (T) GetService (typeof(T)); + yield return new DefaultWorkspaceItemExtension (); } - - public virtual object GetService (Type t) + + public IEnumerable<FilePath> GetItemFiles (bool includeReferencedFiles) { - return Services.ProjectService.GetExtensionChain (this).GetService (this, t); + return ItemExtension.GetItemFiles (includeReferencedFiles);
} - public virtual List<FilePath> GetItemFiles (bool includeReferencedFiles) - {
+ protected virtual IEnumerable<FilePath> OnGetItemFiles (bool includeReferencedFiles) + { List<FilePath> col = FileFormat.Format.GetItemFiles (this); if (!string.IsNullOrEmpty (FileName) && !col.Contains (FileName)) col.Add (FileName); return col; } - - public virtual SolutionEntityItem FindSolutionItem (string fileName) - { - return null; - } - - public virtual bool ContainsItem (IWorkspaceObject obj) + + public virtual bool ContainsItem (WorkspaceObject obj) { return this == obj; } - public ReadOnlyCollection<SolutionItem> GetAllSolutionItems () - { - return GetAllSolutionItems<SolutionItem> (); - } - - public virtual ReadOnlyCollection<T> GetAllSolutionItems<T> () where T: SolutionItem - { - return new List<T> ().AsReadOnly (); - } - - public ReadOnlyCollection<Project> GetAllProjects () - { - return GetAllSolutionItems<Project> (); - } - - public virtual ReadOnlyCollection<Solution> GetAllSolutions () - { - return GetAllItems<Solution> (); - } - - public ReadOnlyCollection<WorkspaceItem> GetAllItems () - { - return GetAllItems<WorkspaceItem> (); - } - - public virtual ReadOnlyCollection<T> GetAllItems<T> () where T: WorkspaceItem - { - List<T> list = new List<T> (); - if (this is T) - list.Add ((T)this); - return list.AsReadOnly (); - }
- [Obsolete("Use GetProjectsContainingFile() (plural) instead")]
public virtual Project GetProjectContainingFile (FilePath fileName) { @@ -232,119 +213,12 @@ namespace MonoDevelop.Projects { return new ReadOnlyCollection<string> (new string [0]); } - - protected internal virtual void OnSave (IProgressMonitor monitor) - { - Services.ProjectService.InternalWriteWorkspaceItem (monitor, FileName, this); - } - + internal void SetParentWorkspace (Workspace workspace) { parentWorkspace = workspace; } - - public BuildResult RunTarget (IProgressMonitor monitor, string target, string configuration) - { - return RunTarget (monitor, target, (SolutionConfigurationSelector) configuration); - } - - public BuildResult RunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) - { - return Services.ProjectService.GetExtensionChain (this).RunTarget (monitor, this, target, configuration); - } - - public bool SupportsBuild () - { - return SupportsTarget (ProjectService.BuildTarget); - } - public void Clean (IProgressMonitor monitor, string configuration) - { - Clean (monitor, (SolutionConfigurationSelector) configuration); - } - - public void Clean (IProgressMonitor monitor, ConfigurationSelector configuration) - { - Services.ProjectService.GetExtensionChain (this).RunTarget (monitor, this, ProjectService.CleanTarget, configuration); - } - - public bool SupportsTarget (string target) - { - return Services.ProjectService.GetExtensionChain (this).SupportsTarget (this, target); - } - - public bool SupportsExecute () - { - return Services.ProjectService.GetExtensionChain (this).SupportsExecute (this); - } - - public BuildResult Build (IProgressMonitor monitor, string configuration) - { - return InternalBuild (monitor, (SolutionConfigurationSelector) configuration); - } - - public BuildResult Build (IProgressMonitor monitor, ConfigurationSelector configuration) - { - return InternalBuild (monitor, configuration); - } - - public void Execute (IProgressMonitor monitor, ExecutionContext context, string configuration) - { - Execute (monitor, context, (SolutionConfigurationSelector) configuration); - } - - public void Execute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) - { - Services.ProjectService.GetExtensionChain (this).Execute (monitor, this, context, configuration); - } - - public bool CanExecute (ExecutionContext context, string configuration) - { - return CanExecute (context, (SolutionConfigurationSelector) configuration); - } - - public bool CanExecute (ExecutionContext context, ConfigurationSelector configuration) - { - return Services.ProjectService.GetExtensionChain (this).CanExecute (this, context, configuration); - } - - public IEnumerable<ExecutionTarget> GetExecutionTargets (string configuration) - { - return GetExecutionTargets ((SolutionConfigurationSelector) configuration); - } - - public IEnumerable<ExecutionTarget> GetExecutionTargets (ConfigurationSelector configuration) - { - return Services.ProjectService.GetExtensionChain (this).GetExecutionTargets (this, configuration); - } - - [Obsolete ("This method will be removed in future releases")] - public bool NeedsBuilding (string configuration) - { - return true; - } - - [Obsolete ("This method will be removed in future releases")] - public bool NeedsBuilding (ConfigurationSelector configuration) - { - return true; - } - - [Obsolete ("This method will be removed in future releases")] - public void SetNeedsBuilding (bool value) - { - } - - [Obsolete ("This method will be removed in future releases")] - public void SetNeedsBuilding (bool needsBuilding, string configuration) - { - } - - [Obsolete ("This method will be removed in future releases")] - public void SetNeedsBuilding (bool needsBuilding, ConfigurationSelector configuration) - { - } - public virtual FileFormat FileFormat { get { if (format == null) { @@ -359,17 +233,13 @@ namespace MonoDevelop.Projects return true; } - public virtual void ConvertToFormat (FileFormat format, bool convertChildren) + public virtual Task ConvertToFormat (FileFormat format, bool convertChildren) { FormatSet = true; this.format = format; if (!string.IsNullOrEmpty (FileName)) FileName = format.GetValidFileName (this, FileName); - } - - internal virtual BuildResult InternalBuild (IProgressMonitor monitor, ConfigurationSelector configuration) - { - return Services.ProjectService.GetExtensionChain (this).RunTarget (monitor, this, ProjectService.BuildTarget, configuration); + return Task.FromResult (0); } protected virtual void OnConfigurationsChanged () @@ -380,17 +250,27 @@ namespace MonoDevelop.Projects ParentWorkspace.OnConfigurationsChanged (); }
- public void Save (FilePath fileName, IProgressMonitor monitor) + public void Save (FilePath fileName, ProgressMonitor monitor) { - FileName = fileName; - Save (monitor); + SaveAsync (fileName, monitor).Wait (); } - public void Save (IProgressMonitor monitor) + public Task SaveAsync (FilePath fileName, ProgressMonitor monitor) + { + FileName = fileName; + return SaveAsync (monitor); + } + + public void Save (ProgressMonitor monitor) + { + SaveAsync (monitor).Wait (); + } + + public async Task SaveAsync (ProgressMonitor monitor) { try { fileStatusTracker.BeginSave (); - Services.ProjectService.GetExtensionChain (this).Save (monitor, this); + await ItemExtension.Save (monitor); SaveUserProperties (); OnSaved (new WorkspaceItemEventArgs (this)); @@ -400,7 +280,12 @@ namespace MonoDevelop.Projects } FileService.NotifyFileChanged (FileName); } - + + protected internal virtual Task OnSave (ProgressMonitor monitor) + { + return Services.ProjectService.InternalWriteWorkspaceItem (monitor, FileName, this); + } + public virtual bool NeedsReload { get { return fileStatusTracker.NeedsReload; @@ -416,59 +301,11 @@ namespace MonoDevelop.Projects } } - internal protected virtual BuildResult OnRunTarget (IProgressMonitor monitor, string target, ConfigurationSelector configuration) - { - if (target == ProjectService.BuildTarget) - return OnBuild (monitor, configuration); - else if (target == ProjectService.CleanTarget) { - OnClean (monitor, configuration); - return null; - } - return null; - } - - internal protected virtual bool OnGetSupportsTarget (string target) - { - return true; - } - internal protected virtual bool OnGetSupportsExecute () { return true; } - protected virtual void OnClean (IProgressMonitor monitor, ConfigurationSelector configuration) - { - } - - protected virtual BuildResult OnBuild (IProgressMonitor monitor, ConfigurationSelector configuration) - { - return null; - } - - internal protected virtual void OnExecute (IProgressMonitor monitor, ExecutionContext context, ConfigurationSelector configuration) - { - } - - internal protected virtual bool OnGetCanExecute (ExecutionContext context, ConfigurationSelector configuration) - { - return true; - } - - internal protected virtual IEnumerable<ExecutionTarget> OnGetExecutionTargets (ConfigurationSelector configuration) - { - yield break; - } - - internal protected virtual bool OnGetNeedsBuilding (ConfigurationSelector configuration) - { - return true; - } - - internal protected virtual void OnSetNeedsBuilding (bool val, ConfigurationSelector configuration) - { - } - void ILoadController.BeginLoad () { loading++; @@ -571,15 +408,9 @@ namespace MonoDevelop.Projects return absPath.ToRelative (BaseDirectory); } - public virtual void Dispose() + public override void Dispose() { - if (extendedProperties != null) { - foreach (object ob in extendedProperties.Values) { - IDisposable disp = ob as IDisposable; - if (disp != null) - disp.Dispose (); - } - } + base.Dispose (); if (userProperties != null) userProperties.Dispose (); } @@ -592,11 +423,6 @@ namespace MonoDevelop.Projects NameChanged (this, e); } - internal protected virtual object OnGetService (Type t) - { - return null; - } - protected void NotifyModified () { OnModified (new WorkspaceItemEventArgs (this)); @@ -632,6 +458,19 @@ namespace MonoDevelop.Projects fileStatusTracker.ReloadRequired -= value; } }*/ + + internal class DefaultWorkspaceItemExtension: WorkspaceItemExtension + { + internal protected override IEnumerable<FilePath> GetItemFiles (bool includeReferencedFiles) + { + return Item.OnGetItemFiles (includeReferencedFiles); + } + + internal protected override Task Save (ProgressMonitor monitor) + { + return Item.OnSave (monitor); + } + } } class FileStatusTracker<TEventArgs> where TEventArgs:EventArgs diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceItemExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceItemExtension.cs new file mode 100644 index 0000000000..5d3d7643ac --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceItemExtension.cs @@ -0,0 +1,67 @@ +// +// WorkspaceItemExtension.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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; +using System.Threading.Tasks; + +namespace MonoDevelop.Projects +{ + public class WorkspaceItemExtension: WorkspaceObjectExtension + { + WorkspaceItemExtension next; + + internal protected override bool SupportsObject (WorkspaceObject item) + { + return item is WorkspaceItem; + } + + internal protected override void InitializeChain (ChainedExtension next) + { + this.next = FindNextImplementation<WorkspaceItemExtension> (next); + } + + protected WorkspaceItem Item { + get { return (WorkspaceItem) base.Owner; } + } + + internal protected virtual bool SupportsItem (WorkspaceItem item) + { + return next.SupportsItem (item); + } + + internal protected virtual Task Save (ProgressMonitor monitor) + { + return next.Save (monitor); + } + + internal protected virtual IEnumerable<FilePath> GetItemFiles (bool includeReferencedFiles) + { + return next.GetItemFiles (includeReferencedFiles); + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceObject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceObject.cs new file mode 100644 index 0000000000..ec464b7769 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceObject.cs @@ -0,0 +1,303 @@ +// IWorkspaceObject.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2008 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.Collections.Generic; +using MonoDevelop.Core; +using MonoDevelop.Core.Serialization; +using System.Collections; +using MonoDevelop.Projects.Extensions; +using Mono.Addins; +using System.Linq; +using System.Threading.Tasks; + + +namespace MonoDevelop.Projects +{ + public abstract class WorkspaceObject: IExtendedDataItem, IFolderItem, IDisposable + { + Hashtable extendedProperties; + bool initializeCalled; + + protected void Initialize<T> (T instance) + { + if (instance.GetType () != typeof(T)) + return; + initializeCalled = true; + OnInitialize (); + InitializeExtensionChain (); + OnExtensionChainInitialized (); + } + + public string Name { + get { return OnGetName (); } + } +
+ public FilePath ItemDirectory { + get { return OnGetItemDirectory (); } + } +
+ public FilePath BaseDirectory { + get { return OnGetBaseDirectory (); } + } + + public WorkspaceObject ParentObject { get; protected set; } + + public IEnumerable<WorkspaceObject> GetChildren () + { + return OnGetChildren (); + } + + public IEnumerable<T> GetAllItems<T> () where T: WorkspaceObject + { + if (this is T) + yield return (T)this; + foreach (var c in OnGetChildren ()) { + foreach (var r in c.GetAllItems<T> ()) + yield return r; + } + } + + /// <summary> + /// Gets extended properties. + /// </summary> + /// <remarks> + /// This dictionary can be used by add-ins to store arbitrary information about this solution item. + /// Keys and values can be of any type. + /// If a value implements IDisposable, the value will be disposed when this item is disposed. + /// </remarks> + public IDictionary ExtendedProperties { + get { + return OnGetExtendedProperties (); + } + } + + /// <summary> + /// Gets a value indicating whether this <see cref="MonoDevelop.Projects.SolutionItem"/> has been disposed. + /// </summary> + /// <value> + /// <c>true</c> if disposed; otherwise, <c>false</c>. + /// </value> + internal protected bool Disposed { get; private set; } + + public virtual void Dispose () + { + if (Disposed) + return; + + Disposed = true; + + if (extensionChain != null) { + extensionChain.Dispose (); + extensionChain = null; + } + + if (extendedProperties != null) { + foreach (object ob in extendedProperties.Values) { + IDisposable disp = ob as IDisposable; + if (disp != null) + disp.Dispose (); + } + extendedProperties = null; + } + } + + /// <summary> + /// Gets a service instance of a given type + /// </summary> + /// <returns> + /// The service. + /// </returns> + /// <typeparam name='T'> + /// Type of the service + /// </typeparam> + /// <remarks> + /// This method looks for an imlpementation of a service of the given type. + /// </remarks> + public T GetService<T> () + { + return (T) GetService (typeof(T)); + } + + /// <summary> + /// Gets a service instance of a given type + /// </summary> + /// <returns> + /// The service. + /// </returns> + /// <param name='t'> + /// Type of the service + /// </param> + /// <remarks> + /// This method looks for an imlpementation of a service of the given type. + /// </remarks> + public object GetService (Type t) + { + return ItemExtension.GetService (t); + } + + /// <summary> + /// Gets a value indicating whether the extension chain for this object has already been created and initialized + /// </summary> + protected bool IsExtensionChainCreated { + get { return extensionChain != null; } + } + + ExtensionChain extensionChain; + protected ExtensionChain ExtensionChain { + get { + if (extensionChain == null) { + if (!initializeCalled) + throw new InvalidOperationException ("The constructor of type " + GetType () + " must call Initialize(this)"); + else + throw new InvalidOperationException ("The extension chain can't be used before OnExtensionChainInitialized() method is called"); + } + return extensionChain; + } + } + + WorkspaceObjectExtension itemExtension; + + WorkspaceObjectExtension ItemExtension { + get { + if (itemExtension == null) + itemExtension = ExtensionChain.GetExtension<WorkspaceObjectExtension> (); + return itemExtension; + } + } + + void InitializeExtensionChain () + { + // Create an initial empty extension chain. This avoid crashes in case a call to SupportsObject ends + // calling methods from the extension + + var tempExtensions = new List<WorkspaceObjectExtension> (); + tempExtensions.AddRange (CreateDefaultExtensions ().Reverse ()); + extensionChain = ExtensionChain.Create (tempExtensions.ToArray ()); + foreach (var e in tempExtensions) + e.Init (this); + + // Collect extensions that support this object + + var extensions = new List<WorkspaceObjectExtension> (); + foreach (ProjectModelExtensionNode node in AddinManager.GetExtensionNodes (ProjectService.ProjectModelExtensionsPath)) { + if (node.CanHandleObject (this)) { + var ext = node.CreateExtension (); + if (ext.SupportsObject (this)) + extensions.Add (ext); + else + ext.Dispose (); + } + } + + foreach (var e in tempExtensions) + e.Dispose (); + + // Now create the final extension chain + + extensions.Reverse (); + extensions.AddRange (CreateDefaultExtensions ().Reverse ()); + extensionChain = ExtensionChain.Create (extensions.ToArray ()); + foreach (var e in extensions) + e.Init (this); + } + + protected virtual IEnumerable<WorkspaceObjectExtension> CreateDefaultExtensions () + { + yield return new DefaultWorkspaceObjectExtension (); + } + + /// <summary> + /// Called after the object is created, but before the extension chain has been created. + /// </summary> + protected virtual void OnInitialize () + { + } + + /// <summary> + /// Called when the extension chain for this object has been created. This method can be overriden + /// to do initializations on the object that require access to the extension chain + /// </summary> + protected virtual void OnExtensionChainInitialized () + { + } + + protected virtual IDictionary OnGetExtendedProperties () + { + if (extendedProperties == null) + extendedProperties = new Hashtable (); + return extendedProperties; + } + + protected virtual IEnumerable<WorkspaceObject> OnGetChildren () + { + yield break; + } + + protected virtual object OnGetService (Type t) + { + return t.IsInstanceOfType (this) ? this : null; + } + + protected abstract string OnGetName (); + + protected abstract string OnGetItemDirectory (); + + protected abstract string OnGetBaseDirectory (); + + internal class DefaultWorkspaceObjectExtension: WorkspaceObjectExtension + { + internal protected override object GetService (Type t) + { + return Owner.OnGetService (t); + } + } + } + + public static class WorkspaceObjectExtensions + { + public static T As<T> (this WorkspaceObject ob) where T:class + { + return ob != null ? ob.GetService<T> () : null; + } + } + + public interface IWorkspaceFileObject: IFileItem, IDisposable + { + FileFormat FileFormat { get; } + Task ConvertToFormat (FileFormat format, bool convertChildren); + bool SupportsFormat (FileFormat format);
+ IEnumerable<FilePath> GetItemFiles (bool includeReferencedFiles); + new FilePath FileName { get; set; } + bool NeedsReload { get; set; } + bool ItemFilesChanged { get; } + Task SaveAsync (ProgressMonitor monitor); + string Name { get; set; } + FilePath BaseDirectory { get; } + FilePath ItemDirectory { get; } + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/PortableDotNetProjectBinding.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceObjectExtension.cs index 4ec770d696..541c25ad7c 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/PortableDotNetProjectBinding.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceObjectExtension.cs @@ -1,20 +1,21 @@ -// -// PortableDotNetProjectBinding.cs -// -// Author: Jeffrey Stedfast <jeff@xamarin.com> -// -// Copyright (c) 2012 Xamarin Inc. -// +// +// WorkspaceObjectExtension.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2014 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 @@ -22,36 +23,41 @@ // 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.Xml; -using MonoDevelop.Core.Serialization; -using MonoDevelop.Core; +using Mono.Addins; namespace MonoDevelop.Projects { - public class PortableDotNetProjectBinding : IProjectBinding + public class WorkspaceObjectExtension: ChainedExtension { - public string Name { - get { return "PortableDotNet"; } + WorkspaceObjectExtension next; + + internal protected override void InitializeChain (ChainedExtension next) + { + this.next = FindNextImplementation<WorkspaceObjectExtension> (next); } - - public Project CreateProject (ProjectCreateInformation info, System.Xml.XmlElement projectOptions) + + protected WorkspaceObject Owner { get; private set; } + + internal protected virtual bool SupportsObject (WorkspaceObject item) { - string languageName = projectOptions.GetAttribute ("language"); - - return new PortableDotNetProject (languageName, info, projectOptions); + return true; } - - public Project CreateSingleFileProject (string sourceFile) + + internal void Init (WorkspaceObject item) { - throw new InvalidOperationException (); + Owner = item; + Initialize (); } - - public bool CanCreateSingleFileProject (string sourceFile) + + internal protected virtual void Initialize () { - return false; + } + + internal protected virtual object GetService (Type t) + { + return t.IsInstanceOfType (this) ? this : next.GetService (t); } } } + |