Standalone StyleCop : a rough draft
Unlike FxCop, the StyleCop tool out of the box only comes with Visual Studio and MSBuild integration; there is no stand-alone command-line tool. Still, as it's now open-source, we can look at the workings of the MSBuild task and see how to drive it directly.
Here is a proof-of-concept PowerShell script which can be used as the basis for a proper command-line tool or script; and as a preliminary "how to" to write unit tests for your rules. Well, they aren't going to be proper unit tests, since they'd have to touch the file system, but they can crawl over test source files in a dummy project in the StyleCop rule solution.
# Load StyleCop | |
$path = "$($env:ProgramFiles)\StyleCop 4.5\StyleCop.dll" | |
Add-Type -Path $path | |
# arguments that must be non-null, non-empty | |
$settings = "$($env:UserProfile)\Documents\Visual Studio 2008\Projects\TestBed\Settings.StyleCop" | |
$here = Get-Location | |
$outputFile = Join-Path $here "output.xml" | |
# UNTESTED : the empty array should be filled with a list of directories containing | |
# StyleCop add-ins that aren't in or beneath the StyleCop install folder | |
$console = new-object StyleCop.StyleCopConsole -arg @($settings, $false, $outputFile, [string[]]@(), $true) | |
# Usual test case #defines | |
$defines = [string[]]@("DEBUG", "CODE_ANALYSIS") | |
$configuration = new-object StyleCop.Configuration -arg (,$defines) | |
$project = "$($env:UserProfile)\Documents\Visual Studio 2008\Projects\TestBed\TestBed.csproj" | |
$codeproject = new-object StyleCop.CodeProject -Arg @($project.GetHashCode(), $project, $configuration) | |
[StyleCop.CodeProject[]] $projects = ,$codeproject | |
## TODO -- get source files from project, do AddSourceCode for each file not excluded from StyleCop | |
$source = "$($env:UserProfile)\Documents\Visual Studio 2008\Projects\TestBed\Class1.cs" | |
$console.Core.Environment.AddSourceCode($codeproject, $source, $null) | Out-Null | |
# These are messages suitable for MSBuild e.g. "Output: (Low) Pass 1: Class1.cs" | |
$outputHandler = { | |
$e = $event.SourceEventArgs | |
$importance = $e.Importance | |
$message = $e.Output | |
$colour = "Green" | |
if ($importance -eq [Microsoft.Build.Framework.MessageImportance]::Low) { $colour = "Gray" } | |
if ($importance -eq [Microsoft.Build.Framework.MessageImportance]::High) { $colour = "Red" } | |
Write-Host "Output: ($importance) $message" -Back White -Fore $colour | |
} | |
# These are the interesting bits, which also go into the XML file e.g. | |
# Violation SA1200 in ...\Class1.cs (1) | |
# All using directives must be placed inside of the namespace. | |
$violationHandler = { | |
$e = $event.SourceEventArgs | |
if ($e.SourceCode -and $e.SourceCode.Path) { | |
$file = $e.SourceCode.Path; | |
} else { | |
if ($e.Element -and $e.Element.Document -and $e.Element.Document.SourceCode -and $e.Element.Document.SourceCode.Path) | |
{ | |
$file = $e.Element.Document.SourceCode.Path; | |
} | |
} | |
$colour = "Black" | |
if ($e.Warning) { $colour = "Blue" } | |
Write-Host "Violation $($e.Violation.Rule.CheckId) in $file ($($e.LineNumber))`r`n`t$($e.message)" -Back White -Fore $colour | |
} | |
try { | |
Register-ObjectEvent -InputObject $console -EventName OutputGenerated -SourceIdentifier "Console.OutputGenerated" -Action $outputHandler | Out-Null | |
Register-ObjectEvent -InputObject $console -EventName ViolationEncountered -SourceIdentifier "Console.ViolationEncountered" -Action $violationHandler | Out-Null | |
$Console.Start($projects, $true) | Out-Null | |
} catch [System.Exception] { | |
Write-Host $_.Exception.ToString() | |
} finally { | |
Unregister-Event "Console.OutputGenerated" | |
Unregister-Event "Console.ViolationEncountered" | |
} | |
## Could dump the XML format output here, like this; or run an XSLT report generation over it | |
##$values = Get-Content $outputFile | |
##Write-Host $values$outputFile |
And that's all there is to it, really.
No comments :
Post a Comment