File Coverage

blib/lib/Panda/Install.pm
Criterion Covered Total %
statement 281 401 70.0
branch 81 182 44.5
condition 60 145 41.3
subroutine 31 36 86.1
pod 2 15 13.3
total 455 779 58.4


line stmt bran cond sub pod time code
1             package Panda::Install;
2 12     12   618772 use strict;
  12         111  
  12         286  
3 12     12   50 use warnings;
  12         17  
  12         228  
4 12     12   47 use Config;
  12         16  
  12         357  
5 12     12   51 use Cwd 'abs_path';
  12         20  
  12         486  
6 12     12   56 use Exporter 'import';
  12         18  
  12         269  
7 12     12   2752 use Panda::Install::Payload;
  12         30  
  12         32227  
8              
9             our $VERSION = '1.2.9';
10              
11             our @EXPORT_OK = qw/write_makefile makemaker_args/;
12             our @EXPORT;
13              
14             if ($0 =~ /Makefile.PL$/) {
15             @EXPORT = qw/write_makefile makemaker_args/;
16             _require_makemaker();
17             }
18              
19             my $xs_mask = '*.xs';
20             my $xsi_mask = '*.xsi';
21             my $c_mask = '*.c *.cc *.cpp *.cxx';
22             my $h_mask = '*.h *.hh *.hpp *.hxx';
23             my $map_mask = '*.map';
24             my $win32 = $^O eq 'MSWin32';
25              
26             sub write_makefile {
27 0     0 1 0 _require_makemaker();
28 0         0 WriteMakefile(makemaker_args(@_));
29             }
30              
31             sub makemaker_args {
32 34     34 1 96173 my %params = @_;
33 34         149 _sync();
34            
35 34   50     256 $params{MIN_PERL_VERSION} ||= '5.10.0';
36            
37 34         70 my $postamble = $params{postamble};
38 34 50 33     125 $postamble = {my => $postamble} if $postamble and !ref($postamble);
39 34   50     184 $postamble ||= {};
40 34 50       153 $postamble->{my} = '' unless defined $postamble->{my};
41 34         73 $params{postamble} = $postamble;
42            
43 34         161 _string_merge($params{CCFLAGS}, '-o $@');
44              
45 34 50       121 die "You must define a NAME param" unless $params{NAME};
46 34 50 66     235 unless ($params{ALL_FROM} || $params{VERSION_FROM} || $params{ABSTRACT_FROM}) {
      33        
47 33         67 my $name = $params{NAME};
48 33         110 $name =~ s#::#/#g;
49 33         115 $params{ALL_FROM} = "lib/$name.pm";
50             }
51            
52 34 50       112 if (my $package_file = delete $params{ALL_FROM}) {
53 34         75 $params{VERSION_FROM} = $package_file;
54 34         68 $params{ABSTRACT_FROM} = $package_file;
55             }
56              
57 34   50     171 $params{CONFIGURE_REQUIRES} ||= {};
58 34   50     176 $params{CONFIGURE_REQUIRES}{'ExtUtils::MakeMaker'} ||= '6.76';
59 34   33     167 $params{CONFIGURE_REQUIRES}{'Panda::Install'} ||= $VERSION;
60            
61 34   50     173 $params{BUILD_REQUIRES} ||= {};
62 34   50     165 $params{BUILD_REQUIRES}{'ExtUtils::MakeMaker'} ||= '6.76';
63 34   50     152 $params{BUILD_REQUIRES}{'ExtUtils::ParseXS'} ||= '3.24';
64            
65 34   50     155 $params{TEST_REQUIRES} ||= {};
66 34   50     174 $params{TEST_REQUIRES}{'Test::Simple'} ||= '0.96';
67 34   50     151 $params{TEST_REQUIRES}{'Test::More'} ||= 0;
68 34   50     172 $params{TEST_REQUIRES}{'Test::Deep'} ||= 0;
69            
70 34   50     171 $params{PREREQ_PM} ||= {};
71 34   33     169 $params{PREREQ_PM}{'Panda::Install'} ||= $VERSION; # needed at runtime because it has payload_dir and xsloader
72            
73 34   50     154 $params{clean} ||= {};
74 34   50     176 $params{clean}{FILES} ||= '';
75            
76 34 50 66     102 delete $params{BIN_SHARE} if $params{BIN_SHARE} and !%{$params{BIN_SHARE}};
  1         4  
77            
78             {
79 34         63 my $val = $params{SRC};
80 34 100 100     93 $val = [$val] if $val and ref($val) ne 'ARRAY';
81 34         88 $params{SRC} = $val;
82             }
83             {
84 34         52 my $val = $params{XS};
  34         52  
  34         61  
85 34 100 100     109 $val = [$val] if $val and ref($val) ne 'ARRAY' and ref($val) ne 'HASH';
      100        
86 34         84 $params{XS} = $val;
87             }
88            
89 34 50 33     102 $params{TYPEMAPS} = [$params{TYPEMAPS}] if $params{TYPEMAPS} and ref($params{TYPEMAPS}) ne 'ARRAY';
90            
91 34   50     147 my $module_info = Panda::Install::Payload::module_info($params{NAME}) || {};
92 34         139 $params{MODULE_INFO} = {BIN_DEPENDENT => $module_info->{BIN_DEPENDENT}};
93            
94 34         147 process_XS(\%params);
95 34         124 process_PM(\%params);
96 34         165 process_C(\%params);
97 34         122 process_OBJECT(\%params);
98 34         125 process_H(\%params);
99 34         117 process_XSI(\%params);
100 34         126 process_CLIB(\%params);
101 34         108 process_PAYLOAD(\%params);
102 34         116 process_BIN_DEPS(\%params);
103 34         113 process_BIN_SHARE(\%params);
104 34         119 attach_BIN_DEPENDENT(\%params);
105 34         115 warn_BIN_DEPENDENT(\%params);
106              
107 34 100       110 if (my $use_cpp = delete $params{CPLUS}) {
108 3   100     10 $params{CC} ||= 'c++';
109 3   50     14 $params{LD} ||= '$(CC)';
110 3         9 _string_merge($params{XSOPT}, '-C++');
111            
112 3         5 my $cppv = int($use_cpp);
113 3 100       14 _string_merge($params{CCFLAGS}, "-std=c++$cppv") if $cppv > 1;
114             }
115            
116             # inject Panda::Install::ParseXS into xsubpp
117 34         88 $postamble->{xsubpprun} = 'XSUBPPRUN = $(PERLRUN) -MPanda::Install::ParseXS $(XSUBPP)';
118 34   50     159 $params{LDFROM} ||= '$(OBJECT)';
119            
120 34         117 delete $params{$_} for qw/SRC/;
121 34 50       95 $params{OBJECT} = '$(O_FILES)' unless defined $params{OBJECT};
122            
123 34 50 33     125 if (my $shared_libs = $params{MODULE_INFO}{SHARED_LIBS} and $^O ne 'darwin') { # MacOSX doesn't allow for linking with bundles :(
124 0         0 my %seen;
125 0         0 @$shared_libs = grep {!$seen{$_}++} reverse @$shared_libs;
  0         0  
126 0 0       0 $params{LDFROM} .= ' '.join(' ', @$shared_libs) if @$shared_libs;
127             }
128            
129 34         79 delete $params{MODULE_INFO};
130              
131 34         908 $params{CCFLAGS} = "$Config{ccflags} $params{CCFLAGS}";
132              
133 34 50 66     178 if (!$params{C} || !@{$params{C}} and !$params{OBJECT} || !@{$params{OBJECT}} and !$params{XS} || !scalar(keys %{$params{XS}})) {
  34   33     132  
  1   66     6  
  1   33     4  
      33        
134 1         5 delete $params{$_} for qw/C H OBJECT XS CCFLAGS LDFROM/;
135             }
136            
137 34         598 return %params;
138             }
139              
140             sub process_PM {
141 34     34 0 59 my $params = shift;
142 34 50       97 return if $params->{PM}; # user-defined value overrides defaults
143            
144 34         103 my $instroot = _instroot($params);
145 34         145 my @name_parts = split '::', $params->{NAME};
146 34   50     215 $params->{PMLIBDIRS} ||= ['lib', $name_parts[-1]];
147 34         111 my $pm = $params->{PM} = {};
148            
149 34         71 foreach my $dir (@{$params->{PMLIBDIRS}}) {
  34         97  
150 68 100       608 next unless -d $dir;
151 34         101 foreach my $file (_scan_files('*.pm *.pl', $dir)) {
152 68         144 my $rel = $file;
153 68         621 $rel =~ s/^$dir//;
154 68         213 my $instpath = "$instroot/$rel";
155 68         326 $instpath =~ s#[/\\]{2,}#/#g;
156 68         231 $pm->{$file} = $instpath;
157             }
158             }
159             }
160              
161             sub process_XS {
162 34     34 0 71 my $params = shift;
163 34         93 my ($xs_files, @xs_list);
164 34 100       107 if ($params->{XS}) {
165 6 100       18 if (ref($params->{XS}) eq 'HASH') {
166 2         6 $xs_files = $params->{XS};
167             } else {
168 4         5 push @xs_list, @{_string_split_array($_)} for @{$params->{XS}};
  4         14  
  6         9  
169             }
170             } else {
171 28         97 @xs_list = _scan_files($xs_mask);
172             }
173 34         85 push @xs_list, _scan_files($xs_mask, $_) for @{$params->{SRC}};
  34         130  
174 34   100     202 $params->{XS} = $xs_files ||= {};
175 34         96 foreach my $xsfile (@xs_list) {
176 94         145 my $cfile = $xsfile;
177 94 50       465 $cfile =~ s/\.xs$/.c/ or next;
178 94         300 $xs_files->{$xsfile} = $cfile;
179             }
180             }
181              
182             sub process_C {
183 34     34 0 63 my $params = shift;
184 34 100       132 my $c_files = $params->{C} ? _string_split_array(delete $params->{C}) : [_scan_files($c_mask)];
185 34         96 push @$c_files, grep { !_includes($c_files, $_) } values %{$params->{XS}};
  94         197  
  34         156  
186 34         72 push @$c_files, _scan_files($c_mask, $_) for @{$params->{SRC}};
  34         94  
187 34         93 $params->{C} = $c_files;
188             }
189              
190             sub process_OBJECT {
191 34     34 0 60 my $params = shift;
192 34         117 my $o_files = _string_split_array(delete $params->{OBJECT});
193 34         57 foreach my $c_file (@{$params->{C}}) {
  34         88  
194 224         318 my $o_file = $c_file;
195 224         718 $o_file =~ s/\.[^.]+$//;
196 224         559 push @$o_files, $o_file.'$(OBJ_EXT)';
197             }
198 34         82 $params->{OBJECT} = $o_files;
199 34         103 $params->{clean}{FILES} .= ' $(O_FILES)';
200             }
201              
202             sub process_H {
203 34     34 0 53 my $params = shift;
204 34 100       128 my $h_files = $params->{H} ? _string_split_array(delete $params->{H}) : [_scan_files($h_mask)];
205 34         76 push @$h_files, _scan_files($h_mask, $_) for @{$params->{SRC}};
  34         102  
206 34         95 $params->{H} = $h_files;
207             }
208              
209             sub process_XSI { # make XS files rebuild if an XSI file changes
210 34     34 0 73 my $params = shift;
211 34         1001 my @xsi_files = glob($xsi_mask);
212 34         93 push @xsi_files, _scan_files($xsi_mask, $_) for @{$params->{SRC}};
  34         101  
213 34 50       116 $params->{postamble}{xsi} = '$(XS_FILES):: '.join(' ', @xsi_files).'; $(TOUCH) $(XS_FILES)'."\n" if @xsi_files;
214             }
215              
216             sub process_CLIB {
217 34     34 0 87 my $params = shift;
218 34         73 my $clibs = '';
219 34 50       125 my $clib = delete $params->{CLIB} or return;
220 0 0       0 $clib = [$clib] unless ref($clib) eq 'ARRAY';
221 0 0       0 return unless @$clib;
222            
223 0         0 foreach my $info (@$clib) {
224 0         0 my $build_cmd = $info->{BUILD_CMD};
225 0         0 my $clean_cmd = $info->{CLEAN_CMD};
226            
227 0 0       0 unless ($build_cmd) {
228 0         0 my $make = '$(MAKE)';
229 0 0 0     0 $make = 'gmake' if $info->{GMAKE} and $^O eq 'freebsd';
230 0   0     0 $info->{TARGET} ||= '';
231 0   0     0 $info->{FLAGS} ||= '';
232 0         0 $build_cmd = "$make $info->{FLAGS} $info->{TARGET}";
233 0         0 $clean_cmd = "$make clean";
234             }
235            
236 0         0 my $path = $info->{DIR}.'/'.$info->{FILE};
237 0         0 $clibs .= "$path ";
238            
239 0         0 $params->{postamble}{clib_build} .= "$path : ; cd $info->{DIR} && $build_cmd\n";
240 0 0       0 $params->{postamble}{clib_clean} .= "clean :: ; cd $info->{DIR} && $clean_cmd\n" if $clean_cmd;
241 0         0 push @{$params->{OBJECT}}, $path;
  0         0  
242             }
243 0         0 $params->{postamble}{clib_ldep} = "linkext:: $clibs";
244             }
245              
246             sub process_PAYLOAD {
247 34     34 0 66 my $params = shift;
248 34 100       111 my $payload = delete $params->{PAYLOAD} or return;
249 10         49 _process_map($payload, '*');
250 10         56 _install($params, $payload, 'payload');
251             }
252              
253             sub process_BIN_DEPS {
254 34     34 0 56 my $params = shift;
255 34 50       111 my $bin_deps = delete $params->{BIN_DEPS} or return;
256 0 0       0 $bin_deps = [$bin_deps] unless ref($bin_deps) eq 'ARRAY';
257 0   0     0 my $typemaps = $params->{TYPEMAPS} ||= [];
258 0         0 $params->{TYPEMAPS} = [];
259 0         0 _apply_BIN_DEPS($params, $_, {}) for @$bin_deps;
260 0         0 push @{$params->{TYPEMAPS}}, @{$typemaps};
  0         0  
  0         0  
261             }
262              
263             sub _apply_BIN_DEPS {
264 0     0   0 my ($params, $module, $seen) = @_;
265 0         0 my $stop_sharing;
266 0 0       0 $stop_sharing = 1 if $module =~ s/^-//;
267            
268 0 0       0 return if $seen->{$module}++;
269            
270 0         0 my $installed_version = Panda::Install::Payload::module_version($module);
271 0   0     0 $params->{CONFIGURE_REQUIRES}{$module} ||= $installed_version;
272 0   0     0 $params->{PREREQ_PM}{$module} ||= $installed_version;
273 0         0 $params->{MODULE_INFO}{BIN_DEPS}{$module} = $installed_version;
274            
275             # add so/dll to linker list
276 0   0     0 my $shared_list = $params->{MODULE_INFO}{SHARED_LIBS} ||= [];
277 0         0 my $module_path = $module;
278 0         0 $module_path =~ s#::#/#g;
279 0 0       0 die "SHOULDN'T EVER HAPPEN" unless $module =~ /([^:]+)$/;
280 0         0 my $module_last_name = $1;
281 0         0 foreach my $dir (@INC) {
282 0         0 my $lib_path = "$dir/auto/$module_path/$module_last_name.$Config{dlext}";
283 0 0       0 next unless -f $lib_path;
284 0         0 push @$shared_list, abs_path($lib_path);
285 0         0 last;
286             }
287            
288 0         0 my $info = Panda::Install::Payload::module_info($module);
289            
290 0 0       0 if ($info->{INCLUDE}) {
291 0         0 my $incdir = Panda::Install::Payload::include_dir($module);
292 0         0 _string_merge($params->{INC}, "-I$incdir");
293             }
294            
295 0         0 _string_merge($params->{INC}, $info->{INC});
296 0         0 _string_merge($params->{CCFLAGS}, $info->{CCFLAGS});
297 0         0 _string_merge($params->{DEFINE}, $info->{DEFINE});
298 0         0 _string_merge($params->{XSOPT}, $info->{XSOPT});
299            
300 0 0       0 if (my $add_libs = $info->{LIBS}) {{
301 0 0       0 last unless @$add_libs;
  0         0  
302 0 0       0 my $libs = $params->{LIBS} or last;
303 0 0       0 $libs = [$libs] unless ref($libs) eq 'ARRAY';
304 0 0 0     0 if ($libs and @$libs) {
305 0         0 my @result;
306 0         0 foreach my $l1 (@$libs) {
307 0         0 foreach my $l2 (@$add_libs) {
308 0         0 push @result, "$l1 $l2";
309             }
310             }
311 0         0 $params->{LIBS} = \@result;
312             }
313             else {
314 0         0 $params->{LIBS} = $add_libs;
315             }
316             }}
317            
318 0 0       0 if (my $passthrough = $info->{PASSTHROUGH}) {
319 0         0 _apply_BIN_DEPS($params, $_, $seen) for @$passthrough;
320             }
321            
322 0 0       0 if (my $typemaps = $info->{TYPEMAPS}) {
323 0         0 my $tm_dir = Panda::Install::Payload::typemap_dir($module);
324 0         0 foreach my $typemap (@$typemaps) {
325 0         0 my $tmfile = "$tm_dir/$typemap";
326 0         0 $tmfile =~ s#[/\\]{2,}#/#g;
327 0   0     0 push @{$params->{TYPEMAPS} ||= []}, $tmfile;
  0         0  
328             }
329             }
330            
331 0 0 0     0 $params->{CPLUS} = $info->{CPLUS} if $info->{CPLUS} and (!$params->{CPLUS} or $params->{CPLUS} < $info->{CPLUS});
      0        
332            
333 0 0 0     0 if (my $bin_share = $params->{BIN_SHARE} and !$stop_sharing) {
334 0   0     0 push @{$bin_share->{PASSTHROUGH} ||= []}, $module;
  0         0  
335             }
336             }
337              
338             sub process_BIN_SHARE {
339 34     34 0 61 my $params = shift;
340 34 100       103 my $bin_share = delete $params->{BIN_SHARE} or return;
341            
342 1   50     3 my $typemaps = delete($bin_share->{TYPEMAPS}) || {};
343 1         5 _process_map($typemaps, $map_mask);
344 1         4 _install($params, $typemaps, 'tm');
345 1 50       5 $bin_share->{TYPEMAPS} = [values %$typemaps] if scalar keys %$typemaps;
346            
347 1   50     4 my $include = delete($bin_share->{INCLUDE}) || {};
348 1         3 _process_map($include, $h_mask);
349 1         3 _install($params, $include, 'i');
350 1 50       5 $bin_share->{INCLUDE} = 1 if scalar(keys %$include);
351            
352 1 50 33     6 $bin_share->{LIBS} = [$bin_share->{LIBS}] if $bin_share->{LIBS} and ref($bin_share->{LIBS}) ne 'ARRAY';
353 1 50 33     4 $bin_share->{PASSTHROUGH} = [$bin_share->{PASSTHROUGH}] if $bin_share->{PASSTHROUGH} and ref($bin_share->{PASSTHROUGH}) ne 'ARRAY';
354            
355 1 50       4 if (my $list = $params->{MODULE_INFO}{BIN_DEPENDENT}) {
356 0 0       0 $bin_share->{BIN_DEPENDENT} = $list if @$list;
357             }
358            
359 1 50       4 if (my $vinfo = $params->{MODULE_INFO}{BIN_DEPS}) {
360 0 0       0 $bin_share->{BIN_DEPS} = $vinfo if %$vinfo;
361             }
362            
363 1 50       3 return unless %$bin_share;
364            
365             # generate info file
366 1         104 mkdir 'blib';
367 1         4 my $infopath = 'blib/info';
368 1         4 _module_info_write($infopath, $bin_share);
369            
370 1   50     5 my $pm = $params->{PM} ||= {};
371 1         4 $pm->{$infopath} = '$(INST_ARCHLIB)/$(FULLEXT).x/info';
372             }
373              
374             sub attach_BIN_DEPENDENT {
375 34     34 0 62 my $params = shift;
376 34 50       58 my @deps = keys %{$params->{MODULE_INFO}{BIN_DEPS} || {}};
  34         231  
377 34 50       141 return unless @deps;
378            
379             $params->{postamble}{sync_bin_deps} =
380 0         0 "sync_bin_deps:\n".
381             "\t\$(PERL) -MPanda::Install -e 'Panda::Install::cmd_sync_bin_deps()' $params->{NAME} @deps\n".
382             "install :: sync_bin_deps";
383             }
384              
385             sub warn_BIN_DEPENDENT {
386 34     34 0 62 my $params = shift;
387 34 50       107 return unless $params->{VERSION_FROM};
388 34         78 my $module = $params->{NAME};
389 34 50       105 my $list = $params->{MODULE_INFO}{BIN_DEPENDENT} or return;
390 0 0       0 return unless @$list;
391 0 0       0 my $installed_version = Panda::Install::Payload::module_version($module) or return;
392 0         0 my $mm = bless {}, 'MM';
393 0 0       0 my $new_version = $mm->parse_version($params->{VERSION_FROM}) or return;
394 0 0       0 return if $installed_version eq $new_version;
395 0         0 warn << "EOF";
396             ******************************************************************************
397             Panda::Install: There are XS modules that binary depend on current XS module $module.
398             They were built with currently installed $module version $installed_version.
399             If you install $module version $new_version, you will have to reinstall all XS modules that binary depend on it:
400             cpanm -f @$list
401             ******************************************************************************
402             EOF
403             }
404              
405             sub cmd_sync_bin_deps {
406 0     0 0 0 my $myself = shift @ARGV;
407 0         0 my @modules = @ARGV;
408 0         0 foreach my $module (@modules) {
409 0 0       0 my $info = Panda::Install::Payload::module_info($module) or next;
410 0   0     0 my $dependent = $info->{BIN_DEPENDENT} || [];
411 0         0 my %tmp = map {$_ => 1} grep {$_ ne $module} @$dependent;
  0         0  
  0         0  
412 0         0 $tmp{$myself} = 1;
413 0         0 $info->{BIN_DEPENDENT} = [sort keys %tmp];
414 0 0       0 delete $info->{BIN_DEPENDENT} unless @{$info->{BIN_DEPENDENT}};
  0         0  
415 0         0 my $file = Panda::Install::Payload::module_info_file($module);
416 0         0 _module_info_write($file, $info);
417             }
418             }
419              
420             sub _install {
421 12     12   44 my ($params, $map, $path) = @_;
422 12 50       50 return unless %$map;
423 12         38 my $xs = $params->{XS};
424 12         45 my $instroot = _instroot($params);
425 12   50     54 my $pm = $params->{PM} ||= {};
426 12         73 while (my ($source, $dest) = each %$map) {
427 29         111 my $instpath = "$instroot/\$(FULLEXT).x/$path/$dest";
428 29         100 $instpath =~ s#[/\\]{2,}#/#g;
429 29         148 $pm->{$source} = $instpath;
430             }
431             }
432              
433             sub _instroot {
434 46     46   98 my $params = shift;
435 46         94 my $xs = $params->{XS};
436 46 100 66     243 my $instroot = ($xs and %$xs) ? '$(INST_ARCHLIB)' : '$(INST_LIB)';
437 46         115 return $instroot;
438             }
439              
440             sub _sync {
441 12     12   91 no strict 'refs';
  12         35  
  12         8592  
442 34     34   84 my $from = 'MYSOURCE';
443 34         73 my $to = 'MY';
444 34         62 foreach my $method (keys %{"${from}::"}) {
  34         199  
445 34 50       68 next unless defined &{"${from}::$method"};
  34         178  
446 34         62 *{"${to}::$method"} = \&{"${from}::$method"};
  34         190  
  34         113  
447             }
448             }
449              
450             sub _scan_files {
451 188     188   415 my ($mask, $dir) = @_;
452 188 100       10216 return grep {_is_file_ok($_)} glob($mask) unless $dir;
  321         658  
453            
454 98         314 my @list = grep {_is_file_ok($_)} glob(join(' ', map {"$dir/$_"} split(' ', $mask)));
  106         317  
  202         5385  
455            
456 98 50       1789 opendir(my $dh, $dir) or die "Could not open dir '$dir' for scanning: $!";
457 98         819 while (my $entry = readdir $dh) {
458 416 100       1470 next if $entry =~ /^\./;
459 220         428 my $path = "$dir/$entry";
460 220 100       1621 next unless -d $path;
461 37         161 push @list, _scan_files($mask, $path);
462             }
463 98         542 closedir $dh;
464            
465 98         545 return @list;
466             }
467              
468             sub _is_file_ok {
469 427     427   734 my $file = shift;
470 427 100       2990 return unless -f $file;
471 424 50       1098 return if $file =~ /\#/;
472 424 50       770 return if $file =~ /~$/; # emacs temp files
473 424 50       713 return if $file =~ /,v$/; # RCS files
474 424 50       713 return if $file =~ m{\.swp$}; # vim swap files
475 424         1276 return 1;
476             }
477              
478             sub _process_map {
479 12     12   46 my ($map, $mask) = @_;
480 12         109 foreach my $source (keys %$map) {
481 18   66     95 my $dest = $map->{$source} || $source;
482 18 100       208 if (-f $source) {
483 11 100       76 $dest .= $source if $dest =~ m#[/\\]$#;
484 11         54 $dest =~ s#[/\\]{2,}#/#g;
485 11         38 $dest =~ s#^[/\\]+##;
486 11         31 $map->{$source} = $dest;
487 11         42 next;
488             }
489 7 50       56 next unless -d $source;
490            
491 7         22 delete $map->{$source};
492 7         27 my @files = _scan_files($mask, $source);
493 7         27 foreach my $file (@files) {
494 18         39 my $dest_file = $file;
495 18         184 $dest_file =~ s/^$source//;
496 18         62 $dest_file = "$dest/$dest_file";
497 18         89 $dest_file =~ s#[/\\]{2,}#/#g;
498 18         59 $dest_file =~ s#^[/\\]+##;
499 18         82 $map->{$file} = $dest_file;
500             }
501             }
502             }
503              
504             sub _includes {
505 94     94   184 my ($arr, $val) = @_;
506 94 50       167 for (@$arr) { return 1 if $_ eq $val }
  357         637  
507 94         266 return;
508             }
509              
510             sub _string_split_array {
511 46     46   69 my $list = shift;
512 46         67 my @result;
513 46 100       107 if ($list) {
514 12 100       36 $list = [$list] unless ref($list) eq 'ARRAY';
515 12         30 push @result, map { glob } split(' ') for @$list;
  12         252  
516             }
517 46         110 return \@result;
518             }
519              
520             sub _string_merge {
521 38 50   38   94 return unless $_[1];
522 38   100     261 $_[0] ||= '';
523 38 100       163 $_[0] .= $_[0] ? " $_[1]" : $_[1];
524             }
525              
526             {
527             package
528             MYSOURCE;
529            
530             sub postamble {
531 0     0   0 my $self = shift;
532 0         0 my %args = @_;
533 0         0 return join("\n", values %args);
534             }
535             }
536              
537             {
538             package
539             MY;
540 12     12   80 use Config;
  12         23  
  12         6157  
541              
542             if ($win32) {
543             my $gcc_compliant = $Config{cc} =~ /\b(gcc|clang)\b/i ? 1 : 0;
544            
545             *dynamic_lib = sub {
546             my ($self, %attribs) = @_;
547             my $code = $self->SUPER::dynamic_lib(%attribs);
548            
549             unless ($gcc_compliant) {
550             warn(
551             "Panda::Install: to maintain UNIX-like shared library behaviour on windows (export all symbols by default), we need gcc-compliant linker. ".
552             "Panda::Install-dependant modules should only be installed on perls with MinGW shell (like strawberry perl), or at least having gcc compiler. ".
553             "I will continue, but this module's binary dependencies may not work."
554             );
555             return $code;
556             }
557             return $code unless $code;
558            
559             # remove .def-related from code, remove double DLL build, remove dll.exp from params, add export all symbols param.
560             my $DLLTOOL = $Config{dlltool} || 'dlltool';
561             my (@out, $last_ld);
562             map { $last_ld = $_ if /\$\(LD\)\s/ } split /\n/, $code;
563             foreach my $line (split /\n/, $code) {
564             next if $line =~ /$DLLTOOL/; # drop dlltool calls (we dont need .def file)
565             if ($line =~ /\$\(LD\)\s/) {
566             next if $line ne $last_ld;
567             $line =~ s/\$\(LD\)\s/\$(LD) -Wl,--export-all-symbols /;
568             $line =~ s/\bdll\.exp\b//;
569             }
570             $line =~ s/\$\(EXPORT_LIST\)//g; # remove .def from target dependency
571             push @out, $line;
572             }
573            
574             $code = join("\n", @out);
575             return $code;
576             };
577            
578             *dlsyms = sub {
579             my ($self, %attribs) = @_;
580             return '' if $gcc_compliant; # our dynamic_lib target doesn't need any .def files with gcc
581             return $self->SUPER::dlsyms(%attribs);
582             };
583             }
584             }
585              
586              
587             # dlsyms
588            
589             # generate DLL file containing all symbols, like default behaviour on UNIX.
590              
591              
592             sub _require_makemaker {
593 0 0   0   0 unless ($INC{'ExtUtils/MakeMaker.pm'}) {
594 0         0 require ExtUtils::MakeMaker;
595 0         0 ExtUtils::MakeMaker->import();
596             }
597             }
598              
599             sub _module_info_write {
600 1     1   3 my ($file, $info) = @_;
601 1         483 require Data::Dumper;
602 1         5198 local $Data::Dumper::Terse = 1;
603 1         3 local $Data::Dumper::Indent = 0;
604 1         4 my $content = Data::Dumper::Dumper($info);
605 1         88 my $restore_mode;
606 1 50       26 if (-e $file) { # make sure we have permissions to write, because perl installs files with 444 perms
607 0         0 my $mode = (stat $file)[2];
608 0 0       0 unless ($mode & 0200) { # if not, temporary enable write permissions
609 0         0 $restore_mode = $mode;
610 0         0 $mode |= 0200;
611 0         0 chmod $mode, $file;
612             }
613             }
614 1 50       64 open my $fh, '>', $file or do {
615 0         0 warn "Cannot open $file for writing: $!, \033[1;31mbinary deps info won't be synced!\033[0m\n";
616 0         0 sleep 2;
617 0         0 return;
618             };
619 1         11 print $fh $content;
620 1         39 close $fh;
621            
622 1 50       7 chmod $restore_mode, $file if $restore_mode; # restore old perms if we changed it
623             }
624              
625             1;