I have a solution in Visual Studio which contains several projects. I'd like to run a command at the very beginning of every build - no matter which projects are involved and whether or not they are up-to-date.
Essentially I need something similar to a solution-wide pre-build event, but unfortunately VS does not appear to support these. Does anyone know an alternative way of achieving what I need?
Unusual requirement. But it can be done. Add a new project to your solution, use the Visual C++ > General > Makefile Project template. Set its NMake > Build Command Line setting to the commands you want to execute. Use Project > Project Dependencies to make all other projects depend on it.
Short overview of my variants below
just a note: it is incomplete list of all existing (see also other answers etc.), I only support my original tricks in actual state...
Notes:
targets mode
to call the after.<name>.sln.targets
that available only for msbuild.exe (this does not requires additional steps, simply action). But only original engine (inc. vsCommandEvent) may allow additional scripting which support for example (7zip archiver, packing of nuget package without nuget.exe, remote servers etc.). However, it's not important for our question/problem and you can use any available option to support the solution-level if you see +
above.This variant is not for simple users of VS. However, it can be useful for your complete solution etc.
You should implement, for example:
e.g:
public sealed class YourPackage: Package, IVsSolutionEvents, IVsUpdateSolutionEvents2
{
...
public int UpdateSolution_Begin(ref int pfCancelUpdate)
{
//TODO:
}
}
Then, register handler with 'Advise' methods as priority listener, i.e. for IVsUpdateSolutionEvents2 you should use the AdviseUpdateSolutionEvents
It is important, because the BuildEvents (see EnvDTE) - probably will not help and may work too late - Example
Sample with AdviseUpdateSolutionEvents:
// http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.shell.interop.ivssolutionbuildmanager2.aspx
private IVsSolutionBuildManager2 sbm;
// http://msdn.microsoft.com/en-us/library/bb141335.aspx
private uint _sbmCookie;
...
sbm = (IVsSolutionBuildManager2)ServiceProvider.GlobalProvider.GetService(typeof(SVsSolutionBuildManager));
sbm.AdviseUpdateSolutionEvents(this, out _sbmCookie);
Where:
sbm
field should be as part of class for protection from GC.Now we can work with all projects at once - solution-level.
ok, you love something like this - MSBuild: Extending the solution build, but this variant may work with build processes from msbuild.exe and not from VS IDE...
But the VS is also uses targets (Build, Rebuild, Clean, ..) in project files (*.csproj, *.vcxproj, ..) when the build-operations is started. So we can also try this, but remember:
...
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\SolutionFile\ImportAfter\*" Condition="'$(ImportByWildcardBeforeSolution)' != 'false' and exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\SolutionFile\ImportAfter')" />
<Import Project="D:\tmp\p\after.name.sln.targets" Condition="exists('D:\tmp\p\after.name.sln.targets')" />
<Target Name="Build" />
<Target Name="Rebuild" />
<Target Name="Clean" />
<Target Name="Publish" />
Therefore, for work with common targets from VS IDE you can use only project files with some limitation (without modification/extending of VS, it means).
And so, if you need the common solution (i.e. you may not know about projects etc. - this can be, for example, for some box solutions and similar):
<Import Project="..\<SolutionFile>.targets" />
And for example, yes, it can be the 'Map of projects':
...
<Target Name="_Build" BeforeTargets="Build" DependsOnTargets="ProjectsMap">
<CallTarget Targets="_BuildPRE" Condition="$(ScopeDetectFirst)" />
<CallTarget Targets="_BuildPOST" Condition="$(ScopeDetectLast)" />
</Target>
<Target Name="_BuildPRE">
<!-- ... -->
</Target>
<Target Name="_BuildPOST">
<!-- ... -->
</Target>
...
In general, we'll use the map of projects and now we know 'what and when' should happen. It's safe for all or most cases (changes of build order or removing the any projects from solution). However! you should manage <Import>
section for new projects in first init. This is really inconvenient, but is also variant...
Today it is the most complete solution for work with a lot of events as the Events-Catcher with a variety of advanced actions for maintenance of your projects and libraries, building processes and processes at runtime from your Visual Studio and MSBuild Tool.
Different action types for all subprojects at once in solution as Solution-Events or individually for each.
https://visualstudiogallery.msdn.microsoft.com/0d1dbfd7-ed8a-40af-ae39-281bfeca2334/
If you want to use the Variant 1 above or need to see how to work with Shell.Interop, EnvDTE, IVsUpdateSolutionEvents2, MSBuild Engine etc., see here:
This variant is also not for simple users of VS. However, as for Variant 1 it can be useful for your box-solution etc.
It is not the same, but yes, it's also possible with EnvDTE.CommandEvents like in Variant 1 above.
You should already know (see above) about this solution for priority work with current type of the build action... So why not to use this as primary solution for current problem ?
_cmdEvents.BeforeExecute += (string guid, int id, object customIn, object customOut, ref bool cancelDefault) => {
if(UnifiedTypes.Build.VSCommand.existsById(id)) {
// ... your action
}
};
Where:
Description | guid | id |In |Out|
--------------------------|---------------------------------------|-----|---|---|
Started: Build Solution |{5EFC7975-14BC-11CF-9B2B-00AA00573819} | 882 | | |
Started: Rebuild Solution |{5EFC7975-14BC-11CF-9B2B-00AA00573819} | 883 | | |
Started: Clean Solution |{5EFC7975-14BC-11CF-9B2B-00AA00573819} | 885 | | |
http://vsce.r-eg.net/doc/Features/Solution-wide/
Moreover, optional you can suppress this commands if you need. In variant below you will see the complete solution for this way.
https://visualstudiogallery.msdn.microsoft.com/ad9f19b2-04c0-46fe-9637-9a52ce4ca661/
It also presents advanced handler of most events, but unlike the first it specialized for MS Visual Studio for advanced work with all commands and output data as manager of this. Not only for projects and solutions, but also for the whole Visual studio IDE.
In general, it is the common solution of Variant 4 and you can simply override all commands above to solve this problem.
And for the same Event-Actions model like in vsSolutionBuildEvent it can be useful for most cases.
There are open implementation for all these variants. See here and smile:
You can take a look on this article: MSBuild: Extending the solution build.
Seems to be exactly what you need.
We do this by adding an empty project and setting build events for this project. Then you have to give each project dependency to this empty project to make sure that it gets built everytime.
Another old post but inspired by @reg solution I wanted to run a simple build timer that would log the elapsed time for a solution build. I got the build events working using a powershell module which I load via the package manager console when the Visual Studio IDE starts.
So create a powershell module like BuildEvents.psm1
:
<#
.SYNOPSIS
Register solution build events
.DESCRIPTION
Registers the OnBuildBegin and OnBuildDone events for the entire solution
De-registers the events if called multiple times.
.EXAMPLE
RegisterBuildEvents
#>
function RegisterBuildEvents{
try {
Unregister-Event -SourceIdentifier "OnBuildBegin" -Force
} catch {
#we don't care if this doesn't work
}
try {
Unregister-Event -SourceIdentifier "OnBuildDone" -Force
} catch {
#we don't care if this doesn't work
}
$obj = [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($dte.Application.Events.BuildEvents, [EnvDTE.BuildEventsClass])
Register-ObjectEvent -InputObject $obj -EventName OnBuildBegin -Action {
# do stuff here on build begin
Write-Host "Solution build started!"
} -SourceIdentifier "OnBuildBegin"
Register-ObjectEvent -InputObject $obj -EventName OnBuildDone -Action {
# do stuff here on build done
Write-Host "Solution build done!"
} -SourceIdentifier "OnBuildDone"
}
# export the functions from the module
export-modulemember -function RegisterBuildEvents
Import the module when the Package Manager host initialises:
NuGet_profile.ps1
) Open the file in notepad and add the following lines
Import-Module -Name <Path to your ps module>\BuildEvents -Force
RegisterBuildEvents
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With