Make ContentCompilerGui compatible to recent changes in pipeline and did some usability changes. Make the Visual Studio Extension work even if the ANX Framework is not installed additionally.. Improve that the path for assembly refernces in a content project doesn't get automatically updated, only if the reference is actually saved, this is so you can specify a relative path yourself. Fix missing icon for ContentProject when it was opened with Visual Studio. Made create_shaders.bat directly executable under windows by fixing the directory separators.
346 lines
15 KiB
C#
346 lines
15 KiB
C#
using ANX.Framework.Build;
|
|
using Microsoft.VisualStudio.Shell.Interop;
|
|
using Microsoft.VisualStudio;
|
|
using Microsoft.VisualStudio.Project;
|
|
using Microsoft.VisualStudio.Project.Automation;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.Composition;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using ANX.Framework.VisualStudio.Nodes;
|
|
using ANX.Framework.Content.Pipeline.Tasks.References;
|
|
using System.Diagnostics;
|
|
|
|
namespace ANX.Framework.VisualStudio.Nodes
|
|
{
|
|
class ContentProjectReferenceContainer : ReferenceContainerNode
|
|
{
|
|
ContentProjectNode contentProjectNode;
|
|
|
|
public bool Loading
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
protected void SetProjectDirty()
|
|
{
|
|
if (Loading)
|
|
return;
|
|
|
|
this.ProjectMgr.SetProjectFileDirty(true);
|
|
}
|
|
|
|
public ContentProjectReferenceContainer(ContentProjectNode root)
|
|
: base(root)
|
|
{
|
|
this.contentProjectNode = root;
|
|
|
|
this.OnChildAdded += ContentProjectReferenceContainer_OnChildAdded;
|
|
this.OnChildRemoved += ContentProjectReferenceContainer_OnChildRemoved;
|
|
}
|
|
|
|
public void LoadReferencesFromContentProject()
|
|
{
|
|
Loading = true;
|
|
|
|
try
|
|
{
|
|
foreach (var item in contentProjectNode.ContentProject.References)
|
|
{
|
|
ReferenceNode node = null;
|
|
if (item is AssemblyReference)
|
|
{
|
|
/*var element = new MsBuildProjectElement(this.ProjectMgr, AssemblyName.GetAssemblyName(item.AssemblyPath).FullName, ProjectFileConstants.ProjectReference);
|
|
|
|
element.SetMetadata(ProjectFileConstants.Name, item.Name);
|
|
element.SetMetadata(ProjectFileConstants.HintPath, item.AssemblyPath);
|
|
element.SetMetadata(ProjectFileConstants.Private, false.ToString()); //By default, never copy the assembly to the output directory for content projects, even if it's not a framework assembly.
|
|
element.SetMetadata(ProjectFileConstants.AssemblyName, Path.GetFileName(item.AssemblyPath));
|
|
node = CreateReferenceNode(ProjectFileConstants.Reference, element);*/
|
|
|
|
var reference = (AssemblyReference)item;
|
|
|
|
node = CreateAssemblyReferenceNode(reference.Name, reference.AssemblyPath);
|
|
}
|
|
else if (item is GACReference)
|
|
{
|
|
var reference = (GACReference)item;
|
|
|
|
node = CreateAssemblyReferenceNode(reference.Name, reference.AssemblyName);
|
|
}
|
|
else if (item is ProjectReference)
|
|
{
|
|
var reference = (ProjectReference)item;
|
|
|
|
/*var element = new MsBuildProjectElement(this.ProjectMgr, reference.Include, ProjectFileConstants.ProjectReference);
|
|
element.SetMetadata(ProjectFileConstants.Project, reference.Guid.ToString());
|
|
element.SetMetadata(ProjectFileConstants.Name, item.Name);
|
|
element.SetMetadata(ProjectFileConstants.Private, false.ToString()); //By default, never copy the assembly to the output directory for content projects, even if it's not a framework assembly.
|
|
node = CreateReferenceNode(ProjectFileConstants.ProjectReference, element);*/
|
|
|
|
string path = string.Empty;
|
|
string uniqueName = string.Empty;
|
|
try
|
|
{
|
|
path = CommonUtils.GetAbsoluteFilePath(Path.GetDirectoryName(this.ProjectMgr.Url), reference.Include);
|
|
}
|
|
catch { }
|
|
try
|
|
{
|
|
uniqueName = reference.Guid.ToString("B") + "|" + CommonUtils.TrimUpPaths(reference.Include);
|
|
}
|
|
catch { }
|
|
|
|
VSCOMPONENTSELECTORDATA selectorData = new VSCOMPONENTSELECTORDATA()
|
|
{
|
|
bstrTitle = reference.Name,
|
|
bstrFile = path,
|
|
bstrProjRef = uniqueName
|
|
};
|
|
node = CreateProjectReferenceNode(selectorData);
|
|
}
|
|
|
|
if (node != null)
|
|
{
|
|
|
|
|
|
// Make sure that we do not want to add the item twice to the ui hierarchy
|
|
// We are using here the UI representation of the Node namely the Caption to find that out, in order to
|
|
// avoid different representation problems.
|
|
// Example :<Reference Include="EnvDTE80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
|
// <Reference Include="EnvDTE80" />
|
|
bool found = false;
|
|
for (HierarchyNode n = this.FirstChild; n != null && !found; n = n.NextSibling)
|
|
{
|
|
if (String.Compare(n.Caption, node.Caption, StringComparison.OrdinalIgnoreCase) == 0)
|
|
{
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
this.AddChild(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
//After all references have been added, we can load them.
|
|
this.LoadAssemblies(this.EnumReferences().ToArray());
|
|
}
|
|
finally
|
|
{
|
|
Loading = false;
|
|
}
|
|
}
|
|
|
|
void ContentProjectReferenceContainer_OnChildAdded(object sender, HierarchyNodeEventArgs e)
|
|
{
|
|
this.SetProjectDirty();
|
|
|
|
//Wait with loading the refernces until the project finished loading.
|
|
if (this.Loading)
|
|
return;
|
|
|
|
LoadReference((ReferenceNode)e.Child);
|
|
}
|
|
|
|
void LoadReference(ReferenceNode node)
|
|
{
|
|
if (node is AnxAssemblyReferenceNode)
|
|
{
|
|
var assemblyReference = (AnxAssemblyReferenceNode)node;
|
|
assemblyReference.ItemNode.SetMetadata(ProjectFileConstants.Private, false.ToString()); //By default, never copy the assembly to the output directory for content projects, even if it's not a framework assembly.
|
|
|
|
if (assemblyReference.IsValid)
|
|
{
|
|
//Keep the ui thread responsive while the tasks battle over the lock for the appDomain.
|
|
LoadAssemblies(assemblyReference);
|
|
}
|
|
}
|
|
else if (node is AnxProjectReferenceNode)
|
|
{
|
|
var projectReference = (AnxProjectReferenceNode)node;
|
|
projectReference.ItemNode.SetMetadata(ProjectFileConstants.Private, false.ToString()); //By default, never copy the assembly to the output directory for content projects, even if it's not a framework assembly.
|
|
|
|
if (File.Exists(projectReference.ReferencedProjectOutputPath))
|
|
{
|
|
LoadAssemblies(projectReference);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ContentProjectReferenceContainer_OnChildRemoved(object sender, HierarchyNodeEventArgs e)
|
|
{
|
|
this.SetProjectDirty();
|
|
|
|
this.RefreshAssemblies(new List<HierarchyNode>(new [] { e.Child }));
|
|
}
|
|
|
|
public void RefreshAssemblies(IEnumerable<HierarchyNode> exclude = null)
|
|
{
|
|
using (var buildDomain = this.contentProjectNode.BuildAppDomain.Aquire())
|
|
{
|
|
buildDomain.Unload();
|
|
buildDomain.Initialize(this.contentProjectNode.ProjectGuid.ToString());
|
|
}
|
|
|
|
List<ReferenceNode> referencesToRefresh = new List<ReferenceNode>();
|
|
foreach (ReferenceNode reference in this.EnumReferences())
|
|
{
|
|
if (exclude != null && exclude.Contains(reference))
|
|
continue;
|
|
|
|
if (reference is AnxAssemblyReferenceNode)
|
|
{
|
|
var assemblyReference = (AnxAssemblyReferenceNode)reference;
|
|
assemblyReference.ItemNode.SetMetadata(ProjectFileConstants.Private, false.ToString()); //By default, never copy the assembly to the output directory for content projects, even if it's not a framework assembly.
|
|
|
|
if (assemblyReference.IsValid)
|
|
{
|
|
referencesToRefresh.Add(reference);
|
|
}
|
|
}
|
|
else if (reference is AnxProjectReferenceNode)
|
|
{
|
|
var projectReference = (AnxProjectReferenceNode)reference;
|
|
projectReference.ItemNode.SetMetadata(ProjectFileConstants.Private, false.ToString()); //By default, never copy the assembly to the output directory for content projects, even if it's not a framework assembly.
|
|
|
|
if (File.Exists(projectReference.ReferencedProjectOutputPath))
|
|
{
|
|
referencesToRefresh.Add(reference);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (referencesToRefresh.Count > 0)
|
|
{
|
|
LoadAssemblies(referencesToRefresh.ToArray());
|
|
}
|
|
}
|
|
|
|
public Task LoadAssemblies(params ReferenceNode[] references)
|
|
{
|
|
return Task.Run(() =>
|
|
{
|
|
var allReferences = this.EnumNodesOfType<AnxAssemblyReferenceNode>().ToArray();
|
|
|
|
using (var buildDomain = this.contentProjectNode.BuildAppDomain.Aquire())
|
|
{
|
|
Dictionary<string, string> assemblyNamesAndPaths = new Dictionary<string, string>();
|
|
foreach (var reference in references)
|
|
{
|
|
//Running asynchronously, so at least write to console.
|
|
//And we also don't want to stop at the first reference that creates problems.
|
|
try
|
|
{
|
|
string path = null;
|
|
string name = null;
|
|
if (reference is AnxAssemblyReferenceNode)
|
|
{
|
|
var assemblyReference = (AnxAssemblyReferenceNode)reference;
|
|
assemblyReference.RefreshReference();
|
|
|
|
if (assemblyReference.AssemblyName == null)
|
|
continue;
|
|
|
|
path = assemblyReference.FullPath;
|
|
name = assemblyReference.AssemblyName.FullName;
|
|
|
|
if (string.IsNullOrEmpty(path))
|
|
path = name;
|
|
}
|
|
else if (reference is AnxProjectReferenceNode)
|
|
{
|
|
var projectReference = (AnxProjectReferenceNode)reference;
|
|
projectReference.RefreshReference();
|
|
|
|
path = projectReference.ReferencedProjectOutputPath;
|
|
if (File.Exists(path))
|
|
{
|
|
name = AssemblyName.GetAssemblyName(path).FullName;
|
|
}
|
|
}
|
|
|
|
if (name != null && path != null && File.Exists(path))
|
|
{
|
|
foreach (var assemblyName in buildDomain.Proxy.GetReferencedAssemblies(path))
|
|
{
|
|
if (!assemblyNamesAndPaths.ContainsKey(assemblyName.FullName))
|
|
{
|
|
string assemblyIdentifier = assemblyName.FullName;
|
|
|
|
foreach (var referenceEntry in allReferences)
|
|
{
|
|
if (referenceEntry.AssemblyName != null && referenceEntry.AssemblyName.FullName == assemblyName.FullName && File.Exists(referenceEntry.Url))
|
|
{
|
|
assemblyIdentifier = referenceEntry.Url;
|
|
break;
|
|
}
|
|
}
|
|
|
|
assemblyNamesAndPaths[assemblyName.FullName] = assemblyIdentifier;
|
|
}
|
|
}
|
|
|
|
assemblyNamesAndPaths[name] = path;
|
|
}
|
|
|
|
var callback = new LoadAssembliesCallback(this);
|
|
buildDomain.Proxy.LoadProjectAssemblies(assemblyNamesAndPaths.Values, buildDomain.SearchPaths, buildDomain.Redirects, callback.ErrorCallback);
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
Debugger.Break();
|
|
Console.WriteLine(exc.Message);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
class LoadAssembliesCallback : MarshalByRefObject
|
|
{
|
|
ContentProjectReferenceContainer parent;
|
|
|
|
internal LoadAssembliesCallback(ContentProjectReferenceContainer parent)
|
|
{
|
|
this.parent = parent;
|
|
}
|
|
|
|
public void ErrorCallback(string assembly, Exception exc)
|
|
{
|
|
ErrorLoggingHelper loggingHelper = new ErrorLoggingHelper(parent.contentProjectNode, parent.contentProjectNode.ErrorListProvider);
|
|
|
|
loggingHelper.LogWarning(null, null, null, assembly, -1, -1, exc.Message);
|
|
}
|
|
}
|
|
|
|
protected override AssemblyReferenceNode CreateAssemblyReferenceNode(ProjectElement element)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
protected override AssemblyReferenceNode CreateAssemblyReferenceNode(string name, string fileName)
|
|
{
|
|
return new AnxAssemblyReferenceNode(this.contentProjectNode, name, fileName);
|
|
}
|
|
|
|
protected override ProjectReferenceNode CreateProjectReferenceNode(ProjectElement element)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
protected override ProjectReferenceNode CreateProjectReferenceNode(VSCOMPONENTSELECTORDATA selectorData)
|
|
{
|
|
return new AnxProjectReferenceNode(this.contentProjectNode, selectorData.bstrTitle, selectorData.bstrFile, selectorData.bstrProjRef);
|
|
}
|
|
}
|
|
}
|