Skip to content

Commit

Permalink
Merge pull request #2174 from SteveGilham/develop/fxcop
Browse files Browse the repository at this point in the history
Develop/fxcop
  • Loading branch information
matthid authored Nov 6, 2018
2 parents 80a8691 + f6d00b7 commit 4eadc22
Show file tree
Hide file tree
Showing 11 changed files with 637 additions and 6 deletions.
15 changes: 15 additions & 0 deletions Fake.sln
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Fake.Core.Vault", "src\app\
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Fake.Installer.Squirrel", "src\app\Fake.Installer.Squirrel\Fake.Installer.Squirrel.fsproj", "{3DEF2E95-4BF8-413F-930E-32103A39364A}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fake.DotNet.FxCop", "src\app\Fake.DotNet.FxCop\Fake.DotNet.FxCop.fsproj", "{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1070,6 +1072,18 @@ Global
{3DEF2E95-4BF8-413F-930E-32103A39364A}.Release|x64.Build.0 = Release|Any CPU
{3DEF2E95-4BF8-413F-930E-32103A39364A}.Release|x86.ActiveCfg = Release|Any CPU
{3DEF2E95-4BF8-413F-930E-32103A39364A}.Release|x86.Build.0 = Release|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Debug|x64.ActiveCfg = Debug|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Debug|x64.Build.0 = Debug|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Debug|x86.ActiveCfg = Debug|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Debug|x86.Build.0 = Debug|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Release|Any CPU.Build.0 = Release|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Release|x64.ActiveCfg = Release|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Release|x64.Build.0 = Release|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Release|x86.ActiveCfg = Release|Any CPU
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1151,6 +1165,7 @@ Global
{F1641150-B89D-40B7-A3BE-9DC357410FDA} = {7BFFAE76-DEE9-417A-A79B-6A6644C4553A}
{AAAF92C5-C40D-40B8-84BA-137DF0E98B56} = {901F162F-8925-4390-89C5-9EE2C343F744}
{3DEF2E95-4BF8-413F-930E-32103A39364A} = {901F162F-8925-4390-89C5-9EE2C343F744}
{69E4C443-11D6-41BC-920C-ED9D7B36DDCA} = {7BFFAE76-DEE9-417A-A79B-6A6644C4553A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {058A0C5E-2216-4306-8AFB-0AE28320C26A}
Expand Down
1 change: 1 addition & 0 deletions build.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ let dotnetAssemblyInfos =
"Fake.DotNet.Fsc", "Running the f# compiler - fsc"
"Fake.DotNet.FSFormatting", "Running fsformatting.exe and generating documentation"
"Fake.DotNet.Fsi", "FSharp Interactive - fsi"
"Fake.DotNet.FxCop", "Running FxCop for static analysis"
"Fake.DotNet.Mage", "Manifest Generation and Editing Tool"
"Fake.DotNet.MSBuild", "Running msbuild"
"Fake.DotNet.NuGet", "Running NuGet Client and interacting with NuGet Feeds"
Expand Down
1 change: 1 addition & 0 deletions help/templates/template.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
<a href="/dotnet-assemblyinfo.html" class="navbar-item">AssemblyInfoFile</a>
<a href="/apidocs/v5/fake-dotnet-fsc.html" class="navbar-item">Fsc</a>
<a href="/apidocs/v5/fake-dotnet-fsi.html" class="navbar-item">Fsi</a>
<a href="/apidocs/v5/fake-dotnet-fxcop.html" class="navbar-item">FxCop</a>
<a href="/apidocs/v5/fake-dotnet-msbuild.html" class="navbar-item">MSBuild</a>
<a href="/apidocs/v5/fake-dotnet-testing-nunit3.html" class="navbar-item">Testing -
NUnit</a>
Expand Down
17 changes: 17 additions & 0 deletions src/app/Fake.DotNet.FxCop/AssemblyInfo.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Auto-Generated by FAKE; do not edit
namespace System
open System.Reflection

[<assembly: AssemblyTitleAttribute("FAKE - F# Make Running FxCop for static analysis")>]
[<assembly: AssemblyProductAttribute("FAKE - F# Make")>]
[<assembly: AssemblyVersionAttribute("5.9.4")>]
[<assembly: AssemblyInformationalVersionAttribute("5.9.4-alpha")>]
[<assembly: AssemblyFileVersionAttribute("5.9.4")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "FAKE - F# Make Running FxCop for static analysis"
let [<Literal>] AssemblyProduct = "FAKE - F# Make"
let [<Literal>] AssemblyVersion = "5.9.4"
let [<Literal>] AssemblyInformationalVersion = "5.9.4-alpha"
let [<Literal>] AssemblyFileVersion = "5.9.4"
32 changes: 32 additions & 0 deletions src/app/Fake.DotNet.FxCop/Fake.DotNet.FxCop.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net462;netstandard2.0</TargetFrameworks>
<AssemblyName>Fake.DotNet.FxCop</AssemblyName>
<OutputType>Library</OutputType>
</PropertyGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants);FX_NO_REMOTING;USE_ASYNC_LOCAL</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net462|AnyCPU'">
<WarningLevel>5</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
<NoWarn>FS2003</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="VisibleTo.fs" />
<Compile Include="FxCop.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Fake.Core.Process\Fake.Core.Process.fsproj" />
<ProjectReference Include="..\Fake.Core.Trace\Fake.Core.Trace.fsproj" />
<ProjectReference Include="..\Fake.Core.Xml\Fake.Core.Xml.fsproj" />
<ProjectReference Include="..\Fake.IO.FileSystem\Fake.IO.FileSystem.fsproj" />
</ItemGroup>
<Import Project="..\..\..\.paket\Paket.Restore.targets" />
</Project>
229 changes: 229 additions & 0 deletions src/app/Fake.DotNet.FxCop/FxCop.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/// Contains a task to invoke the FxCop tool
///
/// ### Sample
///
/// Target.create "FxCop" (fun _ ->
/// Directory.ensure "./_Reports"
/// let rules = [ "-Microsoft.Design#CA1011" // maybe sometimes
/// "-Microsoft.Design#CA1062" ] // null checks, In F#!
/// !! ("**/bin/Debug/*.dll")
/// |> FxCop.run { FxCop.Params.Create() with WorkingDirectory = "."
/// UseGAC = true
/// Verbose = false
/// ReportFileName = "_Reports/FxCopReport.xml"
/// Rules = rules
/// FailOnError = FxCop.ErrorLevel.Warning
/// IgnoreGeneratedCode = true})
///
[<RequireQualifiedAccess>]
module Fake.DotNet.FxCop

open System
open Microsoft.Win32
open Fake.Core
open Fake.IO
open Fake.IO.FileSystemOperators

/// The FxCop error reporting level : warning, critical warning, error, critical error, tool error, don't fail build (Default: DontFailBuild)
type ErrorLevel =
| Warning = 5
| CriticalWarning = 4
| Error = 3
| CriticalError = 2
| ToolError = 1
| DontFailBuild = 0

/// Parameter type for the FxCop tool
[<NoComparison>]
type Params =
{ /// Apply the XSL style sheet to the output. Default false.
ApplyOutXsl : bool
/// Output messages to console, including file and line number information. Default true.
DirectOutputToConsole : bool
/// Locations to search for assembly dependencies. Default empty.
DependencyDirectories : string seq
/// Import XML report(s) or FxCop project file(s). Default empty.
ImportFiles : string seq
/// Directory containing rule assemblies or path to rule assembly. Enables all rules. Default empty.
RuleLibraries : string seq
/// Namespace and CheckId strings that identify a Rule. '+' enables the rule; '-' disables the rule. Default empty.
Rules : string seq
/// Rule set to be used for the analysis. It can be a file path to the rule set
/// file or the file name of a built-in rule set. '+' enables all rules in the
/// rule set; '-' disables all rules in the rule set; '=' sets rules to match the
/// rule set and disables all rules that are not enabled in the rule set.
/// Default empty.
CustomRuleset : string
/// Suppress analysis results against generated code. Default false.
IgnoreGeneratedCode : bool
/// Apply specified XSL to console output. Default empty.
ConsoleXslFileName : string
/// FxCop project or XML report output file. Default "FXCopResults.html" in the current working directory
ReportFileName : string
/// Reference the specified XSL in the XML report file or "none" to generate an XML report with no XSL style sheet.
/// Default empty.
OutputXslFileName : string
/// Location of platform assemblies. Default empty.
PlatformDirectory : string
/// Project file to load. Default empty.
ProjectFile : string
/// Display summary after analysis. Default true.
IncludeSummaryReport : bool
/// Search Global Assembly Cache for missing references. Default false.
UseGAC : bool
/// Analyze only these types and members. Default empty
Types : string seq
/// Update the project file if there are any changes. Default false.
SaveResultsInProjectFile : bool
/// Working directory for relative file paths. Default is the current working directory
WorkingDirectory : string
/// Give verbose output during analysis. Default true.
Verbose : bool
/// The error level that will cause a build failure. Default ontFailBuild.
FailOnError : ErrorLevel
/// Path to the FxCop executable. Default = %VSINSTALLDIR%/Team Tools/Static Analysis Tools/FxCop/FxCopCmd.exe
/// where %VSINSTALLDIR% is a Visual Stdio 2017 installation location derived from the registry
ToolPath : string
/// Write output XML and project files even in the case where no violations
/// occurred. Default false.
ForceOutput : bool
/// Custom dictionary used by spelling rules. Default empty.
CustomDictionary : string }

static member private vsInstallPath() =
if Environment.isWindows then
let instance =
BlackFox.VsWhere.VsInstances.getWithPackage
"Microsoft.VisualStudio.Component.Static.Analysis.Tools" false
|> List.tryHead
match instance with
| Some vs -> vs.InstallationPath
| None -> String.Empty
else String.Empty

/// FxCop Default parameters, values as above
static member Create() =
{ ApplyOutXsl = false
DirectOutputToConsole = true
DependencyDirectories = Seq.empty
ImportFiles = Seq.empty
RuleLibraries = Seq.empty
Rules = Seq.empty
CustomRuleset = String.Empty
IgnoreGeneratedCode = false
ConsoleXslFileName = String.Empty
ReportFileName = Shell.pwd() @@ "FXCopResults.html"
OutputXslFileName = String.Empty
PlatformDirectory = String.Empty
ProjectFile = String.Empty
IncludeSummaryReport = true
Types = Seq.empty
UseGAC = false
SaveResultsInProjectFile = false
WorkingDirectory = Shell.pwd()
Verbose = true
FailOnError = ErrorLevel.DontFailBuild
ToolPath =
Params.vsInstallPath()
@@ "Team Tools/Static Analysis Tools/FxCop/FxCopCmd.exe"
ForceOutput = false
CustomDictionary = String.Empty }

// default Xml reader
let internal XmlReadIntBase failOnError xmlFileName nameSpace prefix xPath =
(Xml.read_Int failOnError xmlFileName nameSpace prefix xPath) |> snd

// Unit test mocking point
let mutable internal XmlReadInt = XmlReadIntBase

/// This checks the result file with some XML queries for errors
/// [omit]
let internal checkForErrors resultFile =
// original version found at http://blogs.conchango.com/johnrayner/archive/2006/10/05/Getting-FxCop-to-break-the-build.aspx
let getErrorValue s =
XmlReadInt false resultFile String.Empty String.Empty
(sprintf "string(count(//Issue[@Level='%s']))" s)
getErrorValue "CriticalError", getErrorValue "Error", getErrorValue "CriticalWarning",
getErrorValue "Warning"

let internal createProcess param args =
CreateProcess.fromRawCommand param.ToolPath args
|> if String.IsNullOrWhiteSpace param.WorkingDirectory then id
else CreateProcess.withWorkingDirectory param.WorkingDirectory

let internal createArgs fxparams assemblies =
let param =
if fxparams.ApplyOutXsl && (String.IsNullOrWhiteSpace fxparams.OutputXslFileName) then
{ fxparams with OutputXslFileName =
fxparams.ToolPath @@ "Xml" @@ "FxCopReport.xsl" }
else fxparams

let Item a x =
if x |> String.IsNullOrWhiteSpace then []
else [ sprintf a x ]

let ItemList a x =
if x |> isNull then []
else
x
|> Seq.collect (fun i -> [ sprintf a i ])
|> Seq.toList

let Flag predicate a =
if predicate then [ a ]
else []

let rules =
param.RuleLibraries |> Seq.map (fun item -> param.ToolPath @@ "Rules" @@ item)
[ Flag param.ApplyOutXsl "/aXsl"
Flag param.DirectOutputToConsole "/c"
Flag param.ForceOutput "/fo"
Item "/cXsl:\"%s\"" param.ConsoleXslFileName
ItemList "/d:\"%s\"" param.DependencyDirectories
ItemList "/f:\"%s\"" assemblies
ItemList "/i:\"%s\"" param.ImportFiles
Item "/o:\"%s\"" param.ReportFileName
Item "/oXsl:\"%s\"" param.OutputXslFileName
Item "/plat:\"%s\"" param.PlatformDirectory
Item "/p:\"%s\"" param.ProjectFile
Item "/ruleset:=\"%s\"" param.CustomRuleset
ItemList "/r:\"%s\"" rules
ItemList "/rid:%s" param.Rules
Flag param.IgnoreGeneratedCode "/ignoregeneratedcode"
Flag param.IncludeSummaryReport "/s"
Item "/t:%s" (String.separated "," param.Types)
Flag param.SaveResultsInProjectFile "/u"
Flag param.Verbose "/v"
Flag param.UseGAC "/gac"
Item "/dic:\"%s\"" param.CustomDictionary ]
|> List.concat

let internal failAsrequired param result =
let ok = 0 = result.ExitCode
if not ok && (param.FailOnError >= ErrorLevel.ToolError) then
failwith "FxCop test failed."
if param.FailOnError > ErrorLevel.ToolError
&& param.ReportFileName <> String.Empty then
let criticalErrors, errors, criticalWarnings, warnings =
checkForErrors param.ReportFileName
if criticalErrors <> 0 && param.FailOnError >= ErrorLevel.CriticalError then
failwithf "FxCop found %d critical errors." criticalErrors
if errors <> 0 && param.FailOnError >= ErrorLevel.Error then
failwithf "FxCop found %d errors." errors
if criticalWarnings <> 0 && param.FailOnError >= ErrorLevel.CriticalWarning then
failwithf "FxCop found %d critical warnings." criticalWarnings
if warnings <> 0 && param.FailOnError >= ErrorLevel.Warning then
failwithf "FxCop found %d warnings." warnings

let internal composeCommandLine param assemblies =
let args = createArgs param assemblies
createProcess param args

/// Run FxCop on a group of assemblies.
let run param (assemblies : string seq) =
if Environment.isWindows then
use __ = Trace.traceTask "FxCop" ""
composeCommandLine param assemblies
|> Proc.run
|> failAsrequired param
__.MarkSuccess()
6 changes: 6 additions & 0 deletions src/app/Fake.DotNet.FxCop/VisibleTo.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace System
open System.Runtime.CompilerServices

[<assembly: InternalsVisibleTo("Fake.Core.IntegrationTests")>]
[<assembly: InternalsVisibleTo("Fake.Core.UnitTests")>]
do ()
5 changes: 5 additions & 0 deletions src/app/Fake.DotNet.FxCop/paket.references
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
group netcore

FSharp.Core
NETStandard.Library
BlackFox.VsWhere
Loading

0 comments on commit 4eadc22

Please sign in to comment.