What is the painless/maintainable way of using MSBuild as your build runner ? (Forgive the length of this post)
I was just trying my hand at TeamCity (which I must say is awesome w.r.t. learning curve and out of the box functionality). I got an SVN > MSBuild > NUnit > NCover combo working.
I was curious as to how moderate to large projects are using MSBuild - I've just pointed MSBuild to my Main sln file. I've spent some time with NAnt some years ago and I found MSBuild to be a bit obtuse. The docs are too dense/detailed for a beginner.
MSBuild seems to have some special magic to handle .sln files ; I tried my hand at writing a custom build script by hand, linking/including .csproj files in order (such that I could have custom pre-post build tasks). However it threw up (citing duplicate target imports). I'm assuming most devs wouldn't want to go messing around with msbuild proj files - they'd be making changes to the .csproj and .sln files. Is there some tool / MSBuild task that reverse-engineers a new script from an existing .sln + its .csproj files that I'm unaware of ?
If I'm using MSBuild just to do the compile step, I might as well use Nant with an exec task to MSBuild for compiling the solution ? I've this nagging feeling that I'm missing something obvious.
My end-goal here is to have a MSBuild build script
I didn't try TeamCity yet but did set up a Build environment for our new BizTalk project.
Following the excellent advice of Sayed Ibrahim Hashimi on my own question before starting out, I created a set of MSBuild .proj and .targets scripts.
The Core
A central .targets script for the actual build steps you want to perform:
<Project DefaultTargets="Deploy" xmlns="...">
<!-- omitted validation steps, see referenced post for details -->
<PropertyGroup>
<PullDependsOn>
$(ValidateDependsOn);
Validate;
</PullDependsOn>
</PropertyGroup>
<PropertyGroup>
<BuildDependsOn>
$(PullDependsOn);
PullFromVersionControl;
</BuildDependsOn>
</PropertyGroup>
<PropertyGroup>
<DeployDependsOn>
$(BuildDependsOn);
Build;
</DeployDependsOn>
</PropertyGroup>
<Target Name="PullFromVersionControl" DependsOnTargets="$(PullDependsOn)">
<Exec Command="..." />
</Target>
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)">
<MSBuild Projects="@(ProjectsToBuild)" />
</Target>
<Target Name="Deploy" DependsOnTargets="$(DeployDependsOn)">
<Exec Command="..." />
</Target>
</Project>
The second core part are the configuration targets like you find them in your .csproj files
<Project xmlns="...">
<PropertyGroup Condition=" '$(Environment)' == 'DEV' ">
<SomeConfigKey Condition=" '$(SomeConfigKey)' == '' ">Foo</SomeConfigKey>
</PropertyGroup>
<PropertyGroup Condition=" '$(Environment)' == 'TEST' ">
<SomeConfigKey Condition=" '$(SomeConfigKey)' == '' ">Bar</SomeConfigKey>
</PropertyGroup>
</Project>
The Projects
The single .csproj itself is represented by a.targets file with just a collection of ItemGroups you need for building.
<Project xmlns="...">
<ItemGroup>
<!-- this group contains the list of items to pull from version control -->
<Sources Include="@(Sources)" />
<Sources Include="MyProjectRootDir" />
<Sources Include="MyDependentProjectRootDir" />
</ItemGroup>
<ItemGroup>
<ProjectsToBuild Include="@(ProjectsToBuild)" />
<ProjectsToBuild Include="MyProject.csproj" />
</ItemGroup>
</Project>
Putting it together
The .proj you are actually going to execute with MSBuild will import your Configuration, your Project (source code files) and the Core (Pull, Build and Deployment commands)
<Project DefaultTargets="Deploy" xmlns="...">
<Import Project="Config.targets"/>
<Import Project="Project.targets"/>
<Import Project="Core.targets"/>
</Project>
Using this approach I was able to reuse the .targets containing the sources to build my some 50 projects in many different combinations instead of creating VS solutions to group them.
I hope you'll find this useful - I can add more details if you're interested.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With