2010-01-29 109 views
5

我的問題類似於avoiding-re-building-prerequisites-in-ant,除了我的需要不涉及創建的對象,但調用的過程,所以在那裏討論的解決方案將不適合我。至少我是這麼認爲的 - 但我對螞蟻很陌生。有沒有辦法保證螞蟻依賴只運行一次?

我的情況是,我正在編寫一組螞蟻目標,並且我需要let-call-it 設置目標只執行一次,無論哪個目標被調用。這裏有一個大大簡化的例子:


    <?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.severaldo.first,或do.second是否被調用。用我上面的天真嘗試,在調用設置三次調用do.several結果。

我想設置一個屬性(我們稱之爲setup.has.been.invoked),並用它來有條件地每個目標中調用設置的,但現在看來,屬性設置僅限於所以如果在設置,我設置setup.has.been.invoked爲true,該值只存在於設置

我錯過了什麼?是否有一段我跳過的教程或在線文檔?任何指針或提示?

回答

9

您應該刪除antcalls並添加do.firstdo.seconddo.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:

被調用的目標在新項目中運行;請注意,這意味着由被調用目標設置的屬性,引用等不會保留回調用項目。

+0

太棒了,謝謝! – CPerkins 2010-01-29 17:55:56

+0

+!爲了擺脫''。 ''讓大衛悲傷...... 99%的時間,''不僅是不必要的,而且也破壞了構建系統的效率。開發人員喜歡使用'',因爲它們用於控制執行順序。一個Ant build.xml文件我們至少調用了每次依賴至少14次。 Ant是一種矩陣依賴語言。你應該只說明每個任務的依賴關係,並讓Ant找出順序。 – 2013-07-01 14:56:29

1
參見

的差,之間包括在Ant手冊進口。還使用宏定義

我已經稍微適應你的榜樣,你需要幾個文件: 的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> 

常見。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的設置。

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 
+0

謝謝,院長。這非常令人印象深刻。我將更詳細地介紹include,import和macrodef。謝謝。 – CPerkins 2010-01-29 20:03:34

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

謝謝,丹。我不知道自定義TaskContainer是一種可能性。很顯然,我有更多的閱讀要做。 – CPerkins 2010-01-31 00:16:55

+0

很棒的工作丹!無論如何,不​​妨試試Ivy,Hudson,特別是使用maven-ant任務的罕見病毒 - 讓構建,部署和測試變得更容易。謝謝。 – user16797 2010-02-05 17:14:12

0

目標可以有一個「除非」元素,將跳過目標,如果財產「除非」設置引用。

6

我只是想添加另一種可能的方式來做到這一點。

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

這樣你只運行一次,然後立即設置它已經執行一次的標誌。此外,它不會中斷代碼的「依賴」部分,因爲它只在可能/必要時運行目標,但不會中斷目標從屬目標的執行。

這也是您的腳本中最少的變化。