Konstantin Koch 805b7032be Fix AudioSample
Convert AudioSample Project file from XNA to ANX and don't throw an
exception if a PCM encoded Wave File should be loaded because it can't
be converted to PCM.
Additionally, the Visual Studio extension will now automatically filter
out unsupported platforms, atleast for now, otherwise the build breaks.
2015-09-06 01:11:09 +02:00

942 lines
38 KiB
C#

using ANX.Framework.Build;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Project;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants;
using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID;
using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID;
using Microsoft.Build.Utilities;
using ANX.Framework.VisualStudio;
using Microsoft.Win32;
using ANX.Framework.Content.Pipeline.Tasks;
using ANX.Framework.Content.Pipeline.Tasks.References;
using ANX.Framework.Content.Pipeline;
using ANX.Framework.Content.Pipeline.Helpers;
using Microsoft.VisualStudio.Project.Automation;
using ContentBuilder;
namespace ANX.Framework.VisualStudio.Nodes
{
[Guid(GuidList.guidANXVisualStudio2012ContentProjectString)]
public class ContentProjectNode : CommonProjectNode
{
ContentProject anxContentProject = null;
BuildAppDomain buildAppDomain;
/// <summary>
/// List of output groups names and their associated target
/// </summary>
private static KeyValuePair<string, string>[] outputGroupNames =
{ // Name Target (MSBuild)
new KeyValuePair<string, string>("Built", "BuiltProjectOutputGroup"),
new KeyValuePair<string, string>("ContentFiles", "ContentFilesProjectOutputGroup"),
};
public ContentProjectNode(CommonProjectPackage package)
: base(package, VsUtilities.GetImageList(File.OpenRead(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "anx.ico"))))
{
buildAppDomain = new BuildAppDomain(this);
//Also allows using the delete key on a node.
this.CanProjectDeleteItems = true;
this.InitilalizeCATIDs();
}
private void InitilalizeCATIDs()
{
//AddCATIDMapping(typeof(ContentProjectSettingsPage), typeof(ContentProjectSettingsPage).GUID);
AddCATIDMapping(typeof(ConfigurableContentProjectSettingsPage), typeof(ConfigurableContentProjectSettingsPage).GUID);
//AddCATIDMapping(typeof(BuildEventsPropPageComClass), typeof(BuildEventsPropPageComClass).GUID);
//AddCATIDMapping(typeof(ReferencePathsPropPageComClass), typeof(ReferencePathsPropPageComClass).GUID);
}
public BuildLaunch PendingBuild
{
get;
private set;
}
public EnvDTE._DTE DTE
{
get
{
return this.Site.GetService(typeof(EnvDTE._DTE)) as EnvDTE._DTE;
}
}
public void InitiateFileBuild(IEnumerable<string> files, bool debug)
{
var activeConfig = this.ActiveContentConfiguration;
var dte = this.Site.GetService(typeof(EnvDTE._DTE)) as EnvDTE._DTE;
//WaitForBuildToFinish = false is important because otherwise, the debugger hangs when started with this build.
string solutionConfiguration = activeConfig.Name;
string projectUniqueName = ((OAProject)this.GetAutomationObject()).UniqueName;
this.PendingBuild = new BuildLaunch(debug, files);
dte.Solution.SolutionBuild.BuildProject(solutionConfiguration, projectUniqueName, WaitForBuildToFinish: false);
}
public void InitiateBuild(bool debug)
{
var activeConfig = this.ActiveContentConfiguration;
var dte = this.Site.GetService(typeof(EnvDTE._DTE)) as EnvDTE._DTE;
//WaitForBuildToFinish = false is important because otherwise, the debugger hangs when started with this build.
string solutionConfiguration = activeConfig.Name;
this.PendingBuild = new BuildLaunch(debug);
dte.Solution.SolutionBuild.BuildProject(solutionConfiguration, ((OAProject)this.GetAutomationObject()).UniqueName, false);
}
protected override Guid[] GetConfigurationIndependentPropertyPages()
{
return new Guid[]
{
//typeof(ContentProjectSettingsPage).GUID,
};
}
protected override Guid[] GetConfigurationDependentPropertyPages()
{
return new[]
{
typeof(ConfigurableContentProjectSettingsPage).GUID,
//typeof(BuildEventsPropPageComClass).GUID,
};
}
public ContentProject ContentProject
{
get { return anxContentProject; }
}
public override int ImageIndex
{
get
{
return 52;
}
}
public virtual string AbsoluteProjectFilePath
{
get
{
return new Uri(new Uri(this.ProjectHome, UriKind.Absolute), new Uri(this.ProjectFile, UriKind.Relative)).LocalPath;
}
}
public ErrorListProvider ErrorListProvider
{
get;
protected set;
}
public BuildAppDomain BuildAppDomain
{
get { return buildAppDomain; }
}
public override Guid ProjectGuid
{
get { return GuidList.guidANXVisualStudio2012ContentProjectFactory; }
}
public override string ProjectType
{
get { return "ANX Content Project"; }
}
public override string Caption
{
get
{
// Default to file name
string caption = this.GetProjectProperty(ProjectFileConstants.Name);
if (String.IsNullOrEmpty(caption))
{
caption = Path.GetFileNameWithoutExtension(this.FileName);
}
return caption;
}
}
public override IList<KeyValuePair<string, string>> GetOutputGroupNames()
{
return outputGroupNames;
}
public Configuration ActiveContentConfiguration
{
get
{
var activeConfig = this.GetProjectProperty(ProjectFileConstants.Configuration);
var activePlatform = this.GetProjectProperty(ProjectFileConstants.Platform);
var config = this.ConfigProvider.FirstOrDefault((x) => x.ConfigName == activeConfig && x.PlatformName == activePlatform) as ContentConfig;
if (config == null)
throw new KeyNotFoundException(string.Format("Error when searching for configuration with name \"{0}\" and platform \"{1}\".", activeConfig, activePlatform));
return config.Configuration;
}
}
/// <summary>
/// Factory method for configuration provider
/// </summary>
/// <returns>Configuration provider created</returns>
protected override ConfigProvider CreateConfigProvider()
{
return new ContentConfigProvider(this);
}
/// <summary>
/// Initialize common project properties with default value if they are empty
/// </summary>
/// <remarks>The following common project properties are defaulted to projectName (if empty):
/// AssemblyName, Name and RootNamespace.
/// If the project filename is not set then no properties are set</remarks>
protected override void InitializeProjectProperties()
{
//base tried to access properties of the project file like assembly name, but we don't load the old ms project.
}
protected override void SaveMSBuildProjectFile(string filename)
{
SaveProjectFile(filename);
}
/// <summary>
/// Saves project file related information to the new file name. It also calls msbuild API to save the project file.
/// It is called by the SaveAs method and the SetEditLabel before the project file rename related events are triggered.
/// An implementer can override this method to provide specialized semantics on how the project file is renamed in the msbuild file.
/// </summary>
/// <param name="newFileName">The new full path of the project file</param>
protected override void SaveMSBuildProjectFileAs(string newFileName)
{
Debug.Assert(!String.IsNullOrEmpty(newFileName), "Cannot save project file for an empty or null file name");
this.FileName = newFileName;
string newFileNameWithoutExtension = Path.GetFileNameWithoutExtension(newFileName);
// Refresh solution explorer
this.SetProjectProperty(ProjectFileConstants.Name, newFileNameWithoutExtension);
// Saves the project file on disk.
this.SaveProjectFile(newFileName);
}
private void SaveProjectFile(string filename)
{
Debug.Assert(!String.IsNullOrEmpty(filename), "Cannot save project file for an empty or null file name");
string newFileNameWithoutExtension = Path.GetFileNameWithoutExtension(filename);
anxContentProject.Name = newFileNameWithoutExtension;
// Refresh solution explorer
this.SetProjectProperty(ProjectFileConstants.Name, newFileNameWithoutExtension);
Uri projectUri = new Uri(ProjectHome);
//Convert from MSBuild back to ContentProject.
anxContentProject.References.Clear();
foreach (var reference in this.GetReferenceContainer().EnumReferences())
{
if (reference is AnxAssemblyReferenceNode)
{
AnxAssemblyReferenceNode assemblyReference = (AnxAssemblyReferenceNode)reference;
if (assemblyReference.IsFrameworkAssembly)
{
GACReference anxReference = new GACReference();
anxReference.Name = assemblyReference.Caption;
anxReference.AssemblyName = assemblyReference.AssemblyName.ToString();
anxContentProject.References.Add(anxReference);
}
else
{
AssemblyReference anxReference = new AssemblyReference();
anxReference.Name = assemblyReference.Caption;
anxReference.AssemblyPath = assemblyReference.OriginalAssemblyPath;
anxContentProject.References.Add(anxReference);
}
}
else if (reference is AnxProjectReferenceNode)
{
AnxProjectReferenceNode projectReferenceNode = (AnxProjectReferenceNode)reference;
ProjectReference anxReference = new ProjectReference()
{
Guid = projectReferenceNode.ReferencedProjectGuid,
Name = projectReferenceNode.ReferencedProjectName,
Include = projectReferenceNode.RelativeUrl,
};
if (!string.IsNullOrEmpty(projectReferenceNode.ReferencedProjectOutputPath))
{
using (var buildDomain = this.BuildAppDomain.Aquire())
{
anxReference.AssemblyPath = buildDomain.MakeRelativeToSearchPaths(projectReferenceNode.ReferencedProjectOutputPath);
}
if (Path.IsPathRooted(anxReference.AssemblyPath))
{
Uri uri = new Uri(projectReferenceNode.ReferencedProjectOutputPath);
anxReference.AssemblyPath = Uri.UnescapeDataString(projectUri.MakeRelativeUri(new Uri(projectReferenceNode.ReferencedProjectOutputPath)).OriginalString);
}
}
anxContentProject.References.Add(anxReference);
}
}
anxContentProject.BuildItems.Clear();
foreach (var file in this.EnumNodesOfType<AssetFileNode>())
{
if (file.IsNonMemberItem)
continue;
BuildItem item = new BuildItem();
item.SourceFilename = CommonUtils.GetRelativeFilePath(this.ProjectFolder, file.Url);
item.AssetName = file.AssetName;
if (string.IsNullOrEmpty(file.ContentImporter))
{
try
{
using (var buildDomain = this.BuildAppDomain.Aquire())
{
file.ContentImporter = buildDomain.Proxy.GuessImporterByFileExtension(file.FileName);
}
}
catch (Exception exc)
{
Trace.TraceWarning(exc.Message);
}
}
if (string.IsNullOrEmpty(file.ContentProcessor))
{
try
{
using (var buildDomain = this.BuildAppDomain.Aquire())
{
file.ContentProcessor = buildDomain.Proxy.GetDefaultProcessorForImporter(file.ContentImporter);
}
}
catch (Exception exc)
{
Trace.TraceWarning(exc.Message);
}
}
item.ProcessorName = file.ContentProcessor;
item.ImporterName = file.ContentImporter;
foreach (var pair in file.ProcessorParameters)
{
item.ProcessorParameters.Add(pair.Key, pair.Value);
}
anxContentProject.BuildItems.Add(item);
}
anxContentProject.Save(filename);
this.SetProjectFileDirty(false);
foreach (var fileNode in this.EnumNodesOfType<AssetFileNode>())
{
fileNode.IsDirty = false;
}
}
public override void Load(string fileName, string location, string name, uint flags, ref Guid iidProject, out int canceled)
{
try
{
this.disableQueryEdit = true;
// set up internal members and icons
canceled = 0;
this.ProjectMgr = this;
Utilities.Initialize((EnvDTE.DTE)this.GetService(typeof(EnvDTE.DTE)));
// based on the passed in flags, this either reloads/loads a project, or tries to create a new one
// now we create a new project... we do that by loading the template and then saving under a new name
// we also need to copy all the associated files with it.
if ((flags & (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE) == (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE)
{
Debug.Assert(!String.IsNullOrEmpty(fileName) && File.Exists(fileName), "Invalid filename passed to load the project. A valid filename is expected");
// Compute the file name
// We try to solve two problems here. When input comes from a wizzard in case of zipped based projects
// the parameters are different.
// In that case the filename has the new filename in a temporay path.
// First get the extension from the template.
// Then get the filename from the name.
// Then create the new full path of the project.
string extension = Path.GetExtension(fileName);
string tempName = String.Empty;
// We have to be sure that we are not going to loose data here. If the project name is a.b.c then for a project that was based on a zipped template(the wizzard calls us) GetFileNameWithoutExtension will suppress "c".
// We are going to check if the parameter "name" is extension based and the extension is the same as the one from the "filename" parameter.
string tempExtension = Path.GetExtension(name);
if (!String.IsNullOrEmpty(tempExtension))
{
bool isSameExtension = (String.Compare(tempExtension, extension, StringComparison.OrdinalIgnoreCase) == 0);
if (isSameExtension)
{
tempName = Path.GetFileNameWithoutExtension(name);
}
// If the tempExtension is not the same as the extension that the project name comes from then assume that the project name is a dotted name.
else
{
tempName = Path.GetFileName(name);
}
}
else
{
tempName = Path.GetFileName(name);
}
Debug.Assert(!String.IsNullOrEmpty(tempName), "Could not compute project name");
string tempProjectFileName = tempName + extension;
this.FileName = Path.Combine(location, tempProjectFileName);
this.anxContentProject = ContentProject.Load(fileName);
// Initialize the common project properties.
this.InitializeProjectProperties();
ErrorHandler.ThrowOnFailure(this.Save(this.FileName, 1, 0));
// now we do have the project file saved. we need to create embedded files.
foreach (BuildItem item in this.ContentProject.BuildItems)
{
string strRelFilePath = item.SourceFilename;
string basePath = Path.GetDirectoryName(fileName);
string strPathToFile;
string newFileName;
// taking the base name from the project template + the relative pathname,
// and you get the filename
strPathToFile = Path.Combine(basePath, strRelFilePath);
// the new path should be the base dir of the new project (location) + the rel path of the file
newFileName = Path.Combine(location, strRelFilePath);
// now the copy file
AddFileFromTemplate(strPathToFile, newFileName);
}
}
else
{
this.FileName = fileName;
}
this.Reload();
}
finally
{
this.disableQueryEdit = false;
}
}
protected override void LoadProjectFile(string filename)
{
this.anxContentProject = ContentProject.Load(filename);
//The project might have been loaded before and someone wanted to reload it. If we don't check, an error would be thrown if we try to set the fullPath.
var project = ProjectCollection.GlobalProjectCollection.GetLoadedProjects(this.FileName).FirstOrDefault();
if (project != null)
this.BuildProject = project;
else
{
this.BuildProject = new Project();
this.BuildProject.FullPath = this.FileName;
}
base.SetProjectProperty(ProjectFileConstants.ProjectGuid, this.ProjectGuid.ToString("B"));
base.SetProjectProperty(ProjectFileConstants.Name, this.anxContentProject.Name);
this.CurrentConfig = this.BuildProject.CreateProjectInstance();
using (var buildDomain = this.BuildAppDomain.Aquire())
{
buildDomain.SearchPaths.Clear();
buildDomain.SearchPaths.Add(new Uri(this.ProjectHome));
Uri anxFrameworkPath;
if (BuildHelper.TryGetAnxFrameworkPath(out anxFrameworkPath))
{
buildDomain.SearchPaths.Add(anxFrameworkPath);
}
buildDomain.Initialize(this.ProjectGuid.ToString());
}
if (ErrorListProvider != null)
ErrorListProvider.Dispose();
ErrorListProvider = new ErrorListProvider(this.Site);
ErrorListProvider.ProviderName = this.ContentProject.Name;
ErrorListProvider.ProviderGuid = new Guid("{90A94AFA-C6E3-42D4-ABD8-B663BEE015F0}"); //custom random guid.
}
protected override ReferenceContainerNode CreateReferenceContainerNode()
{
var referenceContainer = new ContentProjectReferenceContainer(this);
var listener = new SolutionListenerForProjectReferences(this.Site, referenceContainer);
this.Package.SolutionListeners.Add(listener);
listener.Init();
return referenceContainer;
}
public override void ProcessReferences()
{
ContentProjectReferenceContainer container = (ContentProjectReferenceContainer)GetReferenceContainer();
if (null == container)
{
// This project type does not support references or there is a problem
// creating the reference container node.
// In both cases there is no point to try to process references, so exit.
return;
}
// Load the referernces.
container.LoadReferencesFromContentProject();
}
protected override void ProcessConfigurations()
{
base.ProcessConfigurations();
var dte = (EnvDTE.DTE)this.GetService(typeof(EnvDTE.DTE));
var configurations = dte.Solution.SolutionBuild.SolutionConfigurations.AsEnumerable();
var names = configurations.Select((x) => x.Name).Distinct().ToArray();
string[] platforms;
using (var buildAppDomain = this.BuildAppDomain.Aquire())
{
platforms = buildAppDomain.Proxy.GetPlatformDisplayNames();
}
var unsupportedConfigurations = anxContentProject.Configurations.Where((x) => !names.Contains(x.Name) || !platforms.Contains(Utilities.GetDisplayName(x.Platform))).ToArray();
foreach (var config in unsupportedConfigurations)
{
Console.WriteLine("Removed configuration \"{0}\":\"{1}\" because it's not supported by this solution.", config.Name, config.Platform);
anxContentProject.Configurations.Remove(config);
}
}
public override void ProcessFiles()
{
base.ProcessFiles();
foreach (var item in anxContentProject.BuildItems)
{
HierarchyNode parentNode = this;
var directory = Path.GetDirectoryName(item.SourceFilename);
if (!string.IsNullOrEmpty(directory))
{
var directories = directory.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
string currentDir = "";
foreach (var dir in directories)
{
currentDir += dir + Path.DirectorySeparatorChar;
parentNode = this.CreateFolderNodes(currentDir, false);
}
}
var projectItem = this.BuildProject.AddItem(ProjectFileConstants.Content, item.SourceFilename).FirstOrDefault();
var asset = new AssetFileNode(this, new MsBuildProjectElement(this, projectItem));
asset.Loading = true;
try
{
asset.AssetName = item.AssetName;
asset.ContentProcessor = item.ProcessorName;
asset.ContentImporter = item.ImporterName;
foreach (var pair in item.ProcessorParameters)
{
//The processor values are always saved as strings.
asset.ProcessorParameters.Add(pair.Key, (string)pair.Value);
}
}
finally
{
asset.Loading = false;
}
parentNode.AddChild(asset);
}
}
/// <summary>
/// Return the value of a project property
/// </summary>
/// <param name="propertyName">Name of the property to get</param>
/// <param name="resetCache">True to avoid using the cache</param>
/// <returns>null if property does not exist, otherwise value of the property</returns>
public override string GetProjectProperty(string propertyName, bool resetCache)
{
switch (propertyName)
{
case ProjectFileConstants.Name:
return this.anxContentProject.Name;
case ProjectFileConstants.Configuration:
return VsUtilities.GetActiveConfigurationName((EnvDTE.Project)this.GetAutomationObject());
case ProjectFileConstants.Platform:
return VsUtilities.GetActivePlatformName((EnvDTE.Project)this.GetAutomationObject());
}
return base.GetProjectProperty(propertyName, resetCache);
}
public override int GetGuidProperty(int propid, out Guid guid)
{
if (propid == (int)__VSHPROPID2.VSHPROPID_AddItemTemplatesGuid)
{
guid = GuidList.guidANXVisualStudio2012ContentProjectFactory;
return VSConstants.S_OK;
}
return base.GetGuidProperty(propid, out guid);
}
public override void SetProjectProperty(string propertyName, string propertyValue)
{
switch (propertyName)
{
case ProjectFileConstants.Name:
this.anxContentProject.Name = propertyValue;
return;
case ProjectFileConstants.Configuration:
case ProjectFileConstants.Platform:
//Don't do anything, we don't save which configuration is currently active, for that we always use the ActiveConfiguration object from the IDE.
//The reason is, I don't know why the value should be saved in the first place, it always seems to depend on the active configuration and we just transfer the value
//into the project file.
return;
case ProjectFileConstants.ProjectGuid:
//we don't save the guide in the project file, when we load up the project, we always get the guid set but we don't want to make the project file dirty.
bool isDirty = this.IsProjectFileDirty;
base.SetProjectProperty(propertyName, propertyValue);
this.SetProjectFileDirty(isDirty);
return;
}
base.SetProjectProperty(propertyName, propertyValue);
}
/*public void Save()
{
((Microsoft.VisualStudio.Project.Automation.OAProject)this.GetAutomationObject()).Save(this.FileName);
//VsShellUtilities.SaveFileIfDirty(this.Site, this.Url);
// Save the project file and project file related properties.
//Save(this.FileName, 1, 0);
}*/
private void Build(uint vsopts, string target, IEnumerable<string> files, string config, string platform, IVsOutputWindowPane output, Action<MSBuildResult, string> uiThreadCallback)
{
try
{
Configuration configuration;
TargetPlatform targetPlatform = Utilities.ParseTargetPlatform(platform);
if (!ContentProject.Configurations.TryGetConfiguration(config, targetPlatform, out configuration))
{
uiThreadCallback(MSBuildResult.Failed, target);
}
else
{
ErrorLoggingHelper loggingHelper = new ErrorLoggingHelper(this, ErrorListProvider, output);
try
{
this.BuildAppDomain.BuildContent(configuration, files, loggingHelper, (vsopts & (uint)VSConstants.VSStd2KCmdID.Debug) != 0, target);
}
catch (Exception exc)
{
try
{
loggingHelper.LogError(exc);
}
finally
{
uiThreadCallback(MSBuildResult.Failed, target);
}
}
//Do we have a build error?
if (ErrorListProvider.Tasks.Cast<Microsoft.VisualStudio.Shell.Task>().Where((x) => x.Priority == TaskPriority.High).Any())
{
uiThreadCallback(MSBuildResult.Failed, target);
}
else
{
uiThreadCallback(MSBuildResult.Successful, target);
}
if (ErrorListProvider.Tasks.Count > 0)
ErrorListProvider.Show();
}
}
catch
{
uiThreadCallback(MSBuildResult.Failed, target);
}
}
protected override int StartDebug()
{
string configName = this.GetProjectProperty(ProjectFileConstants.Configuration);
string platform = this.GetProjectProperty(ProjectFileConstants.Platform);
IVsCfgProvider cfgProvider;
if (this.GetCfgProvider(out cfgProvider) == VSConstants.S_OK)
{
IVsCfg config;
if (((ConfigProvider)cfgProvider).GetCfgOfName(configName, platform, out config) == VSConstants.S_OK)
{
((Config)config).DebugLaunch((uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_Selected);
return VSConstants.S_OK;
}
}
return VSConstants.E_FAIL;
}
public override void PrepareBuild(uint vsopts, string config, string platform, bool cleanBuild)
{
ErrorListProvider.Tasks.Clear();
base.PrepareBuild(vsopts, config, platform, cleanBuild);
BuildAppDomain.PrepareBuild(cleanBuild);
}
public override void BuildAsync(uint vsopts, string config, string platform, IVsOutputWindowPane output, string target, IEnumerable<string> files, Action<MSBuildResult, string> uiThreadCallback)
{
System.Threading.Tasks.Task.Run(() =>
{
TriggerBuild(vsopts, target, config, platform, files, output, uiThreadCallback);
});
}
public override MSBuildResult InvokeMsBuild(uint vsopts, string target, string config, string platform, IEnumerable<string> files)
{
return TriggerBuild(vsopts, target, config, platform, files);
}
private MSBuildResult TriggerBuild(uint vsopts, string target, string config, string platform, IEnumerable<string> files)
{
IVsOutputWindowPane pane;
if (this.BuildLogger is IDEBuildLogger)
pane = ((IDEBuildLogger)this.BuildLogger).OutputWindowPane;
else
pane = this.Package.GetOutputPane(Microsoft.VisualStudio.VSConstants.SID_SVsGeneralOutputWindowPane, null);
return this.TriggerBuild(vsopts, target, config, platform, files, pane, null);
}
private MSBuildResult TriggerBuild(uint vsopts, string target, string config, string platform, IEnumerable<string> files, IVsOutputWindowPane output, Action<MSBuildResult, string> uiThreadCallback)
{
MSBuildResult result = MSBuildResult.Failed;
const bool designTime = true;
bool requiresUIThread = UIThread.Instance.IsUIThread; // we don't run tasks that require calling the STA thread, so unless we're ON it, we don't need it.
IVsBuildManagerAccessor accessor = this.Site.GetService(typeof(SVsBuildManagerAccessor)) as IVsBuildManagerAccessor;
BuildSubmission submission = null;
if (!TryBeginBuild(designTime, requiresUIThread))
{
throw new InvalidOperationException("A build is already in progress.");
}
try
{
Build(vsopts, target, files, config, platform, output, (buildResult, buildTarget) =>
{
result = buildResult;
if (uiThreadCallback != null)
uiThreadCallback(buildResult, buildTarget);
});
}
finally
{
EndBuild(vsopts, submission, designTime, requiresUIThread);
}
return result;
}
protected override void EndBuild(uint vsopts, BuildSubmission submission, bool designTime, bool requiresUIThread = false)
{
BuildAppDomain.EndBuild();
base.EndBuild(vsopts, submission, designTime, requiresUIThread);
if (this.PendingBuild != null)
{
this.PendingBuild = null;
//A bit of a hack because I wasn't able to find out how to finish a build when it was started by our extension.
//This causes several seconds delay until the build is actually marked as done.
this.DTE.ExecuteCommand("Build.Cancel");
}
}
public override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result)
{
if (cmdGroup == CommonConstants.Std97CmdGroupGuid)
{
switch ((VSConstants.VSStd97CmdID)cmd)
{
case VSConstants.VSStd97CmdID.BuildCtx:
case VSConstants.VSStd97CmdID.RebuildCtx:
result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
return VSConstants.S_OK;
case VSConstants.VSStd97CmdID.CleanCtx:
result |= QueryStatusResult.NOTSUPPORTED | QueryStatusResult.INVISIBLE;
return VSConstants.S_OK;
}
}
else if (cmdGroup == Microsoft.VisualStudio.Project.VsMenus.guidStandardCommandSet2K)
{
switch ((VsCommands2K)cmd)
{
case VsCommands2K.ECMD_PUBLISHSELECTION:
result |= QueryStatusResult.NOTSUPPORTED | QueryStatusResult.INVISIBLE;
return VSConstants.S_OK;
case VsCommands2K.ECMD_PUBLISHSLNCTX:
result |= QueryStatusResult.NOTSUPPORTED | QueryStatusResult.INVISIBLE;
return VSConstants.S_OK;
}
}
return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result);
}
public override CommonFileNode CreateNonCodeFileNode(ProjectElement item)
{
string fileName = item.GetMetadata(ProjectFileConstants.Include);
var file = new AssetFileNode(this, item)
{
AssetName = Path.GetFileNameWithoutExtension(fileName),
};
return file;
}
public override string GetExistingFilesFilter()
{
using (var buildDomain = this.BuildAppDomain.Aquire())
{
return buildDomain.Proxy.GetExistingFilesFilter();
}
}
public override Type GetProjectFactoryType()
{
return typeof(ContentProjectFactory);
}
public override Type GetEditorFactoryType()
{
return null;
}
public override string GetProjectName()
{
return this.GetProjectProperty(ProjectFileConstants.Name);
}
public override string GetFormatList()
{
return "Content Project Files (*.cproj)|*.cproj";
}
public override Type GetLibraryManagerType()
{
return null;
}
public override IProjectLauncher GetLauncher()
{
return new ContentProjectLauncher(this);
}
protected override NodeProperties CreatePropertiesObject()
{
return new ContentProjectNodeProperties(this);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (this.buildAppDomain != null)
{
this.buildAppDomain.Dispose();
}
}
base.Dispose(disposing);
}
/*public override System.Runtime.Versioning.FrameworkName TargetFrameworkMoniker
{
get
{
return ContentProject.DotNetFramework;
}
set
{
if (ContentProject.DotNetFramework != value)
{
var oldFramework = ContentProject.DotNetFramework;
ContentProject.DotNetFramework = value;
UpdateTargetFramework(GetOuterHierarchy(), oldFramework.FullName, value.FullName);
}
}
}*/
}
}