- PVSM.RU - https://www.pvsm.ru -
Так получилось, что год назад, мне пришлось написать билд-план с использованием ant. Он предназначался для нашего небольшого веб-проекта, исполнялся на Hudson и должен был производить: компиляцию, прогон NUnit тестов, подсчет % покрытия кода тестам, поиск дублирующегося кода и выявление основных стилистических несоответствий в коде. Но это вступление, а далее поговорим, о написании билд-плана для анализа файлов проекта, посредством FxCop.
И так! Поехали!
Как водится, я разбил билд-план на несколько составных частей:
Такое модульное построение позволяет легко исключать отдельные части, разделенные по конкретной ответственности. Нам же с Вами, предстоит рассмотреть именно устройство fxcop.build.xml файла.
Сначала я пробовал передавать, список подготовленных путей до анализируемых файлов, посредством командной строки, но как показала практика, это занятие муторное и долгое. И не очень надежное, так как при расширении проекта, нужно будет обновлять и список файлов для анализа. Тогда я стал искать способы динамического формирования списка файлов и передачи FxCop посредством Ant. Так как анализируемых файлов было не мало, нужна была именно автоматическая система поиска нужных файлов и передача их FxCop. Покопавшись в интернете и почитав мануал по командам Ant-Contrib и Ant, нашел, то что надо. Именно команда subant позволила достичь поставленной цели. Но об этом ниже!
Рассмотрим устройство файла. В нем присутствуют несколько задач:
Далее рассмотрим основные команды Apache Ant для решения задачи.
basename [1] — позволяет получить имя файла с расширением из полного пути.
loadfile [2] — позволяет загрузить определенные данных из файла. В данном случае, таска используется для разбора proj файла .NET проекта.
subant [3] — позволяет выполнить таску из другого билд файла, в данном случае из динамически сформированного для FxCop
propertyregex [4] — позволяет выполнить выборку данных посредством заданного регулярного выражения на входной строке.
if [5] — позволяет добавить в билд файл логику выполнения зависящую от логических выражений.
Теперь можно перейти к рассмотрению каждой из задач отдельно.
<target name="write-head-part">
<echo file="${dynafile.path}${dynafilename}"><?xml version="1.0" encoding="UTF-8"?>
<project name="dyna-fxcop-build" default="run-fx-cop-report-creation" basedir=".">
<target name="run-fx-cop-report-creation">
<exec executable="${fxcop.path}FxCopCmd.exe" failonerror="false"></echo>
</target>
В данной задаче, производится запись стандартного заголовка билд-плана в файл с использованием < > для экранирования символов < >, в файл находящийся по пути ${dynafile.path}${dynafilename}. А так же производится запись задачи 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="<OutputPath>"/>
</linecontains>
</filterchain>
</loadfile>
//Загрузка строк содержащих <OutputType> из файла csproj в свойство output.type
<loadfile srcfile="${item.file}" property="output.type">
<filterchain>
<linecontains>
<contains value="<OutputType>"/>
</linecontains>
</filterchain>
</loadfile>
//Загрузка строк содержащих <AssemblyName> из файла csproj в свойство assembly.name
<loadfile srcfile="${item.file}" property="assembly.name">
<filterchain>
<linecontains>
<contains value="<AssemblyName>"/>
</linecontains>
</filterchain>
</loadfile>
//Выделение значения между открывающим и закрывающим тегом OutputPath и запись в output.path.info
<propertyregex property="output.path.info" input="${output.path}"
regexp="<OutputPath>(.*?)</OutputPath>" select="1" />
//Выделение значения между открывающим и закрывающим тегом OutputType и запись в output.type.info
<propertyregex property="output.type.info" input="${output.type}"
regexp="<OutputType>(.*?)</OutputType>" select="1" />
//Выделение значения между открывающим и закрывающим тегом AssemblyName и запись в assembly.name.info
<propertyregex property="assembly.name.info" input="${assembly.name}"
regexp="<AssemblyName>(.*?)</AssemblyName>" 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"> <arg value="/f:${item.path}${output.path.info}${file.name.ext}"/>
</echo>
</target>
В данной задаче производится обработка файлов проекта с расширением proj. Из файла выделяются данные тегов OutputPath, OutputType и AssemblyName для того, чтобы можно было не ориентироваться на название файла проекта (так как попадались такие файлы проектов в которых было изменено имя сборки). И в динамически создаваемый файл билд-плана записываются строки arg для задачи exec с указанием флага /f:.
<target name="write-footer-part">
<echo file="${dynafile.path}${dynafilename}" append="true"> <arg value="/r:${fxcop.path}Rules"/>
<arg value="/o:${fxcop.report.full.path}"/>
</exec>
</target>
</project></echo>
</target>
Эта задача производит запись заключительной части динамически формируемого билд-плана для FxCop, дописывая директивы FxCop, которые предназначены для указания пути папки с правилами /r:${fxcop.path}Rules и папки вывода отчета /o:${fxcop.report.full.path}. Так же производится запись закрывающих тегов для exec, traget и project.
<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}. Далее производится перевод файла в режим добавления данных в конец, посредством команды echo с параметром append="true".
После этих действий производится перебор файлов с расширением csproj при помощи foreach. При этом путь до файла записывается в переменную item.file, которая определена, посредством param="item.file". Ну и для того чтобы ant не просматривал содержимое obj/Debug, при помощи инструкции /> эта папка заносится в игнор.
Далее при помощи write-footer-part записывается заключительная часть динамически формируемого файла билда.
И теперь самое интересное! Так как мы, в динамическом билд-плане, создали задачу с именем run-fx-cop-report-creation, то теперь мы можем ее исполнить, посредством таски subant, с указанием пути до динамически сформированного файла билд-плана из которого собственно и будет выполнена указанная задача.
Надеюсь что данный материал был интересен :) Спасибо за внимание!
<?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="<OutputPath>"/>
</linecontains>
</filterchain>
</loadfile>
<loadfile srcfile="${item.file}" property="output.type">
<filterchain>
<linecontains>
<contains value="<OutputType>"/>
</linecontains>
</filterchain>
</loadfile>
<loadfile srcfile="${item.file}" property="assembly.name">
<filterchain>
<linecontains>
<contains value="<AssemblyName>"/>
</linecontains>
</filterchain>
</loadfile>
<propertyregex property="output.path.info" input="${output.path}"
regexp="<OutputPath>(.*?)</OutputPath>" select="1" />
<propertyregex property="output.type.info" input="${output.type}"
regexp="<OutputType>(.*?)</OutputType>" select="1" />
<propertyregex property="assembly.name.info" input="${assembly.name}"
regexp="<AssemblyName>(.*?)</AssemblyName>" 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"> <arg value="/f:${item.path}${output.path.info}${file.name.ext}"/>
</echo>
</target>
<target name="write-head-part">
<echo file="${dynafile.path}${dynafilename}"><?xml version="1.0" encoding="UTF-8"?>
<project name="dyna-fxcop-build" default="run-fx-cop-report-creation" basedir=".">
<target name="run-fx-cop-report-creation">
<exec executable="${fxcop.path}FxCopCmd.exe" failonerror="false"></echo>
</target>
<target name="write-footer-part">
<echo file="${dynafile.path}${dynafilename}" append="true"> <arg value="/r:${fxcop.path}Rules"/>
<arg value="/o:${fxcop.report.full.path}"/>
</exec>
</target>
</project></echo>
</target>
</project>
Автор: CyberLight
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/razrabotka/12974
Ссылки в тексте:
[1] basename: http://ant.apache.org/manual/Tasks/basename.html
[2] loadfile: http://ant.apache.org/manual/Tasks/loadfile.html
[3] subant: http://ant.apache.org/manual/Tasks/subant.html
[4] propertyregex: http://ant-contrib.sourceforge.net/tasks/tasks/propertyregex.html
[5] if: http://ant-contrib.sourceforge.net/tasks/tasks/if.html
Нажмите здесь для печати.