views:

2454

answers:

5

How would you write a build.xml file, using neither custom code nor external dependencies (such as a shell script), that:

  • Generates a build number of the form major.minor.revision (e.g., 01.02.34).
  • Auto-increments the revision on each compile of the source code.
  • Auto-increments the minor version on each execution of a dist(ribution) task.

Additionally:

  • Provides an option to increment the major number.
  • Provides an option to increment the minor number.
  • Whenever the major number is incremented, the minor and revision numbers are set to 0.
  • Whenever the minor number is incremented, the revision number is set to 0.

Bonus:

  • Creates a variable based on the git revision number (like a subversion revision number).

Clarification:

  • Automatic checkout (or checkin) is not required.
  • Integration with Subversion is not desired.

Thank you for any examples. Here are some related sites that perform similar tasks:

A: 

My approach:

Build Process

  1. build_info.properties will be created during build in your project folder You could write all information about your build in this file.
    • Like build number, major and minor numbers of release, timestamp, and revision number.
  2. Your build script can modify these values how ever your want
  3. After the build was successfull commit the file 'build_info.properties' back to the repository

During development

After first build the file build_info.properties will be placed in the repository. You can change and commit any number (major, minor, build numbers) by your self when ever you want, or increase it automatically during build like build.number in the example below.


Here is the example with svnant 1.3.0

<target name="checkout">
 <echo>Checking out revision ${param_SubProjectSvnREV} of project: ${param_SubProjectSvnName}</echo>
 <svn username="${svnant.repository.user}" password="${svnant.repository.passwd}">
  <checkout url="${svnant.latest.url}/${param_SubProjectSvnName}/" revision="${param_SubProjectSvnREV}" destPath="${all.projects.dir}/${param_SubProjectDirName}" />
  <info target="${all.projects.dir}/${param_SubProjectDirName}" ></info>
 </svn>
 <propertyfile  file="${all.projects.dir}/${param_SubProjectDirName}/build_info.properties" comment="Modify build numbers in a properties file.">
  <entry key="build.number" type="int" operation="+" value="1" pattern="00"/><!--increment it here -->
  <entry key="build.revision" type="string" value="${svn.info.rev}"/>
  <entry key="build.major.number" default="01"/><!-- can do some logic here to increase the values, or write value from somewhere else-->
  <entry key="build.minor.number" default="01"/><!-- can do some logic here to increase the values, or write value from somewhere else-->
 </propertyfile>
</target>

<target name="compile" depends="checkout">
 <property file="${all.projects.dir}/${param_SubProjectDirName}/build_info.properties" />
 <mkdir dir="${release.name}/${param_SubProjectDirName}/${build.major.number}.${build.minor.number}.${build.number}" />
 <!-- compile it to the new folder, an so on... -->
 <!-- after all, if the build wass successfull, commit the file 'build_info.properties' back to repository --> 
</target>
Scrutator
Thanks for this. Am wondering more about git than Subversion. Also, how do you automatically increase the minor number on a dist? And how do you increase the major/minor from the commandline?
Dave Jarvis
A: 

In my project i don’t increase the minor and major number automatically. We set it from our global build properties. Like that:

<entry key="build.major.number" value="${global.release.major}"></entry> 
<entry key="build.minor.number" value="${global.release.minor}"></entry>

That’s because they will be changed for releases (not for test or other builds) and committed together with other sources (we have be able to build some old or branch version).

But if you want to increase the minor number, you can do it like the build number in my example.

<entry key="build.major.number" type="int" operation="+" default="1" pattern="00"/>
Scrutator
The last line is helpful, but the syntax for ant build files are a bit tricky. Am looking for a bigger piece of the puzzle than a single line.
Dave Jarvis
A: 

The easiest way of doing this is to change the problem. Instead of making the Any build do this for you, have whatever process that you're calling Ant calculate what the version number should be, and then pass that in as a property e.g.

ant -Dbuild.version=1.2.3

This has the flexibility of whatever build you're working with being able to take its cue from whatever, such as the SVN revision, the current date and time, or whatever.

ant -Dbuild.version=svnversion .

ant -Dbuild.version=date +"%Y%m%d%H%D"

ant -Dbuild.version=${major}.svnversion ..date +"%Y%m%d%H%D"

etc. You can get pretty comprehensive if you want.

If you want to have an ever incrementing number, then you can store it in a file and then pass that in at compile time. For example, you can do:

VER=cat build.version VER=$((VER+1)) echo $VER > build.version

Lastly, if you really want this to be in the build.xml file, the best thing to do is have a separate task to execute the increment-and-build option and fork off a nested ant build with your 'main' target. You'd thus end up with

ant -> ant -Dbuild.version=1.2.3.4 -> ...

In other words, given your build.xml with a (current) default of 'build', then change it to 'version' and have the ant 'version task do the calculation followed by a nested call to and build.

Implementation is left as an exercise to the reader, as is translating the approach to a non-UNIX platform.

AlBlue
When I wrote "without a custom Java class", I really meant, "without custom code". That is to say, everything from within the ant build script itself. No bash script, no external dependencies, no DOS batch file.
Dave Jarvis
Bear in mind that Git doesn't have the concept of an incrementing version number. Git revisions are SHA hashes and are non numeric (typically, they're represented in hex, though of course it does represent a number under the covers). However, the key important thing is that it's not an ever-increasing number, but rather takes disjoint hops (both up and down) across the numeric spectrum.In addition, the constraints you put on the requirement are inconsistent. You don't want any external dependencies, yet you've got Git, and presumably Ant as well.
AlBlue
Git does have a concept of incrementing numbers. You tag (properly, using -s or -a) when you have a version that's interesting. You use `describe` to get a unique, human-readable version number from your tree.
Dustin
@AlBlue. Using Ant to execute an Ant build script is not an inconsistent requirement; obviously some program external to the build.xml file must be used to execute the build script -- I thought that that went without saying. The bonus part was to see if it was possible to use an ant task to get a Git-based version number (such as an ant task to get the Subversion number). It is not required to answer the question, hence the term "bonus". Ergo, it was not an inconsistent requirement, either.
Dave Jarvis
Dustin,The version number obtained by git describe isn't unique across all distributed repositories; and in any case, is a combination of the tag and the number of commits to the (local) repository since then. Things like rebasing or squashing commits can result in identical numbers but different content.
AlBlue
+8  A: 

The build_info.properties file:

build.major.number=00
build.revision.number=00
build.minor.number=00

The build.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<project name="project" default="current-number">

<property file="build_info.properties"/>
<property name="build.number" value="${build.major.number}.${build.minor.number}.${build.revision.number}"/>

<target name="current-number">
 <echo>Current build number:${build.number}</echo>
</target>

<target name="compile">
   <antcall target="revision"></antcall>
</target>

<target name="dist">
  <antcall target="minor"></antcall>
</target>

<target name="revision">
    <propertyfile  file="build_info.properties">
            <entry key="build.revision.number" type="int" operation="+" value="1" pattern="00"/>
    </propertyfile>
</target>

<target name="minor">
    <propertyfile  file="build_info.properties">
            <entry key="build.minor.number" type="int" operation="+" value="1" pattern="00"/>
            <entry key="build.revision.number" type="int" value="0" pattern="00"/>
    </propertyfile>
</target>

<target name="major">
    <propertyfile  file="build_info.properties">
            <entry key="build.major.number" type="int" operation="+" value="1" pattern="00"/>
            <entry key="build.minor.number" type="int" value="0" pattern="00"/>
            <entry key="build.revision.number" type="int" value="0" pattern="00"/>
    </propertyfile>
</target>

<target name="all">
    <propertyfile  file="build_info.properties">
            <entry key="build.major.number" type="int" operation="+" value="1" pattern="00"/>
            <entry key="build.minor.number" type="int" operation="+" value="1" pattern="00"/>
            <entry key="build.revision.number" type="int" operation="+" value="1" pattern="00"/>
    </propertyfile>
</target>

</project>
rodrigoap
Thank you for this.
Dave Jarvis
+1  A: 

This solution does increment minor or revision number automatically if a compile or a dist target has been selected. The incrementation can be switched off if one of the following properties has been set:

  • -Dno.increment.minor=true
  • -Dno.increment.revision=true

If the property inc.major has been set, then the major number will be incremented and the other both values will be set to zero. The SHA-1 checksum is being calculated by the textual representation of the version file.

By the way: If would have been allowed, you could create your own ant task in java script, which is included in JDK 6.

Now here's the ant file

<?xml version="1.0" encoding="UTF-8"?>
<project name="Numbers" default="dist" basedir=".">

    <property name="version.file" location="${basedir}/version.properties"/>

    <target name="inc.revision.properties" unless="no.increment.revision">
        <propertyfile file="${version.file}">
            <entry key="minor.number" default="00" operation="=" pattern="00" type="int"/>
            <entry key="major.number" default="00" operation="=" pattern="00" type="int"/>
            <entry key="build.number" default="00" operation="+" pattern="00" type="int"/>
        </propertyfile>
    </target>

    <target name="inc.minor.properties" unless="no.increment.minor">
        <propertyfile file="${version.file}">
            <entry key="minor.number" default="00" operation="+" pattern="00" type="int"/>
            <entry key="major.number" default="00" operation="=" pattern="00" type="int"/>
            <entry key="build.number" value="00" operation="="  type="int"/>
        </propertyfile>
    </target>

    <target name="inc.major" if="inc.major">
        <property name="no.increment.minor" value="true" />
        <property name="no.increment.revision" value="true" />
        <propertyfile file="${version.file}">
            <entry key="minor.number" value="00" operation="=" pattern="00" type="int"/>
            <entry key="major.number" default="00" operation="+" pattern="00" type="int"/>
            <entry key="build.number" value="00" operation="=" pattern="00" type="int"/>
        </propertyfile>
        <load.version.info/>
    </target>

    <target name="inc.minor" depends="inc.major,inc.minor.properties">
        <property name="no.increment.revision" value="true"/>
        <load.version.info/>
    </target>

    <target name="inc.revision" depends="inc.major,inc.revision.properties">
        <load.version.info/>
    </target>

    <macrodef name="load.version.info">
        <sequential>
            <property file="${version.file}"/>
            <checksum file="${version.file}" property="sha1.number" algorithm="SHA" format="CHECKSUM"/>
            <echo>Version: ${major.number}.${minor.number}.${build.number}</echo>
            <echo>SHA1: ${sha1.number}</echo>
        </sequential>
    </macrodef>

    <target name="compile" depends="inc.revision" description="Compile Task"/>

    <target name="dist" depends="inc.minor, compile" description="Dest Task"/>

</project>
dz
using <buildnumber> things get much easier. But calculating an SHA1 checksum without the buildnumber in the file isn't an option. My old rule turns true: optional tasks makes the solution more complex.
dz
Have to go with rodrigoap's. Thanks for the suggestion!
Dave Jarvis