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:
- Installed Visual Studio Development Environment
- Create a project of type Console Application named MSBuildExample
- Open the project folder and find the MSBuildExample.csproj file there
- Open MSBuildExample.csproj file in Notepad or another editor.
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.
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.

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

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:

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:
- $ (MSBuildToolsPath) - points to the path to the MSBuild folder. For example, C: \ Program Files (x86) \ MSBuild \ 14.0 \ Bin. This macro is used with a slash when combining the path. For example, $ (MSBuildToolsPath) \ Microsoft.Web.Publishing.Tasks.dll. Otherwise, it may incorrectly form the path and give an error that the file was not found.
- $ (OutputPath) - relative path to the output folder. For example, bin \ Stage. Use this macro with a slash, for example, $ (OutputPath) \ $ (TargetFileName) .config.
- $ (TargetFileName) - the name of the output file along with the extension. For example, MSBuildExample.exe. The extension and format of the output file name may differ from different types of projects. Using this macro, you can safely determine what will be the name of the config file. It can be useful for the transformations of configs.
- $ (Configuration) - the name of the current configuration. For example, Release, Debug
- $ (IntermediateOutputPath) - path to the obj folder. For example, obj \ Stage.
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 )