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

I've just run into a MakeMaker problem for which I've yet to find a solution. Briefly, Along with a .pm module I want to install, I'd like to install 3 other files. In this case they are all Berkeley DB files and I'd like them to wind up in their own directory off of the install directory. I am sort of thinking that this would require some hacking on the Makefile.pl side of things, but since to this point, I've only generated them, I have no clue as to what all I should do or for that matter what documentation to read. I have looked at MakeMaker.pm and found it to be a little slim on 'how to'-ness. I've also done the usual, Supersearched and googled around long enough to find the IBM devworks article and J. Vromans article -- but nothing mentioned seems to be the ticket...

–hsm

"Never try to teach a pig to sing…it wastes your time and it annoys the pig."

Replies are listed 'Best First'.
Re: MakeMaker Hacking?
by rjray (Chaplain) on Feb 20, 2002 at 22:58 UTC

    This sort of thing does require writing code for your Makefile.PL. I can cite an example where I did something similar in one of my modules, the RPC::XML package. In short, you would either add additional actions to the relevant rules (like install) to copy the files (and you will probably use Make variables such as $(INSTALLARCHLIB) or something in the paths), or add them to the list of files that MakeMaker already creates rules for.

    For DB files, I think it would be just as easy to add additional rules to do the copy and installation explicitly, tied perhaps to the "all" and "install" rules.

    --rjray

      Picture me begging for elucidation and an example? Thus far I've tried adding a postamble, re-defining PMLIBDIRS, PM and other such tricks to no avail...

      –hsm

      "Never try to teach a pig to sing…it wastes your time and it annoys the pig."

        Picture me begging for elucidation and an example? Thus far I've tried adding a postamble...

        Certainly! I'll refer to areas in the Makefile.PL for the current version of my RPC::XML package, so you can follow along at home :-).

        First off, what did your postamble() return, and in what form? The overridable methods are expected to return a single string, which MM writes out to the Makefile-in-progress. Take a look at MY::postamble in my example (it's the last subroutine definition in that file). It does a great deal of work-- it creates a specfile for RPM packaging, as well as a macro file and faux-rcfile for rpm itself to use. When it has done all of that, the routine creates an array of lines in the variable @text like so:

        # Create the rules that create RPM and SRPM files push(@text, qq{ rpm: \$(SPECFILE) \$(DISTVNAME).tar\$(SUFFIX) \t\$(RPM) -bb --clean --rcfile rpmrc \$(SPECFILE) srpm: \$(SPECFILE) \$(DISTVNAME).tar\$(SUFFIX) \t\$(RPM) -bs --clean --rcfile rpmrc \$(SPECFILE) }); # Create the dependancy rules for the methods/XPL files for (sort grep(/\.xpl$/, keys %::PM_FILES)) { s/\.xpl$//; push(@text, "$_.xpl: $_.base $_.help $_.code"); } join("\n", @text);

        Amidst all the funky quoting there, what that is doing is pushing plain ol' make syntax for two new rules: rpm and srpm. Right after that, it iterates over a list that was defined earlier, and adds some dependancy rules so that a change to A.code triggers a rebuild of A.xpl. What matters is the last line I provide up there: the join(). That's the last statement of the subroutine, and the result from it is the return value of the sub. In this case, it takes all of @lines and joins them together with line-break characters. The resulting code in the Makefile looks a little like this:

        rpm: $(SPECFILE) $(DISTVNAME).tar$(SUFFIX) $(RPM) -bb --clean --rcfile rpmrc $(SPECFILE) srpm: $(SPECFILE) $(DISTVNAME).tar$(SUFFIX) $(RPM) -bs --clean --rcfile rpmrc $(SPECFILE)

        (I didn't include the dependancy rules because they have lengthy, ugly relative paths and wrap around a lot.)

        So, how would you apply this to your problem? Push onto @lines text that will add copying of the files to the "install" rule. Do you know where you want them to go? Is it a location relative to where the modules themselves go? Maybe you want to define a Makefile variable for the destination, so that you can override it on the command-line if needed. So we'll write two new methods: one for postamble, and one for post_constants. The method post_constants gets called after MM has written all of its internal constants, so it's the ideal place for you to write yours. Let's say that you want to use the existing INSTALLSITELIB value (which is /usr/lib/perl5/5.6.0 on my Red Hat 7.2 box), but with a "/db" appended to it, as the place for your DB files. Then your library can use the Config module to build the pathname. It will look kind of like this:

        sub MY::post_constants { # With this, you can override INSTALLSITELIB when # you call WriteMakefile, or when you run make. # You can also override INSTALL_DB_FILE when you # run make. Assume you've set @list_of_db_files join("\n", 'INSTALL_DB_FILES=\$(INSTALLSITELIB)/db', "DB_FILES=@list_of_db_files"); } sub MY::postamble { my $self = shift; my @text; push(@text, 'install:: install.dbfiles', '', 'install.dbfiles:: $(DB_FILES)', "\t\$(MKPATH) \$(INSTALL_DB_FILES)", "\t\$(CP) \$(DB_FILES) \$(INSTALL_DB_FILES)"); join("\n", @text); }

        Some notes on this example: you don't have to first define a target like "install.dbfiles" for "install", then put all the extra stuff under the new target. That's just a matter of taste and good planning. This approach means you can run "make install.dbfiles" by itself, to make sure it is installing where you expect it to (or run make with "-n" to at least sanity-check it). Secondly, instead of pasting in INSTALLSITELIB directly, I reference it as a make variable. Now INSTALL_DB_FILES is a direct function of that path, unless you explicitly provide it on the command-line to make. And if you change INSTALLSITELIB either on the command-line or in the call to WriteMakefile(), everything still stays in place. Third, instead of calling cp or mkdir, I use constructs that MM provides that are more portable, and more likely to work across platforms. Lastly, MM provides the trailing newlines, so you don't have to feed join() an extra null string to force a final newline.

        I hope this helps. I wrote an early article on MakeMaker for the Perl Journal. It's now archived by the publishers of SysAdmin magazine. If I ever get enough round tuits to spare, I may consider writing a follow-up, to cover more advanced features.

        --rjray

Re: MakeMaker Hacking?
by hsmyers (Canon) on Feb 21, 2002 at 16:05 UTC
    For those following along, here is the solution that worked, based on Randy's advice:
    use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. my @list_of_db_files = ('db\ECO','db\NIC','db\Opening'); WriteMakefile( 'NAME' => 'Chess::PGN::EPD', 'VERSION_FROM' => 'EPD.pm', # finds $VERSION 'PREREQ_PM' => {}, # e.g., Module::Name => 1.1 ($] >= 5.005 ? ## Add these new keywords supported since 5.005 (ABSTRACT_FROM => 'EPD.pm', # retrieve abstract from module AUTHOR => 'Hugh S. Myers <hsmyers@sdragons.com>') : ()), ); sub MY::post_constants { join("\n", 'INSTALL_DB_FILES=$(INSTALLSITELIB)\Chess\PGN\db', "DB_FILES=@list_of_db_files"); } sub MY::postamble { my $self = shift; my @text; push(@text, 'install :: install.dbfiles', '', 'install.dbfiles:: $(DB_FILES)', "\t\$(MKPATH) \$(INSTALL_DB_FILES)", "\t\$(CP) \$(DB_FILES) \$(INSTALL_DB_FILES)"); join("\n",@text); }

    –hsm

    "Never try to teach a pig to sing…it wastes your time and it annoys the pig."