Ant: Importing specific environment variables - ant

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" />

Related

ANT Build: Can the token itself be parsed from other values from within the property file?

Can the token itself be parsed from other values from within the property file?
Is it possible to evaluate the token key, without hardcoding the token? Can the token itself be parsed from other values from within the property file?
For example, if the properties file has the following tokens (test.properties):
module_no = 01
module_code = bb
title_01_aa = ABC
title_02_aa = DEF
title_03_aa = GHI
title_01_bb = JKL
title_02_bb = MNO
title_03_bb = PQR
Contents of build.xml
<?xml version="1.0" encoding="utf-8"?>
<project default="repl">
<property file="test.properties" />
<target name="repl">
<replace file="test.txt" token="module_title" value="title_${module_no}_${module_code}" />
</target>
</project>
Sample content with text:
Welcome to module_title.
The replace task will result in:
Welcome to title_01_bb.
How to achieve this instead?
Welcome to JKL.
This might be very basic, but please do guide me in the right direction. Thank you.
Nested property expansion does not work by default in Ant as described in the documentation:
Nesting of Braces
In its default configuration Ant will not try to balance braces in property expansions, it will only consume the text up to the first closing brace when creating a property name. I.e. when expanding something like ${a${b}} it will be translated into two parts:
the expansion of property a${b - likely nothing useful.
the literal text } resulting from the second closing brace
This means you can't use easily expand properties whose names are given by properties, but there are some workarounds for older versions of Ant. With Ant 1.8.0 and the the props Antlib you can configure Ant to use the NestedPropertyExpander defined there if you need such a feature.
If you check the workarounds link, one solution is to use a macrodef to copy the property:
<property file="test.properties" />
<target name="repl">
<gettitleprop name="titleprop" moduleno="${module_no}" modulecode="${module_code}" />
<replace file="test.txt" token="module_title" value="${titleprop}" />
</target>
<macrodef name="gettitleprop">
<attribute name="name"/>
<attribute name="moduleno"/>
<attribute name="modulecode"/>
<sequential>
<property name="#{name}" value="${title_#{moduleno}_#{modulecode}}"/>
</sequential>
</macrodef>

Iterating through a properties file in 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" />

Referencing global Ant property after <local> is used

I can create a local property in Ant within a "block" scope.
But how can I now reference the global property within the same block?
<property name="myprop" value="global"/>
...
<sequential>
<local name="myprop"/>
<property name="myprop" value="local"/>
<echo message="my local prop is ${myprop}"/> //<-- this works fine
<echo message="my global prop is ????"/> //<-- HOW?
</sequential/>
No ant-contrib please.
I'd also like to keep this in Ant, not resorting to JS.
The only thing I could think of was "copying" the global property under a different name to be used in this block
<local name="myglobprop"/>
<property name"myglobprop" value="${myprop}"/>
<local name="myprop"/>
<property name="myprop" value="local"/>
<echo message="my global prop is ${myglobprop}"/>
But that is rather unsightly and seems redundant. I am really just looking for an out-of-scope property reference method.
Edit - use case
My attempt to coerce Ant to do delayed expansion:
Let's say I have a property whose value is a combination of several other properties.
<property name="mycmdline" value="${cmd}=${type}"/>
If ${cmd} and ${type} are known before the above property, all is great. However in my case, these values are not defined (no property set). These values only become known at later stage inside a build macrodef.
I have another simple macrodef that will perform delayed expansion:
<property name="mycmdline" value="${cmd}=${type}"/>
...
<macrodef name="property-expand">
<attribute name="name"/>
<attribute name="value"/>
<sequential>
<fail if="#{name}" message="Property #{name} is already set"/>
<property name="#{name}" value="#{value}"/>
</sequential>
</macrodef>
Finally my build macrodef would contain the following snippet:
<local name="cmd"/>
<local name="type"/>
<local name="mycmdline"/>
<property name="cmd" value="#{cmd}"/>
<property name="type" value="#{type}"/>
<property-expand name="mycmdline" value="${mycmdline}"/>
The last line is obviously wrong. I am creating a (now local) property called mycmdline with expanded value of global (now out of scope) ${mycmdline}. The macrodef for delayed expansion works great on it's own, but the problem is that the global ${mycmdline} is out of scope and cannot be referenced.
What I really wanted is:
<property name="mycmdline" value="${cmd}=${type}"/>
...
<!-- In macrodef now -->
<local name="mycmdline"/>
<property-expand name="mycmdline" value="GLOBAL:${mycmdline}"/>
Instead, what I have to do is:
<property name="unexpanded_mycmdline" value="${cmd}=${type}"/>
...
<!-- In macrodef now -->
<local name="mycmdline"/>
<property-expand name="mycmdline" value="${unexpanded_mycmdline}"/>
Why?
It may not look like a lot of difference, but it's about readability. unexpanded_mycmdline and mycmdline are now two different names, when trying to follow the way the value of property gets used through a script, it now makes a disconnected jump from one property name to another (not matter how similar the names may look). The whole unexpanded_ prefix looks out of place and doesn't fit with the rest of the naming conventions, unless I name all my global variables with some prefix, which doesn't make sense either.

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>

Resources