diff options
Diffstat (limited to 'main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItem.cs')
-rw-r--r-- | main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItem.cs | 1859 |
1 files changed, 1070 insertions, 789 deletions
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 (); } |