diff options
author | Lluis Sanchez Gual <lluis@xamarin.com> | 2015-04-15 21:25:21 +0300 |
---|---|---|
committer | Lluis Sanchez Gual <lluis@xamarin.com> | 2015-04-15 22:01:36 +0300 |
commit | c01e9a973783cda83d6f6aa702842c6438050bc4 (patch) | |
tree | f0168bc741c06b980e61ef9dd7100756cae73bb4 /main/src/core | |
parent | 4d8e96a1003ead9b3ce76b1c71602795d031eedb (diff) |
Fix unsupported project loading
Properly load unsupported projects as UnknownProject, and support saving
simple changes such as adding/removing files.
Diffstat (limited to 'main/src/core')
7 files changed, 130 insertions, 117 deletions
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectTypeNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectTypeNode.cs index 186bbcd097..140f09a6b9 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectTypeNode.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectTypeNode.cs @@ -24,6 +24,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; +using System.Linq; using System.Threading.Tasks; using System.Collections.Generic; using MonoDevelop.Projects.Formats.MSBuild; @@ -46,6 +47,7 @@ namespace MonoDevelop.Projects.Extensions public override async Task<SolutionItem> CreateSolutionItem (ProgressMonitor monitor, string fileName) { MSBuildProject p = null; + Project project = null; if (!string.IsNullOrEmpty (fileName)) { p = await MSBuildProject.LoadAsync (fileName); @@ -53,24 +55,26 @@ namespace MonoDevelop.Projects.Extensions if (migrators.Count > 0) await MSBuildProjectService.MigrateFlavors (monitor, fileName, Guid, p, migrators); + var unsupporedFlavor = p.ProjectTypeGuids.FirstOrDefault (fid => !MSBuildProjectService.IsKnownFlavorGuid (fid) && !MSBuildProjectService.IsKnownTypeGuid (fid)); + if (unsupporedFlavor != null) { + // The project has a flavor that's not supported. Return a fake project (if possible). + return MSBuildProjectService.CreateUnknownSolutionItem (monitor, fileName, Guid, unsupporedFlavor, null); + } + if (MSBuildSupport == MSBuildSupport.NotSupported || MSBuildProjectService.GetMSBuildSupportForFlavors (p.ProjectTypeGuids) == MSBuildSupport.NotSupported) p.UseMSBuildEngine = false; // Evaluate the project now. If evaluation fails an exception will be thrown, and when that // happens the solution will create a placeholder project. - try { - p.Evaluate (); - } catch (ProjectEvaluationException ex) { - if (p.UseMSBuildEngine) { - p.UseMSBuildEngine = false; - p.Evaluate (); - } - } + p.Evaluate (); } - var project = await base.CreateSolutionItem (monitor, fileName) as Project; + if (project == null) + project = await base.CreateSolutionItem (monitor, fileName) as Project; + if (project == null) throw new InvalidOperationException ("Project node type is not a subclass of MonoDevelop.Projects.Project"); + if (p != null) project.SetCreationContext (Project.CreationContext.Create (p, Guid)); return project; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemTypeNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemTypeNode.cs index b413e0bc69..7757b02041 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemTypeNode.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemTypeNode.cs @@ -109,21 +109,9 @@ namespace MonoDevelop.Projects.Extensions if (factory == null) factory = (SolutionItemFactory)Activator.CreateInstance (ItemType); item = await factory.CreateItem (fileName, Guid); - } else { - try { - // Some subclasses (such as ProjectTypeNode) need to assign some data to - // the object before it is initialized. However, by default initialization - // is automatically made by the constructor, so to support this scenario - // the initialization has to be delayed. This is done by setting the - // MonoDevelop.DelayItemInitialization logical context property. - // When this property is set, the object is not initialized, and it has - // to be manually initialized by calling EnsureInitialized. - CallContext.LogicalSetData ("MonoDevelop.DelayItemInitialization", true); - item = (SolutionItem)Activator.CreateInstance (ItemType, true); - } finally { - CallContext.LogicalSetData ("MonoDevelop.DelayItemInitialization", false); - } - } + } else + item = MSBuildProjectService.CreateUninitializedInstance (ItemType); + item.TypeGuid = Guid; return item; } 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 447003f2a6..754d059e46 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 @@ -129,34 +129,36 @@ namespace MonoDevelop.Projects.Formats.MSBuild return target; return null; } - + + /// <summary> + /// Loads a solution item + /// </summary> + /// <returns>The item.</returns> + /// <param name="monitor">Progress monitor</param> + /// <param name="fileName">File path to the item file</param> + /// <param name="expectedFormat">File format that the project should have</param> + /// <param name="typeGuid">Optional item type GUID. If not provided, the type is guessed from the file extension.</param> + /// <param name="itemGuid">Optional item Id</param> + /// <param name="ctx">Optional solution context</param> public async static Task<SolutionItem> LoadItem (ProgressMonitor monitor, string fileName, MSBuildFileFormat expectedFormat, string typeGuid, string itemGuid, SolutionLoadContext ctx) { - foreach (SolutionItemTypeNode node in GetItemTypeNodes ()) { - if (node.CanHandleFile (fileName, typeGuid)) - return await LoadProjectAsync (monitor, fileName, expectedFormat, null, node, ctx); - } - - // 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 p = (UnknownProject) await LoadProjectAsync (monitor, fileName, expectedFormat, typeof(UnknownProject), null, ctx); - p.UnsupportedProjectMessage = projectInfo.GetInstructions (); - return p; - } - return null; - } + SolutionItem item = null; - internal static async Task<SolutionItem> LoadProjectAsync (ProgressMonitor monitor, string fileName, MSBuildFileFormat format, Type itemType, SolutionItemTypeNode node, SolutionLoadContext ctx) - { - SolutionItem item; + // Find an extension node that can handle this item type + var node = GetItemTypeNodes ().FirstOrDefault (n => n.CanHandleFile (fileName, typeGuid)); - if (itemType != null) - item = (SolutionItem)Activator.CreateInstance (itemType); - else + if (node != null) { item = await node.CreateSolutionItem (monitor, fileName); + if (item == null) + return null; + } + + if (item == null) { + // If it is a known unsupported project, load it as UnknownProject + item = CreateUnknownSolutionItem (monitor, fileName, typeGuid, typeGuid, ctx); + if (item == null) + return null; + } item.EnsureInitialized (); @@ -166,10 +168,69 @@ namespace MonoDevelop.Projects.Formats.MSBuild item.NotifyItemReady (); }; - await item.LoadAsync (monitor, fileName, format); + await item.LoadAsync (monitor, fileName, expectedFormat); return item; } + internal static SolutionItem CreateUnknownSolutionItem (ProgressMonitor monitor, string fileName, string typeGuid, string unknownTypeGuid, SolutionLoadContext ctx) + { + bool loadAsProject = false; + string unsupportedMessage; + + var relPath = ctx != null && ctx.Solution != null ? new FilePath (fileName).ToRelative (ctx.Solution.BaseDirectory).ToString() : new FilePath (fileName).FileName; + var guids = !string.IsNullOrEmpty (unknownTypeGuid) ? unknownTypeGuid.Split (new char[] {';'}, StringSplitOptions.RemoveEmptyEntries) : new string[0]; + + if (!string.IsNullOrEmpty (unknownTypeGuid)) { + var projectInfo = MSBuildProjectService.GetUnknownProjectTypeInfo (guids, fileName); + if (projectInfo != null) { + loadAsProject = projectInfo.LoadFiles; + unsupportedMessage = projectInfo.GetInstructions (); + LoggingService.LogWarning (string.Format ("Could not load {0} project '{1}'. {2}", projectInfo.Name, relPath, projectInfo.GetInstructions ())); + monitor.ReportWarning (GettextCatalog.GetString ("Could not load {0} project '{1}'. {2}", projectInfo.Name, relPath, projectInfo.GetInstructions ())); + } else { + unsupportedMessage = GettextCatalog.GetString ("Unknown project type: {0}", unknownTypeGuid); + LoggingService.LogWarning (string.Format ("Could not load project '{0}' with unknown item type '{1}'", relPath, unknownTypeGuid)); + monitor.ReportWarning (GettextCatalog.GetString ("Could not load project '{0}' with unknown item type '{1}'", relPath, unknownTypeGuid)); + return null; + } + } else { + unsupportedMessage = GettextCatalog.GetString ("Unknown project type"); + LoggingService.LogWarning (string.Format ("Could not load project '{0}' with unknown item type", relPath)); + monitor.ReportWarning (GettextCatalog.GetString ("Could not load project '{0}' with unknown item type", relPath)); + } + if (loadAsProject) { + var project = (Project) CreateUninitializedInstance (typeof(UnknownProject)); + project.UnsupportedProjectMessage = unsupportedMessage; + project.SetCreationContext (Project.CreationContext.Create (typeGuid, new string[0])); + return project; + } else + return null; + } + + + /// <summary> + /// Creates an uninitialized solution item instance + /// </summary> + /// <param name="type">Solution item type</param> + /// <remarks> + /// Some subclasses (such as ProjectTypeNode) need to assign some data to + /// the object before it is initialized. However, by default initialization + /// is automatically made by the constructor, so to support this scenario + /// the initialization has to be delayed. This is done by setting the + /// MonoDevelop.DelayItemInitialization logical context property. + /// When this property is set, the object is not initialized, and it has + /// to be manually initialized by calling EnsureInitialized. + /// </remarks> + internal static SolutionItem CreateUninitializedInstance (Type type) + { + try { + System.Runtime.Remoting.Messaging.CallContext.LogicalSetData ("MonoDevelop.DelayItemInitialization", true); + return (SolutionItem)Activator.CreateInstance (type, true); + } finally { + System.Runtime.Remoting.Messaging.CallContext.LogicalSetData ("MonoDevelop.DelayItemInitialization", false); + } + } + internal static bool CanCreateSolutionItem (string type, ProjectCreateInformation info, System.Xml.XmlElement projectOptions) { foreach (var node in GetItemTypeNodes ()) { @@ -211,7 +272,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild if (node != null) { if (node.MSBuildSupport != MSBuildSupport.Supported) return node.MSBuildSupport; - } else + } else if (!IsKnownTypeGuid (fid)) throw new UnknownSolutionItemTypeException (fid); } return MSBuildSupport.Supported; @@ -308,6 +369,11 @@ namespace MonoDevelop.Projects.Formats.MSBuild throw new InvalidOperationException ("Language not supported: " + guid); } + internal static bool IsKnownFlavorGuid (string guid) + { + return WorkspaceObject.GetModelExtensions (null).OfType<SolutionItemExtensionNode> ().Any (n => n.Guid.Equals (guid, StringComparison.InvariantCultureIgnoreCase)); + } + internal static bool IsKnownTypeGuid (string guid) { foreach (var node in GetItemTypeNodes ()) { @@ -399,6 +465,9 @@ namespace MonoDevelop.Projects.Formats.MSBuild internal static MSBuildSupport GetMSBuildSupportForProject (Project project) { + if (project is UnknownProject) + return MSBuildSupport.NotSupported; + foreach (var node in GetItemTypeNodes ().OfType<ProjectTypeNode> ()) { if (node.Guid.Equals (project.TypeGuid, StringComparison.OrdinalIgnoreCase)) { if (node.MSBuildSupport != MSBuildSupport.Supported) @@ -406,8 +475,7 @@ namespace MonoDevelop.Projects.Formats.MSBuild return GetMSBuildSupportForFlavors (project.FlavorGuids); } } - // The generic handler should always be found - throw new InvalidOperationException (); + return MSBuildSupport.NotSupported; } public static void RegisterGenericProjectType (string projectId, Type type) 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 2a2d5a3148..aec98ee7cc 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 @@ -643,7 +643,6 @@ namespace MonoDevelop.Projects.Formats.MSBuild DateTime ti = DateTime.Now; if (sol.IsSolutionItemEnabled (projectPath)) { - Console.WriteLine (">> Start Loading " + Path.GetFileName (projectPath)); loadTask = Services.ProjectService.ReadSolutionItem (monitor, projectPath, format, projTypeGuid, projectGuid, ctx); } else { loadTask = Task.FromResult<SolutionItem> (new UnloadedSolutionItem () { @@ -653,52 +652,15 @@ namespace MonoDevelop.Projects.Formats.MSBuild var ft = loadTask.ContinueWith (ta => { try { - Console.WriteLine ("<< End Loading " + Path.GetFileName (projectPath) + " t:" + (DateTime.Now - ti).TotalMilliseconds); item = ta.Result; if (item == null) throw new UnknownSolutionItemTypeException (projTypeGuid); } catch (Exception cex) { var e = UnwrapException (cex).First (); - bool loadAsProject = false; string unsupportedMessage = e.Message; - if (e is ProjectEvaluationException) { - var relPath = new FilePath (path).ToRelative (sol.BaseDirectory); - var project = ((ProjectEvaluationException)e).Project; - var projectInfo = MSBuildProjectService.GetUnknownProjectTypeInfo (project.ProjectTypeGuids, sol.FileName); - if (projectInfo != null) { - loadAsProject = projectInfo.LoadFiles; - LoggingService.LogWarning (string.Format ("Could not load {0} project '{1}'. {2}", projectInfo.Name, relPath, projectInfo.GetInstructions ())); - monitor.ReportWarning (GettextCatalog.GetString ("Could not load {0} project '{1}'. {2}", projectInfo.Name, relPath, projectInfo.GetInstructions ())); - unsupportedMessage = projectInfo.GetInstructions (); - } else { - LoggingService.LogWarning (string.Format ("Could not load project '{0}': {1}", relPath, e.Message)); - monitor.ReportWarning (GettextCatalog.GetString ("Could not load project '{0}': {1}'", relPath, e.Message)); - unsupportedMessage = GettextCatalog.GetString ("Project evaluation failed: " + e.Message); - } - } - else if (e is UnknownSolutionItemTypeException) { - var name = ((UnknownSolutionItemTypeException)e).TypeName; - - var relPath = new FilePath (path).ToRelative (sol.BaseDirectory); - if (!string.IsNullOrEmpty (name)) { - var guids = name.Split (';'); - 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 ())); - monitor.ReportWarning (GettextCatalog.GetString ("Could not load {0} project '{1}'. {2}", projectInfo.Name, relPath, projectInfo.GetInstructions ())); - } else { - LoggingService.LogWarning (string.Format ("Could not load project '{0}' with unknown item type '{1}'", relPath, name)); - monitor.ReportWarning (GettextCatalog.GetString ("Could not load project '{0}' with unknown item type '{1}'", relPath, name)); - } - } else { - LoggingService.LogWarning (string.Format ("Could not load project '{0}' with unknown item type", relPath)); - monitor.ReportWarning (GettextCatalog.GetString ("Could not load project '{0}' with unknown item type", relPath)); - } - - } else if (e is UserException) { + if (e is UserException) { var ex = (UserException) e; LoggingService.LogError ("{0}: {1}", ex.Message, ex.Details); monitor.ReportError (string.Format ("{0}{1}{1}{2}", ex.Message, Environment.NewLine, ex.Details), null); @@ -709,17 +671,10 @@ namespace MonoDevelop.Projects.Formats.MSBuild } SolutionItem uitem; - if (loadAsProject) { - uitem = new UnknownProject () { - FileName = projectPath, - UnsupportedProjectMessage = unsupportedMessage, - }; - } else { - uitem = new UnknownSolutionItem () { - FileName = projectPath, - UnsupportedProjectMessage = unsupportedMessage, - }; - } + uitem = new UnknownSolutionItem () { + FileName = projectPath, + UnsupportedProjectMessage = unsupportedMessage, + }; item = uitem; item.ItemId = projectGuid; item.TypeGuid = projTypeGuid; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs index a3b377516c..16584c4677 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs @@ -166,7 +166,8 @@ namespace MonoDevelop.Projects flavorGuids = subtypeGuids.ToArray (); } } else { - this.sourceProject = null; + sourceProject = new MSBuildProject (); + sourceProject.FileName = FileName; flavorGuids = creationContext.FlavorGuids; } } @@ -254,20 +255,22 @@ namespace MonoDevelop.Projects protected override Task OnLoad (ProgressMonitor monitor) { - MSBuildProject p = sourceProject; - return Task.Run (delegate { - if (p == null || p.IsNewProject) - p = MSBuildProject.LoadAsync (FileName).Result; + if (sourceProject == null || sourceProject.IsNewProject) { + sourceProject = MSBuildProject.LoadAsync (FileName).Result; + if (MSBuildEngineSupport == MSBuildSupport.NotSupported) + sourceProject.UseMSBuildEngine = false; + sourceProject.Evaluate (); + } - IMSBuildPropertySet globalGroup = p.GetGlobalPropertyGroup (); + IMSBuildPropertySet globalGroup = sourceProject.GetGlobalPropertyGroup (); // Avoid crash if there is not global group if (globalGroup == null) - p.AddNewPropertyGroup (false); + sourceProject.AddNewPropertyGroup (false); - ProjectExtension.OnPrepareForEvaluation (p); + ProjectExtension.OnPrepareForEvaluation (sourceProject); - ReadProject (monitor, p); + ReadProject (monitor, sourceProject); }); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs index 9295fcf1c4..5a6d34417b 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs @@ -128,7 +128,8 @@ namespace MonoDevelop.Projects using (Counters.ReadSolutionItem.BeginTiming ("Read project " + file)) { file = GetTargetFile (file); SolutionItem loadedItem = await GetExtensionChain ().LoadSolutionItem (monitor, ctx, file, format, typeGuid, itemGuid); - loadedItem.NeedsReload = false; + if (loadedItem != null) + loadedItem.NeedsReload = false; return loadedItem; } }); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownProject.cs index 5074318786..a798b6260f 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/UnknownProject.cs @@ -75,12 +75,6 @@ namespace MonoDevelop.Projects return GettextCatalog.GetString ("Unknown entry"); } - internal protected override Task OnSave (ProgressMonitor monitor) - { - // Do nothing - return Task.FromResult (0); - } - protected override bool OnGetSupportsTarget (string target) { // We can't do anything with unsupported projects, other than display them in the solution pad |