MSBuild Build and Publish pipelines
The Build pipeline
The newly created .csproj file contains the following import near the end of the file:
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
This file then imports Microsoft.CSharp.CurrentVersion.targets and it then imports Microsoft.Common.targets and it then imports Microsoft.Common.CurrentVersion.targets. On my local PC all files are located here: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin. If you use a different version of VisualStudio, BuildTools or project type than me, it can load different files.
The Microsoft.Common.CurrentVersion.targets contains a definition of all targets and properties related to the Build and Publish process. The most critical target is the Build
target. Its definition looks like this:
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)">
The DependsOnTargets
attribute says that Build
target depends on targets defined by BuildDependsOn
property. The BuildDependsOn
looks like this:
<PropertyGroup>
<BuildDependsOn>
BeforeBuild;
CoreBuild;
AfterBuild
</BuildDependsOn>
</PropertyGroup>
So it’s a list of three targets: BeforeBuild
, CoreBuild
and AfterBuild
. The BeforeBuild
and AfterBuild
are empty. They are here for us, so we can redefine them (overwrite) in our project files. In other words, we can create a new .targets file and import it to our .csproj file, then inside this new .targets file we can override these targets like this:
<Project>
<Target Name="BeforeBuild">
<Message Text="Message before build." />
</Target>
<Target Name="AfterBuild">
<Message Text="Message after build." />
</Target>
</Project>
Alternatively, we can update the value of the BuildDependsOn
property in this way:
<PropertyGroup>
<BuildDependsOn>
OurCustomTargetBefore;
$(BuildDependsOn);
OurCustomTargetAfter
</BuildDependsOn>
</PropertyGroup>
The BuildDependsOn
property now contains a list of 5 targets where the first one and the last one are our custom targets, and the other three are defined in original value of the BuildDependsOn
property. So this is a way to extend a list of targets.
The CoreBuild
target is defined like this:
<PropertyGroup>
<CoreBuildDependsOn>
BuildOnlySettings;
PrepareForBuild;
PreBuildEvent;
ResolveReferences;
PrepareResources;
ResolveKeySource;
Compile;
ExportWindowsMDFile;
UnmanagedUnregistration;
GenerateSerializationAssemblies;
CreateSatelliteAssemblies;
GenerateManifests;
GetTargetPath;
PrepareForRun;
UnmanagedRegistration;
IncrementalClean;
PostBuildEvent
</CoreBuildDependsOn>
</PropertyGroup>
<Target Name="CoreBuild" DependsOnTargets="$(CoreBuildDependsOn)">
As you can see it’s a long list of targets. There are some preparation steps first, then the references are resolved, and then the code is compiled and so on.
Describing those targets it’s outside the scope of this article. It’s just a starter. If you want to extend the Build process, you have to dig through it and understand how it works.
Similar for Rebuild
target:
<PropertyGroup>
<RebuildDependsOn>
BeforeRebuild;
Clean;
Build;
AfterRebuild;
</RebuildDependsOn>
</PropertyGroup>
<Target Name="Rebuild" DependsOnTargets="$(RebuildDependsOn)">
The BeforeRebuild
and AfterRebuild
are empty, and we can override them.
And again for Clean target:
<PropertyGroup>
<CleanDependsOn>
BeforeClean;
UnmanagedUnregistration;
CoreClean;
CleanReferencedProjects;
CleanPublishFolder;
AfterClean
</CleanDependsOn>
</PropertyGroup>
<Target Name="Clean" DependsOnTargets="$(CleanDependsOn)">
The BeforeClean
and AfterClean
are empty, and we can override them.
The Publish pipeline
The Microsoft.Common.CurrentVersion.targets also contains Publish
and PublishOnly
(used by OneClick toolbar) targets. Here are the definitions:
<PropertyGroup>
<PublishDependsOn>
SetGenerateManifests;
Build;
PublishOnly
</PublishDependsOn>
</PropertyGroup>
<Target Name="Publish" DependsOnTargets="$(PublishDependsOn)">
<PropertyGroup>
<PublishOnlyDependsOn>
SetGenerateManifests;
PublishBuild;
BeforePublish;
GenerateManifests;
CopyFilesToOutputDirectory;
_CopyFilesToPublishFolder;
_DeploymentGenerateBootstrapper;
ResolveKeySource;
_DeploymentSignClickOnceDeployment;
AfterPublish
</PublishOnlyDependsOn>
</PropertyGroup>
<Target Name="PublishOnly" DependsOnTargets="$(PublishOnlyDependsOn)">
The Publish
target is executed when you run a Publish in VisualStudio. It first generates manifests, then does a build and then executes PublishOnly
target. When you click Publish button on your OneClick Publish toolbar in VisualStudio, the PublishOnly
target is executed.
Again, the BeforePublish
and AfterPublish
targets are empty, and we can override them.
In a web application project, an additional import is added to the project file:
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
The Microsoft.WebApplication.targets then will import Microsoft.Web.Publishing.targets. And this is where some customisation to default Publish pipeline, that is required for web applications, is defined.
I want more
To learn more, read my next article in the series, where I describe a real example of extending Publish pipeline.