跟我重复一遍:Ant 不是编程语言。事实上,在黑板上写下 100 遍。
Ant 不是一种编程语言,所以不要这样想。它是一个构建依赖矩阵。
程序员很难接受这个想法。他们想告诉 Ant 每一步以及何时应该完成。他们想要循环,if 语句。他们会求助于有一个 build.sh 脚本来调用 Ant 中的各种目标,因为您不能轻松地对 Ant 进行编程。
在 Ant 中,您可以指定离散任务,哪些任务依赖于其他任务,并让 Ant 处理事情的执行地点和时间。
我的意思是,您通常不会将任务拆分为子任务,然后尝试对它们调用 <ant> 或 <subant>。
有离散的任务,然后让每个任务知道他们依赖的其他任务。还要记住,Ant 中没有真正的顺序。当您列出 depends= 任务时,无法保证它们将按什么顺序执行。
标准 Ant 样式(这意味着我这样做的方式(又名正确的方式),而不是我同事的方式(又名错误的方式)),通常声明在属性文件的顶部定义任务和不在任何目标中。这是我如何构建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"
(是的,是的,我知道-propertyant 的命令行参数)
我通常将build.xml 中的各种值设置为假定的默认值,但任何人都可以通过创建build.properties 文件来更改它们。而且,由于所有基本属性都位于开头,因此很容易找到。
任务也在这个非目标空间中定义。这样,我可以很容易地找到定义,因为它们在每个build.xml 中都位于相同的位置,并且我知道我可以使用任务而不必担心任务定义目标是否已命中。
现在,回答你的问题:
定义你的任务(不要有一个 tar 定义任务,否则你会把自己逼疯的)。然后,定义每个任务的依赖关系。开发人员可以选择他们想要达到的目标。例如:
<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。如果要运行测试,请使用 coverage 目标执行 ant。不需要depends= 参数。
还要注意description= 参数和<description> 任务。那是因为如果你这样做:
$ ant -p
Ant 将显示<description> 任务中的内容、带有description 参数的所有目标以及该描述。这样,开发人员就知道哪些目标用于哪些任务。
顺便说一句,我还建议以正确的方式做事(也就是按照我的方式做事)并以Maven lifecycle goals 命名您的目标。为什么?因为这是标准化目标名称的好方法。开发人员知道clean 将删除所有构建的工件,compile 将运行<javac> 任务,test 将运行junit 测试。因此,您应该使用Cobertura plugin 中的目标:cobertura。
编辑
我的问题是:我认为“覆盖”与“优化”和“调试”相关,即构建风格。这就是我的困难所在:对于 Java,覆盖会在编译步骤中产生一个额外的中间目标。
我正在查看 Corburta 页面,<javac> 任务(它是 compile 目标的一部分。
相反,您在已构建的 .class 文件上运行 Corburtura,然后运行您的 <junit> 任务。最大的变化在于您的 <junit> 任务现在必须包含对您的 Corburtura jar 和您的检测类的引用。
我想你可以有一个corburturatarget 或者你想怎么称呼它。此目标运行检测的 JUnit 测试。这是您希望开发人员达到的目标,并且应该包含运行插桩测试的描述。
当然,如果不先检测它们,就无法运行检测的 Junit 测试。因此,您的corburtura 目标将依赖于另一个instrument.tests 目标。这个目标是内部的。运行您的build.xml 的人通常不会在不运行这些测试的情况下说“仪器测试”。因此,这个目标没有描述。
当然,instrument.tests 目标依赖于要检测的.class 文件,因此它将依赖于运行<javac> 任务的compile 目标:
<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>