diff options
Diffstat (limited to 'Software/Visual_Studio/Tango.BuildExtensions/TangoBuildCommand.cs')
| -rw-r--r-- | Software/Visual_Studio/Tango.BuildExtensions/TangoBuildCommand.cs | 598 |
1 files changed, 598 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Tango.BuildExtensions/TangoBuildCommand.cs b/Software/Visual_Studio/Tango.BuildExtensions/TangoBuildCommand.cs new file mode 100644 index 000000000..d462e5f75 --- /dev/null +++ b/Software/Visual_Studio/Tango.BuildExtensions/TangoBuildCommand.cs @@ -0,0 +1,598 @@ +//------------------------------------------------------------------------------ +// <copyright file="TangoBuildCommand.cs" company="Company"> +// Copyright (c) Company. All rights reserved. +// </copyright> +//------------------------------------------------------------------------------ + +using System; +using System.ComponentModel.Design; +using System.Globalization; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using System.Collections.Generic; +using EnvDTE; +using EnvDTE80; +using Microsoft.VisualStudio; +using System.Linq; +using TestStack.White.UIItems.Finders; +using TestStack.White.InputDevices; +using TestStack.White.UIItems; +using VSLangProj; +using System.Runtime.InteropServices; +using TestStack.White.WindowsAPI; +using System.IO; + +namespace Tango.BuildExtensions +{ + /// <summary> + /// Command handler + /// </summary> + internal sealed class TangoBuildCommand + { + private DTE2 _dte; + private IList<Project> _projects; + private SelectForm _form; + private TestStack.White.Application _application; + private TestStack.White.UIItems.WindowItems.Window _window; + private System.Diagnostics.Process _vsProcess; + + private const String dalProjectName = "Tango.DAL.Remote"; + private const String edmxModelName = "RemoteADO.edmx"; + private const String observablesGeneratorProjectName = "Tango.DBObservablesGenerator.CLI"; + private const String observablesProjectName = "Tango.BL"; + private const String pmrGeneratorProjectName = "Tango.PMRGenerator.CLI"; + private const String pmrProjectName = "Tango.PMR"; + + #region Redundant + + /// <summary> + /// Command ID. + /// </summary> + public const int CommandId = 0x0100; + + /// <summary> + /// Command menu group (command set GUID). + /// </summary> + public static readonly Guid CommandSet = new Guid("c03a7b01-8109-4ec5-8f90-858bed027e5d"); + + /// <summary> + /// VS Package that provides this command, not null. + /// </summary> + private readonly Package package; + + /// <summary> + /// Initializes a new instance of the <see cref="TangoBuildCommand"/> class. + /// Adds our command handlers for menu (commands must exist in the command table file) + /// </summary> + /// <param name="package">Owner package, not null.</param> + private TangoBuildCommand(Package package) + { + if (package == null) + { + throw new ArgumentNullException("package"); + } + + this.package = package; + + OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService; + if (commandService != null) + { + var menuCommandID = new CommandID(CommandSet, CommandId); + var menuItem = new MenuCommand(this.MenuItemCallback, menuCommandID); + commandService.AddCommand(menuItem); + } + } + + /// <summary> + /// Gets the instance of the command. + /// </summary> + public static TangoBuildCommand Instance + { + get; + private set; + } + + /// <summary> + /// Gets the service provider from the owner package. + /// </summary> + private IServiceProvider ServiceProvider + { + get + { + return this.package; + } + } + + /// <summary> + /// Initializes the singleton instance of the command. + /// </summary> + /// <param name="package">Owner package, not null.</param> + public static void Initialize(Package package) + { + Instance = new TangoBuildCommand(package); + } + + /// <summary> + /// This function is the callback used to execute the command when the menu item is clicked. + /// See the constructor to see how the menu item is associated with this function using + /// OleMenuCommandService service and MenuCommand class. + /// </summary> + /// <param name="sender">Event sender.</param> + /// <param name="e">Event args.</param> + private void MenuItemCallback(object sender, EventArgs e) + { + Start(); + } + + #endregion + + #region Main + + private void Start() + { + _form = null; + + BuildForm buildForm = new BuildForm(); + if (buildForm.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; + + System.Threading.Tasks.Task.Factory.StartNew(() => + { + _projects = Projects().ToList(); + + String vsWindowTitle = _dte.DTE.MainWindow.Caption; + _vsProcess = System.Diagnostics.Process.GetProcesses().ToList().SingleOrDefault(x => x.MainWindowTitle == vsWindowTitle); + _application = TestStack.White.Application.Attach(_vsProcess); + _window = _application.GetWindow(vsWindowTitle); + + OpenProgressForm(); + + try + { + if (buildForm.UpdateDataBaseEntities) + { + UpdateDatabaseEntities(); + } + if (buildForm.GenerateAndBuildObservables) + { + GenerateAndBuildObservables(); + } + if (buildForm.GenerateAutoPmrMessages) + { + GenerateAutoPMRMessages(); + } + if (buildForm.UpdateAndBuildPmrMessages) + { + UpdateAndBuildPmrMessages(); + } + if (buildForm.BuildSolution) + { + BuildSolution(); + } + + SetStatusText("Done!"); + Wait(1000); + CloseProgressForm(); + } + catch (Exception ex) + { + CloseProgressForm(); + ShowMessage(ex.Message); + } + }); + } + + private void UpdateDatabaseEntities() + { + var project = _projects.SingleOrDefault(x => x.Name == dalProjectName); + + if (project == null) + { + throw new NullReferenceException("Could not find the Tango solution!"); + } + + var projectItems = project.ProjectItems.OfType<ProjectItem>().ToList(); + + SetStatusText("Locating " + edmxModelName + " scheme..."); + + var items = GetProjectItemsDeep(project); + + var edmx = GetProjectItemsDeep(project).SingleOrDefault(x => x.Name == edmxModelName); + + if (edmx == null) + { + throw new NullReferenceException("Could not locate " + edmxModelName + "!"); + } + + SetStatusText("Expanding diagram..."); + + edmx.ExpandView(); + + SetStatusText("Opening edmx diagram window..."); + + Window win = edmx.Open(EnvDTE.Constants.vsViewKindPrimary); + win.Visible = true; + + SetStatusText("Waiting for edmx diagram window..."); + + _window.WaitTill(() => _window.Get(SearchCriteria.ByText(edmxModelName + " [Diagram1]")) != null); + + SetStatusText("Cleaning up edmx scheme..."); + + _dte.MainWindow.Activate(); + _dte.ExecuteCommand("Edit.SelectAll"); + Keyboard.Instance.PressSpecialKey(KeyboardInput.SpecialKeys.DELETE); + WaitForWindowOpen("Delete Unmapped Tables and Views").PressKey(KeyboardInput.SpecialKeys.RETURN); + WaitForWindowClose("Delete Unmapped Tables and Views"); + + _window.WaitWhileBusy(); + + SetStatusText("Reinitializing edmx scheme..."); + + var window = WindowInfo.GetWindow(edmxModelName + " [Diagram1]*"); + window.SetActive(); + + Keyboard.Instance.HoldKey(KeyboardInput.SpecialKeys.SHIFT); + Keyboard.Instance.PressSpecialKey(KeyboardInput.SpecialKeys.F10); + Keyboard.Instance.LeaveAllKeys(); + + Wait(100); + + for (int i = 0; i < 7; i++) + { + Keyboard.Instance.PressSpecialKey(KeyboardInput.SpecialKeys.DOWN); + Wait(10); + } + + Keyboard.Instance.PressSpecialKey(KeyboardInput.SpecialKeys.RETURN); + + var updateWindow = WaitForWindowOpen("Update Wizard"); + + _window.WaitWhileBusy(); + + Wait(1000); + + updateWindow.PressKey(KeyboardInput.SpecialKeys.SPACE); + + Wait(50); + + updateWindow.PressKey(KeyboardInput.SpecialKeys.RETURN); + + SetStatusText("Generating edmx scheme..."); + + WaitForWindowClose("Update Wizard"); + + _window.WaitWhileBusy(); + + SetStatusText("Saving changes..."); + + win.Close(vsSaveChanges.vsSaveChangesYes); + + _window.WaitWhileBusy(); + + foreach (var template in edmx.ProjectItems.OfType<ProjectItem>().Where(x => x.Name.EndsWith(".tt"))) + { + SetStatusText("Running custom tool for " + template.Name + "..."); + (template.Object as VSProjectItem).RunCustomTool(); + _window.WaitWhileBusy(); + } + + _window.WaitWhileBusy(); + + SetStatusText("Building project " + dalProjectName + "..."); + + _dte.Solution.SolutionBuild.BuildProject("Debug", project.FullName, true); + + if (_dte.Solution.SolutionBuild.LastBuildInfo > 0) + { + throw new ExternalException(dalProjectName + " failed to build!"); + } + } + + private void GenerateAndBuildObservables() + { + var project = _projects.SingleOrDefault(x => x.Name == observablesGeneratorProjectName); + + if (project == null) + { + throw new NullReferenceException("Could not locate project " + observablesGeneratorProjectName); + } + + SetStatusText("Building project " + observablesGeneratorProjectName + "..."); + + _dte.Solution.SolutionBuild.BuildProject("Debug", project.FullName, true); + + _dte.Solution.Properties.Item("StartupProject").Value = observablesGeneratorProjectName; + + SetStatusText("Executing observables generator..."); + + _dte.ExecuteCommand("Debug.Start"); + + WaitForWindowOpen("Tango Observables Generator"); + + WaitForWindowClose("Tango Observables Generator"); + + var observablesProject = _projects.SingleOrDefault(x => x.Name == observablesProjectName); + + if (observablesProject == null) + { + throw new NullReferenceException("Could not locate project " + observablesProjectName); + } + + SetStatusText("Updating " + observablesProjectName + "..."); + + foreach (var file in Directory.GetFiles(Path.GetDirectoryName(observablesProject.FileName), "*.cs", SearchOption.AllDirectories)) + { + String parentFolderName = Path.GetFileName(Path.GetDirectoryName(file)); + + if (parentFolderName != "Debug" && parentFolderName != "Release" && parentFolderName != "obj") + { + SetStatusText("Adding/Updating file " + Path.GetFileName(file) + "..."); + observablesProject.ProjectItems.AddFromFile(file); + Wait(10); + } + } + + SetStatusText("Building project " + observablesProjectName + "..."); + + _dte.Solution.SolutionBuild.BuildProject("Debug", observablesProject.FullName, true); + + if (_dte.Solution.SolutionBuild.LastBuildInfo > 0) + { + throw new ExternalException(observablesProjectName + " failed to build!"); + } + } + + private void GenerateAutoPMRMessages() + { + var project = _projects.SingleOrDefault(x => x.Name == pmrGeneratorProjectName); + + if (project == null) + { + throw new NullReferenceException("Could not locate project " + pmrGeneratorProjectName); + } + + SetStatusText("Building project " + pmrGeneratorProjectName + "..."); + + _dte.Solution.SolutionBuild.BuildProject("Debug", project.FullName, true); + + _dte.Solution.Properties.Item("StartupProject").Value = pmrGeneratorProjectName; + + SetStatusText("Executing PMR generator..."); + + _dte.ExecuteCommand("Debug.Start"); + + WaitForWindowOpen("Tango PMR Generator"); + + WaitForWindowClose("Tango PMR Generator"); + } + + private void UpdateAndBuildPmrMessages() + { + var project = _projects.SingleOrDefault(x => x.Name == pmrProjectName); + + if (project == null) + { + throw new NullReferenceException("Could not locate project " + pmrProjectName); + } + + SetStatusText("Updating " + pmrProjectName + "..."); + + foreach (var file in Directory.GetFiles(Path.GetDirectoryName(project.FileName), "*.cs", SearchOption.AllDirectories)) + { + String parentFolderName = Path.GetFileName(Path.GetDirectoryName(file)); + + if (parentFolderName != "Debug" && parentFolderName != "Release" && parentFolderName != "obj") + { + SetStatusText("Adding/Updating file " + Path.GetFileName(file) + "..."); + project.ProjectItems.AddFromFile(file); + Wait(10); + } + } + + SetStatusText("Building project " + pmrProjectName + "..."); + + _dte.Solution.SolutionBuild.BuildProject("Debug", project.FullName, true); + + if (_dte.Solution.SolutionBuild.LastBuildInfo > 0) + { + throw new ExternalException(pmrProjectName + " failed to build!"); + } + } + + private void BuildSolution() + { + SetStatusText("Building solution..."); + _dte.Solution.SolutionBuild.Build(true); + + if (_dte.Solution.SolutionBuild.LastBuildInfo > 0) + { + throw new ExternalException("Error building solution!"); + } + } + + #endregion + + #region Solution & Projects + + public DTE2 GetActiveIDE() + { + // Get an instance of currently running Visual Studio IDE. + DTE2 dte2 = Package.GetGlobalService(typeof(DTE)) as DTE2; + _dte = dte2; + return dte2; + } + + public IList<Project> Projects() + { + Projects projects = GetActiveIDE().Solution.Projects; + List<Project> list = new List<Project>(); + var item = projects.GetEnumerator(); + while (item.MoveNext()) + { + var project = item.Current as Project; + if (project == null) + { + continue; + } + + if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder) + { + list.AddRange(GetSolutionFolderProjects(project)); + } + else + { + list.Add(project); + } + } + + return list; + } + + private IEnumerable<Project> GetSolutionFolderProjects(Project solutionFolder) + { + List<Project> list = new List<Project>(); + for (var i = 1; i <= solutionFolder.ProjectItems.Count; i++) + { + var subProject = solutionFolder.ProjectItems.Item(i).SubProject; + if (subProject == null) + { + continue; + } + + // If this is another solution folder, do a recursive call, otherwise add + if (subProject.Kind == ProjectKinds.vsProjectKindSolutionFolder) + { + list.AddRange(GetSolutionFolderProjects(subProject)); + } + else + { + list.Add(subProject); + } + } + return list; + } + + private List<ProjectItem> GetProjectItemsDeep(Project project) + { + List<ProjectItem> results = new List<ProjectItem>(); + FillProjectItems(project.ProjectItems.OfType<ProjectItem>().ToList(), results); + return results; + } + + private void FillProjectItems(List<ProjectItem> rootItems, List<ProjectItem> results) + { + foreach (var item in rootItems) + { + results.Add(item); + + if (item.ProjectItems.Count > 0) + { + FillProjectItems(item.ProjectItems.OfType<ProjectItem>().ToList(), results); + } + } + } + + #endregion + + #region Notifications + + private void WriteToConsole(String text) + { + // Get the output window + var outputWindow = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; + + // Ensure that the desired pane is visible + var paneGuid = Microsoft.VisualStudio.VSConstants.OutputWindowPaneGuid.GeneralPane_guid; + IVsOutputWindowPane pane; + outputWindow.CreatePane(paneGuid, "General", 1, 0); + outputWindow.GetPane(paneGuid, out pane); + + // Output the message + pane.OutputString(text + Environment.NewLine); + } + + private void ShowMessage(String text) + { + // Show a message box to prove we were here + + VsShellUtilities.ShowMessageBox( + ServiceProvider, + text, + "Tango Initializer", + OLEMSGICON.OLEMSGICON_INFO, + OLEMSGBUTTON.OLEMSGBUTTON_OK, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); + } + + #endregion + + #region Windows API + + private WindowInfo WaitForWindowOpen(String title) + { + WindowInfo window = null; + + do + { + window = WindowInfo.GetAllWindows().SelectMany(x => x.Children).FirstOrDefault(x => x.Caption == title); + } while (window == null); + + return window; + } + + private void WaitForWindowClose(String title) + { + while (WindowInfo.GetAllWindows().SelectMany(x => x.Children).ToList().Exists(x => x.Caption == title)) + { + System.Threading.Thread.Sleep(100); + } + } + + #endregion + + #region Threading + + private void Wait(int milli) + { + System.Threading.Thread.Sleep(milli); + } + + #endregion + + #region Status Form + + private void OpenProgressForm() + { + System.Threading.Thread thread = new System.Threading.Thread(() => + { + _form = new SelectForm(); + var handle = _form.Handle; + _form.ShowDialog(); + }); + + thread.SetApartmentState(System.Threading.ApartmentState.STA); + thread.Start(); + + while (_form == null || !_form.IsHandleCreated) { } + } + + private void SetStatusText(String text) + { + _form.Invoke(new Action(() => + { + _form.SetStatus(text); + })); + } + + private void CloseProgressForm() + { + _form.Invoke(new Action(() => + { + _form.Close(); + _form = null; + })); + } + + #endregion + } +} |
