Wednesday, February 4, 2009

Configuring Builds for PEAR Packages in phpUnderControl

As my key goal for 2009, getting my own phpUnderControl (hereafter referred to as "pUC") instance up and running was very satisfying. This was made much easier by having Matthew Turland's post about his experience setting up an instance on Debian, since my setup on Ubuntu Hardy LTS was architecturally nearly identical.

Following the "Getting Started" instructions at pUC's site was easily enough for me to get project sandboxes checked out for all my PEAR packages and Ant builds created for them. Since all my packages already had their own AllTests.php master test suite PHPUnit file, it was a breeze to include the "php-unit" build target. However, there were some things that I had to discover via trial-and-error with regard to the build files, though possibly they are covered in other pUC docs that I didn't check.


The "Getting Started" build example is based on a project sandbox pulling code from a Subversion repository, whereas all my PEAR code comes from CVS. In pUC's top-level config.xml file, the project example includes the plugin and bootstrappers tags specifically due to the example code coming from Subversion. I eventually figured out that these were not necessary at all for CVS. Aside from this difference, the example config.xml has been perfect for my pUC instance.


In writing Ant builds for each PEAR package sandbox, the SVN vs. CVS difference was simple to tackle -- the executable attribute of the exec element in the checkout target changed from "svn" to "cvs". Anyone who has trouble following this change should stop reading now and go back to his chocolate milk.

One difference in writing the arg elements' line attributes for nearly all my projects' build files was that I chose to always use absolute paths for any argument that needed a path. pUC's example did usually use absolute pathing via the ${basedir} property, but not in all cases... I did choose to in all cases, with no problems arising. This ultimately helped tackle the next problem (below), though I don't remember now if I understood why at the time ;-)

The most key issue for me to overcome with my packages was getting the builds' test suites to run against the projects' sandboxes rather than against the box's PEAR installation. This proved the most tricky of all, because I wasn't realizing at first that the projects/my_project/source sandbox structure as outlined by pUC's example doc was throwing off my include_path's ability to find my sandbox files. Chalk this up to making too many assumptions about having "." at the start of the include_path. What I eventually realized, or I should say remembered since I know PEAR packages are installed in this manner, is that my package's tests were written to be run via both sandbox and installation. This meant that, in the case of the XML_Util package, the sandbox files needed to be in an XML/ directory. Pretty obvious, huh... not so much so when I was elbow deep in my first swim through pUC and carrying the fear of "I just have it config'd wrong, that's all...". In the build targets themselves, this meant switching occurrences of dir=${basedir}/source with dir=${basedir}/XML, but I ended up only leaving the subdirectory portion of the path in the checkout target's exec tag. For all my other targets, I chose to just use dir=${basedir} as the exec value's run-from directory... this allowed me to use ${basedir}/XML in all my targets' arg sections, which I found helped me to at least keep each target "grounded" at the same position mentally.

Having the "." at the beginning of the include_path did solve getting the test suite (and therefore the code coverage as well) executed against the sandbox code, once I'd used the necessary sandbox parent directory name (e.g. XML/ for XML_Util).

Ideas to Still Pursue

One small refactoring that occurred to me while writing this is to set a build property in the top-level config.xml to hold the absolute path that points to pUC's custom output converter for phpDocumentor, since it is obviously a global setting for the whole pUC instance.

One big thing to do next is to configure the builds to test the actual installation/upgrade step for each package, i.e. pear upgrade XML_Util. This step could highlight any unexpected issues that might arise when packaging the code up, as well as when using that package file for the upgrade step. This would also mean having a separate testing-only PEAR installation that doesn't touch the box's PEAR installation... I'd hate to break pUC by having a build muck up the PEAR that pUC itself uses ;-)

Once I have that big addition in there, I'll probably add a second php-unit build target that specifically runs against that second PEAR installation, just as a failsafe. Given that there are some PEAR packages whose tests will only run against an installation, while others will only run against a sandbox copy, I think this will be a good addition to have. A third testing redudancy target will be running the tests via the pear run-tests -p XML_Util and pear run-tests -up XML_Util commands. I obviously won't expect these to produce any special output for pUC, aside from just success/failure of the steps themselves. I think there's some level of value from all this testing redundancy, as I think that tests properly written should be expected to run from all four of the build scenarios I've mentioned so far.

Once I've tackled those milestones, I think I'll be ready to create a build for the main PEAR installer package itself. I can think of a few #pear dwellers over on Efnet that might be happy to always have its test results available... I expect one of them is yelling at me in Icelandic to "get on with it!!!!!!1111ones" ;-)

A very minor quibble that I think I'll eventually not be able to stop myself messing with is that the phpDocumentor target, when run against only the project sandbox, won't find classes from dependency packages... case in point, the PEAR_Error class. This only really leads to some lines on phpDocumentor's errors page, but still... the slight OCD in me won't completely rest as long as any errors remain.