Tuesday, September 2, 2008

Automating the Checking of Standards and Best Practices

During the past few months I have been taking care of some developer "ecosystem" tasks at work. You know, all those tedious first-time tasks that nobody looks forward to doing but everyone should see the value of having them done. Things that increase a team's sustainability. Things like setting up a CI build, dealing with tests that have been ostracized for over a year, converting projects to the latest IDE and framework, making sure project files are set to create debug symbols properly, re-checking the "run code analysis on build" setting, deleting the slanderous comments that blame me - I'm sure you get the picture.

One of the more interesting challenges in this endeavour has been trying to create what is essentially automatically enforced Standards and Best Practices (S&BPs) to be used by the entire group. The IDE and Team System can assist in that enforcement with Code Analysis rules, Team System check-in policies, and automatically executed tests. However, if you're a .Net developer you probably know the disdain that some of us have for enforcing the full multitude of available Static Analysis rules. It's a fairly common occurrence to find the rules to be unchecked of the "run code analysis on build" setting to be turned off, or simply that developers suppress all violations with a highly tuned reflex of the mouse-button finger. Additionally, I've never seen anyone spend the time to fix all the code analysis warnings, because they don't "stop the line" by breaking the build.

One FxCop file for all Visual Studio Projects

There is a way to have a single shared FxCop rule file that all projects reference, that project files can't override, and that role-base security can be applied. And it's easy to implement.

  • Create a StaticAnalysis.targets file with the content below
  • Put the file somewhere convenient in TFS source control
  • Change the source control permissions of that file to be limited
  • Edit each csproj or vbproj file in notepad and add the following line just before the </Import Project="$(MSbuildBinPath)\Microsoft.CSharp.targets" /> tag - placement is important for overriding the specific project's Static Analysis settings (c# version shown here)
<import project="[Relative TFS Work Space Path to Project]\StaticAnalysis.targets">

  • Build
  • Make sure your build server will properly get the StaticAnalysis.targets file to the location you referenced in your vbproj or csproj
  • Check-In
  • Notify your team that they need to get latest of the code and the location where you checked in the StaticAnalysis.targets file
What's in the file

The top line includes compiler warning numbers that we want to be treated as errors so that violations of them will "stop the line" and not just be ignored. Most of the numbers below are for XML comment issues. If you want to treat these XML comment problems as errors, consider this macro to help you clean up existing issues. You can get the number from warning statements in the output window when you compile.

The second section is the list of FxCop rules turned on as a waning (+), on as an error (+!) or off (-).

The File


<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WarningsAsErrors>1572,1591,0105,1574,0649</WarningsAsErrors>
<CodeAnalysisRules>
+!JBBrown.FxCop.Rules#JB1502;
+!JBBrown.FxCop.Rules#JB1505;


+!Microsoft.Usage#CA2209;
+!Microsoft.Usage#CA2215;
+!Microsoft.Usage#CA2202;
+!Microsoft.Usage#CA2241;
+!Microsoft.Design#CA1012;
+!Microsoft.Design#CA2210;
+!Microsoft.Design#CA1040;
+!Microsoft.Design#CA1005;
-!Microsoft.Design#CA1020;
+!Microsoft.Design#CA1021;
+!Microsoft.Design#CA1010;
+!Microsoft.Design#CA1011;
+!Microsoft.Design#CA1009;
+!Microsoft.Design#CA1050;
+!Microsoft.Design#CA1026;
+!Microsoft.Design#CA1019;
+!Microsoft.Design#CA1031;
+!Microsoft.Design#CA1047;
+!Microsoft.Design#CA1000;
+!Microsoft.Design#CA1048;
+!Microsoft.Design#CA1051;
+!Microsoft.Design#CA1002;
+!Microsoft.Design#CA1061;
+!Microsoft.Design#CA1006;
+!Microsoft.Design#CA1046;
+!Microsoft.Design#CA1045;
+!Microsoft.Design#CA1038;
+!Microsoft.Design#CA1008;
+!Microsoft.Design#CA1028;
+!Microsoft.Design#CA1004;
+!Microsoft.Design#CA1035;
+!Microsoft.Design#CA1063;
+!Microsoft.Design#CA1032;
+!Microsoft.Design#CA1023;
+!Microsoft.Design#CA1033;
+!Microsoft.Design#CA1039;
-!Microsoft.Design#CA1016;
+!Microsoft.Design#CA1014;
+!Microsoft.Design#CA1017;
+!Microsoft.Design#CA1018;
+!Microsoft.Design#CA1027;
+!Microsoft.Design#CA1059;
+!Microsoft.Design#CA1060;
+!Microsoft.Design#CA1034;
+!Microsoft.Design#CA1013;
+!Microsoft.Design#CA1036;
+!Microsoft.Design#CA1044;
+!Microsoft.Design#CA1041;
+!Microsoft.Design#CA1025;
+!Microsoft.Design#CA1052;
+!Microsoft.Design#CA1053;
+!Microsoft.Design#CA1057;
+!Microsoft.Design#CA1058;
+!Microsoft.Design#CA1001;
+!Microsoft.Design#CA1049;
+!Microsoft.Design#CA1054;
+!Microsoft.Design#CA1056;
+!Microsoft.Design#CA1055;
+!Microsoft.Design#CA1030;
+!Microsoft.Design#CA1003;
+!Microsoft.Design#CA1007;
+!Microsoft.Design#CA1043;
+!Microsoft.Design#CA1024;
+!Microsoft.Design#CA1062;

-Microsoft.Globalization#CA1301;
-Microsoft.Globalization#CA1302;
-Microsoft.Globalization#CA1303;
-Microsoft.Globalization#CA1304;
-Microsoft.Globalization#CA1305;
-Microsoft.Globalization#CA1306;
+!Microsoft.Globalization#CA2101;
-Microsoft.Globalization#CA1300;

-!Microsoft.Interoperability#CA1403;
-!Microsoft.Interoperability#CA1406;
-!Microsoft.Interoperability#CA1413;
-!Microsoft.Interoperability#CA1402;
-!Microsoft.Interoperability#CA1407;
-!Microsoft.Interoperability#CA1404;
-!Microsoft.Interoperability#CA1410;
-!Microsoft.Interoperability#CA1411;
-!Microsoft.Interoperability#CA1405;
-!Microsoft.Interoperability#CA1409;
-!Microsoft.Interoperability#CA1415;
-!Microsoft.Interoperability#CA1408;
-!Microsoft.Interoperability#CA1414;
-!Microsoft.Interoperability#CA1412;
-!Microsoft.Interoperability#CA1400;
-!Microsoft.Interoperability#CA1401;

-!Microsoft.Mobility#CA1600;
-!Microsoft.Mobility#CA1601;

+!Microsoft.Naming#CA1700;
+!Microsoft.Naming#CA1712;
+!Microsoft.Naming#CA1713;
+!Microsoft.Naming#CA1709;
+!Microsoft.Naming#CA1708;
+!Microsoft.Naming#CA1715;
+!Microsoft.Naming#CA1710;
+!Microsoft.Naming#CA1720;
-!Microsoft.Naming#CA1707;
+!Microsoft.Naming#CA1722;
+!Microsoft.Naming#CA1711;
+!Microsoft.Naming#CA1716;
+!Microsoft.Naming#CA1725;
+!Microsoft.Naming#CA1719;
+!Microsoft.Naming#CA1721;
+!Microsoft.Naming#CA1724;
+!Microsoft.Naming#CA1726;

+!Microsoft.Maintainability#CA1501;
+!Microsoft.Maintainability#CA1500;
-Microsoft.Maintainability#CA1502;
-Microsoft.Maintainability#CA1505;

+!Microsoft.Performance#CA1809;
+!Microsoft.Performance#CA1811;
+!Microsoft.Performance#CA1812;
+!Microsoft.Performance#CA1813;
+!Microsoft.Performance#CA1823;
+!Microsoft.Performance#CA1800;
+!Microsoft.Performance#CA1805;
+!Microsoft.Performance#CA1810;
+!Microsoft.Performance#CA1822;
+!Microsoft.Performance#CA1815;
+!Microsoft.Performance#CA1814;
+!Microsoft.Performance#CA1819;
+!Microsoft.Performance#CA1804;
+!Microsoft.Performance#CA1820;
+!Microsoft.Performance#CA1802;
+!Microsoft.Performance#CA1807;
+!Microsoft.Performance#CA1817;
+!Microsoft.Performance#CA1818;

-!Microsoft.Portability#CA1901;
-!Microsoft.Portability#CA1900;

+!Microsoft.Reliability#CA2002;
+!Microsoft.Reliability#CA2003;
+!Microsoft.Reliability#CA2004;
+!Microsoft.Reliability#CA2006;
+!Microsoft.Reliability#CA2000;

+!Microsoft.Security#CA2116;
+!Microsoft.Security#CA2117;
+!Microsoft.Security#CA2105;
+!Microsoft.Security#CA2115;
+!Microsoft.Security#CA2104;
+!Microsoft.Security#CA2122;
+!Microsoft.Security#CA2114;
+!Microsoft.Security#CA2123;
+!Microsoft.Security#CA2111;
+!Microsoft.Security#CA2108;
+!Microsoft.Security#CA2107;
+!Microsoft.Security#CA2103;
+!Microsoft.Security#CA2118;
+!Microsoft.Security#CA2109;
+!Microsoft.Security#CA2119;
+!Microsoft.Security#CA2106;
+!Microsoft.Security#CA2112;
+!Microsoft.Security#CA2120;
+!Microsoft.Security#CA2121;
+!Microsoft.Security#CA2126;
+!Microsoft.Security#CA2124;
+!Microsoft.Security#CA2100;

+!Microsoft.Usage#CA2236;
+!Microsoft.Usage#CA1816;
+!Microsoft.Usage#CA2227;
+!Microsoft.Usage#CA2213;
+!Microsoft.Usage#CA2216;
+!Microsoft.Usage#CA2214;
+!Microsoft.Usage#CA2222;
+!Microsoft.Usage#CA1806;
+!Microsoft.Usage#CA2217;
+!Microsoft.Usage#CA2212;
+!Microsoft.Usage#CA2219;
+!Microsoft.Usage#CA2201;
+!Microsoft.Usage#CA2228;
+!Microsoft.Usage#CA2221;
+!Microsoft.Usage#CA2220;
+!Microsoft.Usage#CA2240;
+!Microsoft.Usage#CA2229;
+!Microsoft.Usage#CA2238;
+!Microsoft.Usage#CA2207;
+!Microsoft.Usage#CA2208;
+!Microsoft.Usage#CA2235;
+!Microsoft.Usage#CA2237;
+!Microsoft.Usage#CA2232;
+!Microsoft.Usage#CA2223;
+!Microsoft.Usage#CA2211;
+!Microsoft.Usage#CA2233;
+!Microsoft.Usage#CA2225;
+!Microsoft.Usage#CA2226;
+!Microsoft.Usage#CA2231;
+!Microsoft.Usage#CA2224;
+!Microsoft.Usage#CA2218;
+!Microsoft.Usage#CA2234;
+!Microsoft.Usage#CA2239;
+!Microsoft.Usage#CA2200;
+!Microsoft.Usage#CA1801;
+!Microsoft.Usage#CA2205;
+!Microsoft.Usage#CA2230;
</codeanalysisrules>
<RunCodeAnalysis>true</RunCodeAnalysis>
</PropertyGroup>
</Project>

7 comments:

xidey said...

Very timely post for me - I'll soon be looking into integrating FxCop into our CC.NET builds. I'll let you know my experience with your suggestions.

mkamoski said...

Is this still the best way to share a common rules file for FxCop 1.36 now? I am just checking. Please advise. Thank you. -- Mark Kamoski

mkamoski said...

JB -- Is it OK to remove this... +!JBBrown.FxCop.Rules#JB1502; ...and this... +!JBBrown.FxCop.Rules#JB1505; ...from the file. I am guessing "yes" but I want to check. What do you think? Please advise. Thank you. -- Mark Kamoski

mkamoski said...

JB --

Regarding your excellent article, http://www.controlstatements.com/2008/07/automating-checking-of-standards-and.html , this is a follow-up regarding your statemtent...

"add the following line just before the ...Microsoft.CSharp.targets... tag - placement is important for overriding the specific project's Static Analysis settings"

...and I am wondering if that is supposed to work if one has already added a target for StyleCope, which is already just above the CSharp.targets tag.

So, are StyleCop and FxCop project file edits compatible?

Please advise.

Thank you.

-- Mark Kamoski

mkamoski said...

All --

FYI, it looks like there is a typo in the XML shown above.

The following error appears...

"The imported project file could not be loaded. The 'CodeAnalysisRules' start tag on line 4 does not match the end tag of 'codeanalysisrules'

...so I just thought I would mention it.

HTH.

Thank you.

-- Mark Kamoski

mkamoski said...

JB --

You say...

"The second section is the list of FxCop rules turned on as a waning (+), on as an error (+!) or off (-)"

...but you do not, AFAICT, let us know what (-!) means...

...so, what do you say?

Please advise.

Thank you.

-- Mark Kamoski

mkamoski said...

JB --

Please help.

I am trying to turn off rule CA1704, as a test.

As such, I added this line...

-Microsoft.Naming#CA1704;

...to the targets file but that rule is still fired when I run FxCop from its GUI so is there a trick to this?

Please advise.

Thank you.

-- Mark Kamoski