Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask

EU::MM recipes -- a lazy Makefile.PL for README and repository info

by Discipulus (Abbot)
on Jan 28, 2021 at 22:10 UTC ( #11127609=perlmeditation: print w/replies, xml ) Need Help??

Hello folks!

I use module-starter to sketch up my modules and it defaults to good 'ol ExtUtils::MakeMaker which is a basic author tool and also an end user tool. After it many more featured authoring tools appeared on the perl scene and I have collected a bounch of useful links at the end of this post, for your pleasure. But...

After asking and digging about let Makefile.PL to do the Readme file for me -- new target? I realized an extra target what not was I needed. To much work to remember to even call it. So I have investigated a bit about standard targets to see if I had the chance to hack some of them and the Makefile.PL iteself, treated as a normal perl program.

My goals..

I had two goals and they are reached with the below code: firstly I needed a way to generate my README file automatically from the pod section of the main module and secondly I want my eventual git repository added automatically to my META.yml and META.json files as explained in perlmaven article but in a lazier way.

With the help you gave me and some important contribution given by the irc #perl channel I have now something working the way I like and that you can review in details below.

..and yours?

I'm sure many of you have other interesting things to show as makefile recipes or makefile tricks as the wise monk pryrt shown in his Re: let Makefile.PL to do the Readme file for me -- new target? about his Win32::Mechanize::NotepadPlusPlus module.

Here you have the basic Makefile.PL produced by module-starter with my modification clearly marked by big comments at start and end:

use 5.010; use strict; use warnings; use ExtUtils::MakeMaker; ################################ # BEGIN CUSTOMIZATION ################################ # used to be passed to WriteMakefile my %meta_merge_args; if (!-e 'META.yml') { my $ask_for_git = prompt ("*** CUSTOM *** (only relevant for autho +r) Do you want to specify a git repository (y or N)?:"); if ( $ask_for_git =~ /^y/i ){ my $git_repo = prompt ("*** CUSTOM *** Full URL of the git rep +ository:"); die "Empty git repository! Answer n to the previous question i +f you dont want to specify a git repository!" unless length $git_repo; $git_repo =~ s/\/$//; my $git_file = prompt ("*** CUSTOM *** Full URL of git repos +itory file:", $git_repo.'.git'); my $git_issues = $git_repo.( $git_repo =~ / ? '/-' +: '').'/issues'; $git_issues = prompt ("*** CUSTOM *** Full URL for git issues: +", $git_issues); my $home_page = prompt ("*** CUSTOM *** Full URL of an eventu +al homepage?:", $git_repo); # fill in the %meta_merge_args in the main package $meta_merge_args{ META_MERGE } = { 'meta-spec' => { version => 2 }, 'resources' => { 'repository' => { 'type' => 'git', 'url' => $git_file, 'web' => $git_repo, }, 'bugtracker' => { 'web' => $git_issues, }, 'homepage' => $home_page, } }; } } # override MY::distdir # see +r-Methods package MY; # so that "SUPER" works right sub distdir { # SAVE WHAT RECEIVED BY THE ORIGINAL distdir my $inherited = $_[0]->SUPER::distdir(@_); #print "DEBUG -->$inherited<--\n"; # uncomment to see what is pas +sed # CALL THE DEFAULT distdir $_[0]->SUPER::distdir(@_); # README: update the README using the POD from main module my $make_readme =<<'MAKE_README'; $(TOUCH) README $(NOECHO) $(ECHO) > README $(NOECHO) $(ECHO) *** CUSTOM *** Generating README from POD of $(V +ERSION_FROM) $(PERLRUN) -MPod::Text \ -e "Pod::Text->new (sentence => 1, width => 78)->parse_from_file( +qq( $(VERSION_FROM) ) ) or die $$!;" >> README $(NOECHO) $(ECHO) *** autogenerated by a lazy Makefile.PL *** >> R +EADME $(NOECHO) $(ECHO) *** CUSTOM *** Regenerating MANIFEST $(PERLRUN) -M"5;push @ARGV,'MANIFEST'" -nle "print unless /^README +$$/" >> $(NOECHO) $(ECHO) *** CUSTOM *** Updating MANIFEST with README ent +ry $(NOECHO) $(ECHO) README >> $(MV) MANIFEST $(CP) MANIFEST $(DISTVNAME)/MANIFEST $(CP) README $(DISTVNAME)/README MAKE_README # make NEEDS tabs: ensure they are there $make_readme =~ s/^\s+/\t/gm; return join '', $inherited, $make_readme; } # back to main package package main; WriteMakefile( %meta_merge_args, ################################################## # END CUSTOMIZATION ################################################## # DEFAULT content made by module-starter NAME => 'Games::Dice::Roller', AUTHOR => q{LorenzoTa <>}, VERSION_FROM => 'lib/Games/Dice/', ABSTRACT_FROM => 'lib/Games/Dice/', LICENSE => 'artistic_2', PL_FILES => {}, MIN_PERL_VERSION => '5.010', CONFIGURE_REQUIRES => { 'ExtUtils::MakeMaker' => '0', }, BUILD_REQUIRES => { 'Test::More' => '0', 'Test::Exception' => '0', }, PREREQ_PM => { Carp => 0, }, );
In details: %meta_merge_args is used to add repository metadata to the final WriteMakefile call.

Then the if (!-e 'META.yml') { checks is to distinguish between author and end user: if META.yml is not there it is me running Makefile.PL so I ask for an eventual git repository. By other hand if the file is there it means that Makefile.PL was called by the end user while installing the module (nice trick from #perl irc channel).

ExtUtils::MakeMaker provides an handy prompt facility and other infos are asked with sane defaults (this version support issues both on gitlab and github ;) and these will be passed directly to WriteMakefile

The real makefile hack is overriding the distdir default target that is called inside the normal make dist action. The original arguments are saved and the original behaviour is replicated ( via my $inherited = $_[0]->SUPER::distdir(@_); and $_[0]->SUPER::distdir(@_); respectively) and then a big heredoc is used to inject new code directly into the generated Makefile

Inside Makefile there are already a lot of useful things to use as the definition of PERLRUN = "C:\ulisse\perl5.24-64b\perl\bin\perl.exe" to be invoked as $(PERLRUN) and bash like commands provided by ExtUtils::Command like MV = $(ABSPERLRUN) -MExtUtils::Command -e mv -- to be used as $(MV) origin destination so even perl oneliners are easy to use, with some caveats: the $ needs to be doubled as in the regex /^README$$/ and I had problems to pass filenames to oneliners and so I used the Maori Farewell trick to simulate a BEGIN block in $(PERLRUN) -M"5;push @ARGV,'MANIFEST'" -nle "print unless /^README$$/" >>

So the README is created by the main module pod ( $(VERSION_FROM) is handy there) then MANIFEST is updated and both files are copied to $(DISTVNAME) ie: the directory with the version numer that then wil be tarballed.

The whole output of the distro creation is (using my recent module):

io@COMP:C>ls -l total 13 -rw-rw-rw- 1 user group 791 Jan 28 17:03 Changes -rw-rw-rw- 1 user group 361 Jan 28 17:01 MANIFEST -rw-rw-rw- 1 user group 42 Jan 24 16:27 MANIFEST.SKIP -rw-rw-rw- 1 user group 5171 Jan 24 16:27 MANIFEST.bak -rw-rw-rw- 1 user group 3490 Jan 28 17:40 Makefile.PL drwxrwxrwx 1 user group 0 Jan 28 21:26 lib drwxrwxrwx 1 user group 0 Jan 28 21:26 t drwxrwxrwx 1 user group 0 Jan 28 21:26 xt io@COMP:C>perl Makefile.PL *** CUSTOM *** (only relevant for author) Do you want to specify a git + repository (y or N)?: y *** CUSTOM *** Full URL of the git repository: +enzoTa/games-dice-roller *** CUSTOM *** Full URL of git repository file: [ +renzoTa/games-dice-roller.git] *** CUSTOM *** Full URL for git issues: [ +games-dice-roller/-/issues] *** CUSTOM *** Full URL of an eventual homepage?: [ +LorenzoTa/games-dice-roller] Checking if your kit is complete... Looks good Generating a dmake-style Makefile Writing Makefile for Games::Dice::Roller Writing MYMETA.yml and MYMETA.json io@COMP:C>dmake dist "C:\ulisse\perl5.24-64b\perl\bin\perl.exe" -MExtUtils::Command -e rm_r +f -- Games-Dice-Roller-0.04 "C:\ulisse\perl5.24-64b\perl\bin\perl.exe" "-MExtUtils::Manifest=manic +opy,maniread" \ -e "manicopy(maniread(),'Games-Dice-Roller-0.04', 'bes +t');" mkdir Games-Dice-Roller-0.04 mkdir Games-Dice-Roller-0.04/lib mkdir Games-Dice-Roller-0.04/lib/Games mkdir Games-Dice-Roller-0.04/lib/Games/Dice mkdir Games-Dice-Roller-0.04/t mkdir Games-Dice-Roller-0.04/xt Generating META.yml Generating META.json "C:\ulisse\perl5.24-64b\perl\bin\perl.exe" -MExtUtils::Command -e touc +h -- README *** CUSTOM *** Generating README from POD of lib/Games/Dice/ "C:\ulisse\perl5.24-64b\perl\bin\perl.exe" -MPod::Text \ -e "Pod::Text->new (sentence => 1, width => 78)->parse_from_fi +le( qq( lib/Games/Dice/ ) ) or die $!;" >> README *** CUSTOM *** Regenerating MANIFEST "C:\ulisse\perl5.24-64b\perl\bin\perl.exe" -M"5;push @ARGV,'MANIFEST'" + -nle "print unless /^README$/" >> *** CUSTOM *** Updating MANIFEST with README entry "C:\ulisse\perl5.24-64b\perl\bin\perl.exe" -MExtUtils::Command -e mv - +- MANIFEST "C:\ulisse\perl5.24-64b\perl\bin\perl.exe" -MExtUtils::Command -e cp - +- MANIFEST Games-Dice-Roller-0.04/MANIFEST "C:\ulisse\perl5.24-64b\perl\bin\perl.exe" -MExtUtils::Command -e cp - +- README Games-Dice-Roller-0.04/README tar cvf Games-Dice-Roller-0.04.tar Games-Dice-Roller-0.04 Games-Dice-Roller-0.04/ Games-Dice-Roller-0.04/Changes Games-Dice-Roller-0.04/lib/ Games-Dice-Roller-0.04/lib/Games/ Games-Dice-Roller-0.04/lib/Games/Dice/ Games-Dice-Roller-0.04/lib/Games/Dice/ Games-Dice-Roller-0.04/Makefile.PL Games-Dice-Roller-0.04/MANIFEST Games-Dice-Roller-0.04/MANIFEST.bak Games-Dice-Roller-0.04/MANIFEST.SKIP Games-Dice-Roller-0.04/META.json Games-Dice-Roller-0.04/META.yml Games-Dice-Roller-0.04/README Games-Dice-Roller-0.04/t/ Games-Dice-Roller-0.04/t/00-load.t Games-Dice-Roller-0.04/t/01-single-die.t Games-Dice-Roller-0.04/t/02-identify-type.t Games-Dice-Roller-0.04/t/03-roll-basic-check.t Games-Dice-Roller-0.04/t/04-validate-expr.t Games-Dice-Roller-0.04/t/05-some-roll.t Games-Dice-Roller-0.04/t/06-keep-drop-rolls.t Games-Dice-Roller-0.04/t/07-rand-custom-funcion.t Games-Dice-Roller-0.04/t/08-result-summation.t Games-Dice-Roller-0.04/t/09-dice-pool.t Games-Dice-Roller-0.04/t/manifest.t Games-Dice-Roller-0.04/t/pod-coverage.t Games-Dice-Roller-0.04/t/pod.t Games-Dice-Roller-0.04/xt/ Games-Dice-Roller-0.04/xt/boilerplate.t "C:\ulisse\perl5.24-64b\perl\bin\perl.exe" -MExtUtils::Command -e rm_r +f -- Games-Dice-Roller-0.04 gzip --best Games-Dice-Roller-0.04.tar 'Created Games-Dice-Roller-0.04.tar.gz'

Interesting resources:

Other authoring tools (not complete list):

Interesting readings:

update 2021 Feb 08 I have added a check to ensure tabs are prepended to make recipe because spaces breaks everything.


There are no rules, there are no thumbs..
Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

Replies are listed 'Best First'.
Re: EU::MM recipes -- a lazy Makefile.PL for README and repository info
by pryrt (Monsignor) on Feb 01, 2021 at 22:46 UTC
    Discipulus, your recent chatterbox mention of this meditation reminded me that I'd never replied. You already linked to the main things I thought of as "tricks" in my Makefile.PL, so at the time, I didn't think I had anything to add. But on a re-read, I might have more to say.

    Your link to overriding existing EU::MM builtin targets was one I hadn't noticed in the EU::MM docs, so that was useful. When I had wanted to accomplish the same goal, I was able to use the Double-Colon Rule syntax and add a second target with the same name, which will get run after the builtin version. In a situation like The drawbacks that I had found over time to that methodology include: 1) it only works on the targets that EU::MM defined with :: instead of : , so it cannot "add" to single-colon rules (like distdir appears to be); (2) it can only be run after the builtin version (the gnu make manual strongly recommends only using it for truly independent rules that you just want to run with the same name, so that order shouldn't matter); and (3) it doesn't allow adding extra dependencies to the builtin version of the rule. Now that I know about the builtin-target override feature in Makefile.PL, I think I can overcome all three of those limitations, which is great.

    And with reference to my Makefile.PL, I might as well briefly describe the "tricks" behind them...

    1. TEST_D - explained in my earlier post
    2. realclean :: - this is my double-colon addition to the builtin realclean target, which deletes extra files that the builtin realclean doesn't
    3. populateversion :: - this grabs the version from the main .pm file and propagates it to the other module files, so that all the modules in the dist will keep the same version
    4. :: , LICENSE :: , and docs :: - these automatically create the from certain sections of the POD and the LICENSE file from appropriate sections of the POD, so that if I update the POD, the standalone files will match
    5. all_msgs :: and following - these are rather specific to the project (they help me automate grabbing source files from Notepad++ so that when NPP updates its list of windows messages and similar, I can more easily incorporate those new messages into my project)

    Like you said, you don't want to have to remember to run commands like gmake docs to update the document; I solve it in a less-automated way -- I have it in my how to release notes, to remind myself to update the documentation. I don't think I'm going to bundle it with gmake dist, because I want to make sure the docs are all up-to-date and committed to the repo before I generate my final distribution tarball. But each of us have our own workflow.

    I've noticed that all of those customizations are on the authoring side. I'm curious whether anyone out there has customized anything from the end-user side (that would change the default behavior of the make/make-test/make-install idiom) -- I cannot think of anything widely practical from the end-user perspective, so I'd like to see if anyone has done something brilliant in that regard.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://11127609]
Approved by marto
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (7)
As of 2022-01-18 13:52 GMT
Find Nodes?
    Voting Booth?
    In 2022, my preferred method to securely store passwords is:

    Results (53 votes). Check out past polls.