Thursday, April 9, 2009

Continuous Integration for C++

I've used Continuous Integration with Java for quite a while-- usually using CruiseControl, JUnit, and PMD. I've had good luck with these running them against PVCS, CVS and Subversion. Lately, though, I've had an interest in using CI for C++ development, which was a whole new story.

This time around, I thought I'd try Hudson instead of CruiseControl. I was pleasantly surprised by the ease of use-- basically, just untar Hudson someplace and run the web application, which is a .jar. Once Hudson's running, you configure it through web forms. It's really easy!

I wanted to use Subversion this time around, it was trivial to enter the URL, user and password.

Next, we had to find a way to cause our build to happen. I already had a Make-style C project to work with, so I started out using Hudson's 'run a script' configuration to invoke Make. This worked great-- right out of the box Hudson will check your project out and invoke Make.

I wanted to use a JUnit-style unit test framework, and decided to go with CPPUnit based on internet reviews. I'd had an earlier failure trying to get CPPUnit to compile under Cygwin, but that was quite a while ago, so I got the most recent build and started building with 'configure, make, make install'. (By the way, I used the relocation option to give 'configure' a hint to build CPPUnit in a local directory. I didn't want to muddy up this shared Linux server until I was confident things were going to work out well.) CPPUnit built well and I had a sample program (based on a 'Net tutorial) running in a short while.

To run CPPUnit from Hudson, I altered my script to invoke the Unit test driver right after 'make clean' and 'make'. I also redirected the output from CPPUnit to a file and echoed the contents of the file at the end of the script. (In this way, you can see everything in the 'console output' view of your Hudson build. No need to reformat the XML output!)

Next up was civilized logging. This really wasn't a necessary part of CI for C++, but I've been so spoiled by Log4J that I had to have it. So I got Log4Cxx (which also required Apache's runtime 'apr' and 'apr-utils') then built all 3 in the order apr, apr-util, then Log4Cxx. All this was done with configure (with relocation), make, make install. Log4Cxx also worked right out of the box-- woohoo!

I wanted to see how well my unit tests were covering the code, so I added new CXXFLAGS to my Make file. I had the Hudson build script use sed to toggle the flags on (which enabled me to run gnu's gcov with the Unit test driver), then I got stats which naturally got redirected into the same file CPPUnit redirects to, and thus is echoed at the end of the Hudson build script. Now our console output for the build shows Unit tests and coverage.

As a final step, I wanted to gather some stats on how my code was performing, so I invoked Valgrind in the Hudson build script and redirected the output again to the tell-all echoed file. I ended up taking this one out in the end, because it got very wordy and made for a pretty large file to view in the scrollable browser Hudson gives you. Maybe I'll try to cut that down sometime and re-add it, but for now I'm just doing without.

So there we have it! I found the whole experience totally satisfying and well worth the minimal effort it took to get the whole toolset going. I hope you have as much luck in your efforts!

Rick