Browse Source

Fix #252 - Adding an existing project to a solution uses a new GUID instead of the <ProjectGuid>

Also adds code to detect+fix duplicate GUIDs. (which might exist due to users creating new projects by copying old ones)
newNR
Daniel Grunwald 12 years ago
parent
commit
d8c2bbfa44
  1. 1
      src/Main/Base/Project/Editor/CodeCompletion/ICompletionItem.cs
  2. 3
      src/Main/Base/Project/Project/ISolutionItem.cs
  3. 28
      src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
  4. 17
      src/Main/Base/Test/Project/AddExistingProjectTests.cs
  5. 7
      src/Main/SharpDevelop/Project/SolutionFileItem.cs
  6. 8
      src/Main/SharpDevelop/Project/SolutionFolder.cs
  7. 24
      src/Main/SharpDevelop/Project/SolutionLoader.cs

1
src/Main/Base/Project/Editor/CodeCompletion/ICompletionItem.cs

@ -56,6 +56,7 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion
public virtual void Complete(CompletionContext context) public virtual void Complete(CompletionContext context)
{ {
context.Editor.Document.Replace(context.StartOffset, context.Length, this.Text); context.Editor.Document.Replace(context.StartOffset, context.Length, this.Text);
// In case someone calls base.Complete() and then continues using the context, update EndOffset:
context.EndOffset = context.StartOffset + this.Text.Length; context.EndOffset = context.StartOffset + this.Text.Length;
} }
} }

3
src/Main/Base/Project/Project/ISolutionItem.cs

@ -36,7 +36,8 @@ namespace ICSharpCode.SharpDevelop.Project
/// <summary> /// <summary>
/// Gets the ID GUID of this solution item. /// Gets the ID GUID of this solution item.
/// </summary> /// </summary>
Guid IdGuid { get; } /// <remarks>SharpDevelop will change an item's GUID in order to automatically solve GUID conflicts.</remarks>
Guid IdGuid { get; set; }
/// <summary> /// <summary>
/// Gets the type GUID of this solution item. /// Gets the type GUID of this solution item.

28
src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs

@ -122,6 +122,19 @@ namespace ICSharpCode.SharpDevelop.Project
public event EventHandler MinimumSolutionVersionChanged; public event EventHandler MinimumSolutionVersionChanged;
public override Guid IdGuid {
get {
return base.IdGuid;
}
set {
if (base.IdGuid != value) {
base.IdGuid = value;
// Save changed GUID to project file
SetPropertyInternal(null, null, ProjectGuidPropertyName, value.ToString("B").ToUpperInvariant(), PropertyStorageLocations.Base, true);
}
}
}
public string ToolsVersion { public string ToolsVersion {
get { return projectFile.ToolsVersion; } get { return projectFile.ToolsVersion; }
protected internal set { protected internal set {
@ -181,6 +194,7 @@ namespace ICSharpCode.SharpDevelop.Project
else else
SetProperty(null, platform, "PlatformTarget", "AnyCPU", PropertyStorageLocations.PlatformSpecific, false); SetProperty(null, platform, "PlatformTarget", "AnyCPU", PropertyStorageLocations.PlatformSpecific, false);
LoadConfigurationPlatformNamesFromMSBuild(); LoadConfigurationPlatformNamesFromMSBuild();
isLoading = false;
} }
/// <summary> /// <summary>
@ -914,7 +928,7 @@ namespace ICSharpCode.SharpDevelop.Project
propertyRemoved = true; propertyRemoved = true;
} }
} }
if (propertyRemoved && propertyGroup.Children.Count() == 0) if (propertyRemoved && propertyGroup.Children.Count == 0)
project.RemoveChild(propertyGroup); project.RemoveChild(propertyGroup);
} }
} }
@ -1188,7 +1202,7 @@ namespace ICSharpCode.SharpDevelop.Project
#endregion #endregion
#region Loading #region Loading
protected bool isLoading; protected bool isLoading = true;
public MSBuildBasedProject(ProjectLoadInformation loadInformation) public MSBuildBasedProject(ProjectLoadInformation loadInformation)
: base(loadInformation) : base(loadInformation)
@ -1197,7 +1211,6 @@ namespace ICSharpCode.SharpDevelop.Project
this.configurationNames = new MSBuildConfigurationOrPlatformNameCollection(this, false); this.configurationNames = new MSBuildConfigurationOrPlatformNameCollection(this, false);
this.platformNames = new MSBuildConfigurationOrPlatformNameCollection(this, true); this.platformNames = new MSBuildConfigurationOrPlatformNameCollection(this, true);
isLoading = true;
bool success = false; bool success = false;
try { try {
try { try {
@ -1277,6 +1290,15 @@ namespace ICSharpCode.SharpDevelop.Project
userProjectFile = ProjectRootElement.Create(userFileName, MSBuildProjectCollection); userProjectFile = ProjectRootElement.Create(userFileName, MSBuildProjectCollection);
} }
// Read IdGuid from project file.
string idGuidString = GetEvaluatedProperty(ProjectGuidPropertyName);
if (idGuidString != null) {
Guid idGuid;
if (Guid.TryParse(idGuidString, out idGuid)) {
// Use 'base.' to avoid writing the changed ID back into the project file
base.IdGuid = idGuid;
}
}
CreateItemsListFromMSBuild(); CreateItemsListFromMSBuild();
LoadConfigurationPlatformNamesFromMSBuild(); LoadConfigurationPlatformNamesFromMSBuild();
} }

17
src/Main/Base/Test/Project/AddExistingProjectTests.cs

@ -30,7 +30,8 @@ namespace ICSharpCode.SharpDevelop.Project
IProject LoadProject(ProjectLoadInformation info) IProject LoadProject(ProjectLoadInformation info)
{ {
var project = MockRepository.GenerateStrictMock<IProject>(); var project = MockRepository.GenerateStrictMock<IProject>();
project.Stub(p => p.IdGuid).Return(projectGuid); project.Stub(p => p.IdGuid).PropertyBehavior();
project.IdGuid = projectGuid;
project.Stub(p => p.FileName).Return(info.FileName); project.Stub(p => p.FileName).Return(info.FileName);
project.Stub(p => p.ParentSolution).Return(info.Solution); project.Stub(p => p.ParentSolution).Return(info.Solution);
project.Stub(p => p.ParentFolder).PropertyBehavior(); project.Stub(p => p.ParentFolder).PropertyBehavior();
@ -67,10 +68,14 @@ namespace ICSharpCode.SharpDevelop.Project
solution.AddExistingProject(project1FileName); solution.AddExistingProject(project1FileName);
} }
// [Test] [Test]
// public void AddTwoProjectsWithSameGUID() public void AddTwoProjectsWithSameGUID()
// { {
// var solution = CreateSolution();
// } var project1 = solution.AddExistingProject(project1FileName);
var project2 = solution.AddExistingProject(project2FileName);
Assert.AreEqual(projectGuid, project1.IdGuid);
Assert.AreNotEqual(projectGuid, project2.IdGuid);
}
} }
} }

7
src/Main/SharpDevelop/Project/SolutionFileItem.cs

@ -9,12 +9,11 @@ namespace ICSharpCode.SharpDevelop.Project
class SolutionFileItem : ISolutionFileItem class SolutionFileItem : ISolutionFileItem
{ {
readonly Solution parentSolution; readonly Solution parentSolution;
readonly Guid idGuid;
public SolutionFileItem(Solution parentSolution) public SolutionFileItem(Solution parentSolution)
{ {
this.parentSolution = parentSolution; this.parentSolution = parentSolution;
this.idGuid = Guid.NewGuid(); this.IdGuid = Guid.NewGuid();
} }
FileName fileName; FileName fileName;
@ -35,9 +34,7 @@ namespace ICSharpCode.SharpDevelop.Project
get { return parentSolution; } get { return parentSolution; }
} }
public Guid IdGuid { public Guid IdGuid { get; set; }
get { return idGuid; }
}
public Guid TypeGuid { public Guid TypeGuid {
get { return Guid.Empty; } get { return Guid.Empty; }

8
src/Main/SharpDevelop/Project/SolutionFolder.cs

@ -16,7 +16,7 @@ namespace ICSharpCode.SharpDevelop.Project
class SolutionFolder : ISolutionFolder class SolutionFolder : ISolutionFolder
{ {
readonly Solution parentSolution; readonly Solution parentSolution;
readonly Guid idGuid; Guid idGuid;
public SolutionFolder(Solution parentSolution, Guid idGuid) public SolutionFolder(Solution parentSolution, Guid idGuid)
{ {
@ -114,6 +114,7 @@ namespace ICSharpCode.SharpDevelop.Project
public Guid IdGuid { public Guid IdGuid {
get { return idGuid; } get { return idGuid; }
set { idGuid = value; }
} }
public Guid TypeGuid { public Guid TypeGuid {
@ -135,7 +136,10 @@ namespace ICSharpCode.SharpDevelop.Project
throw new ProjectLoadException("Project " + fileName + " is already part of this solution."); throw new ProjectLoadException("Project " + fileName + " is already part of this solution.");
ProjectLoadInformation loadInfo = new ProjectLoadInformation(parentSolution, fileName, fileName.GetFileNameWithoutExtension()); ProjectLoadInformation loadInfo = new ProjectLoadInformation(parentSolution, fileName, fileName.GetFileNameWithoutExtension());
IProject project = SD.ProjectService.LoadProject(loadInfo); IProject project = SD.ProjectService.LoadProject(loadInfo);
Debug.Assert(project.IdGuid != Guid.Empty); if (parentSolution.GetItemByGuid(project.IdGuid) != null) {
SD.Log.Warn("Added project has duplicate GUID; a new GUID will be generated.");
project.IdGuid = Guid.NewGuid();
}
this.Items.Add(project); this.Items.Add(project);
project.ProjectLoaded(); project.ProjectLoaded();
ProjectBrowserPad.RefreshViewAsync(); ProjectBrowserPad.RefreshViewAsync();

24
src/Main/SharpDevelop/Project/SolutionLoader.cs

@ -74,6 +74,7 @@ namespace ICSharpCode.SharpDevelop.Project
solutionEntries.Add(information); solutionEntries.Add(information);
if (projectInfoDict.ContainsKey(information.IdGuid)) { if (projectInfoDict.ContainsKey(information.IdGuid)) {
// resolve GUID conflicts // resolve GUID conflicts
SD.Log.WarnFormatted("Detected duplicate GUID in .sln file: {0} is used for {1} and {2}", information.IdGuid, information.ProjectName, projectInfoDict[information.IdGuid].ProjectName);
information.IdGuid = Guid.NewGuid(); information.IdGuid = Guid.NewGuid();
fixedGuidConflicts = true; fixedGuidConflicts = true;
} }
@ -131,9 +132,11 @@ namespace ICSharpCode.SharpDevelop.Project
// Now that the project configurations have been set, we can actually load the projects: // Now that the project configurations have been set, we can actually load the projects:
int projectsLoaded = 0; int projectsLoaded = 0;
foreach (var projectInfo in solutionEntries) { foreach (var projectInfo in solutionEntries) {
// Make copy of IdGuid just in case the project binding writes to projectInfo.IdGuid
Guid idGuid = projectInfo.IdGuid;
ISolutionItem solutionItem; ISolutionItem solutionItem;
if (projectInfo.TypeGuid == ProjectTypeGuids.SolutionFolder) { if (projectInfo.TypeGuid == ProjectTypeGuids.SolutionFolder) {
solutionItem = solutionFolderDict[projectInfo.IdGuid]; solutionItem = solutionFolderDict[idGuid];
} else { } else {
// Load project: // Load project:
projectInfo.ActiveProjectConfiguration = projectInfo.ConfigurationMapping.GetProjectConfiguration(solution.ActiveConfiguration); projectInfo.ActiveProjectConfiguration = projectInfo.ConfigurationMapping.GetProjectConfiguration(solution.ActiveConfiguration);
@ -141,12 +144,29 @@ namespace ICSharpCode.SharpDevelop.Project
using (projectInfo.ProgressMonitor = progress.CreateSubTask(1.0 / projectCount)) { using (projectInfo.ProgressMonitor = progress.CreateSubTask(1.0 / projectCount)) {
solutionItem = LoadProjectWithErrorHandling(projectInfo); solutionItem = LoadProjectWithErrorHandling(projectInfo);
} }
if (solutionItem.IdGuid != idGuid) {
Guid projectFileGuid = solutionItem.IdGuid;
if (!projectInfoDict.ContainsKey(projectFileGuid)) {
// We'll use the GUID from the project file.
// Register that GUID in the dictionary to avoid its use by multiple projects.
projectInfoDict.Add(projectFileGuid, projectInfo);
} else {
// Cannot use GUID from project file due to conflict.
// To fix the problem without potentially introducing new conflicts in other .sln files that contain the project,
// we generate a brand new GUID:
solutionItem.IdGuid = Guid.NewGuid();
}
SD.Log.WarnFormatted("<ProjectGuid> in project '{0}' is '{1}' and does not match the GUID stored in the solution ({2}). "
+ "The conflict was resolved using the GUID {3}",
projectInfo.ProjectName, projectFileGuid, idGuid, solutionItem.IdGuid);
fixedGuidConflicts = true;
}
projectsLoaded++; projectsLoaded++;
progress.Progress = (double)projectsLoaded / projectCount; progress.Progress = (double)projectsLoaded / projectCount;
} }
// Add solutionItem to solution: // Add solutionItem to solution:
SolutionFolder folder; SolutionFolder folder;
if (guidToParentFolderDict != null && guidToParentFolderDict.TryGetValue(projectInfo.IdGuid, out folder)) { if (guidToParentFolderDict != null && guidToParentFolderDict.TryGetValue(idGuid, out folder)) {
folder.Items.Add(solutionItem); folder.Items.Add(solutionItem);
} else { } else {
solution.Items.Add(solutionItem); solution.Items.Add(solutionItem);

Loading…
Cancel
Save