3

I want to be able to generate a number of Ant targets something like this:

<property name="grunt_tasks" value="jsp,css,js,img" />
<foreach list="${grunt_tasks}" param="task">
    <target name="${task}">
        <exec executable="grunt" failonerror="true">
            <arg line="${task}" />
        </exec>
    </target>
</foreach>

allowing me to run ant jsp or ant js.

However, this code fails because a target tag cannot be placed in a foreach tag.

How can I accomplish this?

martin clayton
  • 76,436
  • 32
  • 213
  • 198
Matthew Kime
  • 754
  • 1
  • 6
  • 15

1 Answers1

1

There's a number of ways you might add targets on the fly. Here's one suggestion:

<property name="mybuild" value="mybuild.xml" />

<property name="grunt_tasks" value="jsp,css,js,img" />

<echo message="&lt;project&gt;" file="${mybuild}" />
<for list="${grunt_tasks}" param="task">
    <sequential>
    <echo file="${mybuild}" append="yes"><![CDATA[
    <target name="@{task}">
        <exec executable="grunt" failonerror="true">
            <arg line="@{task}" />
        </exec>
    </target>
    ]]></echo>
    </sequential>
</for>
<echo message="&lt;/project&gt;" file="${mybuild}" append="yes"/>

<import file="${mybuild}" />

Explanation:

  • Use the antcontrib <for> task in preference to <foreach>, else you have to have a separate target for the body of the loop.
  • Create a second buildfile, here called mybuild.xml, to contain your targets.
  • The buildfile content has to be within a <project> element.
  • Import the buildfile.

You can then invoke the on-the-fly targets in the way you wish.

You might alternatively use a <script> task to create the targets if you prefer, which would remove the need for the separate buildfile and import, something like this:

<for list="${grunt_tasks}" param="task">
    <sequential>
    <script language="javascript"><![CDATA[
        importClass(org.apache.tools.ant.Target);

        var exec = project.createTask( "exec" );
        exec.setExecutable( "grunt" );
        exec.setFailonerror( true );
        var arg = exec.createArg( );
        arg.setValue( "@{task}" );

        var target = new Target();
        target.addTask( exec );
        target.setName( "@{task}" );

        project.addOrReplaceTarget( target );
    ]]></script>
    </sequential>
</for>
martin clayton
  • 76,436
  • 32
  • 213
  • 198
  • I'm not so crazy about generating another xml file to accomplish this. I think I'd rather just suffer some repetition in my build.xml. i've taken a look at the script task but have failed to find any documentation that would indicate how i'd go about creating targets. – Matthew Kime Mar 07 '15 at 02:35
  • @Matthew Added a script approach. The documentation for using the API is the source itself. [grepcode](http://grepcode.com/file/repo1.maven.org/maven2/org.apache.ant/ant/1.9.4/org/apache/tools/ant/taskdefs/Execute.java?av=f) is one way to peruse. – martin clayton Mar 07 '15 at 13:44
  • A couple of things I learned along the way - load('nashorn:mozilla_compat.js'); // since i'm on Java 8 and using importClass, in other words Rhino compatibility exec.createArg( ).setValue( "--target_env=" + target_env ); // for some reason ant properties are available to js directly although the task param isn't. ug, formatting isn't my strong suit – Matthew Kime Mar 07 '15 at 15:20