"Override" properties of an imported Ant file - java

I´d like to override some properties of an imported Ant-File which handles most of the basic stuff to deploy an application. First of all I know that Properties in Ant are immutable. But I can think of two ways to "override" properties for my needs and I´d like to know, which one should be the prefered approach, and/or if there are any things to consider by doing it one way or the other.
Imagine we have following master-build-script, which I like to import in my build-script:
<project name="Application" default="build" basedir=".">
<property name="overridden" value="false" />
<target name="build">
<echo message="Value of overridden is ${overridden}" />
</target>
</project>
Now I want to override the overridden property. In both cases I import the master-build-file. I use import over include, because I also want to override some targets. I know of two possible solutions to override them:
1) Since Properties are immutable I can just define the properties I´d like to "override" by defining them before I actually import the master-file:
<project name="MyApplication" default="buildApplication" basedir=".">
<property name="overridden" value="true" />
<import file="master-build.xml" />
<target name="buildApplication">
<antcall target="build" />
</target>
</project>
2) I define Params within the Ant call which seems to redefine a properties value as well:
<project name="MyApplication" default="buildApplication" basedir=".">
<import file="master-build.xml" />
<target name="buildApplication">
<antcall target="build" >
<param name="overridden" value="true"/>
</antcall>
</target>
</project>
In both cases the result is:
build:
[echo] Value of overridden is true
BUILD SUCCESSFUL
Would anyone be so kind and could explain me the difference and might also explain why I should use one way over the other, or even should use a different approach to get the same result?

The main difference between your first and second examples is that in the second case the property is only defined for the sub-build, which might sometimes be what you want, rather than globally for the build.
Other ways you can preemptively define properties globally...
Pass as JVM args when you execute ant, e.g.
ant -Doverridden=true
Define the properties in a file and load at start of your build, e.g.
build.properties:
overridden=true
build.xml:
<project name="MyApplication" default="buildApplication" basedir=".">
<property file="build.properties/>
A useful variant is to load user definable properties from the user home directory, e.g. something like:
<property file="${user.home}/MyApplication/build.properties/>
The advantage of both of these methods over the examples you gave are that the properties are overridden without changing the build file.

Ant versions 1.8 and later come with the <local/> task which allows you to declare a property as local. This might be what you need.
Another choice is to use <macrodef> which allows you to define your own macro (which is better than using <antcall> since <antcall> can break Ant's ability to create a execution matrix).

Related

How to properly use a target of an importing build file?

I am pretty new to ant and extended an existing build file a.xml:
I created an additional build file b.xml which is imported by a.xml. The target example defined in b.xml provides a new (optional) feature when building with ant, that requires the ${version} property. The ${version} property is created/provided by the target determine.version in a.xml.
To ensure the existence/access of ${version} in b.xml I've added the depends="determine.version" parameter to the target example in b.xml. Thereby, I've added an dependency to a non-resolvable target, when running b.xml solely (which is not intended).
The question:
I don't feel comfortable with this solution and want to ask whether there is a cleaner way to ensure the existence and access of ${version} in b.xml.
a.xml
<project name="a" basedir="." >
...
<target name="determine.version">
...
</target>
...
<import file="b.xml" as="b" />
</project>
b.xml
<project name="b" basedir="." >
...
<target name="example" depends="determine.version">
...
do s.th. with ${version}
...
</target>
</project>
I am thankful for any advice, recommendation or comment to this!
If you wish to keep b.xml such that it will work when used directly / independently of a.xml, one solution would be to setup b.xml with its own determine.version target and override that target in a.xml.
b.xml would need:
<target name="determine.version">
... does nothing or whatever is needed to satisfy b's "example" target when a.xml not used
</target>
a.xml can then override determine.version and depend on the default implementation in b.xml so it is called in the correct position when evaluating b.xml's "example" target. The way to reference a dependency of imports is to specify {buildfile}.targetname - in this case b.determine.version - assuming that you want both a+b's determine.version to run:
<target name="determine.version" depends="b.determine.version">
...
</target>
Alternatively
The above setup is awkward in that the names used in a/b xml are linked. Instead you could try out extension-point in ant builds which makes names in a depend on b, but not both ways. It would need something like this:
b.xml - B declares an extension point that referenced by "example":
<extension-point name="setupversion" />
<target name="example" depends="setupversion">
a.xml - A declares a target (which does not need to be called determine.version) that will run before all targets that depend on "setupversion"
<import file="b.xml" />
<target name="determine.version" extensionOf="setupversion">

Running ant-target to send a mail via java

I´m a pretty new to ant and I´m trying to send an email within an ant-target which is called by Java. I´m using the Netbeans IDE.
Ant:
<project name="AntTargets" default="main" basedir=".">
<description>Builds, tests, and runs the project AntTargets.</description>
<import file="nbproject/build-impl.xml"/>
<target name="run" depends="jar">
<java classpathref="AntTargets.classpath" fork="true" classname="Client" />
</target>
<target name="main">
<echo message="******************[MAIN]******************"/>
<property file="nbproject/build.properties"/>
<property name="to" value="${builder}" />
<property name="from" value="${builder}" />
<property name="server" value="${server}" />
<property name="port" value="${port}" />
<mail mailhost="${server}" mailport="${port}" subject="Test build">
<from address="${from}"/>
<replyto address="${to}"/>
<to address="test#domain.de"/>
<message>The build has been completed</message>
</mail>
<echo message="Email to ${to} from ${from} has been sent using the server ${server} on port ${port}."/>
</target>
</project>
Java:
import java.io.File;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
public class AntTargets {
public static void main(String[] args) {
//get new file
File buildFile = new File("build.xml");
//create new project
Project p = new Project();
p.setUserProperty("ant.file", buildFile.getAbsolutePath());
p.init();
//initialize helper
ProjectHelper helper = ProjectHelper.getProjectHelper();
p.addReference("ant.projectHelper", helper);
//parse and execute
helper.parse(p, buildFile);
p.executeTarget(p.getDefaultTarget());
}
}
When I execute the ant part directly, the properties file is found, read and works overall but the mail-part gives me this error:
Failed to send email: javax.mail.internet.MimeMessage
build.xml:16:
java.lang.ClassNotFoundException: javax.mail.internet.MimeMessage
When I call it with java I get:
Reference AntTargets.classpath not found.
BUILD FAILED (total time: 0 seconds)
I would greatly appreciate any help and tip overall.
The issue is Ant doesn't know what AntTargets.classpath is.
You must define a classpath variable and give it id="AntTargets.classpath. In this path variable you should define the physical locations of the libraries you use. (You should look to creating path variables to reduce redundancies in code, particularly when you want to use the same path in different ant targets.)
Here's an example: Let's say that your project requires the Gson library at runtime.
First, create a path for that library. Let's assume you have your libraries in a lib directory.
<path id="library.gson-2.8.0.classpath">
<pathelement location="${basedir}/lib/gson-2.8.0.jar"/>
</path>
Do the same for other dependencies as well.
Next, create a path that will contain all these dependencies, so you can refer to all the dependencies as one variable. This will be your AntTargets.classpath
<path id="AntTargets.classpath">
<path refid="library.gson-2.8.0.classpath"/>
...... <!-- Add paths for your other dependencies by refid here-->
</path>
Now, when you use AntTargets.classpath Ant will know what that is and use it to run your jar.
I notice that you use this in the run target, so if you're trying to run the Client class from your own jar, include that in your classpath as well.
Also keep in mind that "Client" may not be enough information--you must provide the fully qualifying name of the class, including package names, e.g. something like "com.package.Client".
EDIT:
I just noticed the exception you're getting from running directly with Ant. You need to include the javax.mail library in this path variable we create. That should resolve that issue.
EDIT 2:
I seem to have misunderstood. I thought that your ant task was trying to call your Java program which would send the mail, but it couldn't since it didn't know where the javax.mail.jar is, and so I instructed you to add it to the classpath.
But what you want to do is:
Use the Ant mail task to send a mail.
And in the Java you want to read the build file and execute the target that sends the mail.
Here's some information on that: mail is an Ant Task that requires the javax.mail library, if you want to use certain arguments. Looking at a few questions like this and this, I see that you should put ${ANT_HOME}/lib, i.e. where your ant.jar is, so that Ant's classloader can pick it up and use it. In that case, if my assumption about what you want is correct, then you should be able to get rid of AntTargets.classpath, because really, your build file doesn't need to know how you call any of it's targets.

Java Ant - how to pass a ProGuard task arguments and use them inside a configuration file?

I have this in my build.xml:
<target depends="build-jar" name="proguard">
<taskdef resource="proguard/ant/task.properties" classpath="tools/proguard4.6/lib/proguard.jar" />
<proguard configuration="ant/proguard.conf" />
</target>
It works fine.
Inside the configuration file (i.e "ant/proguard.conf") I'm trying to access properties defined in this build.xml file but I'm always getting this kind of error:
Value of system property 'jar.final_name' is undefined in '<jar.final_name>' in line 1 of file '.......\ant\proguard.conf'
The error is clear. Question is how I do what I'm trying to?
If I'd do it the "Embedded ProGuard configuration options" way I could use these properties like any other property in build.xml, but I'm trying to keep the files separate.
How do I do that then?
By default, Ant doesn't provide a way to set java system properties for its tasks. You can only specify -D options in the ANT_OPTS system variable when starting Ant itself.
I'll consider supporting the use of Ant properties in referenced ProGuard configurations (being the developer of ProGuard).
For the time being, an acceptable solution might be to specify input and output jars in Ant's XML-style:
<proguard configuration="ant/proguard.conf">
<injar name="${injar}" />
<outjar name="${outjar}" />
<libraryjar name="${java.home}/lib/rt.jar" />
</proguard>
This part of the configuration is more closely tied to the Ant script anyway.

Ant: Referencing dynamic properties

I want to use the same ant script to do a build in either my local windows environment or on our redhat build server.
I have a variable 'buildDirectory' in two files (build_unix.properties & build_windows). I want to set variables depending on the environment.
<osfamily property="os.family"/>
<property file="./build_${os.family}.properties" />
<property name="tmp-base.folder" value="${buildDirectory}/tmp/"/>
I also tried
<if>
<os family="unix"/>
<then>
<property file="./build_unix.properties" />
</then>
<else>
<property file="./build_windows.properties" />
</else>
</if>
Any ideas?
Are you asking how you can automatically set the os.family property in the first place?
If so, here's one approach:
<available file="C:\\" property="os.family" value="windows" />
<available file="/usr/bin/" property="os.family" value="unix" />
Ah! Edited question makes it more clear what you are asking (not this), and I see that you're using the "osfamily" task from ant-contrib to determine the OS family. But I'll leave this answer up for anyone who is just using ant without ant-contrib.
I would expect your if...then...else version to work. As it apparently isn't I would add some extra echo's to make sure your build is doing what you think it is doing.
An echo inside the then and the else would let you know for certain what path is being executed.
Add a prefix to the properties (e.g. <property file="..." prefix="test" />) and then add an <echoproperties prefix="test" /> to ensure the properties you think are being loaded are.
you can have two files : build-unix.properties and build-windows.properites and in your init target, you simply do a copy:
<copy file="build-${os.family}.properties" tofile="build.properties">
your main build.xml could simply reference the build.properties file.
This could be quite tricky to make it work properly so what I would do is to keep all properties of the build server in the build.properties file and allow local override (for your Windows box) in a build-local.properties. You can also move properties to a build-common.properties so that your build file would include the properties files in the following order
<property file="build-local.properties"/>
<property file="build-common.properties"/>
<property file="build.properties"/>

Is there a way to generalize an Apache ANT target?

We have an Apache ANT script to build our application, then check in the resulting JAR file into version control (VSS in this case). However, now we have a change that requires us to build 2 JAR files for this project, then check both into VSS.
The current target that checks the original JAR file into VSS discovers the name of the JAR file through some property. Is there an easy way to "generalize" this target so that I can reuse it to check in a JAR file with any name? In a normal language this would obviously call for a function parameter but, to my knowledge, there really isn't an equivalent concept in ANT.
I would suggest to work with macros over subant/antcall because the main advantage I found with macros is that you're in complete control over the properties that are passed to the macro (especially if you want to add new properties).
You simply refactor your Ant script starting with your target:
<target name="vss.check">
<vssadd localpath="D:\build\build.00012.zip"
comment="Added by automatic build"/>
</target>
creating a macro (notice the copy/paste and replacement with the #{file}):
<macrodef name="private-vssadd">
<attribute name="file"/>
<sequential>
<vssadd localpath="#{file}"
comment="Added by automatic build"/>
</sequential>
</macrodef>
and invoke the macros with your files:
<target name="vss.check">
<private-vssadd file="D:\build\File1.zip"/>
<private-vssadd file="D:\build\File2.zip"/>
</target>
Refactoring, "the Ant way"
It is generally considered a bad idea to version control your binaries and I do not recommend doing so. But if you absolutely have to, you can use antcall combined with param to pass parameters and call a target.
<antcall target="reusable">
<param name="some.variable" value="var1"/>
</antcall>
<target name="reusable">
<!-- Do something with ${some.variable} -->
</target>
You can find more information about the antcall task here.
Take a look at Ant macros. They allow you to define reusable "routines" for Ant builds. You can find an example here (item 15).
Also check out the subant task, which lets you call the same target on multiple build files:
<project name="subant" default="subant1">
<property name="build.dir" value="subant.build"/>
<target name="subant1">
<subant target="">
<property name="build.dir" value="subant1.build"/>
<property name="not.overloaded" value="not.overloaded"/>
<fileset dir="." includes="*/build.xml"/>
</subant>
</target>
</project>
You can use Gant to script your build with groovy to do what you want or have a look at the groovy ant task.

Categories