File Coverage

blib/lib/FFI/Build/Platform.pm
Criterion Covered Total %
statement 161 200 80.5
branch 40 80 50.0
condition 15 38 39.4
subroutine 37 38 97.3
pod 23 23 100.0
total 276 379 72.8


line stmt bran cond sub pod time code
1             package FFI::Build::Platform;
2              
3 14     14   223518 use strict;
  14         41  
  14         421  
4 14     14   87 use warnings;
  14         28  
  14         337  
5 14     14   272 use 5.008004;
  14         49  
6 14     14   88 use Carp ();
  14         30  
  14         304  
7 14     14   5579 use Text::ParseWords ();
  14         15750  
  14         320  
8 14     14   468 use FFI::Temp;
  14         36  
  14         288  
9 14     14   5037 use Capture::Tiny ();
  14         41504  
  14         324  
10 14     14   90 use File::Spec;
  14         29  
  14         314  
11 14     14   4972 use FFI::Platypus::ShareConfig;
  14         34  
  14         19697  
12              
13             # ABSTRACT: Platform specific configuration.
14             our $VERSION = '2.06_01'; # TRIAL VERSION
15              
16              
17             sub new
18             {
19 42     42 1 11018 my($class, $config) = @_;
20 42   33     220 $config ||= do {
21 42         377 require Config;
22 42         221 \%Config::Config;
23             };
24 42         190 my $self = bless {
25             config => $config,
26             }, $class;
27 42         334 $self;
28             }
29              
30              
31             my $default;
32             sub default
33             {
34 46   66 46 1 361 $default ||= FFI::Build::Platform->new;
35             }
36              
37             sub _self
38             {
39 1327     1327   2421 my($self) = @_;
40 1327 100       10774 ref $self ? $self : $self->default;
41             }
42              
43              
44             sub osname
45             {
46 759     759 1 1496 _self(shift)->{config}->{osname};
47             }
48              
49              
50             sub object_suffix
51             {
52 36     36 1 296 _self(shift)->{config}->{obj_ext};
53             }
54              
55              
56             sub library_suffix
57             {
58 43     43 1 205 my $self = _self(shift);
59 43         168 my $osname = $self->osname;
60 43         134 my @suffix;
61 43 50       479 if($osname eq 'darwin')
    50          
62             {
63 0         0 push @suffix, '.dylib', '.bundle';
64             }
65             elsif($osname =~ /^(MSWin32|msys|cygwin)$/)
66             {
67 0         0 push @suffix, '.dll';
68             }
69             else
70             {
71 43         378 push @suffix, '.' . $self->{config}->{dlext};
72             }
73 43 100       349 wantarray ? @suffix : $suffix[0]; ## no critic (Community::Wantarray)
74             }
75              
76              
77             sub library_prefix
78             {
79 41     41 1 3485 my $self = _self(shift);
80              
81             # this almost certainly requires refinement.
82 41 50       212 if($self->osname eq 'cygwin')
    50          
    50          
83             {
84 0         0 return 'cyg';
85             }
86             elsif($self->osname eq 'msys')
87             {
88 0         0 return 'msys-';
89             }
90             elsif($self->osname eq 'MSWin32')
91             {
92 0         0 return '';
93             }
94             else
95             {
96 41         370 return 'lib';
97             }
98             }
99              
100              
101             sub cc
102             {
103 53     53 1 201 my $self = _self(shift);
104 53         557 my $cc = $self->{config}->{cc};
105 53         274 [$self->shellwords($cc)];
106             }
107              
108              
109             sub cpp
110             {
111 0     0 1 0 my $self = _self(shift);
112 0         0 my $cpp = $self->{config}->{cpprun};
113 0         0 [$self->shellwords($cpp)];
114             }
115              
116              
117             sub cxx
118             {
119 13     13 1 10500 my $self = _self(shift);
120              
121 13         36 my @cc = @{ $self->cc };
  13         46  
122              
123 13 50 0     269 if($self->{config}->{ccname} eq 'gcc')
    0          
124             {
125 13 50       55 if($cc[0] =~ /gcc$/)
126             {
127 0         0 my @maybe = @cc;
128 0         0 $maybe[0] =~ s/gcc$/g++/;
129 0 0       0 return \@maybe if $self->which($maybe[0]);
130             }
131 13 50       52 if($cc[0] =~ /clang/)
132             {
133 0         0 my @maybe = @cc;
134 0         0 $maybe[0] =~ s/clang/clang++/;
135 0 0       0 return \@maybe if $self->which($maybe[0]);
136             }
137              
138             # TODO: there are probably situations, eg solaris
139             # where we don't want to try c++ in the case of
140             # a ccname = gcc ?
141 13         54 my @maybe = qw( c++ g++ clang++ );
142              
143 13         36 foreach my $maybe (@maybe)
144             {
145 13 50       62 return [$maybe] if $self->which($maybe);
146             }
147             }
148             elsif($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
149             {
150             # TODO: see https://github.com/PerlFFI/FFI-Platypus/issues/203
151             #return \@cc;
152             }
153              
154 0         0 Carp::croak("unable to detect corresponding C++ compiler");
155             }
156              
157              
158             sub cxxld
159             {
160 6     6 1 35 my $self = _self(shift);
161              
162 6         40 $DB::single = 1;
163              
164             # This is definitely not exhaustive or complete or even
165             # particularlly good. Patches welcome.
166              
167 6 50       26 if($self->osname eq 'darwin')
168             {
169 0         0 my @cxx = @{ $self->cxx };
  0         0  
170 0 0       0 return [map { /^(cc|clang|gcc)$/ ? @cxx : $_ } @{ $self->ld }];
  0         0  
  0         0  
171             }
172             else
173             {
174 6         29 return $self->cxx;
175             }
176             }
177              
178              
179             sub for
180             {
181 2     2 1 8 my $self = _self(shift);
182              
183 2         9 my @cc = @{ $self->cc };
  2         11  
184              
185 2 50       17 if($self->{config}->{ccname} eq 'gcc')
186             {
187 2 50       11 if($cc[0] =~ /gcc$/)
188             {
189 0         0 my @maybe = @cc;
190 0         0 $maybe[0] =~ s/gcc$/gfortran/;
191 0 0       0 return \@maybe if $self->which($maybe[0]);
192             }
193              
194 2         5 foreach my $maybe (qw( gfortran ))
195             {
196 2 50       7 return [$maybe] if $self->which($maybe);
197             }
198             }
199             else
200             {
201 0         0 Carp::croak("unable to detect correspnding Fortran Compiler");
202             }
203             }
204              
205              
206             sub ld
207             {
208 21     21 1 75 my($self) = @_;
209 21         529 my $ld = $self->{config}->{ld};
210 21         157 [$self->shellwords($ld)];
211             }
212              
213              
214             sub shellwords
215             {
216 226     226 1 644 my $self = _self(shift);
217              
218 226         658 my $win = !!($self->osname eq 'MSWin32');
219              
220 548         28012 grep { defined $_ } map {
221 226         689 ref $_
222             # if we have an array ref then it has already been shellworded
223             ? @$_
224 226 50       2293 : do {
225             # remove leading whitespace, confuses some older versions of shellwords
226 226   33     2493 my $str = /^\s*(.*)$/ && $1;
227             # escape things on windows
228 226 50       729 $str =~ s,\\,\\\\,g if $win;
229 226         1203 Text::ParseWords::shellwords($str);
230             }
231             } @_;
232              
233             }
234              
235              
236             sub ccflags
237             {
238 41     41 1 147 my $self = _self(shift);
239 41         100 my @ccflags;
240 41         201 push @ccflags, $self->shellwords($self->{config}->{cccdlflags});
241 41         243 push @ccflags, $self->shellwords($self->{config}->{ccflags});
242 41         261 push @ccflags, $self->shellwords($self->{config}->{optimize});
243 41         125 my $dist_include = eval { File::Spec->catdir(FFI::Platypus::ShareConfig::dist_dir('FFI-Platypus'), 'include') };
  41         329  
244 41 50       253 push @ccflags, "-I$dist_include" unless $@;
245 41         207 \@ccflags;
246             }
247              
248              
249             sub ldflags
250             {
251 21     21 1 276 my $self = _self(shift);
252 21         492 my @ldflags = $self->shellwords($self->{config}->{lddlflags});
253 21 50 33     193 if($self->osname eq 'cygwin')
    50          
    50          
    50          
254 0         0 {
255 14     14   118 no warnings 'qw';
  14         46  
  14         1627  
256             # doesn't appear to be necessary, Perl has this in lddlflags already on cygwin
257             #push @ldflags, qw( -Wl,--enable-auto-import -Wl,--export-all-symbols -Wl,--enable-auto-image-base );
258             }
259             elsif($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
260             {
261 0         0 push @ldflags, qw( -dll );
262 0         0 @ldflags = grep !/^-nodefaultlib$/, @ldflags;
263             }
264             elsif($self->osname eq 'MSWin32')
265             {
266 14     14   130 no warnings 'qw';
  14         38  
  14         16406  
267 0         0 push @ldflags, qw( -Wl,--enable-auto-import -Wl,--export-all-symbols -Wl,--enable-auto-image-base );
268             }
269             elsif($self->osname eq 'darwin')
270             {
271             # we want to build a .dylib instead of a .bundle
272 0 0       0 @ldflags = map { $_ eq '-bundle' ? '-dynamiclib' : $_ } @ldflags;
  0         0  
273             }
274 21         221 \@ldflags;
275             }
276              
277              
278             sub cc_mm_works
279             {
280 5     5 1 1897 my $self = _self(shift);
281 5         15 my $verbose = shift;
282 5   100     43 $verbose ||= 0;
283              
284 5 50       22 unless(defined $self->{cc_mm_works})
285             {
286 5         558 require FFI::Build::File::C;
287 5         55 my $c = FFI::Build::File::C->new(\"#include \"foo.h\"\n");
288 5         47 my $dir = FFI::Temp->newdir;
289             {
290 5         2323 open my $fh, '>', "$dir/foo.h";
  5         194  
291 5         496 print $fh "\n";
292 5         195 close $fh;
293             }
294              
295 5         39 my @cmd = (
296             $self->cc,
297             $self->ccflags,
298             "-I$dir",
299             '-MM',
300             $c->path,
301             );
302              
303             my($out, $exit) = Capture::Tiny::capture_merged(sub {
304 5     5   6442 $self->run(@cmd);
305 5         276 });
306              
307 5 50       6784 if($verbose >= 2)
    100          
308             {
309 0         0 print $out;
310             }
311             elsif($verbose >= 1)
312             {
313 1         68 print "CC (checkfor -MM)\n";
314             }
315              
316              
317 5 50 33     213 if(!$exit && $out =~ /foo\.h/)
318             {
319 5         308 $self->{cc_mm_works} = '-MM';
320             }
321             else
322             {
323 0         0 $self->{cc_mm_works} = 0;
324             }
325             }
326              
327 5         414 $self->{cc_mm_works};
328             }
329              
330              
331             sub flag_object_output
332             {
333 33     33 1 115 my $self = _self(shift);
334 33         83 my $file = shift;
335 33 50 33     101 if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
336             {
337 0         0 return ("-Fo$file");
338             }
339             else
340             {
341 33         256 return ('-o' => $file);
342             }
343             }
344              
345              
346             sub flag_library_output
347             {
348 19     19 1 206 my $self = _self(shift);
349 19         55 my $file = shift;
350 19 50 33     93 if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
    50          
351             {
352 0         0 return ("-OUT:$file");
353             }
354             elsif($self->osname eq 'darwin')
355             {
356 0         0 return ('-install_name' => "\@rpath/$file", -o => $file);
357             }
358             else
359             {
360 19         259 return ('-o' => $file);
361             }
362             }
363              
364              
365             sub flag_exe_output
366             {
367 8     8 1 24 my $self = _self(shift);
368 8         20 my $file = shift;
369 8 50 33     24 if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
370             {
371 0         0 my $file = File::Spec->rel2abs($file);
372 0         0 return ("/Fe:$file");
373             }
374             else
375             {
376 8         64 return ('-o' => $file);
377             }
378             }
379              
380              
381             sub flag_export
382             {
383 19     19 1 85 my $self = _self(shift);
384 19 50 33     84 return () unless $self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl';
385 0         0 return map { "/EXPORT:$_" } @_;
  0         0  
386             }
387              
388              
389             sub which
390             {
391 17     17 1 194609 my(undef, $command) = @_;
392 17         2486 require IPC::Cmd;
393 17 100       130683 my @command = ref $command ? @$command : ($command);
394 17         77 IPC::Cmd::can_run($command[0]);
395             }
396              
397              
398             sub run
399             {
400 61     61 1 178 my $self = shift;
401 61 100       198 my @command = map { ref $_ ? @$_ : $_ } grep { defined $_ } @_;
  418         1155  
  418         823  
402 61         2602 print "+@command\n";
403 61         2035020 system @command;
404 61         9023 $?;
405             }
406              
407              
408 8     8   30 sub _c { join ',', @_ }
409 14 100   14   98002 sub _l { join ' ', map { ref $_ ? @$_ : $_ } @_ }
  14         110  
410              
411             sub diag
412             {
413 2     2 1 1422 my $self = _self(shift);
414 2         6 my @diag;
415              
416 2         7 push @diag, "osname : ". _c($self->osname);
417 2         10 push @diag, "cc : ". _l($self->cc);
418 2   50     6 push @diag, "cxx : ". (eval { _l($self->cxx) } || '---' );
419 2   50     6 push @diag, "cxxld : ". (eval { _l($self->cxxld) } || '---' );
420 2   50     8 push @diag, "for : ". (eval { _l($self->for) } || '---' );
421 2         9 push @diag, "ld : ". _l($self->ld);
422 2         10 push @diag, "ccflags : ". _l($self->ccflags);
423 2         24 push @diag, "ldflags : ". _l($self->ldflags);
424 2         14 push @diag, "object suffix : ". _c($self->object_suffix);
425 2         8 push @diag, "library prefix : ". _c($self->library_prefix);
426 2         9 push @diag, "library suffix : ". _c($self->library_suffix);
427 2         23 push @diag, "cc mm works : ". $self->cc_mm_works;
428              
429 2         153 join "\n", @diag;
430             }
431              
432             1;
433              
434             __END__