我的问题类似于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,该值仅存在在设置中。
我错过了什么?是否有我跳过的教程或在线文档的一部分?任何指针或提示?
答案 0 :(得分:10)
您应该删除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:
被调用的目标在新项目中运行;请注意,这意味着被调用目标设置的属性,引用等不会持久存回调用项目。
答案 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”元素。