Extending the build process with MSBuild

The purpose of the article is to tell a little about MSBuild, to show what are the targets and tasks in MSBuild, to teach how to work with the .csproj file, to give useful links. If you have a more appropriate title for the article, I will be glad to discuss in the comments.

Menu



Basic Concepts ( Menu )


MSBuild is designed in such a way that the assembly of the project is divided into several stages.

Target is a certain stage (event) that occurs during the assembly of the project. You can use standard targets, or define your own.

Task is some task that can be executed at a certain stage. You can use the standard task or create your own.

Quote from the documentation of the target ( https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-targets ):
The Targets group of tasks together.
For example, one target output line
compiles the inputs for the project directory.

MSBuild assembly life cycle ( Menu )


For MSBuild, Microsoft has defined a number of standard targets (in the files Microsoft.Common.targets, Microsoft.CSharp.targets, etc.). A great number of various targets have been defined, but in this article we will not dwell on this in detail. Some standard targets (ordered):

Target list (spoiler)
  • BeforeRebuild
  • Clean
  • BeforeBuild
  • BuildOnlySettings
  • PrepareForBuild
  • PreBuildEvent
  • ResolveReferences
  • PrepareResources
  • ResolveKeySource
  • Compile
  • UnmanagedUnregistration
  • GenerateSerializationAssemblies
  • CreateSatelliteAssemblies
  • GenerateManifests
  • GetTargetPath
  • PrepareForRun
  • UnmanagedRegistration
  • IncrementalClean
  • Postbuildevent
  • Afterbuild
  • AfterRebuild


Targets BeforeBuild and AfterBuild are specifically designed for overriding and can be used. I do not recommend using the remaining targets from the list so that nothing breaks .

For a more detailed view of the list of targets, you can use the / pp: option. Thanks to this parameter, a file will be generated in which all imports will be included (including .targets files). You can find a lot of targets and variables in it (thanks to aikixd for the hint).


Preparing the environment for examples ( Menu )


For examples you need:


In all examples of this article, you will need to edit the MSBuildExample.csproj file. Each example involves removing the code from the previous example and adding a new one. The code must be added to the end of the .csproj file to the last line containing the closing Project tag.

image

Attention! The case is important in the .csproj file .
To run the sample, you must run build in the Visual Studio development environment. For some examples, you will need to choose a solution configuration.

image

The result will be displayed in the Output window in Visual Studio (below). If not, then open it through the menu items View => Output.

image


Targets in MSBuild ( Menu )


For examples we will use Task Message , which will display information in the Output window in Visual Studio. As mentioned earlier, there are standard BeforeBuild and AfterBuild targets, we will use them. Read about the preparation in the section Preparing the environment for examples .

Example of using target (spoiler)
Example code:

<Target Name="AfterBuild"> <Message Text="AfterBuild event" Importance="high"></Message> </Target> <Target Name="BeforeBuild"> <Message Text="BeforeBuild event" Importance="high"></Message> </Target> 

Result of execution (more than excluded):
...
BeforeBuild event
...
AfterBuild event
...


As you can see, the task Message was executed, which displayed the text we specified at the time of BeforeBuild and AfterBuild in the Output window in Visual Studio.
When defining a target with the same name, it is overwritten!

Example of rewriting target (spoiler)
Example code:

 <Target Name="BeforeBuild"> <Message Text="First message" Importance="high"></Message> </Target> <Target Name="BeforeBuild"> <Message Text="Second message" Importance="high"></Message> </Target> 

Result of execution (more than excluded):
...
Second message
...


Only the second message was displayed, because they used the target with the same name and it was overwritten by the second value.

Creating your own MSBuild target ( Menu )


If BeforeBuild and AfterBuild are not enough, or if the task is to be performed at a different stage of the build life cycle, then you can define your own target. For these purposes there are BeforeTargets and AfterTargets parameters.

An example of determining your own target (spoiler)
Example code:

  <Target Name="BeforeBuild"> <Message Text="BeforeBuild event" Importance="high"></Message> </Target> <Target Name="MyCustomBeforeTarget" BeforeTargets="BeforeBuild"> <Message Text="MyCustomBeforeTarget event" Importance="high"></Message> </Target> <Target Name="MyCustomAfterTarget" AfterTargets="BeforeBuild"> <Message Text="MyCustomAfterTarget event" Importance="high"></Message> </Target> 

Result of execution (more than excluded):
...
MyCustomBeforeTarget event
BeforeBuild event
MyCustomAfterTarget event
...


It was defined two own target - MyCustomBeforeTarget and MyCustomAfterTarget.
The MyCustomBeforeTarget target is executed before the BeforeBuild target, because we specified:

 BeforeTargets="BeforeBuild" 

MyCustomAfterTarget target is executed after BeforeBuild target, because we specified:

 AfterTargets="BeforeBuild" 

MSBuild Tasks ( Menu )


This article does not cover how to write your own tasks, but before you write a task, review the list of tasks provided by Microsoft .

Consider a few examples of using task and macros.

Condition parameter (spoiler)
Condition parameter is present for all tasks. Quote from docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-task-reference :
A Boolean expression that the engine uses to determine whether this task will be executed.
Example code:

 <Target Name="BeforeBuild"> <Message Text="Current configuration is Debug" Condition="'$(Configuration)' == 'Debug'" Importance="high"></Message> <Message Text="Current configuration is Release" Condition="'$(Configuration)' == 'Release'" Importance="high"></Message> </Target> 

If the solution configuration Debug is selected, the result will look like this (the extra is excluded):
...
Current configuration is Debug
...
If the solution configuration Release is selected, the result will look like this (the extra is excluded):
...
Current configuration is Release
...
Information about the $ (Configuration) macro and other macros can be found in the section variables and macros in .csproj .

Information on the syntax of conditions can be found there https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions

Variable definition in csproj (spoiler)
In the example above, we have text that can be unified. To eliminate duplication, we will move the message text into a separate variable.

Example code:

 <PropertyGroup> <MessageText>Current configuration is $(Configuration)</MessageText> </PropertyGroup> <Target Name="BeforeBuild"> <Message Text="$(MessageText)" Condition="'$(Configuration)' == 'Debug'" Importance="high"></Message> <Message Text="$(MessageText)" Condition="'$(Configuration)' == 'Release'" Importance="high"></Message> </Target> 

To define your own variable, use the PropertyGroup element.


Check for file existence, error output (spoiler)
In this example, we'll do a task that checks whether the App.Debug.config file has been created. If it is not created, then we give an error. If an error occurs, the build will be stopped and the error will be displayed as a compilation error in the Error List window.
We use for this task Error and the Condition parameter already familiar to us.

Example code:

 <Target Name="BeforeBuild"> <Error Condition="!Exists('App.Debug.config')" Text="File App.Debug.config not found"></Error> </Target> 

Result:
image

The Exists condition uses the relative path from the folder where the .csproj file is located. To access the folder above the current, use '../'. If you need to access a subfolder, use the format '[DirectoryName] /App.Debug.config'.

Copying files (spoiler)
In this example, we will use Task Copy . Using the task, copy the App.config file to the bin / [Configuration] / Config folder into two App.config and App.test.config files.

Example code:

 <Target Name="BeforeBuild"> <Copy SourceFiles="App.config;App.config" DestinationFiles="$(OutputPath)/Test/App.config;$(OutputPath)/Test/App.test.config"></Copy> </Target> 

The SourceFiles property is an array of files to download. Specify without quotes, separated by semicolons.

The DestinationFiles property is an array of files to copy files to. Specify without quotes, separated by semicolons.

Read more about the $ (OutputPath) macro in the variables and macros section of .csproj .

Variables and macros in .csproj ( Menu )


A number of standard macros can be used in the .csproj file, a list of them can be found here https://msdn.microsoft.com/en-us/library/c02as0cs.aspx and here https://msdn.microsoft.com/en-us/ library / bb629394.aspx . Consider some useful macros:


To define your own parameters use PropertyGroup . An example of defining your own variable can be found in the task section in MSBuild .

Links ( Menu )


Source: https://habr.com/ru/post/414969/


All Articles