1#!/usr/local/bin/perl -w
2# $Id: Makefile.PL,v 1.476 2017/11/28 05:28:04 rmeden Exp $
3
4use ExtUtils::MakeMaker;
5use Config;
6use File::Basename ();
7use File::Find;
8
9# Don't use ':config pass_through' because that requires Getopt::Long
10# 2.24 or later, and we don't have a clean way to require that.
11#
12use Getopt::Long; Getopt::Long::Configure('pass_through');
13
14# Suppress 'isn't numeric' warnings from MakeMaker - see
15# <http://sourceforge.net/tracker/?func=detail&atid=424135&aid=870170&group_id=39046>.
16#
17$SIG{__WARN__} = sub {
18    for (my $msg = shift) {
19        $_ = "warning: something's wrong" if not defined;
20        warn $_
21          unless /Argument .+ isn.t numeric in numeric lt .+MakeMaker.pm/;
22    }
23};
24
25# ExtUtils::MakeMaker before 6.21 or so has a bug when PREFIX is not
26# given explicitly -
27# <http://article.gmane.org/gmane.comp.tv.xmltv.devel/3634>.
28#
29unless ($ExtUtils::MakeMaker::VERSION ge 6.21) {
30    if (not grep /^PREFIX=/, @ARGV) {
31        warn "You may want to explicitly give PREFIX to work around MakeMaker bugs.\n";
32    }
33}
34
35use strict;
36
37sub test_module( $$ );
38sub check_date_manip();
39sub test_special( $$ );
40sub targets( $ );
41
42if ($] == 5.006) {
43    warn <<END
44Some installations of perl 5.6.0 are buggy and crash while running
45this script.  If you get 'segmentation fault' below, please report it
46to the xmltv-users mailing list so we can try to work around the bug.
47At present the only known fix is to upgrade to a newer perl version
48such as 5.6.1 or 5.8.3.
49END
50;
51}
52
53# A couple of undocumented options to help with building the Windows
54# distribution even on hosts that don't have all the necessary
55# modules.  By default, we only warn if dependencies are missing.
56# If --strict-deps is passed in, then the whole process fails.
57#
58my $opt_strictdeps = 0;
59my $opt_yes = 0;
60my $opt_default = 0;
61my $opt_components;
62my $opt_exclude;
63GetOptions('strict-deps'  => \$opt_strictdeps,  # be strict about dependencies
64           yes            => \$opt_yes,         # answer yes to all questions
65           default        => \$opt_default,     # answer default to all questions
66           'components=s' => \$opt_components,
67           'exclude=s'    => \$opt_exclude,
68          );
69
70our $VERSION;
71$VERSION = '0.5.70';
72
73# Fragment of Makefile text to give the directory where files should
74# be installed.  The extra '.' in the middle of the path is to avoid
75# beginning with '//', which means a network share on Cygwin.
76#
77my $location = '$(DESTDIR)/./$(PREFIX)';
78
79our %extra_constants;
80%extra_constants
81  = (INST_PLAINDOC   => 'blib/doc',
82     INSTALLPLAINDOC => "$location/share/doc/xmltv-$::VERSION",
83     INST_SHARE      => 'blib/share',
84     INSTALLSHARE    => "$location/share/xmltv",
85
86     # Manual page constants, shouldn't really be needed, but work
87     # around bugs and make sure this stuff is the same across
88     # MakeMaker versions.
89     INSTALLMAN1DIR  => "$location/man/man1",
90     INSTALLMAN3DIR  => "$location/man/man3",
91     MAN3EXT         => '3',
92
93     # Directory to install into when making Windows binary dist.
94     WINDOWS_DIST    => "xmltv-$VERSION-win32",
95     VERSION         => "$VERSION",
96    );
97
98# The following lists of dependencies and files to be installed may
99# get modified later depending on what the user chooses.
100#
101
102# Documentation files to be installed.  This is a global variable
103# because it is accessed by some code we add to MakeMaker.
104#
105our @docs;
106@docs = qw(doc/COPYING doc/QuickStart doc/README.win32 README);
107
108# Executables to be installed.
109my @exes
110  = qw(filter/tv_augment_tz
111       filter/tv_extractinfo_en
112       filter/tv_extractinfo_ar
113       filter/tv_grep
114       filter/tv_sort
115       filter/tv_to_latex
116       filter/tv_to_text
117       filter/tv_to_potatoe
118       filter/tv_cat
119       filter/tv_split
120       filter/tv_imdb
121       filter/tv_remove_some_overlapping
122       filter/tv_count
123       filter/tv_merge
124       filter/tv_augment
125       tools/tv_validate_grabber
126       tools/tv_validate_file
127       tools/tv_find_grabbers
128      );
129
130# Libraries to be installed.
131my %pm
132  = ('lib/XMLTV.pm'               => '$(INST_LIBDIR)/XMLTV.pm',
133     'lib/TZ.pm'                  => '$(INST_LIBDIR)/XMLTV/TZ.pm',
134     'lib/Clumps.pm'              => '$(INST_LIBDIR)/XMLTV/Clumps.pm',
135     'lib/Usage.pm'               => '$(INST_LIBDIR)/XMLTV/Usage.pm',
136     'lib/Date.pm'                => '$(INST_LIBDIR)/XMLTV/Date.pm',
137     'lib/Version.pm'             => '$(INST_LIBDIR)/XMLTV/Version.pm',
138     'lib/Ask.pm'                 => '$(INST_LIBDIR)/XMLTV/Ask.pm',
139     'lib/Ask/Tk.pm'              => '$(INST_LIBDIR)/XMLTV/Ask/Tk.pm',
140     'lib/Ask/Term.pm'            => '$(INST_LIBDIR)/XMLTV/Ask/Term.pm',
141     'lib/GUI.pm'                 => '$(INST_LIBDIR)/XMLTV/GUI.pm',
142     'lib/ProgressBar.pm'         => '$(INST_LIBDIR)/XMLTV/ProgressBar.pm',
143     'lib/ProgressBar/None.pm'    => '$(INST_LIBDIR)/XMLTV/ProgressBar/None.pm',
144     'lib/ProgressBar/Term.pm'    => '$(INST_LIBDIR)/XMLTV/ProgressBar/Term.pm',
145     'lib/ProgressBar/Tk.pm'      => '$(INST_LIBDIR)/XMLTV/ProgressBar/Tk.pm',
146     'lib/Summarize.pm'           => '$(INST_LIBDIR)/XMLTV/Summarize.pm',
147     'lib/Supplement.pm'          => '$(INST_LIBDIR)/XMLTV/Supplement.pm',
148     'lib/IMDB.pm'                => '$(INST_LIBDIR)/XMLTV/IMDB.pm',
149     'lib/Gunzip.pm'              => '$(INST_LIBDIR)/XMLTV/Gunzip.pm',
150     'lib/Capabilities.pm'        => '$(INST_LIBDIR)/XMLTV/Capabilities.pm',
151     'lib/Description.pm'         => '$(INST_LIBDIR)/XMLTV/Description.pm',
152     'lib/Configure.pm'           => '$(INST_LIBDIR)/XMLTV/Configure.pm',
153     'lib/Configure/Writer.pm'    => '$(INST_LIBDIR)/XMLTV/Configure/Writer.pm',
154     'lib/Options.pm'             => '$(INST_LIBDIR)/XMLTV/Options.pm',
155     'lib/ValidateFile.pm'        => '$(INST_LIBDIR)/XMLTV/ValidateFile.pm',
156     'lib/ValidateGrabber.pm'     => '$(INST_LIBDIR)/XMLTV/ValidateGrabber.pm',
157     'lib/PreferredMethod.pm'     => '$(INST_LIBDIR)/XMLTV/PreferredMethod.pm',
158     'lib/Data/Recursive/Encode.pm' => '$(INST_LIBDIR)/XMLTV/Data/Recursive/Encode.pm',
159     'lib/Augment.pm'             => '$(INST_LIBDIR)/XMLTV/Augment.pm',
160     'filter/Grep.pm'             => '$(INST_LIBDIR)/XMLTV/Grep.pm',
161     'grab/Memoize.pm'            => '$(INST_LIBDIR)/XMLTV/Memoize.pm',
162     'grab/Grab_XML.pm'           => '$(INST_LIBDIR)/XMLTV/Grab_XML.pm',
163     'grab/DST.pm'                => '$(INST_LIBDIR)/XMLTV/DST.pm',
164     'grab/Config_file.pm'        => '$(INST_LIBDIR)/XMLTV/Config_file.pm',
165     'grab/Get_nice.pm'           => '$(INST_LIBDIR)/XMLTV/Get_nice.pm',
166     'grab/Mode.pm'               => '$(INST_LIBDIR)/XMLTV/Mode.pm',
167    );
168
169# Modules required to install.
170my %prereqs
171  = (
172     'LWP'         => 5.65,
173     # XML::Parser is required by XML::Twig, but older versions have a
174     # bug when $SIG{__DIE__} is set (well, could be a perl bug, but
175     # anyway it doesn't appear with 2.34).
176     #
177     'XML::Parser' => 2.34,
178     'XML::Twig'   => 3.10,
179     'Date::Manip' => 5.42,
180     'XML::Writer' => 0.600,
181     'Memoize'     => 0,
182     'Storable'    => 2.04,
183     'File::Slurp' => 0,
184     'XML::TreePP' => 0,
185    );
186
187# Files which are run to generate source code.
188my %pl_files = ('filter/tv_grep.PL' => 'filter/tv_grep',
189                'tools/tv_validate_file.PL' => 'tools/tv_validate_file',
190                'tools/tv_validate_grabber.PL' => 'tools/tv_validate_grabber',
191                'lib/XMLTV.pm.PL' => 'lib/XMLTV.pm',
192                'lib/Supplement.pm.PL' => 'lib/Supplement.pm',
193               );
194
195# Some tools which are generated from .PL files need the share/
196# directory passed as an extra argument.
197#
198my @need_share = ('tools/tv_validate_file',
199                  'tools/tv_validate_grabber',
200                  'lib/Supplement.pm',
201                 );
202
203# Files to be installed in the system-wide share/ directory.
204my %share_files = ('xmltv.dtd' => 'xmltv.dtd',
205                   'xmltv-lineups.xsd' => 'xmltv-lineups.xsd',
206                   'filter/augment/augment.conf' => 'tv_augment/augment.conf',
207                   'filter/augment/augment.rules' => 'tv_augment/augment.rules',
208                  );
209
210# Files which 'make clean' should remove, but doesn't by default, so
211# we have to patch it.
212#
213my @to_clean = ('filter/tv_grep',
214                'tools/tv_validate_file',
215                'tools/tv_validate_grabber',
216                'lib/XMLTV.pm',
217                'lib/Supplement.pm',
218               );
219
220# Extra dependencies to add to the Makefile.
221my @deps = ('filter/tv_grep' => [ qw(filter/tv_grep.in pm_to_blib) ],
222            'tools/tv_validate_file' => [ qw(tools/tv_validate_file.in) ],
223            'tools/tv_validate_grabber' => [ qw(tools/tv_validate_grabber.in) ],
224            'lib/XMLTV.pm' => [ 'lib/XMLTV.pm.in' ],
225            'lib/Supplement.pm' => [ 'lib/Supplement.pm.in' ],
226           );
227
228# Some grabbers which are generated from .PL files need the share/
229# directory passed as an extra argument.
230#
231my @grab_need_share;
232
233# 'Recommended but not required'.  It isn't currently handled to have
234# the same module in both sets.
235#
236my %recommended
237  = (
238     'Lingua::EN::Numbers::Ordinate' => 0,
239     'Lingua::Preferred' => '0.2.4',
240     'Term::ProgressBar' => 2.03,
241     'Compress::Zlib' => 0,
242     'Unicode::String' => 0,
243    );
244
245# And Log::TraceMessages is 'suggested' but we don't warn about that.
246
247if ($opt_yes) {
248    *ask = sub { print "$_[0] yes\n"; 1 };
249}
250elsif ($opt_default) {
251    *ask = sub { print "$_[0] $_[2]\n"; $_[2] };
252}
253else {
254    require './lib/Ask/Term.pm';
255    *ask = \&XMLTV::Ask::Term::ask_boolean;
256}
257
258# Weird shit happens when you change things like PREFIX without
259# rebuilding everything explicitly.
260#
261if (-e 'Makefile') {
262    warn <<END
263There is already a Makefile.  To avoid weird problems it is
264recommended you run 'make distclean' to clear out old built files
265before generating a new Makefile.
266
267END
268;
269    if (! ask(0, "Do you wish to continue anyway?", 0)) {
270        exit(1);
271    }
272}
273
274# Now prompt about how much to install.  This is really only to
275# reduce dependencies.  In the long run things like tv_check can be
276# spun off into separate projects.
277#
278my @opt_components
279  = (
280
281     { name => 'tv_grab_ar',
282       blurb => 'Grabber for Argentina',
283       exes => [ 'grab/ar/tv_grab_ar' ],
284       prereqs => { 'HTML::TreeBuilder' => 0,
285                    'HTML::Entities'    => 0,
286                    'HTTP::Cookies'     => 0,
287                    'LWP::UserAgent'    => 0,
288                    'JSON::PP'          => 0,
289                    'Date::Language'    => 0, },
290     },
291
292# Disabled 2006-04-12 since it doesn't provide data anymore.
293#     { name     => 'tv_grab_au',
294#       blurb    => 'Grabber for Australia',
295#       exes     => [ 'grab/au/tv_grab_au' ],
296#       deps     => [ 'grab/au/tv_grab_au' => [ 'grab/au/tv_grab_au.in' ] ],
297#       pl_files => { 'grab/au/tv_grab_au.PL' => 'grab/au/tv_grab_au' },
298#       to_clean => [ 'grab/au/tv_grab_au' ],
299#       share_files => { 'grab/au/channel_ids' => 'tv_grab_au/channel_ids' },
300#       grab_need_share => [ 'au' ],
301#     },
302
303# 2008-11-09 disabled after ident string was blocked
304#     { name => 'tv_grab_be',
305#       blurb => 'Grabber for Belgium and Luxemburg',
306#       exes => [ 'grab/be/tv_grab_be' ],
307#       pl_files => { 'grab/be/tv_grab_be.PL' => 'grab/be/tv_grab_be' },
308#       share_files => { 'grab/be/channel_ids_fr' => 'tv_grab_be/channel_ids_fr',
309#                        'grab/be/channel_ids_nl' => 'tv_grab_be/channel_ids_nl' },
310#       to_clean => [ 'grab/be/tv_grab_be' ],
311#       deps => [ 'grab/be/tv_grab_be' => [ 'grab/be/tv_grab_be.in' ] ],
312#       grab_need_share => [ 'be' ],
313#       prereqs => { 'HTML::Entities' => 1.27 } ,
314#     },
315
316#     { name => 'tv_grab_br',
317#       blurb => 'Grabber for Brazil',
318#       exes => [ 'grab/br/tv_grab_br' ],
319#       prereqs => { 'HTML::TreeBuilder' => 0 ,
320#                    'HTML::Entities' => 1.27 , }
321#     },
322
323# disabled because it broke due to site changes 2009-03-08
324#     { name => 'tv_grab_br_net',
325#       blurb => 'Grabber for Brazil NET Cable',
326#       exes => [ 'grab/br_net/tv_grab_br_net' ],
327#       prereqs => { 'WWW::Mechanize' => 1.16 ,
328#                   'HTTP::Cookies' => 1.39 , }
329#     },
330
331# disabled since blocked by www.fernsehen.ch guys
332#     { name     => 'tv_grab_ch',
333#       blurb    => 'Grabber for Switzerland',
334#       exes     => [ 'grab/ch/tv_grab_ch' ],
335#       deps     => [ 'grab/ch/tv_grab_ch' => [ 'grab/ch/tv_grab_ch.in' ] ],
336#       pl_files => { 'grab/ch/tv_grab_ch.PL' => 'grab/ch/tv_grab_ch' },
337#       to_clean => [ 'grab/ch/tv_grab_ch' ],
338#       share_files => { 'grab/ch/channel_ids' => 'tv_grab_ch/channel_ids' },
339#       grab_need_share => [ 'ch' ],
340#     },
341
342# disabled after ident string was blocked
343#     { name     => 'tv_grab_ch_bluewin',
344#       blurb    => 'Grabber for Switzerland',
345#       exes     => [ 'grab/ch_bluewin/tv_grab_ch_bluewin' ],
346#       deps     => [ 'grab/ch_bluewin/tv_grab_ch_bluewin'
347#                               => [ 'grab/ch_bluewin/tv_grab_ch_bluewin.in' ] ],
348#       pl_files => { 'grab/ch_bluewin/tv_grab_ch_bluewin.PL'
349#                               => 'grab/ch_bluewin/tv_grab_ch_bluewin' },
350#       to_clean => [ 'grab/ch_bluewin/tv_grab_ch_bluewin' ],
351#       share_files => { 'grab/ch_bluewin/channel_ids'
352#                               => 'tv_grab_ch_bluewin/channel_ids' },
353#       grab_need_share => [ 'ch_bluewin' ],
354#       prereqs => { 'HTML::Entities' => 1.27,
355#                   'HTML::TreeBuilder' => 0 },
356#     },
357
358     { name     => 'tv_grab_ch_search',
359           blurb    => 'Grabber for Switzerland',
360           exes     => [ 'grab/ch_search/tv_grab_ch_search' ],
361           deps     => [ 'grab/ch_search/tv_grab_ch_search'
362                        => [ 'grab/ch_search/tv_grab_ch_search.in' ] ],
363           pl_files => { 'grab/ch_search/tv_grab_ch_search.PL'
364                            => 'grab/ch_search/tv_grab_ch_search' },
365           to_clean => [ 'grab/ch_search/tv_grab_ch_search' ],
366#          share_files => { 'grab/ch_search/channel_ids'
367#                   => 'tv_grab_ch_search/channel_ids' },
368           grab_need_share => [ 'ch_search' ],
369           prereqs  => { 'HTML::Entities'    => 1.27,
370                         'HTML::TreeBuilder' => 0, },
371     },
372
373# 2008-07-22 - removed by rmeden, website changes, won't be fixed
374#     { name => 'tv_grab_dk',
375#       blurb => 'Grabber for Denmark',
376#       exes => [ 'grab/dk/tv_grab_dk' ],
377#       prereqs => { 'HTML::TreeBuilder' => 0 } },
378
379     { name => 'tv_grab_dk_dr',
380       blurb => 'Grabber for Denmark (dr.dk)',
381       exes => [ 'grab/dk_dr/tv_grab_dk_dr' ],
382       prereqs => { 'Data::Dumper'   => 0,
383                    'LWP::UserAgent' => 0,
384                    'IO::Scalar'     => 0,
385                    'DateTime'       => 0,
386                    'JSON'           => 0, },
387     },
388
389     { name => 'tv_grab_dtv_la',
390       blurb => 'Grabber for Latin America & Caribbean',
391       exes => [ 'grab/dtv_la/tv_grab_dtv_la' ],
392       prereqs => { 'LWP::UserAgent'    => 0,
393                    'HTTP::Cookies'     => 0,
394                    'HTML::TreeBuilder' => 0,
395                    'JSON::PP'          => 0,
396                    'Date::Manip'       => 0,
397                    'Date::Parse'       => 0,
398                    'Date::Language'    => 0, },
399     },
400
401# rmeden 2012-06-11 broken since 2011-11-25
402#     { name => 'tv_grab_ee',
403#       blurb => 'Grabber for Estonia',
404#       exes => [ 'grab/ee/tv_grab_ee' ],
405#       prereqs => { 'LWP::Simple' => 0 } },
406
407# 2009-03-08 removed due to breakage after source site changes
408#     { name => 'tv_grab_es',
409#       blurb => 'Grabber for Spain - Analogic Terrestrial/Cable',
410#       exes => [ 'grab/es/tv_grab_es' ],
411#       prereqs => { 'HTML::TreeBuilder' => 0 } },
412
413# tv_grab_es_digital no longer functioning due to site changes
414#     { name => 'tv_grab_es_digital',
415#       blurb => 'Grabber for Spain - Digital Satellite (D+)',
416#       exes => [ 'grab/es_digital/tv_grab_es_digital' ],
417#       prereqs => { 'HTML::TreeBuilder' => 0 } },
418
419     { name => 'tv_grab_es_laguiatv',
420       blurb => 'Alternative grabber for Spain',
421       exes => [ 'grab/es_laguiatv/tv_grab_es_laguiatv' ],
422       prereqs => { 'HTML::TreeBuilder' => 0, },
423     },
424
425# rmeden 2012-06-11 broken since 2012-03-29
426#     { name => 'tv_grab_es_miguiatv',
427#       blurb => 'Alternative grabber for Spain using miguiatv.com',
428#       exes => [ 'grab/es_miguiatv/tv_grab_es_miguiatv' ],
429#       prereqs => { 'HTML::TreeBuilder' => 0 } },#
430
431     { name => 'tv_grab_eu_dotmedia',
432       blurb => 'Grabber for Europe (xmltv.se / dotmedia)',
433       exes => [ 'grab/eu_dotmedia/tv_grab_eu_dotmedia' ],
434       prereqs => { 'XML::LibXML'    => 0,
435                    'Compress::Zlib' => 0,
436                    'IO::Stringy'    => 0,
437                    'HTTP::Cache::Transparent' => 0, },
438     },
439
440     # knowledgejunkie 2017-09-30 disbled as no longer providing data
441     # { name => 'tv_grab_eu_egon',
442     #   blurb => 'Grabber for german speaking area (Egon zappt)',
443     #   exes => [ 'grab/eu_egon/tv_grab_eu_egon' ],
444     #   pl_files => { 'grab/eu_egon/tv_grab_eu_egon.PL'
445     #                 => 'grab/eu_egon/tv_grab_eu_egon' },
446     #   to_clean => [ 'grab/eu_egon/tv_grab_eu_egon' ],
447     #   deps => [ 'grab/eu_egon/tv_grab_eu_egon'
448     #             => [ 'grab/se_swedb/tv_grab_se_swedb.in' ] ],
449     #   prereqs => { 'XML::LibXML'    => 0,
450     #                'Compress::Zlib' => 0,
451     #                'IO::Stringy'    => 0,
452     #                'HTTP::Cache::Transparent' => 0, },
453     # },
454
455     { name => 'tv_grab_eu_epgdata',
456       blurb => '$$ Grabber for some European countries (epgdata.com)',
457       exes => [ 'grab/eu_epgdata/tv_grab_eu_epgdata' ],
458       share_files => { 'grab/eu_epgdata/channel_ids' => 'tv_grab_eu_epgdata/channel_ids' },
459       prereqs => { 'XML::Twig'      => 0,
460                    'Archive::Zip'   => 0,
461                    'HTML::Entities' => 0,
462                    'LWP::Simple'    => 0,
463                    'File::Temp'     => 0,
464                    'DateTime::Format::Strptime' => 0, },
465     },
466
467     { name     => 'tv_grab_fi',
468       blurb    => 'Grabber for Finland',
469       exes     => [ 'grab/fi/tv_grab_fi' ],
470       deps     => [
471                     'grab/fi/tv_grab_fi' => [
472                         'grab/fi/tv_grab_fi.pl',
473                         'grab/fi/fi/common.pm',
474                         'grab/fi/fi/day.pm',
475                         'grab/fi/fi/programme.pm',
476                         'grab/fi/fi/programmeStartOnly.pm',
477                         'grab/fi/fi/source/foxtv.pm',
478                         'grab/fi/fi/source/iltapulu.pm',
479                         'grab/fi/fi/source/telkku.pm',
480                         'grab/fi/fi/source/yle.pm',
481                     ],
482                   ],
483       pl_files => { 'grab/fi/merge.PL' => 'grab/fi/tv_grab_fi' },
484       to_clean => [ 'grab/fi/tv_grab_fi' ],
485       prereqs  => { 'Encode'               => 0,
486                     'HTML::TreeBuilder'    => 0,
487                     'JSON'                 => 0,
488		     'LWP::Protocol::https' => 0,
489                     'Time::Local'          => 0, },
490     },
491
492     { name     => 'tv_grab_fi_sv',
493       blurb    => 'Grabber for Finland (Swedish)',
494       exes     => [ 'grab/fi_sv/tv_grab_fi_sv' ],
495       prereqs  => { 'DateTime'   => 0,
496                     'IO::Scalar' => 0,
497                     'HTML::TreeBuilder' => 0, },
498     },
499
500     { name => 'tv_grab_fr',
501       blurb => 'Grabber for France',
502       exes => [ 'grab/fr/tv_grab_fr' ],
503       prereqs => { 'DateTime'           => 0,
504                    'DateTime::Duration' => 0,
505                    'DateTime::TimeZone' => 0,
506                    'HTML::Entities'     => 1.27,
507                    'HTML::TreeBuilder'  => 0, },
508     },
509
510     { name => 'tv_grab_fr_kazer',
511       blurb => 'Grabber for France (Kazer)',
512       exes => [ 'grab/fr_kazer/tv_grab_fr_kazer' ],
513       prereqs => { 'IO::Uncompress::Unzip' => 0, },
514     },
515
516#
517# 2017-01-22 - source site (www.gonix.net/ gone.
518#
519#     { name => 'tv_grab_hr',
520#       blurb => 'Grabber for Croatia',
521#       exes => [ 'grab/hr/tv_grab_hr' ],
522#       pl_files => { 'grab/hr/tv_grab_hr.PL'
523#                     => 'grab/hr/tv_grab_hr' },
524#       to_clean => [ 'grab/hr/tv_grab_hr' ],
525#       deps => [ 'grab/hr/tv_grab_hr'
526#                 => [ 'grab/se_swedb/tv_grab_se_swedb.in' ] ],
527#       prereqs => { 'XML::LibXML'    => 0,
528#                    'Compress::Zlib' => 0,
529#                    'IO::Stringy'    => 0,
530#                    'HTTP::Cache::Transparent' => 0, },
531#     },
532
533     { name => 'tv_grab_huro',
534       blurb => 'Grabber for Slovakia (Hungary, Romania broken)',
535       exes => [ 'grab/huro/tv_grab_huro' ],
536       pl_files => { 'grab/huro/tv_grab_huro.PL'
537                     => 'grab/huro/tv_grab_huro' },
538       share_files => { 'grab/huro/jobmap' => 'tv_grab_huro/jobmap',
539                        'grab/huro/catmap.hu' => 'tv_grab_huro/catmap.hu',
540                        'grab/huro/catmap.ro' => 'tv_grab_huro/catmap.ro',
541                        'grab/huro/catmap.sk' => 'tv_grab_huro/catmap.sk',
542                        'grab/huro/catmap.cz' => 'tv_grab_huro/catmap.cz',
543                     },
544       to_clean => [ 'grab/huro/tv_grab_huro' ],
545       deps => [ 'grab/huro/tv_grab_huro'
546                 => [ 'grab/huro/tv_grab_huro.in' ] ],
547       grab_need_share => [ 'huro' ],
548       prereqs => { 'HTML::TreeBuilder' => 0,
549                    'JSON' => 0, },
550     },
551
552     { name => 'tv_grab_il',
553       blurb => 'Grabber for Israel',
554       exes => [ 'grab/il/tv_grab_il' ],
555     },
556
557# 2013-05-13 xmltv feed disabled by What's On due to TOS violations. It may come back
558#     { name     => 'tv_grab_in',
559#       blurb => 'Grabber for India (zipazap.com)',
560#       exes     => [ 'grab/in/tv_grab_in' ],
561#       prereqs => { 'XML::LibXML' => 0,
562#                    'IO::Scalar' => 0,
563#                  },
564#     },
565
566     { name => 'tv_grab_is',
567       blurb => 'Grabber for Iceland',
568       exes => [ 'grab/is/tv_grab_is' ],
569       share_files => { 'grab/is/category_map' => 'tv_grab_is/category_map' },
570       prereqs => { 'XML::LibXSLT' => 0,
571                    'XML::DOM'     => 0, },
572     },
573
574     { name => 'tv_grab_it',
575       blurb => 'Grabber for Italy',
576       exes => [ 'grab/it/tv_grab_it' ],
577       pl_files => { 'grab/it/tv_grab_it.PL' => 'grab/it/tv_grab_it' },
578       share_files => { 'grab/it/channel_ids' => 'tv_grab_it/channel_ids' },
579       to_clean => [ 'grab/it/tv_grab_it', 'grab/it/tv_grab_it.in2' ],
580       deps => [ 'grab/it/tv_grab_it' => [ 'grab/it/tv_grab_it.in' ] ],
581       grab_need_share => [ 'it' ],
582     },
583
584     { name => 'tv_grab_it_dvb',
585       blurb => 'Grabber for Italy from DVB-S stream',
586       exes => [ 'grab/it_dvb/tv_grab_it_dvb' ],
587       pl_files => { 'grab/it_dvb/tv_grab_it_dvb.PL' => 'grab/it_dvb/tv_grab_it_dvb' },
588       share_files => { 'grab/it_dvb/channel_ids' => 'tv_grab_it_dvb/channel_ids',
589                        'grab/it_dvb/sky_it.dict' => 'tv_grab_it_dvb/sky_it.dict',
590                        'grab/it_dvb/sky_it.themes' => 'tv_grab_it_dvb/sky_it.themes',
591                   },
592       to_clean => [ 'grab/it_dvb/tv_grab_it_dvb' ],
593       deps => [ 'grab/it_dvb/tv_grab_it_dvb' => [ 'grab/it_dvb/tv_grab_it_dvb.in' ] ],
594       grab_need_share => [ 'it_dvb' ],
595       prereqs => { 'Data::Dump'  => 0,
596                    'IO::Select'  => 0,
597                    'Linux::DVB'  => 0,
598                    'Time::HiRes' => 0, } ,
599     },
600
601# 2009-03-05 rmeden - source site seems to be blocking our agent string
602#     { name => 'tv_grab_jp',
603#       blurb => 'Grabber for Japan',
604#       exes => [ 'grab/jp/tv_grab_jp' ],
605#       prereqs => { 'HTML::TreeBuilder' => 0,
606#                    'Text::Kakasi' => 0 },
607#     },
608
609     { name     => 'tv_grab_na_dd',
610       blurb => '$$ Grabber for North America-schedulesdirect.org',
611       exes     => [ 'grab/na_dd/tv_grab_na_dd' ],
612       pl_files => { 'grab/na_dd/tv_grab_na_dd.PL' => 'grab/na_dd/tv_grab_na_dd' },
613       deps     => [ 'grab/na_dd/tv_grab_na_dd' => [ 'grab/na_dd/tv_grab_na_dd.in' ] ],
614       to_clean => [ 'grab/na_dd/tv_grab_na_dd' ],
615       prereqs => { 'SOAP::Lite'    => 0.67,
616                    'Term::ReadKey' => 0,
617                    'XML::Twig'     => 3.28, },
618       grab_need_share => [ 'na_dd' ],
619     },
620
621     { name => 'tv_grab_na_dtv',
622       blurb => 'Grabber for North America (DirecTV)',
623       exes => [ 'grab/na_dtv/tv_grab_na_dtv' ],
624       prereqs => {
625         'DateTime'         => 0,
626         'HTTP::Cookies'    => 0,
627         'LWP::UserAgent'   => 0,
628         'JSON::PP'         => 0,
629         'HTML::TokeParser' => 0, },
630     },
631
632# 2013-02-02 disabled after site changes broke grabber (no <IMG> tags!)
633#     { name     => 'tv_grab_na_icons',
634#       blurb    => 'Grabber for North American Channel Icons',
635#       exes     => [ 'grab/na_icons/tv_grab_na_icons' ],
636#       deps     => [ 'grab/na_icons/tv_grab_na_icons' => [ 'grab/na_icons/tv_grab_na_icons.in' ] ],
637#       pl_files => { 'grab/na_icons/tv_grab_na_icons.PL' => 'grab/na_icons/tv_grab_na_icons' },
638#       to_clean => [ 'grab/na_icons/tv_grab_na_icons' ],
639#       prereqs  => { 'HTML::TreeBuilder' => 0,
640#                     'XML::Twig'         => 3.28,
641#                     'WWW::Mechanize' => 1.02 },
642#       grab_need_share => [ 'na_icons' ],
643#     },
644
645     { name => 'tv_grab_na_tvmedia',
646       blurb => 'Grabber for North America (TVMedia)',
647       exes => [ 'grab/na_tvmedia/tv_grab_na_tvmedia' ],
648       prereqs => { 'XML::LibXML' => 0,
649                    'LWP::Simple' => 0, },
650     },
651
652# rmeden 2008-02-13 disabled after site changes broke grabber
653#    { name => 'tv_grab_nc',
654#      blurb => 'Grabber for Nouvelle Caledonie Island (France)',
655#      exes => [ 'grab/nc/tv_grab_nc' ],
656#      prereqs => { 'HTML::TreeBuilder' => 0 ,
657#                   'HTML::Entities'    => 0 } },
658
659     { name => 'tv_grab_nl',
660       blurb => 'Grabber for the Netherlands',
661       exes => [ 'grab/nl/tv_grab_nl' ],
662       prereqs => { 'HTTP::Cache::Transparent' => 0,
663                    'HTML::TreeBuilder' => 0,
664                    'HTML::Entities'    => 0,
665                    'IO::File'     => 0,
666                    'Date::Parse'  => 0,
667                    'Date::Format' => 0,
668                    'DateTime'     => 0,
669                    'Encode'       => 0,
670                    'JSON::PP'     => 0, },
671     },
672
673# 2008-07-12 - removed by rmeden, not getting programs for a while.
674#     { name => 'tv_grab_nl_wolf',
675#       blurb => 'Alternative grabber for the Netherlands',
676#       exes => [ 'grab/nl_wolf/tv_grab_nl_wolf' ],
677#       prereqs => { 'HTML::TreeBuilder' => 0 } },
678
679# depreciate tv_grab_no as it is no longer supported (per author, cwattengard@gmail.com)
680# has been replaced by tv_grab_no_gfeed
681#
682#     { name => 'tv_grab_no',
683#       blurb => 'Grabber for Norway',
684#       exes => [ 'grab/no/tv_grab_no' ],
685#       prereqs => { 'HTML::Entities' => 1.27 },
686#     },
687
688# 2016-03-16 - disabled by knowledgejunkie, not well for many months
689#              and data.epg.no server no longer online
690#     { name => 'tv_grab_no_gfeed',
691#       blurb => 'Grabber for Norway (gfeed.info)',
692#       exes => [ 'grab/no_gfeed/tv_grab_no_gfeed' ],
693#       pl_files => { 'grab/no_gfeed/tv_grab_no_gfeed.PL'
694#                     => 'grab/no_gfeed/tv_grab_no_gfeed' },
695#       to_clean => [ 'grab/no_gfeed/tv_grab_no_gfeed' ],
696#       deps => [ 'grab/no_gfeed/tv_grab_no_gfeed'
697#                 => [ 'grab/se_swedb/tv_grab_se_swedb.in' ] ],
698#       prereqs => { 'XML::LibXML'    => 0,
699#                    'Compress::Zlib' => 0,
700#                    'IO::Stringy'    => 0,
701#                    'HTTP::Cache::Transparent' => 0, },
702#     },
703
704#
705# 2017-01-22 Broken due to site changes
706#
707#     { name => 'tv_grab_pt',
708#       blurb => 'Grabber for Portugal',
709#       exes => [ 'grab/pt/tv_grab_pt' ],
710#       prereqs => { 'Getopt::Long'      => 0,
711#                    'HTML::TreeBuilder' => 0,
712#                    'HTML::Entities'    => 1.27,
713#                    'HTTP::Cache::Transparent' => 0,
714#                    'DateTime'          => 0,
715#                    'Encode'            => 0,
716#                    'JSON::PP'          => 0, },
717#     },
718
719     { name => 'tv_grab_pt_meo',
720       blurb => 'Grabber for Portugal (MEO)',
721       exes => [ 'grab/pt_meo/tv_grab_pt_meo' ],
722       prereqs => { 'DateTime'    => 0,
723                    'XML::LibXML' => 0,
724                    'LWP'         => 0, },
725     },
726
727# rmeden 2011-06-12 not working
728#    { name => 'tv_grab_re',
729#       blurb => 'Grabber for Reunion Island (France)',
730#       exes => [ 'grab/re/tv_grab_re' ],
731#       prereqs => { 'HTML::TreeBuilder' => 0 ,
732#                    'HTML::Entities'    => 0 } },
733
734     { name => 'tv_grab_se_swedb',
735       blurb => 'Grabber for Sweden',
736       exes => [ 'grab/se_swedb/tv_grab_se_swedb' ],
737       pl_files => { 'grab/se_swedb/tv_grab_se_swedb.PL'
738                     => 'grab/se_swedb/tv_grab_se_swedb' },
739       to_clean => [ 'grab/se_swedb/tv_grab_se_swedb' ],
740       deps => [ 'grab/se_swedb/tv_grab_se_swedb'
741                 => [ 'grab/se_swedb/tv_grab_se_swedb.in' ] ],
742       prereqs => { 'XML::LibXML'    => 0,
743                    'Compress::Zlib' => 0,
744                    'IO::Stringy'    => 0,
745                    'HTTP::Cache::Transparent' => 0, },
746     },
747
748     { name => 'tv_grab_se_tvzon',
749       blurb => 'Grabber for Sweden (tvzon.se)',
750       exes => [ 'grab/se_tvzon/tv_grab_se_tvzon' ],
751       pl_files => { 'grab/se_tvzon/tv_grab_se_tvzon.PL'
752                     => 'grab/se_tvzon/tv_grab_se_tvzon' },
753       to_clean => [ 'grab/se_tvzon/tv_grab_se_tvzon' ],
754       deps => [ 'grab/se_tvzon/tv_grab_se_tvzon'
755                 => [ 'grab/se_swedb/tv_grab_se_swedb.in' ] ],
756       prereqs => { 'XML::LibXML'    => 0,
757                    'Compress::Zlib' => 0,
758                    'IO::Stringy'    => 0,
759                    'HTTP::Cache::Transparent' => 0, },
760     },
761
762     { name => 'tv_grab_tr',
763       blurb => 'Grabber for Turkey (Digiturk)',
764       exes => [ 'grab/tr/tv_grab_tr' ],
765       prereqs => { 'HTTP::Cache::Transparent' => 1.0,
766                    'LWP::UserAgent' => 0,
767                    'HTTP::Cookies'  => 0,
768                    'DateTime'       => 0,
769                    'Date::Parse'    => 0,
770                    'Encode'         => 0,
771                    'URI::Escape'    => 0,
772                    'File::Path'     => 0, },
773     },
774
775#
776# 2017-01-22  tv_grab_uk_atlas removed due to site rule changes
777#
778#     { name => 'tv_grab_uk_atlas',
779#       blurb => 'Fast grabber for UK and Ireland using Atlas database',
780#       exes => [ 'grab/uk_atlas/tv_grab_uk_atlas' ],
781#       share_files => {
782#            'grab/uk_atlas/tv_grab_uk_atlas.user.map.conf'
783#                => 'tv_grab_uk_atlas/tv_grab_uk_atlas.user.map.conf',
784#            'grab/uk_atlas/tv_grab_uk_atlas.pa.genres.conf'
785#                => 'tv_grab_uk_atlas/tv_grab_uk_atlas.pa.genres.conf',
786#            'grab/uk_atlas/tv_grab_uk_atlas.map.genres.conf'
787#                => 'tv_grab_uk_atlas/tv_grab_uk_atlas.map.genres.conf',
788#            'grab/uk_atlas/tv_grab_uk_atlas.map.channels.conf'
789#                => 'tv_grab_uk_atlas/tv_grab_uk_atlas.map.channels.conf',
790#            'grab/uk_atlas/cgi-bin/getatlas.pl'
791#                => 'tv_grab_uk_atlas/cgi-bin/getatlas.pl', },
792#       prereqs => { 'HTTP::Cache::Transparent' => 1.0,
793#                    'LWP::UserAgent'  => 0,
794#                    'HTTP::Cookies'   => 0,
795#                    'JSON::PP'        => 0,
796#                    'DateTime'        => 0,
797#                    'Date::Parse'     => 0,
798#                    'Encode'          => 0,
799#                    'URI::Escape'     => 0,
800#                    'File::Path'      => 0,
801#                    'Data::Dumper'    => 0,
802#                    'Pod::Usage'      => 0, },
803#     },
804
805     { name => 'tv_grab_uk_bleb',
806       blurb => 'Fast alternative grabber for the UK',
807       exes => [ 'grab/uk_bleb/tv_grab_uk_bleb' ],
808       pl_files => { 'grab/uk_bleb/tv_grab_uk_bleb.PL' => 'grab/uk_bleb/tv_grab_uk_bleb' },
809       share_files => { 'grab/uk_bleb/icon_urls' => 'tv_grab_uk_bleb/icon_urls' },
810       to_clean => [ 'grab/uk_bleb/tv_grab_uk_bleb' ],
811       deps => [ 'grab/uk_bleb/tv_grab_uk_bleb' => [ 'grab/uk_bleb/tv_grab_uk_bleb.in' ] ],
812       grab_need_share => [ 'uk_bleb' ],
813       prereqs => { 'IO::Stringy' => 0, 'Archive::Zip' => 0, },
814     },
815
816# [bilbo_uk] The Guardian have removed their online tv listings service @ 1/Oct/14
817#     { name => 'tv_grab_uk_guardian',
818#       blurb => 'Grabber for UK and Ireland using The Guardian website',
819#       exes => [ 'grab/uk_guardian/tv_grab_uk_guardian' ],
820#       share_files => {
821#            'grab/uk_guardian/tv_grab_uk_guardian.map.conf'
822#                => 'tv_grab_uk_guardian/tv_grab_uk_guardian.map.conf',
823#       },
824#       prereqs => { 'HTTP::Cache::Transparent' => 1.0,
825#                    'DateTime'        => 0,
826#                    'File::Path'      => 0,
827#                    'Encode'          => 0, },
828#     },
829
830# knowledgejunkie 2016-06-17 data feed turned off after nearly 12 years
831#    { name => 'tv_grab_uk_rt',
832#      blurb => 'Grabber for UK and Ireland (Radio Times)',
833#      exes => [ 'grab/uk_rt/tv_grab_uk_rt' ],
834#      share_files => {
835#           'grab/uk_rt/channel_ids'
836#               => 'tv_grab_uk_rt/channel_ids',
837#           'grab/uk_rt/prog_titles_to_process'
838#               => 'tv_grab_uk_rt/prog_titles_to_process',
839#           'grab/uk_rt/regional_channels_by_postcode'
840#               => 'tv_grab_uk_rt/regional_channels_by_postcode',
841#           'grab/uk_rt/channels_platforms'
842#               => 'tv_grab_uk_rt/channels_platforms',
843#           'grab/uk_rt/utf8_fixups'
844#               => 'tv_grab_uk_rt/utf8_fixups',
845#           'grab/uk_rt/channel_icons'
846#               => 'tv_grab_uk_rt/channel_icons',
847#
848#           'grab/uk_rt/lineups/xmltv-lineups.xsl'
849#               => 'tv_grab_uk_rt/lineups/xmltv-lineups.xsl',
850#
851#           'grab/uk_rt/lineups/lineups.xml'
852#               => 'tv_grab_uk_rt/lineups/lineups.xml',
853#
854#           'grab/uk_rt/lineups/freesatfromsky.xml'
855#               => 'tv_grab_uk_rt/lineups/freesatfromsky.xml',
856#           'grab/uk_rt/lineups/freesathd.xml'
857#               => 'tv_grab_uk_rt/lineups/freesathd.xml',
858#           'grab/uk_rt/lineups/freesat.xml'
859#               => 'tv_grab_uk_rt/lineups/freesat.xml',
860#           'grab/uk_rt/lineups/freeviewhd.xml'
861#               => 'tv_grab_uk_rt/lineups/freeviewhd.xml',
862#           'grab/uk_rt/lineups/freeview.xml'
863#               => 'tv_grab_uk_rt/lineups/freeview.xml',
864#           'grab/uk_rt/lineups/saorview.xml'
865#               => 'tv_grab_uk_rt/lineups/saorview.xml',
866#           'grab/uk_rt/lineups/skyhd.xml'
867#               => 'tv_grab_uk_rt/lineups/skyhd.xml',
868#           'grab/uk_rt/lineups/sky.xml'
869#               => 'tv_grab_uk_rt/lineups/sky.xml',
870#           'grab/uk_rt/lineups/upcirelandhd.xml'
871#               => 'tv_grab_uk_rt/lineups/upcirelandhd.xml',
872#           'grab/uk_rt/lineups/upcireland.xml'
873#               => 'tv_grab_uk_rt/lineups/upcireland.xml',
874#           'grab/uk_rt/lineups/virginhd.xml'
875#               => 'tv_grab_uk_rt/lineups/virginhd.xml',
876#           'grab/uk_rt/lineups/virgin.xml'
877#               => 'tv_grab_uk_rt/lineups/virgin.xml',
878#
879#           'grab/uk_rt/lineups/freeview.map'
880#               => 'tv_grab_uk_rt/lineups/freeview.map',
881#           'grab/uk_rt/lineups/freesat.map'
882#               => 'tv_grab_uk_rt/lineups/freesat.map',
883#           'grab/uk_rt/lineups/saorview.map'
884#               => 'tv_grab_uk_rt/lineups/saorview.map', },
885#      prereqs => { 'HTTP::Cache::Transparent' => 1.0,
886#                   'IO::Stringy'        => 0,
887#                   'LWP::UserAgent'     => 0,
888#                   'DateTime'           => 0,
889#                   'DateTime::Duration' => 0,
890#                   'DateTime::TimeZone' => 0,
891#                   'HTML::Entities'     => 1.27,
892#                   'XML::LibXML'        => 0, },
893#    },
894
895     { name => 'tv_grab_uk_tvguide',
896       blurb => 'Grabber for UK and Ireland using TV Guide website',
897       exes => [ 'grab/uk_tvguide/tv_grab_uk_tvguide' ],
898       share_files => {
899            'grab/uk_tvguide/tv_grab_uk_tvguide.map.conf'
900                => 'tv_grab_uk_tvguide/tv_grab_uk_tvguide.map.conf',
901       },
902       prereqs => { 'HTTP::Cache::Transparent' => 1.0,
903                    'HTML::TreeBuilder' => 0,
904                    'DateTime'          => 0,
905                    'Date::Parse'       => 0,
906                    'Encode'            => 0,
907                    'URI::Escape'       => 0,
908                    'File::Path'        => 0,
909                    'Data::Dumper'      => 0, },
910     },
911
912     { name => 'tv_grab_zz_sdjson',
913       blurb => '$$ Grabber for schedulesDirect.org SD-JSON service (many countries)',
914       exes => [ 'grab/zz_sdjson/tv_grab_zz_sdjson' ],
915       prereqs => { 'DateTime' => 0,
916                    'Digest::SHA' => 0,
917                    'JSON' => 0,
918                    'LWP::Protocol::https' => 0,
919                    'LWP::UserAgent' => 0,
920                    'Storable' => 0,
921                    'Try::Tiny' => 0, },
922     },
923
924     { name => 'tv_grab_zz_sdjson_sqlite',
925       blurb => '$$ Grabber for schedulesDirect.org SD-JSON service (many countries, using sqlite)',
926       exes => [ 'grab/zz_sdjson_sqlite/tv_grab_zz_sdjson_sqlite' ],
927       prereqs => { 'Encode' => 0,
928                    'JSON' => 0,
929                    'Digest::SHA' => 0,
930                    'File::Basename' => 0,
931                    'File::Which' => 0,
932                    'File::HomeDir' => 0,
933                    'File::Path' => 0,
934                    'DateTime' => 0,
935                    'DateTime::TimeZone' => 0,
936                    'DateTime::Format::ISO8601' => 0,
937                    'DateTime::Format::SQLite' => 0,
938                    'List::MoreUtils' => 0,
939                    'DBI' => 0,
940                    'DBD::SQLite' => 0,
941                    'Scalar::Util' => 0,
942                    'Data::Dumper' => 0,
943                    'LWP::UserAgent::Determined' => 0, },
944     },
945
946
947# honir 2015-03-05 hasn't worked since 2014-11-19 - source site permanently broken
948#     { name => 'tv_grab_za',
949#       blurb => 'Grabber for South Africa',
950#       exes => [ 'grab/za/tv_grab_za' ],
951#       prereqs => { 'HTML::TreeBuilder' => 0,
952#                    'HTML::Entities' => 1.27 } },
953
954     { name => 'tv_check',
955       blurb => 'Program to report exceptions and changes in a schedule',
956       exes => [ 'choose/tv_check/tv_check' ],
957       docs => [ qw(choose/tv_check/README.tv_check
958                    choose/tv_check/tv_check_doc.html
959                    choose/tv_check/tv_check_doc.jpg
960                   ) ],
961       prereqs => { 'Tk' => 0,
962                    'Tk::TableMatrix' => 0, }
963     },
964
965     { name => 'tv_grab_combiner',
966       blurb => 'Grabber that combines data from other grabbers',
967       exes => [ 'grab/combiner/tv_grab_combiner' ],
968       prereqs => { 'XML::LibXML' => 0, },
969     },
970
971     { name => 'tv_pick_cgi',
972       blurb => 'CGI program to filter listings (to install manually)',
973       prereqs => { 'CGI' => 0, },
974       type => 'run',
975     },
976
977    );
978
979# Now we need to prompt about each optional component.  The style of
980# prompting, though not the code, is based on SOAP::Lite.  I would
981# like to add '--noprompt' and '--with tv_grab_nl' options to help
982# automated package building, but I haven't implemented that yet.
983#
984
985# For each component work out whether its prereqs are installed and
986# store the result in {missing} - either false, or a hashref.
987#
988foreach my $info (@opt_components) {
989    my $name = $info->{name};
990    my %modules_missing;
991    our %module_prereqs;
992    local *module_prereqs = $info->{prereqs} || {};
993    foreach (sort keys %module_prereqs) {
994        my $ver = $module_prereqs{$_};
995        next if test_module($_, $ver)->[0] eq 'OK';
996        warn "strange, module prereq $_ mentioned twice"
997          if defined $modules_missing{$_};
998        $modules_missing{$_} = $ver;
999    }
1000
1001    our @special_prereqs;
1002    my %special_missing;
1003    local *special_prereqs = $info->{special_prereqs} || {};
1004    foreach (@special_prereqs) {
1005        my ($sub, $name, $ver, $friendly_ver) = @$_;
1006        next if test_special($sub, $ver)->[0] eq 'OK';
1007        warn "strange, special prereq $name mentioned twice"
1008          if defined $special_missing{$name};
1009        $special_missing{$name} = $friendly_ver;
1010    }
1011
1012    my %missing = (%modules_missing, %special_missing);
1013    if (not keys %missing) {
1014        $info->{missing} = 0;
1015    }
1016    else {
1017        $info->{missing} = \%missing;
1018    }
1019}
1020
1021if (not defined $opt_components) {
1022    # Generate a default configuration that installs as much as possible.
1023    print STDERR <<END
1024
1025Choose which optional components of xmltv you want to install.  The
1026XMLTV.pm library and the filter programs such as tv_grep and tv_sort
1027are installed by default; here you choose grabbers for different
1028countries and front-ends for managing listings.
1029
1030END
1031;
1032
1033    my %by_name;
1034    foreach (@opt_components) {
1035        $by_name{$_->{name}} = $_;
1036        $_->{exclude} = 0; # default if not mentioned
1037    }
1038
1039    if (defined $opt_exclude) {
1040        foreach (split /,/, $opt_exclude) {
1041            my $i = $by_name{$_};
1042            die "unknown component $_\n" if not $i;
1043            $i->{exclude} = 1;
1044        }
1045    }
1046
1047    my $width = 0;
1048    foreach my $info (@opt_components) {
1049        my $w = length("$info->{blurb} ($info->{name})");
1050        $width = $w if $w > $width;
1051    }
1052    foreach my $info (@opt_components) {
1053        my $missing = $info->{missing};
1054        my $s = "$info->{blurb} ($info->{name})";
1055
1056        # Guess a default value for {install} based on whether
1057        # prerequisites were found.
1058        #
1059        $info->{install} = 1;(not $info->{exclude}) && ($opt_yes || not $info->{missing});
1060
1061        print STDERR ($s, ' ' x (1 + $width - length $s),
1062                      $info->{install} ? '[yes]' : '[no]',
1063                      "\n");
1064    }
1065    print STDERR "\n";
1066    if (1 or not ask(0, 'Do you want to proceed with this configuration?', 1)) {
1067        # Need to set {install} for each component by prompting.
1068        foreach my $info (@opt_components) {
1069            my $missing = $info->{missing};
1070            my $name = $info->{name};
1071            print STDERR "\n* $info->{blurb} ($name)\n\n";
1072            if ($missing) {
1073                print STDERR "These dependencies are missing for $name:\n\n";
1074                foreach (sort keys %$missing) {
1075                    print STDERR "$_";
1076                    my $min_ver = $missing->{$_};
1077                    if ($min_ver) {
1078                        print STDERR " (version $min_ver or higher)";
1079                    }
1080                    print STDERR "\n";
1081                }
1082                print STDERR "\n";
1083            }
1084
1085            my $msg;
1086            my $type = $info->{type};
1087            if (not defined $type or $type eq 'install') {
1088                $msg = "Do you wish to install $name?";
1089            } elsif ($type eq 'run') {
1090                $msg = "Do you plan to run $name?";
1091            } else {
1092                die;
1093            }
1094
1095            $info->{install} = 1;
1096            #  ask(0, $msg, not $missing);
1097        }
1098    }
1099}
1100else {
1101    my @to_install = split /\s+/, $opt_components;
1102    my %by_name;
1103    foreach (@opt_components) {
1104        $by_name{$_->{name}} = $_;
1105        $_->{install} = 0; # default if not mentioned
1106    }
1107    foreach (@to_install) {
1108        my $i = $by_name{$_};
1109        die "unknown component $_\n" if not $i;
1110        $i->{install} = 1;
1111    }
1112}
1113
1114foreach my $info (@opt_components) {
1115    next if not $info->{install};
1116    push @exes, @{$info->{exes}}                  if $info->{exes};
1117    push @docs, @{$info->{docs}}                  if $info->{docs};
1118    %pm = (%pm, %{$info->{pm}})                   if $info->{pm};
1119    %prereqs = (%prereqs, %{$info->{prereqs}})    if $info->{prereqs};
1120    %pl_files = (%pl_files, %{$info->{pl_files}}) if $info->{pl_files};
1121    %share_files = (%share_files, %{$info->{share_files}})
1122      if $info->{share_files};
1123    push @to_clean, @{$info->{to_clean}}          if $info->{to_clean};
1124    push @deps, @{$info->{deps}}                  if $info->{deps};
1125    push @grab_need_share, @{$info->{grab_need_share}}
1126      if $info->{grab_need_share};
1127}
1128
1129my $warned_uninstall_broken = 1;
1130
1131
1132# Test the installed version of a module.
1133#
1134# Parameters:
1135#   Name of module
1136#   Version required, or 0 for don't care
1137#
1138# Returns a tuple of two scalars: the first scalar is one of
1139#
1140# OK            - a recent enough version is installed.
1141# NOT_INSTALLED - the module is not installed.
1142# FAILED        - the second scalar contains an error message.
1143# TOO_OLD       - the second scalar contains the version found.
1144#
1145sub test_module( $$ ) {
1146    my ($mod, $minver) = @_;
1147    die if not defined $mod; die if not defined $minver;
1148    eval "require $mod";
1149    if ($@) {
1150        # This if-test is separate to suppress spurious 'Use of
1151        # uninitialized value in numeric lt (<)' warning.
1152        #
1153        if ($@ ne '') {
1154            if ($@ =~ /^Can\'t locate \S+\.pm in \@INC/) {
1155                return [ 'NOT_INSTALLED', undef ];
1156            }
1157            else {
1158                chomp (my $msg = $@);
1159                return [ 'FAILED', $msg ];
1160            }
1161        }
1162    }
1163
1164    my $ver = $mod->VERSION;
1165    if ($minver ne '0') {
1166        return [ 'TOO_OLD', undef ] if not defined $ver;
1167        return [ 'TOO_OLD', $ver ] if $ver lt $minver;
1168    }
1169
1170    return [ 'OK', undef ];
1171}
1172
1173# Run a subroutine and check that its output has the correct version.
1174#
1175# Parameters:
1176#   code reference to run
1177#   minumum version
1178#
1179# The code ref should return undef meaning 'package not present' or
1180# else a version number.
1181#
1182# Returns as for test_module() (but 'FAILED' not an option).
1183#
1184sub test_special( $$ ) {
1185    my ($sub, $minver) = @_;
1186    my $ver = $sub->();
1187    return [ 'NOT_INSTALLED', undef ] if not defined $ver;
1188    if ($minver ne '0') {
1189        return [ 'TOO_OLD', undef ] if not defined $ver;
1190        return [ 'TOO_OLD', $ver ] if $ver lt $minver;
1191    }
1192    return [ 'OK', undef ];
1193}
1194
1195# MakeMaker's warning message can be intimidating, check ourselves
1196# first.  We warn about missing 'recommended' modules but don't abort
1197# because of them.
1198#
1199my $err = 0;
1200foreach my $p ((sort keys %prereqs), (sort keys %recommended)) {
1201    my $required = (defined $prereqs{$p});
1202    my $verbed = $required ? 'required' : 'recommended';
1203    my $Verbed = uc(substr($verbed, 0, 1)) . substr($verbed, 1);
1204    my $minver = $required ? $prereqs{$p} : $recommended{$p};
1205    die "bad minver for $p" if not defined $minver;
1206    my ($r, $more) = @{test_module($p, $minver)};
1207    if ($r eq 'OK') {
1208        # Installed and recent enough.
1209    }
1210    elsif ($r eq 'NOT_INSTALLED') {
1211        print STDERR "Module $p seems not to be installed.\n";
1212        print(($minver ? "$p $minver" : $p), " is $verbed.\n");
1213        ++ $err if $required;
1214    }
1215    elsif ($r eq 'FAILED') {
1216        print STDERR "$Verbed module $p failed to load: $more\n";
1217        print(($minver ? "$p $minver" : $p), " is $verbed.\n");
1218        ++ $err if $required;
1219    }
1220    elsif ($r eq 'TOO_OLD') {
1221        if (defined $more) {
1222            print STDERR "$p-$minver is $verbed, but $more is installed\n";
1223        }
1224        else {
1225            print STDERR "$p-$minver is $verbed, but an unknown version is installed\n";
1226        }
1227        ++ $err if $required;
1228    }
1229    else { die }
1230}
1231if ($err) {
1232    if ($opt_strictdeps) {
1233        die "Required modules missing.  Makefile will not be created.\n";
1234    }
1235    else {
1236        warn "Required modules missing, 'make' is unlikely to work\n";
1237    }
1238}
1239
1240WriteMakefile
1241  (
1242   'NAME'          => 'XMLTV',
1243   # No VERSION_FROM, it's set in this file
1244   'EXE_FILES'     => \@exes,
1245   'PL_FILES'      => \%pl_files,
1246   'PM'            => \%pm,
1247   'PREREQ_PM'     => \%prereqs,
1248    # No special parameters for 'make clean' or 'make dist'
1249  );
1250
1251
1252sub MY::constants {
1253    package MY;
1254    my $inherited = shift->SUPER::constants(@_);
1255    die if not keys %::extra_constants;
1256    foreach (sort keys %::extra_constants) {
1257        $inherited .= "$_ = $::extra_constants{$_}\n";
1258    }
1259    return $inherited;
1260}
1261sub MY::install {
1262    package MY;
1263    my $inherited = shift->SUPER::install(@_);
1264
1265    # Decided that 'plaindoc_install' should be directly under
1266    # 'install', not under the misleadingly named 'doc_install'.
1267    #
1268    my %extra_deps = (install => [ 'plaindoc_install', 'share_install' ]);
1269    foreach my $t (keys %extra_deps) {
1270        foreach my $d (@{$extra_deps{$t}}) {
1271            $inherited =~ s/^(\s*$t\s+::\s.+)/$1 $d/m or die;
1272        }
1273    }
1274
1275    foreach (qw(plaindoc share)) {
1276        my $target = $_ . '_install';
1277        my $uc = uc;
1278
1279        my $inst_var = "INST_$uc";
1280        my $extra = <<END
1281# Add code to create the directory under blib/.
1282\$($inst_var)/.exists :: \$(PERL_INC)/perl.h
1283	\@\$(MKPATH) \$($inst_var)
1284	\@\$(EQUALIZE_TIMESTAMP) \$(PERL_INC)/perl.h \$($inst_var)/.exists
1285	-\@\$(CHMOD) \$(PERM_RWX) \$($inst_var)
1286
1287# Create a target to install to the final location.
1288$target ::
1289        \@echo Installing contents of \$(INST_$uc) into \$(INSTALL$uc)
1290        \@\$(MOD_INSTALL) \\
1291                \$(INST_$uc) \$(INSTALL$uc)
1292
1293END
1294;
1295        $extra =~ s/ {8}/\t/g;
1296        $inherited .= $extra;
1297    }
1298
1299    # Remove existing non-working 'uninstall' target.
1300    $inherited =~ s!^uninstall\s::?.*?\n\t+.*$!!m;
1301    $inherited =~ s!^uninstall\s:.*$!!m;
1302
1303    # For each *_install create a corresponding _uninstall.
1304    my $targets = ::targets($inherited);
1305    foreach (qw(pure_perl_install pure_site_install plaindoc_install share_install)) {
1306        die "no $_ in: $inherited" if not defined $targets->{$_};
1307        my @t = @{$targets->{$_}}; # make a copy
1308        my $done = 0;
1309        foreach (@t) {
1310            if (s/\@\$\(MOD_INSTALL\)/\$(PERL) -I. -MUninstall -e "uninstall(\@ARGV)"/) {
1311                $done = 1;
1312                last;
1313            }
1314            s/Installing contents of (\S+) into (\S+)/Removing contents of $1 from $2/;
1315        }
1316        if (not $done) {
1317            print STDERR "couldn't find \@\$(MOD_INSTALL) in target $_, uninstall may not work\n"
1318              unless $warned_uninstall_broken;
1319        }
1320        (my $new_target = $_) =~ s/install$/uninstall/ or die;
1321        foreach ("\n\n$new_target ::\n", @t) {
1322            $inherited .= $_;
1323        }
1324    }
1325    $inherited .= 'pure_uninstall :: pure_$(INSTALLDIRS)_uninstall' . "\n";
1326    $inherited .= 'uninstall :: all pure_uninstall plaindoc_uninstall share_uninstall' . "\n";
1327
1328    # Add a target for a Windows distribution.  Note this is
1329    # singlequoted and then we substitute one variable by hand!
1330    #
1331    $inherited .= q{
1332
1333xmltv.exe :: $(EXE_FILES) lib/exe_wrap.pl lib/exe_opt.pl
1334	echo $(EXE_FILES)                             >exe_files.txt
1335	perl lib/exe_opt.pl $(VERSION)                >exe_opt.txt
1336	echo -lib  $(INST_ARCHLIB) --lib $(INST_LIB) >>exe_opt.txt
1337	echo -add  "$(EXE_FILES)"                    >>exe_opt.txt
1338	echo -bind exe_files.txt                     >>exe_opt.txt
1339	echo -exe  xmltv.exe                         >>exe_opt.txt
1340	perlapp @exe_opt.txt lib/exe_wrap.pl
1341	$(RM_F) exe_files.txt
1342	$(RM_F) exe_opt.txt
1343
1344windows_dist ::
1345	@perl -e "if (-e '$location') { print STDERR qq[To build a Windows distribution, please rerun Makefile.PL with\nPREFIX set to a new (nonexistent) directory then 'make windows_dist'.\n(Remember that only absolute paths work properly with MakeMaker.)\n]; exit 1 }"
1346	@perl -e 'print "Have you updated doc/README.win32 for this release? "; exit 1 unless <STDIN> =~ /^[yY]/'
1347	$(MAKE) install
1348	perl -MExtUtils::Command -e mv $(INSTALLPLAINDOC) $location/doc/
1349	perl -MExtUtils::Command -e rm_r $location/share/doc
1350	perl -MExtUtils::Command -e mkpath $location/doc/man
1351	# Generate plain text documentation from pod.
1352	perl -e "chdir 'blib/script' or die; foreach (<*>) { system qq'pod2text <\$$_ >$location/doc/man/\$$_.txt' }"
1353	# Remove 'real' manual pages, not needed on Windows.
1354	perl -MExtUtils::Command -e rm_rf $location/man $location/man
1355	# My MakeMaker creates this dud directory.
1356	perl -MExtUtils::Command -e rm_rf $location/5.8.0
1357	rmdir $location/share/doc
1358	# Generate Date::Manip docs by filtering perldoc output.  The
1359	# use of temp files instead of pipes is so set -e works properly.
1360	#
1361	echo Extracting part of Date::Manip manual page into $location/doc/man/date_formats.txt
1362	echo "This is an extract from the documentation of Perl's Date::Manip module," >>$location/doc/man/date_formats.txt
1363	echo "describing the different format strings that may be used for dates."     >>$location/doc/man/date_formats.txt
1364	echo "Bear in mind that depending on your Windows version you will need to"    >>$location/doc/man/date_formats.txt
1365	echo "quote the % characters on the command line somehow (see README.win32)."  >>$location/doc/man/date_formats.txt
1366	echo "" >>$location/doc/man/date_formats.txt
1367	perldoc -u Date::Manip >$location/doc/man/date_formats.txt.tmp
1368	perl -ne "BEGIN { print qq'\n=pod\n\n' } print if (/^The format options are:/ .. /^=/) and not /^=/" <$location/doc/man/date_formats.txt.tmp >$location/doc/man/date_formats.txt.tmp.1
1369	pod2text <$location/doc/man/date_formats.txt.tmp.1 >>$location/doc/man/date_formats.txt
1370	perl -MExtUtils::Command -e rm_f $location/doc/man/date_formats.txt.tmp*
1371	# Don't use $(INSTALLBIN), it seems to disregard PREFIX passed
1372	# to 'make'.
1373	#
1374	perl -MExtUtils::Command -e rm_rf $location/bin/ $location/lib/ $(INSTMANDIR) $(INSTALLMAN3DIR)
1375	perl -MExtUtils::Command -e cp xmltv.dtd $location
1376	perl -MExtUtils::Command -e cp xmltv-lineups.xsd $location
1377	perl -MExtUtils::Command -e cp ChangeLog $location/ChangeLog.txt
1378	# The following command will not be necessary when the source
1379	# tree was checked out on a DOSish system.  It may not even
1380	# work properly when run on a DOSish system - should check.
1381	#
1382	# (Simulation in perl of find | xargs; there's probably a
1383	# better way but I'm too lazy to find it.)
1384	#
1385	perl -MFile::Find -e "find(sub { print qq[\\$$File::Find::name\n] if -f and not /[.]jpg/ }, '$location')" | perl -e 'chomp(@ARGV = (@ARGV, <STDIN>)); exec @ARGV' perl -i -pe 'BEGIN { binmode STDIN } s/\r*\n*$$/\r\n/'
1386	perl -MExtUtils::Command -e mv $location/doc/README* $location
1387	perl -MExtUtils::Command -e mv $location/README.win32 $location/README.txt
1388	@echo
1389	@echo Part of a Windows distribution tree has been made in $location/.
1390	@echo Now copy in the executables!
1391
1392};
1393    $inherited =~ s/\$location/$location/g or die;
1394
1395    return $inherited;
1396}
1397
1398# Extend installbin() to put doc and share under blib/.
1399sub MY::installbin {
1400    package MY;
1401    my $inherited = shift->SUPER::installbin(@_);
1402
1403    # Add a target for each documentation file.
1404    my %doc_files;
1405    foreach (@::docs) {
1406        $doc_files{$_} = File::Basename::basename($_);
1407    }
1408
1409    my %new_filetypes = (plaindoc => \%doc_files, share => \%share_files);
1410    my %seen_dir;
1411    foreach my $filetype (sort keys %new_filetypes) {
1412        my $uc = uc $filetype;
1413        our %files; local *files = $new_filetypes{$filetype};
1414        foreach my $src (sort keys %files) {
1415            my $inst_pos = $files{$src};
1416            my $extra = '';
1417
1418            # The directory containing this file in blib/ needs to be created.
1419            my @dirs = split m!/!, $inst_pos; pop @dirs;
1420            foreach (0 .. $#dirs) {
1421                my $dir = join('/', @dirs[0 .. $_]);
1422                my $parent = join('/', @dirs[0 .. $_-1]);
1423                next if $seen_dir{$dir}++;
1424                die if (length $parent and not $seen_dir{$parent});
1425                my $parent_exists = "\$(INST_$uc)/$parent/.exists";
1426                $parent_exists =~ tr!/!/!s;
1427                $extra .= <<END
1428\$(INST_$uc)/$dir/.exists :: \$(PERL_INC)/perl.h $parent_exists
1429	\@\$(MKPATH) \$(INST_$uc)/$dir
1430	\@\$(EQUALIZE_TIMESTAMP) \$(PERL_INC)/perl.h \$(INST_$uc)/$dir/.exists
1431	-\@\$(CHMOD) \$(PERM_RWX) \$(INST_$uc)/$dir
1432END
1433;
1434            }
1435            my $dir_exists = "\$(INST_$uc)/" . join('/', @dirs) . '/.exists';
1436            $dir_exists =~ tr!/!/!s;
1437
1438            $extra .= <<END
1439\$(INST_$uc)/$inst_pos: $src Makefile $dir_exists
1440	\@\$(RM_F) \$(INST_$uc)/$inst_pos
1441	perl -MExtUtils::Command -e cp $src \$(INST_$uc)/$inst_pos
1442	-\@\$(CHMOD) \$(PERM_RW) \$(INST_$uc)/$inst_pos
1443END
1444;
1445            $extra =~ s/ {8}/\t/g;
1446            $inherited .= $extra;
1447        }
1448
1449        # These targets need to be added to pure_all, using a new target
1450        # pure_$filetype.
1451        #
1452        $inherited =~ s/^(\s*pure_all\s+::\s.+)/$1 pure_$filetype/m
1453          or die "no pure_all in: $inherited";
1454        $inherited .= "pure_$filetype :: ";
1455        foreach (sort keys %files) {
1456            my $inst_pos = $files{$_};
1457            $inherited .= "\$(INST_$uc)/$inst_pos ";
1458        }
1459        $inherited .= "\n\t\@\$(NOOP)\n";
1460
1461        # And realclean should remove them, by calling realclean_$filetype.
1462        $inherited =~ s/^(\s*realclean\s+::\s[^\\]+)/$1 realclean_$filetype /m or die;
1463        $inherited .= "realclean_$filetype ::\n\t\$(RM_F) ";
1464        foreach (sort keys %files) {
1465            my $inst_pos = $files{$_};
1466            $inherited .= "\$(INST_$uc)/$inst_pos ";
1467        }
1468        $inherited .= "\n";
1469    }
1470
1471    return $inherited;
1472}
1473
1474# 'make clean' doesn't remove generated files from *.PL (see posting
1475# to makemaker@perl.org).  Fix it.
1476#
1477sub MY::clean {
1478    package MY;
1479    my $inherited = shift->SUPER::clean(@_);
1480    $inherited =~ s/\s+$//;
1481    $inherited .= "\n\t-\$(RM_F) $_\n" foreach @to_clean;
1482    return $inherited;
1483}
1484
1485sub MY::processPL {
1486    package MY;
1487    my $inherited = shift->SUPER::processPL(@_);
1488
1489    # Add some exra dependencies.
1490    my ($k, $v);
1491    while (@deps) {
1492        ($k, $v, @deps) = @deps;
1493        $inherited =~ s!^(\s*$k\s+::\s.+)!"$1 " . join(' ', @$v)!me
1494          or die "no $k in: $inherited";
1495    }
1496
1497    # And some of the .in generators need the share/ directory passed
1498    # as an extra argument.  This is the location in the installed
1499    # system, not that where files are being copied, so $(PREFIX) but
1500    # no $(DESTDIR).
1501    #
1502    foreach (@grab_need_share) {
1503        $inherited =~
1504          s<(grab/$_/tv_grab_$_.PL grab/$_/tv_grab_$_)\s*$>
1505            <$1 \$(PREFIX)/share/xmltv>m
1506            or die "no call to $_.PL in: $inherited";
1507    }
1508
1509    foreach (@need_share) {
1510        $inherited =~
1511          s<($_.PL $_)\s*$>
1512            <$1 \$(PREFIX)/share/xmltv>m
1513            or die "no call to $_.PL in: $inherited";
1514    }
1515
1516
1517    return $inherited;
1518}
1519
1520sub MY::makefile {
1521    package MY;
1522    my $inherited = shift->SUPER::makefile(@_);
1523    return $inherited;
1524}
1525
1526# Fix filename of some generated manpages.
1527sub MY::manifypods {
1528    package MY;
1529    for (my $inherited = shift->SUPER::manifypods(@_)) {
1530        foreach my $s (qw(Augment Grab_XML Configure::Writer Configure Data::Recursive::Encode Date GUI Gunzip Options PreferredMethod Summarize Supplement ValidateFile ValidateGrabber Version)) {
1531            s!\$\(INST_MAN3DIR\)/(?:grab::|)$s[.]\$\(MAN3EXT\)!"\$(INST_MAN3DIR)/XMLTV::$s.\$(MAN3EXT)"!;
1532            s!\$\(INSTALLMAN3DIR\)/$s.\$\(MAN3EXT\)!"\$(INSTALLMAN3DIR)/XMLTV::$s.\$(MAN3EXT)"!;
1533        }
1534        return $_;
1535    }
1536}
1537
1538# Split a section of makefile into targets.
1539sub targets( $ ) {
1540    my @lines = split /\n/, shift;
1541    $_ .= "\n" foreach @lines;
1542    my %r;
1543    my $curr_target;
1544    foreach (@lines) {
1545        if (/^(\S+)\s+:/) {
1546            # Beginning of a new target.
1547            my $name = $1;
1548            die "target $name seen twice" if defined $r{$name};
1549            $r{$name} = $curr_target = [];
1550        }
1551        elsif (/^\s+/ and defined $curr_target) {
1552            # Commands for the target.
1553            push @$curr_target, $_;
1554        }
1555        elsif (/^$/) {
1556            # Blank lines are legal in a target definition
1557        }
1558        elsif (/^\s*(?:\#.*)?$/) {
1559            undef $curr_target;
1560        }
1561        else {
1562            chomp;
1563            die "bad makefile line: '$_'";
1564        }
1565    }
1566    return \%r;
1567}
1568