How can I make Module::Build install both X and X::Y? - perl

If I have a distribution with X and X::Y also in it, how do I make Module::Build install both the modules? I have put X.pm in lib, written a file Build.PL with the line
my $build = Module::Build->new
(
module_name => "X",
);
This installs X OK, but how do I tell Module::Build to also include X::Y in the distribution?

Module::Build should automatically find and install both modules, though you should indicate to it (in Build.PL) which one the distribution name/version is taken from.
Try creating your distribution with module-starter and let it worry about the details?
module-starter -mb --module=X --module=X::Y --author=Me --email=me#example.com

Related

Installing additional files at install time with ExtUtils::MakeMaker/Dist::Zilla (dzil)

tl;dr I want to ship a package.json with my Perl library, run yarn install (or npm install during the installation) and install the downloaded JavaScript dependencies with the Perl modules.
I have the following dist.ini:
name = Foobar
version = 1.2.3
license = Perl_5
copyright_holder = Yours Truly
copyright_year = 2018
[#Filter]
-bundle = #Basic
-remove = GatherDir
[Git::GatherDir]
[Web::FileHeader]
header_filename = EMM-include.pm
file_match = ^Makefile\.PL$
The file EMM-include.pm contains a MY::postamble method:
package MY;
use strict;
use Cwd qw(abs_path);
use File::Spec;
sub postamble {
my ($self) = #_;
my $here = Cwd::abs_path();
my $libdir = File::Spec->catdir($here, 'lib', 'Foobar');
chdir $libdir or die;
0 == system 'yarn', 'install' or die;
chdir $here or die;
return '';
}
The plug-in [Web::FileHeader] takes the file and patches it to the beginning of Makefile.PL.
Then there is a lib/Foobar/package.json:
{
"name": "foobar",
"version": "1.2.3",
"main": "index.js",
"dependencies": {
"ajv": "^6.5.4"
}
}
The MY::postamble section from EMM-include.pm invokes yarn install (replace it with npm install if you don't have yarn) and populate the directory lib/Foobar/node_modules with ajv and its dependencies.
Finally, there must be a module lib/Foobar.pm:
package Foobar;
# ABSTRACT: Just a test.
1;
That almost works as intended: The distribution can be created with dzil build. In the distribution directory, perl Makefile.PL invokes yarn install, the directory lib/Foobar/node_modules gets populated but the files in there are not installed with make install.
If I run perl Makefile.PL a second time, everything works, the JavaScript dependencies make it into blib/ and make install would install the JavaScript modules alongside the Perl modules.
Shipping the JavaScript dependencies with the distribution is not an option. They are already too many and they may have conflicting licenses (I am using GPLv3 here). Downloading the deps at runtime, after the installation will mostly fail because of missing privileges.
True, this has not that much to do with Dist::Zilla, it's rather a problem with ExtUtils::MakeMaker. But I am actually using Dist::Zilla here.
In case it matters, the real distribution is https://github.com/gflohr/qgoda and the last commit at the time of this writing is https://github.com/gflohr/qgoda/commit/3f34a3dfec8da665061432c3a8f7bd8eef28b95e.
First, instead of using [Web::FileHeader] to alter your Makefile.PL, replace [MakeMaker] (used by #Basic) with [MakeMaker::Awesome], which allows you to modify Makefile.PL directly, and correctly enables dynamic_config since your distribution needs it. Also, don't give your include-file a .pm extension since it's not a perl module, and exclude it from being gathered into the resulting distribution so it doesn't accidentally get installed.
[#Filter]
-bundle = #Basic
-remove = GatherDir
-remove = MakeMaker
[Git::GatherDir]
exclude_filename = EMM-include
[MakeMaker::Awesome]
header_file = EMM-include
I strongly suggest using my #Starter bundle instead of the outdated #Basic, but if not at least add [MetaJSON] so you have modern metadata.
[#Starter::Git]
revision = 3
installer = MakeMaker::Awesome
Git::GatherDir.exclude_filename[] = EMM-include
MakeMaker::Awesome.header_file = EMM-include
Regarding what needs to be done at install time. First I will caution that requiring an internet connection to install is not something you can always rely on, nor is having yarn available of course. But the Alien series of modules for installing external libraries does this sort of thing often. Since you don't need to compile this code you probably don't need the whole Alien::Build/Alien::Base setup, but it may turn out to be an easier way to solve your problem than Makefile hacking described below. Basically you would first release an Alien distribution which installs your javascript library if necessary, and then this distribution could depend on that to load the library. If you decide to pursue this direction, check out Alien::Build, and the IRC channel #native on irc.perl.org.
The postamble section for ExtUtils::MakeMaker is not for running arbitrary code; it's for adding custom rules to the Makefile it generates; this is the way you need to influence the make process. I know very little about Makefiles so I can't help you further here, all I can suggest is to read all of the EUMM docs and note that the postamble is a function from MM_Any which you override to add your text, among other options from MM_Any and MM_Unix. You may be able to find people to help you in this direction on the IRC channel #toolchain on irc.perl.org.

Perl: Makefile.PL, File::ShareDir::Install, chicken and eggs

The File::ShareDir::Install module represents a practical way of carrying around auxiliary files with a Perl distribution/module. I feel however a bit puzzled on how to include it in the dependencies of my project.
I tried to install my package on a fresh machine (actually a docker container with base OS + Perl + CPAN) and I got the error:
Can't locate File/ShareDir/Install.pm in #INC ... at Makefile.PL line 7.
According to the documentation (perldoc File::ShareDir::Install), the pattern should be, in my Makefile.PL:
use ExtUtils::MakeMaker;
use File::ShareDir::Install;
install_share 'share';
install_share dist => 'dist-share';
install_share module => 'My::Module' => 'other-share';
WriteMakefile( ... ); # As you normaly would
package MY;
use File::ShareDir::Install qw(postamble);
However, by doing so I need File::ShareDir::Install to be pre-installed on my system as requirement to run the Makefile.PL script. Declaring it as dependency will not work, for obvious reasons!
Should I instruct my users to explicitly instasll File::ShareDir::Install before my module? Would it be possible to install it programmatically, within Makefile.PL, by directly calling the CPAN module?
This is what CONFIGURE_REQUIRES is for:
Available in version 6.52 and above.
A hash of modules that are required to run Makefile.PL itself, but not to run your distribution.
This will go into the configure_requires field of your META.yml and the configure of the prereqs field of your META.json.
Defaults to { "ExtUtils::MakeMaker" => 0 } if this attribute is not specified.
The format is the same as PREREQ_PM.
So you would add to your WriteMakefile parameters:
CONFIGURE_REQUIRES => {
"ExtUtils::MakeMaker" => '6.52',
"File::ShareDir::Install" => 0,
},
Then people using cpan or cpanm to install modules will get File::ShareDir::Install installed automatically.
To build the distribution
Make a File::ShareDir::Install a build-time dependency.
CONFIGURE_REQUIRES => {
'ExtUtils::MakeMaker' => '6.52',
'File::ShareDir::Install' => 0,
},
Install File::ShareDir::Install on the machine on which you will build the distribution.
Build the distribution.
This will create a META.yml that includes File::ShareDir::Install as a build-time dependency.
To install the distribution
Just use cpan or cpanm as normal. cpan and cpanm will extract build-time dependencies from META.yml and install them before running Makefile.PL.

Building perl module gives metafile error

I'm working on installing a perl module (not using CPAN) on a Linux machine. When I run the command:
perl Build.PL
I get the following error:
ERROR: Missing required field 'dist_abstract' for metafile
Could not get valid metadata. Error is: Invalid metadata structure.
Errors: Missing mandatory field, 'abstract' (abstract) [Validation: 1.4],
value is an undefined string (abstract) [Validation: 1.4]
at /usr/local/share/perl5/Module/Build/Base.pm line 4559
Could not create MYMETA files
I've tried Googling bits and pieces of this error but haven't found any solutions. Just looking for a clue as to what might be causing this error.
Here's a link to a zip file containing the files required to install it:
https://oncourse.iu.edu/access/content/user/brilewis/Filemanager_Public_Files/DataDownloader.zip
First at all please make sure you have package Module::Build installed.
You need ungzip few gzipped files in this package. I don't realize why author gzipped them:
gzip -d *.gz
I really don't know why author archived each install file. It looks like some mistake to me.
Than you can install all dependencies (this module requires some):
./Build installdeps
And then finally install module itself:
./Build
./Build test
./Build install
However I must warn you that this module packaged in a bit strange way and there's no guarantee it works.
The NAME section of the module does not have a - in it, e.g.,
=head1 NAME
Foo::Bar implements a Foo framework.
will fail, but if you make it
=head1 NAME
Foo::Bar - implements a Foo framework.
then it will work.
Do you have root access on your machine? Can you use the cpan utility to build and install your module. Using cpan is fairly straight forward:
$ cpan
After that, it will do a lot of configuration, simply take the default values. When it finishes, it'll come to a cpan> prompt. All you have to do there is type this:
cpan> install Module::Name
Where Module::Name is the module you're trying to install. Check the CPAN archive to get the name of your module.
If there are any dependencies, CPAN will ask if you want to download and install those. Say Yes, and CPAN will install the dependencies, then your module.
Using cpan is the best way to install third party modules you find in the CPAN archive. It takes care of all the dependencies, testing, and building for you.
Try installing through CPAN, and then see if you still have your issues.

How do I use a dependency on a Perl module installed in a non-standard location?

I need to install two Perl modules on a web host. Let's call them A::B and X::Y. X::Y depends on A::B (needs A::B to run). Both of them use Module::Install. I have successfully installed A::B into a non-system location using
perl Makefile.PL PREFIX=/non/system/location
make; make test; make install
Now I want to install X::Y, so I try the same thing
perl Makefile.PL PREFIX=/non/system/location
The output is
$ perl Makefile.PL PREFIX=/non/system/location/
Cannot determine perl version info from lib/X/Y.pm
*** Module::AutoInstall version 1.03
*** Checking for Perl dependencies...
[Core Features]
- Test::More ...loaded. (0.94)
- ExtUtils::MakeMaker ...loaded. (6.54 >= 6.11)
- File::ShareDir ...loaded. (1.00)
- A::B ...missing.
==> Auto-install the 1 mandatory module(s) from CPAN? [y]
It can't seem to find A::B in the system, although it is installed, and when it tries to auto-install the module from CPAN, it tries to write it into the system directory (ignoring PREFIX). I have tried using variables like PERL_LIB and LIB on the command line, after PREFIX=..., but nothing I have done seems to work.
I can do make and make install successfully, but I can't do make test because of this problem. Any suggestions?
I found some advice at http://servers.digitaldaze.com/extensions/perl/modules.html to use an environment variable PERL5LIB, but this also doesn't seem to work:
export PERL5LIB=/non/system/location/lib/perl5/
didn't solve the problem.
The answer is local::lib, but you probably already know that :)
OK, the following prescription did it:
perl Makefile.PL --skipdeps --no-manpages PREFIX=/non/system/location INSTALLSITELIB=/non/system/location/lib INSTALLSITEBIN=/non/system/location/bin INSTALLMAN1DIR=/non/system/location/man/man1 INSTALLMAN3DIR=/non/system/location/man/man3
This is just "monkey see monkey do" but now make test works.
The --skipdeps option here suppresses a convenient feature/exasperating problem with Module::Install where it tries to use CPAN.pm to download missing modules.
The --no-manpages is supposed to stop it installing man pages but it doesn't work.
Because this is the top link i thought i'd update with my experience (which has taken a while to get working, hence updating the 7 year old post).
first run perl -le 'print join $/, #INC'
add (note, no / at the end!!)
export PERL5LIB=/nonstddir/scripts/modules/lib/site_perl:/nonstddir/scripts/modules/lib
run perl -le 'print join $/, #INC' make sure the new dirs are added. this makes it work. if you add a / at the end of the path, the INC entry will look weird and wrong. Mine had a // in the middle.
When done and working, mine looks like
/nonstddir/scripts/modules/lib/site_perl/5.8.4/sun4-solaris-64int
/nonstddir/scripts/modules/lib/site_perl/5.8.4
/nonstddir/scripts/modules/lib/site_perl
/nonstddir/scripts/modules/lib/sun4-solaris-64int
/nonstddir/scripts/modules/lib
/usr/perl5/5.8.4/lib/sun4-solaris-64int
/usr/perl5/5.8.4/lib
/usr/perl5/site_perl/5.8.4/sun4-solaris-64int
/usr/perl5/site_perl/5.8.4
/usr/perl5/site_perl
/usr/perl5/vendor_perl/5.8.4/sun4-solaris-64int
/usr/perl5/vendor_perl/5.8.4
/usr/perl5/vendor_perl

How can I check if a binary dependency is available in Perl?

Having volunteered to maintain a stagnant CPAN package (GnuPG) I'd like improve the install files such that they exit gracefully if the gpg binary (which GnuPG is a wrapper for) cannot be found. After a bit of seeking inspiration from other packages, I've come up with adding this to Makefile.PL:
my #paths = grep { -x "$_/gpg" } split /:/, $ENV{PATH}, $ENV{PGP_PATH};
unless ( scalar #paths ) {
print <<EOD;
I can't find the gpg binary on your system. If it's not installed in your usual PATH, set $ENV{PGP_PATH} to include where it can be found and try installing again.
EOD
exit(0);
}
WriteMakefile(
'NAME' => 'GnuPG',
'VERSION_FROM' => 'GnuPG.pm',
'EXE_FILES' => [ gpgmailtunl ],
'LICENSE' => 'GPL',
'LIBS' => [ #paths ],
);
Does that look sane?
If you're using Module::Install or part of that family, you can use do
requires_external_bin 'gpg';
See Module::Install::External for details.
No good reason to reinvent the wheel.
The general concept is fine - if what you need to work is not present, don't create the makefile. The CPAN Testers have rules about exiting with a zero status on failure (which annoys me a lot, but never mind; I hate failing with a success status!).
Question: do you keep a record of where PGP was found at install time, so that if somebody else uses the Perl module without the location on their path, the module can still run?
For DBD::Informix, I have rigid dependencies without which the module cannot be compiled; the Makefile.PL is a major production in its own right because of that. It also tries to handle versions of the software covering over 15 years; that complicates its life, too. If the pre-requisites (some Perl modules; some non-Perl software) is not available, it won't install.
Wouldn't it make more sense to just print a warning? Is gpg necessary for the installation itself?
The code itself looks fine by me. But perhaps there's a built-in 'which' functionality. :).
For better accuracy you should look at File::Which or at least use File::Spec->path().
File::Which would be a crossplatform solution. You will need to either bundle it into inc/ directory or require it to be installed with configure_requires. For EU::MM it can be done with
META_MERGE => {
configure_requires => {
'File::Which' => 0,
Module::Install is also a good solution, but you will need to release new version of distribution each time new version of Module::Install is released and changes are important.