Finally implement compound OptDep group augmentation
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Optional / Dependencies.pm
1 package DBIx::Class::Optional::Dependencies;
2
3 ### This may look crazy, but it in fact tangibly ( by 50(!)% ) shortens
4 #   the skip-test time when everything requested is unavailable
5 use if $ENV{RELEASE_TESTING} => 'warnings';
6 use if $ENV{RELEASE_TESTING} => 'strict';
7
8 sub croak {
9   require Carp;
10   Carp::croak(@_);
11 };
12 ###
13
14 # NO EXTERNAL NON-5.8.1 CORE DEPENDENCIES EVER (e.g. C::A::G)
15 # This module is to be loaded by Makefile.PM on a pristine system
16
17 # POD is generated automatically by calling _gen_pod from the
18 # Makefile.PL in $AUTHOR mode
19
20 # *DELIBERATELY* not making a group for these - they must disappear
21 # forever as optdeps in the first place
22 my $moose_basic = {
23   'Moose'                         => '0.98',
24   'MooseX::Types'                 => '0.21',
25   'MooseX::Types::LoadableClass'  => '0.011',
26 };
27
28 my $dbic_reqs = {
29
30   # NOTE: the rationale for 2 JSON::Any versions is that
31   # we need the newer only to work around JSON::XS, which
32   # itself is an optional dep
33   _json_any => {
34     req => {
35       'JSON::Any' => '1.23',
36     },
37   },
38
39   _json_xs_compatible_json_any => {
40     req => {
41       'JSON::Any' => '1.31',
42     },
43   },
44
45   # a common placeholder for engines with IC::DT support based off DT::F::S
46   _icdt_strptime_based => {
47     augment => {
48       icdt => {
49         req => {
50           'DateTime::Format::Strptime' => '1.2',
51         },
52       },
53     }
54   },
55
56   _rdbms_generic_odbc => {
57     req => {
58       'DBD::ODBC' => 0,
59     }
60   },
61
62   _rdbms_generic_ado => {
63     req => {
64       'DBD::ADO' => 0,
65     }
66   },
67
68   replicated => {
69     req => $moose_basic,
70     pod => {
71       title => 'Storage::Replicated',
72       desc => 'Modules required for L<DBIx::Class::Storage::DBI::Replicated>',
73     },
74   },
75
76   test_replicated => {
77     include => 'replicated',
78     req => {
79       'Test::Moose' => '0',
80     },
81   },
82
83   admin => {
84     include => '_json_any',
85     req => {
86       %$moose_basic,
87       'MooseX::Types::Path::Class' => '0.05',
88       'MooseX::Types::JSON' => '0.02',
89     },
90     pod => {
91       title => 'DBIx::Class::Admin',
92       desc => 'Modules required for the DBIx::Class administrative library',
93     },
94   },
95
96   admin_script => {
97     include => 'admin',
98     req => {
99       'Getopt::Long::Descriptive' => '0.081',
100       'Text::CSV' => '1.16',
101     },
102     pod => {
103       title => 'dbicadmin',
104       desc => 'Modules required for the CLI DBIx::Class interface dbicadmin',
105     },
106   },
107
108   deploy => {
109     req => {
110       'SQL::Translator'           => '0.11018',
111     },
112     pod => {
113       title => 'Storage::DBI::deploy()',
114       desc => 'Modules required for L<DBIx::Class::Storage::DBI/deployment_statements> and L<DBIx::Class::Schema/deploy>',
115     },
116   },
117
118   icdt => {
119     req => {
120       'DateTime' => '0.55',
121     },
122     pod => {
123       title => 'InflateColumn::DateTime support',
124       desc =>
125         'Modules required for L<DBIx::Class::InflateColumn::DateTime>. '
126       . 'Note that this group does not require much on its own, but '
127       . 'instead is augmented by various RDBMS-specific groups. See the '
128       . 'documentation of each C<rbms_*> group for details',
129     },
130   },
131
132   id_shortener => {
133     req => {
134       'Math::BigInt' => '1.80',
135       'Math::Base36' => '0.07',
136     },
137   },
138
139   test_component_accessor => {
140     req => {
141       'Class::Unload'             => '0.07',
142     },
143   },
144
145   test_pod => {
146     req => {
147       'Test::Pod'                 => '1.42',
148     },
149     release_testing_mandatory => 1,
150   },
151
152   test_podcoverage => {
153     req => {
154       'Test::Pod::Coverage'       => '1.08',
155       'Pod::Coverage'             => '0.20',
156     },
157     release_testing_mandatory => 1,
158   },
159
160   test_whitespace => {
161     req => {
162       'Test::EOL'                 => '1.0',
163       'Test::NoTabs'              => '0.9',
164     },
165     release_testing_mandatory => 1,
166   },
167
168   test_strictures => {
169     req => {
170       'Test::Strict'              => '0.20',
171     },
172     release_testing_mandatory => 1,
173   },
174
175   test_prettydebug => {
176     include => '_json_any',
177   },
178
179   test_admin_script => {
180     include => [qw( admin_script _json_xs_compatible_json_any )],
181     req => {
182       'JSON' => 0,
183       'JSON::PP' => 0,
184       'Cpanel::JSON::XS' => 0,
185       'JSON::XS' => 0,
186       $^O eq 'MSWin32'
187         # for t/admin/10script.t
188         ? ('Win32::ShellQuote' => 0)
189         # DWIW does not compile (./configure even) on win32
190         : ('JSON::DWIW' => 0 )
191       ,
192     }
193   },
194
195   test_leaks_heavy => {
196     req => {
197       'Class::MethodCache' => '0.02',
198       'PadWalker' => '1.06',
199     },
200   },
201
202   test_cdbicompat => {
203     include => 'icdt',
204     req => {
205       'Class::DBI::Plugin::DeepAbstractSearch' => '0',
206       'Time::Piece::MySQL'        => '0',
207       'Date::Simple'              => '3.03',
208     },
209   },
210
211   # this is just for completeness as SQLite
212   # is a core dep of DBIC for testing
213   rdbms_sqlite => {
214     req => {
215       'DBD::SQLite' => 0,
216     },
217     pod => {
218       title => 'SQLite support',
219       desc => 'Modules required to connect to SQLite',
220     },
221     augment => {
222       icdt => {
223         req => {
224           'DateTime::Format::SQLite' => '0',
225         },
226       },
227     },
228   },
229
230   # centralize the specification, as we have ICDT tests which can
231   # test the full behavior of RDBMS-specific ICDT on top of bare SQLite
232   # not _-prefixed so that it will show up under req_group_list
233   icdt_pg => {
234     augment => {
235       icdt => {
236         req => {
237           'DateTime::Format::Pg' => '0.16004',
238         },
239       },
240     },
241   },
242
243   rdbms_pg => {
244     include => 'icdt_pg',
245     req => {
246       # when changing this list make sure to adjust xt/optional_deps.t
247       'DBD::Pg' => 0,
248     },
249     pod => {
250       title => 'PostgreSQL support',
251       desc => 'Modules required to connect to PostgreSQL',
252     },
253   },
254
255   _rdbms_mssql_common => {
256     include => '_icdt_strptime_based',
257   },
258
259   rdbms_mssql_odbc => {
260     include => [qw( _rdbms_generic_odbc _rdbms_mssql_common )],
261     pod => {
262       title => 'MSSQL support via DBD::ODBC',
263       desc => 'Modules required to connect to MSSQL via DBD::ODBC',
264     },
265   },
266
267   rdbms_mssql_sybase => {
268     include => '_rdbms_mssql_common',
269     req => {
270       'DBD::Sybase' => 0,
271     },
272     pod => {
273       title => 'MSSQL support via DBD::Sybase',
274       desc => 'Modules required to connect to MSSQL via DBD::Sybase',
275     },
276   },
277
278   rdbms_mssql_ado => {
279     include => [qw( _rdbms_generic_ado _rdbms_mssql_common )],
280     pod => {
281       title => 'MSSQL support via DBD::ADO (Windows only)',
282       desc => 'Modules required to connect to MSSQL via DBD::ADO. This particular DBD is available on Windows only',
283     },
284   },
285
286   _rdbms_msaccess_common => {
287     include => '_icdt_strptime_based',
288   },
289
290   rdbms_msaccess_odbc => {
291     include => [qw( _rdbms_generic_odbc _rdbms_msaccess_common )],
292     pod => {
293       title => 'MS Access support via DBD::ODBC',
294       desc => 'Modules required to connect to MS Access via DBD::ODBC',
295     },
296   },
297
298   rdbms_msaccess_ado => {
299     include => [qw( _rdbms_generic_ado _rdbms_msaccess_common )],
300     pod => {
301       title => 'MS Access support via DBD::ADO (Windows only)',
302       desc => 'Modules required to connect to MS Access via DBD::ADO. This particular DBD is available on Windows only',
303     },
304   },
305
306   # centralize the specification, as we have ICDT tests which can
307   # test the full behavior of RDBMS-specific ICDT on top of bare SQLite
308   # not _-prefixed so that it will show up under req_group_list
309   icdt_mysql => {
310     augment => {
311       icdt => {
312         req => {
313           'DateTime::Format::MySQL' => '0',
314         },
315       },
316     },
317   },
318
319   rdbms_mysql => {
320     include => 'icdt_mysql',
321     req => {
322       'DBD::mysql' => 0,
323     },
324     pod => {
325       title => 'MySQL support',
326       desc => 'Modules required to connect to MySQL',
327     },
328   },
329
330   rdbms_oracle => {
331     include => 'id_shortener',
332     req => {
333       'DBD::Oracle' => 0,
334     },
335     pod => {
336       title => 'Oracle support',
337       desc => 'Modules required to connect to Oracle',
338     },
339     augment => {
340       icdt => {
341         req => {
342           'DateTime::Format::Oracle' => '0',
343         },
344       },
345     },
346   },
347
348   rdbms_ase => {
349     include => '_icdt_strptime_based',
350     req => {
351       'DBD::Sybase' => 0,
352     },
353     pod => {
354       title => 'Sybase ASE support',
355       desc => 'Modules required to connect to Sybase ASE',
356     },
357   },
358
359   _rdbms_db2_common => {
360     augment => {
361       icdt => {
362         req => {
363           'DateTime::Format::DB2' => '0',
364         },
365       },
366     },
367   },
368
369   rdbms_db2 => {
370     include => '_rdbms_db2_common',
371     req => {
372       'DBD::DB2' => 0,
373     },
374     pod => {
375       title => 'DB2 support',
376       desc => 'Modules required to connect to DB2',
377     },
378   },
379
380   rdbms_db2_400 => {
381     include => [qw( _rdbms_generic_odbc _rdbms_db2_common )],
382     pod => {
383       title => 'DB2 on AS/400 support',
384       desc => 'Modules required to connect to DB2 on AS/400',
385     },
386   },
387
388   rdbms_informix => {
389     include => '_icdt_strptime_based',
390     req => {
391       'DBD::Informix' => 0,
392     },
393     pod => {
394       title => 'Informix support',
395       desc => 'Modules required to connect to Informix',
396     },
397   },
398
399   _rdbms_sqlanywhere_common => {
400     inclide => '_icdt_strptime_based',
401   },
402
403   rdbms_sqlanywhere => {
404     include => '_rdbms_sqlanywhere_common',
405     req => {
406       'DBD::SQLAnywhere' => 0,
407     },
408     pod => {
409       title => 'SQLAnywhere support',
410       desc => 'Modules required to connect to SQLAnywhere',
411     },
412   },
413
414   rdbms_sqlanywhere_odbc => {
415     include => [qw( _rdbms_generic_odbc _rdbms_sqlanywhere_common )],
416     pod => {
417       title => 'SQLAnywhere support via DBD::ODBC',
418       desc => 'Modules required to connect to SQLAnywhere via DBD::ODBC',
419     },
420   },
421
422   _rdbms_firebird_common => {
423     include => '_icdt_strptime_based',
424   },
425
426   rdbms_firebird => {
427     include => '_rdbms_firebird_common',
428     req => {
429       'DBD::Firebird' => 0,
430     },
431     pod => {
432       title => 'Firebird support',
433       desc => 'Modules required to connect to Firebird',
434     },
435   },
436
437   rdbms_firebird_interbase => {
438     include => '_rdbms_firebird_common',
439     req => {
440       'DBD::InterBase' => 0,
441     },
442     pod => {
443       title => 'Firebird support via DBD::InterBase',
444       desc => 'Modules required to connect to Firebird via DBD::InterBase',
445     },
446   },
447
448   rdbms_firebird_odbc => {
449     include => [qw( _rdbms_generic_odbc _rdbms_firebird_common )],
450     pod => {
451       title => 'Firebird support via DBD::ODBC',
452       desc => 'Modules required to connect to Firebird via DBD::ODBC',
453     },
454   },
455
456   test_rdbms_sqlite => {
457     include => 'rdbms_sqlite',
458     req => {
459       ###
460       ### IMPORTANT - do not raise this dependency
461       ### even though many bugfixes are present in newer versions, the general DBIC
462       ### rule is to bend over backwards for available DBDs (given upgrading them is
463       ### often *not* easy or even possible)
464       ###
465       'DBD::SQLite' => '1.29',
466     },
467   },
468
469   test_rdbms_pg => {
470     include => 'rdbms_pg',
471     env => [
472       DBICTEST_PG_DSN => 1,
473       DBICTEST_PG_USER => 0,
474       DBICTEST_PG_PASS => 0,
475     ],
476     req => {
477       # the order does matter because the rdbms support group might require
478       # a different version that the test group
479       #
480       # when changing this list make sure to adjust xt/optional_deps.t
481       'DBD::Pg' => '2.009002',  # specific version to test bytea
482     },
483   },
484
485   test_rdbms_mssql_odbc => {
486     include => 'rdbms_mssql_odbc',
487     env => [
488       DBICTEST_MSSQL_ODBC_DSN => 1,
489       DBICTEST_MSSQL_ODBC_USER => 0,
490       DBICTEST_MSSQL_ODBC_PASS => 0,
491     ],
492   },
493
494   test_rdbms_mssql_ado => {
495     include => 'rdbms_mssql_ado',
496     env => [
497       DBICTEST_MSSQL_ADO_DSN => 1,
498       DBICTEST_MSSQL_ADO_USER => 0,
499       DBICTEST_MSSQL_ADO_PASS => 0,
500     ],
501   },
502
503   test_rdbms_mssql_sybase => {
504     include => 'rdbms_mssql_sybase',
505     env => [
506       DBICTEST_MSSQL_DSN => 1,
507       DBICTEST_MSSQL_USER => 0,
508       DBICTEST_MSSQL_PASS => 0,
509     ],
510   },
511
512   test_rdbms_msaccess_odbc => {
513     include => 'rdbms_msaccess_odbc',
514     env => [
515       DBICTEST_MSACCESS_ODBC_DSN => 1,
516       DBICTEST_MSACCESS_ODBC_USER => 0,
517       DBICTEST_MSACCESS_ODBC_PASS => 0,
518     ],
519     req => {
520       'Data::GUID' => '0',
521     },
522   },
523
524   test_rdbms_msaccess_ado => {
525     include => 'rdbms_msaccess_ado',
526     env => [
527       DBICTEST_MSACCESS_ADO_DSN => 1,
528       DBICTEST_MSACCESS_ADO_USER => 0,
529       DBICTEST_MSACCESS_ADO_PASS => 0,
530     ],
531     req => {
532       'Data::GUID' => 0,
533     },
534   },
535
536   test_rdbms_mysql => {
537     include => 'rdbms_mysql',
538     env => [
539       DBICTEST_MYSQL_DSN => 1,
540       DBICTEST_MYSQL_USER => 0,
541       DBICTEST_MYSQL_PASS => 0,
542     ],
543   },
544
545   test_rdbms_oracle => {
546     include => 'rdbms_oracle',
547     env => [
548       DBICTEST_ORA_DSN => 1,
549       DBICTEST_ORA_USER => 0,
550       DBICTEST_ORA_PASS => 0,
551     ],
552     req => {
553       'DBD::Oracle'              => '1.24',
554     },
555   },
556
557   test_rdbms_ase => {
558     include => 'rdbms_ase',
559     env => [
560       DBICTEST_SYBASE_DSN => 1,
561       DBICTEST_SYBASE_USER => 0,
562       DBICTEST_SYBASE_PASS => 0,
563     ],
564   },
565
566   test_rdbms_db2 => {
567     include => 'rdbms_db2',
568     env => [
569       DBICTEST_DB2_DSN => 1,
570       DBICTEST_DB2_USER => 0,
571       DBICTEST_DB2_PASS => 0,
572     ],
573   },
574
575   test_rdbms_db2_400 => {
576     include => 'rdbms_db2_400',
577     env => [
578       DBICTEST_DB2_400_DSN => 1,
579       DBICTEST_DB2_400_USER => 0,
580       DBICTEST_DB2_400_PASS => 0,
581     ],
582   },
583
584   test_rdbms_informix => {
585     include => 'rdbms_informix',
586     env => [
587       DBICTEST_INFORMIX_DSN => 1,
588       DBICTEST_INFORMIX_USER => 0,
589       DBICTEST_INFORMIX_PASS => 0,
590     ],
591   },
592
593   test_rdbms_sqlanywhere => {
594     include => 'rdbms_sqlanywhere',
595     env => [
596       DBICTEST_SQLANYWHERE_DSN => 1,
597       DBICTEST_SQLANYWHERE_USER => 0,
598       DBICTEST_SQLANYWHERE_PASS => 0,
599     ],
600   },
601
602   test_rdbms_sqlanywhere_odbc => {
603     include => 'rdbms_sqlanywhere_odbc',
604     env => [
605       DBICTEST_SQLANYWHERE_ODBC_DSN => 1,
606       DBICTEST_SQLANYWHERE_ODBC_USER => 0,
607       DBICTEST_SQLANYWHERE_ODBC_PASS => 0,
608     ],
609   },
610
611   test_rdbms_firebird => {
612     include => 'rdbms_firebird',
613     env => [
614       DBICTEST_FIREBIRD_DSN => 1,
615       DBICTEST_FIREBIRD_USER => 0,
616       DBICTEST_FIREBIRD_PASS => 0,
617     ],
618   },
619
620   test_rdbms_firebird_interbase => {
621     include => 'rdbms_firebird_interbase',
622     env => [
623       DBICTEST_FIREBIRD_INTERBASE_DSN => 1,
624       DBICTEST_FIREBIRD_INTERBASE_USER => 0,
625       DBICTEST_FIREBIRD_INTERBASE_PASS => 0,
626     ],
627   },
628
629   test_rdbms_firebird_odbc => {
630     include => 'rdbms_firebird_odbc',
631     env => [
632       DBICTEST_FIREBIRD_ODBC_DSN => 1,
633       DBICTEST_FIREBIRD_ODBC_USER => 0,
634       DBICTEST_FIREBIRD_ODBC_PASS => 0,
635     ],
636   },
637
638   test_memcached => {
639     env => [
640       DBICTEST_MEMCACHED => 1,
641     ],
642     req => {
643       'Cache::Memcached' => 0,
644     },
645   },
646
647   dist_dir => {
648     # we need to run the dbicadmin so we can self-generate its POD
649     # also we do not want surprises in case JSON::XS is in the path
650     # so make sure we get an always-working JSON::Any
651     include => [qw( admin_script _json_xs_compatible_json_any )],
652     req => {
653       'ExtUtils::MakeMaker' => '6.64',
654       'Pod::Inherit'        => '0.91',
655     },
656   },
657
658   dist_upload => {
659     req => {
660       'CPAN::Uploader' => '0.103001',
661     },
662   },
663 };
664
665
666
667 ### Public API
668
669 sub import {
670   my $class = shift;
671
672   if (@_) {
673
674     my $action = shift;
675
676     if ($action eq '-die_without') {
677       my $err;
678       {
679         local $@;
680         eval { $class->die_unless_req_ok_for(\@_); 1 }
681           or $err = $@;
682       }
683       die "\n$err\n" if $err;
684     }
685     elsif ($action eq '-list_missing') {
686       print $class->modreq_missing_for(\@_);
687       print "\n";
688       exit 0;
689     }
690     elsif ($action eq '-skip_all_without') {
691
692       # sanity check - make sure ->current_test is 0 and no plan has been declared
693       do {
694         local $@;
695         defined eval {
696           Test::Builder->new->current_test
697             or
698           Test::Builder->new->has_plan
699         };
700       } and croak("Unable to invoke -skip_all_without after testing has started");
701
702       if ( my $missing = $class->req_missing_for(\@_) ) {
703
704         die ("\nMandatory requirements not satisfied during release-testing: $missing\n\n")
705           if $ENV{RELEASE_TESTING} and $class->_groups_to_reqs(\@_)->{release_testing_mandatory};
706
707         print "1..0 # SKIP requirements not satisfied: $missing\n";
708         exit 0;
709       }
710     }
711     elsif ($action =~ /^-/) {
712       croak "Unknown import-time action '$action'";
713     }
714     else {
715       croak "$class is not an exporter, unable to import '$action'";
716     }
717   }
718
719   1;
720 }
721
722 sub unimport {
723   croak( __PACKAGE__ . " does not implement unimport" );
724 }
725
726 # OO for (mistakenly considered) ease of extensibility, not due to any need to
727 # carry state of any sort. This API is currently used outside, so leave as-is.
728 # FIXME - make sure to not propagate this further if module is extracted as a
729 # standalone library - keep the stupidity to a DBIC-secific shim!
730 #
731 sub req_list_for {
732   shift->_groups_to_reqs(shift)->{effective_modreqs};
733 }
734
735 sub modreq_list_for {
736   shift->_groups_to_reqs(shift)->{modreqs};
737 }
738
739 sub req_group_list {
740   +{ map
741     { $_ => $_[0]->_groups_to_reqs($_) }
742     grep { $_ !~ /^_/ } keys %$dbic_reqs
743   }
744 }
745
746 sub req_errorlist_for { shift->modreq_errorlist_for(shift) }  # deprecated
747 sub modreq_errorlist_for {
748   my ($self, $groups) = @_;
749   $self->_errorlist_for_modreqs( $self->_groups_to_reqs($groups)->{modreqs} );
750 }
751
752 sub req_ok_for {
753   shift->req_missing_for(shift) ? 0 : 1;
754 }
755
756 sub req_missing_for {
757   my ($self, $groups) = @_;
758
759   my $reqs = $self->_groups_to_reqs($groups);
760   my $mods_missing = $self->modreq_missing_for($groups);
761
762   return '' if
763     ! $mods_missing
764       and
765     ! $reqs->{missing_envvars}
766   ;
767
768   my @res = $mods_missing || ();
769
770   push @res, 'the following group(s) of environment variables: ' . join ' and ', sort map
771     { __envvar_group_desc($_) }
772     @{$reqs->{missing_envvars}}
773   if $reqs->{missing_envvars};
774
775   return (
776     ( join ' as well as ', @res )
777       .
778     ( $reqs->{modreqs_fully_documented} ? " (see @{[ ref $self || $self ]} documentation for details)" : '' ),
779   );
780 }
781
782 sub modreq_missing_for {
783   my ($self, $groups) = @_;
784
785   my $reqs = $self->_groups_to_reqs($groups);
786   my $modreq_errors = $self->_errorlist_for_modreqs($reqs->{modreqs})
787     or return '';
788
789   join ' ', map
790     { $reqs->{modreqs}{$_} ? qq("$_~>=$reqs->{modreqs}{$_}") : $_ }
791     sort { lc($a) cmp lc($b) } keys %$modreq_errors
792   ;
793 }
794
795 sub die_unless_req_ok_for {
796   if (my $err = shift->req_missing_for(shift) ) {
797     die "Unable to continue due to missing requirements: $err\n";
798   }
799 }
800
801
802
803 ### Private functions
804
805 # potentially shorten group desc
806 sub __envvar_group_desc {
807   my @envs = @{$_[0]};
808
809   my (@res, $last_prefix);
810   while (my $ev = shift @envs) {
811     my ($pref, $sep, $suff) = split / ([\_\-]) (?= [^\_\-]+ \z )/x, $ev;
812
813     if ( defined $sep and ($last_prefix||'') eq $pref ) {
814         push @res, "...${sep}${suff}"
815     }
816     else {
817       push @res, $ev;
818     }
819
820     $last_prefix = $pref if $sep;
821   }
822
823   join '/', @res;
824 }
825
826 # Expand includes from a random group in a specific order:
827 # nonvariable groups first, then their includes, then the variable groups,
828 # then their includes.
829 # This allows reliably marking the rest of the mod reqs as variable (this is
830 # also why variable includes are currently not allowed)
831 sub __expand_includes {
832   my ($groups, $seen) = @_;
833
834   # !! DIFFERENT !! behavior and return depending on invocation mode
835   # (easier to recurse this way)
836   my $is_toplevel = $seen
837     ? 0
838     : !! ($seen = {})
839   ;
840
841   my ($res_per_type, $missing_envvars);
842
843   # breadth-first evaluation, with non-variable includes on top
844   for my $g (@$groups) {
845
846     croak "Invalid requirement group name '$g': only ascii alphanumerics and _ are allowed"
847       if $g !~ /\A [A-Z_a-z][0-9A-Z_a-z]* \z/x;
848
849     my $r = $dbic_reqs->{$g}
850       or croak "Requirement group '$g' is not defined";
851
852     # always do this check *before* the $seen check
853     croak "Group '$g' with variable effective_modreqs can not be specified as an 'include'"
854       if ( $r->{env} and ! $is_toplevel );
855
856     next if $seen->{$g}++;
857
858     my $req_type = 'static';
859
860     if ( my @e = @{$r->{env}||[]} ) {
861
862       croak "Unexpected 'env' attribute under group '$g' (only allowed in test_* groups)"
863         unless $g =~ /^test_/;
864
865       croak "Unexpected *odd* list in 'env' under group '$g'"
866         if @e % 2;
867
868       # deconstruct the whole thing
869       my (@group_envnames_list, $some_envs_required, $some_required_missing);
870       while (@e) {
871         push @group_envnames_list, my $envname = shift @e;
872
873         # env required or not
874         next unless shift @e;
875
876         $some_envs_required ||= 1;
877
878         $some_required_missing ||= (
879           ! defined $ENV{$envname}
880             or
881           ! length $ENV{$envname}
882         );
883       }
884
885       croak "None of the envvars in group '$g' declared as required, making the requirement moot"
886         unless $some_envs_required;
887
888       if ($some_required_missing) {
889         push @{$missing_envvars->{$g}}, \@group_envnames_list;
890         $req_type = 'variable';
891       }
892     }
893
894     push @{$res_per_type->{"base_${req_type}"}}, $g;
895
896     if (my $i = $dbic_reqs->{$g}{include}) {
897       $i = [ $i ] unless ref $i eq 'ARRAY';
898
899       croak "Malformed 'include' for group '$g': must be another existing group name or arrayref of existing group names"
900         unless @$i;
901
902       push @{$res_per_type->{"incs_${req_type}"}}, @$i;
903     }
904   }
905
906   my @ret = map {
907     @{ $res_per_type->{"base_${_}"} || [] },
908     ( $res_per_type->{"incs_${_}"} ? __expand_includes( $res_per_type->{"incs_${_}"}, $seen ) : () ),
909   } qw(static variable);
910
911   return ! $is_toplevel ? @ret : do {
912     my $rv = {};
913     $rv->{$_} = {
914       idx => 1 + keys %$rv,
915       missing_envvars => $missing_envvars->{$_},
916     } for @ret;
917     $rv->{$_}{user_requested} = 1 for @$groups;
918     $rv;
919   };
920 }
921
922 ### Private OO API
923 our %req_unavailability_cache;
924
925 # this method is just a lister and envvar/metadata checker - it does not try to load anything
926 sub _groups_to_reqs {
927   my ($self, $groups) = @_;
928
929   $groups = [ $groups || () ]
930     unless ref $groups eq 'ARRAY';
931
932   croak "@{[ (caller(1))[3] ]}() expects a requirement group name or arrayref of group names"
933     unless @$groups;
934
935   my $ret = {
936     modreqs => {},
937     modreqs_fully_documented => 1,
938   };
939
940   my $all_groups = __expand_includes($groups);
941
942   # pre-assemble list of augmentations, perform basic sanity checks
943   # Note that below we *DO NOT* respect the source/target reationship, but
944   # instead always default to augment the "later" group
945   # This is done so that the "stable/variable" boundary keeps working as
946   # expected
947   my $augmentations;
948   for my $requesting_group (keys %$all_groups) {
949     if (my $ag = $dbic_reqs->{$requesting_group}{augment}) {
950       for my $target_group (keys %$ag) {
951
952         croak "Group '$requesting_group' claims to augment a non-existent group '$target_group'"
953           unless $dbic_reqs->{$target_group};
954
955         croak "Augmentation combined with variable effective_modreqs currently unsupported for group '$requesting_group'"
956           if $dbic_reqs->{$requesting_group}{env};
957
958         croak "Augmentation of group '$target_group' with variable effective_modreqs unsupported (requested by '$requesting_group')"
959           if $dbic_reqs->{$target_group}{env};
960
961         if (my @foreign = grep { $_ ne 'req' } keys %{$ag->{$target_group}} ) {
962           croak "Only 'req' augmentations are currently supported (group '$requesting_group' attempts to alter '$foreign[0]' of group '$target_group'";
963         }
964
965         $ret->{augments}{$target_group} = 1;
966
967         # no augmentation for stuff that hasn't been selected
968         if ( $all_groups->{$target_group} and my $ar = $ag->{$target_group}{req} ) {
969           push @{$augmentations->{
970             ( $all_groups->{$requesting_group}{idx} < $all_groups->{$target_group}{idx} )
971               ? $target_group
972               : $requesting_group
973           }}, $ar;
974         }
975       }
976     }
977   }
978
979   for my $group (sort { $all_groups->{$a}{idx} <=> $all_groups->{$b}{idx} } keys %$all_groups ) {
980
981     my $group_reqs = $dbic_reqs->{$group}{req};
982
983     # sanity-check
984     for my $req_bag ($group_reqs, @{ $augmentations->{$group} || [] } ) {
985       for (keys %$req_bag) {
986
987         $_ =~ /\A [A-Z_a-z][0-9A-Z_a-z]* (?:::[0-9A-Z_a-z]+)* \z /x
988           or croak "Requirement '$_' in group '$group' is not a valid module name";
989
990         # !!!DO NOT CHANGE!!!
991         # remember - version.pm may not be available on the system
992         croak "Requirement '$_' in group '$group' specifies an invalid version '$req_bag->{$_}' (only plain non-underscored floating point decimals are supported)"
993           if ( ($req_bag->{$_}||0) !~ / \A [0-9]+ (?: \. [0-9]+ )? \z /x );
994       }
995     }
996
997     if (my $e = $all_groups->{$group}{missing_envvars}) {
998       push @{$ret->{missing_envvars}}, @$e;
999     }
1000
1001     # assemble into the final ret
1002     for my $type (
1003       'modreqs',
1004       ( $ret->{missing_envvars} ? () : 'effective_modreqs' ),
1005     ) {
1006       for my $req_bag ($group_reqs, @{ $augmentations->{$group} || [] } ) {
1007         for my $mod (keys %$req_bag) {
1008
1009           $ret->{$type}{$mod} = $req_bag->{$mod}||0 if (
1010
1011             ! exists $ret->{$type}{$mod}
1012               or
1013             # we sanitized the version to be numeric above - we can just -gt it
1014             ($req_bag->{$mod}||0) > $ret->{$type}{$mod}
1015
1016           );
1017         }
1018       }
1019     }
1020
1021     $ret->{modreqs_fully_documented} &&= !!$dbic_reqs->{$group}{pod}
1022       if $all_groups->{$group}{user_requested};
1023
1024     $ret->{release_testing_mandatory} ||= !!$dbic_reqs->{$group}{release_testing_mandatory};
1025   }
1026
1027   return $ret;
1028 }
1029
1030
1031 # this method tries to load specified modreqs and returns a hashref of
1032 # module/loaderror pairs for anything that failed
1033 sub _errorlist_for_modreqs {
1034   # args supposedly already went through _groups_to_reqs and are therefore sanitized
1035   # safe to eval at will
1036   my ($self, $reqs) = @_;
1037
1038   my $ret;
1039
1040   for my $m ( keys %$reqs ) {
1041     my $v = $reqs->{$m};
1042
1043     if (! exists $req_unavailability_cache{$m}{$v} ) {
1044       local $@;
1045       eval( "require $m;" . ( $v ? "$m->VERSION(q($v))" : '' ) );
1046       $req_unavailability_cache{$m}{$v} = $@;
1047     }
1048
1049     $ret->{$m} = $req_unavailability_cache{$m}{$v}
1050       if $req_unavailability_cache{$m}{$v};
1051   }
1052
1053   $ret;
1054 }
1055
1056
1057 # This is to be called by the author only (automatically in Makefile.PL)
1058 sub _gen_pod {
1059   my ($class, $distver, $pod_dir) = @_;
1060
1061   die "No POD root dir supplied" unless $pod_dir;
1062
1063   $distver ||=
1064     eval { require DBIx::Class; DBIx::Class->VERSION; }
1065       ||
1066     die
1067 "\n\n---------------------------------------------------------------------\n" .
1068 'Unable to load core DBIx::Class module to determine current version, '.
1069 'possibly due to missing dependencies. Author-mode autodocumentation ' .
1070 "halted\n\n" . $@ .
1071 "\n\n---------------------------------------------------------------------\n"
1072   ;
1073
1074   # do not ask for a recent version, use 1.x API calls
1075   # this *may* execute on a smoker with old perl or whatnot
1076   require File::Path;
1077
1078   (my $modfn = __PACKAGE__ . '.pm') =~ s|::|/|g;
1079
1080   (my $podfn = "$pod_dir/$modfn") =~ s/\.pm$/\.pod/;
1081   (my $dir = $podfn) =~ s|/[^/]+$||;
1082
1083   File::Path::mkpath([$dir]);
1084
1085   my $sqltver = $class->req_list_for('deploy')->{'SQL::Translator'}
1086     or die "Hrmm? No sqlt dep?";
1087
1088
1089   my @chunks;
1090
1091 #@@
1092 #@@ HEADER
1093 #@@
1094   push @chunks, <<"EOC";
1095 #########################################################################
1096 #####################  A U T O G E N E R A T E D ########################
1097 #########################################################################
1098 #
1099 # The contents of this POD file are auto-generated.  Any changes you make
1100 # will be lost. If you need to change the generated text edit _gen_pod()
1101 # at the end of $modfn
1102 #
1103
1104 =head1 NAME
1105
1106 $class - Optional module dependency specifications (for module authors)
1107 EOC
1108
1109
1110 #@@
1111 #@@ SYNOPSIS HEADING
1112 #@@
1113   push @chunks, <<"EOC";
1114 =head1 SYNOPSIS
1115
1116 Somewhere in your build-file (e.g. L<ExtUtils::MakeMaker>'s F<Makefile.PL>):
1117
1118   ...
1119
1120   \$EUMM_ARGS{CONFIGURE_REQUIRES} = {
1121     \%{ \$EUMM_ARGS{CONFIGURE_REQUIRES} || {} },
1122     'DBIx::Class' => '$distver',
1123   };
1124
1125   ...
1126
1127   my %DBIC_DEPLOY_AND_ORACLE_DEPS = %{ eval {
1128     require $class;
1129     $class->req_list_for([qw( deploy rdbms_oracle icdt )]);
1130   } || {} };
1131
1132   \$EUMM_ARGS{PREREQ_PM} = {
1133     \%DBIC_DEPLOY_AND_ORACLE_DEPS,
1134     \%{ \$EUMM_ARGS{PREREQ_PM} || {} },
1135   };
1136
1137   ...
1138
1139   ExtUtils::MakeMaker::WriteMakefile(\%EUMM_ARGS);
1140
1141 B<Note>: The C<eval> protection within the example is due to support for
1142 requirements during L<the C<configure> build phase|CPAN::Meta::Spec/Phases>
1143 not being available on a sufficient portion of production installations of
1144 Perl. Robust support for such dependency requirements is available in the
1145 L<CPAN> installer only since version C<1.94_56> first made available for
1146 production with perl version C<5.12>. It is the belief of the current
1147 maintainer that support for requirements during the C<configure> build phase
1148 will not be sufficiently ubiquitous until the B<year 2020> at the earliest,
1149 hence the extra care demonstrated above. It should also be noted that some
1150 3rd party installers (e.g. L<cpanminus|App::cpanminus>) do the right thing
1151 with configure requirements independent from the versions of perl and CPAN
1152 available.
1153 EOC
1154
1155
1156 #@@
1157 #@@ DESCRIPTION HEADING
1158 #@@
1159   push @chunks, <<'EOC';
1160 =head1 DESCRIPTION
1161
1162 Some of the less-frequently used features of L<DBIx::Class> have external
1163 module dependencies on their own. In order not to burden the average user
1164 with modules they will never use, these optional dependencies are not included
1165 in the base Makefile.PL. Instead an exception with a descriptive message is
1166 thrown when a specific feature can't find one or several modules required for
1167 its operation. This module is the central holding place for the current list
1168 of such dependencies, for DBIx::Class core authors, and DBIx::Class extension
1169 authors alike.
1170
1171 Dependencies are organized in L<groups|/CURRENT REQUIREMENT GROUPS> where each
1172 group can list one or more required modules, with an optional minimum version
1173 (or 0 for any version). In addition groups prefixed with C<test_> can specify
1174 a set of environment variables, some (or all) of which are marked as required
1175 for the group to be considered by L</req_list_for>
1176
1177 Each group name (or a combination thereof) can be used in the
1178 L<public methods|/METHODS> as described below.
1179 EOC
1180
1181
1182 #@@
1183 #@@ REQUIREMENT GROUPLIST HEADING
1184 #@@
1185   push @chunks, '=head1 CURRENT REQUIREMENT GROUPS';
1186
1187   my $standalone_info;
1188
1189   for my $group (sort keys %$dbic_reqs) {
1190
1191     my $info = $standalone_info->{$group} ||= $class->_groups_to_reqs($group);
1192
1193     next unless (
1194       $info->{modreqs_fully_documented}
1195         and
1196       ( $info->{augments} or $info->{modreqs} )
1197     );
1198
1199     my $p = $dbic_reqs->{$group}{pod};
1200
1201     push @chunks, (
1202       "=head2 $p->{title}",
1203       "=head3 $group",
1204       $p->{desc},
1205       '=over',
1206     );
1207
1208     if ( keys %{ $info->{modreqs}||{} } ) {
1209       push @chunks, map
1210         { "=item * $_" . ($info->{modreqs}{$_} ? " >= $info->{modreqs}{$_}" : '') }
1211         ( sort keys %{ $info->{modreqs} } )
1212       ;
1213     }
1214     else {
1215       push @chunks, '=item * No standalone requirements',
1216     }
1217
1218     push @chunks, '=back';
1219
1220     for my $ag ( sort keys %{ $info->{augments} || {} } ) {
1221       my $ag_info = $standalone_info->{$ag} ||= $class->_groups_to_reqs($ag);
1222
1223       my $newreqs = $class->modreq_list_for([ $group, $ag ]);
1224       for (keys %$newreqs) {
1225         delete $newreqs->{$_} if (
1226           ( defined $info->{modreqs}{$_}    and $info->{modreqs}{$_}    == $newreqs->{$_} )
1227             or
1228           ( defined $ag_info->{modreqs}{$_} and $ag_info->{modreqs}{$_} == $newreqs->{$_} )
1229         );
1230       }
1231
1232       if (keys %$newreqs) {
1233         push @chunks, (
1234           "Combined with L</$ag> additionally requires:",
1235           '=over',
1236           ( map
1237             { "=item * $_" . ($newreqs->{$_} ? " >= $newreqs->{$_}" : '') }
1238             ( sort keys %$newreqs )
1239           ),
1240           '=back',
1241         );
1242       }
1243     }
1244   }
1245
1246
1247 #@@
1248 #@@ API DOCUMENTATION HEADING
1249 #@@
1250   push @chunks, <<'EOC';
1251
1252 =head1 IMPORT-LIKE ACTIONS
1253
1254 Even though this module is not an L<Exporter>, it recognizes several C<actions>
1255 supplied to its C<import> method.
1256
1257 =head2 -skip_all_without
1258
1259 =over
1260
1261 =item Arguments: @group_names
1262
1263 =back
1264
1265 A convenience wrapper for use during testing:
1266 EOC
1267
1268   push @chunks, " use $class -skip_all_without => qw(admin test_rdbms_mysql);";
1269
1270   push @chunks, 'Roughly equivalent to the following code:';
1271
1272   push @chunks, sprintf <<'EOS', ($class) x 2;
1273
1274  BEGIN {
1275    require %s;
1276    if ( my $missing = %s->req_missing_for(\@group_names_) ) {
1277      print "1..0 # SKIP requirements not satisfied: $missing\n";
1278      exit 0;
1279    }
1280  }
1281 EOS
1282
1283   push @chunks, <<'EOC';
1284
1285 It also takes into account the C<RELEASE_TESTING> environment variable and
1286 behaves like L</-die_without> for any requirement groups marked as
1287 C<release_testing_mandatory>.
1288
1289 =head2 -die_without
1290
1291 =over
1292
1293 =item Arguments: @group_names
1294
1295 =back
1296
1297 A convenience wrapper around L</die_unless_req_ok_for>:
1298 EOC
1299
1300   push @chunks, " use $class -die_without => qw(deploy admin);";
1301
1302   push @chunks, <<'EOC';
1303
1304 =head2 -list_missing
1305
1306 =over
1307
1308 =item Arguments: @group_names
1309
1310 =back
1311
1312 A convenience wrapper around L</modreq_missing_for>:
1313
1314  perl -Ilib -MDBIx::Class::Optional::Dependencies=-list_missing,deploy,admin | cpanm
1315
1316 =head1 METHODS
1317
1318 =head2 req_group_list
1319
1320 =over
1321
1322 =item Arguments: none
1323
1324 =item Return Value: \%list_of_requirement_groups
1325
1326 =back
1327
1328 This method should be used by DBIx::Class packagers, to get a hashref of all
1329 dependencies B<keyed> by dependency group. Each key (group name), or a combination
1330 thereof (as an arrayref) can be supplied to the methods below.
1331 The B<values> of the returned hash are currently a set of options B<without a
1332 well defined structure>. If you have use for any of the contents - contact the
1333 maintainers, instead of treating this as public (left alone stable) API.
1334
1335 =head2 req_list_for
1336
1337 =over
1338
1339 =item Arguments: $group_name | \@group_names
1340
1341 =item Return Value: \%set_of_module_version_pairs
1342
1343 =back
1344
1345 This method should be used by DBIx::Class extension authors, to determine the
1346 version of modules a specific set of features requires for this version of
1347 DBIx::Class (regardless of their availability on the system).
1348 See the L</SYNOPSIS> for a real-world example.
1349
1350 When handling C<test_*> groups this method behaves B<differently> from
1351 L</modreq_list_for> below (and is the only such inconsistency among the
1352 C<req_*> methods). If a particular group declares as requirements some
1353 C<environment variables> and these requirements are not satisfied (the envvars
1354 are unset) - then the C<module requirements> of this group are not included in
1355 the returned list.
1356
1357 =head2 modreq_list_for
1358
1359 =over
1360
1361 =item Arguments: $group_name | \@group_names
1362
1363 =item Return Value: \%set_of_module_version_pairs
1364
1365 =back
1366
1367 Same as L</req_list_for> but does not take into consideration any
1368 C<environment variable requirements> - returns just the list of required
1369 modules.
1370
1371 =head2 req_ok_for
1372
1373 =over
1374
1375 =item Arguments: $group_name | \@group_names
1376
1377 =item Return Value: 1|0
1378
1379 =back
1380
1381 Returns true or false depending on whether all modules/envvars required by
1382 the group(s) are loadable/set on the system.
1383
1384 =head2 req_missing_for
1385
1386 =over
1387
1388 =item Arguments: $group_name | \@group_names
1389
1390 =item Return Value: $error_message_string
1391
1392 =back
1393
1394 Returns a single-line string suitable for inclusion in larger error messages.
1395 This method would normally be used by DBIx::Class core features, to indicate to
1396 the user that they need to install specific modules and/or set specific
1397 environment variables before being able to use a specific feature set.
1398
1399 For example if some of the requirements for C<deploy> are not available,
1400 the returned string could look like:
1401 EOC
1402
1403   push @chunks, qq{ "SQL::Translator~>=$sqltver" (see $class documentation for details)};
1404
1405   push @chunks, <<'EOC';
1406 The author is expected to prepend the necessary text to this message before
1407 returning the actual error seen by the user. See also L</modreq_missing_for>
1408
1409 =head2 modreq_missing_for
1410
1411 =over
1412
1413 =item Arguments: $group_name | \@group_names
1414
1415 =item Return Value: $error_message_string
1416
1417 =back
1418
1419 Same as L</req_missing_for> except that the error string is guaranteed to be
1420 either empty, or contain a set of module requirement specifications suitable
1421 for piping to e.g. L<cpanminus|App::cpanminus>. The method explicitly does not
1422 attempt to validate the state of required environment variables (if any).
1423
1424 For instance if some of the requirements for C<deploy> are not available,
1425 the returned string could look like:
1426 EOC
1427
1428   push @chunks, qq{ "SQL::Translator~>=$sqltver"};
1429
1430   push @chunks, <<'EOC';
1431
1432 See also L</-list_missing>.
1433
1434 =head2 die_unless_req_ok_for
1435
1436 =over
1437
1438 =item Arguments: $group_name | \@group_names
1439
1440 =back
1441
1442 Checks if L</req_ok_for> passes for the supplied group(s), and
1443 in case of failure throws an exception including the information
1444 from L</req_missing_for>. See also L</-die_without>.
1445
1446 =head2 modreq_errorlist_for
1447
1448 =over
1449
1450 =item Arguments: $group_name | \@group_names
1451
1452 =item Return Value: \%set_of_loaderrors_per_module
1453
1454 =back
1455
1456 Returns a hashref containing the actual errors that occurred while attempting
1457 to load each module in the requirement group(s).
1458
1459 =head2 req_errorlist_for
1460
1461 Deprecated method name, equivalent (via proxy) to L</modreq_errorlist_for>.
1462
1463 EOC
1464
1465 #@@
1466 #@@ FOOTER
1467 #@@
1468   push @chunks, <<'EOC';
1469 =head1 FURTHER QUESTIONS?
1470
1471 Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
1472
1473 =head1 COPYRIGHT AND LICENSE
1474
1475 This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
1476 by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
1477 redistribute it and/or modify it under the same terms as the
1478 L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.
1479 EOC
1480
1481   eval {
1482     open (my $fh, '>', $podfn) or die;
1483     print $fh join ("\n\n", @chunks) or die;
1484     print $fh "\n" or die;
1485     close ($fh) or die;
1486   } or croak( "Unable to write $podfn: " . ( $! || $@ || 'unknown error') );
1487 }
1488
1489 1;