Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/VisualStudio/ProjectBase/ProjectNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6701,6 +6701,14 @@ public virtual IVsBuildDependency[] BuildDependencies
var projectInfo = ProjectInfo.GetProjectInfo(url);
if (projectInfo == null)
{
if (node.ReferencedProjectGuid == Guid.Empty)
{
// The referenced project has not been loaded, so we do not know its guid yet.
// Do not register a ProjectInfo with an empty guid: that would be cached by url
// and would prevent the guid from being resolved later on.
Logger.Information($"No BuildDependency for project {this.Caption} on {url}: the guid of the referenced project is unknown");
continue;
}
projectInfo = new ProjectInfo(node.ReferencedProjectGuid, url);
}
var dependency = new BuildDependency(this, projectInfo.Id);
Expand Down
55 changes: 45 additions & 10 deletions src/VisualStudio/ProjectBase/ProjectReferenceNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,36 @@ public void DropReferencedProjectCache()
referencedProjectIsCached = false;
}

/// <summary>
/// Assign the guid of the referenced project.
/// </summary>
/// <remarks>
/// SDK style project files do not store the guid of the referenced project, so it can only be
/// resolved when the referenced project has been loaded. This also updates the build dependency,
/// which could not be created when the node was constructed without a guid.
/// </remarks>
public void UpdateReferencedProjectGuid(Guid guid)
{
if (guid == this.referencedProjectGuid)
{
return;
}
ThreadHelper.ThrowIfNotOnUIThread();
if (this.buildDependency != null)
{
this.ProjectMgr.RemoveBuildDependency(this.buildDependency);
this.buildDependency = null;
}
this.referencedProjectGuid = guid;
if (guid != Guid.Empty)
{
this.buildDependency = new BuildDependency(this.ProjectMgr, guid);
this.ProjectMgr.AddBuildDependency(this.buildDependency);
}
this.DropReferencedProjectCache();
this.ReDraw(UIHierarchyElement.Icon);
}


EnvDTE.Property GetPropertySave(string name, bool onConfig)
{
Expand Down Expand Up @@ -399,21 +429,26 @@ public ProjectReferenceNode(ProjectNode root, ProjectElement element)
ReferencedProjectName = Path.GetFileNameWithoutExtension(referencedProjectRelativePath);
}

// Continue even if project settings cannot be read.
try
// An SDK style project file does not store the guid of the referenced project, so we may
// not have one here, for example when the referenced project has not been loaded yet.
// Do not throw in that case: the node must appear in the hierarchy anyway. The guid is
// filled in later, see XSharpProjectNode.FixReferences() and UpdateReferencedProjectGuid().
if (!Guid.TryParse(guidString, out var guid))
{
guid = Guid.Empty;
}
this.referencedProjectGuid = guid;
if (guid != Guid.Empty)
{
this.referencedProjectGuid = new Guid(guidString);

this.buildDependency = new BuildDependency(this.ProjectMgr, this.referencedProjectGuid);
this.ProjectMgr.AddBuildDependency(this.buildDependency);
}
finally
var name = this.ItemNode.GetMetadata(ProjectFileConstants.Name);
if (!string.IsNullOrEmpty(name))
{
//Debug.Assert(this.referencedProjectGuid != Guid.Empty, "Could not retrieve referenced project guidproject file");

this.ReferencedProjectName = this.ItemNode.GetMetadata(ProjectFileConstants.Name);

//Debug.Assert(!String.IsNullOrEmpty(this.referencedProjectName), "Could not retrieve referenced project name form project file");
// Do not clear the name that was determined above when the metadata could not be written,
// which happens for imported items.
this.ReferencedProjectName = name;
}

Uri uri = new Uri(this.ProjectMgr.BaseURI.Uri, this.referencedProjectRelativePath);
Expand Down
21 changes: 12 additions & 9 deletions src/VisualStudio/ProjectPackage/Nodes/XSharpProjectNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1490,16 +1490,19 @@ internal bool FixReferences()
var refnode = FindProject(completePath);
Guid refnodeGuid = Guid.Empty;
string refnodeName = child.Caption;
if (refnode == null)
if (refnode != null)
{
refnodeGuid = refnode.ProjectIDGuid;
}
else
{
// this must be a foreign project reference
var projectInfo = ProjectInfo.GetProjectInfo(completePath);
if (projectInfo == null)
{
if (this.GetProjectGuid(completePath, out refnodeGuid))
if (this.GetProjectGuid(completePath, out refnodeGuid) && refnodeGuid != Guid.Empty)
{
projectInfo = new ProjectInfo(refnodeGuid, completePath);
element.SetMetadata(ProjectFileConstants.Project, refnodeGuid.ToString("B").ToUpperInvariant());
}
}
else
Expand All @@ -1511,19 +1514,19 @@ internal bool FixReferences()
{
element.SetMetadata(ProjectFileConstants.Project, refnodeGuid.ToString("B").ToUpperInvariant());
element.SetMetadata(ProjectFileConstants.Name, refnodeName);
sdkref.SaveProperties();
// The node was created before the referenced project was available, so it has no
// guid and no build dependency yet.
sdkref.UpdateReferencedProjectGuid(refnodeGuid);
}
else
{
Logger.Information($"Could not determine the guid of project {completePath}, referenced by {this.Caption}");
found = false;
}
if (found)
{
sdkref.SaveProperties();
}
}

HasIncompleteReferences = !found;
}
HasIncompleteReferences = !found;
this.SetProjectFileDirty(false);
return found;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,40 @@ protected override ProjectReferenceNode CreateProjectReferenceNode(ProjectElemen
bool changed = false;
if (parent.IsSdkProject)
{
// already handled in the DependenciesContainer
// An SDK style project file does not store the guid and the name of the referenced project,
// so we resolve them here and store them in the (in memory) project element. The base
// ProjectReferenceNode reads them from there. BeforeSave() removes them again, so they are
// not written to the project file, see XSharpSdkProjectNode.Clean().
name = System.IO.Path.GetFileNameWithoutExtension(path);
guid = null;
if (refnode != null)
{
guid = refnode.ProjectIDGuid.ToString("B");
}
else
{
guid = null;
// Not one of our projects, or a project that has not been loaded yet
var projectInfo = ProjectInfo.GetProjectInfo(path);
if (projectInfo == null && parent.GetProjectGuid(path, out var foreignGuid) && foreignGuid != Guid.Empty)
{
projectInfo = new ProjectInfo(foreignGuid, path);
}
if (projectInfo != null && projectInfo.Id != Guid.Empty)
{
guid = projectInfo.Id.ToString("B");
}
}
element.SetMetadata(ProjectFileConstants.Name, name);
if (string.IsNullOrEmpty(guid))
{
// The referenced project is not available yet. The node is created without a guid and
// FixReferences() completes it when the solution has finished loading.
parent.HasIncompleteReferences = true;
}
else
{
element.SetMetadata(ProjectFileConstants.Project, guid);
}
}
else if (string.IsNullOrEmpty(guid) || string.IsNullOrEmpty(name))
{
Expand Down Expand Up @@ -84,7 +107,17 @@ protected override ProjectReferenceNode CreateProjectReferenceNode(ProjectElemen
parent.BuildProject.Save();
}

var node = (XSharpProjectReferenceNode) Activator.CreateInstance(ProjectReferenceType,this.ProjectMgr, element);
XSharpProjectReferenceNode node = null;
try
{
node = (XSharpProjectReferenceNode)Activator.CreateInstance(ProjectReferenceType, this.ProjectMgr, element);
}
catch (Exception e)
{
// Do not let this bubble up: ReferenceContainerNode.CreateReferenceNode() swallows the
// exception without logging it and the reference would disappear from the hierarchy.
Logger.Exception(e, "CreateProjectReferenceNode");
}
ReferenceNode existing = null;
if (isDuplicateNode(node, ref existing))
{
Expand Down
17 changes: 14 additions & 3 deletions src/VisualStudio/ProjectPackage/XSharpShellEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,23 @@ internal void Initialize()
#if DEV17
private void SolutionEvents_OnAfterOpenSolution(Solution solution)
{
FixIncompleteReferences();
}

/// <summary>
/// Complete the project references of SDK style projects that could not be resolved while the
/// project was loading, because the referenced project was not loaded yet.
/// </summary>
private void FixIncompleteReferences()
{
foreach (var project in XSharpProjectNode.AllProjects)
{
if (project.HasIncompleteReferences)
{

project.FixReferences();
}
}

}
}
#endif
private void SolutionEvents_OnBeforeCloseSolution()
{
Expand Down Expand Up @@ -129,6 +135,11 @@ private void SolutionEvents_OnBeforeCloseSolution()
private void SolutionEvents_OnAfterBackgroundSolutionLoadComplete()
{
ThreadHelper.ThrowIfNotOnUIThread();
#if DEV17
// Projects may have been loaded after OnAfterOpenSolution, so check again now that all
// projects of the solution are available.
FixIncompleteReferences();
#endif
RestoreDesignerWindows();
RestoreStartupProject();
}
Expand Down