Separation of concerns in Ant

Separation of concerns in AntThere’s nothing wrong with Ant. No, really! True, there’s some nasty Ant files out there. Perhaps that’s because we often treat our build as a second class citizen. How do you keep your build files from becoming bloated and hard to maintain? Break ‘em up!

I’m going to use a classic problem to illustrate this: deployment. Have you seen an Ant build that seemed to know far too much about the innards of the container? First you end up with a lot of properties. Then you need to maintain loads of odd targets to try and make it all work together. We can do better.

Step 1: Break it out. You want to have a totally separate Ant buildfile that you can use for deployment. Acceptance criteria: You can kick off a deploy by calling the separate file with a few properties (like where the deployable is, etc.)

Step 2: Import it. Use Ant’s import task in the body of your buildfile. Never inside a task!

Step 3: Prefix. A colon is a legal character in an Ant property or task name. So make the prefix match the name of the project. Each distinct buildfile should have the name attribute of the project element set with a meaningful name. Use that.

Step 4: Maintain discipline. It doesn’t matter how you do this. Cold showers, if you like. Just make sure that you keep the properties in the right place with the right name.

Here’s an example:

<project name="base" default="deploy">
	<property name="container" value="tomcat" description="The Java container that we use" />
    <import file="${container}.xml" />
</project>

Note that there’s no deploy target in the file. That resides elsewhere. Running the default target will kick off a deploy to Tomcat from …

<project name="tomcat">
	
  	<property name="container:hostname" value="some.great.hostname" />
	<property name="container:remote.user" value="deploy" />
	<property name="tomcat:admin_url" value="http://${container:hostname}/admin" />
	
	<target name="tomcat:deploy" description="This throws a war file at tomcat">
		<echo message="gory details of tomcat deploy go here"/>
	</target>
	<target name="deploy" depends="tomcat:deploy" />
	

</project>


.. here. Note that there are properties with a nice generic prefix. Keep those generic because …

<project name="jboss">
	
  	<property name="container:hostname" value="some.great.other.hostname" />
	<property name="container:remote.user" value="fleury" />
	<property name="jboss:some.jboss.property" value="Paula is brilliant" />
	
	<target name="jboss:deploy" description="This throws a war file at jboss">
		<echo message="jboss deploy goodness here"/>
	</target>
	<target name="deploy" depends="jboss:deploy" />
	

</project>

… all you need to do is pass a different container property to have it deploy elsewhere. What I love about this is that the two implementations cannot exist side-by-side. Only one can be imported, and the property namespace isn’t polluted.

(image thanks to Hansol)

8 thoughts on “Separation of concerns in Ant

  1. dan twining says:

    <target name=”jboss:deploy” description=”This throws a war file at tomcat”> shouldn't this throw a war file at jboss?…Thanks for posting this. Our build scripts are a mess, and I've been meaning to delve in there and sort things out for a while. Now I've got some idea of where to start!That in mind, do you have any experience with the open-source generic ant build script libraries? I've heard of two, neither of which I have links to right now… but they seem to offer generic targets (macros?) that can be extended to fit ones build.

  2. Dan, it should throw a war file at Jboss. You busted me doing a copy and paste. :) Thanks for the link. I'd love to collect a few of these opinionated Ant libraries and road test 'em.

  3. Jay Flowers says:

    I think this is over complicated.Why not simply name the targets deploy and be done with it. All targets are entered into Ant dispatch under two names: for example the tomcat project would have one target named deploy and tomcat.deploy. So if you included both the tomcat and jboss projects you would have three target names in the dispatch tomcat.deploy, jboss.dispatch and dispatch; the entry named dispatch would point to the last loaded project's target dispatch.Basically any target you override is not made unreachable, it is always reachable by the full target name: [project name].[target name]. So why not use this feature instead reinventing?

  4. Jay, you make a valid point about the scoped target names. But I don't think it goes far enough. What I see tends to be a soup of properties and targets strewn throughout a project, broken up into multiple files with random imports. This approach stops leakage and (IMHO) prevents the build becoming a hairball so quickly. I wish I could agree with you – it would make my work easier … !

  5. Jay Flowers says:

    Either way requires discipline. If you are going to exert effort, disciple, I would try to keep it simple. Which ever is easier to know what is the right thing to do…I have been thinking about a post on the build idiom for packages/namespaces for organizing scripts. Have you seen this used effectively?build.targets.xml <-includes all the packages and defines the orchestrating targetsPackages include the following files: [package name].targets.xml, [package name].properties.xml, [package name].macros.xmlPackage list:* compile* ivy* corbertura* package* junit* soapui* etcEach projects' build.xml includes just the build.targets.xml…Makes it easy to know what the right this is to do.

  6. sr says:

    My “deploy” target is depend upon the “war” target, now obviously I can't give that dependency in tomcat.xml ant file. The workaround is something like this<antcall target=”war”/><antcall target=”deploy”/>Do you have any better approach ?

  7. builddoctor says:

    Sr, suggest you email me at 'medic@build-doctor.com' and we'll discuss.Sent from a closed system

Comments are closed.

Follow

Get every new post delivered to your Inbox.

Join 3,285 other followers

%d bloggers like this: