Iterating through a properties file in Ant - ant

So I need to write an Ant script that iterates through a properties file and uses the keys from that file to pull values from a few other properties files (using the same key).
I haven't been able to find any examples remotely similar to what I'm trying to accomplish. Is this something thats plausible with Ant? I know its rather old. I've never used Maven but I believe our platform would be able to support that if this isn't possible in Ant

Using the <script> command you can execute arbirary java/javascript code and do not supported code.
For your case, perhaps some similar to :
<scriptdef name="iterateprops" language="javascript">
<attribute name="src" />
<![CDATA[
importClass(java.util.Properties);
importClass(java.io.FileInputStream);
var src = attributes.get("src");
var properties = new Properties();
properties.load(new FileInputStream(src));
var names = properties.propertyNames();
while(names.hasMoreElements()) {
println (names.nextElement());
}
]]>
</scriptdef>
An later use it:
<iterateprops src="file.properties" />

Related

How to set the modified date of a file to that of the most recently modified of a list of files?

During a build process using Ant, I want to update the last modified date of a generated file. I am concatenating (using concat task) several files to generate this file, and I want to set the modified date of this file to the date of the most recently modified of the source files.
I don't see any option in the touch task to use several files as the source for the date.
Here's an example solution:
<scriptdef name="filedate" language="javascript">
<attribute name="file"/>
<attribute name="property"/>
<![CDATA[
var file_name = attributes.get( "file" );
var property_to_set = attributes.get( "property" );
var file = new java.io.File( file_name );
var file_date = file.lastModified();
project.setProperty( property_to_set, file_date );
]]>
</scriptdef>
<last id="last.dir">
<sort>
<fileset dir="folder" includes="*" />
<date />
</sort>
</last>
<filedate file="${ant.refid:last.dir}" property="file.ts" />
<touch file="concat.file" millis="${file.ts}" />
The scriptdef is derived from this answer by David W. and simplified as we don't need to format the date-time, we can just use the "epoch milliseconds" that Java File lastModified() provides and the Ant touch task expects.

Ant: Importing specific environment variables

A quick question, but probably one that doesn't have an answer (or at least an answer I want):
I want to only import a specific set of environment variables into an Ant script. I know I can import the entire environment via the <property environment="env"/> task. However, I'm using Jenkins and it just seems silly to import the whole environment because I want a couple of variables like $BUILD_NUMER and $JOB_NAME
I know I could do something like this:
$ ant -DBUILD_NUMBER=$BUILD_NUMBER package
I thought someone might have figured out a way to do this via a resource collection. If not, I'll just have to accept the fact that all environment variables are imported.
Possibly cheating ... use a scriptdef?
<scriptdef name="envproperty" language="javascript">
<attribute name="name" />
<attribute name="fromenv" />
<![CDATA[
importClass( java.lang.System );
project.setProperty(
attributes.get( "name" ),
System.getenv( attributes.get( "fromenv" ) )
);
]]>
</scriptdef>
<envproperty name="BUILD_NUMBER" fromenv="BUILD_NUMBER" />
<envproperty name="JOB_NAME" fromenv="JOB_NAME" />

ANT: Load file names and extract data from the file names

Hello I wasn't quite sure how to title this question, but I'll explain why I'm trying to do.
First of all, I have a folder that contains SQL scripts in a specific format, which is updateXtoY.sql, where X and Y are integers. What I need is to know which Y is the highest number. (basically, to know which is the latest script)
So if I have in my folder "scripts/" 3 files:
update3to5.sql
update2to5.sql
update1to6.sql
the result I need is to have assign a property 'latest.version' the value of 6.
From that point I can easily run the script. So the problem I have is 3-fold:
1- How to load the file names into a data structure.
2- How to iterate over the data structure.
3- How to evaluate each file name so that I can extract the "Y" part of the file and get the highest value. (I'm reading on regex right now)
I'm new to ANT and I'm not sure if this is possible and/or feasible.
Thanks for any suggestions.
The first part of the task - getting the filenames into a 'structure' is best done using a FileSet - say for SQL scripts in a directory called scripts:
<fileset dir="scripts" includes="*.sql" id="versions" />
That creates an Ant resource collection that can be referred to using the id versions.
The collection knows about your SQL script files.
Using (as you suggest) a regexp mapper, we can convert the set of files into a collection of strings, holding just the version parts from the filenames:
<mappedresources id="versions">
<fileset dir="scripts" includes="*.sql" />
<regexpmapper from="update.*to(.*).sql" to="\1" />
</mappedresources>
In this example versions now holds a 'list', which would be "5,5,6" for your example files.
It gets trickier now, because you probably need to carry out a numeric sort on what is a list of strings - to avoid 10 sorting as 'less' than 9. Ant ships with an embbeded Javascript interpreter, so you could use that to find the maximum. Another option would be to make use of the numeric sorting capability that ant-contrib has to offer.
Here's a Javascript 'max finder':
<scriptdef name="numeric_max" language="javascript">
<attribute name="property" />
<attribute name="resources_id" />
<![CDATA[
var iter = project.getReference(
attributes.get( "resources_id" )
).iterator( );
var max_n = 0.0;
while ( iter.hasNext() )
{
var n = parseFloat( iter.next() );
if ( n > max_n ) max_n = n;
}
project.setProperty( attributes.get( "property" ), max_n );
]]>
</scriptdef>
That defines a new Ant XML entity - numeric_max - that looks like a task, and can be used to find the numeric maximum of a collection of strings.
It's not perfect - there's no validation of the strings, and I've used floats rather than ints.
Combining that with the mappedresources above:
<mappedresources id="versions">
<fileset dir="scripts" includes="*.sql" />
<regexpmapper from="update.*to(.*).sql" to="\1" />
</mappedresources>
<numeric_max property="latest.version" resources_id="versions" />
<echo message="Latest SQL script version: ${latest.version}." />
When I run that with your three files I get:
[echo] Latest SQL script version: 6.

How can I allow an Ant property file to override the value set in another?

I have an ant file that does the following:
<property file="project.properties" description="Project configuration properties"/>
<property file="build-defaults.properties" description="default build configuration."/>
<property file="build.properties" description="local build configuration overrides"/>
I want to have defaults set in build-defaults.properties (which is checked in to SCM) but allow developers to override values in a local build.properties so that they can work with local paths.
The problem is, it doesn't seem to be working; I've set this up, created an override in build.properties, but the value of my path remains the one set in build-defaults.properties. How do I accomplish this?
The initial problem with your set up is that you've got build.properties and build-defaults.properties reversed.
Ant Properties are set once and then can never be overridden. That's why setting any property on the command line via a -Dproperty=value will always override anything you've set in the file; the property is set and then nothing can override it.
So the way you want this set up is:
<property file="build.properties" description="local build configuration overrides"/>
<property file="project.properties" description="Project configuration properties"/>
<property file="build-defaults.properties" description="default build configuration."/>
This way:
Anything set at the command line takes precedence over build.properties
Anything set in build.properties overrides other values
etc. on down the line.
Actually ant properties may be overriden. See the documentation of the property task:
Normally property values can not be changed, once a property is set,
most tasks will not allow its value to be modified.
One of the tasks that are able to override the property value is script. Also any custom task may use this backdoor. Other proposals are in question Ant loadfile override property. This is against the spirit of ant and usually unnecessary. But it's good to know that, because I just had an opposite problem: why the property value changed although it is immutable.
Here is a sample target that uses script task to change the value of a property. It shows the basic methods to work with properties. All methods are described in Ant Api which is not available online. You need to download the Ant Manual. In its api directory there is the api documentation.
<target name="t1">
<property name="a" value="one" />
<script language="javascript">
sProp = project.getProperty("a");
sProp = sProp.replace("e", "ly");
project.setProperty("a", sProp);
project.setNewProperty("a", "new value");
</script>
<property name="a" value="two" />
<echo>a=${a}</echo>
</target>
How to easily setup the script task? Making the script task running with beanshell language is a bit tricky and non-trivial, but it's explained in this answer. However as Rebse noted, using javascript language is supported out of the box in jdk 6.
Ant property can't be overwritten unless using macro and javascript plug-in to do:
Step 1: define a macro function to overwrite property
<!--overwrite property's value-->
<macrodef name="set" >
<attribute name="name"/>
<attribute name="value"/>
<sequential>
<script language="javascript">
<![CDATA[
project.setProperty("#{name}", "#{value}");
]]>
</script>
</sequential>
</macrodef>
Step 2: use the macro in the ant xml
<set
name="your_target_property"
value="your_value" or "${another_property}"
</set>

Text manipulation in ant

Given an ant fileset, I need to perform some sed-like manipulations on it, condense it to a multi-line string (with effectively one line per file), and output the result to a text file.
What ant task am I looking for?
The Ant script task allows you to implement a task in a scripting language. If you have JDK 1.6 installed, Ant can execute JavaScript without needing any additional dependent libraries. The JavaScript code can read a fileset, transform the file names, and write them to a file.
<fileset id="jars" dir="${lib.dir}">
<include name="*.jar"/>
</fileset>
<target name="init">
<script language="javascript"><![CDATA[
var out = new java.io.PrintWriter(new java.io.FileWriter('jars.txt'));
var iJar = project.getReference('jars').iterator();
while (iJar.hasNext()) {
var jar = new String(iJar.next());
out.println(jar);
}
out.close();
]]></script>
</target>
Try the ReplaceRegExp optional task.
ReplaceRegExp is a directory based task for replacing the occurrence of a given regular expression with a substitution pattern in a selected file or set of files.
There are a few examples near the bottom of the page to get you started.
Looks like you need a conbination of tasks:
This strips the '\r' and '\n' characters of a file and load it to a propertie:
<loadfile srcfile="${src.file}" property="${src.file.contents}">
<filterchain>
<filterreader classname="org.apache.tools.ant.filters.StripLineBreaks"/>
</filterchain>
</loadfile>
After loading the files concatenate them to another one:
<concat destfile="final.txt">
...
</concat>
Inside concat use a propertyset to reference the files content:
<propertyset id="properties-starting-with-bar">
<propertyref prefix="src.file"/>
</propertyset>
rodrigoap's answer is enough to build a pure ant solution, but it's not clean enough for me and would be some very complicated ant code, so I used a different method: I subclassed ant's echo task to make an echofileset task, which takes a fileset and a mapper. Subclassing echo buys me the ability to output to a file. A regexmapper performs the transformation on filenames that I need. I hardcoded it to print out each file on a separate line, but if I needed more flexibility I could add an optional separator attribute. I also thought about providing the ability to output to a property, too, but it turned out I didn't need it since I echo'ed straight to a file.

Resources