2011-10-07 78 views
1

我对蚂蚁相当陌生,我见过叔叔鲍勃的“提取,直到你放弃”插曲。蚂蚁有条件的目标和'递归'

因此,我尝试定义尽可能小的蚂蚁目标,以便您可以精确地看到目标的本质,而不是更多。有关更多详细信息,您必须参考子目标。

无论好坏风格是不同的辩论(或可能是一场火焰战争)。

因此,我创建一个构建脚本,在伪代码,应该是这样的:

build = 
    compile 
    instrument if coverage 

coverage任务分割成子目标,太:

coverage: 
    create-coverage-dirs 
    call-cobertura 

编辑 - 我想表示coverage子目标不应运行。

但是...我很难在ant-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> 

Whow这看起来不错!

只有它似乎是,执行时的coverage任务duefully省略,但它的子任务仍执行build.with.coveragefalse

>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. 是我的蚂蚁ESE一个可怕的方言?我是不是在做'蚂蚁'?
  2. if应该以这种方式使用,还是有if-and-recurse种属性?
+0

(我使用ant 1.8.2) – xtofl

回答

2

我不会在这种情况下,在所有使用属性,但仅仅依靠depends(这似乎更自然给我这个任务):

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

<target name="compile"> ... 

<target name="coverage" 
     depends="compile, instrument, 
       create-coverage-dirs, taskdef-cobertura"> ... 
+1

这也是我的感受。只有'coverage'还依赖于'compile',否? – xtofl

+0

好点,我会更新我的回答 – michael667

1

if attribute测试属性是否存在,而不是如果它是真或假。如果您不想运行覆盖率目标,则不要定义属性build.with.coverage

蚂蚁的1.8.0您可以使用属性扩展resplver财产作为布尔:

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

感谢您的信息;即使'build.with.coverage'没有定义,唯一被排除的目标是'主'覆盖'目标。我想表达的是,它的子目标也不应该运行。 – xtofl

+0

您可以从相关列表中删除子目标,并通过覆盖率任务中的[antcall](http://ant.apache.org/manual/Tasks/antcall.html)调用它们。 – krock

+1

Antcall与depends相同:不会覆盖现有引用,即使将inheritRefs设置为true也是如此。由于被调用的构建文件与调用构建文件是相同的构建文件,这意味着它不会通过id属性覆盖任何参考集。子项目可以继承的唯一引用是由嵌套的元素或直接由任务定义的引用定义的引用(不使用id属性)。请参阅http://ant.apache.org/manual/Tasks/antcall.html – michael667

3

重复:蚂蚁是不是一种编程语言。实际上,在黑板上写下100次。

蚂蚁不是一种编程语言,所以不要这样想。它是一个构建依赖矩阵。

对于程序员来说,围绕这个想法很困难。他们想要告诉Ant每一步以及何时应该完成。他们想要循环,如果语句。他们会求助于使用build.sh脚本来调用Ant中的各种目标,因为您无法轻松编程Ant。

在Ant中,您指定了离散任务,以及哪些任务依赖于其他任务,并让Ant处理执行任务的位置和时间。

我在说的是,你通常不会将任务分成子任务,然后尝试对它们调用<ant><subant>

有离散任务,但让每个任务都知道他们依赖的其他任务。还要记住在Ant中没有真正的顺序。当你列出depends=任务,也不能保证其顺序,他们将在执行。


标准的Ant风格(这意味着我的方式做到这一点(又名正确的方式),而不是我的方式同事这样做(aka The Wrong Way)),通常规定在属性文件的顶部定义任务,而不是在任何目标中定义任务。下面是我如何构建一个基本的轮廓我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> 

您可以通过仅仅在build.properties文件中设置copy.verbose = true开启冗长的副本。

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

(是的,是的,我知道有一个为ant一个-property命令行参数)

我通常:而且,你可以仅仅通过指定在命令行上指定一个不同的构建属性文件将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> 

如果你想编译代码,但不运行任何测试,你与compile目标执行ant。如果您想运行测试,则执行ant,并使用coverage目标。没有必要使用depends=参数。

另请注意description=参数和<description>任务。这是因为如果你这样做:

$ ant -p 

蚂蚁会显示哪些内容在<description>任务,具有description参数所有目标,而且描述。这样,开发人员就知道哪些目标用于哪些任务。

顺便说一句,我还建议做正确的方式(也就是我这样做),并在Maven lifecycle goals后命名您的目标。为什么?因为这是标准化目标名称的好方法。开发人员知道clean将删除所有构建的工件,并且compile将运行<javac>任务,并且该test将运行junit测试。因此,您应该使用Cobertura plugincobertura中的目标。


编辑

我的问题是:我把“覆盖”作为与“优化”和“调试”,即构建味道。这就是我的困难所在:对于Java,覆盖率在编译步骤中会产生额外的中间目标。

我看Corburta页面,并且有在<javac>任务没有真正的改变(这是的一部分编译目标。

相反,你对已建成.class文件运行Corburtura ,然后运行<junit>任务。最大的变化是在你的<junit>现在的任务,其中必须包括您Corburtura罐子引用,以及对你的仪表类。

我想像你可以有一个corburtura目标或要什么都叫它。该目标运行仪器化的JUnit测试。这是您希望开发人员打的目标,并且应该包含运行仪器测试的说明。

当然,如果不先测试它们,您将无法运行测试的Junit测试。因此,您的corburtura目标将取决于另一个instrument.tests目标。这个目标是内部的。运行你的build.xml的人通常不会在没有运行这些测试的情况下说“仪器测试”。因此,这个目标没有描述。

当然,instrument.tests目标取决于其.class文件到仪器,所以它必须就compile目标的依赖在运行<javac>任务:

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

哇,谢谢你这个详细的答案。我完全同意'depends ='i.s.o. 'antcall';我的问题是:我认为'coverage'与'优化'和'调试'有关,即构建风格。这就是我的困难所在:对于Java,覆盖率在编译步骤中会导致**额外的中间目标**。 – xtofl

+0

就像例如我在发布版本时对非优化代码不感兴趣,我不需要任何javac输出。 – xtofl

+0

顺便说一句,我称之为构建样式'coverage'iso'cobertura',因为我想指定**它做了什么** iso **是谁做的**。这对我来说是正确的方式,因为我也在用相同的语义进行C++构建。 – xtofl