Ant contrib, Gradle, taskdef and available tasks

Continuing old discussions from the old forum, i’m still having trouble to import a legacy ant file and use it from a custom gradle plugin.

The ant file has the following explicit check, done from a sub ant file (check.xml), imported from my general ant xml file (build.xml)

<echo message="checking that the Ant If and For tasks are available for Ant" level="info"/>
<!-- ensure that the If task is defined, otherwise everything would fail -->
<condition property="IfTaskAvailable">
    <available classname="net.sf.antcontrib.logic.IfTask"/>
</condition>
<fail message="The If task is not defined, please add ant-contrib jar file as a -lib parameter to ant or within your class path" unless="IfTaskAvailable"/>
<!-- ensure that the For task is defined, otherwise everything would fail -->
<condition property="ForAvailable">
  <or>
    <available classname="net.sf.antcontrib.logic.For"/>
    <available classname="net.sf.antcontrib.logic.ForTask"/>
  </or>
</condition>
<fail message="The For task is not defined, please add ant-contrib jar file as a -lib parameter to ant or within your class path" unless="ForAvailable"/>
<!-- Ant-contrib ant tasks definition -->
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
<echo message=" ... OK : Ant If and For tasks loaded ..." level="info"/>

I’m doing this in my gradle plugin:

project.configurations{
            antcp {
                description = 'ant classpath'
                transitive = true
                exclude module: 'ant'
            }
        }
        project.dependencies{
            // add ant contrib and pmd
            antcp 'pmd:pmd:4.2.5'
            antcp 'ant-contrib:ant-contrib:1.0b3'
        }
project.ant.taskdef(uri:'antlib:net.sf.antcontrib', resource:'net/sf/antcontrib/antcontrib.properties', classpath: project.configurations.antcp.asPath)

project.ant.importBuild('my main build.xml file')
project.ant.antProject.executeTarget('myTarget')

It kind of works, since I see in the log output

[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] Finding class net.sf.antcontrib.logic.IfTask
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] Loaded from xxx/caches/modules-2/files-2.1/ant-contrib/ant-contrib/1.0b3/943cd5c8802b2a3a64a010efb86ec19bac142e40/ant-contrib-1.0b3.jar net/sf/antcontrib/logic/IfTask.class
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] Class net.sf.antcontrib.logic.IfTask loaded from ant loader (parentFirst)

But then, the ant available check fails anyway

[INFO] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:echo] checking that the Ant If and For tasks are available for Ant
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:available] class “net.sf.antcontrib.logic.IfTask” was not found
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:available] Unable to load class net.sf.antcontrib.logic.IfTask
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] Condition false; not setting IfTaskAvailable
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:fail] failing due to The If task is not defined, please add ant-contrib jar file as a -lib parameter to ant or within your class path

I can provide more details on the ant build file or my plugin, but I don’t understand why this does not work.
The IfTask (among other tasks from the ant contrib jar) is well 'imported’
I load the main ant build.xml file and call the default target
It calls the secondary xml file, that contains the <available/> check on the IfTask, that fails

Here is a small Gradle build to reproduce my problem

build.gradle

repositories {
mavenCentral()
}
configurations{
antcp {
description = 'ant classpath’
transitive = true
exclude module: ‘ant’
}
}
dependencies{ antcp ‘ant-contrib:ant-contrib:1.0b3’ }

task callAnt << {
println ‘install ant-contrib in ant’
ant.taskdef(uri:‘antlib:net.sf.antcontrib’, resource:‘net/sf/antcontrib/antcontrib.properties’, classpath: project.configurations.antcp.asPath)
println ‘import ant build file’
ant.importBuild(file(’./build.xml’))
println 'Call the all target’
ant.antProject.executeTarget(‘all’)
}

build.xml file

<project name="antbuild" default="all" xmlns:ac="antlib:net.sf.antcontrib">
    <!-- ensure that the If task is defined, otherwise everything would fail -->
    <import file="./secondary.xml" />
    
    <target name="all"/>
</project>

secondary ant xml file

<project name="antsecondary" xmlns:ac="antlib:net.sf.antcontrib">
        <echo message="checking that the Ant If and For tasks are available for Ant" level="info"/>
        <!-- ensure that the If task is defined, otherwise everything would fail -->
        <condition property="IfTaskAvailable">
        <available classname="net.sf.antcontrib.logic.IfTask"/>
        </condition>
        <fail message="The If task is not defined, please add ant-contrib jar file as a -lib parameter to ant or within your class path" unless="IfTaskAvailable"/>

        <!-- ensure that the For task is defined, otherwise everything would fail -->
        <condition property="ForAvailable">
        <or>
        <available classname="net.sf.antcontrib.logic.For"/>
        <available classname="net.sf.antcontrib.logic.ForTask"/>
        </or>
        </condition>
        <fail message="The For task is not defined, please add ant-contrib jar file as a -lib parameter to ant or within your class path" unless="ForAvailable"/>

        <!-- Ant-contrib ant tasks definition -->
        <taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
        <echo message=" ... OK : Ant If and For tasks loaded ..." level="info"/>
</project>

Then call gradle callAnt --debug --stacktrace

The ant taskdef from my gradle task imports ant contrib
but then the ant ‘available’ block fails anyway.


[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] Finding class net.sf.antcontrib.logic.IfTask
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] Loaded from D:…gradle…
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] Class net.sf.antcontrib.logic.IfTask loaded from ant loader (parentFirst)

[INFO] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:echo] checking that the Ant If and For tasks are available for Ant
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:available] class “net.sf.antcontrib.logic.IfTask” was not found
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:available] Unable to load class net.sf.antcontrib.logic.IfTask
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] Condition false; not setting IfTaskAvailable
[DEBUG] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:fail] failing due to The If task is not defined, please add ant-contrib

I really don’t know what’s wrong here !

Since Gradle is supposed to help transitioning from a pure ant-based build file to a pure Gradle build file, I would have expected this sort of things to work out of the box:

The ant build need ant-contrib, I give it using a taskdef, but it cannot be found by the ant ‘available’ task.
I don’t know how I can continue investigating this further …

My hunch is that the available check is done via a different classloader and therefore cannot find the class. Not sure how you could proceed here without rewriting the original Ant logic. In a hybrid approach, I’d try to bring this code into the Gradle world as soon as possible.

Perhaps try adding ant-contrib to the buildscript classpath?

Outch :frowning:

Unfortunately it’s not working.

Funny enough, if I remove the available task call from the ant build file, I can use the antcontrib tasks (if, foreach, etc etc) anyway …
There is probably an ant limitation on the available task ?

Maybe I could override the ant:available task to basically do nothing

I know we can override ant target easily but I’m not sure if it’s possible to override core ant task (such as available) with a custom implementation

maybe a taskdef on ‘available’ with a custom class ?
Would this work ?

Allright, I managed to find a solution without touching the ant build file.

As a reminder, the culprit is

<condition property="IfTaskAvailable">
  <available classname="net.sf.antcontrib.logic.IfTask" />
</condition>
<fail message="The If task is not defined" unless="IfTaskAvailable"/>

Prepare yourself for the ugliest thing you’ve ever seen (BRACE, BRACE …)
ant.setProperty(‘IfTaskAvailable’,’’)
By calling this before the ant build script import, I see in the debug output

[ant:available] class “net.sf.antcontrib.logic.IfTask” was not found
[ant:available] Unable to load class net.sf.antcontrib.logic.IfTask
Condition false; not setting IfTaskAvailable

So the available task is still failing the condition, but since the property is already set anyway, the ‘fail’ task is tricked into thinking everything is OK.
Since ant-contrib CAN be found anyway, everything works fine after that.

I’m not proud of it but I can live with that and still look at me in the mirror :smile: