Build Refactoring: Extract Target

Your build files probably need some TLC. Sorry. It’s true. If you’re lucky enough to work on a project with clean code (and let’s face it, most codebases aren’t too pretty), then you’re even luckier to have a nice clean build. Wait! Don’t go getting upset, I can help you.

I’ll be introducing some build refactorings in this series. You can refactor code, so why not your build files? I wrote an article about this in the ThoughtWorks Anthology. The Pragmatic Programmers own that now so I’m writing some short articles for this blog. Please consider using the Amazon link above if you want to buy a copy of the book – it will help the cause.

Today’s refactoring is extract target. This example is written in Apache Ant syntax. It holds equally true for NAnt and even other build tools. Consider the following build file:

<project name="unrefactored" default="compile_and_configure_web_app">

	<target name="compile_and_configure_web_app">
		<delete dir="build" />
		<mkdir dir="build/classes" />
		<javac srcdir="src" destdir="build/classes"/>
		<!-- the config file  -->
		<copy file="src/web.xml" todir="build/web.xml">
			<filterset>
				<filter token="DB_HOST" value="oradvdb17"/>
			</filterset>
		</copy>
		<mkdir dir="build/WEB-INF/lib" />
		<!-- build the jar file -->
		<jar  jarfile="build/WEB-INF/lib/webapp.jar"
				basedir="build/classes"
				manifest="src/manifest.mf">
		</jar>
		<!-- and the war file  -->
		<war file="build/webapp.war"
					webxml="build/web.xml"
					manifest="src/manifest.mf"
					basedir="build">
			<include name="**/*.jar" />
		</war>
	</target>

</project>

It’s a bit obtuse, isn’t it? It’s doing several things at once and it’s difficult to see what’s going on.
Let’s start by teasing it apart into it’s constituent parts:

<project name="refactored" default="default">

	<target name="clean">
		<delete dir="build" includeemptydirs="true"/>
		<mkdir dir="build/classes" />
	</target>

	<target name="web.xml">
		<copy file="src/web.xml" todir="build/web.xml">
			<filterset>
				<filter token="DB_HOST" value="oradvdb17"/>
			</filterset>
		</copy>
	</target>

	<target name="webapp.jar">
		<javac srcdir="src" destdir="build/classes"/>
		<mkdir dir="build/WEB-INF/lib" />
		<jar  jarfile="build/WEB-INF/lib/webapp.jar"
				basedir="build/classes"
				manifest="src/manifest.mf">
		</jar>
	</target>

	<target name="webapp.war" depends="webapp.jar,web.xml">
		<war file="build/webapp.war"
					manifest="src/manifest.mf"
					webxml="build/web.xml"

					basedir="build">
			<include name="**/*.jar" />
		</war>
	</target>

	<target name="default" depends="clean, webapp.war" />

</project>

I think that’s much nicer. You don’t need XML comments in the middle of your target line because each smaller target can have a descriptive name.

When you make a minor change, you’re likely only to change one target. You can contain your changes, and if you’re debugging you can run just one target if you need to. And most importantly, it’s readable. I find that I tend to naturally do this as I evaluate a complex build. Tool support would be nice.

Tagged ,

6 thoughts on “Build Refactoring: Extract Target

  1. Andy Palmer says:

    Dan North showed me a nice pattern where each of the build targets does one thing, and the rule target (default in this case) is responsible for determining the order.
    That way, you can see the order of targets at a glance, without having to follow each build target through it’s dependencies

    <target name=”assemble-war” depends=”web.xml, collect-web-app-files” />
    <target name=”default” depends=”clean, assemble-jar, assemble-war” />

  2. @Andy: I think the pattern you’re describing actually does more harm than good, because it removes the dependency information from where it belongs (on each target) and treats Ant more like a procedural scripting language. I prefer Julian’s approach.

  3. simpsonjulian says:

    I prefer Julian’s approach, obviously 🙂 Agree with Andy that one target should do one thing. The argument about placing dependencies in targets or in a target that does flow control will run and run. I can’t help feeling that we need better editors for all this. The Eclipse Ant editor is the most helpful (I use it for NAnt as well) – it allows you to complete on target and property names. Still nothing actually shows you the dependency graph as you edit. That would be sweet.

  4. […] time, you read about the extract target refactoring. Today, I’m going to show you one that I love to perform: delete boolean […]

  5. […] write about it here and here and […]

Comments are closed.

%d bloggers like this: