Wednesday, October 28, 2009

Setting Version in your manifest using ANT

While the Gant folks will tell you that is not natural to use XML for your makefile as it is nicer to use a real scripting language (and perhaps today’s post will enforce this for you!), it is doable and in my opinion readable. The below is an excerpt of our ant buildfile that we use for upping the manifest version in an interactive way with the the person doing the build.

We added the ant-contrib task in order to enable us to have “if” statements in the XML, so we define the task as follows:

   1: <taskdef resource="net/sf/antcontrib/antlib.xml">

   2:    <classpath>

   3:       <pathelement location="${3rdParty.home}/ant-contrib/ant-contrib-1.0b3.jar" />

   4:    </classpath>

   5: </taskdef>


This will be used below to drive the logic if the user wants to keep the same version of the manifest or change it.

There are two macros that we have defined, loadmanifest – which reads and parses the manifest and createmanifest – which invokes loadmanifest and then interactively decides whether to write a new manifest or not.

Here is loadmanifest:


   1: <macrodef name="loadManifest">

   2:   <attribute name="manifest" />

   3:   <sequential>

   4:     <echo message="about to load file: @{manifest}"/>

   5:         <loadfile property="@{manifest}.build.version" srcFile="@{manifest}">

   6:             <filterchain>

   7:   <filterreader classname="org.apache.tools.ant.filters.LineContains">

   8:                         <param type="contains" value="Implementation-Version:" />

   9:                 </filterreader>

  10:                 <tokenfilter>

  11:   <replacestring from="Implementation-Version: " to="" />

  12:                 </tokenfilter>

  13:                 <striplinebreaks />

  14:           </filterchain>

  15:           </loadfile>

  16:         </sequential>

  17:     </macrodef>


On line 5 we use the loadfile task to read the version number into a property that we will display to the user. We then use the very powerful filterchain to get to the correct line and the correct section of the line with the version number. For lack of a better way, we strip away the text part of the line (Implementation-Version:) on line 11, and are left with just the version number to be stored in the loadfile property @{manifest}.build.version.

Using this property we can now query the user to determine if the version number suits him or not, and then if necessary put out a new manifest file. This is the macro below:



   1: <macrodef name="createManifest">

   2:         <attribute name="manifest" />

   3:         <attribute name="jarDescription" />

   4:         <sequential>

   5:             <loadManifest manifest="@{manifest}" />

   6:             <echo message="Current Version: ${@{manifest}.build.version}" />

   7:             <input message="Enter new version?" validargs="y,n" addproperty="@{jarDescription}.do.newversion" />

   8:             <if>

   9:                 <equals arg1="${@{jarDescription}.do.newversion}" arg2="y" />

  10:                 <then>

  11:                     <input message="Please enter new version number" addproperty="@{jarDescription}.new.build.version" />

  12:                     <manifest file="@{manifest}">

  13:                         <section name="${@{jarDescription}.jar.description}">

  14:                             <attribute name="Implementation-Title" value="${@{jarDescription}.jar.description}" />

  15:                             <attribute name="Implementation-Version" value="${@{jarDescription}.new.build.version}" />

  16:                         </section>

  17:                     </manifest>

  18:                 </then>

  19:                 <else>

  20:                     <property name="@{jarDescription}.new.build.version" value="${@{manifest}.build.version}" />

  21:                 </else>

  22:             </if>

  23:         </sequential>

  24:     </macrodef>


Other than “if”, the rest are all standard ant tasks that you can look up in the manual on the ant site.

The correct thing to do at this point would be to create a parallel GANT site. Unfortunately, i dont have one ready at the moment so this will have to wait until i begin to play with GANT

Monday, October 19, 2009

SVN Build automation with ant

As i indicated in my last post, we use Ant to create our new scripts. In response to some requests, i am sharing here our “commonbuild.xml” file that shows how we do the key parts of this build in a reusable way. Before you get started you will need to make sure you have late version of ant (1.7 please) and you will need to add some libraries to your ant lib folder. These are the subclipse libraries as well as commons-net if you want to send emails at the end of your build.

Here is is a snapshot of all new libraries added:

antlibs

The first 4 are needed for mail functions and the last 4 are added from svn. I am using svn 1.5 due to the fact that i have not yet updated my Intellij past version 7 so only 1.5 is supported.

In addition you need to install collabnet subversion client (windows) for this to work.

For Svn Purposes, we have set up the following task:

  1: <taskdef resource="org/tigris/subversion/svnant/svnantlib.xml">
  2:         <classpath>
  3:             <fileset dir="${3rdParty.home}/svnant-1.2.0-RC1/lib" includes="**/*.jar" />
  4:         </classpath>
  5: </taskdef>

Using it is pretty straightforward. Below is a macro that we use for commit and tag:

  1: <macrodef name="commitAndTag">
  2:         <attribute name="commitMessage" />
  3:         <attribute name="tagName" />
  4:         <sequential>
  5:             <echo message="checking in dist folder and manifest versions" />
  6:             <svn>
  7:                 <commit message="@{commitMessage}">
  8:                     <fileset dir="${trunk.dir}">
  9:                         <include name="dist/**" />
 10:                         <include name="**/build/**.txt" />
 11:                     </fileset>
 12:                 </commit>
 13:             </svn>
 14:             <svn>
 15:                 <copy srcUrl="${svnUrl}/${svn.projectName}/trunk" destUrl="${svnUrl}/${svn.projectName}tags/@{tagName}" message="@{tagName} tag" />
 16:             </svn>
 17:         </sequential>
 18:     </macrodef>

And to invoke this macro is simple:

  <commitAndTag commitMessage="${release.version}" tagName="${release.version}" />

In the next post I hope to show the macro used for editing the manifest file and upping the version number.

Thursday, October 15, 2009

Time to become a Maven?

I was afraid to post about the fact that we have not moved over all my builds to maven, but i saw that the iBatis team only recently moved. And anyway, last year at a Java conference, the presenter about Maven talked about how only recently had the Maven folks gotten their act together to the point where he was not pulling out his hair over new jars that appeared and changed dependencies happening without his knowing. (I really hate things that happen without my knowing!)

I will start by saying that all our current projects are happily using Ant. We have built some pretty nice scripts to create a full build that include using svn (subclipse) to get the latest version of our code, up the version in the manifest, commit, create a tag, and copy the file to the distribution location. Additionally, the start of a project by our small teams have usually been through a project started in an IDE, (Intellij), and after we have something running, we then take our project and create an ant build, using our existing scripts. So, using maven does not easily fit this model since at this point we already have an existing structure. But everybody is doing it nowadays, so we cant easily dismiss it.

So, we have done a few projects starting with Maven. And of course, it meant we had to break our methodology described above. We used Maven to create the original infrastructure for the project as well as the project file for Intellij. And, as the project would progress and added more dependencies, it seems that the simplest way to move forward was not to add the dependencies to the project in Intellij, but to fix the POM and then have maven regenerate the project file. I believe this is fixed in the latest version of Intellij where it knows how to sync with the POM.

So, what are my conclusions?

First of all, the documentation needs improvement! But, notwithstanding that, I think it makes sense to use Maven to create new projects and this will mean that you will have a simple structure where it is obvious where to put all your additional resources, config files. The alternative would be that we need to keep worrying about these “copy” lines in my ant file.

But the power of ant to do what you want, and use legacy processes (like our use of JWSDP 1.6) would mean that we still want ant in our build process. Luckily this is possible, as I saw here.

Of course, Ant will remain a key tool for us as i use it to run apps and more easily build classpaths and pass params to it than alternatives that I know of. And as a big believer in “If it ain’t broke don’t fix it”, i dont see us moving our old applications to Maven.