Transforming multiple maps with Ant

198 views
Skip to first unread message

Frank Ralf

unread,
Jul 14, 2015, 8:32:48 AM7/14/15
to dita-o...@googlegroups.com
Hi,

I want to run a PDF transformation on multiple maps in one go using an Ant build script. I suppose I have to create a separate target for each map, is that correct? Or can I set multiple <property name="args.input"...> values for the same target?

Kind regards,
Frank

que...@gmail.com

unread,
Jul 14, 2015, 11:33:27 AM7/14/15
to dita-o...@googlegroups.com
You can include the calls to ant one after another in the same task, e.g.:

    <target name="go">
        <ant antfile="${dita.dir}\build.xml">
            <property name="args.input" value="/tmp/map1.ditamap"/>
</ant>
        <ant antfile="${dita.dir}\build.xml">
            <property name="args.input" value="/tmp/map2.ditamap"/>
</ant>
</target>

Properties are "immutable" (can only be set once). But, invoking ant from ant, gets around that feature. So, if there is other logic in your ant script before invoking dita-ot you might bump into the immutable variables phenomenon.

Kendall

Frank Ralf

unread,
Jul 14, 2015, 11:56:36 AM7/14/15
to dita-o...@googlegroups.com
Hi Kendall,

Many thanks for your reply and the explanation. As an Ant newbie this helps me a lot!

Cheers,
Frank

Frank Ralf

unread,
Jul 17, 2015, 4:58:46 AM7/17/15
to dita-o...@googlegroups.com
JFTR, here's Eliot Kimber's reply which only reached me by email (thanks, Eliot):

As far as I know you need to create a separate target for each map.
There might be a way to have Ant iterate over a list of map filenames and call the main transform template but if there is I don't know how to do it.

If the set of maps is large it's relatively easy to generate the Ant targets using regular expression search and replace in oXygenXML:

1. Get the list of map files into a text file, e.g.:

dir /s /b *.ditamap > maps.txt

2. Work out what the Ant target will look like so you have the model for the markup

3. Edit the maps.txt file  and apply a search and replace like:

Find: ^(.+\\([a-z0-9_\-]+\.ditamap))$
Replace: <target name="$2">...$1...</target>

Where $2 will be the base filename of the ditamap and $1 is the full path to the map and "..." is the guts of the Ant target you want to generate.

My regular expression may not be exactly correct but it will be close.

Under Windows you can a FOR loop to iterate over a list of files, which might be easier:

*for*/R . %%F in (*.ditamap) do (
  ant -Dargs.input=%%F -Dtranstype=pdf ... {remaining arguments}
)

Under OSX or Linux you can use find -exec:

find . -name *.ditamap -exec ant -Dargs.input={} -Dtranstype=pdf ...
{remaining arguments} \;

(or something close to that--there are subtle details around quoting that I have to always google to get good answers for).

Cheers,
Eliot

Kendall Shaw

unread,
Jul 17, 2015, 12:03:46 PM7/17/15
to dita-o...@googlegroups.com
I suspect his main point is that he doesn't know of a way to invoke
DITA-OT once on a list of maps, nor do I.

If you do this, for example:


<project default="go">

<target name="go">
<ant antfile="build.xml">
<property name="args.input" value="samples/hierarchy.ditamap"/>
<property name="transtype" value="pdf"/>
</ant>
<ant antfile="build.xml">
<property name="args.input" value="samples/taskbook.ditamap"/>
<property name="transtype" value="pdf"/>
</ant>
</target>

</project>

You invoke ant once and it invokes DITA-OT twice.

If you have additional logic in the ant script, or it is a simple case
like the example above, some people might like it better to modify the
ant script, because a non-programmer might find it slightly easier to
have the "declarative" syntax in the ant file.

Since scripting in ant is icky to me and other people, and the list of
maps probably changes, I like his idea better because you don't have to
modify an ant script or have a script that assembles an ant script.

Kendall
> --
> You received this message because you are subscribed to the Google
> Groups "DITA-OT Users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to dita-ot-user...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.


Kendall Shaw

unread,
Jul 17, 2015, 12:15:05 PM7/17/15
to dita-o...@googlegroups.com
To put it another way. The literal answer to the question is: No, you
don't need a separate target per map. There are various ways that one
can avoid adding a separate target per map in ant script to process the
maps.

Kendall

Eliot Kimber

unread,
Jul 17, 2015, 3:28:23 PM7/17/15
to Kendall Shaw, dita-o...@googlegroups.com
I did some more research on this because I wanted for some stuff I'm doing and as far as I can tell, there's no way using out-of-the-box Ant to apply the same target to list of files or directories.

There is a contributed "for each" task that can do it, but that requires finding the contribution and installing it, so if you're looking for a stock-Ant solution there isn't one using only Ant.

With both Windows batch and bash shell it's fairly easy to apply a command to a list of files although the details are not necessarily easy to work out given the insane syntax of both scripting languages.

Cheers,

Eliot
--
Eliot Kimber

Robert D Anderson

unread,
Jul 17, 2015, 4:01:50 PM7/17/15
to Eliot Kimber, dita-o...@googlegroups.com

I just recently used the contributed "for each" task and it worked well for what I needed. Find all files with a specific extension across a directory (and subdirectories), and run an XSL or other program on them. It was pretty straightforward to add the antcontrib library to DITA-OT as a plugin, and then make use of it in other plugins, so that the "for each" command was then available to other transform targets.

Of course that's not quite the same as having antcontrib available already, when you call Ant as an entry point to DITA-OT, which would require some additional setup on your system.

Robert D Anderson
IBM Authoring Tools Development
Chief Architect, DITA Open Toolkit (http://www.dita-ot.org/)

Inactive hide details for Eliot Kimber ---07/17/2015 14:28:29---I did some more research on this because I wanted for some stufEliot Kimber ---07/17/2015 14:28:29---I did some more research on this because I wanted for some stuff I'm doing and as far as I can tell,

Kendall Shaw

unread,
Jul 17, 2015, 7:04:54 PM7/17/15
to dita-o...@googlegroups.com
For the literal answer to the question, you don't need a separate ant
target for each map. For example, this has one target and and rungs OT
on two maps:

<project default="go">

<target name="go">
<ant antfile="build.xml">
<property name="args.input" value="samples/hierarchy.ditamap"/>
<property name="transtype" value="pdf"/>
</ant>
<ant antfile="build.xml">
<property name="args.input" value="samples/taskbook.ditamap"/>
<property name="transtype" value="pdf"/>
</ant>
</target>

</project>

For, invoking one target from the command line on a list of files with
out of the box ant, you can do it by recursively invoking ant, but it's
ugly and probably not practical. The main idea is the usual first and
rest thing:

<path id="car">
<first>
<resources refid="initial-files"/>
</first>
</path>

<path id="cdr">
<difference> <!-- or allbutfirst in ant 1.9.5 -->
<resources refid="car"/>
<resources refid="initial-files"/>
</difference>
</path>

For the question, how should I invoke dita-ot on a list of files, I
would say that using a script like yours that invokes ant multiple times
is best. But, if you have other logic in ant it can make sense to use
ant. In the later case, using antcontrib can make sense.

Kendall
> it, send an email to dita-ot-users
> +unsub...@googlegroups.com.

Kendall Shaw

unread,
Jul 17, 2015, 8:52:02 PM7/17/15
to dita-o...@googlegroups.com
Here is an example of the recursive processing of a list of files method
using out-of-the-box ant. You would invoke DITA-OT where it is echoing
the file name:

<project default="go">

<macrodef name="do">
<sequential>
<path id="first-file">
<first>
<resources refid="initial-files"/>
</first>
</path>
<path id="fewer-files">
<difference>
<resources refid="first-file"/>
<resources refid="initial-files"/>
</difference>
</path>
<condition property="stop">
<resourcecount refid="fewer-files" when="lt" count="1"/>
</condition>
<resourcecount property="file-count" refid="fewer-files"/>
<condition property="next-target" value="go" else="gogo">
<equals arg1="${target}" arg2="gogo"/>
</condition>
<ant antfile="build.xml" target="${next-target}"
inheritall="false">
<property name="target" value="${next-target}"/>
<property name="went" value="true"/>
<propertyset>
<propertyref name="stop"/>
</propertyset>
<reference refid="first-file" torefid="the-file"/>
<reference refid="fewer-files" torefid="initial-files"/>
</ant>
</sequential>
</macrodef>

<target name="go" depends="init" unless="stop">
<do/>
</target>

<target name="gogo" depends="init" unless="stop">
<do/>
</target>

<target name="init" unless="went">
<path id="initial-files">
<fileset dir=".">
<include name="**/*"/>
</fileset>
</path>
</target>

</project>

It could be useful for something, as long as it only recurses a
relatively small number of times. And, I think it shows some ways of
grappling with the immutable variables phenomenon.

Kendall

Jarno Elovirta

unread,
Jul 18, 2015, 4:31:19 AM7/18/15
to Kendall Shaw, dita-o...@googlegroups.com
IMO, programming in Ant is just about as simple and easy to read as… something that shouldn’t be done. If you want to do something Ant wasn’t intended to be used, you really should use custom tasks from e.g. contrib lib or just roll out your own.

Jarno
signature.asc

Kendall Shaw

unread,
Jul 18, 2015, 12:53:49 PM7/18/15
to dita-o...@googlegroups.com
I think that to. Unless there is a reason to be using ant, or it is
something as simple as a list of invocations, the page on invoking
DITA-OT from ant probably doesn't apply to the task at hand.

Concerning the question about the page on how to invoke DITA-OT from
ant: If I want to process more than one map (in the ant script in
question), do I need to have a separate target for each map (meaning
that you would then need to invoke ant once per map specifying a
different target), I think the answer is no, because you can have one
target, call ant once and have it invoke dita-ot on each map.

If your task involves finding maps to apply, probably not using ant is
best, if that is okay to do for the project. Otherwise, if you have to
use ant and if it is as simple as iterating over files and it's okay to
include ant-contrib, then there is a contribued ant task for that that
you can use.

Kendall

Frank Ralf

unread,
Jul 20, 2015, 7:02:56 AM7/20/15
to dita-o...@googlegroups.com
Many thanks to all of you for these contributions and thorough explanations!

To broaden the scope, my actual use case is using Jenkins for automaticly building PDF output from a list of DITA maps that reside in Subversion. Ant was just my first idea because it is mentioned prominently in the DITA-OT documentation. However, what I've learned from this thread is that I definitely should explore other options for triggering the DITA-OT from Jenkins.

Kind regards,
Frank

Frank Ralf

unread,
Oct 12, 2015, 4:06:26 AM10/12/15
to DITA-OT Users
Just a follow-up:
Because I want to keep as much of the configuration in Ant as possible, I ended with using an Ant for loop for reading a text file with a list of the map URLs and create a combinded build script for the DITA-OT.

I've also stumbled across another Ant trick which I haven't tried yet, though:

Set the @inheritall attribute in each antcall to “false”. That way, you can specify the peoperty multiple times.

<target name="all">
 
<antcall target="target1" inheritall="false" />
 
<antcall target="target2" inheritall="false" />
</target>
Source: http://ditawizard.com/bath-processing-of-ant-tasks-in-dita-ot/

Kind regards,
Frank

Eero Helenius

unread,
Oct 12, 2015, 4:27:35 AM10/12/15
to DITA-OT Users
Hi Frank,

I'm probably a bit late on this, but just thought I'd point you to an Ant task I created for my own use for precisely this sort of thing a while back:

https://github.com/eerohele/dottask

The sample buildfile shows how you can use it in your own buildfile:

https://github.com/eerohele/dottask/blob/master/samples/build.xml

FWIW, I'm also currently working on a Gradle plugin that does (among other things) the same thing, but that's not quite ready for prime time yet.

Best,

--
Eero

Frank Ralf

unread,
Nov 2, 2015, 4:56:51 AM11/2/15
to DITA-OT Users
Hi Eero,

Sorry for my late reply. Many thanks for this pointer. I hope I will be able to test your Ant task soon. Will report back.

Kind regards,
Frank
Reply all
Reply to author
Forward
0 new messages