anjoschu has asked for the wisdom of the Perl Monks concerning the following question:

I am looking for a way to include the module Clone from CPAN with my script so that users need not install the module themselves.

They should just extract the zip file and there would be a subdir Modules in the distribution which contains Clone and works out-of-the-box. Ideally, the solution should be independent from the user's architecture. My own system is Mac OS X.

Read on for what I tried so far...

I followed this guide to install the Module locally to ~/clone. But as I want the module in a subdir of my distribution, I did not leave it there and copied the files Clone.pm, auto/Clone/autosplit.ix, auto/Clone/Clone.bs and auto/Clone/Clone.bundle to the distribution's Modules directory and added ./Modules to @INC. So far, this works on various machines with Mac OS X and devtools installed.

Contents of the disto directory:

test.pl
Modules
   Clone.pm
   auto
      autosplit.ix
      Clone.bs
      Clone.bundle

Contents of test.pl:

use lib qw(./Modules); use Clone qw(clone); use Data::Dumper 'Dumper'; my $struct = { a => 1, b => [2, 2.5] }; print "The original structure:\n"; print Dumper $struct; my $clonedstruct = clone($struct); print "\n\nThe cloned structure:\n"; print Dumper $clonedstruct;

This works on several OS X machines I tested (v5.8.1-RC3 built for darwin-thread-multi-2level). But on Linux (v5.8.0 built for i586-linux-thread-multi), it gives me an error: Can't locate loadable object for module Clone in @INC. I then did the same procedure again for Linux (installing Clone locally and moving files into Modules dir) when I noticed that the file Clone.bundle appears to be called Clone.so here. I then copied both Clone.bundle and Clone.so into Modules/auto and now it appears to work on both OS X and Linux.

So I assume that this solution (if it may be called so), while it seems to work on OS X and Linux, is some dirty hack which will break as soon as I turn my back. Plus, it probably doesn't work on Win32, which bugs me.

Now, is there a right way to do this? And is this kind of architecture independence which I want even achievable?

--anjoschu

P.S.: I am aware of the fact that I must ensure that the module's license permits bundling it with my distro.

Update: Added cross-platform to the title for search-friendliness.

Replies are listed 'Best First'.
Re: Bundling a module with a distribution
by dragonchild (Archbishop) on Dec 29, 2004 at 16:55 UTC
    You will want to lok at PAR.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

      The amazing PAR is indeed capable of handling XS Modules cross-platform. In order to create such a package, I would need to run the packager on each platform.

      Unfortunately, for some reason I cannot get PAR to install on my university's linux boxes (I tried a local install and there's an error about insufficient permissions on ~/.cpan/sources. Oddly, the perms look ok to me).

      Par files are essentially zip files, but I don't know whether it would be possible to add the multi-platform modules manually to the archive so that they work. You could manually create the necessary directory structure as it is described in PAR::Tutorial, but I'm not sure about the meta files.

      Update: Happy New Year!

Re: Bundling a module with a distribution
by Solo (Deacon) on Dec 29, 2004 at 17:13 UTC
    This is where I often use FindBin to avoid problems based on the user's current directory.

    use strict; use FindBin; use lib "$FindBin::Bin/Modules"; print "\@INC: (" . join(",",@INC) . ")\n";

    It's unclear from your message how you invoke the script, so I'm not sure if it solves your problem, YMMV. If you need to load different modules (or different builds of the same module that are OS-dependent), take a look at how File::Spec handles it.

    Update: Note that I'm not recommending one try to pre-build modules for different platforms to distribute with scripts... only how one might handle loading the appropriate module if one chooses to do so.

    --Solo

    --
    You said you wanted to be around when I made a mistake; well, this could be it, sweetheart.

      I did not know FindBin, thanks for the hint. This will certainly save me some trouble (unless I use PAR). I'm skimming PAR::Tutorial and am going to examine File::Spec, too.

      Update: FindBin works great, thanks again.

Re: Bundling a module with a distribution
by Anonymous Monk on Dec 29, 2004 at 21:30 UTC
    Clone seems to be an XS module, which short of compiling it on the system you are installing this on, is going to make portability a real pain. While slower, you might look at Storable's dclone (as the Clone docs mention), since Storable is part of the core distribution.

        Devel::UseAnyFunc, which Clone::Any depends on, uses eval { require $pm } to check for available modules. It does not check whether the call of the clone function succeeds.

        In this case with an unsupported architecture, the whole thing fails when clone is first called. That's why, unfortunately, Clone::Any does not work here. simonm pointed me to this in an email.

        Probably Devel::UseAnyFunc can be expanded by a eval call of the function in question (here clone). I'll mention this to simonm. On the other hand, Devel::UseAnyFunc would need to know how the function wants to be called. Oh well...

        Clone::PP looks like the solution (thanks qq). Albeit slower than Clone (and Storable for that matter) and having some limitations, it probably will work well for my purposes.

        Storable's dclone would also work. It is part of the core distribution of perl. Maybe I should use that. On the other hand, this may change with Perl 5.10.

        What, me indecisive? ;-)

      ++ to parent.

      You are on to the right track, but the module in question is a compiled binary, which means you are not going to be able to distribute an identical version of your app for a different platform. You'll have to have an "installer" script unpack and compile the module (perhaps in your local dir location).

      I can report that I package up several CPAN modules into a local ./Modules directory so that I can easily move the entire local dir tree up for packaging and installation onto different machines. However, I am fortunate to be installing on to identical hardware.

      -------------------------------------
      Nothing is too wonderful to be true
      -- Michael Faraday

Re: Bundling a module with a distribution
by perrin (Chancellor) on Dec 29, 2004 at 18:44 UTC
    Why install it and then move it? Why not just install it where you want it to end up, in your Modules/ directory?

      You're right, of course. At the time of writing, I was unsure of where make puts the files that are to become installed. They can be found in the blib directory.

      When I make install run perl Makefile.PL with the PREFIX option and then make install, it creates the whole architecture dependent tree within Modules, sort of like Modules/Library/Perl/5.8.1/darwin-thread-multi-2level, which isn't what I wanted. That's why I decided to copy the files by hand.

      Update: Noticed mixup with make install.

        When I make install with the PREFIX option, it creates the whole architecture dependent tree within Modules, sort of like Modules/Library/Perl/5.8.1/darwin-thread-multi-2level, which isn't what I wanted.
        use LIB=/path/to/Modules additionally to PREFIX. this won't create the whole tree.