I've been looking at ways to improve the reporting of Code Analysis as part of our Team Build. During my research I found that the RunCodeAnalysis setting as defined in the TFSBuild.proj differs significantly from the local MSBuild project schema options (see this post for a detailed break down of project level analysis settings).
Specifically, TFSBuild defines RunCodeAnalysis as "Always", "Default" and "Never" while MSBuild defines this as a simple true/false Boolean. To make sense of this I hunted this down to this section of the Microsoft.TeamFoundation.Build.targets file:
<Target Name="CoreCompileSolution"> <PropertyGroup> <CodeAnalysisOption Condition=" '$(RunCodeAnalysis)'=='Always'">RunCodeAnalysis=true</CodeAnalysisOption> <CodeAnalysisOption Condition=" '$(RunCodeAnalysis)'=='Never'">RunCodeAnalysis=false</CodeAnalysisOption> <!-- ... --> </PropertyGroup> <!-- ... --> </Target>
From this we can infer that "Default" setting does not provide a value to the runtime, while "Always" and "Never" map to True/False respectively. But this raises the question, what's the difference between Default and Always, and what should I specify to effectively run Code Analysis?
To answer this question, let's take an example of a small project with two projects. Both projects are configured in the csproj with default settings. For the purposes of the demo, I've altered the output folder to be at a folder called Output at the root of the solution:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>..\Output</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup>
When we specify code analysis to run with the "Always" setting:
Msbuild.exe Example.sln /p:RunCodeAnalysis=True
The Output folder contains the following files:
CodeAnalysisExperiment1.dll CodeAnalysisExperiment1.dll.CodeAnalysisLog.xml CodeAnalysisExperiment1.dll.lastcodeanalysissucceeded CodeAnalysisExperiment1.pdb CodeAnalysisExperiment2.dll CodeAnalysisExperiment2.dll.CodeAnalysisLog.xml CodeAnalysisExperiment2.dll.lastcodeanalysissucceeded CodeAnalysisExperiment2.pdb
Close inspection of the console output shows that the Minimal ruleset was applied to my empty projects, despite having not configured either project for code analysis.
If I tweak the project settings slightly such that one project explicitly declares RunCodeAnalysis as True and the other as False
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>..\Output</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <RunCodeAnalysis>false</RunCodeAnalysis> </PropertyGroup>
and…
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>..\Output</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <RunCodeAnalysis>true</RunCodeAnalysis> </PropertyGroup>
...and then specify the "Default" setting:
msbuild Example.sln
The Output folder now contains the following files:
CodeAnalysisExperiment1.dll CodeAnalysisExperiment1.dll.CodeAnalysisLog.xml CodeAnalysisExperiment1.dll.lastcodeanalysissucceeded CodeAnalysisExperiment1.pdb CodeAnalysisExperiment2.dll CodeAnalysisExperiment2.pdb
From this we can infer that the "Default" setting looks to the project settings to determine if analysis should be done, whereas the "Always" setting will override the analysis and "Never" will disable analysis altogether.
For my purposes, I have some projects (unit tests, etc) that I don't necessarily want to run code analysis on -- for me, "Always" is a bit of a deal breaker. However, there's an interesting lesson to take away here: command line arguments passed to msbuild are carried to all targets. Which means we can override most project settings this way:
// use 'all rules' for projects that have analysis turned on msbuild.exe Example.sln /p:CodeAnalysisRuleSet="Allrules.set" // use a custom dictionary during code analysis msbulid.exe Example.sln /p:CodeAnalysisDictionary="..\AnalysisDictionary.xml" // redirect output to a different folder msbulid.exe Example.sln /p:OutputPath="..\Output2"
Cheers!
Happy Coding.