Ant replace property value by property name in a xml file - ant

I need to replace property value in a xml file using the property name.
Ex:
<property name="test-name" value="default-value"/>
I have a target to replace this value . i.e "default-value". User can run this target several times if he's given a wrong value for property test-name he can try again running target with correct value. Therefore i can not use regular expression to replace "default-value". I can only rely on property name. Is there a way to replace property value using it's name in ant ?

The typical way to do that in Ant is to copy or move the files you want to change, using a FilterSet to define the token-value pairs you want to replace in the files.
So in your "template" version of the file you might have something like this
<document>
<element value="#test-name#"/>
</document>
And in your build file you might have something like this:
<property name="my.value" value="default-value"/>
<copy file="${build.dir}/version.txt" toFile="${dist.dir}/version.txt" override="true">
<filterset>
<filter token="test-name" value="${my.value}"/>
</filterset>
</copy>

Since I wanted to allow the user to replace value multiple times ( if he/she given a wrong value) i came up with following solution,
<replaceregexp
replace="property name="my.propertyKey"
value="user.value""
byline="true" file="${basedir}/test.xml">
<regexp pattern="property name="my.property"
value="(.*)""/>
</replaceregexp>
This searches property, using property key and replace entire line.

Related

Store a Value in a Property with Ant or Phing

With Ant or Phing, I need to load a file's contents into a property, run a regular expression on the value of that property, and then store the result of that regular expression in another property. What's the best way to do this?
I can load the file into a property easily (with Phing) like so:
<loadfile file="myfile.txt" property="my.file" />
And I know how to update the file, but I can't seem to figure out how to run a regex on that property, and store the result in a new property for future use.
Any help would be greatly appreciated!
Update
I've been tinkering with it, and this will work. Let me know if there's a streamlined way though! The code below loads a file into a property, then reduces it to only the line that contains the title tag. And then, it runs a regular expression on that line, and stores the contents of that tag in my.prop.
<loadfile file="../index.html" property="my.prop">
<filterchain>
<linecontainsregexp>
<regexp pattern="<title>" />
</linecontainsregexp>
<replaceregexp>
<regexp pattern="[\s\S]+<title>(.+?)</title>" replace="$1" />
</replaceregexp>
</filterchain>
</loadfile>
Update 2
Actually, I ended up using an adhoc task to create my own. Worked perfectly!
You can run an arbitrary command from an ant target like this:
<exec executable="bash">
<arg line="script.sh"/>
</exec>
You can for example store the result of the regexp in a tmp file and then load it into another property the same way as the initial one.
In Phing you could process the content of the property where you loaded the file using the "php" task. See: http://www.phing.info/docs/guide/stable/chapters/appendixes/AppendixB-CoreTasks.html#PhpEvalTask

How to use a glob pattern for a location attribute of a property?

I want to set a property to a filename, for which I know a pattern that will match a unique file. For example, I have the file:
plugins/doc.en_20110608.zip
I define in my ant file:
<property name="doc.zip" location="plugins/doc.en_*.zip" />
I know the pattern will match only one file. The problem is that ant doesn't try to match any pattern at this point, and fails because there is no file named plugins/doc.en_*.zip.
If I'm not using a fileset, it's because the property can be substituted where a fileset is not allowed, like destfile attribute of zip task.
<zip destfile="${doc.zip}" update="true"> ... </zip>
The answer is no I think, but you can use reference/path shortcuts to 'stringify' a fileset into a property. Something like:
<fileset id="doc.zip.fs" dir="plugins" includes="doc.en_*.zip"/>
<property name="doc.zip" value="${toString:doc.zip.fs}" />

concat as resource collection in zip not working?

I'm trying to use <concat> as a resource collection in a <zip> task and, according to the documentation, this should work. I'd like to do this because some of the files I want to include in the zip need to have some properties expanded, so I will also add a <filterchain> to the <concat> to do this. I'd prefer to do it directly rather than copying to a temp location (with property substitution) and including the copy in the zip file.
However, I can't seem to get <zip> to correctly use the <concat> element.
A simplified example of what I have so far:
<zip destfile="target/dist.zip">
<concat>
<fileset file="CHANGES.txt" />
</concat>
</zip>
This creates a zip file containing several directories all named concat (C: (obviously this is on a Windows machine).
What am I missing?
A colleague and I came up with the answer by looking through the <zip> and <concat> source. There are really two answers:
<concat>'s implementation of the ResourceCollection interface is odd, but we understand why.
There's a way around that.
For #1, while <concat> is a ResourceCollection (like FileSet), under the hood it returns as the name of single Resource it contains a hard-coded value:
"concat (" + String.valueOf(c) + ")";
Look familiar?
The name of resources is normally ignored--except by <zip> and its related tasks, which uses the resource name as the ZipEntry name. Since <concat> returns the odd-looking name, that's what we get in the zip file.
I haven't quite figured out why I get multiple entries, but it doesn't matter: the observation leads to a convoluted solution.
Since I know the name of the ZipEntry I want to create, I can use a <mapper> to give the <concat> resource a name. Here's what I came up with in all its glory:
<zip destfile="target/distribution.zip">
<fileset dir=".">
<exclude name="target/**" />
<exclude name="CHANGES.txt" />
</fileset>
<mappedresources>
<concat>
<fileset file="CHANGES.txt" />
<filterchain>
<expandproperties />
</filterchain>
</concat>
<mergemapper to="CHANGES.txt" />
</mappedresources>
</zip>
As my colleague says "In Ant every problem can be solved using a mapper."
This only works for Ant 1.8+ because <mappedresources> was added in that release.
I'm going to post some comments to the Ant mailing list and suggest a couple of enhancements:
Allow a resource name to be specified as an attribute on <concat> when it's being used as a ResourceCollection.
Throw an exception (and don't create a synthetic value) if getName() is called without having a value specified.
Finally, though not directly related, I do wish <expandproperties> could take a <propertyset> so I can control which properties get substituted.
Do you want the final zip to contain a single file or multiple files? As far as I can see, using concat (when done successfully, which isn't done above) would produce a single file, the result of concatenation of all files in the resource collection.
If you want multiple files rather than concatenation, I think intermediate copy is what you'll need.
From Ant manual for the concat task:
Since Apache Ant 1.7.1, this task can
be used as a Resource Collection that
will return exactly one resource.

How do I assign a wild-carded path to a property

I have a directory, let's call it /tmp/foo/bar_v101/scripts that I need to assign to an ant property. In general, I'd do it like this:
<property name="prop" value="/tmp/foo/bar_v101/scripts" />
But, the v101 portion of the property will be changing, so I'd like to do something like this:
<property name="prop" value="/tmp/foo/bar_*/scripts" />
And the value of prop will be automatically expanded to something like this:
/tmp/foo/bar_v101/scripts
Or whatever is currently in the filesystem. Unfortunately, this does not work. Does anyone have any ideas on how to do this?
You can use a resource collection - either fileset or files in this way:
<files includes="/tmp/foo/bar_*/scripts" id="my.file" />
<property name="prop" value="${toString:my.file}" />
Note that this will set prop to a semi-colon separated list of all matches found - which may not be useful if there is more than one.
A fileset works in a similar way, except that it supports a dir attribute and relative paths, rather than the absolute paths used by files.

Valid <property> names in Ant

I'd like to set some properties in my ant build file, the names of which, are based on ant's build in properties. In particular, I'd like to set a property like:
<property name="${ant.project.name}.compiled" value="true" />
However, when I tried this the ${ant.project.home} portion was not expanded.
Is it possible to use the value of properties as the names of other properties, and if so, how?
<property name="name.holder" value="iamholder" />
<property name="${name.holder}.flag" value="true" />
<echoproperties></echoproperties>
result:
[echoproperties] iamholder.flag=true
this is definitely valid ant code and the property iamholder.flag gets the value of true.
If ${name.holder} does not get expanded, it means it has not been set yet (like if the first line in my sample was missing).
Anyways, this still does not quite solve your problem, as you have pretty much no means of getting the value of this property as you don't know it's name and you can't do a nested resolve in pure ant. Depending on what you are trying to do it could still be useful to you though. This one would work (keep in mind, that until 1.8 the value is irrelevant as long as the property is set):
<target name="compile_stuff" unless="${name.holder}.flag">
<echo>compiling...</echo>
</target>
To really get the value of such a property you have to use ant-contrib's propertycopy as suggested in one of the answers. That way you can get the value in a property whose name you know. Just make sure to do the trick just before use and set the override parameter to true (your post implies that you would be setting more properties like these, but without override your final property could not be changed). Another option for working with such properties is to use ant macros.
I think the only way is to echo your values to a .properties file and then load them back.
However, you should ask yourself if you really need it; when I last used ant I tried to do the same thing but concluded I didn't really need to.
Is
$ant.project.home.compiled
not just as useful?
It can be done, a bit ugly, though. You need the < propertycopy > task from ant-contrib for this. The following shows an example
<property name="projectNameCompiled" value="${ant.project.name}.compiled" />
<property name="${projectNameCompiled}" value="true" />
<propertycopy property="final" from="${ant.project.name}.compiled" />
The property final contains the value true.
There are several ways to achieve that, see Ant FAQ
One possible solution via macrodef simulates the antcontrib / propertycopy task but doesn't need any external library.

Resources