diff --git a/.gitignore b/.gitignore index 757a920..c58e9b5 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,6 @@ _ReSharper*/ *.IntegrationTests/App.config *.bin /packages -/Solution Items \ No newline at end of file +/Solution Items +/backup +/.vs \ No newline at end of file diff --git a/BuildMonitor.UnitTests/BuildMonitor.UnitTests.csproj b/BuildMonitor.UnitTests/BuildMonitor.UnitTests.csproj index 9c7cd44..940c247 100644 --- a/BuildMonitor.UnitTests/BuildMonitor.UnitTests.csproj +++ b/BuildMonitor.UnitTests/BuildMonitor.UnitTests.csproj @@ -1,5 +1,7 @@  - + + + Debug @@ -9,10 +11,13 @@ Properties BuildMonitor.UnitTests BuildMonitor.UnitTests - v4.5 + v4.7.2 512 ..\ true + + + true @@ -32,8 +37,11 @@ 4 - - ..\packages\NUnit.2.6.2\lib\nunit.framework.dll + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + + ..\packages\NUnit.3.11.0\lib\net45\nunit.framework.dll @@ -41,6 +49,8 @@ + + @@ -56,11 +66,21 @@ BuildMonitor + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/BuildMonitorPackage/BuildMonitorPackage.vsct b/BuildMonitorPackage/BuildMonitorPackage.vsct new file mode 100644 index 0000000..2f39af2 --- /dev/null +++ b/BuildMonitorPackage/BuildMonitorPackage.vsct @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BuildMonitorPackage/EnvDTEConstants.cs b/BuildMonitorPackage/EnvDTEConstants.cs new file mode 100644 index 0000000..cf03221 --- /dev/null +++ b/BuildMonitorPackage/EnvDTEConstants.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BuildMonitorPackage +{ + // I don't unerstand why we need this class, but it mimics EnvDTE.Constants + // However using EnvDTE.Constants produces compile errors, which is why we have this instead + // More information here: https://blogs.msdn.microsoft.com/mshneer/2009/12/07/vs-2010-compiler-error-interop-type-xxx-cannot-be-embedded-use-the-applicable-interface-instead/ + internal class EnvDTEConstants + { + public const string vsWindowKindOutput = "{34E76E81-EE4A-11D0-AE2E-00A0C90FFFC3}"; + } +} diff --git a/BuildMonitorPackage/OutputWindowWrapper.cs b/BuildMonitorPackage/OutputWindowWrapper.cs new file mode 100644 index 0000000..8f0e3ec --- /dev/null +++ b/BuildMonitorPackage/OutputWindowWrapper.cs @@ -0,0 +1,45 @@ +using System; +using EnvDTE; +using Microsoft.VisualStudio.Shell.Interop; + +namespace BuildMonitorPackage { + internal class OutputWindowWrapper { + private readonly OutputWindowPane outputWindowPane; + + public OutputWindowWrapper(IServiceProvider serviceContainer) + { + var dte = serviceContainer.GetService(typeof(SDTE)) as DTE; + var outputWindow = (OutputWindow)dte.Windows.Item(EnvDTEConstants.vsWindowKindOutput).Object; + + foreach (OutputWindowPane pane in outputWindow.OutputWindowPanes) + { + if (pane.Name == "Build monitor") + { + outputWindowPane = pane; + } + } + + if (outputWindowPane == null) + { + outputWindowPane = outputWindow.OutputWindowPanes.Add("Build monitor"); + } + } + + public void Write(string text) { + outputWindowPane.OutputString(text); + } + + public void Write(string format, params object[] args) { + outputWindowPane.OutputString(string.Format(format, args)); + } + + public void WriteLine(string text) { + Write(text + Environment.NewLine); + } + + public void WriteLine(string format, params object[] args) + { + Write(format + Environment.NewLine, args); + } + } +} diff --git a/BuildMonitorPackage/Properties/AssemblyInfo.cs b/BuildMonitorPackage/Properties/AssemblyInfo.cs index 0f12b42..9f3281c 100644 --- a/BuildMonitorPackage/Properties/AssemblyInfo.cs +++ b/BuildMonitorPackage/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyTitle("BuildMonitorPackage")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Daniel Vinntreus")] +[assembly: AssemblyCompany("RES")] [assembly: AssemblyProduct("BuildMonitorPackage")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] @@ -29,8 +29,8 @@ // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("2.0.0.0")] +[assembly: AssemblyFileVersion("2.0.0.0")] diff --git a/BuildMonitorPackage/Resources.Designer.cs b/BuildMonitorPackage/Resources.Designer.cs index abbe023..3985635 100644 --- a/BuildMonitorPackage/Resources.Designer.cs +++ b/BuildMonitorPackage/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.34003 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace BuildMonitorPackage { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { diff --git a/BuildMonitorPackage/Resources/AnalyseBuildTimesCommand.png b/BuildMonitorPackage/Resources/AnalyseBuildTimesCommand.png new file mode 100644 index 0000000..b22d975 Binary files /dev/null and b/BuildMonitorPackage/Resources/AnalyseBuildTimesCommand.png differ diff --git a/BuildMonitorPackage/Settings.cs b/BuildMonitorPackage/Settings.cs index 372846d..cf38544 100644 --- a/BuildMonitorPackage/Settings.cs +++ b/BuildMonitorPackage/Settings.cs @@ -1,5 +1,7 @@ using System; +using System.Diagnostics; using System.IO; +using Microsoft.VisualStudio.Settings; // ReSharper disable InconsistentNaming namespace BuildMonitorPackage @@ -9,27 +11,114 @@ public static class OptionKey public const string SolutionId = "bm_solution_id"; } - public static class Settings - { - public static string RepositoryPath = string.Format("{0}\\{1}\\{2}", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ApplicationFolderName, JsonFileName); + public class Settings { + + private static readonly string DefaultRepositoryPath = Path.Combine("%ApplicationData%", ApplicationFolderName, JsonFileName); - private static string ApplicationFolder = string.Format("{0}\\{1}", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ApplicationFolderName); + private string rawRepositoryPath = DefaultRepositoryPath; private const string ApplicationFolderName = "Build Monitor"; + private const string JsonFileName = "buildtimes.json"; - - public static void CreateApplicationFolderIfNotExist() + private readonly WritableSettingsStore settingsStore; + + private const string CollectionPath = "BuildMonitor"; + + public static Settings Instance { get; set; } + + public Settings(WritableSettingsStore settingsStore) { + this.settingsStore = settingsStore; + LoadSettings(); + CreateApplicationFolderIfNotExist(); + } + + public string RawRepositoryPath + { + get => rawRepositoryPath; + set + { + if (rawRepositoryPath != value) + { + rawRepositoryPath = value; + SaveSettings(); + } + } + } + + public string RepositoryPath => ExpandPath(rawRepositoryPath); + + private void CreateApplicationFolderIfNotExist() { - if(!Directory.Exists(ApplicationFolder)) + string folder = Path.GetDirectoryName(RepositoryPath); + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } + if (!File.Exists(RepositoryPath)) + { + using (var f = File.Create(RepositoryPath)){} + } + } + + private void LoadSettings() { + try { - Directory.CreateDirectory(ApplicationFolder); + RawRepositoryPath = settingsStore.GetString(CollectionPath, "RepositoryPath", DefaultRepositoryPath); } - if(!File.Exists(RepositoryPath)) + catch (Exception ex) { - using(var f = File.Create(RepositoryPath)){} + Debug.Fail(ex.Message); } } + + private void SaveSettings() { + try + { + if (!settingsStore.CollectionExists(CollectionPath)) + { + settingsStore.CreateCollection(CollectionPath); + } + + settingsStore.SetString(CollectionPath, "RepositoryPath", rawRepositoryPath); + } + catch (Exception ex) + { + Debug.Fail(ex.Message); + } + } + + /// + /// Expands a path possibly starting with a + /// to a full path. + /// + private static string ExpandPath(string path) + { + if (!path.StartsWith("%")) + { + return path; + } + + int splitIndex = path.IndexOf("%", 1, StringComparison.InvariantCulture) + 1; + string maybeSpecialFolder = path.Substring(0, splitIndex).Trim('%'); + string rest = path.Substring(splitIndex); + while (rest.StartsWith(Path.DirectorySeparatorChar.ToString())) + { + // The remaining path cannot start with a rooted path as that + // will "break" Path.Combine. + rest = rest.Substring(1); + } + + foreach (var @enum in Enum.GetNames(typeof(Environment.SpecialFolder))) + { + if (@enum.Equals(maybeSpecialFolder, StringComparison.InvariantCultureIgnoreCase)) + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), rest); + } + } + + return path; + } } } // ReSharper restore InconsistentNaming \ No newline at end of file diff --git a/BuildMonitorPackage/SettingsPage.cs b/BuildMonitorPackage/SettingsPage.cs new file mode 100644 index 0000000..afb75c1 --- /dev/null +++ b/BuildMonitorPackage/SettingsPage.cs @@ -0,0 +1,27 @@ +using System.ComponentModel; +using Microsoft.VisualStudio.Shell; + +namespace BuildMonitorPackage +{ + public class SettingsPage : DialogPage + { + [Category("Build Monitor")] + [DisplayName("Output File Path")] + [Description("Specifies the path to the file to which data is persisted. Can contain special folders, see https://msdn.microsoft.com/en-us/library/system.environment.specialfolder(v=vs.110).aspx, enclosed in %. Example: %ApplicationData%\\Build Monitor\\buildtimes.json (default)")] + public string RepositoryPath { get; set; } + + public SettingsPage() + { + RepositoryPath = Settings.Instance.RawRepositoryPath; + } + + protected override void OnApply(PageApplyEventArgs args) + { + base.OnApply(args); + Settings.Instance.RawRepositoryPath = RepositoryPath; + + var output = new OutputWindowWrapper(ServiceProvider.GlobalProvider); + output.WriteLine("New path to persist data: {0}", Settings.Instance.RepositoryPath); + } + } +} diff --git a/BuildMonitorPackage/app.config b/BuildMonitorPackage/app.config new file mode 100644 index 0000000..e386bb9 --- /dev/null +++ b/BuildMonitorPackage/app.config @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BuildMonitorPackage/packages.config b/BuildMonitorPackage/packages.config new file mode 100644 index 0000000..e47f702 --- /dev/null +++ b/BuildMonitorPackage/packages.config @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BuildMonitorPackage/source.extension.vsixmanifest b/BuildMonitorPackage/source.extension.vsixmanifest index a09d9e9..99ca386 100644 --- a/BuildMonitorPackage/source.extension.vsixmanifest +++ b/BuildMonitorPackage/source.extension.vsixmanifest @@ -1,27 +1,26 @@  - - - Build Monitor - Measure how long it takes to build your solution and individual projects in Visual Studio. - http://visualstudiogallery.msdn.microsoft.com/b0c87e47-f4ee-4935-9a59-f2c81ce692ab - license.txt - Build, Monitor, Build Monitor, Build times - - - - - - - - - - - - - - - - - + + + Build Monitor + Measure how long it takes to build your solution and individual projects in Visual Studio. + http://visualstudiogallery.msdn.microsoft.com/b0c87e47-f4ee-4935-9a59-f2c81ce692ab + license.txt + Build, Monitor, Build Monitor, Build times + + + + + + + + + + + + + + + + diff --git a/BuildMonitorPackage/source.extension.vsixmanifest.orig b/BuildMonitorPackage/source.extension.vsixmanifest.orig new file mode 100644 index 0000000..1c20c74 --- /dev/null +++ b/BuildMonitorPackage/source.extension.vsixmanifest.orig @@ -0,0 +1,31 @@ + + + +<<<<<<< HEAD + +======= + +>>>>>>> daniel/master + Build Monitor + Measure how long it takes to build your solution and individual projects in Visual Studio. + http://visualstudiogallery.msdn.microsoft.com/b0c87e47-f4ee-4935-9a59-f2c81ce692ab + license.txt + Build, Monitor, Build Monitor, Build times + + + + + + + + + + + + + + + + + + diff --git a/README.markdown b/README.markdown index 7303def..91c22eb 100644 --- a/README.markdown +++ b/README.markdown @@ -1,17 +1,28 @@ # Build monitor -Measures build times in Visual Studio + +[![Build status](https://ci.appveyor.com/api/projects/status/pmlrigeep485pv0m?svg=true)](https://ci.appveyor.com/project/ceddlyburge/buildmonitor) + +Measures build times in Visual Studio. + +This is a fork of the original, that adds the following: +- Support for Visual Studio 2019 +- An IsRebuildAll flag +- A form showing the breakdown by month and solution (under `Tools - Show Solution Build Times`) +- An option to choose the location of the json file where the results are saved ## To install -Download either via searching in Extension Manager in Visual studio or [download it manually](http://visualstudiogallery.msdn.microsoft.com/b0c87e47-f4ee-4935-9a59-f2c81ce692ab) + +Clone this repo, build, and then double click on the vsix in `BuildMonitorPackage\bin\Release` (or debug output folder). ## How it works + When any solution is opened, the extension is loaded and will start output to a output-window called "Build monitor" (Found in View -> Output -> Change "Show output from:" dropdown-list). + It measures solution total build time and each project built. -After every build the data is also saved to a file on disk (see output-window for path) as json. -## Notes for contributors -- Install the [Visual Studio 2013 SDK](http://www.microsoft.com/en-us/download/confirmation.aspx?id=40758) to get references needed to be able to open the projects. +After every build the data is also saved to a file on disk (see output-window for path) as json. ## Features and bugs -If you find a bug or desire some feature, please create an issue here in Github. \ No newline at end of file + +If you find a bug or desire some feature, please create an issue here in Github. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..9abaa9b --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,34 @@ +image: Visual Studio 2017 +environment: + github_token: + secure: MkekX4fNuL+MhfZYwE9xv40NFVl2PrRzIapgQVXoHnZb3aVWrgPKnoJ/PneRrLcL + code_cov_token: + secure: iXZYGk5+JJMkYPYRYhFBRXpG3KN/Q7gkyReh3hZAFYtX1p/885y1ZY6fdD1XzBSU +build_script: + # install sonarqube runner + - choco install "msbuild-sonarqube-runner" -y + - choco install opencover -y + - nuget restore "BuildMonitor.sln" -verbosity quiet + #- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER) { SonarQube.Scanner.MSBuild.exe begin /k:"res.configuration" /d:"sonar.host.url=https://sonarcloud.io" /d:"sonar.organization=res" /d:"sonar.login=a8e51e7eccb2eaa68c5f8d3c50e827ee2a0fa545" /d:sonar.verbose=false /d:"sonar.analysis.mode=preview" /d:"sonar.github.pullRequest=$env:APPVEYOR_PULL_REQUEST_NUMBER" /d:"sonar.github.repository=resgroup/configuration" /d:"sonar.github.oauth=$env:github_token" /d:sonar.cs.opencover.it.reportsPaths=coverage.xml } + #- ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { SonarQube.Scanner.MSBuild.exe begin /k:"res.configuration" /d:"sonar.host.url=https://sonarcloud.io" /d:"sonar.organization=res" /d:"sonar.login=a8e51e7eccb2eaa68c5f8d3c50e827ee2a0fa545" /d:sonar.verbose=false /d:sonar.cs.opencover.it.reportsPaths=coverage.xml } + #- SonarQube.Scanner.MSBuild.exe begin /k:"res.configuration" /d:"sonar.host.url=https://sonarcloud.io" /d:"sonar.organization=res" /d:"sonar.login=a8e51e7eccb2eaa68c5f8d3c50e827ee2a0fa545" /d:sonar.verbose=false + - msbuild BuildMonitor.sln /p:IncludeSymbols=true /verbosity:quiet /p:Configuration=Debug + #- msbuild RES.Configuration.Test\RES.Configuration.Test.csproj /nowarn:1125 /verbosity:quiet + #- SonarQube.Scanner.MSBuild.exe end /d:"sonar.login=a8e51e7eccb2eaa68c5f8d3c50e827ee2a0fa545" + +test_script: + # test and create coverage report. + OpenCover.Console.exe -register:user -target:"nunit3-console.exe" -targetargs:"BuildMonitor.UnitTests\bin\BuildMonitor.UnitTests.dll --result=myresults.xml;format=AppVeyor" -output:"coverage.xml" -returntargetcode + +after_test: + # upload the code coverage to codcov.io + - ps: | + $env:PATH = 'C:\msys64\usr\bin;' + $env:PATH + Invoke-WebRequest -Uri 'https://codecov.io/bash' -OutFile codecov.sh + bash codecov.sh -f "coverage.xml" + #- "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%" + #- pip install codecov + #- codecov -f "coverage.xml" -t %code_cov_token% +artifacts: +- path: coverage.xml +# - path: BuildMonitorPackage\obj\Release\BuildMonitorPackage.vsix