Building Perl projects with MakeMaker
By Sean Dague2005-05-01
Your First MakeMaker Project (make)
This project will contain a core module, Net::Ping::HTTP, which will implement the guts of what is happening, and pingwww, a program that provides a user interface to Net::Ping::HTTP.
Writing the Makefile.PL
I will begin by writing the Makefile.PL. At first it has only the most basic entries in it.
Makefile.PL for Net::Ping::HTTP, take 1
use ExtUtils::MakeMaker;This specifies that the name of the project is Net::Ping::HTTP, and that the $VERSION variable contained within lib/Net/Ping/HTTP.pm will be considered the authoritative version of the project.
WriteMakefile(
'NAME' => 'Net::Ping::HTTP',
'VERSION_FROM' => 'lib/Net/Ping/HTTP.pm', # finds $VERSION
);
Now I have a Makefile.PL, and one other file in the project. It is now time to create the MANIFEST, so that the build process will work correctly.
The first pass at a MANIFEST will look as follows:
MANIFEST, take 1
MANIFESTRemember, every time new files are added to the project, an entry for each of them must also be added to the MANIFEST file.Net::Ping::HTTP
Makefile.PL
lib/Net/Ping/HTTP.pm
The code for this module will be a simple layer on top of LWP::UserAgent, which is included in the libwww-perl package. Many flavors of Linux and UNIX include this library in their distribution by default. Also, if you have installed ActiveState Perl for Windows, you already have LWP::UserAgent. However, if you are unfortunate enough to be on an operating system that doesn't provide this module by default, you may still download it from CPAN (see Resources).
The strategy for Net::Ping::HTTP is simple. The HTTP protocol supports a method HEAD, which is designed to return only HTTP headers, but no content. Net::Ping::HTTP will send an HTTP HEAD request for the root document of the Web server you are attempting to ping, and will return the status code that is returned by that Web server. In the case where the Web server is available, this status code should be 200 (though in rare cases it might be a 30x or 40x code as well).
Here is a first pass at Net::Ping::HTTP, including the POD documentation for the module:
Net::Ping::HTTP library
package Net::Ping::HTTP;Net::Ping::HTTP uses both LWP::UserAgent, and HTTP::Request, and will not work without them. Instead of having a user install this module, then have it break because the dependencies aren't there, the module can specify which other modules it requires to run.
=head1 NAME
Net::Ping::HTTP - An interface for determining whether an HTTP
server is listening
=head1 SYNOPSIS
my $pinger = new Net::Ping::HTTP;
my $rc = $pinger->ping('http://localhost');
my $pinger = new Net::Ping::HTTP(
TIMEOUT => 15,
PROXY => 'http://someproxy.net',
);
my $rc = $pinger-> ping('http://www.ibm.com');
=head1 DESCRIPTION
Net::Ping::HTTP is a simple interface on top of LWP::UserAgent
that lets you ping a Web server to see if it is responding to HTTP
requests. This would make it suitable for monitoring Web applications.
=head1 AUTHOR
Sean Dague <sldague@us.ibm.com>
=head1 SEE ALSO
L<perl>, <LWP::UserAgent>
=cut
use strict;
use LWP::UserAgent;
use HTTP::Request;
use vars qw($VERSION);
$VERSION = '0.02';
sub new {
my $class = shift;
my %this = (
PROXY => undef,
TIMEOUT => 10,
@_,
);
my $ua = new LWP::UserAgent("Net::Ping::HTTP - $VERSION");
$this{UA} = $ua;
$this{UA}->timeout($this{TIMEOUT});
if($this{PROXY}) {
$this{UA}->proxy('http',$this{PROXY});
}
return bless \%this, $class;
}
sub ping {
my ($this, $url) = @_;
my $request = new HTTP::Request('HEAD',"$url");
my $response = $this->{UA}->request($request);
return $response->code();
}
1;
These modules can be added as requirements to the Makefile.PL. Their existence will be verified before the master Makefile is generated. The format of this new variable for Makefile.PL is as follows:
Makefile.PL prerequisites
'PREREQ_PM' => {
Module::Name => minimum.version.number,
...
},The code works with the version of LWP::UserAgent and HTTP::Request that I have installed on my workstation, so I will prereq those versions in my module. To determine those versions, I can run the following commands on the command line:
Finding module versions
perl -MLWP::UserAgent -e 'print $LWP::UserAgent::VERSION'
perl -MHTTP::Request -e 'print $HTTP::Request::VERSION'
The -M flag from perl loads that Module, the -e flag runs the following string as the perl script.
With the information collected, the Makefile.PL looks as follows:
Makefile.PL take 2
use ExtUtils::MakeMaker;pingwww
WriteMakefile(
'NAME' => 'Net::Ping::HTTP',
'VERSION_FROM' => 'lib/Net/Ping/HTTP.pm', # finds $VERSION
'PREREQ_PM' => {
LWP::UserAgent => 1.73,
HTTP::Request => 1.27,
});
The core library for the project is not complete. For it to be useful, there must exist a program that exploits its functionality and provides a user interface. This program will be called pingwww and will be placed in the bin/ directory of the project tree.
Two features of the ping command that would be nice to simulate in pingwww are the ability to specify the number of times ping sends packets out to the server before it exits, and a synchronous display of how long each request took to respond.
To add these features, the program will need two additional modules that were not required for Net::Ping::HTTP. These modules are Getopt::Std, which parses command line options, and Time::HiRes, which provides microsecond resolution timing. Getopt::Std is part of the core Perl distribution, so there is no need to add it as a PREREQ_PM, however Time::HiRes is not (though it appears that it will be part of the standard library as of Perl 5.8). Time::HiRes is hence added to the PREREQ_PM variable in Makefile.PL.
Here is the first pass at pingwww:
pingwww program
#!/usr/bin/perlBecause EXE_FILES includes pingwww, some interesting events happen during installation, which I will discuss later. The MANIFEST file must also contain an entry for the new file. The new MANIFEST file is shown below:
#
# This program is licensed under the same terms as Perl itself
#
# Sean Dague <sldague@us.ibm.com>
=head1 NAME
pingwww - program for "pinging" webservers
=head1 SYNOPSIS
pingwww [-c iterations] [-p proxy] [-t timeout] [-h] hostname
=head1 DESCRIPTION
Pingwww uses the Net::Ping::HTTP module to send HEAD requests
to the host specified by hostname.
=head1 AUTHOR
Sean Dague <sldague@us.ibm.com>
=head1 SEE ALSO
L<Net::Ping::HTTP>, L<Time::HiRes>, L<perl>
=cut
use strict;
use Carp;
use Getopt::Std;
use Net::Ping::HTTP;
use Time::HiRes qw(gettimeofday tv_interval);
$| = 1;
my %opts;
getopt('cpth',\%opts) or croak("Couldn't parse options");
if($opts{h}) {
usage();
}
my $max = $opts{c} || 1000;
my %vars = ();
if($opts{p}) {
$vars{PROXY} = $opts{p};
}
if($opts{t}) {
$vars{TIMEOUT} = $opts{t};
}
my $pinger = new Net::Ping::HTTP(%vars);
my $host = $ARGV[0];
my $packedip = gethostbyname($host);
my $ip = inet_ntoa($packedip);
print "PING HTTP $host ($ip):\n";
for(my $i = 0 ; $i < $max; $i++) {
my $start = [gettimeofday()];
my $rc = $pinger->ping("http://$host");
if ($rc < 400) {
print "Response $rc from $ip: ";
my $elapsed = tv_interval ( $start );
print "$elapsed seconds\n";
} else {
print "Response $rc from $ip: ";
print "failed\n";
}
}
sub usage {
print <<USAGE;
usage: $0 [-c number of iterations] [-p proxy] [-t timeout] [-h] hostname
where
-h : this message
-c : number of iterations to ping the host. Defaults to 1000
-p : http proxy to use for the ping
-t : timeout value. Defaults to 10 secs if none is specified
USAGE
exit(1);
}
Manifest, take 2
MANIFEST
Makefile.PL
lib/Net/Ping/HTTP.pm
bin/pingwww
Tutorial pages:
|
First published by IBM DeveloperWorks
|
|||||||||
You might also want to check these out:
|
Leave a Comment on "Building Perl projects with MakeMaker"
You must be logged in to post a comment.
Link to This Tutorial Page!

