I have a little project called Parrot::Embed. It’s a Perl 5 extension that makes Parrot available to Perl 5 programs.

Parrot makes a shared library called libparrot. The actual parrot executable is usually just a little program which uses this shared library. This is very handy for my extension; I can use all of the public functions in the shared library myself.

Actually building this code is trickier than it should be, however.

Linking Perl 5 to libparrot requires a little bit of C code, itself a shared library that perl can load through the DynaLoader module. That’s easy and handy and even though I know how it works, I don’t need to know how it works in order to use it.

When Parrot::Embed loads, it attempts to load libparrot and that’s where my troubles begin. Why?

Multiple Options

There are two ways to build Parrot::Embed. The first is as a normal user, with Parrot installed. In this case, libparrot will be on your system somewhere in a normal shared library search path. The compilation and configuration process should find it appropriately. If you have a working compiler and build environment, the Parrot::Embed shared library should contain all of the necessary information to find and load libparrot when it runs.

The other way to build Parrot::Embed is more common, right now. Because the source code is in the Parrot Subversion repository, I expect that a lot of people will build it within the tree without having a Parrot installed. Certainly that’s how I use it most.

This build process is trickier; there is no guarantee that the necessary C headers are available in a standard location, nor that there is a parrot executable or libparrot available anywhere outside of the source tree. Fortunately, I can assume that the necessary files are in the tree relative to the location of Parrot::Embed, and pass the appropriate compiler flags.

At least, I know the relevant compiler flags for gcc and ld for ELF format shared libraries. Now things get tricky.

I want this to work on Windows, so I need to support cl. I also want this to work on Mac OS X, but it doesn’t use ELF. Uh oh.

gcc allows you to pass commands to ld with the -Wl flag. The relevant flag is -rpath, which I can set to the absolute path of the built libparrot within the source tree. That gets Linux working (and I assume the *BSDs and Solaris).

Windows looks for DLLs in the current directory (not correct anywhere for this), in a couple of system directories, and in the directories specified by PATH.

It would be nice if there were a cross-platform, cross-compiler way to say “Here’s where I expect this library to be, so load it as late and as lazily as possible.” I’d like to avoid a nest of OS- and compiler-dependent code, but I also want to avoid fragility as much as possible. I could manage the loading with DynaLoader and pass function pointers around, but I’d be reinventing dlopen() and dlfunc() in a higher-level language, and I’d like to avoid that.

Setting the appropriate environment variables within Parrot::Embed really might be the way to go. On free Unixes, at least, LD_RUNPATH looks like the correct environment variable to use. (I’m familiar with LD_LIBRARY_PATH, which is why I’m not using it for this.)

It’s days like this (weekdays) where I really wish there were better languages and tools and operating system for doing this sort of thing.