Tag Archives: msbuild

Enabling a project to have multiple StyleCop targets.

As the RC is now out for StyleCop 4.5 I wanted to give it a go mainly to see if the ReSharper integration included works with Resharper 6 (unfortunately it doesn’t :(). Unfortunately it includes a breaking-change if you import the StyleCop.targets file (so that violations are shown as either errors or warnings when building the project) as the path has changed.

To enable me to continue to use the 4.5RC version and my workmates to carry on using 4.4 I had to make some changes to the csproj file to cater for both:

<PropertyGroup>
    <StyleCop4_4Path>$(MSBuildExtensionsPath32)\Microsoft\StyleCop\v4.4\Microsoft.StyleCop.targets</StyleCop4_4Path>
    <StyleCop4_5Path>$(MSBuildExtensionsPath32)\StyleCop\v4.5\StyleCop.targets</StyleCop4_5Path>
</PropertyGroup>
<Import Condition="!exists($(StyleCop4_5Path))" Project="$(StyleCop4_4Path)" />
<Import Condition="exists($(StyleCop4_5Path))" Project="$(StyleCop4_5Path)" />

This disables the 4.4 target if 4.5 is installed, however if neither are installed it will fail the build trying to find 4.4. If you need to disable both if they are not installed you can replace the first import with the following:

  <Import Condition="exists($(StyleCop4_4Path))" Project="$(StyleCop4_4Path)" />

A few “features” in MSBuild that caused me pain today…

Today I decided to work on deploying to both our internal load-balanced app servers directly from a manually run build. Unfortunately I came across the following issues that caused me to spend far too long on this task!!

Currently I have a library of MSBuild .targets files that load in generic tasks that I can run after a build has run. One of them is a DeployWebsite task that uses a couple of Properties and can clean down and deploy the latest code from the build. I realised that all I needed to do was modify the current build to set the deploy location first. However this turned out to be a lot harder than I thought it would be…

Firstly I tried the following code which doesn’t set the Global property “AppFolderPath”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <AppFolderPath>[INVALID]</AppFolderPath>
    </PropertyGroup>
    <Target Name="SetDeployLocation">
        <PropertyGroup>
            <AppFolderPath>[SOME SHARE ON SERVER]</AppFolderPath>
        </PropertyGroup>
        <CallTarget Targets="DeployWebsite" />
    </Target>
    <Target Name="DeployWebsite">
        <Message Text="AppFolderPath=$(AppFolderPath)" />
    </Target>
</Project>

To fix this you need to seperate the tasks into two Targets and call them from a single parent Target and set the Property using CreateProperty (not declaratively and doesn’t matter if you have already created it!):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <AppFolderPath>[INVALID SHARE]</AppFolderPath>
    </PropertyGroup>
    <Target Name="DeployToLive">
        <CallTarget Targets="SetDeployLocation" />
        <CallTarget Targets="DeployWebsite" />
    </Target>
    <Target Name="SetDeployLocation">
        <CreateProperty Value="[SOME SHARE ON SERVER]">
            <Output TaskParameter="Value" PropertyName="AppFolderPath" />
        </CreateProperty>
    </Target>
    <Target Name="DeployWebsite">
        <Message Text="AppFolderPath=$(AppFolderPath)" />
    </Target>
</Project>

Once I’d fixed those it still wouldn’t call the DeployWebsite multiple times. The first worked fine, however it turns out that MSBuild will not allow you to call a target with the same name twice. To get around this you can spawn a separate MSBuild task for each instance you need to run:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="DeployWebsiteToLive">
        <MSBuild Targets="DeployWebsite"
            Properties="AppFolderPath=[SHARE ON SERVER 1];BuildDirectory=$(BuildDirectory)"
            Projects="$(MSBuildProjectFile)" />

        <MSBuild Targets="DeployWebsite"
            Properties="AppFolderPath=[SHARE ON SERVER 2];BuildDirectory=$(BuildDirectory)"
            Projects="$(MSBuildProjectFile)" />
    </Target>
    <Target Name="DeployWebsite">
        <Message Text="AppFolderPath=$(AppFolderPath)" />
    </Target>
</Project>

One thing to notice there is you need to pass across ALL properties that are not set declaratively. If you do not pass anything these are all sent across automatically, but MSBuild still detects that the two tasks are exactly the same and only runs the first one.

Hopefully you won’t have to spend as long as I did on it now! 🙂