Préambule

Si vous vous souvenez de ce qu'on avait ajouté dans le fichier NAnt lors de la précédente partie, vous savez que nous avons dans le répertoire BuildOutput/net-4.0.win32-Emidee-debug/app toutes nos assemblies relatives aux programmes de notre solution. Je pense que vous serez d'accord avec moi pour penser que seules les assemblies des programmes méritent une analyse de code. Nous nous limiterons donc à ce dossier.

Le fichier NAnt

Lors de mes recherches sur le sujet, j'ai vu qu'il existait un projet nommé NAntContrib, dont on place les fichiers dans le même répertoire que l'exécutable de NAnt, qui ajoute toute une série de tâches supplémentaires à NAnt, non prises en charge initialement, comme par exemple la génération de fichiers d'aide au format Microsoft, l'utilisation de divers CVS, etc... (La liste de toutes les tâches se trouve ici). L'une d'elles a particulièrement attiré mon attention, puisqu'il s'agit d'une tâche permettant d'utiliser, vous l'aurez deviné, FxCop. Malheureusement, je n'ai jamais su la faire fonctionner: impossible pour la tâche de trouver le chemin de l'exécutable. J'ai donc fini par lancer l'analyse via une tâche normale, en attaquant directement FxCopCmd, qui est la console fournie avec FxCop, et en lui passant les bons arguments.

La tâche que nous allons créer a 2 impératifs qu'on devine facilement: les sources doivent avoir été compilées, et les assemblies doivent avoir été copiées dans les bons dossiers. Et les arguments que nous allons passer à FxCopCmd sont eux-aussi simples à mettre en oeuvre:

  • le flag f pour spécifier les assemblies à tester
  • le flag out pour spécifier le fichier de sortie de l'analyse

Ce qui nous donne les ajouts suivants à notre fichier NAnt:

<property name="fxcop.dir" value="C:\Program Files\Microsoft FxCop 1.36" dynamic="true" />
<property name="fxcopcmd.name" value="fxcopcmd.exe" />
<property name="fxcopcmd.path" value="${fxcop.dir}\fxcopcmd.exe" />
<property name="fxcop.outfile.name" value="fxcop.xml" />
<property name="fxcop.output.dir" value="${build.output.dir}/FXCopOutput" />

<target name="run.fxcop" depends="compile.sources, move.assemblies" failonerror="false">
<exec program="${fxcopcmd.path}">
  <arg value="/f:${app.assemblies}" />
  <arg value="/out:${fxcop.output.dir}/${fxcop.outfile.name}" />
</exec>

Le souci lorsqu'on exécute cette tâche est que comme mes assemblies sont compilées avec le framework .NET 4, FxCop ne va pas trouver certaines assemblies:

NAnt 0.86 (Build 0.86.2898.0; beta1; 8/12/2007)
Copyright (C) 2001-2007 Gerry Shaw
http://nant.sourceforge.net

Buildfile: file:///D:/__PROG STUFF__/VS/2010/Projects/emidee/tools/emidee.build
Target framework: Microsoft .NET Framework 4.0
Target(s) specified: run.fxcop

[loadtasks] Scanning assembly "NCover.NAntTasks" for extensions.
   [script] Scanning assembly "ze5ejqtg" for extensions.

run.fxcop:

     [exec] Microsoft (R) FxCop Command-Line Tool, Version 1.36 (9.0.30729.1)
     [exec] Copyright (C) 2007 Microsoft Corporation.  All rights reserved.
     [exec]
     [exec] Loaded DesignRules.dll...
     [exec] Loaded GlobalizationRules.dll...
     [exec] Loaded InteroperabilityRules.dll...
     [exec] Loaded MobilityRules.dll...
     [exec] Loaded NamingRules.dll...
     [exec] Loaded PerformanceRules.dll...
     [exec] Loaded PortabilityRules.dll...
     [exec] Loaded SecurityRules.dll...
     [exec] Loaded UsageRules.dll...
     [exec] Using system files at: C:\Windows\Microsoft.NET\Framework\v4.0.21006.
     [exec] Loaded Emidee.Basket.exe...
     [exec] Loaded Emidee.Commons.dll...
     [exec] Loaded Emidee.Commons.GUI.dll...
     [exec] Loaded Emidee.Log.dll...
     [exec] Loaded Emidee.Updater.dll...
     [exec] Initializing Introspection engine...
     [exec] Using system files at: C:\Windows\Microsoft.NET\Framework\v4.0.21006.
     [exec] Could not resolve reference to PresentationFramework.
     [exec] Analysis Complete.
     [exec]
     [exec] NOTE: One or more referenced assemblies could not be found. Use the '/directory' switch to specify additional assembly reference search pa
ths.
     [exec]
     [exec] * Analysis was not performed; at least one valid rules assembly and one valid
     [exec] target file must be specified.
     [exec] * 2 total analysis engine exceptions.
     [exec] Writing report to D:\__PROG STUFF__\VS\2010\Projects\emidee\BuildOutput\FXCopOutput\fxcop.xml...
     [exec] Done.

BUILD FAILED - 0 non-fatal error(s), 3 warning(s)

D:\__PROG STUFF__\VS\2010\Projects\emidee\tools\emidee.build(181,6):
External Program Failed: C:\Program Files\Microsoft FxCop 1.36\fxcopcmd.exe (return code was 513)

Total time: 2.6 seconds.

Il nous suffit donc d'ajouter un nouveau flag à notre commande, qui va indiquer à FxCopCmd où chercher pour les assemblies .NET 4 qu'il ne trouve pas:

<property name="fxcop.assembly.dependencies" value="C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\" />

<target name="run.fxcop" depends="compile.sources, move.assemblies" failonerror="false">
  <exec program="${fxcopcmd.path}">
    <arg value="/f:${app.assemblies}" />
    <arg value="/out:${fxcop.output.dir}/${fxcop.outfile.name}" />
    <arg value="/d:${fxcop.assembly.dependencies}" />
  </exec>
</target>

Et voilà le travail! Vous avez dans votre dossier BuildOutput/FxCopOutput/ un fichier nommé fxcop.xml qui va contenir tous les messages de FxCop.

Sauf que... oui, il reste une dernière amélioration à apporter à cette tâche. Si vous utilisez l'application FxCop traditionnelle, vous avez peut-être choisi d'exclure certains avertissements dans la liste proposée. Par exemple certains avertissements concernant la casse de certains membres de vos classes. Avec la version graphique de FxCop, tout va bien, il ne vous affiche plus ce que vous avez choisi d'exclure de l'analyse (heureusement d'ailleurs) car il a sauvegardé tout ça dans le fichier FxCop. Mais la version console, par défaut, continuera de vous spammer avec tous les messages. La solution à ce petit souci est d'ajouter un nouvel argument à la tâche (/i), qui pointera vers votre fichier FxCop. Ainsi la console va prendre en compte les exclusions de messages qui y auront été enregistrés et ne vous embêtera plus avec ça.

Le plus simple est donc d'enregistrer votre fichier FxCop dans le répertoire Tools de votre solution (donc au même endroit que le fichier NAnt). Puis de modifier le fichier de build:

<property name="fxcop.project.dir" value="${solution.dir}\Tools" />
<property name="fxcop.project.file.path" value="${fxcop.project.dir}\${fxcop.project.file.name}" />

<target name="run.fxcop" depends="compile.sources, move.assemblies" failonerror="false">
  <exec program="${fxcopcmd.path}">
    <arg value="/f:${app.assemblies}" />
    <arg value="/i:${fxcop.project.file.path}" />
    <arg value="/out:${fxcop.output.dir}/${fxcop.outfile.name}" />
    <arg value="/d:${fxcop.assembly.dependencies}" />
  </exec>
</target>

Configurer Hudson

Comme pour les tests unitaires, nous allons passer par un plugin pour afficher le rapport d'erreurs de FxCop. Ledit plugin s'appelant Violations. Après l'avoir installé, vous avez une nouvelle ligne dans la configuration de votre projet, intitulée Report Violations. En cochant la case vous faites apparaître toute une section, que vous n'avez qu'à configurer comme suit:

HudsonViolationsConfiguration.PNG

Rien de bien compliqué donc, vous n'avez qu'à spécifier le chemin vers le fichier XML. Vous pouvez changer les valeurs des colonnes pour spécifier le nombre de messages au dessus duquel l'état de votre build va changer.

Une fois que vous avez lancé un nouveau build dans Hudson, et que tout s'est bien déroulé, vous pourrez cliquez sur le lien Violations sur la page de votre projet pour y voir tous les messages de FxCop.

HudsonViolationsLink.PNG

Et comme pour les tests unitaires, Hudson va garder en mémoire les résultats des cette analyse au fur et à mesure des builds, et vous proposera un graphique avec l'évolution du nombre de messages, et de leur type:

HudsonViolationsSummary.PNG

Si vous cliquez sur un des fichiers contenant des messages, vous avez tous les messages relatifs à ce fichier.

HudsonViolationsFileSummary.PNG

Petite précision utile: les liens de cette page pointent directement vers la page MSDN expliquant le message et sa possible résolution.

Conclusion

Notre procédure de compilation s'améliore et se complexifie au fur et à mesure. Nous sommes maintenant capables de lancer les tests unitaires de la solution, et de lancer une analyse de code sur les assemblies. Nous verrons dans la prochaine partie comment utiliser un outil de couverture de code, qui nous permettra de savoir si nos tests unitaires couvrent bien à 100% toutes les classes et fonctions du code de la solution, grâce à l'utilisation de nCover.