File Coverage

lib/FFI/Build/Platform.pm
Criterion Covered Total %
statement 162 200 81.0
branch 42 80 52.5
condition 16 38 42.1
subroutine 37 38 97.3
pod 23 23 100.0
total 280 379 73.8


line stmt bran cond sub pod time code
1             package FFI::Build::Platform;
2              
3 16     16   177525 use strict;
  16         28  
  16         551  
4 16     16   102 use warnings;
  16         25  
  16         813  
5 16     16   258 use 5.008004;
  16         49  
6 16     16   122 use Carp ();
  16         57  
  16         420  
7 16     16   6471 use Text::ParseWords ();
  16         22421  
  16         455  
8 16     16   1075 use FFI::Temp;
  16         30  
  16         409  
9 16     16   5633 use Capture::Tiny ();
  16         65336  
  16         455  
10 16     16   93 use File::Spec;
  16         34  
  16         449  
11 16     16   6269 use FFI::Platypus::ShareConfig;
  16         88  
  16         27900  
12              
13             # ABSTRACT: Platform specific configuration.
14             our $VERSION = '2.11'; # VERSION
15              
16              
17             sub new
18             {
19 41     41 1 373789 my($class, $config) = @_;
20 41   66     204 $config ||= do {
21 39         361 require Config;
22 39         210 \%Config::Config;
23             };
24 41         198 my $self = bless {
25             config => $config,
26             }, $class;
27 41         351 $self;
28             }
29              
30              
31             my $default;
32             sub default
33             {
34 44   66 44 1 339 $default ||= FFI::Build::Platform->new;
35             }
36              
37             sub _self
38             {
39 1783     1783   3381 my($self) = @_;
40 1783 100       11848 ref $self ? $self : $self->default;
41             }
42              
43              
44             sub osname
45             {
46 976     976 1 3427 _self(shift)->{config}->{osname};
47             }
48              
49              
50             sub object_suffix
51             {
52 65     65 1 386110 _self(shift)->{config}->{obj_ext};
53             }
54              
55              
56             sub library_suffix
57             {
58 43     43 1 339622 my $self = _self(shift);
59 43         121 my $osname = $self->osname;
60 43         108 my @suffix;
61 43 50       409 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         352 push @suffix, '.' . $self->{config}->{dlext};
72             }
73 43 100       465 wantarray ? @suffix : $suffix[0]; ## no critic (Community::Wantarray)
74             }
75              
76              
77             sub library_prefix
78             {
79 41     41 1 2546 my $self = _self(shift);
80              
81             # this almost certainly requires refinement.
82 41 50       275 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         303 return 'lib';
97             }
98             }
99              
100              
101             sub cc
102             {
103 80     80 1 258 my $self = _self(shift);
104 80         749 my $cc = $self->{config}->{cc};
105 80         530 [$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 6     6 1 355123 my $self = _self(shift);
120              
121 6         14 my @cc = @{ $self->cc };
  6         32  
122              
123 6 50 0     239 if($self->{config}->{ccname} eq 'gcc')
    0          
124             {
125 6 50       30 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 6 50       26 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 6         35 my @maybe = qw( c++ g++ clang++ );
142              
143 6         18 foreach my $maybe (@maybe)
144             {
145 18 50       341569 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 6         2838 Carp::croak("unable to detect corresponding C++ compiler");
155             }
156              
157              
158             sub cxxld
159             {
160 2     2 1 9 my $self = _self(shift);
161              
162 2         13 $DB::single = 1;
163              
164             # This is definitely not exhaustive or complete or even
165             # particularlly good. Patches welcome.
166              
167 2 50       10 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 2         9 return $self->cxx;
175             }
176             }
177              
178              
179             sub for
180             {
181 2     2 1 7 my $self = _self(shift);
182              
183 2         5 my @cc = @{ $self->cc };
  2         8  
184              
185 2 50       22 if($self->{config}->{ccname} eq 'gcc')
186             {
187 2 50       10 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       8 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 22     22 1 76 my($self) = @_;
209 22         634 my $ld = $self->{config}->{ld};
210 22         216 [$self->shellwords($ld)];
211             }
212              
213              
214             sub shellwords
215             {
216 356     356 1 20934 my $self = _self(shift);
217              
218 356         946 my $win = !!($self->osname eq 'MSWin32');
219              
220 1061         31622 grep { defined $_ } map {
221 356         910 ref $_
222             # if we have an array ref then it has already been shellworded
223             ? @$_
224 356 100       3111 : do {
225             # remove leading whitespace, confuses some older versions of shellwords
226 208   33     2483 my $str = /^\s*(.*)$/ && $1;
227             # escape things on windows
228 208 50       562 $str =~ s,\\,\\\\,g if $win;
229 208         942 Text::ParseWords::shellwords($str);
230             }
231             } @_;
232              
233             }
234              
235              
236             sub ccflags
237             {
238 72     72 1 193 my $self = _self(shift);
239 72         143 my @ccflags;
240 72         387 push @ccflags, $self->shellwords($self->{config}->{cccdlflags});
241 72         323 push @ccflags, $self->shellwords($self->{config}->{ccflags});
242 72         2568 push @ccflags, $self->shellwords($self->{config}->{optimize});
243 72         202 my $dist_include = eval { File::Spec->catdir(FFI::Platypus::ShareConfig::dist_dir('FFI-Platypus'), 'include') };
  72         536  
244 72 100       387 push @ccflags, "-I$dist_include" unless $@;
245 72         588 \@ccflags;
246             }
247              
248              
249             sub ldflags
250             {
251 22     22 1 305 my $self = _self(shift);
252 22         436 my @ldflags = $self->shellwords($self->{config}->{lddlflags});
253 22 50 33     122 if($self->osname eq 'cygwin')
    50          
    50          
    50          
254 0         0 {
255 16     16   148 no warnings 'qw';
  16         30  
  16         2313  
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 16     16   97 no warnings 'qw';
  16         40  
  16         24106  
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 22         195 \@ldflags;
275             }
276              
277              
278             sub cc_mm_works
279             {
280 7     7 1 4720 my $self = _self(shift);
281 7         14 my $verbose = shift;
282 7   100     52 $verbose ||= 0;
283              
284 7 100       45 unless(defined $self->{cc_mm_works})
285             {
286 5         1268 require FFI::Build::File::C;
287 5         53 my $c = FFI::Build::File::C->new(\"#include \"foo.h\"\n");
288 5         41 my $dir = FFI::Temp->newdir;
289             {
290 5         2502 open my $fh, '>', "$dir/foo.h";
  5         176  
291 5         901 print $fh "\n";
292 5         244 close $fh;
293             }
294              
295 5         41 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   7720 $self->run(@cmd);
305 5         228 });
306              
307 5 50       6748 if($verbose >= 2)
    100          
308             {
309 0         0 print $out;
310             }
311             elsif($verbose >= 1)
312             {
313 1         40 print "CC (checkfor -MM)\n";
314             }
315              
316              
317 5 50 33     181 if(!$exit && $out =~ /foo\.h/)
318             {
319 5         191 $self->{cc_mm_works} = '-MM';
320             }
321             else
322             {
323 0         0 $self->{cc_mm_works} = 0;
324             }
325             }
326              
327 7         381 $self->{cc_mm_works};
328             }
329              
330              
331             sub flag_object_output
332             {
333 62     62 1 196 my $self = _self(shift);
334 62         139 my $file = shift;
335 62 50 33     161 if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
336             {
337 0         0 return ("-Fo$file");
338             }
339             else
340             {
341 62         397 return ('-o' => $file);
342             }
343             }
344              
345              
346             sub flag_library_output
347             {
348 20     20 1 143 my $self = _self(shift);
349 20         65 my $file = shift;
350 20 50 33     100 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 20         173 return ('-o' => $file);
361             }
362             }
363              
364              
365             sub flag_exe_output
366             {
367 7     7 1 32 my $self = _self(shift);
368 7         18 my $file = shift;
369 7 50 33     23 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 7         51 return ('-o' => $file);
377             }
378             }
379              
380              
381             sub flag_export
382             {
383 20     20 1 67 my $self = _self(shift);
384 20 50 33     95 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 20     20 1 72 my(undef, $command) = @_;
392 20         7192 require IPC::Cmd;
393 20 50       215332 my @command = ref $command ? @$command : ($command);
394 20         77 IPC::Cmd::can_run($command[0]);
395             }
396              
397              
398             sub run
399             {
400 93     93 1 237 my $self = shift;
401 93 100       334 my @command = map { ref $_ ? @$_ : $_ } grep { defined $_ } @_;
  668         4417  
  668         1240  
402 93         3616 print "+@command\n";
403 93         26077020 system @command;
404 93         14903 $?;
405             }
406              
407              
408 8     8   34 sub _c { join ',', @_ }
409 10 100   10   1350 sub _l { join ' ', map { ref $_ ? @$_ : $_ } @_ }
  10         107  
410              
411             sub diag
412             {
413 2     2 1 1888 my $self = _self(shift);
414 2         5 my @diag;
415              
416 2         9 push @diag, "osname : ". _c($self->osname);
417 2         12 push @diag, "cc : ". _l($self->cc);
418 2   50     6 push @diag, "cxx : ". (eval { _l($self->cxx) } || '---' );
419 2   50     11 push @diag, "cxxld : ". (eval { _l($self->cxxld) } || '---' );
420 2   50     11 push @diag, "for : ". (eval { _l($self->for) } || '---' );
421 2         13 push @diag, "ld : ". _l($self->ld);
422 2         26 push @diag, "ccflags : ". _l($self->ccflags);
423 2         13 push @diag, "ldflags : ". _l($self->ldflags);
424 2         10 push @diag, "object suffix : ". _c($self->object_suffix);
425 2         11 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         143 join "\n", @diag;
430             }
431              
432             1;
433              
434             __END__