diff --git a/src/VisualStudio/ProjectBase/ProjectNode.cs b/src/VisualStudio/ProjectBase/ProjectNode.cs
index 4467c3b8d0..79df53994c 100644
--- a/src/VisualStudio/ProjectBase/ProjectNode.cs
+++ b/src/VisualStudio/ProjectBase/ProjectNode.cs
@@ -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);
diff --git a/src/VisualStudio/ProjectBase/ProjectReferenceNode.cs b/src/VisualStudio/ProjectBase/ProjectReferenceNode.cs
index 803a943a0d..20c60bea25 100644
--- a/src/VisualStudio/ProjectBase/ProjectReferenceNode.cs
+++ b/src/VisualStudio/ProjectBase/ProjectReferenceNode.cs
@@ -252,6 +252,36 @@ public void DropReferencedProjectCache()
referencedProjectIsCached = false;
}
+ ///
+ /// Assign the guid of the referenced project.
+ ///
+ ///
+ /// 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.
+ ///
+ 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)
{
@@ -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);
diff --git a/src/VisualStudio/ProjectPackage/Nodes/XSharpProjectNode.cs b/src/VisualStudio/ProjectPackage/Nodes/XSharpProjectNode.cs
index bc3de027d9..7ade0d6c35 100644
--- a/src/VisualStudio/ProjectPackage/Nodes/XSharpProjectNode.cs
+++ b/src/VisualStudio/ProjectPackage/Nodes/XSharpProjectNode.cs
@@ -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
@@ -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;
}
diff --git a/src/VisualStudio/ProjectPackage/Nodes/XSharpReferenceContainerNode.cs b/src/VisualStudio/ProjectPackage/Nodes/XSharpReferenceContainerNode.cs
index 817266d169..575aa290b8 100644
--- a/src/VisualStudio/ProjectPackage/Nodes/XSharpReferenceContainerNode.cs
+++ b/src/VisualStudio/ProjectPackage/Nodes/XSharpReferenceContainerNode.cs
@@ -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))
{
@@ -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))
{
diff --git a/src/VisualStudio/ProjectPackage/XSharpShellEvents.cs b/src/VisualStudio/ProjectPackage/XSharpShellEvents.cs
index 5783cfcd53..112d7cd99e 100644
--- a/src/VisualStudio/ProjectPackage/XSharpShellEvents.cs
+++ b/src/VisualStudio/ProjectPackage/XSharpShellEvents.cs
@@ -86,17 +86,23 @@ internal void Initialize()
#if DEV17
private void SolutionEvents_OnAfterOpenSolution(Solution solution)
{
+ FixIncompleteReferences();
+ }
+ ///
+ /// 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.
+ ///
+ private void FixIncompleteReferences()
+ {
foreach (var project in XSharpProjectNode.AllProjects)
{
if (project.HasIncompleteReferences)
{
-
project.FixReferences();
}
}
-
- }
+ }
#endif
private void SolutionEvents_OnBeforeCloseSolution()
{
@@ -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();
}