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   232602 use strict;
  14         46  
  14         423  
4 14     14   90 use warnings;
  14         47  
  14         337  
5 14     14   281 use 5.008004;
  14         48  
6 14     14   106 use Carp ();
  14         53  
  14         333  
7 14     14   5895 use Text::ParseWords ();
  14         16548  
  14         320  
8 14     14   477 use FFI::Temp;
  14         32  
  14         298  
9 14     14   5346 use Capture::Tiny ();
  14         42452  
  14         347  
10 14     14   85 use File::Spec;
  14         66  
  14         352  
11 14     14   5066 use FFI::Platypus::ShareConfig;
  14         46  
  14         21047  
12              
13             # ABSTRACT: Platform specific configuration.
14             our $VERSION = '2.08'; # VERSION
15              
16              
17             sub new
18             {
19 42     42 1 12054 my($class, $config) = @_;
20 42   33     205 $config ||= do {
21 42         380 require Config;
22 42         258 \%Config::Config;
23             };
24 42         206 my $self = bless {
25             config => $config,
26             }, $class;
27 42         341 $self;
28             }
29              
30              
31             my $default;
32             sub default
33             {
34 46   66 46 1 412 $default ||= FFI::Build::Platform->new;
35             }
36              
37             sub _self
38             {
39 1327     1327   2689 my($self) = @_;
40 1327 100       12469 ref $self ? $self : $self->default;
41             }
42              
43              
44             sub osname
45             {
46 759     759 1 1622 _self(shift)->{config}->{osname};
47             }
48              
49              
50             sub object_suffix
51             {
52 36     36 1 435 _self(shift)->{config}->{obj_ext};
53             }
54              
55              
56             sub library_suffix
57             {
58 43     43 1 295 my $self = _self(shift);
59 43         150 my $osname = $self->osname;
60 43         137 my @suffix;
61 43 50       590 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         478 push @suffix, '.' . $self->{config}->{dlext};
72             }
73 43 100       437 wantarray ? @suffix : $suffix[0]; ## no critic (Community::Wantarray)
74             }
75              
76              
77             sub library_prefix
78             {
79 41     41 1 4522 my $self = _self(shift);
80              
81             # this almost certainly requires refinement.
82 41 50       250 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         399 return 'lib';
97             }
98             }
99              
100              
101             sub cc
102             {
103 53     53 1 207 my $self = _self(shift);
104 53         699 my $cc = $self->{config}->{cc};
105 53         398 [$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 11633 my $self = _self(shift);
120              
121 13         42 my @cc = @{ $self->cc };
  13         57  
122              
123 13 50 0     270 if($self->{config}->{ccname} eq 'gcc')
    0          
124             {
125 13 50       83 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       49 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         62 my @maybe = qw( c++ g++ clang++ );
142              
143 13         44 foreach my $maybe (@maybe)
144             {
145 13 50       51 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 40 my $self = _self(shift);
161              
162 6         37 $DB::single = 1;
163              
164             # This is definitely not exhaustive or complete or even
165             # particularlly good. Patches welcome.
166              
167 6 50       30 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         28 return $self->cxx;
175             }
176             }
177              
178              
179             sub for
180             {
181 2     2 1 9 my $self = _self(shift);
182              
183 2         5 my @cc = @{ $self->cc };
  2         11  
184              
185 2 50       28 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         6 foreach my $maybe (qw( gfortran ))
195             {
196 2 50       9 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 67 my($self) = @_;
209 21         635 my $ld = $self->{config}->{ld};
210 21         170 [$self->shellwords($ld)];
211             }
212              
213              
214             sub shellwords
215             {
216 226     226 1 725 my $self = _self(shift);
217              
218 226         734 my $win = !!($self->osname eq 'MSWin32');
219              
220 548         30426 grep { defined $_ } map {
221 226         857 ref $_
222             # if we have an array ref then it has already been shellworded
223             ? @$_
224 226 50       2401 : do {
225             # remove leading whitespace, confuses some older versions of shellwords
226 226   33     2713 my $str = /^\s*(.*)$/ && $1;
227             # escape things on windows
228 226 50       730 $str =~ s,\\,\\\\,g if $win;
229 226         1384 Text::ParseWords::shellwords($str);
230             }
231             } @_;
232              
233             }
234              
235              
236             sub ccflags
237             {
238 41     41 1 185 my $self = _self(shift);
239 41         95 my @ccflags;
240 41         209 push @ccflags, $self->shellwords($self->{config}->{cccdlflags});
241 41         309 push @ccflags, $self->shellwords($self->{config}->{ccflags});
242 41         307 push @ccflags, $self->shellwords($self->{config}->{optimize});
243 41         166 my $dist_include = eval { File::Spec->catdir(FFI::Platypus::ShareConfig::dist_dir('FFI-Platypus'), 'include') };
  41         290  
244 41 50       299 push @ccflags, "-I$dist_include" unless $@;
245 41         216 \@ccflags;
246             }
247              
248              
249             sub ldflags
250             {
251 21     21 1 281 my $self = _self(shift);
252 21         639 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   146 no warnings 'qw';
  14         41  
  14         1670  
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   121 no warnings 'qw';
  14         31  
  14         17064  
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         254 \@ldflags;
275             }
276              
277              
278             sub cc_mm_works
279             {
280 5     5 1 1400 my $self = _self(shift);
281 5         13 my $verbose = shift;
282 5   100     43 $verbose ||= 0;
283              
284 5 50       28 unless(defined $self->{cc_mm_works})
285             {
286 5         658 require FFI::Build::File::C;
287 5         52 my $c = FFI::Build::File::C->new(\"#include \"foo.h\"\n");
288 5         34 my $dir = FFI::Temp->newdir;
289             {
290 5         2331 open my $fh, '>', "$dir/foo.h";
  5         214  
291 5         459 print $fh "\n";
292 5         193 close $fh;
293             }
294              
295 5         42 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   6257 $self->run(@cmd);
305 5         267 });
306              
307 5 50       7763 if($verbose >= 2)
    100          
308             {
309 0         0 print $out;
310             }
311             elsif($verbose >= 1)
312             {
313 1         47 print "CC (checkfor -MM)\n";
314             }
315              
316              
317 5 50 33     405 if(!$exit && $out =~ /foo\.h/)
318             {
319 5         185 $self->{cc_mm_works} = '-MM';
320             }
321             else
322             {
323 0         0 $self->{cc_mm_works} = 0;
324             }
325             }
326              
327 5         512 $self->{cc_mm_works};
328             }
329              
330              
331             sub flag_object_output
332             {
333 33     33 1 120 my $self = _self(shift);
334 33         98 my $file = shift;
335 33 50 33     106 if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
336             {
337 0         0 return ("-Fo$file");
338             }
339             else
340             {
341 33         301 return ('-o' => $file);
342             }
343             }
344              
345              
346             sub flag_library_output
347             {
348 19     19 1 168 my $self = _self(shift);
349 19         64 my $file = shift;
350 19 50 33     75 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         306 return ('-o' => $file);
361             }
362             }
363              
364              
365             sub flag_exe_output
366             {
367 8     8 1 33 my $self = _self(shift);
368 8         16 my $file = shift;
369 8 50 33     27 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         59 return ('-o' => $file);
377             }
378             }
379              
380              
381             sub flag_export
382             {
383 19     19 1 96 my $self = _self(shift);
384 19 50 33     105 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 197697 my(undef, $command) = @_;
392 17         2527 require IPC::Cmd;
393 17 100       135046 my @command = ref $command ? @$command : ($command);
394 17         94 IPC::Cmd::can_run($command[0]);
395             }
396              
397              
398             sub run
399             {
400 61     61 1 164 my $self = shift;
401 61 100       216 my @command = map { ref $_ ? @$_ : $_ } grep { defined $_ } @_;
  418         2756  
  418         841  
402 61         2746 print "+@command\n";
403 61         2212966 system @command;
404 61         9781 $?;
405             }
406              
407              
408 8     8   35 sub _c { join ',', @_ }
409 14 100   14   99937 sub _l { join ' ', map { ref $_ ? @$_ : $_ } @_ }
  14         124  
410              
411             sub diag
412             {
413 2     2 1 1558 my $self = _self(shift);
414 2         5 my @diag;
415              
416 2         10 push @diag, "osname : ". _c($self->osname);
417 2         13 push @diag, "cc : ". _l($self->cc);
418 2   50     10 push @diag, "cxx : ". (eval { _l($self->cxx) } || '---' );
419 2   50     9 push @diag, "cxxld : ". (eval { _l($self->cxxld) } || '---' );
420 2   50     8 push @diag, "for : ". (eval { _l($self->for) } || '---' );
421 2         22 push @diag, "ld : ". _l($self->ld);
422 2         12 push @diag, "ccflags : ". _l($self->ccflags);
423 2         17 push @diag, "ldflags : ". _l($self->ldflags);
424 2         20 push @diag, "object suffix : ". _c($self->object_suffix);
425 2         10 push @diag, "library prefix : ". _c($self->library_prefix);
426 2         9 push @diag, "library suffix : ". _c($self->library_suffix);
427 2         9 push @diag, "cc mm works : ". $self->cc_mm_works;
428              
429 2         211 join "\n", @diag;
430             }
431              
432             1;
433              
434             __END__