XML is not a pretty programming language. I suspect this fact is at the heart of many peoples frustration with maven as a build system for Java projects. I know that it was one of the reasons the Candlepin team decided to use buildr in stead of Maven as the build and package system.
My job has been to work on getting Candlepin ready for acceptance into the Fedora project. There are two major chunks of work necessary to make this a reality. First, buildr itself needs to get into Fedora in order to build Candlepin. Second, all of the Candlepin’s dependencies need to get into Fedora as well.
Getting the buildr dependencies converted to rpm was greatly eased by the program gem2rpm. A Gem is the ruby package format, and it contains all of the information required to populate a spec file for a comparable RPM. Additionally, the Gem team made the decision that binary extension to Ruby would go into the Gem as source code, and get compiled upon Gem install. I applaud this decision. I really understand the BSD approach of shipping everything as source and compiling upon install. It works really well, and the source code is automatically available. The gem2rpm program generates a spec file that uses the gem as the primary source for the RPMs. THe spec files don’t meet Fedora standards by default, but they are a great first step, and the generated RPMs were sufficient for priming our team’s development needs.
Since this approach worked so well, I wondered if a similar approach could work for Java. Maven has a norm (not quite a standard yet, but close to it) where A binary Java Archive (JAR) file has an other archive with the sources for that file right next to it in the repository. Additionally, the Project Object Model (POM) file associated with the binary jar can have dependency and licensing information embedded. It seemed to me that these two files combined should be sufficient to create an RPM.
Thus was born pom2rpm. I was working in Ruby for buildr, so it made sense to me to script it in Ruby. It reads a POM file and generates a spec file. It is far from perfect, but can prime the pump for generating an RPM for a Java project.
The code can be found in my Github repository.
As I worked with Pom2rpm, I came to a better understanding of the integration between Maven and Fedora. The JPackage project has provided a bounty of RPMs for many of the most popular Java projects: Jakarta commons is very well covered, there are JBoss AS RPMs as well as Glassfish, HIbernate, and so on. But JPackage has to deal with the same problem I was dealing with: how can you do a clean build of an open source package if Maven insists on downloading untrusted binaries from random repositories on the web? It turns out that JPAckage has a decent mechanism for dealing with Maven issues: create a local repository in /usr/share/maven2. JPackage RPMs that want to participate in the the JPackage Maven repository (usually abreviated JPP, although I profess ignorance to the meaning of the second P) require a few additions to the Specfile: they need a dependency on the JPackage utilities package, and must call a macro during install that regusters them with the JPackage Maven infrastructure by placing a file into /etc/maven/fragments. These fragments are then assembled into a mapping file /etc/maven/mave-depmap.xml. To make use of this XML file, I wrote a small utility that is both part of pom2rpm and a standalone script. It is called jpp.rb.
One process that I found myself going through time and again was figuring out which Jar file provided which class. I initially wrote a utility for finding things in a zipfile called zipfind. However, this required executing the unzip process on so many files, it became a major time sink. So I created another script that dumps the name of all of the classes and their enclosing jar files into a single file, which I kept in /tmp. I called this script build_jar_cache.
There are a couple other one-off scripts in that directory. All code is Copyright Adam Young 2010, and can be used under the terms of the GPL. I have not prepared it for packaging, but would ideally create a gem file for it, and the gem2rpm it. I use a couple different ruby Libraries XML, all of which can be found packaged up in my buildr-repo. I did create a file for /etc/yum.repos.d, but I make no promise that yum install will work. Caveat Admin. The non-builder RPMS can be found in the candlepin-repo, with a different yum config file.
A couple lessons learned:
Run rpmlint on the binary RPM file.
Build the RPM in a mock environment. (Mock always reminds me of these Calvin and Hobbes Comics). Make sure you do an –init and then build with –installdeps so that you confirm the RPM has the appropriate BuildRequires lines in it.
You can get the -sources rpms you require in buildr by running:
There is an equivalent command for Maven:
mvn eclipse:eclipse -DdownloadSources
Hibernate tools was a bear, and deserves a post all of its own.