FxCopを使用して.NETプロジェクトファイルを分析するためのビルドプランを動的に作成する



1年前に、antを使用してビルドプランを作成する必要が生じました。 Hudsonで実行される小さなWebプロジェクトを対象としており、コンパイル、NUnitテストの実行、テスト用のコードのカバレッジ率のカウント、重複コードの検出、コード内の基本的なスタイルの不一致の特定を生成する必要がありました。 しかし、これは序論であり、FxCopを使用してプロジェクトファイルを分析するためのビルドプランの作成について説明します。



それで! 行こう!







入門





いつものように、ビルドプランをいくつかのコンポーネントに分割しました。



  1. dbdeploy.build.xml-テストデータベースの作成と表示されるスクリプトのロールアップを担当
  2. fxcop.build.xml-プロジェクトファイルの分析とFxCopによる処理を開始し、見つかった問題に関するレポートを作成します。
  3. main.build.xml-ここでは、設定を記入するためにメインアクションが実行され、アセンブリのslnファイルを自動的に検索します
  4. ncover.build.xml-このパートでは、テストでコードのカバレッジに基づいてレポートが構築されます
  5. simian.build.xml-コード内の重複に関するレポートです。
  6. tests.build.xml-それでは 、プロジェクトフォルダー内のすべてのNUnitテストを検索して開始します




このようなモジュール構造により、特定の責任で分離された個々の部品を簡単に除外できます。 あなたと私にとって、考慮されるのはfxcop.build.xmlファイルのデバイス次第です。



さあ始めましょう





最初に、コマンドラインを使用して、分析されたファイルへの準備されたパスのリストを転送しようとしましたが、実践が示しているように、これは退屈で長い練習です。 また、プロジェクトを展開するとき、分析のためにファイルのリストを更新する必要があるため、あまり信頼できません。 その後、ファイルのリストを動的に作成し、Antを介してFxCopを転送する方法を探し始めました。 分析されたファイルが多数あったため、必要なファイルを見つけてFxCopに転送するための正確な自動システムが必要でした。 インターネットで調べて、Ant-ContribとAntコマンドのマニュアルを読んで、必要なものを見つけました。 目標を達成することができたのは、サブチームでした。 しかし、それについては以下をご覧ください!



実装




ファイルデバイスを検討します。 いくつかのタスクが含まれています。







次に、問題を解決するための基本的なApache Antコマンドを検討します。



basename-フルパスから拡張子付きのファイル名を取得できます。

loadfile-ファイルから特定のデータをロードできます。 この場合、タスクは.NETプロジェクトファイルのprojを解析するために使用されます。

subant-別のビルドファイル、この場合はFxCop用に動的に生成されたファイルからタスクを実行できます

propertyregex-入力行で指定された正規表現を使用してデータを選択できます。

if-論理式に応じた実行ロジックをビルドファイルに追加できます。



これで、各タスクを個別に検討することができます。



書き込みヘッド部分




<target name="write-head-part"> <echo file="${dynafile.path}\${dynafilename}">&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;project name="dyna-fxcop-build" default="run-fx-cop-report-creation" basedir="."&gt; &lt;target name="run-fx-cop-report-creation"&gt; &lt;exec executable="${fxcop.path}\FxCopCmd.exe" failonerror="false"&gt;</echo> </target>
      
      







このタスクでは、ビルドプランの標準ヘッダーが、パス${dynafile.path}\${dynafilename}



にあるファイルに書き込まれます&lt; &gt;



&lt; &gt;



、文字< >



をエスケープします。 また、 exec



タスクを記録して、必要なパラメーターをFxCopアプリケーションに渡します。 このようにして、 arg



を介してパラメーターを渡すことにより、分析されたファイルへのパスの長いリストの問題を解決できます。



作成引数




  <target name="create-arguments"> //        <echo message="${item.file}"/> //    ,     filename <basename property="filename" file="${item.file}"/> //   output.path   <OutputPath>   csproj <loadfile srcfile="${item.file}" property="output.path"> <filterchain> <linecontains> <contains value="&lt;OutputPath&gt;"/> </linecontains> </filterchain> </loadfile> //   <OutputType>   csproj   output.type <loadfile srcfile="${item.file}" property="output.type"> <filterchain> <linecontains> <contains value="&lt;OutputType&gt;"/> </linecontains> </filterchain> </loadfile> //   <AssemblyName>   csproj   assembly.name <loadfile srcfile="${item.file}" property="assembly.name"> <filterchain> <linecontains> <contains value="&lt;AssemblyName&gt;"/> </linecontains> </filterchain> </loadfile> //       OutputPath    output.path.info <propertyregex property="output.path.info" input="${output.path}" regexp="&lt;OutputPath&gt;(.*?)&lt;/OutputPath&gt;" select="\1" /> //       OutputType    output.type.info <propertyregex property="output.type.info" input="${output.type}" regexp="&lt;OutputType&gt;(.*?)&lt;/OutputType&gt;" select="\1" /> //       AssemblyName    assembly.name.info <propertyregex property="assembly.name.info" input="${assembly.name}" regexp="&lt;AssemblyName&gt;(.*?)&lt;/AssemblyName&gt;" select="\1" /> //       <propertyregex property="item.path" input="${item.file}" regexp="(.*)\\" select="\1" /> <echo message="output.type.info = ${output.type.info}"/> <echo message="output.path = ${output.path}"/> //         output.type.info <if> <contains string="WinExe" substring="${output.type.info}"/> <then> <property name="file.name.ext" value="${assembly.name.info}.exe"/> </then> <elseif> <contains string="Exe" substring="${output.type.info}"/> <then> <property name="file.name.ext" value="${assembly.name.info}.exe"/> </then> </elseif> <else> <property name="file.name.ext" value="${assembly.name.info}.dll"/> </else> </if> //  <arg value=""/>    value      . <echo file="${dynafile.path}\${dynafilename}" append="true"> &lt;arg value="/f:${item.path}\${output.path.info}${file.name.ext}"/&gt; </echo> </target>
      
      







このタスクでは、拡張子がcsprojのプロジェクトファイルの処理が実行されます。 ファイルから、タグデータが強調表示されます:OutputPath、OutputType、およびAssemblyName。 これは、プロジェクトファイルの名前に集中しないようにするために必要です(アセンブリ名が変更されたプロジェクトファイルがあるため)。 また、ビルドプランの動的に作成されたファイルには、 execタスクのarg行が/ f:フラグが指定されて書き込まれます



書き込みフッター部分




 <target name="write-footer-part"> <echo file="${dynafile.path}\${dynafilename}" append="true"> &lt;arg value="/r:${fxcop.path}\Rules"/&gt; &lt;arg value="/o:${fxcop.report.full.path}"/&gt; &lt;/exec&gt; &lt;/target&gt; &lt;/project&gt;</echo> </target>
      
      







このタスクは、ルールフォルダー/r:${fxcop.path}\Rules



およびレポート出力フォルダー/o:${fxcop.report.full.path}



へのパスを設定するためのFxCopディレクティブを追加して、動的に生成されたビルドプランである最終部分を記録します/o:${fxcop.report.full.path}



exec



traget



project



の終了タグもtraget



ます。



run-fxcop




 <target name="run-fxcop"> //   - <antcall target="write-head-part"/> //        <echo file="${dynafile.path}\${dynafilename}" append="true"> </echo> <var name="dll.names" value=""/> //  csproj    ,     ,    item.file, //  create-arguments <foreach target="create-arguments" param="item.file" inheritall="true"> <fileset dir="${basedir}" casesensitive="no"> <include name="**/*.csproj"/> //      /obj/Debug/ <exclude name="**/obj/Debug/**.*"/> </fileset> </foreach> //    - <antcall target="write-footer-part"/> //     -. <subant target="run-fx-cop-report-creation"> <fileset dir="${dynafile.path}" includes="${dynafilename}"/> </subant> </target>
      
      







さて、FxCopビルドプランの最も重要なタスクは、サブビルドプランを動的に作成することです。 まさにそのようなアプローチを使用して、.NETプロジェクトのファイルの分析が実行されます。 このタスクでは、 write-head-partを使用して、パス${dynafile.path}\${dynafilename}



沿って作成されたファイルにヘッダーが書き込まれます。 次に、ファイルは、 append="true"



パラメーターを指定したechoコマンドを使用して、データを最後に追加するモードに転送されます。



これらの手順の後、 foreach



を使用してcsproj



拡張子を持つファイルを反復処理します。 この場合、ファイルへのパスはparam="item.file"



定義される変数item.fileに書き込まれます。 さて、antがobj / Debugの内容を表示しないように、 />



命令を使用して、無視しました。



次に、 write-footer-part



を使用しwrite-footer-part



、動的に生成されたビルドファイルの最後の部分が書き込まれます。



そして今、楽しい部分です! 動的ビルドプランでrun-fx-cop-report-creation



というタスクを作成したため、subant taskを使用して実行できるようになりました。 subantのパラメーターで、 run-fx-cop-report-creation



タスクが実行されるビルドプランの動的に生成されたファイルへのパスを示します。



おわりに





この資料が面白かったと思います:)ご清聴ありがとうございました!



FxCopの完全なXMLビルドファイルコード
 <?xml version="1.0" encoding="UTF-8"?> <project name="fxcop-xxx-project" default="run-fxcop" basedir="."> <property name="dynafile.path" value="${basedir}"/> <property name="dynafilename" value="dynabuild.xml"/> <property name="fxcop.report.dir" value="${basedir}\FxCopReports"/> <property name="fxcop.report.full.path" value="${fxcop.report.dir}\fxcop.report.xml"/> <target name="clean-fxcop-result-folder"> <echo message="Cleaning FxCop result report dir, and dynamic xml"/> <delete> <fileset dir="${fxcop.report.dir}" includes="**/*.*"/> </delete> <delete file="${dynafile.path}\${dynafilename}" failonerror="false"/> </target> <target name="run-fxcop"> <antcall target="write-head-part"/> <echo file="${dynafile.path}\${dynafilename}" append="true"> </echo> <var name="dll.names" value=""/> <foreach target="create-arguments" param="item.file" inheritall="true"> <fileset dir="${basedir}" casesensitive="no"> <include name="**/*.csproj"/> <exclude name="**/obj/Debug/**.*"/> </fileset> </foreach> <antcall target="write-footer-part"/> <subant target="run-fx-cop-report-creation"> <fileset dir="${dynafile.path}" includes="${dynafilename}"/> </subant> </target> <target name="create-arguments"> <echo message="${item.file}"/> <basename property="filename" file="${item.file}"/> <loadfile srcfile="${item.file}" property="output.path"> <filterchain> <linecontains> <contains value="&lt;OutputPath&gt;"/> </linecontains> </filterchain> </loadfile> <loadfile srcfile="${item.file}" property="output.type"> <filterchain> <linecontains> <contains value="&lt;OutputType&gt;"/> </linecontains> </filterchain> </loadfile> <loadfile srcfile="${item.file}" property="assembly.name"> <filterchain> <linecontains> <contains value="&lt;AssemblyName&gt;"/> </linecontains> </filterchain> </loadfile> <propertyregex property="output.path.info" input="${output.path}" regexp="&lt;OutputPath&gt;(.*?)&lt;/OutputPath&gt;" select="\1" /> <propertyregex property="output.type.info" input="${output.type}" regexp="&lt;OutputType&gt;(.*?)&lt;/OutputType&gt;" select="\1" /> <propertyregex property="assembly.name.info" input="${assembly.name}" regexp="&lt;AssemblyName&gt;(.*?)&lt;/AssemblyName&gt;" select="\1" /> <propertyregex property="item.path" input="${item.file}" regexp="(.*)\\" select="\1" /> <echo message="output.type.info = ${output.type.info}"/> <echo message="output.path = ${output.path}"/> <if> <contains string="WinExe" substring="${output.type.info}"/> <then> <property name="file.name.ext" value="${assembly.name.info}.exe"/> </then> <elseif> <contains string="Exe" substring="${output.type.info}"/> <then> <property name="file.name.ext" value="${assembly.name.info}.exe"/> </then> </elseif> <else> <property name="file.name.ext" value="${assembly.name.info}.dll"/> </else> </if> <echo file="${dynafile.path}\${dynafilename}" append="true"> &lt;arg value="/f:${item.path}\${output.path.info}${file.name.ext}"/&gt; </echo> </target> <target name="write-head-part"> <echo file="${dynafile.path}\${dynafilename}">&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;project name="dyna-fxcop-build" default="run-fx-cop-report-creation" basedir="."&gt; &lt;target name="run-fx-cop-report-creation"&gt; &lt;exec executable="${fxcop.path}\FxCopCmd.exe" failonerror="false"&gt;</echo> </target> <target name="write-footer-part"> <echo file="${dynafile.path}\${dynafilename}" append="true"> &lt;arg value="/r:${fxcop.path}\Rules"/&gt; &lt;arg value="/o:${fxcop.report.full.path}"/&gt; &lt;/exec&gt; &lt;/target&gt; &lt;/project&gt;</echo> </target> </project>
      
      








All Articles