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.
