муравьиные условные цели и «рекурсия» - PullRequest
1 голос
/ 07 октября 2011

Я довольно новичок в муравье, и я видел эпизод дядюшки Боба "пока не уронишь".

В результате я стараюсь определить муравьиные цели как можно меньше, чтобы вы могли точно видеть суть цели, и не более.Для более подробной информации, вы должны обратиться к подцелям.

Является ли это хорошим или плохим стилем, это другая дискуссия (или, может быть, огненная война).

Поэтому я создавалскрипт компоновки, который в псевдокоде будет выглядеть так:

build =
   compile
   instrument if coverage

Задача coverage тоже разбита на подзадачи:

coverage:
   create-coverage-dirs
   call-cobertura

РЕДАКТИРОВАТЬ - я хочу выразитьчто coverage подцели не должны выполняться.

Но ... мне трудно выразить это "чисто" в анти-ese.

Предполагая, что я могу использоватьатрибут depends для обозначения ... межцелевых зависимостей, я получил что-то вроде этого:

<target name="build" depends="compile, coverage"/>

<target name="compile"> .... </target>

<target name="coverage" depends="
       create-coverage-dirs, 
       taskdef-cobertura"
 if="build.with.coverage">

  <cobertura-instrument ...> ... </cobertura-instrument>

</target>

<target name="create-coverage-dirs">
  ...    
</target>

<target name="taskdef-cobertura">
  ...
</target>

Чего это выглядело хорошо!

Только казалось, что при выполнении,задача coverage была должным образом опущена, но ее подзадачи все еще выполнялись , когда build.with.coverage было false!

>ant -v compile
Build sequence for target(s) `build' is 
  [compile, create-coverage-dirs, taskdef-    cobertura, coverage, build]
Complete build sequence is 
  [compile, create-coverage-dirs, taskdef-cobertura, coverage,    build, ]

Я могу поставить атрибут if вкаждая подзадача освещения, но это не кажется мне чистым.

Так вот вопрос:

  1. Является ли моя муравья ужаснойalect?Я «превращаю муравья в make»?
  2. Должен ли if использоваться таким образом, или есть if-and-recurse атрибут вида?

Ответы [ 3 ]

3 голосов
/ 07 октября 2011

Повторяйте за мной: Муравей не является языком программирования .Фактически, запишите это 100 раз на доске.

Ant не является языком программирования, поэтому не думайте об этом как таковом.Это матрица зависимостей сборки.

Программистам сложно обдумать эту идею.Они хотят сообщить муравью каждый шаг и когда это должно быть сделано.Они хотят петли, если заявления.Они прибегнут к использованию скрипта build.sh для вызова различных целей в Ant, потому что вы не можете легко запрограммировать Ant.

В Ant вы указываете отдельные задачи, и какие задачи зависят от других задач, и позволяетеAnt обрабатывает, где и когда выполняются вещи.

Я говорю о том, что обычно вы не разбиваете задачи на подзадачи, а затем пытаетесь вызвать на них <ant> или <subant>.

Имейте отдельные задачи, но затем дайте каждой задаче знать, от каких других задач они зависят.Также помните, что в Ant нет истинного порядка.Когда вы перечисляете задачи depends=, нет никакой гарантии, в каком порядке они будут выполняться.


Стандартный стиль муравья (что означает способ, которым я это делаю (иначе Правильный путь), ине то, как мой коллега делает это (он же «Неправильный путь»)), обычно говорится, что задачи определяются в верхней части файла свойств, а не в какой-либо цели.Вот основная схема того, как я структурирую build.xml:

<project name=...>

      <!-- Build Properties File -->
      <property name="build.properties.file"
           value="${basedir}/build.properties"/>
      <property file="${build.properties.file"/>

      <!-- Base Java Properties -->
      <property name="..." value="..."/>

      <taskdef/>
      <taskdef/>

      <!-- Javac properties -->
      <property name="javac..."  value="..."/>

      <task/>
      <task/>
 </project>

Это создает интересную иерархию.Если у вас есть файл с именем build.properties, он переопределит свойства, определенные в сценарии build.xml.Например, у вас есть:

<property name="copy.verbose"   value="false"/>

<copy todir="${target}"
   verbose="${copy.verbose}">
   <fileset dir="${source}"/>
</copy>

Вы можете включить подробную копию, просто установив copy.verbose = true в своем файле build.properties.И вы можете указать другой файл свойств сборки, просто указав это в командной строке:

$ ant -Dbuild.properties.file="my.build.properties"

(Да, да, я знаю, что есть параметр -property командной строки для ant)

Обычно я устанавливаю различные значения в build.xml на предполагаемые значения по умолчанию, но любой может изменить их, создав файл build.properties.И так как все базовые свойства находятся в начале, их легко найти.

Задачи определены и в этом нецелевом пространстве.Таким образом, я легко могу найти определение, поскольку они находятся в одном и том же месте в каждом build.xml, и я знаю, что могу использовать задачу, не беспокоясь о том, была ли достигнута цель, определяющая цель.

Теперь,на ваш вопрос:

Определите свои задачи (и у вас нет задачи по определению смолы, иначе вы сведете с ума).Затем определите зависимости для каждой из этих задач.Разработчики могут выбрать цели, которые они хотят поразить.Например:

<project>
   <description>
      yadda, yadda, yadda
   </description>

   <taskdef name="cobertura"/>

   <target name="compile"
        description="Compile the code"/>

    <!-- Do you have to compile code before you run Cobertura?--> 
    <target name="coverage"
       description="Calculate test coverage"
       depends="compile">

       <mkdir dir="${coverage.dir}"/>
       <cobertura-instrument/>
    </target>
<project>

Если вы хотите скомпилировать свой код, но не запускать какие-либо тесты, вы выполняете ant с целью compile.Если вы хотите запустить тесты, вы выполняете ant с целью coverage.Параметр depends= не требуется.

Также обратите внимание на параметр description= и задачу <description>.Это потому, что если вы сделаете это:

$ ant -p

Ant покажет, что находится в задаче <description>, все цели с параметром description и это описание.Таким образом, разработчики знают, какие цели использовать для каких задач.

Кстати, я также рекомендую делать все правильно (то есть так, как я делаю) и назовите свойцели после целей жизненного цикла Maven .Зачем?Потому что это был хороший способ стандартизировать имена целей.Разработчики знают, что clean удалит все встроенные артефакты, а compile запустит задачу <javac>, а test выполнит тесты junit.Таким образом, вы должны использовать цели в плагине Cobertura : cobertura.


Edit

Моя проблема заключается в следующем: я рассматриваю «охват»как относящиеся к «оптимизированным» и «отладочным», т.е.Вот в чем моя трудность: для Java покрытие приводит к дополнительной промежуточной цели на этапе компиляции.

Я смотрю на страницу Corburta, и в <javac> нет никаких реальных измененийзадача (которая является частью цели compile .

Вместо этого вы запускаете Corburtura для уже созданных .class файлов, а затем запускаете задачу <junit>.Большое изменение в вашей <junit> задаче, которая теперь должна включать ссылки на ваши банки Corburtura и ваши инструментированные классы.

Я полагаю, у вас может быть цель corburtura или как вы хотите ее называть,Эта цель запускает инструментальные тесты JUnit.Это цель, которую разработчики должны поразить, и она должна содержать описание запуска инструментальных тестов.

Конечно, вы не можете запускать инструментальные тесты Junit без предварительного инструментария.Таким образом, ваша цель corburtura будет зависеть от другой цели instrument.tests.Эта цель является внутренней.Люди, которые запускают ваш build.xml, обычно не говорят «тесты приборов» без выполнения этих тестов.Таким образом, эта цель не имеет описания.

Конечно, цель instrument.tests зависит от наличия на инструменте файлов .class, поэтому она будет зависеть от цели compile, которая запускает <javac>task:

<target name="instrument.classes"
   depends="compile">
   <coburtura-instrument/>
</target>

<target name="corburtura"
   depends="instrument.classes"
   description="Runs the JUnit tests instrumented with Corburtura">
   <junit/>
</target>

Единственная проблема заключается в том, что вы задаете цель <junit> дважды: один раз при инструментировании и один раз для обычного тестирования.Это может быть незначительной проблемой.Если вы обновляете работу своих тестов JUnit, вы должны сделать это в двух местах.

Если вы хотите решить эту проблему, вы можете использовать <macrodef> для определения теста JUnit, выполняющего макрос.Я использовал то, что было на странице Corbertura , чтобы помочь с планом.Полностью не проверен и, вероятно, полон синтаксических ошибок:

<target name="instrument.tests"
    depends="compile">
    <corburtura-instrument/>
</target>

<target name="corburtura"
    depends="instrument.tests"
    description="Instrument and run the JUnit tests">

    <run.junit.test fork.flag="true">
        <systemproperty.addition>
            <sysproperty key="net.sourceforge.corbertura.datafile"
                file="${basedir}/cobertura.ser" />
        </systemproperty.addition>
        <pre.classpath>
            <classpath location="${instrumented.dir}" />
        </pre.classpath>
        <post.classpath>
            <classpath refid="cobertura_classpath" />
        </post.classpath>
    </run.junit.test>
</target>

<target name="test"
    description="Runs the Junit tests without any instrumentation">
    <run.junit.test/>
</target>

<macrodef name="run.junit.test">
    <attribute name="fork.flag" default="false"/>
    <element name="sysproperty.addition" optional="yes"/>
    <element name="pre.classpath" optional="yes"/>
    <element name="post.classpath" optional="yes"/>
    <sequential>
        <junit fork="@{fork.flag}" dir="${basedir}" failureProperty="test.failed">

            <systemproperty.addtion/>
            <pre.classpath/>
            <classpath location="${classes.dir}" />
            <post.classpath/>

            <formatter type="xml" />
            <test name="${testcase}" todir="${reports.xml.dir}" if="testcase" />
            <batchtest todir="${reports.xml.dir}" unless="testcase">
                <fileset dir="${src.dir}">
                    <include name="**/*Test.java" />
                </fileset>
            </batchtest>
        </junit>
    </sequential>
</macrodef>
2 голосов
/ 07 октября 2011

Я бы не стал использовать свойство вообще в этом случае, но полагался бы исключительно на depends (что мне кажется более естественным для этой задачи):

<target name="build" depends="compile, coverage"/>

<target name="compile"> ...

<target name="coverage" 
        depends="compile, instrument,
                 create-coverage-dirs, taskdef-cobertura"> ...
1 голос
/ 07 октября 2011

Атрибут if проверяет, существует ли свойство, а не имеет значение true или false.Если вы не хотите запускать целевой объект покрытия, не определяйте свойство build.with.coverage.

Начиная с Ant 1.8.0, вы можете использовать расширение свойства, чтобы переосмыслить свойство как логическое значение:

<target name="coverage" depends="
           create-coverage-dirs, 
           taskdef-cobertura"
        if="${build.with.coverage}">
...