有没有办法保证ant依赖只运行一次?

时间:2010-01-29 17:23:23

标签: ant

我的问题类似于avoiding-re-building-prerequisites-in-ant,除了我的需要不涉及创建的对象,而是调用了进程,因此那里讨论的解决方案对我不起作用。至少我是这么认为的 - 但我是蚂蚁的新手。

我的情况是我正在编写一组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>

无论是 do.several do.first 还是,我都需要一次调用 setup 。第二个被调用。通过上面的天真尝试,在三次调用 setup 时调用do.several结果。

我想过设置一个属性(让我们称之为 setup.has.been.invoked ),并使用它来有条件地从每个目标中调用 setup ,但似乎属性设置仅限于其完成的范围,因此如果在 setup 中,我将 setup.has.been.invoked 设置为true,该值仅存在在设置中。

我错过了什么?是否有我跳过的教程或在线文档的一部分?任何指针或提示?

5 个答案:

答案 0 :(得分:10)

您应该删除antcalls并添加do.firstdo.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:

  

被调用的目标在新项目中运行;请注意,这意味着被调用目标设置的属性,引用等不会持久存回调用项目。

答案 1 :(得分:9)

我只想添加另一种方法来实现这一目标。

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

这样你只运行一次然后立即设置它已经执行过一次的标志。此外,它不会破坏代码的“依赖”部分,因为它只在可能/必要时才运行目标,但它不会破坏目标依赖目标的执行。

此脚本中的更改量也是最少的。

编辑: “不打破依赖部分”的解释:

如果调用'ant do.first do.second',即使所有目标都使用setup作为依赖项,它也会导致设置被调用两次。如果设置正在执行克隆repo或其他耗时的操作,那将是一个问题。这种方法适用于两种情况 - 即'ant do.several'或'ant do.first do.second'。

答案 2 :(得分:3)

您已经收到的答案的替代方法是创建一个自定义任务容器,以确保它不会重复操作。我在my personal Antlib中有这样一个自定义任务,但是你可能不想要其他垃圾,所以也许你可以复制the source并将它添加到你自己的项目中。它看起来像这样:

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");
        }
    }
}

答案 3 :(得分:1)

在Ant手册中查看 include import 之间的区别。也可以使用 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。

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

答案 4 :(得分:1)

如果设置了“除非”引用的属性,则目标可以具有将跳过目标的“unless”元素。