Есть ли способ гарантировать, что муравьиная зависимость запускается только один раз? - PullRequest
7 голосов
/ 29 января 2010

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

Моя ситуация такова, что я пишу набор целей ant, и мне нужно, чтобы цель let-call-it setup выполнялась один раз и только один раз, независимо от того, какая цель вызывается. Вот очень упрощенный пример:


    <?xml version="1.0"?>
    <project name="Ant_Test" basedir=".">
      <target name="setup">
        <echo message="In setup" />
      </target>
      <target name="do.several" depends="setup">
        <echo message="In do.several, calling do.first" />
        <antcall target="do.first" />
        <echo message="In do.several, calling do.second" />
        <antcall target="do.second" />
      </target>
      <target name="do.first" depends="setup">
        <echo message="In do.first" />
      </target>
      <target name="do.second" depends="setup">
        <echo message="In do.second" />
      </target>
    </project>

Мне нужно, чтобы setup вызывался ровно один раз, независимо от того, do.several , do.first или do.second вызваны. С моей наивной попыткой выше, вызов do.several приводит к трем вызовам setup .

Я думал об установке свойства (назовем его setup.has.been.invoked ) и использовании его для условного вызова setup из каждой цели, но это Похоже, что настройка свойства ограничена областью, в которой оно выполнено, поэтому если в setup я установлю setup.has.been.invoked в значение true, это значение существует только в установка .

Что мне не хватает? Есть ли раздел руководств или онлайн-документации, которую я пропустил? Есть указатели или подсказки?

Ответы [ 5 ]

10 голосов
/ 29 января 2010

Вы должны удалить antcalls и добавить do.first и do.second как зависимости do.several:

<target name="do.several" depends="setup, do.first, do.second">
</target>

Это гарантирует, что setup вызывается только один раз:

setup:
     [echo] In setup

do.first:
     [echo] In do.first

do.second:
     [echo] In do.second

do.several:

BUILD SUCCESSFUL
Total time: 0 seconds

Документация говорит, почему набор свойств в настройке не работает с antcall:

Вызванные цели выполняются в новом проекте; Имейте в виду, что это означает, что свойства, ссылки и т. д., установленные вызываемыми целями, не сохранятся обратно в вызывающий проект.

9 голосов
/ 01 июля 2013

Я просто хотел бы добавить еще один возможный способ сделать это.

<target name="setup" unless="setup.already.executed">
    <echo message="In setup" />
    <property name="setup.already.executed" value="x" />
</target>

Таким образом, вы запускаете его только один раз, а затем мгновенно устанавливаете флаг, что он уже был выполнен один раз. Кроме того, он не нарушает «зависимую» часть вашего кода, поскольку он запускает цели, только если это возможно / необходимо, но не нарушает выполнение целевой цели.

Также это наименьшее количество изменений в ваших сценариях.

Edit: Объяснение «не нарушает зависимую часть»:

Если вызывается 'ant do.first do.second', это приводит к тому, что setup вызывается дважды, даже если все цели используют установку в качестве зависимости. Это было бы проблемой, если программа установки выполняет клонирование репо или другие трудоемкие операции. Этот подход работает в обоих случаях - то есть «ant do.several» или «ant do.first do.second».

3 голосов
/ 30 января 2010

Альтернативой ответам, которые вы уже получили, является создание пользовательского контейнера задач, который гарантирует, что он не повторяет действие. У меня есть такая нестандартная задача в моем личном Antlib , но там куча другого мусора, который вам, вероятно, не нужен, так что, возможно, вы могли бы просто скопировать источник и добавить его в свой собственный проект. Это выглядит примерно так:

import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskContainer;
import org.apache.tools.ant.BuildException;
import java.util.List;
import java.util.LinkedList;

/**
 * Task container to ensure that some set of actions is only performed
 * once per build.  On completion of the actions a property is set that
 * prevents subsequent executions.
 */
public class Once extends Task implements TaskContainer
{
    private final List<Task> tasks = new LinkedList<Task>();

    private String property;

    /**
     * The name of the property to consult in order to determine whether
     * the nested tasks should be performed.  This is a required attribute.
     */
    public void setProperty(String property)
    {
        this.property = property;
    }

    public void addTask(Task task)
    {
        tasks.add(task);
    }     

    @Override
    public void execute() throws BuildException
    {
        if (property == null || property.length() == 0)
        {
            throw new BuildException("Property name must be specified.");
        }
        // If no value is specified, the tasks are performed if the property
        // is set to any value.  If a value is specified, the tasks are only
        // performed if the property matches that value.
        if (getProject().getProperty(property) == null)
        {
            for (Task task : tasks)
            {
                task.perform();
            }
            getProject().setProperty(property, "done");
        }
    }
}
1 голос
/ 24 мая 2011

Цели могут иметь элемент «кроме», который пропускает цель, если установлено свойство, на которое ссылается «если».

1 голос
/ 29 января 2010

См. Разницу между , включая и import в руководстве по Ant Также используйте macrodefs .

Я немного адаптировал ваш пример, вам понадобятся несколько файлов: build.xml, common.xml и macrodef_project_setup.xml

build.xml

<?xml version="1.0"?>
<project name="Ant_Test" basedir="." default="init">

<import file="common.xml"/>

<!-- This target overridden by the one in common.xml -->
<target name="common.init"/>

<target name="setup" depends="init"/>

<target name="do.several" depends="common.setup">
    <echo message="In do.several, calling do.first" />
    <antcall target="do.first" />
    <echo message="In do.several, calling do.second" />
    <antcall target="do.second" />
</target>

<target name="do.first" depends="common.setup">
    <echo message="In do.first" />
</target>
<target name="do.second" depends="common.setup">
    <echo message="In do.second" />
</target>

</project>

common.xml

<?xml version="1.0"?>
<project name="common">

<target name="init">
    <project_setup option="Stack.Over.Flow"/>
</target>

<target name="setup">
</target>

<import file="macrodef_project_setup.xml"/>

</project>

macrodef

<?xml version="1.0"?>
<project name="project_setup" basedir=".">

<macrodef name="project_setup">
        <attribute name="option" default=""/>
        <sequential>

            <!-- some process -->
            <echo>THIS IS MY SETUP OPTION: @{option}</echo>

        </sequential>
</macrodef>

</project>

Выход:

ant -p
Buildfile: build.xml
Main targets:

Other targets:

 common.setup
 do.first
 do.second
 do.several
 init
 setup
Default target: init

Целью по умолчанию теперь является init.

ant
Buildfile: build.xml

Ant_Test.init:
     [echo] In setup initialization
     [echo] THIS IS MY SETUP OPTION: Stack.Over.Flow

Но вы все равно можете использовать настройку муравья.

ant setup
Buildfile: build.xml

Ant_Test.init:
     [echo] In setup initialization 
     [echo] THIS IS MY SETUP OPTION: Stack.Over.Flow

Запустите его с do.several.

ant do.several
Buildfile: build.xml

Ant_Test.init:
     [echo] In setup initialization 
     [echo] THIS IS MY SETUP OPTION: Stack.Over.Flow

Ant_Test.do.several:
     [echo] In do.several, calling do.first

Ant_Test.do.first:
     [echo] In do.first

     [echo] In do.several, calling do.second

Ant_Test.do.second:
     [echo] In do.second
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...