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   181774 use strict;
  14         37  
  14         380  
4 14     14   75 use warnings;
  14         25  
  14         284  
5 14     14   208 use 5.008004;
  14         66  
6 14     14   58 use Carp ();
  14         25  
  14         241  
7 14     14   4462 use Text::ParseWords ();
  14         13435  
  14         268  
8 14     14   666 use FFI::Temp;
  14         28  
  14         241  
9 14     14   4200 use Capture::Tiny ();
  14         34142  
  14         263  
10 14     14   66 use File::Spec;
  14         26  
  14         292  
11 14     14   4054 use FFI::Platypus::ShareConfig;
  14         30  
  14         16578  
12              
13             # ABSTRACT: Platform specific configuration.
14             our $VERSION = '2.07'; # VERSION
15              
16              
17             sub new
18             {
19 42     42 1 8814 my($class, $config) = @_;
20 42   33     220 $config ||= do {
21 42         308 require Config;
22 42         179 \%Config::Config;
23             };
24 42         159 my $self = bless {
25             config => $config,
26             }, $class;
27 42         265 $self;
28             }
29              
30              
31             my $default;
32             sub default
33             {
34 46   66 46 1 308 $default ||= FFI::Build::Platform->new;
35             }
36              
37             sub _self
38             {
39 1327     1327   1933 my($self) = @_;
40 1327 100       8711 ref $self ? $self : $self->default;
41             }
42              
43              
44             sub osname
45             {
46 759     759 1 1276 _self(shift)->{config}->{osname};
47             }
48              
49              
50             sub object_suffix
51             {
52 36     36 1 237 _self(shift)->{config}->{obj_ext};
53             }
54              
55              
56             sub library_suffix
57             {
58 43     43 1 181 my $self = _self(shift);
59 43         121 my $osname = $self->osname;
60 43         87 my @suffix;
61 43 50       368 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         358 push @suffix, '.' . $self->{config}->{dlext};
72             }
73 43 100       319 wantarray ? @suffix : $suffix[0]; ## no critic (Community::Wantarray)
74             }
75              
76              
77             sub library_prefix
78             {
79 41     41 1 2436 my $self = _self(shift);
80              
81             # this almost certainly requires refinement.
82 41 50       174 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         277 return 'lib';
97             }
98             }
99              
100              
101             sub cc
102             {
103 53     53 1 129 my $self = _self(shift);
104 53         536 my $cc = $self->{config}->{cc};
105 53         222 [$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 12167 my $self = _self(shift);
120              
121 13         24 my @cc = @{ $self->cc };
  13         31  
122              
123 13 50 0     231 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       50 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         48 my @maybe = qw( c++ g++ clang++ );
142              
143 13         35 foreach my $maybe (@maybe)
144             {
145 13 50       42 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 22 my $self = _self(shift);
161              
162 6         20 $DB::single = 1;
163              
164             # This is definitely not exhaustive or complete or even
165             # particularlly good. Patches welcome.
166              
167 6 50       21 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         17 return $self->cxx;
175             }
176             }
177              
178              
179             sub for
180             {
181 2     2 1 5 my $self = _self(shift);
182              
183 2         4 my @cc = @{ $self->cc };
  2         11  
184              
185 2 50       14 if($self->{config}->{ccname} eq 'gcc')
186             {
187 2 50       6 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       6 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 60 my($self) = @_;
209 21         454 my $ld = $self->{config}->{ld};
210 21         136 [$self->shellwords($ld)];
211             }
212              
213              
214             sub shellwords
215             {
216 226     226 1 519 my $self = _self(shift);
217              
218 226         608 my $win = !!($self->osname eq 'MSWin32');
219              
220 548         26318 grep { defined $_ } map {
221 226         633 ref $_
222             # if we have an array ref then it has already been shellworded
223             ? @$_
224 226 50       1788 : do {
225             # remove leading whitespace, confuses some older versions of shellwords
226 226   33     2114 my $str = /^\s*(.*)$/ && $1;
227             # escape things on windows
228 226 50       496 $str =~ s,\\,\\\\,g if $win;
229 226         1025 Text::ParseWords::shellwords($str);
230             }
231             } @_;
232              
233             }
234              
235              
236             sub ccflags
237             {
238 41     41 1 118 my $self = _self(shift);
239 41         70 my @ccflags;
240 41         161 push @ccflags, $self->shellwords($self->{config}->{cccdlflags});
241 41         184 push @ccflags, $self->shellwords($self->{config}->{ccflags});
242 41         207 push @ccflags, $self->shellwords($self->{config}->{optimize});
243 41         102 my $dist_include = eval { File::Spec->catdir(FFI::Platypus::ShareConfig::dist_dir('FFI-Platypus'), 'include') };
  41         200  
244 41 50       197 push @ccflags, "-I$dist_include" unless $@;
245 41         143 \@ccflags;
246             }
247              
248              
249             sub ldflags
250             {
251 21     21 1 222 my $self = _self(shift);
252 21         409 my @ldflags = $self->shellwords($self->{config}->{lddlflags});
253 21 50 33     118 if($self->osname eq 'cygwin')
    50          
    50          
    50          
254 0         0 {
255 14     14   107 no warnings 'qw';
  14         46  
  14         1305  
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   92 no warnings 'qw';
  14         25  
  14         13522  
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         208 \@ldflags;
275             }
276              
277              
278             sub cc_mm_works
279             {
280 5     5 1 1224 my $self = _self(shift);
281 5         22 my $verbose = shift;
282 5   100     36 $verbose ||= 0;
283              
284 5 50       19 unless(defined $self->{cc_mm_works})
285             {
286 5         456 require FFI::Build::File::C;
287 5         41 my $c = FFI::Build::File::C->new(\"#include \"foo.h\"\n");
288 5         31 my $dir = FFI::Temp->newdir;
289             {
290 5         1731 open my $fh, '>', "$dir/foo.h";
  5         148  
291 5         364 print $fh "\n";
292 5         154 close $fh;
293             }
294              
295 5         30 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   4924 $self->run(@cmd);
305 5         208 });
306              
307 5 50       6689 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     191 if(!$exit && $out =~ /foo\.h/)
318             {
319 5         255 $self->{cc_mm_works} = '-MM';
320             }
321             else
322             {
323 0         0 $self->{cc_mm_works} = 0;
324             }
325             }
326              
327 5         456 $self->{cc_mm_works};
328             }
329              
330              
331             sub flag_object_output
332             {
333 33     33 1 94 my $self = _self(shift);
334 33         65 my $file = shift;
335 33 50 33     84 if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
336             {
337 0         0 return ("-Fo$file");
338             }
339             else
340             {
341 33         211 return ('-o' => $file);
342             }
343             }
344              
345              
346             sub flag_library_output
347             {
348 19     19 1 117 my $self = _self(shift);
349 19         56 my $file = shift;
350 19 50 33     60 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         199 return ('-o' => $file);
361             }
362             }
363              
364              
365             sub flag_exe_output
366             {
367 8     8 1 18 my $self = _self(shift);
368 8         14 my $file = shift;
369 8 50 33     19 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         47 return ('-o' => $file);
377             }
378             }
379              
380              
381             sub flag_export
382             {
383 19     19 1 56 my $self = _self(shift);
384 19 50 33     69 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 166292 my(undef, $command) = @_;
392 17         2011 require IPC::Cmd;
393 17 100       112814 my @command = ref $command ? @$command : ($command);
394 17         68 IPC::Cmd::can_run($command[0]);
395             }
396              
397              
398             sub run
399             {
400 61     61 1 122 my $self = shift;
401 61 100       208 my @command = map { ref $_ ? @$_ : $_ } grep { defined $_ } @_;
  418         2553  
  418         636  
402 61         2291 print "+@command\n";
403 61         1833503 system @command;
404 61         6476 $?;
405             }
406              
407              
408 8     8   27 sub _c { join ',', @_ }
409 14 100   14   78700 sub _l { join ' ', map { ref $_ ? @$_ : $_ } @_ }
  14         93  
410              
411             sub diag
412             {
413 2     2 1 1322 my $self = _self(shift);
414 2         4 my @diag;
415              
416 2         6 push @diag, "osname : ". _c($self->osname);
417 2         7 push @diag, "cc : ". _l($self->cc);
418 2   50     6 push @diag, "cxx : ". (eval { _l($self->cxx) } || '---' );
419 2   50     5 push @diag, "cxxld : ". (eval { _l($self->cxxld) } || '---' );
420 2   50     6 push @diag, "for : ". (eval { _l($self->for) } || '---' );
421 2         9 push @diag, "ld : ". _l($self->ld);
422 2         9 push @diag, "ccflags : ". _l($self->ccflags);
423 2         8 push @diag, "ldflags : ". _l($self->ldflags);
424 2         8 push @diag, "object suffix : ". _c($self->object_suffix);
425 2         9 push @diag, "library prefix : ". _c($self->library_prefix);
426 2         7 push @diag, "library suffix : ". _c($self->library_suffix);
427 2         6 push @diag, "cc mm works : ". $self->cc_mm_works;
428              
429 2         99 join "\n", @diag;
430             }
431              
432             1;
433              
434             __END__