为什么Ant插件taskdefs的变化?

时间:2013-04-10 22:43:04

标签: java plugins ant findbugs cobertura

我真的很困惑正确的(最现代,最佳实践等)为Ant插件定义taskdef的方式。下面是我从一个内部Java应用程序build.xml中拼凑而来的代码片段。请注意,以下所有Ant目标都可以完美地完成100%(即使我将它们拼接在一起,并为了简洁起见而剥离了大部分内容):

<project name="MyApp" default="package" basedir="." xmlns:jacoco="antlib:org.jacoco.ant">
    <path id="cobertura.path">
        <!-- ... -->
    </path>

    <taskdef name="jacoco-coverage" classname="org.jacoco.ant.CoverageTask"/>
    <taskdef name="jacoco-report" classname="org.jacoco.ant.ReportTask"/>
    <taskdef classpathref="cobertura.path" resource="tasks.properties" />
    <taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"/>

    <target name="run-coco" depends="doOtherStuff">
        <jacoco:coverage>
            <!-- ... -->
        </jacoco:covergae>

        <jacoco-report>
            <!-- ... -->
        </jacoco-report>        
    </target>

    <target name="findbugs">
        <antcall target="compile" />
        <findbugs home="${findbugs.home}" output="xml:withMessages" outputFile="findbugs.xml">
            <!-- ... -->
        </findbugs>
    </target>
</project>
  1. 为什么我需要为JaCoCo定义XML命名空间而不是为Findbugs定义XML命名空间?
  2. 什么是antlib?有时我会在XML命名空间定义中看到它,有时它不在那里。
  3. 虽然jacoco-coveragejavacoco-report taskdefs在其名称中都使用了连字符(“ - ”),但相应的任务分别称为jacoco:coveragejacoco-report ...为什么用于覆盖的冒号(“:”),以及它是如何工作的(确实如此,相信我!)?
  4. 提前致谢!

1 个答案:

答案 0 :(得分:10)

让我们一步一步地回答这个问题:

步骤1:定义任务

您可以通过以下两种方式之一定义任务:

  1. 您可以指向一个类文件,然后给出classfile定义名称的任务。
  2. 您可以指向jar中具有任务名称和这些任务名称使用的类文件的 resource 文件。
  3. 在JaCoCo的情况下,你做了第一个:

    <taskdef name="jacoco-coverage" 
        classname="org.jacoco.ant.CoverageTask"/>
    <taskdef name="jacoco-report"
        classname="org.jacoco.ant.ReportTask"/>
    

    但是,您也可以采用第二种方式:

    <taskdef resource="org/jacoco/ant/antlib.xml"/>
    

    如果打开jacoco jar文件并在该目录中向下钻取,您将看到一个名为antlib.xml的文件。如果您查看该文件,您会看到:

    <antlib>
        <taskdef name="coverage"  classname="org.jacoco.ant.CoverageTask"/>
        <taskdef name="agent"     classname="org.jacoco.ant.AgentTask"/>
        <taskdef name="report"    classname="org.jacoco.ant.ReportTask"/>
        <taskdef name="merge"     classname="org.jacoco.ant.MergeTask"/>
        <taskdef name="dump"      classname="org.jacoco.ant.DumpTask"/>
    </antlib>
    

    因此,如果jar中有资源文件,您可以使用单个<taskdef>定义所有任务。请注意,您在此处调用的jacoco-coverage简称为coverage,您在此处调用jacoco-report称为report

    如果您使用<jacoco-coverage/>作为Ant任务,我会使用<coverage/>

    步骤2:JAR文件在哪里?

    在上文中,我将任务定义为:

    <taskdef resource="org/jacoco/ant/antlib.xml">
    

    在某个地方,Ant必须在其类路径中找到该路径,但在哪里?如果将JaCoCo jar放入$ANT_HOME/lib文件夹,它将自动包含在类路径中。它使定义任务变得非常容易。不幸的是,这也意味着如果其他人想要运行你的Ant脚本,他们将不得不下载JaCoCo jar并将其放在他们的$ANT_HOME/lib文件夹中。不太便携。

    幸运的是,您可以指定JaCoCo jar的位置。由于您的项目通常受版本控制,因此放置它的最佳位置位于build.xml所在位置的项目目录下。当有人签出您的项目和build.xml时,他们也会获得JaCoCo.jar。

    我的首选是创建一个名为antlib的目录,然后在该antlib目录中为每个任务集创建一个子目录。在这种情况下,我将有一个名为antlib/jacoco的目录。

    一旦我的jacoco.jar文件被安全地放置了。在该目录中,我可以向<classpath>添加<taskdef>子实体,说明在哪里找jacoco.jar

    在Classpath之前:

    <taskdef resource="org/jacoco/ant/antlib.xml"/>
    

    Classpath后:

    <taskdef resource="org/jacoco/ant/antlib.xml">
       <classpath>
           <fileset dir="${basedir}/antlib/jacoco"/>
       </classpath>
    </taskdef>
    

    使用<fileset/>注意,我不关心我的JaCoCo jar文件是jacoco.jar还是jacoco-2.3.jar。如果你知道jar的确切名称,你可以这样做:

    <taskdef resource="org/jacoco/ant/antlib.xml">
       <classpath path="${basedir}/antlib/jacoco/jacoco.jar"/>
    </taskdef>
    

    并保存几行。

    步骤3:XML命名空间

    到目前为止,我不需要jacoco:作为我的任务名称的前缀。

    我可以这样做:

    <project name="MyApp" default="package" basedir=".">
    
        <taskdef resource="org/jacoco/ant/antlib.xml">
            <classpath path="${basedir}/antlib/jacoco/jacoco.jar"/>
        </taskdef>
    
        <target name="run-coco" depends="doOtherStuff">
            <coverage>
                <!-- ... -->
            <coverage>
    
            <report>
                <!-- ... -->
            <report>        
        </target>
    </project>
    

    而且,你可以留下它。没问题。简单干净。

    但是,如果您使用两个不同的Ant任务集,一个名为Foo,一个名为Bar,并且两者都碰巧在其中定义了<munge/>任务,该怎么办?

    <taskdef resource="org/foo/ant/antlib.xml">
        <classpath path="${basedir}/antlib/foo.jar/>
    </taskdef>
    
    <taskdef resource="org/bar/ant/antlib.xml">
        <classpath path="${basedir}/antlib/bar.jar/>
    </taskdef>
    
    <target name="munge-this">
        <!-- Is this foo.jar's or bar.jar's munge task? -->
        <munge vorbix="fester"/>
    </target>
    

    正在执行哪个munge任务?它是foo.jar中的那个还是bar.jar中的那个?

    为了解决这个问题,Ant允许您为每组任务定义 XML命名空间。您可以在最顶层<project>实体,任务本身或<target>名称中定义此类命名空间。 99%的情况下,它是在<project>实体中完成的。

    <project name="demo" default="package" basename="."
        xmlns:foo="I-will-have-fries-with-that"
        xmlns:bar="Never-on-a-first-date">
    
     <taskdef uri="I-will-have-fries-with-that"
        resource="org/foo/ant/antlib.xml">
        <classpath path="${basedir}/antlib/foo.jar/>
    </taskdef>
    
    <taskdef uri="Never-on-a-first-date"
        resource="org/bar/ant/antlib.xml">
        <classpath path="${basedir}/antlib/bar.jar/>
    </taskdef>
    
    <target name="munge-this">
        <!-- Look the 'foo:' XMLNS prefix! It's foo.jar's much task! -->
        <foo:munge vorbix="fester"/>
    </target>
    

    现在,我知道这是Foo任务列表中的<munge/>任务!

    xmlns定义XML命名空间。 xmlns:foo定义了Foo设置的命名空间,xmlns:bar为任务栏集定义了命名空间。当我使用foo.jar中的任务时,我在其前面加上foo:命名空间。请注意,此前缀是xmlns:之后的那个。

    uri只是一个与命名空间字符串匹配的字符串。我使用字符串I-will-have-fries-with-thatNever-on-a-first-date来向您显示字符串本身并不重要。重要的是此字符串与uri任务的<taskdef>参数匹配。这样,定义的任务就知道他们应该使用什么 namespace

    现在,通常URI字符串是URI定义。有两种方法可以解决这个问题:

    • 使用有点标准化的antlib: URI,并对命名空间使用反向命名:antlib:org.jacoco.ant
    • 使用指向项目的网址:http://www.eclemma.org/jacoco/

    我更喜欢后者,因为它会指向文档。然而,看起来第一个更受欢迎。

    那么,让我们来看看Jacoco将如何运作:

    <project name="MyApp" default="package" basedir="."
        xmlns:jacoco="antlib:org.jacoco.ant">
    
        <taskdef uri="antlib:org.jacoco.ant" 
            resource="org/jacoco/ant/antlib.xml">
            <classpath path="${basedir}/antlib/jacoco/jacoco.jar"/>
        </taskdef>
    
        <target name="run-coco" depends="doOtherStuff">
            <jacoco:coverage>
                <!-- ... -->
            <jacoco:coverage>
    
            <jacoco:report>
                <!-- ... -->
            <jacoco:report>        
        </target>
    </project>
    

    请注意,JaCoCo uri中的<taskdef/>xmlns:jacoco字符串相同。

    这就是任务定义的全部内容。我希望它解释了jacoco:前缀的来源以及如何通过指向包含该类的实际类名来定义任务,或者指向同时指向任务名称的资源文件< strong> 该课程。