我真的对为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>
  • 为什么需要为JaCoCo而不是Findbugs定义XML名称空间?
  • 什么是antlib?有时我在XML名称空间定义中看到它,有时它不存在。
  • 尽管jacoco-coveragejavacoco-report taskdef的名称中都使用连字符(“-”),但是相应的任务分别称为jacoco:coveragejacoco-report ...为什么用冒号(“:”)进行覆盖,以及它如何工作(它相信我!)?

  • 提前致谢!

    最佳答案

    让我们一次一步地解决这个问题:
    步骤1:定义任务
    您可以通过以下两种方式之一来定义任务:

  • 您可以指向类文件,然后为该任务指定类文件定义名称。
  • 您可以指向jar中的资源文件,该资源文件包含任务名称和这些任务名称使用的类文件。

  • 对于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。如果您知道罐子的确切名称,则可以执行以下操作:
    <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 Taks集合定义了一个名称空间,而xmlns:bar为任务栏集合定义了名称空间。当我使用foo.jar中的任务时,我给它添加了foo:命名空间的前缀。请注意,此前缀是xmlns:后的一个前缀。uri仅仅是与名称空间字符串匹配的字符串。我使用了字符串I-will-have-fries-with-thatNever-on-a-first-date向您展示了字符串本身并不重要。重要的是此字符串与uri任务的<taskdef>参数匹配。这样,被定义的任务知道它们应使用的命名空间。
    现在,通常URI字符串是URI定义。有两种解决方法:
  • 使用某种标准化的antlib: URI,并对该名称空间使用反向命名:antlib:org.jacoco.ant
  • 使用指向项目的URL: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:前缀的位置以及如何通过指向包含该类的实际类名或指向该任务名的资源文件来定义任务。

    关于java - 为什么在Ant插件taskdefs上有变化?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15937805/

    10-12 21:58