using ANX.Framework.Build; using ANX.Framework.Content.Pipeline; using ANX.Framework.Content.Pipeline.Tasks; using ANX.Framework.Graphics; using ANX.Framework.VisualStudio.Nodes; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Project; using Microsoft.VisualStudio.Project.Automation; using Microsoft.VisualStudio.Shell.Interop; using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ANX.Framework.VisualStudio { public class ContentConfig : CommonConfig { ContentProjectNode node; Configuration contentConfiguration; ContentBuildableProjectConfig buildableConfig; public ContentConfig(ContentProjectNode node, string configuration, string platform) : base(node, configuration, platform) { this.node = node; TargetPlatform targetPlatform = Utilities.ParseTargetPlatform(platform); if (!node.ContentProject.Configurations.TryGetConfiguration(configuration, targetPlatform, out contentConfiguration)) { contentConfiguration = new Configuration(configuration, targetPlatform); contentConfiguration.Profile = GraphicsProfile.Reach; contentConfiguration.OutputDirectory = Path.Combine("bin", platform, configuration); node.ContentProject.Configurations.Add(contentConfiguration); node.SetProjectFileDirty(true); } } public ContentConfig(ContentProjectNode node, Configuration config) : base(node, config.Name, Utilities.GetDisplayName(config.Platform)) { this.node = node; this.contentConfiguration = config; } public override string ConfigName { get { return this.Configuration.Name; } } public override string PlatformName { get { return Utilities.GetDisplayName(this.Configuration.Platform); } } public Configuration Configuration { get { return contentConfiguration; } } public new ContentProjectNode ProjectMgr { get { return this.node; } } public override int get_Platform(out Guid platform) { return base.get_Platform(out platform); } public override int EnumOutputs(out IVsEnumOutputs eo) { return base.EnumOutputs(out eo); } /// /// Determines whether the debugger can be launched, given the state of the launch flags. /// /// Flags that determine the conditions under which to launch the debugger. /// For valid grfLaunch values, see __VSDBGLAUNCHFLAGS or __VSDBGLAUNCHFLAGS2. /// true if the debugger can be launched, otherwise false /// S_OK if the method succeeds, otherwise an error code public override int QueryDebugLaunch(uint flags, out int fCanLaunch) { fCanLaunch = 1; return VSConstants.S_OK; } public override int DebugLaunch(uint flags) { IProjectLauncher starter = node.GetLauncher(); IVsOutputWindowPane pane; if ((flags & 16) != 0) { pane = this.ProjectMgr.Package.GetOutputPane(GuidList.VsGuids.guidDebugOutputWindowPane, PackageResources.GetString(PackageResources.Debugging)); flags |= (uint)VSConstants.VSStd2KCmdID.Debug; } else pane = this.ProjectMgr.Package.GetOutputPane(GuidList.VsGuids.guidBuildOutputWindowPane, PackageResources.GetString(PackageResources.Building)); return starter.LaunchProject(flags | (uint)VSConstants.VSStd2KCmdID.Debug, this, pane); } public override int get_BuildableProjectCfg(out IVsBuildableProjectCfg pb) { if (buildableConfig == null) { buildableConfig = new ContentBuildableProjectConfig(this); } pb = buildableConfig; return VSConstants.S_OK; } } public class ContentBuildableProjectConfig : BuildableProjectConfig { public const uint SelfInitiatedBuild = 0x80000000; ContentConfig config; public ContentBuildableProjectConfig(ContentConfig config) : base(config) { this.config = config; } public override int QueryStartUpToDateCheck(uint options, int[] supported, int[] ready) { if (supported != null && supported.Length > 0) supported[0] = 1; if (ready != null && ready.Length > 0) ready[0] = (!this.config.ProjectMgr.BuildInProgress && !this.config.ProjectMgr.BuildAppDomain.IsBuildRunning) ? 1 : 0; return VSConstants.S_OK; } public override int StartUpToDateCheck(IVsOutputWindowPane pane, uint options) { //If we are doing a file build, always force a rebuild. if (this.config.ProjectMgr.PendingBuild != null) return VSConstants.E_FAIL; ErrorLoggingHelper loggingHelper = new ErrorLoggingHelper(config.ProjectMgr, config.ProjectMgr.ErrorListProvider, pane); //Call into builder process to see if it returns that everything is up to date. using (var domain = config.ProjectMgr.BuildAppDomain.Aquire()) { if (domain.Proxy.IsUpDoDate(config.ProjectMgr.ProjectHome, config.ProjectMgr.ContentProject.BuildItems, config.ProjectMgr.ActiveContentConfiguration, loggingHelper)) { loggingHelper.LogMessage(null, "Still up to date. Skipping build."); return VSConstants.S_OK; } else return VSConstants.E_FAIL; } } public override int StartBuild(IVsOutputWindowPane pane, uint options) { // If we are not asked for a rebuild, then we build the default target (by passing null) IProjectLauncher starter = ((ContentProjectNode)this.config.ProjectMgr).GetLauncher(); //Always output to the build pane, not the debugger pane. pane = this.config.ProjectMgr.Package.GetOutputPane(GuidList.VsGuids.guidBuildOutputWindowPane, PackageResources.GetString(PackageResources.Building)); if (config.ProjectMgr.PendingBuild != null) { options |= SelfInitiatedBuild; if (config.ProjectMgr.PendingBuild.IsDebug) options |= (uint)VSConstants.VSStd2KCmdID.Debug; } //Find the actually active config instead of just the current, Visual Studio calls the build just an any configuration. var activeContentConfig = this.config.ProjectMgr.ActiveContentConfiguration; IVsCfg cfg; ErrorHandler.ThrowOnFailure(this.config.ProjectMgr.ConfigProvider.GetCfgOfName(activeContentConfig.Name, activeContentConfig.Platform.ToDisplayName(), out cfg)); Config activeConfig = (Config)cfg; if (config.ProjectMgr.PendingBuild != null && config.ProjectMgr.PendingBuild.IsFileBuild) { starter.LaunchFiles(config.ProjectMgr.PendingBuild.FilesToBuild, options, activeConfig, pane); } else { starter.LaunchProject(options, activeConfig, pane); } return VSConstants.S_OK; } protected override void RefreshReferences() { ContentProjectReferenceContainer references = (ContentProjectReferenceContainer)this.config.ProjectMgr.GetReferenceContainer(); references.RefreshAssemblies(); } protected override void OnNotifyBuildEnd(MSBuildResult result, string buildTarget, IVsBuildStatusCallback cb, int success) { //No need for reloading any references as we are automatically reloading if the assemblies get changed. try { ErrorHandler.ThrowOnFailure(cb.BuildEnd(success)); } catch (Exception e) { // If those who ask for status have bugs in their code it should not prevent the build/notification from happening Debug.Fail(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.BuildEventError, CultureInfo.CurrentUICulture), e.Message)); } } } }