Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing StyleCop MSBUILD From NuGet Package

A quick precursor to say that I've done the usual searching around the forums and net in general, and I've tried a multitude of suggestions found on this forum and elsewhere to no avail.

The problem I'm having is that my company is looking to implement internal automated peer-reviews (to an extent of course) with the use of tools such as StyleCop, ReSharper and JSLint (etc etc).

We're using a custom NuGet package against our internal NuGet Package Repository (feed) so that our developers get an administered release of tools (i.e. they cannot download the latest StyleCop when it comes out until its been reviewed and released) with the addition of our custom rules (StyleCop Settings file). Because we want to use StyleCop MSBUILD tasks to highlight errors at build time, the assemblies for StyleCop need to be exact and therefore we're ruling out installations of the C:\Program Files\ version in favour of a NuGet release.

I've managed to create a NuGet package that installs to a project (class library, web site, etc), copies the approved StyleCop assemblies (StyleCop.dll, StyleCop.CSharp.dll and StyleCop.CSharpRules.dll), Settings.StyleCop and StyleCop.Targets to the package folder, and modifies the .csproj file to include the following nodes (this just a snippet of course):

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup Condition=" '$(Configuration)' != 'Debug' ">
        <StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
    </PropertyGroup>

    <Import Project="$(SolutionDir)\packages\NuGetPackageName.1.0.0\StyleCop.Targets" />
</Project>

If I have StyleCop installed as C:\Program Files\ and in C:\Program Files\MSBUILD then everything works, but using this method the StyleCop.Targets doesn't seem to work correctly. It is being used as Visual Studio 2010 throws errors if the file is removed and it creates a StyleCop.Cache file. The StyleCop.Targets file has been changed to point at the local DLL in the NuGet package folder, and I have tried a couple of different .Targets files including the standard StyleCop one (with relative filepath changes). Even if I override the MSBUILD properties locally it doesn't work, such as OverrideSettingsFile.

The StyleCop.Targets file looks like this currently:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <!-- Specify where tasks are implemented. -->
    <UsingTask AssemblyFile="$(SolutionDir)\packages\NuGetPackageName.1.0.0\lib\net40\StyleCop.dll" TaskName="StyleCopTask"/>

    <PropertyGroup>
        <BuildDependsOn>$(BuildDependsOn);StyleCop</BuildDependsOn>
        <RebuildDependsOn>StyleCopForceFullAnalysis;$(RebuildDependsOn)</RebuildDependsOn>
    </PropertyGroup>

    <!-- Define StyleCopForceFullAnalysis property. -->
    <PropertyGroup Condition="('$(SourceAnalysisForceFullAnalysis)' != '') and ('$(StyleCopForceFullAnalysis)' == '')">
        <StyleCopForceFullAnalysis>$(SourceAnalysisForceFullAnalysis)</StyleCopForceFullAnalysis>
    </PropertyGroup>
    <PropertyGroup Condition="'$(StyleCopForceFullAnalysis)' == ''">
        <StyleCopForceFullAnalysis>false</StyleCopForceFullAnalysis>
    </PropertyGroup>

    <!-- Define StyleCopCacheResults property. -->
    <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>

    <!-- Define StyleCopEnabled property. -->
    <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="('$(SourceAnalysisOutputFile)' != '') and ('$(StyleCopOutputFile)' == '')">
        <StyleCopOutputFile>$(SourceAnalysisOutputFile)</StyleCopOutputFile>
    </PropertyGroup>
    <PropertyGroup Condition="'$(StyleCopOutputFile)' == ''">
        <StyleCopOutputFile>$(IntermediateOutputPath)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)' == ''">0</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'">
        <Message Text="Forcing full StyleCop reanalysis." Condition="'$(StyleCopForceFullAnalysis)' == 'true'" Importance="Low" />

        <!-- 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="Analyzing @(StyleCopFiles)" Importance="Low" />

        <!-- Show list of what files should be excluded. checked. Take all Compile items, but exclude those that have set ExcludeFromStyleCop=true or ExcludeFromSourceAnalysis=true. -->
        <CreateItem Include="@(Compile)" Condition="('%(Compile.ExcludeFromStyleCop)' == 'true') or ('%(Compile.ExcludeFromSourceAnalysis)' == 'true')">
            <Output TaskParameter="Include" ItemName="StyleCopExcludedFiles"/>
        </CreateItem>

        <ItemGroup>
            <StyleCopFiles Remove="@(ExcludeFromStyleCop)" />
        </ItemGroup>

        <Message Text="Excluding @(StyleCopExcludedFiles)" Importance="Normal" />

        <!-- Run the StyleCop MSBuild task. -->
        <StyleCopTask ProjectFullPath="$(MSBuildProjectDirectory)" 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>

Does anyone know how I can get this working? Visual Studio 2010 doesn't show any messages in the Output window.

Cheers all!

like image 202
Jimmy Avatar asked Feb 14 '12 14:02

Jimmy


1 Answers

I'm posting the solution I came across for anyone in a similar position.

Firstly I followed this excellent guide on debugging MSBuild operations, which helped me to iron out some of the logic and variables:

http://blogs.msdn.com/b/visualstudio/archive/2010/07/06/debugging-msbuild-script-with-visual-studio.aspx

Following this I checked the StyleCopViolations.xml which was in my /obj/Debug/ folder. This however always returned 0 results, with just the following XML:

<StyleCopViolations/>

I did some research on this and found that I needed to include StyleCop.CSharpRules.dll in to my NuGet/lib/net40/ folder with the StyleCop.dll, StyleCop.CSharp.dll and StyleCop.Settings files.

My project didn't needed references to any of the aforementioned assemblies, but I realised that my NuGet package had a dependency on the StyleCop NuGet package which did not include StyleCop.CSharpRules.dll.

After adding this assembly I was still seeing 0 results, so I did a manual override of all 3 assemblies and the .Settings file from a fresh install of the StyleCop C:\Program Files\StyleCop\ installer (from CodePlex). After overwriting the NuGet package assemblies this started working!

So to wrap up, debug your MSBuild (with the link at the top of this post) and don't use the NuGet package just yet!

Cheers

like image 97
Jimmy Avatar answered Sep 22 '22 20:09

Jimmy