What tools can help build an XS project? - perl

I've recently started learning XS using perlxstut and the tutorial suggests that I create my module using the old h2xs tool to create an ExtUtils::MakeMaker-based project. However for pure Perl projects, h2xs/EUMM has long been disfavoured in favour of Module::Install, Module::Build or Dist::Zilla.
Is there a more modern way of creating XS projects? Can Module::Starter create XS projects? Can Module::Build or Dist::Zilla build XS projects? Their pod pages are silent on the matter.
On the flip side, does the criticism that was levelled at h2xs/EUMM apply to XS projects? If you need a C compiler anyway, is it reasonable to demand a make tool as well?
EDIT: I see this question answers my question about creating a project. I'd still like to know about building: is EUMM the only option, or are Module::Build and Dist::Zilla also capable of building XS?

It turns out that Module::Build is perfectly capable of compiling XS. Here is a complete Build.PL I managed to scrape together:
use strict;
use Module::Build;
my $build = Module::Build->new(
module_name => 'Chocolate::Belgian',
dynamic_config => 1,
license => 'perl',
requires => {
'Module::Build' => '0.19', # xs
'Test::More' => 0,
},
extra_compiler_flags => '-Iinclude',
extra_linker_flags => '',
c_source => 'src',
needs_compiler => 1,
xs_files => {
'./Belgian.xs' => 'lib/Chocolate/Belgian.xs',
},
);
$build->create_build_script;
This will build a distribution with .h include files (such as ppport.h) in the include/ directory, .c source files in the src/ directory, and an .xs file corresponding to package Chocolate::Belgian in the project base directory.
extra_compiler_flags corresponds to make CCFLAGS, while extra_linker_flags corresponds to LIBS (so you might want -lm there to link the C math library).

Dist::Zilla is not a replacement for EUMM or Module::Build, what it will do is generate a Makefile.Pl (etc) for you, I would not be surprised to hear that it can't do this for an XS project, but there are ways of managing your own for a dzil project. It can work with whatever Makefile.Pl it is provided with (or Build.pl).
So my answer on the Dist::Zilla part of your question is, Dist::Zilla does not fill that role in a project.

I always just use some fairly simple XS distribution as a starting point. h2xs can do some of the XS generation by parsing a header, but most of the time, I found that too limited to be useful.
If you're planning to wrap C++, you may want to take a look at Module::Build::WithXSpp.

Related

ExtUtils::MakeMaker doesn't generate make target to install dependencies for development

I'm building a Perl module. Makefile.PL has
WriteMakefile(
PREREQ_PM => {
'DBI' => '>= 1.641, < 2',
# etc, ...
},
TEST_REQUIRES => {
'Test::More' => 0,
},
# more stuff ...
);
Recently, my ~/perl5 tree got corrupted (PERL5LIB=~/perl5) so I decided to rebuild it, so I blew it away. Doesn't WriteMakefile() generate some make target that will do this for me with all the modules/packages in PREREQ_PM and TEST_REQUIRES. Instead, it just prints warnings:
Warning: prerequisite DBI >= 1.641, < 2 not found.
So I have to start re-installing all these by hand in order to run tests. For my small project, this isn't such a problem, but what if the project had many dependencies? Isn't there a way to automate this?
There is no Makefile target that will do what you want, but the cpan tool will use the information in the META file created by Makefile.PL to fetch and install dependencies.
If you to use cpan offline, consider cloning CPAN using CPAN::Mini.

Can a Perl module distribution include a script

I'm considering of using modules to group scripts. I often end up in the scenario that I have one or more scripts that uses a certain module.
Is it possible to pack the scripts together with the module?
Are there any specifics to how to achieve that?
Thanks for your input.
You can include scripts along with your module by including the EXE_FILES directive in a Makefile.PL file, and supplying the relative path to the scripts in an array reference. In the below example, the scripts live in a bin/ directory within the top level of your module's directory. You do not need to have your files in a distribution-like layout, but it helps (see below).
After setting up your directory structure and fiddling with the paths within the Makefile.PL, running make install will install your scripts along with your module.
use ExtUtils::MakeMaker;
WriteMakefile(
NAME => 'My::Module',
VERSION_FROM => 'lib/My/Module.pm',
($] >= 5.005 ?
(ABSTRACT_FROM => 'lib/My/Module.pm',
AUTHOR => 'My Name <email.addr>') : ()),
LIBS => [],
EXE_FILES => [qw(bin/script1 bin/script2)],
DEFINE => '',
INC => '-I.',
PREREQ_PM => {},
);
Personally, I'd recommend turning your module into a full-blown distribution. Here's an example. First, install Module::Starter, then:
module-starter --author="My Name" --email="my#email.com" --module=My::Module --eumm
Now you can copy your module's code to lib/My/Module.pm, create a bin/ directory, put your scripts in it, add the EXE_FILES directive to the Makefile.PL, and you can use the full suite of make commands to make, test and install your modules and the scripts, eg:
perl Makefile.PL
make
make test
make install
Here's a real-world example, one of my own distributions on the CPAN that includes a bundled pinmap Perl script along with the distribution's modules:

CPAN module prereqs

I plan on uploading a module to CPAN, shortly. This is the first module I've contributed. I've got the module to what I'd consider a "beta" stage. I'm using ExtUtils::MakeMaker to generate a Makefile through Makefile.PL (I've pasted the contents of it below). The Makefile.PL script has all the prereq modules listed. I'm wondering at which point in the installation process, the prereq modules are installed if they're not present? I'm wondering because I ran Makefile.PL followed by make then make install in a separate environment that's missing some of the prereq modules. However, they were not installed? I was under the impression they would be but maybe im missing something? I'm looking for someone to provide some clarity. Thanks in advance~
Makefile.PL
#!/usr/bin/env perl
use strict;
use warnings;
use ExtUtils::MakeMaker;
WriteMakefile(
NAME => 'Imgur',
VERSION => '0.01',
PREREQ_PM => {
'JSON' => 2.90,
'LWP::UserAgent' => 6.05,
'HTTP::Request::Common' => 6.04,
'Data::Dumper' => 2.154,
'DateTime::Format::ISO8601' => 0.08,
'Config::IniFiles' => 2.86,
'Scalar::Util' => 1.42,
'Class::Std::Utils' => 0.0.3,
'MIME::Base64' => 3.15,
'File::Slurp' => 9999.19
}
);
The Makefile.PL doesn't install prerequisites; it just complains if they're not installed. It's the CPAN client's job to install prerequisites.
Note: Module::Install has an auto_install feature that does this, but the general consensus seems to be that using it is a bad idea.

Can I build Perl modules with ExtUtils::MakeMaker-based build system "out of tree"?

Instead of adding or modifying files in the directory where the sources of a Perl module are unpacked, I would like to build everything in a separate directory. Is this easily achievable with a fairly standard Makefile.PL that uses ExtUtils::MakeMaker? (By easy, I mean something like one or a few command line parameters.) If no, does any of the other build systems support this?
Update / Reason: The Perl module is a binding to a library whose build system is autoconf/automake/libtool-based. The Perl module is shipped together with this library and calling make in the top directory eventually also builds the Perl library. I am interested in building the entire project in a separate build tree. At the moment I do something similar to what runrig suggested, only using symlinks. (cp -sru $(srcdir)/. $(builddir)/.). This has worked so far, but if there is a more elegant solution, I'd like to read about it.
MakeMaker already copies the sources and builds them in a separate directory (that's what blib/ is). You can control the build location with the INST_* set of arguments to WriteMakefile(). This example changes the location from blib/ to foo/.
INST_ARCHLIB => "foo/arch",
INST_LIB => "foo/lib",
INST_BIN => "foo/bin",
INST_SCRIPT => "foo/script",
INST_MAN1DIR => 'foo/man1',
INST_MAN3DIR => 'foo/man3',
In addition you have to tell MakeMaker to cleanup the new build directory.
clean => {
FILES => 'foo'
},
See "Determination of Perl Library and Installation Locations" in the ExtUtils::MakeMaker docs for more info.
cp -R Module-Directory-0.01 Module-Directory-0.01.copy
cd Module-Directory-0.01.copy
perl Makefile.PL
make
make test
...etc.
I ended up using symlinks:
The library to which the Perl module provides bindings uses an
autoconf/automake/libtool-based build system. Makefile.PL is
generated from Makefile.PL.in by configure. Makefile.PL
generates Makefile-pl (Makefile has already been taken by
autoconf/automake).
This is the relevant part of Makefile.am:
all: Makefile-pl src_deps
$(MAKE) -f Makefile-pl
Makefile-pl: Makefile.PL
perl Makefile.PL INSTALLDIRS=$(INSTALLDIRS) PREFIX=$(prefix)
I changed the second target to:
Makefile-pl: Makefile.PL
-[ $(srcdir) != $(builddir) ] && cp -rsu $(abs_srcdir)/. $(builddir)/.
perl Makefile.PL INSTALLDIRS=$(INSTALLDIRS) PREFIX=$(prefix)
This should work as long as building or installing the Perl module
does not result in any files being modified in-place.

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.