VS 2017 builds code redundantly when switching from msbuild console build over to IDE (the first time) - visual-studio

VS 2017 sets LastActiveSolutionConfig for a web application on its project load, thus triggering subsequent build of that project, because:
The property is set in the respective .csproj.user file, which is created, if needed.
The .csproj.user file is part of project dependencies
So by creating it, VS causes the project to be built the next time.
Imagine building it all on the command line with msbuild after cleaning up the workspace, then switching back to VS and hitting the build button. And it is building again!
So, there are these stupid auto generated CS files related to workflows, that are generated only by VS, not msbuild (TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs and friends) - our msbuild generates them on purpose to satisfy VS. Check.
Next we ensure all the Copy to Output Directory files use PreserveNewest - check.
I already forgot what else we had to do to make sure VS does not rebuild code redundantly when switching from msbuild to IDE. Now this one, which is new.
How can I prevent VS 2017 from adding this property? Is it absolutely necessary to have it?
In the mean-time, I will modify the .csproj files by adding it and see if it helps. Nobody builds Release locally at our place and it is always AnyCPU platform, so I do not care about other configurations, except Debug|AnyCPU.

This is what I would do:
Set the build verbosity to Diagnostic.
Build and look for where the msbuild file is located that generates this file:
TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs
Look for some conditions that you can alter to prevent the file from being
generated.
Set some property to alter the condition and prevent that file from being generated.

My solution is to generate the .csproj.user files if needed with the expected property. Which is incredibly annoying that one has to do it. Here is the build code that can go into your Directory.Build.Targets:
<Target Name="EnsureCSProjUserForWebApplications"
Condition="'$(IsWebApplication)' == True And !Exists('$(MSBuildProjectFullPath).user')">
<ItemGroup>
<CSProjUserContent Include="<?xml version="1.0" encoding="utf-8"?>" />
<CSProjUserContent Include="<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">" />
<CSProjUserContent Include="<PropertyGroup>" />
<CSProjUserContent Include="<LastActiveSolutionConfig>Debug|Any CPU</LastActiveSolutionConfig>" />
<CSProjUserContent Include="</PropertyGroup>" />
<CSProjUserContent Include="</Project>" />
</ItemGroup>
<WriteLinesToFile File="$(MSBuildProjectFullPath).user" Lines="#(CSProjUserContent)" ContinueOnError="true" />
</Target>
IsWebApplication is computed like this:
<IsWebApplication>$(ProjectTypeGuids.Contains('349c5851-65df-11da-9384-00065b846f21'))</IsWebApplication>
Finally, the target is part of a larger series of targets that run at the beginning and validate the csproj matches our requirements or ensure certain conditions:
<PropertyGroup>
<EnsureXyzProjectSettingsDependsOn>
AssertIISExpress;
AssertNoAssemblyInfo;
AssertDebugSymbols;
AssertLocalApplicationHostFile;
AssertImportsDFVersioning;
EnsureSharedBinLink;
AssertSharedBinOutputPath;
AssertHintPaths;
EnsureCSProjUserForWebApplications
</EnsureXyzProjectSettingsDependsOn>
</PropertyGroup>
<Target Name="EnsureXyzProjectSettings"
DependsOnTargets="$(EnsureXyzProjectSettingsDependsOn)"
BeforeTargets="BeforeBuild"
Condition="'$(SuppressStrictXyzTargetsChecks)' != true" />

Related

Visual Studio constantly triggers MSBuild Targets

I want to execute a text template before my MSBuild project in Visual Studio. I have added the following to my project file:
<Target Name="TransformOnBuild" BeforeTargets="ResolveProjectReferences">
<PropertyGroup>
<_TransformExe>$(MSBuildExtensionsPath)\..\Common7\IDE\TextTransform.exe</_TransformExe>
<_TextTransform>$(ProjectDir)AssemblyInfo.tt</_TextTransform>
<_TextTransformResult>$(ProjectDir)AssemblyInfo.cs</_TextTransformResult>
</PropertyGroup>
<Exec Command="del "$(_TextTransformResult)"" />
<Exec Command=""$(_TransformExe)" "$(_TextTransform)" -out "$(_TextTransformResult)"" />
</Target>
This simply deletes my AssemblyInfo.cs and regenerates it from AssemblyInfo.tt.
I use BeforeTargets="ResolveProjectReferences" because I need this file regenerated before any of the referenced projects get built.
Basically, this already works but I have noticed something strange: When I have this in my project file while Visual Studio is open, the AssemblyInfo.cs file constantly dissappears and then reappears. To me it looks like VS repeatedly executes my build target in the background. Of course I don't want it to behave like this. I want it to regenerate the file only when I start a build.
Is there any way to achieve my goal without generating constant CPU load and annoying file-wobbling in the explorer? Maybe a different base target than ResolveProjectReferences?
I use Visual Studio Professional 2022, Version 17.2.6
Update based on latest comments.
You could also try Condition="'$(DesignTimeBuild)' != 'true'".
Details/Background.
If you can live withit never being run inside Visual Studio, you can add this condition to the target element:
Condition="'$(BuildingInsideVisualStudio)' != 'true'"
Otherwise you can try this:
<Target Name="TransformOnBuild" BeforeTargets="ResolveProjectReferences"
Inputs="$(MSBuildProjectDirectory)AssemblyInfo.tt"
Outputs="$(MSBuildProjectDirectory)AssemblyInfo.cs">
<PropertyGroup>
<!-- ... -->
</Target>
You can learn more about Inputs/Outputs here. Basically, in this case, it means that the target will only be run, when AssemblyInfo.tt is newer than AssemblyInfo.cs.
Note that VS (for intellisence, etc.) will run targets in the background.

How to avoid that Visual Studio incremental build does not run when files outside <Compile> and <EmbeddedResource> are changed?

I have a VS2017 csharp project and the .csproj file looks like the following:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<MyItem Include="file.dat" />
</ItemGroup>
<PropertyGroup>
<PrepareResourcesDependsOn>
$(PrepareResourcesDependsOn);
MyCompileTarget
</PrepareResourcesDependsOn>
<CoreCompileDependsOn>
$(CoreCompileDependsOn);
MyCompileTarget
</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="MyCompileTarget" Inputs="#(MyItem)" Outputs="#(MyItem->'%(FileName).out')">
...
</Target>
</Project>
Where MyCompileTarget is a target that generates the file.out from file.dat (in the actual code the incremental build target and properties are in a target file automatically included via a NuGet package).
The issue is that if I change file.dat and press on Build, no target is executed at all, (but MyTarget is correctly executed with Rebuild or when running with msbuild). I would expect the MyCompileTarget to be executed so that the file.out file is updated.
The same issue occurs if I use BeforeBuild or AfterBuild instead of PrepareResourcesDependsOn etc.
It seems that Visual Studio incremental build won't start unless some file in #(Compile) or #(EmbeddedResource) is changed. Indeed, if I add the following
<EmbeddedResource>file.dat</EmbeddedResource>
the incremental build works as expected (but clearly I do not want to embeed the file.dat into the generated assembly).
Is it possible to force Visual Studio to enable incremental build if file.dat is modified, and if the corresponding generated file is older than file.dat or it does not exist?
Note: the same issue occurs using VS2015, with .NET CORE or .NET FRAMEWORK.
Also, incremental build will be triggered if I change a csharp file, and it will therefore trigger MyTask, but only if file.dat is newer than the generated file (as expected).
Thanks in advance,
Fabio.
Is it possible to force Visual Studio to enable incremental build if file.dat is modified
You can set the property DisableFastUpToDateCheck to true in the project file to disable FastUpToDateCheck for Visual Studio build manager:
<PropertyGroup>
<DisableFastUpToDateCheck>True</DisableFastUpToDateCheck>
</PropertyGroup>
Check MSDN about DisableFastUpToDateCheck:
A boolean value that applies to Visual Studio only. The Visual Studio
build manager uses a process called FastUpToDateCheck to determine
whether a project must be rebuilt to be up to date. This process is
faster than using MSBuild to determine this. Setting the
DisableFastUpToDateCheck property to true lets you bypass the Visual
Studio build manager and force it to use MSBuild to determine whether
the project is up to date
Update:
Also, we can set the UpToDateCheckInput to the item:
<UpToDateCheckInput Include="file.dat" />
Disabling the VS fast up-to-date check will make your builds much slower. Don't do it!
Instead, make sure the up-to-date check knows about the items in your project and how they relate to build. There are two kinds of item you can add to your project for this:
UpToDateCheckInput for inputs
UpToDateCheckBuilt for outputs
In your case you need the second option as there is both an input and an output. You need to ensure that if you delete the output, it is rebuilt.
<PropertyGroup>
<UpToDateCheckBuilt Original="#(MyItem)" Include="#(MyItem->'%(FileName).out')">
</PropertyGroup>
For more information, see the documentation:
https://github.com/dotnet/project-system/blob/main/docs/up-to-date-check.md

Sequentially build configurations in Visual Studio without MSBuild or plugins?

MSDN describes how to create a batch build, but does not provide a way to automate different batches (and one click solution for the GUI)
This question describes conditionally invoking a second build but doesn't appear to suffice for more than two sequential configurations
This question addresses the same situation, but again only for two configurations
In my test case, each configuration:
defines its own MACROS (which impact source code)
is applicable to multiple projects (class libraries). The projects are interdependent and require a specific build order in the context of the current configuration
I would like visual studio to build multiple configurations sequentially with a single build command.
Can child configurations be nested under a parent configuration, and be executed sequentially by visual studio when the parent configuration is built?
UPDATE : ATTEMPTED SOLUTION 1 [2016-03-11]
In response to Stijn's suggested answer I've tried the following:
Setup DotNetFramework 4.5 WinForms solution with 3 test projects and with 6 Configurations:
CORE_DEBUG
CORE_RELEASE
EXTENDED_DEBUG
EXTENDED_RELEASE
Debug
Release
The Debug Configuration must:
NOT trigger it's own configuration build (i.e. 'Debug')
must trigger the CORE_DEBUG and EXTENDED_DEBUG Configurations in sequence
I've added the following modified target to the first project's project file:
<Target Name="AfterBuild" Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
Building with the 'Debug' Configuration now, causes an EXTENDED_RELEASE build to trigger. Having a look at the solution file, I see that Visual Studio decided to automatically link 'Debug' to 'EXTENDED_RELEASE':
{4F9706AA-26A9-483C-81C4-22E301C54C89}.Debug|Any CPU.ActiveCfg = EXTENDED_RELEASE|Any CPU
{4F9706AA-26A9-483C-81C4-22E301C54C89}.Debug|Any CPU.Build.0 = EXTENDED_RELEASE|Any CPU
Removing the above two lines from the solution file doesn't help, since Visual Studio just regenerates them. In summary this now has two undesirable outcomes:
Visual Studio executes a 'Debug' build for Project1
Visual Studio then executes an 'EXTENDED_RELEASE' for Project2 and Project3
Conclusion: While this approach can work, it also (first) performs debug and release configuration builds respectively. Visual Studio
also lists all 6 Configurations in the build menu (we only want Debug
and Release to be visible, and behind the scenes Debug must trigger
CORE_DEBUG and EXTENDED_DEBUG, and Release must trigger CORE_RELEASE
and EXTENDED_RELEASE)
UPDATE : ATTEMPTED SOLUTION 2 [2016-03-16]
Moving on to a makefile project solution: I've created a makefile project as specified by stijn's answer below, and it worked perfectly!
Conclusion : This is the preferred solution in my opinion because it gives the user the most power and ability to control exactly how the build(s) must be executed and how the configurations must be handled.
The principle of the second SO question can be adjusted to build more than one configuration/platform sequentially by just invoking MsBuild multiple times. For instance:
<Target Name="AfterBuild" Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<MSBuild Projects="$(MySolution)" Properties="Configuration=Release;Platform=x86"/>
<MSBuild Projects="$(MySolution)" Properties="Configuration=Debug;Platform=x64"/>
<MSBuild Projects="$(MySolution)" Properties="Configuration=Release;Platform=x64"/>
</Target>
This can be cleaned up by using item batching, removing the condition and instead automatically determining which config is invoked and then only building the others etc but that's a bit out of scope here.
I'm not really convinced doing this in an AfterBuild target is the best way though, because then you'd need to adjust one of your 'normal' projects to also trigger a build of everything else. An alternative is to add a MakeFile Project to your solution, set up it's dependencies so that it comes last in the build order (at least if that is what you need), and set it's command line to invoke msbuild in a way similar as described above. You can even keep all logic in the same project file: set the 'Build Command Line' to
msbuild $(MsBuildThisFile) /t:CustomBuild /p:Configuration=$(Configuration);Platform=$(Platform)
so building the project will 'recurse' and make it call itself again with the same properties as called with by VS, but executing the CustomBuild target where you can then build your other projects/solutions to taste.
EDIT re: update
You're almost there, but you have to go to Configuration Manager and make sure the configurations are setup properly to begin with. From the start:
create new solution, add 3 projects
right-click solution, select Configuration Manager
in the Active solution configuration combobox select new
enter CORE_DEBUG for name, select DEBUG under Copy settings from and make sure the Create new project configurations is checked like
repeat for other configurations
for EXTENDED_RELEASE for instance, it should now look like
you probably did most of this already, but somehow Debug got assigned to EXTENDED_RELEASE somehow so that is one thing you should fix; you could do that by editing the solution manually but instead of removing lines you'd have to edit them to be correct else VS just adds them again, as you noticed
Now open first project in a text editor and near the end of the file where AfterBuild is already inserted but commented out, add
<ItemGroup>
<Configurations Condition="'$(Configuration)'=='Debug'" Include="CORE_DEBUG;EXTENDED_DEBUG" />
<Configurations Condition="'$(Configuration)'=='Release'" Include="CORE_RELEASE;EXTENDED_RELEASE" />
<Projects Include="$(SolutionDir)WindowsFormsApplication1.csproj;$(SolutionDir)WindowsFormsApplication2.csproj;$(SolutionDir)WindowsFormsApplication3.csproj" />
</ItemGroup>
<Target Name="AfterBuild" Condition="'#(Configurations)' != ''">
<Message Text="Projects=#(Projects) Configuration=%(Configurations.Identity)" />
<MSBuild Projects="#(Projects)" Targets="Build" Properties="Configuration=%(Configurations.Identity)" />
</Target>
you might need to adjust the paths to the projects. This will build CORE_DEBUG and EXTENDED_DEBUG for Debug builds, and likewise for Release builds. AfterBuild is skipped when the Configurations ItemGroup is empty, i.e. when not building Debug or Release which is exactly the point.
EDIT re: makefile
You can specify multiple commands for the makefile commandline. Click the arrow next to the 'Build Command Line' box and select '' To be sure you have everything right, Configuration Manager has to be set up to only build the makefile project for Debug/Release like:
and the makefile project's commandline looks like
Alternatively, and I'd prefer this myself, you create an msbuild file with the same content as above:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Configurations Condition="'$(Configuration)'=='Debug'" Include="CORE_DEBUG;EXTENDED_DEBUG" />
<Configurations Condition="'$(Configuration)'=='Release'" Include="CORE_RELEASE;EXTENDED_RELEASE" />
<Projects Include="$(SolutionDir)WindowsFormsApplication1.csproj;$(SolutionDir)WindowsFormsApplication2.csproj;$(SolutionDir)WindowsFormsApplication3.csproj" />
</ItemGroup>
<Target Name="Build" Condition="'#(Configurations)' != ''">
<Message Text="Projects=#(Projects) Configuration=%(Configurations.Identity)" />
<MSBuild Projects="#(Projects)" Targets="Build" Properties="Configuration=%(Configurations.Identity)" />
</Target>
</Project>
and your makefile command then invokes that file like
msbuild /path/to/msbuildfile /t:Build /p:Configuration=Debug;SolutionDir=$(SolutionDir)

Emulate Devenv/Runexit under MSBuild

I am new to MSBuild and busy automating tests of Visual Studio solutions.
I previously worked with the command line of Devenv, which provides a convenient /Runexit mode of operation. From the manual:
/Runexit (devenv.exe)
Compiles and runs the specified solution, minimizes the IDE when the solution is run,
and closes the IDE after the solution has finished running.
This is exactly the functionality that I need. I am now migrating to MSBuild. I have discovered that the project files in the Solution can be directly used for building, as the default target is precisely Build.
What can I do to handle a different target, that will have the same effect as /Runexit ? Can you help me through the maze ?
This is the most basic Target which runs a projects' output file:
<Target Name="RunTarget">
<Exec Command="$(TargetPath)" />
</Target>
For c++ unittests I use something like this; it's a property sheet so it's easy to add to any project without needing to manually modify it. It automatcially runs the output after the build so there is no need to specify an extra target and it works the same for VS and from the command line. Moreover in VS you'll get unittest errors from frameworks like Unittest++ or Catch displayed right away in the error list, so you can doubleclick them. Also the UnitTestExtraPath property can be set elsewhere just in case (e.g. on a buildserver we always want to keep the PATH clean but sometimes we do need to modify it to run built exes).
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup />
<ItemGroup />
<!--Used to be AfterTargets="AfterBuild", but that is unusable since a failing test marks the build as unsuccessful,
but in a way that VS will always try to build again. As a consequence debugging in VS is impossible since
VS will build the project before starting the debugger but building fails time and time again.-->
<Target Name="RunUnitTests" AfterTargets="FinalizeBuildStatus">
<Exec Condition="$(UnitTestExtraPath)!=''" Command="(set PATH="%PATH%";$(UnitTestExtraPath)) & $(TargetPath)" />
<Exec Condition="$(UnitTestExtraPath)==''" Command="$(TargetPath)" />
</Target>
</Project>

Determine whether it's a build or rebuild in .cmd script called in prelink step inside Visual Studio

How can a .cmd script run from within a Visual Studio (2005, 2008, 2010, 2012 and 2013 respectively) project's pre-link stage determine whether this is a full rebuild (Build.RebuildSolution/Build.RebuildOnlyProject) or "ordinary" build (Build.BuildSolution/Build.BuildOnlyProject)?
This is an external script (LuaJIT, if you must know) and I don't want to rebuild the library every single build of the project. Instead I'd like to limit the complete rebuild to situations where I choose exactly that option.
How can a .cmd script run from within a Visual Studio (2005, 2008, 2010, 2012 and 2013 respectively) project's pre-link stage determine whether this is a full rebuild ... or "ordinary" build ... ?
I do not know if the exact thing that you are asking can be done - perhaps someone else knows how to do it. I will, however, suggest an alternate approach.
My approach is to remove the build of the Lua library from the pre-link step to a separate Visual Studio NMake project. If you create an NMake project, you will be able to know which type of build (build or rebuild) is occurring.
Note that later versions of Visual Studio simply refer to the project type as "Make". For discussion purposes here, I will refer to the project type as "NMake". I believe this is just a naming difference, and that the underlying build project remains the same between the two versions.
As a simple test, I created two Visual Studio applications: 1) an NMake project that calls a batch file to create a static library, and 2) a console application that consumes the library from step 1.
The NMake Project
In Visual Studio, if you create a new NMake project, you will see a dialog that allows you to provide MS-DOS commands:
As you can see, there are commands for: Build, Clean, Rebuild, and others. I don't have a screen shot of the above dialog with my commands, but here is my NMake project's properties:
My Build command just checks for the existence of the output file (lua.lib). If it does not exist, then it calls the rebuild.bat batch file. My Rebuild command always calls the batch file. My Clean command just deletes the output. I am not really sure what the Output command is used for, but I just filled in the path to the build output (lua.lib).
Now if you do a build, the lua.lib file will only be created if it is not there. If it is already there, nothing is done. If you do a rebuild, then a new lua.lib file is created.
The Console Application
In my console application, I added a reference to the NMake project - this way the NMake project is built prior to the console application. Here is the console application's reference page:
I also added the lua.lib file as an input during the application's link stage:
When the console application is built (during a build), it will build the NMake project if needed, and use the output (lua.lib) during the linker stage. When the console application is rebuilt (during a rebuild), it will also rebuild the NMake project.
Other Thoughts
My screen shots above only show the debug version of the properties. Your projects will have to account for the release version. There probably is a VS macro to handle this, but I am not sure since it has been ages since I've done anything with C/ C++.
In my testing above I use a single build batch file for both the build and rebuild. Obviously, you could do the same or you could use different batch files.
It may be a bit of a hack, but in .csproj file there are sections
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
You can set an variable from BeforeBuild and retrieve it from cmd script. Later on reset this variable in AfterBuild and you should be good to go.
Ok, this is going to be a long one.
First of all - do not take my code 'as is' - it is terrible one with lots of hacks, I had no idea msbuild is so broken by default (it seems at work I have access to waaaay more commands that make life easier). And another thing - it seems vcxproj is broken at some poin - I was not able to integrate the way I wanted with only BeforeRebuild and AfterRebuild targets - I had to redefine hole Rebuild target (it is located in C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets)
So, the idea is the following: when a Rebuild is happening we create an anchor. Then, during PreLink stage we execute cmd which is able to use created anchor. If the anchor is in place - we deal with Rebuild, if there is no anchor - it is a simple Build. After Rebuild is done - we delete the anchor.
modifications in vcxproj file:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
....
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
....
<PreLinkEventUseInBuild>true</PreLinkEventUseInBuild>
....
</PropertyGroup>
....
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
.....
<PreLinkEvent>
<Command>D:\PreLink\b.cmd</Command>
</PreLinkEvent>
.....
</ItemDefinitionGroup>
.....
<Target Name="BeforeRebuild">
<Exec Command="echo 2 > D:\PreLink\2.txt" />
</Target>
<Target Name="AfterRebuild">
<Exec Command="del D:\PreLink\2.txt" />
</Target>
<!-- This was copied from MS file -->
<PropertyGroup>
<_ProjectDefaultTargets Condition="'$(MSBuildProjectDefaultTargets)' != ''">$(MSBuildProjectDefaultTargets)</_ProjectDefaultTargets>
<_ProjectDefaultTargets Condition="'$(MSBuildProjectDefaultTargets)' == ''">Build</_ProjectDefaultTargets>
<RebuildDependsOn>
BeforeRebuild;
Clean;
$(_ProjectDefaultTargets);
AfterRebuild;
</RebuildDependsOn>
<RebuildDependsOn Condition=" '$(MSBuildProjectDefaultTargets)' == 'Rebuild' " >
BeforeRebuild;
Clean;
Build;
AfterRebuild;
</RebuildDependsOn>
</PropertyGroup>
<Target
Name="Rebuild"
Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
DependsOnTargets="$(RebuildDependsOn)"
Returns="$(TargetPath)"/>
<!-- End of copy -->
</Project>
And the cmd looks like this:
if exist 2.txt (
echo Rebuild818181
) else (
echo Build12312312
)
The output from Output window:
1>Task "Exec" (TaskId:41)
1> Task Parameter:Command=D:\PreLink\b.cmd
1> :VCEnd (TaskId:41)
1> Build12312312 (TaskId:41)
Things to improve:
Use normal variables instead of external file (it seems MsBuild extension pack should do it)
Probably find a way to override only BeforeRebuild and AfterRebuild instead of the hole Rebuild part
It is much easier. Just add the following target to your build file or visual Studio Project
<Target Name="AfterRebuild">
<Message Text="AFTER REBUILD" Importance="High" />
<!--
Do whatever Needs to be done on Rebuild - as the message shows in VS Output
window it is only executed when an explicit rebuild is triggered
-->
</Target>
If you want a two step solution use this as a template:
<PropertyGroup>
<IsRebuild>false</IsRebuild>
</PropertyGroup>
<Target Name="BeforeRebuild">
<Message Text="BEFORE REBUILD" Importance="High" />
<PropertyGroup>
<IsRebuild>true</IsRebuild>
</PropertyGroup>
</Target>
<Target Name="BeforeBuild">
<Message Text="BEFORE BUILD: IsRebuild: $(IsRebuild)" Importance="High" />
</Target>

Resources