I am working on an Ant script that aims to mimic the ‘shadow folder’ concept of VSS as a stop-gap solution to migrating from VSS to Subversion (without needing to reconfigure our build management software). The script is fairly straightforward:
- create build folder
- extract source files from Subversion into build folder
- translate source files
- copy source and object code files from build folder to final spot used for deployment
All works ‘well’ in the case where I pull down the entire trunk into the build folder and go from there…except that it takes 30+ minutes to ‘build’ and I was hoping to kick this off as part of the post-commit event (again, to mimic VSS’s ‘shadow folder’ feature, which keeps a ‘most recent’ copy of all files in a folder independent of the VSS database and is updated in response to all check-ins).
I’ve been hacking away at the script with the goal to perform a more targeted download of just the files that have been changed (svn diff gives me the list of files, svn export can be used to pull them down individually, I believe).
Now, to the question: is there a way, with Ant Core, to ‘iteratively’ invoke targets for each element of a list? This is a repeat of the question posted here:
In ant, how to apply a target on a list of files (without ant-contrib)?
but I refuse to accept that this cannot be done natively. This post:
How to distribute each element of a list to argument of a task Ant?
gives me hope, but I lack the fundamental understanding of how Ant works (or was designed) to give up. I’m holding out hope that I can do something like:
<?xml version="1.0" encoding="UTF-8"?>
<project name="Test XmlProperty" default="test" basedir=".">
<target name="test">
<xmlproperty file="log.xml" collapseAttributes="true" />
<echo>Affected resources: ${log.logentry.paths.path}</echo>
<macrodef name="svnExportFile">
<attribute name="svnRepoUrls"/>
<sequential>
<loadresource property="lead.path">
<string value="${svnRepoUrls}"/>
<filterchain>
<tokenfilter>
<replaceregex pattern="(\w+)[,${line.separator}]*.*" replace="\1" flags="g"/>
</tokenfilter>
</filterchain>
</loadresource>
<loadresource property="rest.paths">
<string value="${svnRepoUrls}"/>
<filterchain>
<tokenfilter>
<replaceregex pattern="(\w+)[,${line.separator}]*(.*)" replace="\2" flags="g"/>
</tokenfilter>
</filterchain>
</loadresource>
<echo message="${lead.path}"/>
<echo message="${rest.paths}"/>
<svnExportFile svnRepoUrls="${rest.paths}"/>
</sequential>
</macrodef>
<svnExportFile svnRepoUrls="${log.logentry.paths.path}"/>
</target>
</project>
though I don’t have the regular expression working quite right. Is this a major violation of Ant? Should I give up hope and simply run with Ant-contrib? I’ve been slow to adopt it, because it appears there have been no updates since 2008 and I was hoping to limit the amount of dependencies required for all of this stuff to work.
Any thoughts?
Edit: I am getting closer (sort of)…the code above wasn’t quoted correctly in the initial post and I hadn’t had a chance to try it out before posting (needed to leave and didn’t want to lose all I had typed). Anyway, there is no way to stop recursing (and I suspect the properties are immutable, thus will not reset as I’d like them to), so it doesn’t work (yet?).
I tried to accomplish the same basic concept as above with (I know it’s frowned upon, but it’s quite convenient in that it allows you to conditionally execute tasks via ‘s if/unless attributes) — this does not work, though, because Ant doesn’t want to allow recursion, apparently:
‘antcall task calling its own parent target’
and trying to kick off recursion via depends:
‘Circular dependency: svn.export.file <- svn.export.file’
In case anyone is curious, here is the version:
<?xml version="1.0" encoding="UTF-8"?>
<project name="Test XmlProperty" default="test" basedir=".">
<target name="test">
<xmlproperty file="log.xml" collapseAttributes="true" />
<echo>Affected resources: ${log.logentry.paths.path}</echo>
<antcall target="svn.export.file" inheritAll="false">
<param name="svnRepoUrls" value="${log.logentry.paths.path}"/>
</antcall>
</target>
<target name="svn.export.file" if="svnRepoUrls">
<loadresource property="lead.path">
<string value="${svnRepoUrls}"/>
<filterchain>
<tokenfilter>
<replaceregex pattern="(\w+)[,${line.separator}]*.*" replace="\1" flags="g"/>
</tokenfilter>
</filterchain>
</loadresource>
<loadresource property="rest.paths">
<string value="${svnRepoUrls}"/>
<filterchain>
<tokenfilter>
<replaceregex pattern="(\w+)[,${line.separator}]*(.*)" replace="\2" flags="g"/>
</tokenfilter>
</filterchain>
</loadresource>
<echo message="${lead.path}"/>
<echo message="${rest.paths}"/>
<antcall target="svn.export.file" inheritAll="false">
<param name="svnRepoUrls" value="${rest.paths}"/>
</antcall>
</target>
</project>
I admit, it’s ugly…and I’m not too certain if I’m going to stick with it as my solution (I think I might go ahead and install ant-contrib, since this ‘code’ is both ugly and confusing) — but it seems to do the trick and it sort of proves my theory that, yes, you can invoke a target for each element in a list without relying on !