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

I've run into a problem with a module I'm writing for CPAN. Using Extutils::makeMaker, I have it install into /path/to/perl/modules/Foo.pm . It has some plugins which go into /path/to/perl/modules/Foo/Bar.pm, /path/to/perl/modules/Foo/blurp.pm etc.

Now the problem is how to avoid hardcoding /path/to/perl/module/Foo into Foo.pm? I discovered PM_FILTER and changed Foo.pm to say INST_LIBDIR/Foo and made a filter like this in Makefile.PL:

'PM_FILTER' => 'perl -pe \"s|INST_LIBDIR|$(INST_LIBDIR)|g;\"'
The problem is this changes to blib/lib/Foo .

Now I have

'PM_FILTER' => 'perl -pe \"s|INST_LIBDIR|$(INSTALLVENDORLIB)|g;\"'
which is more like what I want but obviously won't work if the user does a site, or private install.

So what should I do to solve this problem?

--
જલધર

Replies are listed 'Best First'.
Re: How to make a module aware of where it is installed?
by chromatic (Archbishop) on Dec 04, 2002 at 06:26 UTC

    I may not understand the nuances of your question, and I apologize in advance if this is the case. However, I wonder if you've tried placing the modules in an appropriate directory structure under lib/ in your distribution directory. I've done that successfully before.

      Yes, the module structure is correct under blib/lib. The problem is the main module itself has to know what blib/lib expands to. For instance under perl 5.8 on Debian GNU/Linux,

      INSTALLPRIVLIB='/usr/share/perl/5.8.0' INSTALLSITELIB='/usr/local/share/perl/5.8.0' INSTALLVENDORLIB='/usr/share/perl5'
      The INSTALLDIRS variable in Makefile.PL determines which one gets used or it defaultes to site. It will translate instances of blib/lib in the Makefile itself but doesn't do that elsewhere without the PM_FILTER.

      What I am doing for now is:

      'PM_FILTER' => 'perl -pe \" if (\"$(INSTALLDIRS)\" eq \"perl\") { s|INST_LIBDIR|$(INSTALLPRIVLIB)|g; } elsif (\"$(INSTALLDIRS)\" eq \"vendor\") { s|INST_LIBDIR|$(INSTALLVENDORLIB)|g; } else { s|INST_LIBDIR|$(INSTALLSITELIB)|g; } \"'
      but there has to be a better solution than this.

      --
      જલધર

        chromatic said "lib" not "blib". Create a "lib" subdirectory at the top of your package and put any extra files to be installed in there with the proper directory structure.

        For example, if you are building your module in /home/tye/Foo-Bar then you could have something like the following files:

        /home/tye/Foo-Bar/MANIFEST /home/tye/Foo-Bar/Makefile.PL /home/tye/Foo-Bar/Bar.pm /home/tye/Foo-Bar/lib/Foo/blurp.pm /home/tye/Foo-Bar/lib/Foo/Bar/Baz.pm
        and "make" would create the following files:
        /home/tye/Foo-Bar/blib/lib/Foo/Bar.pm /home/tye/Foo-Bar/blib/lib/Foo/blurp.pm /home/tye/Foo-Bar/blib/lib/Foo/Bar/Baz.pm
        and "make install" would then put them in the correct final location.

                - tye
Re: How to make a module aware of where it is installed?
by Courage (Parson) on Dec 04, 2002 at 09:09 UTC
    It changes to "blib" during compile process and will expand to appropriate subdirectory during "make install" invocation.

    Please read "perldoc ExtUtils::MakeMaker" near section "make install" and "PREFIX and LIB attribute", those sections contain answers to your questions.

    Best wishes,
    Courage, the Cowardly Dog

      This is the correct answer but to the wrong question. :) See my reply to chromatic for more details

      --
      જલધર

Re: How to make a module aware of where it is installed?
by atcroft (Abbot) on Dec 04, 2002 at 13:06 UTC

    In one of the first modules I ever created, I needed something similar due to my use of a configuration file, which by default I placed in the directory with the module. My solution was similar to the following code:

    # Module file: ATC::static # Default configuration file: ATC/static.conf my $module_group = 'ATC'; my $module_piece = 'static'; my $module_ext = 'pm'; my $module_conf_ext = 'conf'; $conf_file = $INC{$module_group . '/' . $module_piece . '.' . $module_extension}; $conf_file =~ s/($module_piece\.)$module_ext$/$1.$module_conf_ext/;

    Perhaps not the best of methods or the cleanest of code (I still have much to learn of writing my own modules), but it worked for me at the time, and perhaps it can at least offer you an idea of one possible way.

    Update: If memory serves, the code above was derived from a sample or example in Perl Cookbook, although now I cannot remember the specific location of the code in question in that publication. My appologies for failing to note the reference earlier.

      Very clever! This is what I'm going to do now:

      use File::Basename; my ($base,$dir,$ext) = fileparse($INC{'Foo.pm'}, '.pm'); my $plugindir = "$dir$base";
      I'd still like to know if this is possible to do in the Makefile.

      --
      જલધર

Re: How to make a module aware of where it is installed?
by mojotoad (Monsignor) on Dec 04, 2002 at 15:10 UTC
    What do you mean by 'plugins'? By the sound of it (.pm files) they should always be accessible via 'use' or 'require' as long as they exist relative to Foo.pm. In your answer to atcroft, I still see nothing that is not already accomplished by default in a normal module installation -- namely, relying on @INC to do the right thing with 'use' or 'require'.

    Matt

      Ok this is how it works. Foo.pm has certain hook subroutines for modifiying the way it behaves. Each plugin is a file which belongs to the same package as Foo.pm and implements one or more of these hooks. Although Foo.pm is object-oriented, I didn't want to make the plugins subclasses because you might have multiple plugins which implement a hook that you might want to call in different places. On one invocation if certain conditions are met I can require Bar.pm and call its' left_hook() subroutine and on a second invocation if other conditions are met, require Blurp.pm and call its' left_hook(). As far as I can tell, you can't have two methods with the same name and signature in the same class so I would have to implement one left_hook() and include the conditional logic there which is not flexible in the long run.

      Now you're asking why if Bar.pm and Blurp.pm are installed in the right place, I can't just find them in @INC? The main reason is security. I was thinking paranoidly that what if someone put a module of similiar name to a real plugin which implemented the right type of functions earlier in the @INC path? A different user could be in for unexpected and potentially nasty results. I suppose I could scrub @INC clean but it just seemed easier to define my own fixed directory for plugins.

      --
      જલધર

        That's not very effective security. What if I put the following code in a module with a dissimilar name to a real plugin? It's still game over.

        no warnings 'redefine'; *Your::Plugin::left_hook = sub { unlink <*>; };

        Update: I could put this in a file called strict.pm and it would still work. I meant to mention that in the first version of this node.