├── Plugin ├── Resources │ ├── ParallelBuildsMonitorWindowLicense.txt │ ├── Command.png │ ├── Package.ico │ └── Preview.png ├── Key.snk ├── License.txt ├── ReleaseNotes.txt ├── PBMControl.xaml ├── Utils.cs ├── ViewModel.cs ├── source.extension.vsixmanifest ├── Events │ ├── SolutionEvents.cs │ └── BuildEvents.cs ├── Properties │ └── AssemblyInfo.cs ├── PBMWindow.cs ├── stylesheet.css ├── SavePng.cs ├── PBMControl.xaml.cs ├── BuildInfo.cs ├── Package.vsct ├── VSPackage.resx ├── ParallelBuildsMonitor.csproj ├── MachineInfo.cs ├── Package.cs ├── PBMCommand.cs └── DataModel.cs ├── Tests ├── Key.snk ├── BuildFinished.png ├── UnitTestImage.png ├── BeforeBuildStart.png ├── BuildInProgress.png ├── UnitTestImageCopy.png ├── UnitTestImageChanged.png ├── PBM Example.sln CP WithoutBuildTiming.csv ├── PBM Example.sln CP WithBuildTiming.csv ├── MachineInfoTests.cs ├── Properties │ └── AssemblyInfo.cs ├── TestOutputWithoutBuildTiming.txt └── ParallelBuildsMonitorTests.csproj ├── Example ├── junk-finddir │ ├── stdafx.h │ ├── stdafx.cpp │ ├── targetver.h │ ├── junk-finddir.cpp │ ├── junk-finddir.vcxproj.filters │ └── junk-finddir.vcxproj ├── junk-vector-const │ ├── stdafx.h │ ├── stdafx.cpp │ ├── targetver.h │ ├── junk-vector-const.cpp │ ├── junk-vector-const.vcxproj.filters │ └── junk-vector-const.vcxproj ├── constexpr_templates │ ├── stdafx.h │ ├── stdafx.cpp │ ├── targetver.h │ ├── constexpr_templates.cpp │ ├── PostBuildEvent.bat │ ├── Problem.cpp │ ├── constexpr_templates.vcxproj.filters │ ├── left.cpp │ ├── Old Trials.cpp │ └── constexpr_templates.vcxproj ├── variadic-macros-v1 │ ├── stdafx.cpp │ ├── stdafx.h │ ├── targetver.h │ ├── variadic-macros-v1.cpp │ ├── variadic-macros-v1.vcxproj.filters │ └── variadic-macros-v1.vcxproj ├── WpfToolTip │ ├── App.config │ ├── PreBuildEvent.bat │ ├── Properties │ │ ├── Settings.settings │ │ ├── Settings.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── App.xaml │ ├── App.xaml.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ └── WpfToolTip.csproj ├── DiskPerformance │ ├── App.config │ ├── Properties │ │ ├── Settings.settings │ │ ├── Settings.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── App.xaml │ ├── App.xaml.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ └── DiskPerformance.csproj ├── README.md └── Example.sln ├── .editorconfig ├── .gitattributes ├── README.md ├── ParallelBuildsMonitor.sln └── .gitignore /Plugin/Resources/ParallelBuildsMonitorWindowLicense.txt: -------------------------------------------------------------------------------- 1 | Author: Krzysztof Buchacz -------------------------------------------------------------------------------- /Plugin/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Plugin/Key.snk -------------------------------------------------------------------------------- /Tests/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Tests/Key.snk -------------------------------------------------------------------------------- /Tests/BuildFinished.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Tests/BuildFinished.png -------------------------------------------------------------------------------- /Tests/UnitTestImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Tests/UnitTestImage.png -------------------------------------------------------------------------------- /Tests/BeforeBuildStart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Tests/BeforeBuildStart.png -------------------------------------------------------------------------------- /Tests/BuildInProgress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Tests/BuildInProgress.png -------------------------------------------------------------------------------- /Tests/UnitTestImageCopy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Tests/UnitTestImageCopy.png -------------------------------------------------------------------------------- /Example/junk-finddir/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/junk-finddir/stdafx.h -------------------------------------------------------------------------------- /Plugin/Resources/Command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Plugin/Resources/Command.png -------------------------------------------------------------------------------- /Plugin/Resources/Package.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Plugin/Resources/Package.ico -------------------------------------------------------------------------------- /Plugin/Resources/Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Plugin/Resources/Preview.png -------------------------------------------------------------------------------- /Example/junk-finddir/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/junk-finddir/stdafx.cpp -------------------------------------------------------------------------------- /Example/junk-finddir/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/junk-finddir/targetver.h -------------------------------------------------------------------------------- /Tests/UnitTestImageChanged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Tests/UnitTestImageChanged.png -------------------------------------------------------------------------------- /Example/junk-vector-const/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/junk-vector-const/stdafx.h -------------------------------------------------------------------------------- /Example/constexpr_templates/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/constexpr_templates/stdafx.h -------------------------------------------------------------------------------- /Example/junk-finddir/junk-finddir.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/junk-finddir/junk-finddir.cpp -------------------------------------------------------------------------------- /Example/junk-vector-const/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/junk-vector-const/stdafx.cpp -------------------------------------------------------------------------------- /Example/junk-vector-const/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/junk-vector-const/targetver.h -------------------------------------------------------------------------------- /Example/variadic-macros-v1/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/variadic-macros-v1/stdafx.cpp -------------------------------------------------------------------------------- /Example/variadic-macros-v1/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/variadic-macros-v1/stdafx.h -------------------------------------------------------------------------------- /Example/constexpr_templates/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/constexpr_templates/stdafx.cpp -------------------------------------------------------------------------------- /Example/constexpr_templates/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/constexpr_templates/targetver.h -------------------------------------------------------------------------------- /Example/variadic-macros-v1/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/variadic-macros-v1/targetver.h -------------------------------------------------------------------------------- /Example/junk-vector-const/junk-vector-const.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/junk-vector-const/junk-vector-const.cpp -------------------------------------------------------------------------------- /Example/variadic-macros-v1/variadic-macros-v1.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/variadic-macros-v1/variadic-macros-v1.cpp -------------------------------------------------------------------------------- /Example/constexpr_templates/constexpr_templates.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrzysztofBuchacz/ParallelBuildsMonitor/HEAD/Example/constexpr_templates/constexpr_templates.cpp -------------------------------------------------------------------------------- /Example/WpfToolTip/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Example/DiskPerformance/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Example/WpfToolTip/PreBuildEvent.bat: -------------------------------------------------------------------------------- 1 | @echo PreBuildEvent.bat Begin 2 | @rem @TIMEOUT /T 2 - doesn't work when called in VS Post Build Event 3 | @rem @sleep 2 - unknown command 4 | PING localhost -n 3 >NUL 5 | @echo PreBuildEvent.bat End 6 | -------------------------------------------------------------------------------- /Example/WpfToolTip/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Example/constexpr_templates/PostBuildEvent.bat: -------------------------------------------------------------------------------- 1 | @echo PostBuildEvent.bat Begin 2 | @rem @TIMEOUT /T 2 - doesn't work when called in VS Post Build Event 3 | @rem @sleep 2 - unknown command 4 | PING localhost -n 1 >NUL 5 | @echo PostBuildEvent.bat End 6 | -------------------------------------------------------------------------------- /Example/DiskPerformance/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ############################### 2 | # Core EditorConfig Options # 3 | ############################### 4 | 5 | root = true 6 | 7 | # All files 8 | [*] 9 | indent_style = space 10 | 11 | # Code files 12 | [*.{cs,csx,vb,vbx,xaml,txt,vsct}] 13 | indent_size = 4 14 | insert_final_newline = true 15 | -------------------------------------------------------------------------------- /Example/README.md: -------------------------------------------------------------------------------- 1 | # Example to test Parallel Builds Monitor 2 | 3 | Created in VS 2017 4 | 5 | . 6 | 7 | Just and Rebuild with "Parallel Builds Monitor" installed and shown to see Gantt chart. 8 | 9 | . 10 | 11 | Projects inside doesn't do anything special nor smart and are full of bugs. 12 | Intent of this solution is to have some projects with dependencies. 13 | 14 | -------------------------------------------------------------------------------- /Example/WpfToolTip/App.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Example/WpfToolTip/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace WpfToolTip 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Example/DiskPerformance/App.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Example/DiskPerformance/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace DiskPerformance 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /Tests/PBM Example.sln CP WithoutBuildTiming.csv: -------------------------------------------------------------------------------- 1 | "CRICTICAL PATH for | Build Started: 0001-01-01 00:00:00 | Processors: 1 | Cores: 2 | CPU Speed: 2.7GHz | Hyper Threading: Enabled | RAM: 8GB | HDD: 1 SSD" 2 | "Summary Report:" 3 | "Critical Path Order", "Project Name", "Build Time in [s] (Sorted Descending)", "Start Time [s]", "End Time [s]" 4 | "2", "WpfToolTip.csproj", "6", "4", "10" 5 | "3", "variadic-macros-v1.vcxproj", "4", "10", "14" 6 | "1", "junk-finddir.vcxproj", "4", "0", "4" 7 | -------------------------------------------------------------------------------- /Plugin/License.txt: -------------------------------------------------------------------------------- 1 | Parallel Builds Monitor License 2 | Copyright 2016 Krzysztof Buchacz 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /Example/WpfToolTip/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 66 | 72 | 78 | 79 | 80 | 81 | 82 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /Plugin/VSPackage.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | ParallelBuildsMonitorWindow Extension 122 | 123 | 124 | ParallelBuildsMonitorWindow Visual Studio Extension Detailed Info 125 | 126 | 127 | 128 | Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | -------------------------------------------------------------------------------- /Plugin/ParallelBuildsMonitor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | true 9 | 10 | 11 | 12 | 13 | 14.0 14 | publish\ 15 | true 16 | Disk 17 | false 18 | Foreground 19 | 7 20 | Days 21 | false 22 | false 23 | true 24 | 0 25 | 1.0.0.%2a 26 | false 27 | false 28 | true 29 | 30 | 31 | 32 | true 33 | 34 | 35 | Key.snk 36 | 37 | 38 | 39 | Debug 40 | AnyCPU 41 | 2.0 42 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 43 | {D045634D-A98B-4D34-9B92-2C1F2D45CB8F} 44 | Library 45 | Properties 46 | ParallelBuildsMonitor 47 | ParallelBuildsMonitor 48 | v4.7.2 49 | true 50 | true 51 | true 52 | true 53 | true 54 | false 55 | 56 | 57 | true 58 | full 59 | false 60 | bin\Debug\ 61 | DEBUG;TRACE 62 | prompt 63 | 4 64 | 65 | 66 | pdbonly 67 | true 68 | bin\Release\ 69 | TRACE 70 | prompt 71 | 4 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | PBMControl.xaml 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | Designer 97 | 98 | 99 | 100 | 101 | Menus.ctmenu 102 | 103 | 104 | Always 105 | true 106 | 107 | 108 | Always 109 | true 110 | 111 | 112 | Always 113 | true 114 | 115 | 116 | Always 117 | true 118 | 119 | 120 | Always 121 | true 122 | 123 | 124 | 125 | 126 | 127 | Designer 128 | MSBuild:Compile 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | true 148 | VSPackage 149 | Designer 150 | 151 | 152 | 153 | 154 | compile; build; native; contentfiles; analyzers; buildtransitive 155 | 156 | 157 | runtime; build; native; contentfiles; analyzers; buildtransitive 158 | all 159 | 160 | 161 | 162 | 163 | 170 | -------------------------------------------------------------------------------- /Example/junk-finddir/junk-finddir.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {2076BD99-2EBE-4026-AE0B-8A3A85183AA3} 24 | Win32Proj 25 | junkfinddir 26 | 10.0.17763.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level3 89 | Disabled 90 | true 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | Use 102 | Level3 103 | Disabled 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | 108 | 109 | Console 110 | true 111 | 112 | 113 | 114 | 115 | Use 116 | Level3 117 | MaxSpeed 118 | true 119 | true 120 | true 121 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | true 128 | true 129 | 130 | 131 | 132 | 133 | Use 134 | Level3 135 | MaxSpeed 136 | true 137 | true 138 | true 139 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 140 | true 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | Create 157 | Create 158 | Create 159 | Create 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /Example/junk-vector-const/junk-vector-const.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {A2FD53C9-7BFD-4DA2-B486-22D86277B8BB} 24 | Win32Proj 25 | junkvectorconst 26 | 10.0.17763.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level4 89 | Disabled 90 | true 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | Use 102 | Level3 103 | Disabled 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | 108 | 109 | Console 110 | true 111 | 112 | 113 | 114 | 115 | Use 116 | Level3 117 | MaxSpeed 118 | true 119 | true 120 | true 121 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | true 128 | true 129 | 130 | 131 | 132 | 133 | Use 134 | Level3 135 | MaxSpeed 136 | true 137 | true 138 | true 139 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 140 | true 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | Create 157 | Create 158 | Create 159 | Create 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /Example/variadic-macros-v1/variadic-macros-v1.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {95A0AA1F-4910-42AF-9310-0D34DCBCA526} 24 | Win32Proj 25 | variadicmacrosv1 26 | 10.0.17763.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level4 89 | Disabled 90 | true 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | Use 102 | Level3 103 | Disabled 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | 108 | 109 | Console 110 | true 111 | 112 | 113 | 114 | 115 | Use 116 | Level3 117 | MaxSpeed 118 | true 119 | true 120 | true 121 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | true 128 | true 129 | 130 | 131 | 132 | 133 | Use 134 | Level3 135 | MaxSpeed 136 | true 137 | true 138 | true 139 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 140 | true 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | Create 156 | Create 157 | Create 158 | Create 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /Plugin/MachineInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Management; 7 | 8 | namespace ParallelBuildsMonitor 9 | { 10 | /// 11 | /// MachineInfo collect information about machine configuration and return as human readable string. 12 | /// 13 | /// 14 | /// Informations are collected only once and then cached. 15 | /// 16 | public class MachineInfo 17 | { 18 | public string MachineName { get; private set; } 19 | public UInt32 PhysicalProcessorsNumber { get; private set; } = 0; 20 | public UInt32 PhysicalCoresNumber { get; private set; } = 0; 21 | public UInt32 LogicalCoresNumber { get; private set; } = 0; 22 | public ReadOnlyCollection CpusSpeedInMHz { get { return cpusSpeedInMHz.AsReadOnly(); } } 23 | public bool HyperThreadingEnabled { get; private set; } = false; 24 | public int TotalPhysicalMemoryInGB { get; private set; } = 0; 25 | public int PhysicalHDDsNumber { get; private set; } = 0; 26 | public ReadOnlyCollection HddsTypes { get { return hddsTypes.AsReadOnly(); } } 27 | 28 | private List cpusSpeedInMHz = new List(); 29 | private List hddsTypes = new List(); 30 | private string separatorCached; 31 | 32 | static private MachineInfo instance; 33 | static public MachineInfo Instance 34 | { 35 | get 36 | { 37 | if (instance == null) 38 | instance = new MachineInfo(); 39 | return instance; 40 | } 41 | } 42 | private string info = null; 43 | 44 | private MachineInfo() 45 | { 46 | MachineName = Environment.MachineName; 47 | 48 | try 49 | { // Not sure if try{} catch{} is needed here 50 | foreach (var item in new ManagementObjectSearcher("Select NumberOfProcessors, TotalPhysicalMemory from Win32_ComputerSystem").Get()) 51 | { 52 | PhysicalProcessorsNumber = (UInt32)item["NumberOfProcessors"]; 53 | TotalPhysicalMemoryInGB = (Int32)Math.Round(Convert.ToDouble(item.Properties["TotalPhysicalMemory"].Value) / 1048576 / 1024, 0); 54 | } 55 | } 56 | catch 57 | { 58 | Debug.Assert(false, "Getting Physical Processors Number or Total Physical Memory failed! Exception thrown while trying to possess those values."); 59 | } 60 | 61 | try 62 | { // Not sure if try{} catch{} is needed here 63 | foreach (var item in new ManagementObjectSearcher("Select NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed from Win32_Processor").Get()) 64 | { 65 | PhysicalCoresNumber += (UInt32)item["NumberOfCores"]; 66 | LogicalCoresNumber += (UInt32)item["NumberOfLogicalProcessors"]; //Environment.ProcessorCount is the same as NumberOfLogicalProcessors 67 | cpusSpeedInMHz.Add((UInt32)item["MaxClockSpeed"]); 68 | } 69 | } 70 | catch 71 | { 72 | Debug.Assert(false, "Getting Physical Cores Number, Logical Cores Number or CPU Speed failed! Exception thrown while trying to possess those values."); 73 | } 74 | 75 | 76 | if (PhysicalCoresNumber != LogicalCoresNumber) 77 | HyperThreadingEnabled = true; 78 | 79 | try 80 | { // Not sure if try{} catch{} is needed here 81 | foreach (var item in new ManagementObjectSearcher("SELECT DeviceID, TotalHeads FROM Win32_DiskDrive").Get()) 82 | { 83 | PhysicalHDDsNumber += 1; 84 | } 85 | } 86 | catch 87 | { 88 | Debug.Assert(false, "Getting Physical HDDs Number failed! Exception thrown while trying to possess those values."); 89 | } 90 | 91 | 92 | if (PhysicalHDDsNumber > 0) 93 | { 94 | for (int ii = 0; ii < PhysicalHDDsNumber; ii++) 95 | { 96 | //DetectSsd.DriveType driveType = DetectSsd.IsSsdDrive(ii); // Temporary removing old way 97 | DetectSsd.DriveType driveType = GetDriveType(ii); 98 | hddsTypes.Add(driveType); 99 | } 100 | } 101 | } 102 | 103 | public static DetectSsd.DriveType GetDriveType(int physicalDriveNumber) 104 | { 105 | try 106 | { // Not sure if try{} catch{} is needed here 107 | using (ManagementObjectSearcher physicalDiskSearcher = new ManagementObjectSearcher( 108 | @"\\localhost\ROOT\Microsoft\Windows\Storage", 109 | $"SELECT MediaType FROM MSFT_PhysicalDisk WHERE DeviceID='{physicalDriveNumber}'")) 110 | { 111 | ManagementBaseObject physicalDisk = physicalDiskSearcher.Get().Cast().Single(); 112 | UInt16 mediaType = (UInt16)physicalDisk["MediaType"]; 113 | switch (mediaType) 114 | { // According to: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-physicaldisk 115 | case 0: return DetectSsd.DriveType.Unknown; 116 | case 3: return DetectSsd.DriveType.Rotational; // Is it correct? 117 | case 4: return DetectSsd.DriveType.SSD; 118 | case 5: return DetectSsd.DriveType.Unknown; // Is it correct? 119 | } 120 | 121 | return DetectSsd.DriveType.Unknown; 122 | } 123 | } 124 | catch 125 | { 126 | Debug.Assert(false, "Getting Type of HDDs failed! Exception thrown while trying to to determine HDD type through ManagementObject."); 127 | return DetectSsd.DriveType.Unknown; 128 | } 129 | } 130 | 131 | 132 | public override string ToString() 133 | { 134 | return ToString(" | "); 135 | } 136 | 137 | public string ToString(string separator) 138 | { 139 | if (separatorCached == separator && info != null) 140 | return info; 141 | 142 | List list = new List(); 143 | //if (MachineName.Length > 0) // For now do not add machine name 144 | // list.Add("Machine: " + MachineName); 145 | if (PhysicalProcessorsNumber > 0) 146 | list.Add("Processors: " + PhysicalProcessorsNumber.ToString()); 147 | if (PhysicalCoresNumber > 0) 148 | list.Add("Cores: " + PhysicalCoresNumber.ToString()); 149 | if (CpusSpeedInMHz.Count > 0) 150 | { 151 | bool AreAllValuesTheSame = !CpusSpeedInMHz.Any(oo => oo != CpusSpeedInMHz[0]); 152 | List values = new List(); 153 | foreach (UInt32 value in CpusSpeedInMHz) 154 | { 155 | values.Add(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:0.0}GHz", ((double)value) / 1000)); //This also do the rounding 156 | if (AreAllValuesTheSame) 157 | break; 158 | } 159 | list.Add("CPU" + ((values.Count > 1) ? "s" : "") + " Speed: " + string.Join(", ", values)); 160 | } 161 | if (PhysicalCoresNumber > 0) 162 | list.Add("Hyper Threading: " + (HyperThreadingEnabled ? "Enabled" : "Disabled")); 163 | if (TotalPhysicalMemoryInGB > 0) 164 | list.Add("RAM: " + TotalPhysicalMemoryInGB.ToString() + "GB"); 165 | if (PhysicalHDDsNumber > 0) 166 | { 167 | List values = new List(); 168 | bool AreAllValuesTheSame = !HddsTypes.Any(oo => oo != HddsTypes[0]); 169 | foreach (DetectSsd.DriveType value in HddsTypes) 170 | { 171 | if (AreAllValuesTheSame) 172 | { // Add only one value if all the same type 173 | if (DetectSsd.DriveType.Unknown != value) 174 | values.Add(value); //If all Unknown then do not add any info. 175 | break; 176 | } 177 | values.Add(value); 178 | } 179 | list.Add("HDD: " + PhysicalHDDsNumber + ((values.Count > 1) ? ":" : "") + ((values.Count > 0) ? " " : "") + string.Join(", ", values)); 180 | } 181 | 182 | 183 | separatorCached = separator; 184 | info = string.Join(separator, list); 185 | 186 | return info; 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /Plugin/Package.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Design; 3 | using System.Runtime.InteropServices; 4 | using Microsoft.VisualStudio.Shell; 5 | using Microsoft.VisualStudio.Settings; 6 | using Microsoft.VisualStudio.Shell.Interop; 7 | using Microsoft.VisualStudio.Shell.Settings; 8 | 9 | namespace ParallelBuildsMonitor 10 | { 11 | /// 12 | /// This is the class that implements the package exposed by this assembly. 13 | /// 14 | /// 15 | /// 16 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 17 | /// is to implement the IVsPackage interface and register itself with the shell. 18 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 19 | /// to do it: it derives from the Package class that provides the implementation of the 20 | /// IVsPackage interface and uses the registration attributes defined in the framework to 21 | /// register itself and its components with the shell. These attributes tell the pkgdef creation 22 | /// utility what data to put into .pkgdef file. 23 | /// 24 | /// 25 | /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. 26 | /// 27 | /// 28 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 29 | [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // Info on this package for Help/About 30 | [ProvideMenuResource("Menus.ctmenu", 1)] 31 | // Not sure if VsDockStyle.Linked is the correctly selected value 32 | [ProvideToolWindow(typeof(PBMWindow), Style = VsDockStyle.Linked, DockedHeight = 200, Window = "DocumentWell", Orientation = ToolWindowOrientation.Bottom)] 33 | [Guid(Package.PackageGuidString)] 34 | public sealed class Package : Microsoft.VisualStudio.Shell.AsyncPackage 35 | { 36 | /// 37 | /// ParallelBuildsMonitorWindowPackage GUID string. 38 | /// 39 | public const string PackageGuidString = "3fe81f94-00df-4a3d-bff1-ae20f305aedb"; 40 | 41 | 42 | public Package() 43 | { 44 | // Inside this method you can place any initialization code that does not require 45 | // any Visual Studio service because at this point the package object is created but 46 | // not sited yet inside Visual Studio environment. The place to do all the other 47 | // initialization is the Initialize method. 48 | } 49 | 50 | /// 51 | /// Asynchronic Initialization of the package; this method is called right after the package is sited, so this is the place 52 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 53 | /// 54 | /// 55 | /// This method is called in Background Thread! 56 | /// 57 | /// This method is called: 58 | /// - when user click on "VS -> Menu -> View -> Other Windows -> Parallel Builds Monitor" menu item (menu item is added to VS menu by other means) 59 | /// - if attribute [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExistsAndFullyLoaded_string, PackageAutoLoadFlags.None)] is applied to AsyncPackage class 60 | /// 61 | /// 62 | /// This method is NOT called: 63 | /// - "Parallel Builds Monitor" pane is available but not active (e.g.: tabbed together with "Output" pange and "Output" pane active). 64 | /// Such state is achieved e.g.: when previous session of Visual Studio is left with with "Output" pane active and "Parallel Builds Monitor" 65 | /// is tabbed with it. That occur even when [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExistsAndFullyLoaded_string, ...] is applied. 66 | /// This method will be called when solution (SolutionExistsAndFullyLoaded_string) is loaded to VS. 67 | /// 68 | /// 69 | protected override async System.Threading.Tasks.Task InitializeAsync(System.Threading.CancellationToken cancellationToken, IProgress progress) 70 | { 71 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 72 | await ShowToolWindow.InitializeAsync(this); 73 | } 74 | 75 | ///// 76 | ///// This method is called only when Visual Studio is closed, NOT when "Parallel Builds Monitor" pane/frame is closed! 77 | ///// 78 | //protected override int QueryClose(out bool canClose) 79 | //{ 80 | // return base.QueryClose(out canClose); 81 | //} 82 | } 83 | 84 | 85 | /// 86 | /// Two goals: 87 | /// 1) Adding "Parallel Builds Monitor" menu item into "VS -> Menu -> View -> Other Windows" menu 88 | /// 2) When "VS -> Menu -> View -> Other Windows -> Parallel Builds Monitor" is clicked, 89 | /// showing "Parallel Builds Monitor" tool window. 90 | /// 91 | internal sealed class ShowToolWindow 92 | { 93 | [Guid("0617d7cf-8a0f-436f-8b05-4be366046686")] 94 | public enum MainMenuCommandSet 95 | { 96 | ShowToolWindow = 0x0100 97 | } 98 | 99 | public static async System.Threading.Tasks.Task InitializeAsync(AsyncPackage package) 100 | { 101 | { // Adding callback method to "Parallel Builds Monitor" menu item into "VS -> Menu -> View -> Other Windows" menu 102 | IMenuCommandService commandService = (IMenuCommandService)await package.GetServiceAsync(typeof(IMenuCommandService)); 103 | Microsoft.Assumes.Present(commandService); 104 | CommandID menuCommandID = new CommandID(typeof(MainMenuCommandSet).GUID, (int)MainMenuCommandSet.ShowToolWindow); 105 | MenuCommand menuItem = new MenuCommand((s, e) => Execute(package), menuCommandID); 106 | commandService.AddCommand(menuItem); 107 | } 108 | 109 | { // Start listening VS Events... 110 | // Should we always collect data even when "Parallel Builds Monitor" pane is closed? 111 | // What if user open PBM pane in the the middle of build? Should we show Gantt chart or draw notice like "Restart build to see results"? 112 | // If we decide not to collect data when PBM pane is closed, then user must manually activate PBM before build in order to have Gantt. 113 | // This is because "Output" pane is left as active after each build, so "Output" pane will be active pane after VS restart. 114 | PBMCommand.Initialize(package); 115 | } 116 | 117 | { // Show and Activate "Parallel Builds Monitor" pane 118 | // Do we really want to activate "Parallel Builds Monitor" pane after each solution load? 119 | // Or maybe we want to do that only once after installation? 120 | // Will it work when PBM plugin is installed whn solution is already opened? 121 | 122 | const string collectionName = "PBMSettings"; 123 | const string propertyName = "FirstRun"; 124 | SettingsManager settingsManager = new ShellSettingsManager(PBMCommand.ServiceProvider); 125 | WritableSettingsStore writableUserSettingsStore = settingsManager.GetWritableSettingsStore(SettingsScope.UserSettings); 126 | if (!writableUserSettingsStore.CollectionExists(collectionName)) 127 | writableUserSettingsStore.CreateCollection(collectionName); 128 | 129 | bool firstRun = writableUserSettingsStore.GetBoolean(collectionName, propertyName, true); 130 | if (firstRun) 131 | { 132 | writableUserSettingsStore.SetBoolean(collectionName, propertyName, false); 133 | Execute(package); 134 | } 135 | } 136 | } 137 | 138 | /// 139 | /// Shows "Parallel Builds Monitor" tool window when "VS -> Menu -> View -> Other Windows -> Parallel Builds Monitor" is clicked. 140 | /// 141 | private static void Execute(AsyncPackage package) 142 | { 143 | ThreadHelper.ThrowIfNotOnUIThread(); 144 | 145 | // Get the instance number 0 of this tool window. This window is single instance so this instance 146 | // is actually the only one. 147 | // The last flag is set to true so that if the tool window does not exists it will be created. 148 | ToolWindowPane window = package.FindToolWindow(typeof(PBMWindow), 0, true); 149 | if ((null == window) || (null == window.Frame)) 150 | { 151 | throw new NotSupportedException("Cannot create tool window"); 152 | } 153 | 154 | IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame; 155 | Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show()); 156 | } 157 | } 158 | 159 | //// see InitializeToolWindowAsync() method for details about this 160 | //public class PBMWindowState 161 | //{ 162 | // public EnvDTE80.DTE2 DTE { get; set; } 163 | //} 164 | } 165 | -------------------------------------------------------------------------------- /Example/constexpr_templates/constexpr_templates.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {3982C5EC-7B79-41B1-B5C8-700E57723704} 24 | Win32Proj 25 | constexprtemplates 26 | 10.0.17763.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | true 48 | 49 | 50 | Application 51 | false 52 | v141 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | true 79 | 80 | 81 | false 82 | 83 | 84 | false 85 | 86 | 87 | 88 | Use 89 | Level3 90 | Disabled 91 | true 92 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 93 | true 94 | /Bv %(AdditionalOptions) 95 | 96 | 97 | Console 98 | true 99 | 100 | 101 | "$(ProjectDir)PostBuildEvent.bat" 102 | 103 | 104 | 105 | 106 | Use 107 | Level3 108 | Disabled 109 | true 110 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 111 | true 112 | /Bv %(AdditionalOptions) 113 | 114 | 115 | Console 116 | true 117 | 118 | 119 | "$(ProjectDir)PostBuildEvent.bat" 120 | 121 | 122 | 123 | 124 | Use 125 | Level3 126 | MaxSpeed 127 | true 128 | true 129 | true 130 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 131 | true 132 | /Bv %(AdditionalOptions) 133 | 134 | 135 | Console 136 | true 137 | true 138 | true 139 | 140 | 141 | "$(ProjectDir)PostBuildEvent.bat" 142 | 143 | 144 | 145 | 146 | Use 147 | Level3 148 | MaxSpeed 149 | true 150 | true 151 | true 152 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 153 | true 154 | /Bv %(AdditionalOptions) 155 | 156 | 157 | Console 158 | true 159 | true 160 | true 161 | 162 | 163 | "$(ProjectDir)PostBuildEvent.bat" 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | Create 177 | Create 178 | Create 179 | Create 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /Plugin/PBMCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Design; 3 | using System.Runtime.InteropServices; 4 | using Microsoft; 5 | using Microsoft.VisualStudio.Shell; 6 | using Microsoft.VisualStudio.Shell.Interop; 7 | using EnvDTE; 8 | using EnvDTE80; 9 | using System.Collections.Generic; 10 | using System.Diagnostics; 11 | 12 | namespace ParallelBuildsMonitor 13 | { 14 | /// 15 | /// Goal of this class is: 16 | /// - subscrible/unsubscribe to Visual Studio Events 17 | /// - provide access to IServiceProvider interface for other classes 18 | /// - create menu 19 | /// - provide commands and query statuses for menu items 20 | /// 21 | internal static class PBMCommand 22 | { 23 | #region Members 24 | 25 | private static Microsoft.VisualStudio.Shell.Package Package { get; set; } 26 | public static IServiceProvider ServiceProvider { get { return Package; } } 27 | private static DTE2 Dte { get; set; } 28 | private static Events.SolutionEvents SolutionEvents { get; set; } 29 | private static Events.BuildEvents BuildEvents { get; set; } 30 | private static DataModel DataModel { get { return DataModel.Instance; } } // Convinient accessor to data. 31 | 32 | #endregion Members 33 | 34 | #region Initialize 35 | 36 | /// 37 | /// 38 | /// 39 | /// Owner package, not null. 40 | public static void Initialize(Microsoft.VisualStudio.Shell.Package package) 41 | { 42 | ThreadHelper.ThrowIfNotOnUIThread(); 43 | 44 | if (package == null) 45 | return; 46 | 47 | if (Package != null) 48 | return; // Protection against double initialization (double subscription to events, double menus etc) 49 | 50 | Package = package; 51 | Dte = ServiceProvider.GetService(typeof(DTE)) as EnvDTE80.DTE2; 52 | Assumes.Present(Dte); 53 | 54 | var svc = ServiceProvider.GetService(typeof(SVsSolution)) as IVsSolution; 55 | Assumes.Present(svc); 56 | 57 | SolutionEvents = new ParallelBuildsMonitor.Events.SolutionEvents(); 58 | 59 | svc.AdviseSolutionEvents(SolutionEvents, out _); 60 | 61 | var svb = ServiceProvider.GetService(typeof(SVsSolutionBuildManager)) as IVsSolutionBuildManager; 62 | Assumes.Present(svb); 63 | 64 | BuildEvents = new ParallelBuildsMonitor.Events.BuildEvents(); 65 | svb.AdviseUpdateSolutionEvents(BuildEvents, out _); 66 | 67 | CreateMenu(); 68 | } 69 | 70 | #endregion Initialize 71 | 72 | #region Menu 73 | 74 | [Guid("048AF9A5-402D-4441-B221-5EEC9ACD93DB")] 75 | public enum ContextMenuCommandSet 76 | { 77 | idContextMenu = 0x1000, 78 | SaveAsPng = 0x0101, 79 | SaveAsCsv = 0x0102 80 | } 81 | 82 | private static void CreateMenu() 83 | { 84 | OleMenuCommandService commandService = ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService; 85 | Assumes.Present(commandService); 86 | 87 | // Save As .png 88 | var SaveAsPngCommandID = new CommandID(typeof(ContextMenuCommandSet).GUID, (int)ContextMenuCommandSet.SaveAsPng); 89 | var menuItemSaveAsPng = new OleMenuCommand(SaveAsPng, SaveAsPngCommandID); 90 | menuItemSaveAsPng.BeforeQueryStatus += MenuItemSaveAsPng_BeforeQueryStatus; 91 | commandService.AddCommand(menuItemSaveAsPng); 92 | 93 | // Save As .csv 94 | var SaveAsCsvCommandID = new CommandID(typeof(ContextMenuCommandSet).GUID, (int)ContextMenuCommandSet.SaveAsCsv); 95 | var menuItemSaveAsCsv = new OleMenuCommand(SaveAsCsv, SaveAsCsvCommandID); 96 | menuItemSaveAsCsv.BeforeQueryStatus += MenuItemSaveAsCsv_BeforeQueryStatus; 97 | commandService.AddCommand(menuItemSaveAsCsv); 98 | } 99 | 100 | private static void MenuItemSaveAsPng_BeforeQueryStatus(object sender, EventArgs e) 101 | { 102 | if (sender is OleMenuCommand myCommand) 103 | myCommand.Enabled = ViewModel.Instance.IsGraphDrawn; 104 | } 105 | 106 | private static void SaveAsPng(object sender, EventArgs e) 107 | { 108 | try 109 | { 110 | PBMWindow window = Package.FindToolWindow(typeof(PBMWindow), 0, true) as PBMWindow; 111 | PBMControl control = window?.Content as PBMControl; 112 | control?.SaveGraph(null /*pathToPngFile*/); 113 | } 114 | catch 115 | { 116 | Debug.Assert(false, "Saving Gantt chart as .png failed! Exception thrown while trying save .png file."); 117 | } 118 | } 119 | 120 | private static void MenuItemSaveAsCsv_BeforeQueryStatus(object sender, EventArgs e) 121 | { 122 | if (sender is OleMenuCommand myCommand) 123 | myCommand.Enabled = (DataModel.CriticalPath.Count > 0); 124 | } 125 | 126 | private static void SaveAsCsv(object sender, EventArgs e) 127 | { 128 | try 129 | { 130 | ThreadHelper.ThrowIfNotOnUIThread(); 131 | string outputPaneContent = GetAllTextFromPane(GetOutputBuildPane()); // Output Build Pane/Window can be cleared even during build, so this is not perfect solution... 132 | SaveCsv.SaveAsCsv(outputPaneContent); 133 | } 134 | catch 135 | { 136 | Debug.Assert(false, "Saving .csv failure! Exception thrown while trying save .csv file."); 137 | } 138 | } 139 | 140 | #endregion Menu 141 | 142 | #region IdeEvents 143 | 144 | /// 145 | /// 146 | /// 147 | /// The same as Project.UniqueName property 148 | /// 149 | /// 150 | /// 151 | public static void BuildEvents_OnBuildProjConfigBegin(string ProjectUniqueName) 152 | { 153 | DataModel.AddCurrentBuild(ProjectUniqueName); 154 | } 155 | /// 156 | /// 157 | /// 158 | /// The same as Project.UniqueName property 159 | /// 160 | /// 161 | /// 162 | /// 163 | public static void BuildEvents_OnBuildProjConfigDone(string ProjectUniqueName, bool Success) 164 | { 165 | DataModel.FinishCurrentBuild(ProjectUniqueName, Success); 166 | } 167 | 168 | public static void BuildEvents_OnBuildBegin() 169 | { 170 | ThreadHelper.ThrowIfNotOnUIThread(); 171 | 172 | DataModel.BuildBegin(System.IO.Path.GetFileName(Dte.Solution.FileName)); 173 | GraphControl.Instance?.BuildBegin(); 174 | } 175 | 176 | /// 177 | /// Get build dependencies. 178 | /// 179 | /// Dictionary where key is Project.UniqueName 180 | /// and value is list of projects that this projects depend on in Project.UniqueName form 181 | static private Dictionary> GetProjectDependenies() 182 | { 183 | ThreadHelper.ThrowIfNotOnUIThread(); 184 | 185 | Dictionary> deps = new Dictionary>(); 186 | 187 | foreach (BuildDependency bd in Dte.Solution.SolutionBuild.BuildDependencies) 188 | { 189 | string name = bd.Project.UniqueName; 190 | List RequiredProjects = new List(); 191 | if (bd.RequiredProjects is Array dep) 192 | { 193 | foreach (Project proj in dep) 194 | RequiredProjects.Add(proj.UniqueName); 195 | } 196 | 197 | deps.Add(name, RequiredProjects); 198 | } 199 | 200 | return deps; 201 | } 202 | 203 | /// 204 | /// BuildEvents_OnBuildDone is called when solution build is finished. 205 | /// 206 | /// 207 | /// 208 | public static void BuildEvents_OnBuildDone(bool findAndSetCriticalPath) 209 | { 210 | DataModel.BuildDone(GetProjectDependenies(), findAndSetCriticalPath); 211 | GraphControl.Instance?.BuildDone(); 212 | } 213 | 214 | /// 215 | /// Event called on Closing Solution without closing VS ("VS -> File -> Close Solution"). 216 | /// 217 | /// 218 | /// Clear data and graph from prevously executed build. 219 | /// 220 | public static void AfterSolutionClosing() 221 | { 222 | DataModel.Reset(); 223 | GraphControl.Instance?.InvalidateVisual(); 224 | } 225 | 226 | #endregion IdeEvents 227 | 228 | #region HelperMethods 229 | 230 | public static EnvDTE.OutputWindowPane GetOutputBuildPane() 231 | { 232 | ThreadHelper.ThrowIfNotOnUIThread(); 233 | 234 | EnvDTE.OutputWindowPanes panes = Dte.ToolWindows.OutputWindow.OutputWindowPanes; 235 | foreach (EnvDTE.OutputWindowPane pane in panes) 236 | { 237 | if (pane.Name.Contains("Build")) 238 | return pane; 239 | } 240 | 241 | return null; 242 | } 243 | 244 | public static string GetAllTextFromPane(EnvDTE.OutputWindowPane Pane) 245 | { 246 | ThreadHelper.ThrowIfNotOnUIThread(); 247 | 248 | if (Pane == null) 249 | return null; 250 | 251 | TextDocument doc = Pane.TextDocument; 252 | TextSelection sel = doc.Selection; 253 | sel.StartOfDocument(false); 254 | sel.EndOfDocument(true); 255 | 256 | string content = sel.Text; 257 | 258 | return content; 259 | } 260 | 261 | #endregion HelperMethods 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /Plugin/DataModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Timers; 7 | 8 | namespace ParallelBuildsMonitor 9 | { 10 | /// 11 | /// Container to holds all build statistics data 12 | /// 13 | public class DataModel : IDisposable 14 | { 15 | #region Properties 16 | 17 | public string SolutionName { get; private set; } 18 | /// 19 | /// Holds point in time when entire build started (Solution). 20 | /// 21 | public DateTime StartTime { get; private set; } 22 | public ReadOnlyDictionary> CurrentBuilds { get { return new ReadOnlyDictionary>(currentBuilds); } } 23 | public ReadOnlyCollection FinishedBuilds { get { return finishedBuilds.AsReadOnly(); } } 24 | public ReadOnlyDictionary> ProjectDependenies { get { return new ReadOnlyDictionary>(projectDependenies); } } 25 | public ReadOnlyCollection CriticalPath { get { return criticalPath.AsReadOnly(); } } 26 | 27 | public ReadOnlyCollection> CpuUsage { get { return cpuUsage.AsReadOnly(); } } 28 | public ReadOnlyCollection> HddUsage { get { return hddUsage.AsReadOnly(); } } 29 | public int MaxParallelBuilds { get; private set; } = 0; 30 | 31 | public bool IsBuilding { get { return performanceTimer.Enabled; } } 32 | 33 | #endregion Properties 34 | 35 | #region Members 36 | 37 | private readonly Dictionary> currentBuilds = new Dictionary>(); //string is ProjectUniqueName, uint is project build order number, long is project Start time, relative, counted since DataModel.StartTime in DateTime.Ticks units. 38 | private readonly List finishedBuilds = new List(); 39 | private Dictionary> projectDependenies = new Dictionary>(); //string is ProjectUniqueName, List is list of projects that Key project depends on 40 | private readonly List criticalPath = new List(); 41 | 42 | private readonly List> cpuUsage = new List>(); 43 | private readonly List> hddUsage = new List>(); 44 | 45 | private readonly PerformanceCounter cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); 46 | private readonly PerformanceCounter hddCounter = new PerformanceCounter("PhysicalDisk", "% Disk Time", "_Total"); 47 | private readonly static double performanceTimerInterval = 1000; // 1000 means collect data every 1s. 48 | private readonly System.Timers.Timer performanceTimer = new System.Timers.Timer(performanceTimerInterval); 49 | 50 | static uint projectBuildOrderNumber = 0; 51 | 52 | #endregion Members 53 | 54 | #region Creator, Constructors 55 | 56 | private DataModel() 57 | { 58 | performanceTimer.Elapsed += new ElapsedEventHandler(PerformanceTimerEventTick); 59 | } 60 | 61 | private static DataModel instance = null; 62 | // Singleton 63 | public static DataModel Instance 64 | { 65 | get 66 | { 67 | if (instance == null) 68 | instance = new DataModel(); 69 | return instance; 70 | } 71 | } 72 | 73 | public void Reset() 74 | { 75 | //instance = new DataModel(); // It doesn't work! So do it manually... 76 | 77 | StartTime = DateTime.Now; 78 | MaxParallelBuilds = 0; 79 | currentBuilds.Clear(); 80 | projectBuildOrderNumber = 0; 81 | finishedBuilds.Clear(); 82 | criticalPath.Clear(); 83 | cpuUsage.Clear(); 84 | hddUsage.Clear(); 85 | } 86 | 87 | #endregion Creator, Constructors 88 | 89 | #region Dispose 90 | 91 | private bool disposed = false; // Flag: Has Dispose already been called? 92 | 93 | // Public implementation of Dispose pattern callable by consumers. 94 | public void Dispose() 95 | { 96 | Dispose(true); 97 | GC.SuppressFinalize(this); 98 | } 99 | 100 | // Protected implementation of Dispose pattern. 101 | protected virtual void Dispose(bool disposing) 102 | { 103 | if (disposed) 104 | return; 105 | 106 | if (disposing) 107 | { 108 | // Free any other managed objects here. 109 | cpuCounter.Dispose(); 110 | hddCounter.Dispose(); 111 | performanceTimer.Dispose(); 112 | } 113 | 114 | disposed = true; 115 | } 116 | 117 | #endregion Dispose 118 | 119 | #region Manipulation 120 | 121 | /// 122 | /// Call this method when starting colleting statistics for solution (.sln) 123 | /// 124 | public void BuildBegin(string solutionName) 125 | { 126 | Reset(); 127 | SolutionName = solutionName; 128 | StartTime = DateTime.Now; 129 | performanceTimer.Start(); 130 | CollectPerformanceData(); // Collect 1st sample. Second will be taken after performanceTimerInterval 131 | } 132 | 133 | public void BuildDone(Dictionary> projectDependenies, bool findAndSetCriticalPath) 134 | { 135 | this.projectDependenies = projectDependenies; 136 | performanceTimer.Stop(); 137 | 138 | if (findAndSetCriticalPath) 139 | FindAndSetCriticalPath(); 140 | } 141 | 142 | /// 143 | /// Call this method when new project (inside solution) starts building. 144 | /// 145 | /// 146 | public void AddCurrentBuild(string projectKey) 147 | { 148 | // It is assumed that OnBuildProjConfigBegin event come in the same order as numbering (1>...) shows in the Output Build Pane/Window 149 | currentBuilds[projectKey] = new Tuple(++projectBuildOrderNumber, DateTime.Now.Ticks - StartTime.Ticks); 150 | if (currentBuilds.Count > MaxParallelBuilds) 151 | { 152 | MaxParallelBuilds = currentBuilds.Count; 153 | } 154 | } 155 | 156 | /// 157 | /// This method move project from CurrentBuilds to FinishedBuilds array 158 | /// 159 | /// ProjectUniqueName in Project.UniqueName format 160 | /// true on success (when project was successfully moved from CurrentBuilds to FinishedBuilds array 161 | public bool FinishCurrentBuild(string ProjectUniqueName, bool wasBuildSucceessful) 162 | { 163 | if (!CurrentBuilds.ContainsKey(ProjectUniqueName)) 164 | return false; 165 | 166 | uint projectBuildOrderNumber = CurrentBuilds[ProjectUniqueName].Item1; 167 | long start = CurrentBuilds[ProjectUniqueName].Item2; 168 | long end = DateTime.Now.Ticks - StartTime.Ticks; 169 | currentBuilds.Remove(ProjectUniqueName); 170 | finishedBuilds.Add(new BuildInfo(ProjectUniqueName, GetHumanReadableProjectName(ProjectUniqueName), projectBuildOrderNumber, start, end, wasBuildSucceessful)); 171 | 172 | return true; 173 | } 174 | 175 | #endregion Manipulation 176 | 177 | #region HelperMethods 178 | 179 | /// 180 | /// Return common string with solution name, build start time and machine info. 181 | /// 182 | /// 183 | public string GetSolutionNameWithMachineInfo(string Separator, bool WithBuildStartedStr) 184 | { 185 | return SolutionName + Separator + ((WithBuildStartedStr) ? "Build Started: " : "") + StartTime.ToString("yyyy-MM-dd HH:mm:ss") + Separator + MachineInfo.Instance.ToString(Separator); // Data format "2018-05-08 01.09.07" to preserve correct sorting 186 | } 187 | 188 | /// 189 | /// Return common part for PBM file names, so they are unambiguous and can be sorted on HDD in easy way. 190 | /// 191 | /// 192 | public string GetSaveFileNamePrefix() 193 | { 194 | string date = StartTime.ToString("yyyy-MM-dd HH.mm.ss"); //Format "2018-05-08 01.09.07" to preserve correct sorting 195 | return "PBM " + SolutionName + " " + date; 196 | } 197 | 198 | /// 199 | /// Number of time (in %) for how long max parallel builds were run during solution buils. 200 | /// 201 | /// 202 | public long PercentageProcessorUse() 203 | { 204 | long percentage = 0; 205 | if (MaxParallelBuilds > 0) 206 | { 207 | long nowTicks = DateTime.Now.Ticks; 208 | long firstTick = long.MaxValue; 209 | long lastTick = 0; 210 | long totTicks = 0; 211 | foreach (BuildInfo info in FinishedBuilds) 212 | { 213 | totTicks += info.end - info.begin; 214 | if (info.end > lastTick) 215 | lastTick = info.end; 216 | if (info.begin < firstTick) 217 | firstTick = info.begin; 218 | } 219 | foreach (Tuple start in CurrentBuilds.Values) 220 | { 221 | lastTick = nowTicks - StartTime.Ticks; 222 | totTicks += nowTicks - (start.Item2 + StartTime.Ticks); 223 | } 224 | totTicks /= MaxParallelBuilds; 225 | if (lastTick > firstTick) 226 | { 227 | percentage = totTicks * 100 / (lastTick - firstTick); 228 | } 229 | } 230 | return percentage; 231 | } 232 | 233 | private BuildInfo? GetFinishedProject(string ProjectUniqueName) 234 | { 235 | foreach (BuildInfo proj in finishedBuilds) 236 | { 237 | if (proj.ProjectUniqueName == ProjectUniqueName) 238 | return proj; 239 | } 240 | 241 | return null; 242 | } 243 | 244 | /// 245 | /// Find critical path and store it in criticalPath list. 246 | /// 247 | /// 248 | /// ProjectDependenies need to be initialized before calling this method. 249 | /// 250 | /// true when successfully critical path found and stored in CriticalPath property. 251 | private bool FindAndSetCriticalPath() 252 | { 253 | criticalPath.Clear(); 254 | 255 | if ((finishedBuilds.Count < 1) || (ProjectDependenies.Count < 1)) 256 | return false; 257 | 258 | BuildInfo lastProject = finishedBuilds[finishedBuilds.Count-1]; 259 | criticalPath.Add(lastProject); 260 | if (finishedBuilds.Count == 1) 261 | return true; // Case when Build/Rebuild only 1 project 262 | 263 | while (true) 264 | { 265 | List precedentProjects = ProjectDependenies[lastProject.ProjectUniqueName]; 266 | if (precedentProjects.Count < 1) 267 | { 268 | criticalPath.Reverse(); 269 | return true; // No precendent project means that this is first project. 270 | } 271 | 272 | double minDiff = double.MaxValue; 273 | BuildInfo minProject = new BuildInfo(); 274 | foreach (string precendentProjectUN in precedentProjects) 275 | { 276 | BuildInfo? precendentProject = GetFinishedProject(precendentProjectUN); 277 | if (precendentProject == null) 278 | { 279 | Debug.Assert(false, "Missing project in finishedBuilds collection. Critical path will be drawn incorrectly!"); 280 | continue; 281 | } 282 | 283 | long diff = lastProject.begin - precendentProject.Value.end; 284 | if (diff < minDiff) 285 | { 286 | minDiff = diff; 287 | minProject = precendentProject.Value; 288 | } 289 | } 290 | 291 | criticalPath.Add(minProject); 292 | lastProject = minProject; 293 | } 294 | } 295 | 296 | /// 297 | /// Convert Project.UniqueName into short human readable string 298 | /// 299 | /// Project name in Project.UniqueName format 300 | /// Human readable project name 301 | static public string GetHumanReadableProjectName(string ProjectUniqueName) 302 | { 303 | FileInfo fi = new FileInfo(ProjectUniqueName); 304 | string key = fi.Name; 305 | return key; 306 | } 307 | 308 | #endregion HelperMethods 309 | 310 | #region CPU, HDD Performance 311 | 312 | private void PerformanceTimerEventTick(object sender, ElapsedEventArgs e) 313 | { 314 | CollectPerformanceData(); 315 | } 316 | 317 | private void CollectPerformanceData() 318 | { 319 | long ticks = DateTime.Now.Ticks; 320 | cpuUsage.Add(new Tuple(ticks, cpuCounter.NextValue())); 321 | hddUsage.Add(new Tuple(ticks, hddCounter.NextValue())); 322 | } 323 | 324 | #endregion CPU, HDD Performance 325 | } 326 | } 327 | --------------------------------------------------------------------------------