]> git.donarmstrong.com Git - term-progressbar.git/blob - make-pm
remove unused read_file
[term-progressbar.git] / make-pm
1 # (X)Emacs mode: -*- cperl -*-
2
3 use 5.005;
4 use strict;
5
6 =head1 NAME
7
8 make - tools for making makefiles with.
9
10 =head1 SYNOPSIS
11
12   use constant MOD_REQS =>
13     [
14      { name    => 'Pod::Usage',
15        version => '1.12', },
16
17      { name    => 'IPC::Run',
18        package => 'IPC-Run',
19        version => '0.44', },
20
21      { name     => 'DBI::Wrap',
22        package  => 'DBI-Wrap',
23        version  => '1.00',
24        optional => 1, },
25     ];
26
27   use constant EXEC_REQS =>
28     [
29      { name    => 'blastpgp',
30        version => '1.50',
31        vopt    => '--version', },
32
33      { name    => 'mkprofile', },
34
35      { name    => 'mp3id',
36        version => '0.4',
37        vopt    => '--help',
38        vexpect => 255, },
39     ];
40
41   use constant NAME         => 'Module-Name';
42   use constant VERSION_FROM => catfile (qw( lib Module Name.pm ));
43   use constant AUTHOR       => 'Martyn J. Pearce fluffy@cpan.org';
44   use constant ABSTRACT     => 'This module makes chocolate biscuits';
45
46   use make.pm
47
48 =head1 DESCRIPTION
49
50 This package provides methods and initialization to build standard perl
51 modules.
52
53 The plan is, you define the requirements, and let the module take care of the
54 rest.
55
56 The requirements you must define are:
57
58 =over 4
59
60 =item MOD_REQS
61
62 An arrayref of hashrefs.  Each hashref represents a required Perl module, and
63 has the following keys:
64
65 =over 4
66
67 =item name
68
69 B<Mandatory> Name of the module used.  The presence of this module is checked,
70 and an exception is raised if it does not exist.
71
72 =item package
73
74 B<Optional> Name of the package in which the module is to be found.  If not
75 defined, the package is assumed to be present in core Perl.
76
77 Modules that have been in core Perl since 5.005 need not be listed; the "core
78 perl" default is for modules such as C<Pod::Usage> which have been added to
79 the core since 5.005.
80
81 =item version
82
83 B<Optional> If supplied, the version of the module is checked against this
84 number, and an exception raised if the version found is lower than that
85 requested.
86
87 =item optional
88
89 B<Optional> If true, then failure to locate the package (or a suitable
90 version) is not an error, but will generate a warning message.
91
92 =item message
93
94 If supplied, then this message will be given to the user in case of failure.
95
96 =back
97
98 =item EXEC_REQS
99
100 =over 4
101
102 =item name
103
104 Name of the executable used.  The presence of this executable is checked, and
105 an exception is raised if it does not exist (in the PATH).
106
107 =item package
108
109 B<Optional> Name of the package in which the executable is to be found.
110
111 =item version
112
113 B<Optional> If supplied, the version of the module is checked against this
114 number, and an exception raised if the version found is lower than that
115 requested.
116
117 If supplied, the L<vopt> key must also be supplied.
118
119 =item vopt
120
121 B<Optional> This is used only if the C<version> key is also used.  This is the
122 option that is passed to the executable to ask for its version number.  It may
123 be the empty string if no option is used (but must be defined if C<version> is
124 defined).
125
126 =item vexpect
127
128 B<Optional> This is used only if the C<version> key is also used.  This is the
129 exit code to expect from the program when polling for its version number.
130 Defaults to 0.  This is the exit code (value of C<$?> in the shell) to use,
131 I<not> the value of the C<wait> call.
132
133 =item optional
134
135 B<Optional> If true, then failure to locate the package (or a suitable
136 version) is not an error, but will generate a warning message.
137
138 =item message
139
140 If supplied, then this message will be given to the user in case of failure.
141
142 =back
143
144 =item NAME
145
146 The module name.  It must conform to the established standard; in particular,
147 it must B<not> contain colon characters.  The usual process, when providing a
148 single-package module (e.g., to provide C<MIME::Base64>), is to replace the
149 C<::> occurences with hyphens (hence, C<MIME-Base64>).
150
151 =item VERSION_FROM
152
153 The module from which to establish the version number.  This module must have
154 a line of the form C<$VERSION = '0.01';>.  Declarative prefixes (.e.g, C<our>)
155 are fine; C<our> is the usual one, since C<$VERSION> is almost always a
156 package variable.
157
158 =item AUTHOR
159
160 The name of the module author(s), along with an email address.  This is
161 normally the person primarily responsible for the upkeep of the module.
162
163 =item ABSTRACT
164
165 A single (concise!) sentence describing the rough purpose of the module.  It
166 is not expected to be mightily accurate, but is for quick browsing of modules.
167
168 =item DEPENDS
169
170 I<Optional>
171
172 If defined, this must be an arrayref of additional targets to insert into
173 F<Makefile>.  Each element must be a hashref, with the following keys:
174
175 =over 4
176
177 =item target
178
179 Name of the rule target
180
181 =item reqs
182
183 Arrayref of rule requisites
184
185 =item rules
186
187 Arrayref of rule lines.  Do not precede these with a tab character; this will
188 be inserted for you.  Likewise, do not break the lines up.
189
190 =back
191
192 E.g.,
193
194   use constant DEPENDS      => [
195                                 { target => 'lib/Class/MethodMaker.pm',
196                                   reqs   => [qw/ cmmg.pl /],
197                                   rules  => [ '$(PERL) $< > $@' ],
198                                 },
199                                ];
200
201 =item DERIVED_PM
202
203 I<Optional>.  If defined, this is expected to be an arrayref of file names
204 (relative to the dist base), that are pm files to be installed.
205
206 By default, F<make.pm> finds the pms to install by a conducting a C<find> over
207 the F<lib> directory when C<perl Makefile.PL> is run.  However, for pm files
208 that are created, that will be insufficient.  By specifying extras with this
209 constant, such files may be named (and therefore made), and also cleaned when
210 a C<make clean> is issued.  This might well be used in conjunction with the
211 L<DEPENDS|"DEPENDS"> constant to auto-make pm files.
212
213 E.g.,
214
215   use constant DERIVED_PM     => [qw( lib/Class/MethodMaker.pm )];
216
217 =cut
218
219 use Config                   qw( %Config );
220 use ExtUtils::MakeMaker      qw( WriteMakefile );
221 use File::Find               qw( find );
222 use File::Spec               qw( );
223 sub catfile { File::Spec->catfile(@_) }
224
225
226 # Constants ---------------------------
227
228 use constant TYPE_EXEC => 'executable';
229 use constant TYPE_MOD  => 'module';
230 use constant TYPES     => [ TYPE_EXEC, TYPE_MOD ];
231
232 use constant CONFIG =>
233   {
234    TYPE_MOD  , { defaults => { package => 'core perl',
235                              },
236                  find     => sub { eval "require $_[0]"; $@ eq '' },
237                  vers     => sub {
238                    no strict 'refs';
239                    # Fool emacs indenter
240                    my $_x = q={=; my $pv = ${"$_[0]::VERSION"};
241                    return defined $pv ? $pv : -1;
242                  },
243                },
244    TYPE_EXEC , { defaults => { vexpect => 0, },
245                  find     => sub {
246                    my ($name) = @_;
247                    my $exec;
248                  PATH_COMPONENT:
249                    for my $path (split /:/, $ENV{PATH}) {
250                      my $try = catfile $path, $name;
251                      if ( -x $try ) {
252                        $exec = $try;
253                        last PATH_COMPONENT;
254                      }
255                    }
256                    defined $exec;
257                  },
258                  vers     => sub {
259                    my ($name, $vopt, $expect) = @_;
260                    die "Cannot test version of $name without vopt\n"
261                      unless defined $vopt;
262                    my $cmd = join ' ', $name, $vopt;
263                    my $vstr = qx($cmd 2>&1);
264                    my $rv = $? >> 8;
265                    die sprintf "Command $cmd exited with value: $rv\n"
266                      if $rv != $expect;
267                    if ( $vstr =~ /(?:^|\D)v?(\d+(?:[._]\d+)+)(?![\d_.])/ ) {
268                      (my $version = $1) =~ tr/_/./;
269                      return $version;
270                    } else {
271                      return -1;
272                    }
273                  },
274                },
275   };
276
277 # Subrs ----------------------------------------------------------------------
278
279 sub warn_missing {
280   my ($missing) = @_;
281
282   my ($type_max) = sort { $b <=> $a } map length $_->{type}, @$missing;
283   my ($name_max) = sort { $b <=> $a } map length $_->{name}, @$missing;
284
285   for (@$missing) {
286     my ($type, $name, $pkg, $vers, $pv, $optional, $message) =
287       @{$_}{qw( type name package vers_req vers_fnd optional message )};
288
289     if ( defined $pv ) {
290       print STDERR sprintf("%-${type_max}s %${name_max}s requires version " .
291                            "$vers (found $pv)",
292                            $type, $name)
293     } else {
294       print STDERR sprintf("Couldn't find %${type_max}s %${name_max}s",
295                            $type, $name);
296     }
297
298     print STDERR " (from $pkg)"
299       if defined $pkg;
300     print STDERR "\n";
301
302     print STDERR "  ...but this isn't fatal\n"
303       if $optional;
304
305     if ( defined $message ) {
306       $message =~ s/(^|\n)/$1    /g;
307       $message =~ s/([^\n])$/$1\n/;
308       print STDERR "\n";
309       print STDERR $message;
310       print STDERR "\n";
311     }
312   }
313 }
314
315 # -------------------------------------
316
317 sub check {
318   my ($items, $verbose) = @_;
319
320   my ($type_max) = sort { $b <=> $a } map length, @{TYPES()};
321   my ($name_max) = sort { $b <=> $a } map length($_->{name}), @$items;
322
323   my @missing;
324
325   foreach my $item (@$items) {
326     my $type = $item->{type};
327     my $defaults = CONFIG->{$type}->{defaults};
328     $item->{$_} = $defaults->{$_}
329       for grep ! exists $item->{$_}, keys %$defaults;
330     my ($name, $pkg, $vers, $vopt, $vexpect) =
331       @{$item}{qw( name package version vopt vexpect)};
332
333     printf STDERR "Checking for %-${type_max}s %-${name_max}s...", $type, $name
334       if $verbose;
335     if ( CONFIG->{$type}->{find}->($name) ) {
336       print STDERR " found\n"
337         if $verbose;
338
339       if ( defined $vers ) {
340         my $vfound = CONFIG->{$type}->{vers}->($name, $vopt, $vexpect);
341         my $str_v_reqd  = join '_', map sprintf('%09d',$_), split /\./,$vers;
342         my $str_v_found = join '_', map sprintf('%09d',$_), split /\./,$vfound;
343         push @missing, { type     => $type,
344                          name     => $name,
345                          package  => $pkg,
346                          vers_req => $vers,
347                          vers_fnd => $vfound,
348                          optional => $item->{optional},
349                          message  => $item->{message},
350                        }
351           if $str_v_reqd gt $str_v_found;
352       }
353     } else {
354       print STDERR " failed\n"
355         if $verbose;
356       push @missing, { type     => $type,
357                        name     => $name,
358                        package  => $pkg,
359                        vers_req => $vers,
360                        optional => $item->{optional},
361                        message  => $item->{message},
362                      };
363     }
364   }
365
366   return @missing;
367 }
368
369 # Main -----------------------------------------------------------------------
370
371 # Self Test
372
373 if ( $ENV{MAKE_SELF_TEST} ) {
374   # Find Module (no version)
375   check([{ name => 'integer' , type => TYPE_MOD, }])
376     and die "Internal Check (1) failed\n";
377   # Fail module (no version)
378   check([{ name => 'flubble' , type => TYPE_MOD, }])
379     or die "Internal Check (2) failed\n";
380   # Find module, wrong version
381   check([{ name => 'IO'      , type => TYPE_MOD, version => '100.0', }])
382     or die "Internal Check (3) failed\n";
383   # Find module, right version
384   check([{ name => 'IO'      , type => TYPE_MOD, version => '1.00',  }])
385     and die "Internal Check (4) failed\n";
386
387   # Find exec (no version)
388     # Use more (common to dog/windoze too!) (mac?)
389   check([{ name => 'more'    , type => TYPE_EXEC, }])
390     and die "Internal Check (5) failed\n";
391   # Fail exec (no version)
392   check([{ name => ' wibwib' , type => TYPE_EXEC, }])
393     or die "Internal Check (6) failed\n";
394
395   # Could do with one that works on dog/windoze/mac...
396   if ( $Config{osname} eq 'linux' ) {
397     # Find exec, wrong version
398     check([{ name => 'cut'     , type => TYPE_EXEC,
399              version => '100.0', vopt => '--version', }])
400       or die "Internal Check (7) failed\n";
401     # Find exec, right version
402     check([{ name => 'cut'     , type => TYPE_EXEC,
403              version => '1.0', vopt => '--version', }])
404       and die "Internal Check (8) failed\n";
405   }
406 }
407 # -------------------------------------
408
409 my @missing;
410
411 {
412   no strict 'refs';
413   die "$_ not defined\n"
414     for grep ! defined *$_{CODE}, qw( MOD_REQS EXEC_REQS
415                                       NAME VERSION_FROM AUTHOR ABSTRACT );
416 }
417
418 die sprintf(<<'END', NAME) unless NAME =~ /^[A-Za-z0-9-]+$/;
419 The module name:%s: is illegal (letters, numbers & hyphens only, please)
420 END
421
422 $_->{type} = TYPE_MOD
423   for @{MOD_REQS()};
424 $_->{type} = TYPE_EXEC
425   for @{EXEC_REQS()};
426
427 push @missing, check(MOD_REQS, 1), check(EXEC_REQS, 1);
428
429 warn_missing(\@missing);
430
431 exit 2
432   for grep ! $_->{optional}, @missing;
433
434 my %pm;
435 find (sub {
436         $File::Find::prune = 1, return
437           if -d $_ and $_ eq 'CVS';
438         return unless /\.pm$/;
439         (my $target = $File::Find::name) =~
440           s/^$File::Find::topdir/\$(INST_LIBDIR)/;
441         $pm{$File::Find::name} = $target;
442       },
443       'lib');
444
445 sub MY::postamble {
446   <<EOF;
447 check: test
448 EOF
449 }
450
451 my %Config =
452   (NAME         => NAME,
453    VERSION_FROM => VERSION_FROM,
454    AUTHOR       => AUTHOR,
455    ABSTRACT     => ABSTRACT,
456    PREREQ_PM    => { map (($_->{name} => $_->{version} || 0 ),
457                           grep ! $_->{optional}, @{MOD_REQS()})},
458    PM           => \%pm,
459    # Need this to stop Makefile treating Build.PL as a producer of Build as a
460    # target for 'all'.
461    PL_FILES     => +{},
462    EXE_FILES    => [ grep !/(?:CVS|~)$/, glob catfile (qw( bin * )) ],
463    clean        => +{ FILES => [qw( Build _build )] },
464    realclean    => +{ FILES => [qw( Build.PL META.yml
465                                     INSTALL
466                                     SIGNATURE
467                                     make-pm )] },
468   );
469
470 $Config{PREFIX} = *PREFIX{CODE}->()
471   if defined *PREFIX{CODE};
472 push @{$Config{clean}->{FILES}}, @{*EXTRA_CLEAN{CODE}->()}
473   if defined *EXTRA_CLEAN{CODE};
474 push @{$Config{realclean}->{FILES}}, qw( Makefile.PL configure README )
475   if -e 'INFO.yaml';
476
477 if ( defined *DEPENDS{CODE} ) {
478   my $depends = *DEPENDS{CODE}->();
479   my %depends;
480   for (@$depends) {
481     my ($target) = $_->{target};
482     my ($reqs)   = $_->{reqs};
483     my ($rules)  = $_->{rules};
484
485     $depends{$target} = join("\n\t", join(' ', @$reqs), @$rules) . "\n";
486   }
487   $Config{depend} = \%depends;
488 }
489
490 if ( defined *DERIVED_PM{CODE} ) {
491   my $extra = *DERIVED_PM{CODE}->();
492   die sprintf "Don't know how to handle type: %s\n", ref $extra
493     unless UNIVERSAL::isa($extra, 'ARRAY');
494
495   for (@$extra) {
496     $Config{PM}->{catfile('lib', $_)} = catfile '$(INST_LIBDIR)', $_;
497     push @{$Config{clean}->{FILES}}, $_;
498   }
499 }
500
501 $Config{clean}->{FILES}     = join ' ', @{$Config{clean}->{FILES}};
502 $Config{realclean}->{FILES} = join ' ', @{$Config{realclean}->{FILES}};
503
504 WriteMakefile (%Config);
505
506 # ----------------------------------------------------------------------------
507
508 =head1 EXAMPLES
509
510 Z<>
511
512 =head1 BUGS
513
514 Z<>
515
516 =head1 REPORTING BUGS
517
518 Email the author.
519
520 =head1 AUTHOR
521
522 Martyn J. Pearce C<fluffy@cpan.org>
523
524 =head1 COPYRIGHT
525
526 Copyright (c) 2001, 2002, 2003 Martyn J. Pearce.  This program is free
527 software; you can redistribute it and/or modify it under the same terms as
528 Perl itself.
529
530 =head1 SEE ALSO
531
532 Z<>
533
534 =cut
535
536 1; # keep require happy
537
538 __END__