Introducing StyleCop in MSBuild

    Increasingly, there are tasks of automating various processes within the framework of CI. After digging in with MSBuild, I am becoming more and more convinced that this is a fairly powerful tool. If desired, they can do a lot of things. However, I did not find articles on it as a whole, or specifically on the Habré, and decided to fill this gap as far as strength and availability of free time.
    So today we will cook

    Stylecop



    Objective: implement a total forced code check (C #) for compliance with the design rules.

    Condition: total, forced. Those. all code falling into the assembly must be verified without fail. In case of violations - build error and forward, refactor.

    Tools: StyleCop , MSBuild (TFS or TeamCity - it doesn't matter).

    So. We want the code to run through StyleCop. We are aware of the laziness and irresponsibility of the developers, so we do not trust client verification. Those. VSIX, which is installed from the StyleCop distribution available at the link above, and which allows the studio to simply right-click on a project / solution and run Run Stylecop, does not suit us.
    All sorts of StyleCop Checkin Policy is also not our way. Because they are tied to TFS, moreover, just like a repository. Thus, by launching Git for TFS, we will already lose these policies. No, we are not considering (although, purely ideologically, this is the most correct way - the "dirty" code simply should not get into the repository).

    Therefore, we simply cannot run the code 1 . All that remains is to prevent him from gathering. To do this, we have well-studied implementation mechanisms in the MSBuild pipeline. Fortunately, most of the work has already been passed for us - with the nuget package StyleCop.MSBuild there are all the files you need. These are the files of StyleCop itself (the StyleCopTask class that implements the Task we need for MSBuild is already in StyleCop.dll itself), and the default settings ( StyleCop.Settings) and, most importantly - StyleCop.MSBuild.targets. This last one is what we most need.

    How will we be introduced?



    For MSBuild, there is a pretty convenient extension mechanism through ImportBefore / After . In short:
    ... if you want to offer an extensibility point so that someone else can import a file without requiring you to explicitly add the file name to the importing file. For this purpose, Microsoft.Common.Targets contains the following line at the top of the file.


    And in the same way, files from ImportAfter are automatically imported . Those. at each build, MSBuild imports files that lie in $ (MSBuildExtensionsPath) \ $ (MSBuildToolsVersion) \ Microsoft.Common.Targets \ ImportBefore \ and ImportAfter. The difference between them is before or after loading the collected file (sln / csproj / other proj), this is true for ... another time I’ll tell you.

    You can follow the path of the monkey work: download .msi from the stylecop website, install it on the machine where MSBuild is spinning, put the .targets file in the same hands ...
    But we have several machines with build agents (formerly TFS, now also under TeamCity ) and somehow I don’t want to copy / install everything with my hands. God forbid another update then spill ... no, thank you.

    We will automate.


    Since we have TFS / TeamCity, we can go head on. We make a repository with the following file structure:
    /
    ├ [lib]
    │ ├mssp7en.dll
    │ ├mssp7en.lex
    │ ├Settings.StyleCop
    │ ├StyleCop.CSharp.dll
    │ ├StyleCop.CSharp.Rules.dll
    │ ├StyleCop.dll
    ├ [targets]
    │ ├StyleCop.MSBuild.Targets
    └build.proj
    


    build.proj


    This is just a project-file in msbuild format, which will be executed on the agent and perform operations to decompose the files to the places we need.
    The content, let's say, is
    $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.targets\ImportAfter$(MSBuildExtensionsPath)\StyleCop



    Running msbuild.exe build.proj, we get:
    0. if there are old files - deleting
    1. copying files from targets to $ (MSBuildExtensionsPath) \ $ (MSBuildToolsVersion) \ Microsoft.Common.targets \ ImportAfter (depending on the version of MSBuild to be launched, this maybe something like C: \ Program Files (x86) \ MSBuild \ 14.0 \ Microsoft.Common.Targets \ ImportAfter
    2. copy files from lib to $ (MSBuildExtensionsPath) \ StyleCop (for example C: \ Program Files (x86) \ MSBuild \ StyleCop )

    We configure in TFS / TeamCity a build that, in Continuous Integration mode, responds to commits to this repository and makes it rotten forcibly on each agent. Thus, after making changes with us, all agents will automatically update to fresh scripts / dll / configs StyleCop. Milota.

    Customize StyleCop



    In StyleCop.MSBuild.targets, it’s important for us to fix two things.
    1. Enable StyleCop. Find changing false to true:
    truefalse

    (second - so that the test results fall out in Error, not Warn).
    2. Correct the path to StyleCop.dll

    and to the config
    $(MSBuildExtensionsPath)\StyleCop\Settings.StyleCop

    There is a nuance here.

    Settings.StyleCop File


    StyleCop rules are pretty controversial by default. There are a number of both doubtful and simply uncomfortable to us. Editing this file with your hands is inconvenient. To do this, it’s easier to put the same .msi from the site and it will install at the same time StyleCopSettingsEditor.exe - a convenient GUI for editing the list of rules.
    The trick is that, by default, StyleCop includes inheritance of settings and defaults are taken from the one you installed from msi (though, the entire directory tree is scanned up first in search of the .stylecop file to inherit it automatically too).
    So by opening the file in your own folder, correcting the rules and saving, you actually save only the differences from the default. And if you put only this modified file of your own in the repository, then only what is in it will be checked. StyleCop will not receive the rules included by default under MSBuil, because it does not have the same default file.
    In order not to edit the source file and be able to replace it in case of something, while preserving my modifications, I did this: The

    original Settings.StyleCop file was renamed to default.StyleCop and placed next to it.

    What is the result?


    We have a forced code check through StyleCop. Violations are immediately visible as errors in the build log.
    We have a mechanism that automatically updates stylecop on build machines.

    PS: You can locally run this "msbuild.exe build.proj". Then all these same errors will be visible immediately in the studio. Those. msbuild imports these ImportBefore / After even when it is run from the studio. Without any plug-in installations and other things, but immediately in the Error List - know yourself click on the list and edit where the studio puts the cursor. Without subtracting logs from the build server.

    1 . I honestly tried to write an extension for TFS through ISubscriber, as described here , but ended up drowning in meager documentation, complete with no publishing tools ("drop the assembly in the plugins directory on your server C: \ Program Files \ Microsoft Team Foundation Server 12.0 \ Application Tier \ Web Services \ bin \ Plugins. Then recycle your app pool and the plugin will be activated. "- this is ridiculous), the very unobvious and confusing structure of classes / dependencies in this Microsoft.TeamFoundation.Git.Server library and even the inconsistency of the methods with their actions. And how I realized that I would have to reassemble it later under TFS2015 - I made a Close solution and went look for other ways to implement.

    Also popular now: