File Coverage

blib/lib/Inline/Module.pm
Criterion Covered Total %
statement 36 229 15.7
branch 4 86 4.6
condition 1 27 3.7
subroutine 11 35 31.4
pod 1 21 4.7
total 53 398 13.3


line stmt bran cond sub pod time code
1 1     1   647 use strict; use warnings;
  1     1   1  
  1         31  
  1         3  
  1         1  
  1         64  
2             package Inline::Module;
3             our $VERSION = '0.34';
4             our $API_VERSION = 'v2';
5              
6 1     1   4 use Carp 'croak';
  1         1  
  1         59  
7 1     1   5 use Config();
  1         1  
  1         13  
8 1     1   4 use File::Find();
  1         1  
  1         12  
9 1     1   3 use File::Path();
  1         1  
  1         11  
10 1     1   3 use File::Spec();
  1         1  
  1         33  
11              
12             my $inline_build_path = '.inline';
13              
14 1 50   1   3 use constant DEBUG_ON => $ENV{PERL_INLINE_MODULE_DEBUG} ? 1 : 0;
  1         1  
  1         191  
15 0     0 0 0 sub DEBUG { if (DEBUG_ON) { print "DEBUG >>> ", sprintf(shift, @_), "\n" }}
16              
17             #------------------------------------------------------------------------------
18             # This import serves multiple roles:
19             # - ::Inline module's proxy to Inline.pm
20             # - Makefile.PL postamble
21             # - Makefile rule support
22             #------------------------------------------------------------------------------
23             sub import {
24 1     1   8 my $class = shift;
25 1         1 DEBUG_ON && DEBUG "$class->import(@_)";
26              
27 1         3 my ($stub_module, $program) = caller;
28 1         6 $program =~ s!.*[\\\/]!!;
29              
30 1 50 33     6 if ($program eq "Makefile.PL" and not -e 'INLINE.h') {
    50          
31 0         0 $class->check_inc_inc($program);
32 1     1   7 no warnings 'once';
  1         2  
  1         708  
33 0         0 *MY::postamble = \&postamble;
34 0         0 return;
35             }
36             elsif ($program eq 'Build.PL') {
37 0         0 $class->check_inc_inc($program);
38 0         0 return;
39             }
40              
41 1 50       15 return unless @_;
42 0           my $cmd = shift;
43              
44 0 0         return $class->handle_stub($stub_module, @_)
45             if $cmd eq 'stub';
46 0 0         return $class->handle_makestub(@_)
47             if $cmd eq 'makestub';
48 0 0         return $class->handle_distdir(@ARGV)
49             if $cmd eq 'distdir';
50 0 0         return $class->handle_fixblib()
51             if $cmd eq 'fixblib';
52              
53             # TODO: Deprecated 12/26/2014. Remove this in a month.
54 0 0         die "Inline::Module 'autostub' no longer supported. " .
55             "Remove this option from PERL5OPT."
56             if $cmd eq 'autostub';
57              
58 0           die "Unknown Inline::Module::import argument '$cmd'"
59             }
60              
61             sub check_api_version {
62 0     0 0   my ($class, $stub_module, $api_version) = @_;
63 0 0         if ($api_version ne $API_VERSION) {
64 0           warn <<"...";
65             It seems that '$stub_module' is out of date.
66             It is using Inline::Module API version '$api_version'.
67             You have Inline::Module API version '$API_VERSION' installed.
68              
69             Make sure you have the latest version of Inline::Module installed, then run:
70              
71             perl -MInline::Module=makestub,$stub_module
72              
73             ...
74             # XXX 'exit' is used to get a cleaner error msg.
75             # Try to redo this without 'exit'.
76 0           exit 1;
77             }
78             }
79              
80             sub check_inc_inc {
81 0     0 0   my ($class, $program) = @_;
82 0 0         my $first = $INC[0] or die;
83 0 0         if ($first !~ /^(\.[\/\\])?inc[\/\\]?$/) {
84 0           die <<"...";
85             First element of \@INC should be 'inc'.
86             It's '$first'.
87             Add this line to the top of your '$program':
88              
89             use lib 'inc';
90              
91             ...
92             }
93             }
94              
95             sub importer {
96 0     0 0   my ($class, $stub_module) = @_;
97             return sub {
98 0     0     my ($class, $lang) = @_;
99 0 0         return unless defined $lang;
100 0           require File::Path;
101 0 0         File::Path::mkpath($inline_build_path)
102             unless -d $inline_build_path;
103 0           require Inline;
104 0 0         Inline->import(
105             Config =>
106             directory => $inline_build_path,
107             ($lang eq 'C') ? (using => 'Inline::C::Parser::RegExp') : (),
108             name => $stub_module,
109             CLEAN_AFTER_BUILD => 0,
110             );
111 0           shift(@_);
112 0           DEBUG_ON && DEBUG "Inline::Module::importer proxy to Inline::%s", @_;
113 0           Inline->import_heavy(@_);
114 0           };
115             }
116              
117             #------------------------------------------------------------------------------
118             # The postamble method:
119             #------------------------------------------------------------------------------
120             sub postamble {
121 0     0 1   my ($makemaker, %args) = @_;
122 0           DEBUG_ON && DEBUG "Inline::Module::postamble(${\join', ',@_})";
123              
124 0 0         my $meta = $args{inline}
125             or croak "'postamble' section requires 'inline' key in Makefile.PL";
126 0 0         croak "postamble 'inline' section requires 'module' key in Makefile.PL"
127             unless $meta->{module};
128              
129 0           my $class = __PACKAGE__;
130 0           $class->default_meta($meta);
131              
132 0           my $code_modules = $meta->{module};
133 0           my $stub_modules = $meta->{stub};
134 0           my $included_modules = $class->included_modules($meta);
135              
136 0 0 0       if ($meta->{makestub} and not -e 'inc' and not -e 'INLINE.h') {
      0        
137 0           $class->make_stub_modules(@{$meta->{stub}});
  0            
138             }
139              
140 0           my $section = <<"...";
141             clean ::
142             \t- \$(RM_RF) $inline_build_path
143              
144             distdir : distdir_inline
145              
146             distdir_inline : create_distdir
147             \t\$(NOECHO) \$(ABSPERLRUN) -MInline::Module=distdir -e 1 -- \$(DISTVNAME) @$stub_modules -- @$included_modules
148              
149             pure_all ::
150             ...
151 0           for my $module (@$code_modules) {
152 0           $section .=
153             "\t\$(NOECHO) \$(ABSPERLRUN) -Iinc -Ilib -M$module -e 1 --\n";
154             }
155             $section .=
156 0           "\t\$(NOECHO) \$(ABSPERLRUN) -Iinc -MInline::Module=fixblib -e 1 --\n";
157              
158 0           return $section;
159             }
160              
161             #------------------------------------------------------------------------------
162             # The handle methods.
163             #------------------------------------------------------------------------------
164             sub handle_stub {
165 0     0 0   my ($class, $stub_module, $api_version) = @_;
166 0           DEBUG_ON && DEBUG "$class->handle_stub($stub_module, $api_version)";
167 0           $class->check_api_version($stub_module, $api_version);
168 1     1   4 no strict 'refs';
  1         2  
  1         1803  
169 0           *{"${stub_module}::import"} = $class->importer($stub_module);
  0            
170 0           return;
171             }
172              
173             sub handle_makestub {
174 0     0 0   my ($class, @args) = @_;
175 0           DEBUG_ON && DEBUG "$class->handle_makestub(${\join', ',@args})";
176              
177 0           my @modules;
178 0           for my $arg (@args) {
179 0 0         if ($arg =~ /::/) {
180 0           push @modules, $arg;
181             }
182             else {
183 0           croak "Unknown 'makestub' argument: '$arg'";
184             }
185             }
186              
187 0           $class->make_stub_modules(@modules);
188              
189 0           exit 0;
190             }
191              
192             sub handle_distdir {
193 0     0 0   my ($class, $distdir, @args) = @_;
194 0           DEBUG_ON && DEBUG "$class->handle_distdir($distdir, ${\join', ',@args})";
195 0           my $stub_modules = [];
196 0           my $included_modules = [];
197              
198 0   0       while (@args and ($_ = shift(@args)) ne '--') {
199 0           push @$stub_modules, $_;
200             }
201 0   0       while (@args and ($_ = shift(@args)) ne '--') {
202 0           push @$included_modules, $_;
203             }
204 0           $class->add_to_distdir($distdir, $stub_modules, $included_modules);
205             }
206              
207             sub handle_fixblib {
208 0     0 0   my ($class) = @_;
209 0           DEBUG_ON && DEBUG "$class->handle_fixblib()";
210 0           my $ext = $Config::Config{dlext};
211 0 0         -d 'blib'
212             or die "Inline::Module::fixblib expected to find 'blib' directory";
213             File::Find::find({
214             wanted => sub {
215 0 0   0     -f or return;
216 0 0         if (m!^($inline_build_path/lib/auto/.*)\.$ext$!) {
217 0           my $blib_ext = $_;
218 0 0         $blib_ext =~ s!^$inline_build_path/lib!blib/arch! or die;
219 0           my $blib_ext_dir = $blib_ext;
220 0 0         $blib_ext_dir =~ s!(.*)/.*!$1! or die;
221 0           File::Path::mkpath $blib_ext_dir;
222 0           link $_, $blib_ext;
223             }
224             },
225 0           no_chdir => 1,
226             }, $inline_build_path);
227             }
228              
229             #------------------------------------------------------------------------------
230             # Worker methods.
231             #------------------------------------------------------------------------------
232             sub default_meta {
233 0     0 0   my ($class, $meta) = @_;
234 0 0         defined $meta->{module}
235             or die "Meta 'module' not defined";
236 0 0         $meta->{module} = [ $meta->{module} ] unless ref $meta->{module};
237 0   0       $meta->{stub} ||= [ map "${_}::Inline", @{$meta->{module}} ];
  0            
238 0 0         $meta->{stub} = [ $meta->{stub} ] unless ref $meta->{stub};
239 0   0       $meta->{ilsm} ||= 'Inline::C';
240 0 0         $meta->{ilsm} = [ $meta->{ilsm} ] unless ref $meta->{ilsm};
241 0 0         $meta->{bundle} = 1 unless defined $meta->{bundle};
242             }
243              
244             sub included_modules {
245 0     0 0   my ($class, $meta) = @_;
246 0           DEBUG_ON && DEBUG "$class->included_modules($meta)";
247 0 0         return [] if not $meta->{bundle};
248 0           my $ilsm = $meta->{ilsm};
249 0           my $include = [
250             'Inline',
251             'Inline::denter',
252             'Inline::Module',
253             @$ilsm,
254             ];
255 0 0         if (caller eq 'Module::Build::InlineModule') {
256 0           push @$include, 'Module::Build::InlineModule';
257             }
258 0 0         if (grep /:C$/, @$ilsm) {
259 0           push @$include,
260             'Inline::C::Parser::RegExp';
261             }
262 0 0         if (grep /:CPP$/, @$ilsm) {
263 0           push @$include, (
264             'Inline::C',
265             'Inline::CPP::Config',
266             'Inline::CPP::Parser::RecDescent',
267             'Parse::RecDescent',
268             'ExtUtils::CppGuess',
269             'Capture::Tiny',
270             );
271             }
272 0           return $include;
273             }
274              
275             sub add_to_distdir {
276 0     0 0   my ($class, $distdir, $stub_modules, $included_modules) = @_;
277 0           DEBUG_ON && DEBUG "$class->add_to_distdir($distdir) [@$stub_modules] [@$included_modules]";
278 0           my $manifest = []; # files created under distdir
279 0           for my $module (@$stub_modules) {
280 0           my $code = $class->dyna_module($module);
281 0           $class->write_module("$distdir/lib", $module, $code);
282 0           $code = $class->proxy_module($module);
283 0           $class->write_module("$distdir/inc", $module, $code);
284 0           $module =~ s!::!/!g;
285 0 0         push @$manifest, "lib/$module.pm"
286             unless -e "lib/$module.pm";
287 0           push @$manifest, "inc/$module.pm";
288             }
289 0           for my $module (@$included_modules) {
290 0 0         my $code = $module eq 'Inline::CPP::Config'
291             ? $class->read_share_cpp_config
292             : $class->read_local_module($module);
293 0           $class->write_module("$distdir/inc", $module, $code);
294 0           $module =~ s!::!/!g;
295 0           push @$manifest, "inc/$module.pm";
296             }
297              
298 0           $class->add_to_manifest($distdir, @$manifest);
299              
300 0           return $manifest; # return a list of the files added
301             }
302              
303             sub make_stub_modules {
304 0     0 0   my ($class, @modules) = @_;
305 0           DEBUG_ON && DEBUG "$class->make_stub_modules(@modules)";
306              
307 0           for my $module (@modules) {
308 0           my $code = $class->proxy_module($module);
309 0           my $path = $class->write_module('lib', $module, $code, 'onchange');
310 0 0         if ($path) {
311 0           print "Created stub module '$path' (Inline::Module $VERSION)\n";
312             }
313             }
314             }
315              
316             sub read_local_module {
317 0     0 0   my ($class, $module) = @_;
318 0 0         eval "require $module; 1" or die $@;
319 0           my $file = $module;
320 0           $file =~ s!::!/!g;
321 0           $class->read_file($INC{"$file.pm"});
322             }
323              
324             sub read_share_cpp_config {
325 0     0 0   my ($class) = @_;
326 0           require File::Share;
327 0           my $dir = File::Share::dist_dir('Inline-Module');
328 0           my $path = File::Spec->catfile($dir, 'CPPConfig.pm');
329 0           $class->read_file($path);
330             }
331              
332             sub proxy_module {
333 0     0 0   my ($class, $module) = @_;
334 0           DEBUG_ON && DEBUG "$class->proxy_module($module)";
335              
336 0           return <<"...";
337             # DO NOT EDIT. GENERATED BY: Inline::Module
338             #
339             # This module is for author-side development only. When this module is shipped
340             # to CPAN, it will be automagically replaced with content that does not
341             # require any Inline framework modules (or any other non-core modules).
342             #
343             # To regenerate this stub module, run this command:
344             #
345             # perl -MInline::Module=makestub,$module
346              
347             use strict; use warnings;
348             package $module;
349             use Inline::Module stub => '$API_VERSION';
350             1;
351             ...
352             }
353              
354             sub dyna_module {
355 0     0 0   my ($class, $module) = @_;
356 0           DEBUG_ON && DEBUG "$class->dyna_module($module)";
357 0           return <<"...";
358             # DO NOT EDIT. GENERATED BY: Inline::Module $Inline::Module::VERSION
359              
360             use strict; use warnings;
361             package $module;
362             use base 'DynaLoader';
363             bootstrap $module;
364             1;
365             ...
366              
367             # TODO: Add XS VERSION checking support:
368             # our \$VERSION = '0.0.5';
369             # bootstrap $module \$VERSION;
370             }
371              
372             sub read_file {
373 0     0 0   my ($class, $filepath) = @_;
374 0           DEBUG_ON && DEBUG "$class->read_file($filepath)";
375 0 0         open IN, '<', $filepath
376             or die "Can't open '$filepath' for input:\n$!";
377 0           my $code = do {local $/; };
  0            
  0            
378 0           close IN;
379 0           return $code;
380             }
381              
382             sub write_module {
383 0     0 0   my $class = shift;
384 0           my ($dest, $module, $code, $onchange) = @_;
385 0           DEBUG_ON && DEBUG "$class->write_module($dest, $module, ..., $onchange)";
386 0   0       $onchange ||= 0;
387              
388 0           $code =~ s/\n+__END__\n.*//s;
389              
390 0           my $filepath = $module;
391 0           $filepath =~ s!::!/!g;
392 0           $filepath = "$dest/$filepath.pm";
393 0           my $dirpath = $filepath;
394 0           $dirpath =~ s!(.*)/.*!$1!;
395 0           File::Path::mkpath($dirpath);
396              
397 0 0 0       return if $onchange and
      0        
398             -e $filepath and
399             $class->read_file($filepath) eq $code;
400              
401 0           unlink $filepath;
402 0 0         open OUT, '>', $filepath
403             or die "Can't open '$filepath' for output:\n$!";
404 0           print OUT $code;
405 0           close OUT;
406              
407 0           return $filepath;
408             }
409              
410             sub add_to_manifest {
411 0     0 0   my ($class, $distdir, @files) = @_;
412 0           DEBUG_ON && DEBUG "$class->add_to_manifest($distdir) (@files)";
413 0           my $manifest = "$distdir/MANIFEST";
414              
415 0 0         if (-w $manifest) {
416 0 0         open my $out, '>>', $manifest
417             or die "Can't open '$manifest' for append:\n$!";
418 0           for my $file (@files) {
419 0           print $out "$file\n";
420             }
421 0           close $out;
422             }
423             }
424              
425             sub smoke_system_info_dump {
426 0     0 0   my ($class, @msg) = @_;
427 0           my $msg = sprintf(@msg);
428 0           chomp $msg;
429 0           require Data::Dumper;
430 0           local $Data::Dumper::Sortkeys = 1;
431 0           local $Data::Dumper::Terse = 1;
432 0           local $Data::Dumper::Indent = 1;
433              
434 0           my @path_files;
435             File::Find::find({
436             wanted => sub {
437 0 0   0     push @path_files, $File::Find::name if -f;
438             },
439 0           }, File::Spec->path());
440 0           my $dump = Data::Dumper::Dumper(
441             {
442             'ENV' => \%ENV,
443             'Config' => \%Config::Config,
444             'Path Files' => \@path_files,
445             },
446             );
447 0           Carp::confess <<"..."
448             Error: $msg
449              
450             System Data:
451             $dump
452              
453             Error: $msg
454             ...
455             }
456              
457             1;