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 @@ -56,6 +56,7 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion
public virtual void Complete(CompletionContext context)
{
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;
}
}

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

@ -36,7 +36,8 @@ namespace ICSharpCode.SharpDevelop.Project @@ -36,7 +36,8 @@ namespace ICSharpCode.SharpDevelop.Project
/// <summary>
/// Gets the ID GUID of this solution item.
/// </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>
/// 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 @@ -122,6 +122,19 @@ namespace ICSharpCode.SharpDevelop.Project
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 {
get { return projectFile.ToolsVersion; }
protected internal set {
@ -181,6 +194,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -181,6 +194,7 @@ namespace ICSharpCode.SharpDevelop.Project
else
SetProperty(null, platform, "PlatformTarget", "AnyCPU", PropertyStorageLocations.PlatformSpecific, false);
LoadConfigurationPlatformNamesFromMSBuild();
isLoading = false;
}
/// <summary>
@ -914,7 +928,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -914,7 +928,7 @@ namespace ICSharpCode.SharpDevelop.Project
propertyRemoved = true;
}
}
if (propertyRemoved && propertyGroup.Children.Count() == 0)
if (propertyRemoved && propertyGroup.Children.Count == 0)
project.RemoveChild(propertyGroup);
}
}
@ -1188,7 +1202,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -1188,7 +1202,7 @@ namespace ICSharpCode.SharpDevelop.Project
#endregion
#region Loading
protected bool isLoading;
protected bool isLoading = true;
public MSBuildBasedProject(ProjectLoadInformation loadInformation)
: base(loadInformation)
@ -1197,7 +1211,6 @@ namespace ICSharpCode.SharpDevelop.Project @@ -1197,7 +1211,6 @@ namespace ICSharpCode.SharpDevelop.Project
this.configurationNames = new MSBuildConfigurationOrPlatformNameCollection(this, false);
this.platformNames = new MSBuildConfigurationOrPlatformNameCollection(this, true);
isLoading = true;
bool success = false;
try {
try {
@ -1277,6 +1290,15 @@ namespace ICSharpCode.SharpDevelop.Project @@ -1277,6 +1290,15 @@ namespace ICSharpCode.SharpDevelop.Project
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();
LoadConfigurationPlatformNamesFromMSBuild();
}

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

@ -30,7 +30,8 @@ namespace ICSharpCode.SharpDevelop.Project @@ -30,7 +30,8 @@ namespace ICSharpCode.SharpDevelop.Project
IProject LoadProject(ProjectLoadInformation info)
{
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.ParentSolution).Return(info.Solution);
project.Stub(p => p.ParentFolder).PropertyBehavior();
@ -67,10 +68,14 @@ namespace ICSharpCode.SharpDevelop.Project @@ -67,10 +68,14 @@ namespace ICSharpCode.SharpDevelop.Project
solution.AddExistingProject(project1FileName);
}
// [Test]
// public void AddTwoProjectsWithSameGUID()
// {
//
// }
[Test]
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 @@ -9,12 +9,11 @@ namespace ICSharpCode.SharpDevelop.Project
class SolutionFileItem : ISolutionFileItem
{
readonly Solution parentSolution;
readonly Guid idGuid;
public SolutionFileItem(Solution parentSolution)
{
this.parentSolution = parentSolution;
this.idGuid = Guid.NewGuid();
this.IdGuid = Guid.NewGuid();
}
FileName fileName;
@ -35,9 +34,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -35,9 +34,7 @@ namespace ICSharpCode.SharpDevelop.Project
get { return parentSolution; }
}
public Guid IdGuid {
get { return idGuid; }
}
public Guid IdGuid { get; set; }
public Guid TypeGuid {
get { return Guid.Empty; }

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

@ -16,7 +16,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -16,7 +16,7 @@ namespace ICSharpCode.SharpDevelop.Project
class SolutionFolder : ISolutionFolder
{
readonly Solution parentSolution;
readonly Guid idGuid;
Guid idGuid;
public SolutionFolder(Solution parentSolution, Guid idGuid)
{
@ -114,6 +114,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -114,6 +114,7 @@ namespace ICSharpCode.SharpDevelop.Project
public Guid IdGuid {
get { return idGuid; }
set { idGuid = value; }
}
public Guid TypeGuid {
@ -135,7 +136,10 @@ namespace ICSharpCode.SharpDevelop.Project @@ -135,7 +136,10 @@ namespace ICSharpCode.SharpDevelop.Project
throw new ProjectLoadException("Project " + fileName + " is already part of this solution.");
ProjectLoadInformation loadInfo = new ProjectLoadInformation(parentSolution, fileName, fileName.GetFileNameWithoutExtension());
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);
project.ProjectLoaded();
ProjectBrowserPad.RefreshViewAsync();

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

@ -74,6 +74,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -74,6 +74,7 @@ namespace ICSharpCode.SharpDevelop.Project
solutionEntries.Add(information);
if (projectInfoDict.ContainsKey(information.IdGuid)) {
// 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();
fixedGuidConflicts = true;
}
@ -131,9 +132,11 @@ namespace ICSharpCode.SharpDevelop.Project @@ -131,9 +132,11 @@ namespace ICSharpCode.SharpDevelop.Project
// Now that the project configurations have been set, we can actually load the projects:
int projectsLoaded = 0;
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;
if (projectInfo.TypeGuid == ProjectTypeGuids.SolutionFolder) {
solutionItem = solutionFolderDict[projectInfo.IdGuid];
solutionItem = solutionFolderDict[idGuid];
} else {
// Load project:
projectInfo.ActiveProjectConfiguration = projectInfo.ConfigurationMapping.GetProjectConfiguration(solution.ActiveConfiguration);
@ -141,12 +144,29 @@ namespace ICSharpCode.SharpDevelop.Project @@ -141,12 +144,29 @@ namespace ICSharpCode.SharpDevelop.Project
using (projectInfo.ProgressMonitor = progress.CreateSubTask(1.0 / projectCount)) {
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++;
progress.Progress = (double)projectsLoaded / projectCount;
}
// Add solutionItem to solution:
SolutionFolder folder;
if (guidToParentFolderDict != null && guidToParentFolderDict.TryGetValue(projectInfo.IdGuid, out folder)) {
if (guidToParentFolderDict != null && guidToParentFolderDict.TryGetValue(idGuid, out folder)) {
folder.Items.Add(solutionItem);
} else {
solution.Items.Add(solutionItem);

Loading…
Cancel
Save