Using StyleCop in TFS Team Build

The recent release of the MSBuild Extensions includes a task for StyleCop 4.3. I have been trying to get this integrated into our TFS TeamBuild, I think it is a far more preferable way to do it than editing the various project files in our solution to link in StyleCop as you had to do in 4.2.

There are a good few steps I had to follow to get it doing:

  • Install the StyleCop 4.3 Msi
  • Install the MSBuild Extensions Msi
  • Now we have to do some fixed/changes. First copy the MSBuild.ExtensionPack.StyleCop.dll from the C:\Program Files\MSBuild\ExtensionPack to C:\Program Files\MSBuild\Microsoft\StyleCop\v4.3. We need to do this as the StyleCop DLLs are not automagically found (you could fix this using a search path I suppose)
  • Next we need to modify the C:\Program Files\MSBuild\ExtensionPack\MSBuild.ExtensionPack.tasks file to fix a typo that is a known issue. The StyleCop line at the end of the file should read
  • <UsingTask AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\StyleCop\v4.3\MSBuild.ExtensionPack.StyleCop.dll" TaskName="MSBuild.ExtensionPack.CodeQuality.StyleCop"/>

  • Now edit your Team Build tfsbuild.proj file; import the extension tasks
  • <PropertyGroup>
      <TPath>C:\Program Files\MSBuild\ExtensionPack\MSBuild.ExtensionPack.tasks</TPath>
      <TPath Condition="Exists('C:\Program Files\MSBuild\ExtensionPack\MSBuild.ExtensionPack.tasks')">C:\Program Files\MSBuild\ExtensionPack\MSBuild.ExtensionPack.tasks</TPath>
    </PropertyGroup>
    <Import Project="$(TPath)"/>

  • Now you need to edit or add the AfterCompile target, something like as shown below. I have added comments for each block.

<Target Name="AfterCompile">
   <!– Create a collection of files to scan –>
  <CreateItem Include="c:\MsBuild\MySolution\MyProject\**\*.cs">
    <Output TaskParameter="Include" ItemName="StyleCopFiles"/>
  </CreateItem>

   <!– Run the StyleCop MSBuild task using the setting file in the same directory as sln file and also stored in TFS –>
  <MSBuild.ExtensionPack.CodeQuality.StyleCop
      TaskAction="Scan"
      SourceFiles="@(StyleCopFiles)"
      ShowOutput="true"
      ForceFullAnalysis="true"
      CacheResults="false"
      logFile="$(DropLocation)\$(BuildNumber)\StyleCopLog.txt"
      SettingsFile="c:\MsBuild\MySolution\Settings.StyleCop"
      ContinueOnError="false">
    <Output TaskParameter="Succeeded" PropertyName="AllPassed"/>
    <Output TaskParameter="ViolationCount" PropertyName="Violations"/>
    <Output TaskParameter="FailedFiles" ItemName="Failures"/>
  </MSBuild.ExtensionPack.CodeQuality.StyleCop>

<!—Log the summary of the results –>
<Message Text="StyleCop Succeeded: $(AllPassed), Violations: $(Violations)"/>

  <!– FailedFile format is:
      <ItemGroup>
          <FailedFile Include="filename">
              <CheckId>SA Rule Number</CheckId>
              <RuleDescription>Rule Description</RuleDescription>
              <RuleName>Rule Name</RuleName>
              <LineNumber>Line the violation appears on</LineNumber>
              <Message>SA violation message</Message>
          </FailedFile>
      </ItemGroup>—>

<!– Log the details of any violations –>
<Warning Text="%(Failures.Identity) – Failed on Line %(Failures.LineNumber). %(Failures.CheckId): %(Failures.Message)"/>

<!– The StyleCop task does not throw an error if the analysis failed, 
so we need to check the return value and if we choose to treat errors as warnings 
we need to set the error state, if set it will cause us to jump to the failure target –>
  <Error Text="StyleCop analysis warnings occured" Condition="'$(AllPassed)' == 'False'"  />

  <!– List out the issues, we only need this if we are not forcing the error above, as if we have we never get here –>
  <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
          BuildUri="$(BuildUri)"
          Message="%(Failures.Identity) – Failed on Line %(Failures.LineNumber). %(Failures.CheckId): %(Failures.Message)"/>

  <!—Complete the stylecop step, we get here if we have no thrown an error  –>
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                BuildUri="$(BuildUri)"
                Id="$(StyleCopStep)"
                Status="Succeeded"
                Message="StyleCop Succeeded: $(AllPassed), Violations: $(Violations)"/>

  <!– If an error has been raised we need to call the failure target
       You might have thought you could get the same effect as the error line a few lines above by adding a condition to the OnError as shown commented out below. However this does not work as the OnError condition is not evaluated unless an error has previously occured in a task, the condition clause is secondary–>
  <OnError ExecuteTargets="FailTheBuild" />
  <!–<OnError ExecuteTargets="FailTheBuild" Condition="'$(AllPassed)' == 'False'"  />–>

</Target>

<Target Name="FailTheBuild">
  <!– We are failing the build due to stylecop issues –>
  <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
          BuildUri="$(BuildUri)"
          Id="$(StyleCopStep)"
          Status="Failed"
          Message="StyleCop Failed: $(AllPassed), Violations: $(Violations) [See $(DropLocation)\$(BuildNumber)\StyleCopLog.txt]"/>

  <!– List out the issues–>
  <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
          BuildUri="$(BuildUri)"
          Message="%(Failures.Identity) – Failed on Line %(Failures.LineNumber). %(Failures.CheckId): %(Failures.Message)"/>

</Target>

 

So this will run StyleCop as a separate build step and you have the option to fail the build or not depending on your view of StyleCop violations by commenting out one line. Either way violations will be listed as rows in the list of build steps.

I had really wanted to get the number of violations added to either the total errors or warnings as listed in the Results Details at the end of the build, also I had wanted a simple way to access the StyleCopLog.txt created in the drops directory. However, I have not worked out how this final step yet. If I manage to work it out I will blog on the solution – or if you know to do please post a comment.