File Coverage

blib/lib/Inline/Module.pm
Criterion Covered Total %
statement 36 215 16.7
branch 4 86 4.6
condition 1 27 3.7
subroutine 11 35 31.4
pod 1 21 4.7
total 53 384 13.8


line stmt bran cond sub pod time code
1 2     2   1359 use strict; use warnings;
  2     2   4  
  2         69  
  2         8  
  2         3  
  2         139  
2             package Inline::Module;
3             our $VERSION = '0.32';
4             our $API_VERSION = 'v2';
5              
6 2     2   16 use Carp 'croak';
  2         2  
  2         121  
7 2     2   9 use Config();
  2         2  
  2         24  
8 2     2   7 use File::Find();
  2         2  
  2         20  
9 2     2   9 use File::Path();
  2         3  
  2         21  
10 2     2   7 use File::Spec();
  2         2  
  2         67  
11              
12             my $inline_build_path = '.inline';
13              
14 2 50   2   8 use constant DEBUG_ON => $ENV{PERL_INLINE_MODULE_DEBUG} ? 1 : 0;
  2         2  
  2         367  
15 0     0 0 0 sub DEBUG { if (DEBUG_ON) { print "DEBUG >>> ", sprintf(@_), "\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 2     2   16 my $class = shift;
25 2         3 DEBUG_ON && DEBUG "Inline::Module::import(@_)";
26              
27 2         7 my ($stub_module, $program) = caller;
28 2         11 $program =~ s!.*[\\\/]!!;
29              
30 2 50 33     16 if ($program eq "Makefile.PL" and not -e 'INLINE.h') {
    50          
31 0         0 $class->check_inc_inc($program);
32 2     2   8 no warnings 'once';
  2         2  
  2         1186  
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 2 50       24 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              
123 0 0         my $meta = $args{inline}
124             or croak "'postamble' section requires 'inline' key in Makefile.PL";
125 0 0         croak "postamble 'inline' section requires 'module' key in Makefile.PL"
126             unless $meta->{module};
127              
128 0           my $class = __PACKAGE__;
129 0           $class->default_meta($meta);
130              
131 0           my $code_modules = $meta->{module};
132 0           my $stub_modules = $meta->{stub};
133 0           my $included_modules = $class->included_modules($meta);
134              
135 0 0 0       if ($meta->{makestub} and not -e 'inc' and not -e 'INLINE.h') {
      0        
136 0           $class->make_stub_modules(@{$meta->{stub}});
  0            
137             }
138              
139 0           my $section = <<"...";
140             clean ::
141             \t- \$(RM_RF) $inline_build_path
142              
143             distdir : distdir_inline
144              
145             distdir_inline : create_distdir
146             \t\$(NOECHO) \$(ABSPERLRUN) -MInline::Module=distdir -e 1 -- \$(DISTVNAME) @$stub_modules -- @$included_modules
147              
148             pure_all ::
149             ...
150 0           for my $module (@$code_modules) {
151 0           $section .=
152             "\t\$(NOECHO) \$(ABSPERLRUN) -Iinc -Ilib -M$module -e 1 --\n";
153             }
154             $section .=
155 0           "\t\$(NOECHO) \$(ABSPERLRUN) -Iinc -MInline::Module=fixblib -e 1 --\n";
156              
157 0           return $section;
158             }
159              
160             #------------------------------------------------------------------------------
161             # The handle methods.
162             #------------------------------------------------------------------------------
163             sub handle_stub {
164 0     0 0   my ($class, $stub_module, $api_version) = @_;
165 0           $class->check_api_version($stub_module, $api_version);
166 2     2   9 no strict 'refs';
  2         3  
  2         2925  
167 0           *{"${stub_module}::import"} = $class->importer($stub_module);
  0            
168 0           return;
169             }
170              
171             sub handle_makestub {
172 0     0 0   my ($class, @args) = @_;
173              
174 0           my @modules;
175 0           for my $arg (@args) {
176 0 0         if ($arg =~ /::/) {
177 0           push @modules, $arg;
178             }
179             else {
180 0           croak "Unknown 'makestub' argument: '$arg'";
181             }
182             }
183              
184 0           $class->make_stub_modules(@modules);
185              
186 0           exit 0;
187             }
188              
189             sub handle_distdir {
190 0     0 0   my ($class, $distdir, @args) = @_;
191 0           my $stub_modules = [];
192 0           my $included_modules = [];
193              
194 0   0       while (@args and ($_ = shift(@args)) ne '--') {
195 0           push @$stub_modules, $_;
196             }
197 0   0       while (@args and ($_ = shift(@args)) ne '--') {
198 0           push @$included_modules, $_;
199             }
200 0           $class->add_to_distdir($distdir, $stub_modules, $included_modules);
201             }
202              
203             sub handle_fixblib {
204 0     0 0   my ($class) = @_;
205 0           my $ext = $Config::Config{dlext};
206 0 0         -d 'blib'
207             or die "Inline::Module::fixblib expected to find 'blib' directory";
208             File::Find::find({
209             wanted => sub {
210 0 0   0     -f or return;
211 0 0         if (m!^($inline_build_path/lib/auto/.*)\.$ext$!) {
212 0           my $blib_ext = $_;
213 0 0         $blib_ext =~ s!^$inline_build_path/lib!blib/arch! or die;
214 0           my $blib_ext_dir = $blib_ext;
215 0 0         $blib_ext_dir =~ s!(.*)/.*!$1! or die;
216 0           File::Path::mkpath $blib_ext_dir;
217 0           link $_, $blib_ext;
218             }
219             },
220 0           no_chdir => 1,
221             }, $inline_build_path);
222             }
223              
224             #------------------------------------------------------------------------------
225             # Worker methods.
226             #------------------------------------------------------------------------------
227             sub default_meta {
228 0     0 0   my ($class, $meta) = @_;
229 0 0         defined $meta->{module}
230             or die "Meta 'module' not defined";
231 0 0         $meta->{module} = [ $meta->{module} ] unless ref $meta->{module};
232 0   0       $meta->{stub} ||= [ map "${_}::Inline", @{$meta->{module}} ];
  0            
233 0 0         $meta->{stub} = [ $meta->{stub} ] unless ref $meta->{stub};
234 0   0       $meta->{ilsm} ||= 'Inline::C';
235 0 0         $meta->{ilsm} = [ $meta->{ilsm} ] unless ref $meta->{ilsm};
236 0 0         $meta->{bundle} = 1 unless defined $meta->{bundle};
237             }
238              
239             sub included_modules {
240 0     0 0   my ($class, $meta) = @_;
241 0 0         return [] if not $meta->{bundle};
242 0           my $ilsm = $meta->{ilsm};
243 0           my $include = [
244             'Inline',
245             'Inline::denter',
246             'Inline::Module',
247             @$ilsm,
248             ];
249 0 0         if (caller eq 'Module::Build::InlineModule') {
250 0           push @$include, 'Module::Build::InlineModule';
251             }
252 0 0         if (grep /:C$/, @$ilsm) {
253 0           push @$include,
254             'Inline::C::Parser::RegExp';
255             }
256 0 0         if (grep /:CPP$/, @$ilsm) {
257 0           push @$include, (
258             'Inline::C',
259             'Inline::CPP::Config',
260             'Inline::CPP::Parser::RecDescent',
261             'Parse::RecDescent',
262             'ExtUtils::CppGuess',
263             'Capture::Tiny',
264             );
265             }
266 0           return $include;
267             }
268              
269             sub add_to_distdir {
270 0     0 0   my ($class, $distdir, $stub_modules, $included_modules) = @_;
271 0           my $manifest = []; # files created under distdir
272 0           for my $module (@$stub_modules) {
273 0           my $code = $class->dyna_module($module);
274 0           $class->write_module("$distdir/lib", $module, $code);
275 0           $code = $class->proxy_module($module);
276 0           $class->write_module("$distdir/inc", $module, $code);
277 0           $module =~ s!::!/!g;
278 0 0         push @$manifest, "lib/$module.pm"
279             unless -e "lib/$module.pm";
280 0           push @$manifest, "inc/$module.pm";
281             }
282 0           for my $module (@$included_modules) {
283 0 0         my $code = $module eq 'Inline::CPP::Config'
284             ? $class->read_share_cpp_config
285             : $class->read_local_module($module);
286 0           $class->write_module("$distdir/inc", $module, $code);
287 0           $module =~ s!::!/!g;
288 0           push @$manifest, "inc/$module.pm";
289             }
290              
291 0           $class->add_to_manifest($distdir, @$manifest);
292              
293 0           return $manifest; # return a list of the files added
294             }
295              
296             sub make_stub_modules {
297 0     0 0   my ($class, @modules) = @_;
298              
299 0           for my $module (@modules) {
300 0           my $code = $class->proxy_module($module);
301 0           my $path = $class->write_module('lib', $module, $code, 'onchange');
302 0 0         if ($path) {
303 0           print "Created stub module '$path' (Inline::Module $VERSION)\n";
304             }
305             }
306             }
307              
308             sub read_local_module {
309 0     0 0   my ($class, $module) = @_;
310 0 0         eval "require $module; 1" or die $@;
311 0           my $file = $module;
312 0           $file =~ s!::!/!g;
313 0           $class->read_file($INC{"$file.pm"});
314             }
315              
316             sub read_share_cpp_config {
317 0     0 0   my ($class) = @_;
318 0           require File::Share;
319 0           my $dir = File::Share::dist_dir('Inline-Module');
320 0           my $path = File::Spec->catfile($dir, 'CPPConfig.pm');
321 0           $class->read_file($path);
322             }
323              
324             sub proxy_module {
325 0     0 0   my ($class, $module) = @_;
326              
327 0           return <<"...";
328             # DO NOT EDIT. GENERATED BY: Inline::Module
329             #
330             # This module is for author-side development only. When this module is shipped
331             # to CPAN, it will be automagically replaced with content that does not
332             # require any Inline framework modules (or any other non-core modules).
333             #
334             # To regenerate this stub module, run this command:
335             #
336             # perl -MInline::Module=makestub,$module
337              
338             use strict; use warnings;
339             package $module;
340             use Inline::Module stub => '$API_VERSION';
341             1;
342             ...
343             }
344              
345             sub dyna_module {
346 0     0 0   my ($class, $module) = @_;
347 0           return <<"...";
348             # DO NOT EDIT. GENERATED BY: Inline::Module $Inline::Module::VERSION
349              
350             use strict; use warnings;
351             package $module;
352             use base 'DynaLoader';
353             bootstrap $module;
354             1;
355             ...
356              
357             # TODO: Add XS VERSION checking support:
358             # our \$VERSION = '0.0.5';
359             # bootstrap $module \$VERSION;
360             }
361              
362             sub read_file {
363 0     0 0   my ($class, $filepath) = @_;
364 0 0         open IN, '<', $filepath
365             or die "Can't open '$filepath' for input:\n$!";
366 0           my $code = do {local $/; };
  0            
  0            
367 0           close IN;
368 0           return $code;
369             }
370              
371             sub write_module {
372 0     0 0   my ($class, $dest, $module, $code, $onchange) = @_;
373 0   0       $onchange ||= 0;
374              
375 0           $code =~ s/\n+__END__\n.*//s;
376              
377 0           my $filepath = $module;
378 0           $filepath =~ s!::!/!g;
379 0           $filepath = "$dest/$filepath.pm";
380 0           my $dirpath = $filepath;
381 0           $dirpath =~ s!(.*)/.*!$1!;
382 0           File::Path::mkpath($dirpath);
383              
384 0 0 0       return if $onchange and
      0        
385             -e $filepath and
386             $class->read_file($filepath) eq $code;
387              
388 0           unlink $filepath;
389 0 0         open OUT, '>', $filepath
390             or die "Can't open '$filepath' for output:\n$!";
391 0           print OUT $code;
392 0           close OUT;
393              
394 0           return $filepath;
395             }
396              
397             sub add_to_manifest {
398 0     0 0   my ($class, $distdir, @files) = @_;
399 0           my $manifest = "$distdir/MANIFEST";
400              
401 0 0         if (-w $manifest) {
402 0 0         open my $out, '>>', $manifest
403             or die "Can't open '$manifest' for append:\n$!";
404 0           for my $file (@files) {
405 0           print $out "$file\n";
406             }
407 0           close $out;
408             }
409             }
410              
411             sub smoke_system_info_dump {
412 0     0 0   my ($class, @msg) = @_;
413 0           my $msg = sprintf(@msg);
414 0           chomp $msg;
415 0           require Data::Dumper;
416 0           local $Data::Dumper::Sortkeys = 1;
417 0           local $Data::Dumper::Terse = 1;
418 0           local $Data::Dumper::Indent = 1;
419              
420 0           my @path_files;
421             File::Find::find({
422             wanted => sub {
423 0 0   0     push @path_files, $File::Find::name if -f;
424             },
425 0           }, File::Spec->path());
426 0           my $dump = Data::Dumper::Dumper(
427             {
428             'ENV' => \%ENV,
429             'Config' => \%Config::Config,
430             'Path Files' => \@path_files,
431             },
432             );
433 0           Carp::confess <<"..."
434             Error: $msg
435              
436             System Data:
437             $dump
438              
439             Error: $msg
440             ...
441             }
442              
443             1;