Running a target before the CoreBuild? - visual-studio

I'm adding a custom .tt template generation target to my project to run before CoreBuild, and there appear to be 2 ways of doing it:
<Project...>
<Target Name="TransformOnBuild" AfterTargets="BeforeBuild">
</Project>
and
<Project...>
<Target Name="TransformOnBuild" BeforeTargets="CoreBuild">
</Project>
If my target should run before my project is built, as the project relies on it, would it be better for me to use the latter? I've seen the former used to do things like generate text templates, but it seems like an unreliable way to do it because it might get run after CoreBuild, which is too late. Or is there some reason why AfterTargets="BeforeBuild" is still guaranteed to run before the core build?
I've also seen BeforeTargets="BeforeBuild" which will build even earlier. Is this a better place to put a `.tt text generation target?

Building on #stjin's answer, a good solution seems to be using
BeforeTargets="CoreCompile" DependsOnTargets="PrepareForBuild"
which is what the .net sdk (new-style csproj for .net core / standard projects) is doing for automatic AssemblyInfo.cs generation.
It uses the following comment to explain why:
Note that this must run before every invocation of CoreCompile to
ensure that all compiler runs see the generated assembly info. There
is at least one scenario involving Xaml where CoreCompile is invoked
without other potential hooks such as Compile or CoreBuild, etc., so
we hook directly on to CoreCompile. Furthermore, we must run after
PrepareForBuild to ensure that the intermediate directory has been
created.
Note that the "intermediate directory" (obj/[TargetFramework] in this case) is where the output .cs file is placed in this case which may also be what you might want to do.

Update 2
Based on the documentation from Microsoft:
This is the Target Build Order
InitialTargets targets are run.
Targets specified on the command line by the /target switch are run. If you specify no targets on the command line, then the DefaultTargets targets are run. If neither is present, then the first target encountered is run.
The Condition attribute of the target is evaluated. If the Condition attribute is present and evaluates to false, the target isn't executed and has no further effect on the build.
Before a target is executed, its DependsOnTargets targets are run.
Before a target is executed, any target that lists it in a BeforeTargets attribute is run.
Before a target is executed, its Inputs attribute and Outputs attribute are compared. If MSBuild determines that any output files are out of date with respect to the corresponding input file or files, then MSBuild executes the target. Otherwise, MSBuild skips the target.
After a target is executed or skipped, any target that lists it in an AfterTargets attribute is run.
These are the answers to your questions:
If my target should run before my project is built, as the project relies on it, would it be better for me to use the latter?
No, because all the dependencies of CoreBuild will be executed before your template generation target and that will be too late.
Or is there some reason why AfterTargets="BeforeBuild" is still guaranteed to run before the core build?
AfterTargets="BeforeBuild" guarantees that your target will be executed on time because it will be executed before all the CoreBuild dependencies.
I've also seen BeforeTargets="BeforeBuild" which will build even earlier. Is this a better place to put a `.tt text generation target?
In both cases AfterTargets="BeforeBuild" or BeforeTargets="BeforeBuild" your target will be executed before all the CoreBuild dependencies, however in both cases you still have the risk to affect the results of your template generation target depending on what you have to execute in BeforeBuild. If you have this under control, you can use any of these options safely.
Running a target before the CoreBuild?
> there appear to be 2 ways of doing it.
There are more options to achieve this. Please review below.
You should use the specific built in targets (BeforeBuild or AfterBuild) for this purpose. Thisis the mechanism provided by Microsoft to safely extend the build process when using projects that depends on Microsoft.Common.targets
If you have only one target to run before CoreBuild, you can do this:
<Target Name="BeforeBuild">
<!-- add your tasks here -->
</Target>
If you have more than one target to run before CoreBuild, you can define a property with all the targets that needs to be called in the required order of execution:
<PropertyGroup>
<BeforeBuildDependsOn>
CustomTarget1;
CustomTarget2;
CustomTarget3
</BeforeBuildDependsOn>
</PropertyGroup>
<Target Name="BeforeBuild" DependsOnTargets="$(BeforeBuildDependsOn)"/>
UPDATE:
Based on the fragment provided by #stijn:
AfterTargets="BeforeBuild" will insert/execute the custom target like this: (Depends on BeforeBuild
<BuildDependsOn>
BeforeBuild;
|-> Custom Target
CoreBuild;
AfterBuild
</BuildDependsOn>
BeforeTargets="CoreBuild" will insert/execute the custom like this (Depends on CoreBuild):
<BuildDependsOn>
BeforeBuild;
|-> Custom Target
CoreBuild;
AfterBuild
</BuildDependsOn>
So the "template generation target" will be executed in the same place (Between BeforeBuild and CoreBuild, but depending on different targets, that's why the apropriate Target to be used should be BeforeBuild inline or with dependencies.
Now regarding the 3rd party issue comment, the BeforeBuild/AfterBuild targets are intended for final users, the third party providers should implement their scripts without affect the basic workflow. These are some of the options a 3rd party should use to avoid breaking the regular flow:
Considering this as the base:
<PropertyGroup>
<BuildDependsOn>
BeforeBuild;
CoreBuild;
AfterBuild
</BuildDependsOn>
</PropertyGroup>
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
Option 1: This will inject your custom 3rd party script before BeforeBuild without affect the default BuildDependsOn sequence, that still allows the final user to use the BeforeBuild target without.
<PropertyGroup>
<BuildDependsOn>
MyCustomThirdParty;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
<PropertyGroup>
<MyCustomThirdPartyDependsOn>
BeforeMyCustomThirdParty;
CustomStep1;
CustomStep2;
CustomStep1;
AfterMyCustomThirdParty
</MyCustomThirdPartyDependsOn>
</PropertyGroup>
<Target Name="MyCustomThirdParty" DependsOnTargets="$(MyCustomThirdPartyDependsOn)"/>
Option 2: If the 3rd party script needs to be executed after the BeforeBuild target, this can be done like this:
<PropertyGroup>
<BuildDependsOn>
BeforeBuild;
MyCustomThirdParty;
CoreBuild;
AfterBuild
</BuildDependsOn>
</PropertyGroup>
NOTE: In order for this to work properly, you must add the PropertyGroup and targets AFTER the Microsoft.CSharp.targets import.
This way you will be able to use multiple 3rd party scripts respecting the general workflow.
You can obviously use a combination of these options depending on the situation. But you should follow these general rules:
Respect the default workflow (BeforeBuild, CoreBuild, AfterBuild).
Include Before/After targets for your 3rd party script to allow the final user to inject anything before or after the 3rd party script execution.
These should be considered when you are using the default visual studio generated build scripts (Projects like .csproj, .vbproj, etc). If you are implementing your own scripts for other languajes or purposes, you can use BeforeTargets and AfterTargets wherever you want, but why don't you follow the good practices based on the existing scripts?

From Microsoft.Common.CurrentVersion.targets, the Build target is basically:
<BuildDependsOn>
BeforeBuild;
CoreBuild;
AfterBuild
</BuildDependsOn>
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
<PropertyGroup>
<CoreBuildDependsOn>
PrepareForBuild;
PreBuildEvent;
...
Compile;
...
PostBuildEvent
</CoreBuildDependsOn>
</PropertyGroup>
<Target Name="CoreBuild" DependsOnTargets="$(CoreBuildDependsOn)">
So using BeforeTargets="CoreBuild" will run before CoreBuild indeed, but that is after all it's dependent targets ran, so after all actual build steps. That's usually not what you want, instead if you want to run something before compilation etc, use BeforeTargets="PrepareForBuild" or indeed AfterTargets="BeforeBuild" or even BeforeTargets="BeforeBuild".

Related

How to run MSBuild Target BEFORE compilation but only when compilation will happen

I have a C# library project that hase some dependencies that a created in "BeforeBuild" with PowerShell.
MSBuild seems to execute target "BeforeBuild" each time, also if the library project itself does not need to build.
I'd like to configure the build process to only run the PowerShell script if the library needs to (re)build.
Is there a way to do this?
I tried to use targets "BeforeBuild" and "BeforeCompile". But it seems to execute every time.
You can't really predict whether compilation will happen, as you can't know what targets may or may not do after your target runs. But you may be able to predict whether your task needs to run based on the current state of the build directory. This is how you'd implement Incremental building in MsBuild, which sounds like what you're really after.
All targets have an optional Inputs and Outputs parameter. These two are used to calculate whether any of the inputs have changed since the last build and based on that MsBuild decides whether to run your target or whether it can skip it.
This requires some knowledge, based on the current state of the ItemGroups and Properties in your build to predict the outcomes of your target.
When Inputs and Outputs aren't specified, MsBuild can't figure out whether or not your target will impact anything, Thus it will run it to be sure.
The docs provide a good explanation on how to enable incremental building of your custom targets.
To predict the outputs you'd need to write a transformation of some kind. Or, you'd write out some kind of marker file (like codeanalysis.lastsucceeded) and use that to compare against. The incremental build will ONLY look at the Last Changed Date of all inputs and compares those against all outputs. So you need to express the need to run or not as an operation on that data.
Basic structure:
<target name="RunMyPowerShell"
beforetargets="Compile" Inputs="#(Content)"
Outputs"#(Content->'%(Filename).translated.content')">
...
<exec Command="powershell .\mypsfile.ps1" />
...
</targets>
As an alternative for BeforeTargets="Build", you can overwrite the BuildDependsOn property:
<PropertyGroup>
<CompileDependsOn>
RunMyPowerShell;
$(CompileDependsOn)
</CompileDependsOn>
</PropertyGroup>
If you haven't dug into this area before, all the intricacies of MsBuild can be overwhelming and to make this work, you need to master quite a few things. It may als require changes to what the PowerShell script does to make its output predictable enough for MsBuild to do its magic.
Alternatively, you can add a condition on your target based on some expression, but given the way build files are parsed, computed and executed, it can be pretty hard to figure out how to do that correctly.
There can be massive performance gains if you get these things right.
See also:
BeforeTargets / AfterTargets
Extend the build process
Build Incrementally
Incremental Builds
Item Transformations
MSBuild seems to execute target "BeforeBuild" each time, also as if the library project itself does not need to build.
What do you mean the msbuild execute the target each time also if the library project itself does not need to build? As I know, BeforeBuild is one necessary target during the build process, so if you build one project, it will always call this target. If the library project doesn't need to build, what's the reason you call msbuild to build it? More details could be better :)
I'd like to configure the build process to only run the PowerShell
script if the library needs to (re)build.
Is there a way to do this?
I tried to use targets "BeforeBuild" and "BeforeCompile". But it seems
to execute every time.
BeforeBuild and BeforeCompile are predefined targets in msbuild system. You can overwrite them to customize your build steps, but you can't avoid running them during build process. So they'll execute every time if the build starts, it's expected behavior by design.
According to your description, you may overwrite these targets and run the power shell script in them. That's why in your machine it will always call the ps script.
Here's a workaround if you build the project by msbuild.exe instead of VS IDE:
Assuming you have overwritten BeforeTarget in your project file. You can add a condition to determine if to run the custom BeforeTarget(call ps script) or original BeforeTarget.
Define a property named RunPS, and add a condition to the BeforeTarget you use in this way:
<PropertyGroup>
<RunPS>false</RunPS>
</PropertyGroup>
<Target Name="BeforeBuild" Condition="$(RunPS)=='true'">
<!--Run the ps script here-->
</Target>
Then you can control if use the default BeforeTarget or custom one by setting the value of RunPS.
msbuild xx.csproj /p:RunPS=true to run ps script during build process, msbuild xx.csproj to run original BeforeTarget
And if you build the project in VS IDE,you can create new Configuration(CustomDebug) copied from Debug or Release, and set Condition="$(Configuration)=='CustomDebug'" to the target.
Update:
For the workaround which works in VS and command-line:
See this document, in VS we can create new configurations easily by copying settings from Debug and Release Configurations.
1.In my opinion, for your library project, you have normal Debug and Release Configurations, you can follow tips in the document to create corresponding custom configurations. Copy the setting from Debug and create a new configuration named PSDebug, copy the setting from Release and create a new configuration named PSRelease.
2.Then edit the xx.csproj, add a condition in this format:
<Target Name="BeforeBuild" Condition="'$(Configuration)'=='PSDebug' OR '$(Configuration)'=='PSRelease'">
<!--Run the ps script here-->
<Message Text="showsth" Importance="high" />
</Target>
Then in VS IDE, if you build with normal Configuration Debug or Release, it will use default BeforeBuild target. And only when you build with custom PSDebug or PSRelease, it will call your custom BeforeBuild target.In VS, you can easily switch between those configurations by this box:
And for command-line, it also works. If you use command like msbuild xx.csproj /p:Configuration=Debug, it use predefined BeforeBuild target to do this, and if you use command like msbuild xx.csproj /p:Configuration=PSDebug, it will call your custom target and run the PS script.

Visual Studio Optimizing Build Speed Only Locally

The scenario is this one:
We need to reduce the time of building a solution that contains several projects. We have the constraint that we cannot consolidate projects, thus we are having around 50 projects. The build time at this moment is around 3 minutes. A quick test by setting manually all project references property CopyLocal to False and changing the output directory to a central one improved the building performance by more than 50%. However, the problem is by doing so, when we deploy or run out test on CI, DLLs are missing (I suspect it builds by using the main project and not all projects of the solution).
I thought that I can have 2 sets of build directives. One when developing that will set CopyLocal to false and output into a single directory all DLL, and one when deploying in the CI and Azure website (which preserve the normal DLL location).
I have read in previous post and here that is it possible to the CopyLocal with MsBuild by using a target file (not sure yet about the output directory). Thus, I could have this one use the targets file on the local machine and not when deploying.
My question is how can I have Visual Studio's Build action to use a specific targets file when developing and not when deploying with the IDE to Azure or the CI environment?
You can use a itemgroup with a condition to optionally override the output directory. Or use it to remove the copy-local flag from your Reference items. The same trick works for ProjectReferences.
See:
https://stackoverflow.com/a/1684045/736079
Then make this group conditional using a number of flags:
IsDesktopBuild Is true for a build that's running outside of a build server
BuildingInsideVisualStudio Is true for a build that's running inside VS
See:
https://stackoverflow.com/a/20402204/736079
Putting it all together:
<Target Name="BeforeBuild" Condition="'$(IsDesktopBuild)' != 'true'">
<ItemGroup>
<ProjectReferenceNew Include="#(ProjectReference)">
<Private>False</Private>
</ProjectReferenceNew>
<ProjectReference Remove="#(ProjectReference)"/>
<ProjectReference Include="#(ProjectReferenceNew)"/>
</ItemGroup>
<ItemGroup>
<ReferenceNew Include="#(Reference)">
<Private>False</Private>
</ReferenceNew>
<Reference Remove="#(Reference)"/>
<Reference Include="#(ReferenceNew)"/>
</ItemGroup>
</Target>
To improve performance of this translation, you'll want to specify the Input and Output parameters of the Target:
<Target Name="BeforeBuild" Condition="'$(IsDesktopBuild)' != 'true'"
Inputs="#(Reference);#(ProjectReference)"
Outputs="#(Reference);#(ProjectReference)"
>
....
</Target>

inject version to DLLs during build process

this is my situation:
I have VS2010 solution with X projects included.
Wix project that can create msi from all compiled artifacts.
I have build machine \ Jenkins that first compile (MSBuild .Net 4) all the solution, then compile the wix to package it to msi.
What\how can I inject to all artifacts\dlls the number of the product (e.g 11.2.0.4789) - as simple as possible?
Is there and command line arguments that can be passed while compiling the solution?
There are tools, such as several extensions for MSBuild, that do version stamping but each assumes a particular workflow. You might find one that works for you but a DIY method would help you evaluate them, even if it isn't your final solution.
You can add a property to the MSBuild command-line like this:
msbuild /p:VersionStamp=11.2.0.4789
Note: I assume you are going to parameterize the Jenkins build in some way or generate the number during a preceding build step. Here is a simulation of that:
echo 11.2.0.4789 >version.txt
set /p version=reading from pipe <version.txt
msbuild /p:VersionStamp=%version%
Now, the work is in getting each project to use it. That would depend on the project type and where you want VersionStamp to appear.
For a csproj, you might want to use it as the AssemblyVersion. The simplest way is to move the attribute to a cs file by itself and rewrite it every time. I would leave a comment in AssemblyInfo.cs as a clue to where it now comes from. You can include the cs file in your project either dynamically or permanently. I prefer dynamically since it is effectively an intermediate file for the build. So, in your .csproj add the following in a text editor (e.g. Visual Studio. Unload and Edit project):
<Target Name="BeforeBuild">
<PropertyGroup>
<AssemblyVersionPath>$(IntermediateOutputDir)AssemblyVersion.cs</AssemblyVersionPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(AssemblyVersionPath)" />
</ItemGroup>
<WriteLinesToFile
File='$(AssemblyVersionPath)'
Overwrite="true"
Condition="'$(ProductVersion)' != ''"
Lines='using System.Reflection%3b;
[assembly: AssemblyVersion("$(VersionStamp)")]' />
</Target>
This is sufficient but a more thorough solution would include adding the file to a list so it is cleaned with other files and only writing the file if the version changed to prevent unnecessary rebuilds, etc.
Use a similar technique for other project types.

How does Visual Studio decide when and how to rebuild IntelliSense?

I'm trying to write a code generation tool. For this tool it's important that the generated code is available prior to building (i.e., for IntelliSense). I know Visual Studio will at least partially evaluate the project build plan automatically to generate IntelliSense, but I can't find much information on the details.
As a simpler example, let's say I want to take all items with build action None and compile them. I have a project like this:
<Project [...]>
[...]
<Compile Include="Foo.cs" />
<None Include="Bar.cs" />
</Project>
One way to get Bar.cs to compile is to add the following to the project:
<PropertyGroup>
<CoreCompileDependsOn>
$(CoreCompileDependsOn);IndirectCompile
</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="IndirectCompile">
<CreateItem Include="#(None)">
<Output ItemName="Compile" TaskParameter="Include" />
</CreateItem>
</Target>
If I do it this way, Visual Studio acts basically the same as if Bar.cs had the Compile action to begin with. IntelliSense is fully available; if I make a change in Bar.cs it's reflected immediately (well, as immediate as the background operation normally is) in IntelliSense when I'm editing Foo.cs, and so on.
However, say instead of directly compiling the None entry, I want to copy it to the obj directory and then compile it from there. I can do this by changing the IndirectCompile target to this:
<Target Name="IndirectCompile"
Inputs="#(None)"
Outputs="#(None->'$(IntermediateOutputPath)%(FileName).g.cs')"
>
<Copy SourceFiles="#(None)"
DestinationFiles="#(None->'$(IntermediateOutputPath)%(FileName).g.cs')"
>
<Output TaskParameter="DestinationFiles" ItemName="Compile" />
</Copy>
</Target>
Doing this causes IntelliSense to stop updating. The task works on build, dependency analysis and incremental building work, Visual Studio just stops automatically running it when an input file is saved.
So, that leads to the title question: How does Visual Studio choose to run targets or not for IntelliSense? The only official documentation I've found has been this, specifically the "Design-Time IntelliSense" section. I'm pretty sure my code meets all those criteria. What am I missing?
After a few days of experimenting and poking around in the debugger I think I have found the answer, and unfortunately that answer is that this is not possible (at least not in a clearly supported way -- I'm sure there are ways to trick the system).
When a project is loaded, and when the project itself changes (files added/removed, build actions changed, etc), the IntelliSense build is executed (csproj.dll!CLangCompiler::RunIntellisenseBuild). This build will run tasks up to and including the Csc task. Csc will not execute normally, but instead just feed its inputs back into its host (Visual Studio).
From this point on, Visual Studio keeps track of the files that were given as Sources to the Csc task. It will monitor those files for changes, and when they change, update IntelliSense. So in my example, if I manually edit Bar.g.cs those changes will be picked up. But the build tasks themselves will not be run again until the project changes or a build is explicitly requested.
So, that's disappointing, but not surprising, I guess. It also explains something else I had always wondered about -- XAML files with a code-behind tend to have a Custom Tool action of MSBuild:Compile, presumably for exactly this reason.
I'm going to mark this as the answer, but I'd love to be told I'm wrong and that I missed something.

msbuild specific projects configurations. an easy solution?

I have a solution file K.sln with many projects in it. Using vs2010.
I need to compile only three of those C++ projects, let's say X, Y, Z, T
but only for some configurations.
How do I do that?
More in detail, I need to
build X in "Debug" configuration only
clean Y in "Release" only
rebuild T in "Static Debug" only
re-link Z in "Release Optim" ony (I can live without knowing this)
Is there a way to do it from a single batch file or a command line, or even better creating a simple appropriate msbuild project file? or both way?
Independently from how dependencies are set inside the solution file.
Someone can provide a text of the msbuild project for such simple task?
Is it possible to run these task in parallel?
Lost already enough time looking around and try to implement it, can someone help me? Thanks.
You can setup your msbuild.xml file to do all the operations that you want.
You will need:
Debug or Release configuration to be able to set the target builds you need.
Passing parameters to MSBuild to specify specific targets, like running clean for project "Y" only
Once all the targets and conditionals set you would do something like this:
<Target Name="MakeMyStuff">
<CallTarget Targets="DebugX" />
<CallTarget Targets="ReleaseY" />
<CallTarget Targets="StaticDebugT" />
<CallTarget Targets="ReleaseOptimZ" />
</Target>
After you need to run a command like:
msbuild msbuild.xml /t:MakeMyStuff (/p:BuildCmd=Clean) if you want to add params
or just one specific
msbuild msbuild.xml /t:DebugX
Good luck

Resources