June 30, 2008

Simultaneous Development with Maven

Migrating Project Darkstar from Ant to Maven for our builds has been a mostly positive experience. Now that the migration is complete, though, and the team is beginning to use it more extensively, we're still working out a few problematic use cases. One such scenario just came up today. Here's the problem:

Let's suppose you are doing development against the Project Darkstar server. As part of your mini development cycle, in addition to running the unit tests against the server, you also want to run an example application specifically designed to provide performance benchmarks for the system. By convention, this application would specify as a dependency the latest 'SNAPSHOT' of the Project Darkstar server (in this case the latest snapshot is 0.9.7-SNAPSHOT). In order for the example application to be run against your SNAPSHOT, though, you need to build the server component which includes the changes made in your development branch, and install it into your local repository. This is straightforward to do and requires basically an mvn install from your development branch. Running the example application will then pick up your latest SNAPSHOT from the local repository, and all is well.

However, what if you wish to compare the results of running this example application against both your development SNAPSHOT, and also the latest SNAPSHOT from the trunk. If you execute mvn install from these respective codebases, they'll clobber eachother because they both have the same version number (0.9.7-SNAPSHOT). The problem is exacerbated even more if you have multiple development branches that you are working on simultaneously. You can workaround it by rebuilding each copy before running the example, but this is tedious and error prone. You could also make temporary changes to the version numbers of the artifacts by hacking your POMs so that each development branch has a unique version. This is also tedious and error prone, though, and is ugly because it requires reverting changes to your POMs before merging patches into the trunk.
Ultimately, we want a solution that meets the following requirements:
  • Modifying the POMs or assigning temporary version numbers is not necessary.
  • It does not require rebuilding each development version as they are needed.
  • Ideally, a simple configuration parameter on the command line is all that is required to specify which development version we wish to use.
The solution that I came up with is actually surprisingly simple. All that you need to do is isolate each of your development versions by using separate local repositories. By default, Maven stores artifacts into a local repository located in the ~/.m2/repository directory. However, this location is configurable, and a different location can be swapped in and out depending on your needs.
To put this into the context of our example, let's say you have a checked out version of the Project Darkstar server trunk, and you also have two development branches that you are working on, branchA, and branchB. You would then do the following to install each of the respective artifacts into separate local repositories:
trunk $ mvn install -Dmaven.repo.local=/home/owen/.m2/repository-trunk
branchA $ mvn install -Dmaven.repo.local=/home/owen/.m2/repository-A
branchB $ mvn install -Dmaven.repo.local=/home/owen/.m2/repository-B
At this point, you have local repositories which are completely isolated from eachother. You can use each one of them independently to build and/or run your example application against the respective development version of the Project Darkstar server:
example $ mvn package -Dmaven.repo.local=/home/owen/.m2/repository-trunk
example $ mvn package -Dmaven.repo.local=/home/owen/.m2/repository-A
example $ mvn package -Dmaven.repo.local=/home/owen/.m2/repository-B
That's it. No POM hacking, no building and rebuilding the same code, just isolate your development versions with separate local repositories. Hope this helps someone out there.

1 comment :