Parrot Plumage Day 5: Configure.nqp and a 'proper' Makefile

geoffrey on 2009-09-22T07:03:22

Shortly after Day 4 day job deadlines loomed, and I expected Day 5 to be delayed at least a week. Thanks to some late nights this morning's status meeting at the day job went well, so Tene++'s ping on #parrot caught me with a few spare tuits and the strong desire to take a break from the tasks of the last few days.

Tene had pasted a patch to the Makefile for Plumage, essentially suggesting that it be a bit less hard-coded and instead use the parrot_config binary to fill in some of the variables. Unfortunately, his patch used backticks (`...`) to capture the output of parrot_config, which is not portable across make implementations. In fact, it appears that every make does this differently -- GNU Make uses the $(shell ...) construction, BSD Make uses the special != assignment operator, and so on.

Asking around, it soon became clear that this morass of incompatible syntax for capturing shell output is one of the reasons everybody just uses a Configure script of some sort. In fact, a simple Configure script does nothing more than replace markers in a makefile template (typically called Makefile.in) with the proper text, and write out the completed Makefile to be fed to make.

That sounded like exactly the kind of simple substitution that I needed for the Plumage Makefile, but I couldn't just use one of the zillion existing Configure scripts, because it needs to be written in NQP for much the same reasons that Plumage itself does.

Well, I clearly couldn't leave that situation lie, so a couple hours later, Configure.nqp was born. After implementing slurp() and spew() functions in the Glue.pir "builtins" library, reading the template and writing the output again were trivial, but the interesting task turned out to be implementing subst() to do the text substitutions.

Parrot has for quite some time now shipped with PGE, the Parrot Grammar Engine. Among other things, PGE has built-in support for the Perl 5 and Perl 6 grammar/regex languages. With some gracious help from pmichaud++ and a good bit of spelunking in the Rakudo src/builtins/ directory, I pieced together the necessary bits to instantiate the Perl 6 variant, use it to compile the regex argument to subst(), and iterate over the matches performing the substitutions.

At first I implemented purely static string replacement; the same new string would be used to replace every match point in the original. This method would however be horribly slow for the task at hand -- I'd have to run the substitution across the entire makefile text for each of the hundreds of config values that parrot_config knows about.

Taking a hint again from the Rakudo implementation of subst(), I also made it possible to supply a sub instead of a plain string as the replacement argument. This sub gets called with each match object in turn, and returns the appropriate string to use for the replacement. This makes the relevant code from Configure.nqp remarkably clean:

my $replaced := subst($unconfigured, '\@\@', replacement);

sub replacement ($match) {
    my $key    := $match;
    my $config := %VM{$key} || '';

    return $config;
}

(Yes, replacement() could easily be written as a one-liner. It seemed a bit clearer this way.)

The plain string form of subst() still gets used, to fix the slash orientation for Windows systems:

if ($OS eq 'MSWin32') {
    $replaced := subst($replaced, '/', '\\');
}

A few miscellaneous cleanups and an update to README later, and the Makefile cleanup task (which I expected to remain near the bottom of the task list for a fair bit, nibbling at the edge of my conciousness) is Just Done. That's a good feeling.

As always, you can check out the code at the Parrot Plumage repository, and don't hesitate to ping me on IRC -- I'm japhb on #parrot at irc.parrot.org.