views:

1757

answers:

1

I have been asked to integrate StyleCop in our CI build process in such a way that:

  • Individual project file in our (large) solution are not affected
  • I don't have to use any 3rd party tool

The first requirement (and I don't fully understand this yet) is due to the fact that we don't want to run StyleCop on our entire solution straight off. Apparently, when StyleCop is run from within VS it ignores certain attributes that specify files to ignore. Because of this, if we have it running on the dev machines we will continously be hit by thousands of violations we are not yet ready to deal with. So the bottom line is we want to be able to run it only on the build server.

Our build environment currently consists of:

Cruise control > nant task that executes msbuild (via exec)

The nant task is below:

<target name="buildSolution">  
 <echo message="Building solution..." />
 <exec program="C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe"            
  commandline="${Project.SolutionFile} /t:Rebuild /p:Configuration=${Project.SolutionBuildConfiguration} /v:q" workingdir="." />      
</target>

When I first looked at this I thought it would be a simple case of executing StyleCop in a similar manner to the way msbuild is being executed.

However, StyleCop comes as a set of dlls...

So this means I cannot do what I intended......I think....

All the articles I have googled today have said "use StyleCopCmd" which I also cannot do because of the 3rd party tool restriction.

I've looked at the tool and it appears to implement a custom nant task that kicks off the StyleCopConsole, hooks into a couple of events and outputs a nicely formatted report. But in order to be able to justify the creation of any tool in-house I need to be able to fully explain why I cannot just achieve what I want in the nant config file. Or in any other way that does not involve creating or using a tool. And ideally, it would be faster if I didn't have to write or use a tool anyway.

So my question is, is that possible?

+2  A: 

We've managed to do this. Just follow these steps:

  • Create a directory in your project and copy all the StyleCop files there (Microsoft.StyleCop.CSharp.dll, Microsoft.StyleCop.Targets, etc.)

  • Edit Microsoft.StyleCop.Targets to look like this:

--

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"&gt;
    <UsingTask AssemblyFile="Microsoft.StyleCop.dll" TaskName="StyleCopTask" />
    <PropertyGroup>
        <BuildDependsOn>StyleCop</BuildDependsOn>
        <RebuildDependsOn>StyleCopForceFullAnalysis;StyleCop</RebuildDependsOn>
    </PropertyGroup>
    <PropertyGroup Condition="('$(SourceAnalysisForceFullAnalysis)' != '') and ('$(StyleCopForceFullAnalysis)' == '')">
        <StyleCopForceFullAnalysis>$(SourceAnalysisForceFullAnalysis)</StyleCopForceFullAnalysis>
    </PropertyGroup>
    <PropertyGroup Condition="'$(StyleCopForceFullAnalysis)' == ''">
        <StyleCopForceFullAnalysis>false</StyleCopForceFullAnalysis>
    </PropertyGroup>
    <PropertyGroup Condition="('$(SourceAnalysisCacheResults)' != '') and ('$(StyleCopCacheResults)' == '')">
        <StyleCopCacheResults>$(SourceAnalysisCacheResults)</StyleCopCacheResults>
    </PropertyGroup>
    <PropertyGroup Condition="'$(StyleCopCacheResults)' == ''">
        <StyleCopCacheResults>true</StyleCopCacheResults>
    </PropertyGroup>

    <!-- Define StyleCopTreatErrorsAsWarnings property. -->
    <PropertyGroup Condition="('$(SourceAnalysisTreatErrorsAsWarnings)' != '') and ('$(StyleCopTreatErrorsAsWarnings)' == '')">
        <StyleCopTreatErrorsAsWarnings>$(SourceAnalysisTreatErrorsAsWarnings)</StyleCopTreatErrorsAsWarnings>
    </PropertyGroup>
    <PropertyGroup Condition="'$(StyleCopTreatErrorsAsWarnings)' == ''">
        <StyleCopTreatErrorsAsWarnings>true</StyleCopTreatErrorsAsWarnings>
    </PropertyGroup>

    <PropertyGroup Condition="('$(SourceAnalysisEnabled)' != '') and ('$(StyleCopEnabled)' == '')">
        <StyleCopEnabled>$(SourceAnalysisEnabled)</StyleCopEnabled>
    </PropertyGroup>
    <PropertyGroup Condition="'$(StyleCopEnabled)' == ''">
        <StyleCopEnabled>true</StyleCopEnabled>
    </PropertyGroup>

    <!-- Define StyleCopOverrideSettingsFile property. -->
    <PropertyGroup Condition="('$(SourceAnalysisOverrideSettingsFile)' != '') and ('$(StyleCopOverrideSettingsFile)' == '')">
        <StyleCopOverrideSettingsFile>$(SourceAnalysisOverrideSettingsFile)</StyleCopOverrideSettingsFile>
    </PropertyGroup>
    <PropertyGroup Condition="'$(StyleCopOverrideSettingsFile)' == ''">
        <StyleCopOverrideSettingsFile> </StyleCopOverrideSettingsFile>
    </PropertyGroup>

    <!-- Define StyleCopOutputFile property. -->
    <PropertyGroup Condition="('$(StyleCopOutputPath)' == '')">
        <StyleCopOutputPath>$(IntermediateOutputPath)</StyleCopOutputPath>
    </PropertyGroup>
    <PropertyGroup Condition="'$(StyleCopOutputFile)' == ''">
        <StyleCopOutputFile Condition="!HasTrailingSlash('$(StyleCopOutputPath)')">$(StyleCopOutputPath)\$(AssemblyName).StyleCopViolations.xml</StyleCopOutputFile>
        <StyleCopOutputFile Condition="HasTrailingSlash('$(StyleCopOutputPath)')">$(StyleCopOutputPath)$(AssemblyName).StyleCopViolations.xml</StyleCopOutputFile>
    </PropertyGroup>

    <!-- Define all new properties which do not need to have both StyleCop and SourceAnalysis variations. -->
    <PropertyGroup>
        <!-- Specifying 0 will cause StyleCop to use the default violation count limit.
         Specifying any positive number will cause StyleCop to use that number as the violation count limit.
         Specifying any negative number will cause StyleCop to allow any number of violations without limit. -->
        <StyleCopMaxViolationCount Condition="'$(StyleCopMaxViolationCount)' == ''">100</StyleCopMaxViolationCount>
    </PropertyGroup>

    <!-- Define target: StyleCopForceFullAnalysis -->
    <Target Name="StyleCopForceFullAnalysis">
        <CreateProperty Value="true">
            <Output TaskParameter="Value" PropertyName="StyleCopForceFullAnalysis" />
        </CreateProperty>
    </Target>

    <!-- Define target: StyleCop -->
    <Target Name="StyleCop" Condition="'$(StyleCopEnabled)' != 'false'">
        <!-- Determine what files should be checked. Take all Compile items, but exclude those that have set ExcludeFromStyleCop=true or ExcludeFromSourceAnalysis=true. -->
        <CreateItem Include="@(Compile)" Condition="('%(Compile.ExcludeFromStyleCop)' != 'true') and ('%(Compile.ExcludeFromSourceAnalysis)' != 'true')">
            <Output TaskParameter="Include" ItemName="StyleCopFiles"/>
        </CreateItem>

        <Message Text="Forcing full StyleCop reanalysis." Condition="'$(StyleCopForceFullAnalysis)' == 'true'" Importance="Low" />

        <Message Text="Analyzing @(StyleCopFiles)" Importance="Low" />

        <!-- Run the StyleCop MSBuild task. -->
        <StyleCopTask
            ProjectFullPath="$(MSBuildProjectFile)"
            SourceFiles="@(StyleCopFiles)"
            AdditionalAddinPaths="@(StyleCopAdditionalAddinPaths)"
            ForceFullAnalysis="$(StyleCopForceFullAnalysis)"
            DefineConstants="$(DefineConstants)"
            TreatErrorsAsWarnings="$(StyleCopTreatErrorsAsWarnings)"
            CacheResults="$(StyleCopCacheResults)"
            OverrideSettingsFile="$(StyleCopOverrideSettingsFile)"
            OutputFile="$(StyleCopOutputFile)"
            MaxViolationCount="$(StyleCopMaxViolationCount)"
            />

        <!-- Make output files cleanable -->
        <CreateItem Include="$(StyleCopOutputFile)">
            <Output TaskParameter="Include" ItemName="FileWrites"/>
        </CreateItem>

        <!-- Add the StyleCop.cache file to the list of files we've written - so they can be cleaned up on a Build Clean. -->
        <CreateItem Include="StyleCop.Cache" Condition="'$(StyleCopCacheResults)' == 'true'">
            <Output TaskParameter="Include" ItemName="FileWrites"/>
        </CreateItem>
    </Target>
</Project>
  • Add the following tasks to your NAnt script where you want to run StyleCop. Just replace the NAnt properties with the properties or values that make sense for your build script.

        <msbuild project="${solutionfile}" target="ReBuild" verbosity="Quiet">
           <property name="CustomAfterMicrosoftCommonTargets" value="${path::combine(project:get-base-directory(), relative-path-to-Microsoft.StyleCop.Targets)}"/>
           <property name="Configuration" value="Release"/>
           <property name="StyleCopEnabled" value="true" />
           <property name="StyleCopOutputPath" value="${directory-to-dump-output-report-to}" />
           <arg value="/noconsolelogger" />
        </msbuild>
    
Trumpi
Hi Trumpi - I am going through it now and trying it out - one question - does "target="ReBuild"" remain the same or will that now point to my original build target (shown in my post)?
J M
also, the path to the modified Microsoft.StyleCop.Targets file - is this relative from the build file or the solution file?
J M
hmm been googling and think it needs to go inside my original task i.e. replcae the exec call - trying that now...
J M
that tried but faile dbecause it was trying to execute .net 2.0 msbuild instead of .net 3.5 msbuild - googling some more - i am close, i can feel it lol :)
J M
You can try passing -t:net-3.5 as an argument to the NAnt invocation, but you will need to be using NAnt 0.86.The path is relative to the NAnt build file and I think that you need to make it an absolute path. I'll modify the answer with the correct script.
Trumpi
target="ReBuild" : The Microsoft.StyleCop.Targets redefines the dependencies of ReBuild to only perform the StyleCop analysis.
Trumpi
I hit a wall due to the requirement for Nant 0.86. It wouldn't let me change the target framework to 3.5. However, your answer helped me figure out how to modify my original exec call to the 3.5 msbuild exe and initialise the stylecop settings this way:<exec program="C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe" commandline="SolutionPath /t:Rebuild /p:Configuration=Blah;CustomAfterMicrosoftCommonTargets=Blah\Microsoft.StyleCop.Targets;StyleCopEnabled=true/v:q" workingdir="." /> Thanks again. I'm accepting your post as the answer and voting it up for usefulness.
J M
Just one other thing came up - using msbuild in this way runs a StyleCop analysis but dies not actually do a build. So I ended up with two exec calls to msbuild (this first with style cop parameters set, teh second without). This is now working with warnings being directed out to the Cruise Control Nant windows (and violation files generated if the relevant setting is on). There are a few annoyances with it that I will be getting around by creating our own wrapper tool (when I have the time) but for now I at least have StyleCop anaylsis working in our CI. Thanks again for your help.
J M