1package FFI::Build::Platform;
2
3use strict;
4use warnings;
5use 5.008004;
6use Carp ();
7use Text::ParseWords ();
8use FFI::Temp;
9use Capture::Tiny ();
10use File::Spec;
11use FFI::Platypus::ShareConfig;
12
13# ABSTRACT: Platform specific configuration.
14our $VERSION = '1.56'; # VERSION
15
16
17sub new
18{
19  my($class, $config) = @_;
20  $config ||= do {
21    require Config;
22    \%Config::Config;
23  };
24  my $self = bless {
25    config => $config,
26  }, $class;
27  $self;
28}
29
30
31my $default;
32sub default
33{
34  $default ||= FFI::Build::Platform->new;
35}
36
37sub _self
38{
39  my($self) = @_;
40  ref $self ? $self : $self->default;
41}
42
43
44sub osname
45{
46  _self(shift)->{config}->{osname};
47}
48
49
50sub object_suffix
51{
52  _self(shift)->{config}->{obj_ext};
53}
54
55
56sub library_suffix
57{
58  my $self = _self(shift);
59  my $osname = $self->osname;
60  my @suffix;
61  if($osname eq 'darwin')
62  {
63    push @suffix, '.dylib', '.bundle';
64  }
65  elsif($osname =~ /^(MSWin32|msys|cygwin)$/)
66  {
67    push @suffix, '.dll';
68  }
69  else
70  {
71    push @suffix, '.' . $self->{config}->{dlext};
72  }
73  wantarray ? @suffix : $suffix[0];  ## no critic (Community::Wantarray)
74}
75
76
77sub library_prefix
78{
79  my $self = _self(shift);
80
81  # this almost certainly requires refinement.
82  if($self->osname eq 'cygwin')
83  {
84    return 'cyg';
85  }
86  elsif($self->osname eq 'msys')
87  {
88    return 'msys-';
89  }
90  elsif($self->osname eq 'MSWin32')
91  {
92    return '';
93  }
94  else
95  {
96    return 'lib';
97  }
98}
99
100
101sub cc
102{
103  my $self = _self(shift);
104  my $cc = $self->{config}->{cc};
105  [$self->shellwords($cc)];
106}
107
108
109sub cpp
110{
111  my $self = _self(shift);
112  my $cpp = $self->{config}->{cpprun};
113  [$self->shellwords($cpp)];
114}
115
116
117sub cxx
118{
119  my $self = _self(shift);
120
121  my @cc = @{ $self->cc };
122
123  if($self->{config}->{ccname} eq 'gcc')
124  {
125    if($cc[0] =~ /gcc$/)
126    {
127      my @maybe = @cc;
128      $maybe[0] =~ s/gcc$/g++/;
129      return \@maybe if $self->which($maybe[0]);
130    }
131    if($cc[0] =~ /clang/)
132    {
133      my @maybe = @cc;
134      $maybe[0] =~ s/clang/clang++/;
135      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    my @maybe = qw( c++ g++ clang++ );
142
143    foreach my $maybe (@maybe)
144    {
145      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  Carp::croak("unable to detect corresponding C++ compiler");
155}
156
157
158sub cxxld
159{
160  my $self = _self(shift);
161
162  $DB::single = 1;
163
164  # This is definitely not exhaustive or complete or even
165  # particularlly good.  Patches welcome.
166
167  if($self->osname eq 'darwin')
168  {
169    my @cxx = @{ $self->cxx };
170    return [map { /^(cc|clang|gcc)$/ ? @cxx : $_ } @{ $self->ld }];
171  }
172  else
173  {
174    return $self->cxx;
175  }
176}
177
178
179sub for
180{
181  my $self = _self(shift);
182
183  my @cc = @{ $self->cc };
184
185  if($self->{config}->{ccname} eq 'gcc')
186  {
187    if($cc[0] =~ /gcc$/)
188    {
189      my @maybe = @cc;
190      $maybe[0] =~ s/gcc$/gfortran/;
191      return \@maybe if $self->which($maybe[0]);
192    }
193
194    foreach my $maybe (qw( gfortran ))
195    {
196      return [$maybe] if $self->which($maybe);
197    }
198  }
199  else
200  {
201    Carp::croak("unable to detect correspnding Fortran Compiler");
202  }
203}
204
205
206sub ld
207{
208  my($self) = @_;
209  my $ld = $self->{config}->{ld};
210  [$self->shellwords($ld)];
211}
212
213
214sub shellwords
215{
216  my $self = _self(shift);
217
218  my $win = !!($self->osname eq 'MSWin32');
219
220  grep { defined $_ } map {
221    ref $_
222      # if we have an array ref then it has already been shellworded
223      ? @$_
224      : do {
225        # remove leading whitespace, confuses some older versions of shellwords
226        my $str = /^\s*(.*)$/ && $1;
227        # escape things on windows
228        $str =~ s,\\,\\\\,g if $win;
229        Text::ParseWords::shellwords($str);
230      }
231  } @_;
232
233}
234
235
236sub ccflags
237{
238  my $self = _self(shift);
239  my @ccflags;
240  push @ccflags, $self->shellwords($self->{config}->{cccdlflags});
241  push @ccflags, $self->shellwords($self->{config}->{ccflags});
242  push @ccflags, $self->shellwords($self->{config}->{optimize});
243  my $dist_include = eval { File::Spec->catdir(FFI::Platypus::ShareConfig::dist_dir('FFI-Platypus'), 'include') };
244  push @ccflags, "-I$dist_include" unless $@;
245  \@ccflags;
246}
247
248
249sub ldflags
250{
251  my $self = _self(shift);
252  my @ldflags = $self->shellwords($self->{config}->{lddlflags});
253  if($self->osname eq 'cygwin')
254  {
255    no warnings 'qw';
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    push @ldflags, qw( -dll );
262    @ldflags = grep !/^-nodefaultlib$/, @ldflags;
263  }
264  elsif($self->osname eq 'MSWin32')
265  {
266    no warnings 'qw';
267    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    @ldflags = map { $_ eq '-bundle' ? '-dynamiclib' : $_ } @ldflags;
273  }
274  \@ldflags;
275}
276
277
278sub cc_mm_works
279{
280  my $self = _self(shift);
281  my $verbose = shift;
282  $verbose ||= 0;
283
284  unless(defined $self->{cc_mm_works})
285  {
286    require FFI::Build::File::C;
287    my $c = FFI::Build::File::C->new(\"#include \"foo.h\"\n");
288    my $dir = FFI::Temp->newdir;
289    {
290      open my $fh, '>', "$dir/foo.h";
291      print $fh "\n";
292      close $fh;
293    }
294
295    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      $self->run(@cmd);
305    });
306
307    if($verbose >= 2)
308    {
309      print $out;
310    }
311    elsif($verbose >= 1)
312    {
313      print "CC (checkfor -MM)\n";
314    }
315
316
317    if(!$exit && $out =~ /foo\.h/)
318    {
319      $self->{cc_mm_works} = '-MM';
320    }
321    else
322    {
323      $self->{cc_mm_works} = 0;
324    }
325  }
326
327  $self->{cc_mm_works};
328}
329
330
331sub flag_object_output
332{
333  my $self = _self(shift);
334  my $file = shift;
335  if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
336  {
337    return ("-Fo$file");
338  }
339  else
340  {
341    return ('-o' => $file);
342  }
343}
344
345
346sub flag_library_output
347{
348  my $self = _self(shift);
349  my $file = shift;
350  if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
351  {
352    return ("-OUT:$file");
353  }
354  elsif($self->osname eq 'darwin')
355  {
356    return ('-install_name' => "\@rpath/$file", -o => $file);
357  }
358  else
359  {
360    return ('-o' => $file);
361  }
362}
363
364
365sub flag_exe_output
366{
367  my $self = _self(shift);
368  my $file = shift;
369  if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
370  {
371    my $file = File::Spec->rel2abs($file);
372    return ("/Fe:$file");
373  }
374  else
375  {
376    return ('-o' => $file);
377  }
378}
379
380
381sub flag_export
382{
383  my $self = _self(shift);
384  return () unless $self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl';
385  return map { "/EXPORT:$_" } @_;
386}
387
388
389sub which
390{
391  my(undef, $command) = @_;
392  require IPC::Cmd;
393  my @command = ref $command ? @$command : ($command);
394  IPC::Cmd::can_run($command[0]);
395}
396
397
398sub run
399{
400  my $self = shift;
401  my @command  = map { ref $_ ? @$_ : $_ } grep { defined $_ } @_;
402  print "+@command\n";
403  system @command;
404  $?;
405}
406
407
408sub _c { join ',', @_ }
409sub _l { join ' ', map { ref $_ ? @$_ : $_ } @_ }
410
411sub diag
412{
413  my $self = _self(shift);
414  my @diag;
415
416  push @diag, "osname            : ". _c($self->osname);
417  push @diag, "cc                : ". _l($self->cc);
418  push @diag, "cxx               : ". (eval { _l($self->cxx) } || '---' );
419  push @diag, "cxxld             : ". (eval { _l($self->cxxld) } || '---' );
420  push @diag, "for               : ". (eval { _l($self->for) } || '---' );
421  push @diag, "ld                : ". _l($self->ld);
422  push @diag, "ccflags           : ". _l($self->ccflags);
423  push @diag, "ldflags           : ". _l($self->ldflags);
424  push @diag, "object suffix     : ". _c($self->object_suffix);
425  push @diag, "library prefix    : ". _c($self->library_prefix);
426  push @diag, "library suffix    : ". _c($self->library_suffix);
427  push @diag, "cc mm works       : ". $self->cc_mm_works;
428
429  join "\n", @diag;
430}
431
4321;
433
434__END__
435
436=pod
437
438=encoding UTF-8
439
440=head1 NAME
441
442FFI::Build::Platform - Platform specific configuration.
443
444=head1 VERSION
445
446version 1.56
447
448=head1 SYNOPSIS
449
450 use FFI::Build::Platform;
451
452=head1 DESCRIPTION
453
454This class is used to abstract out the platform specific parts of the L<FFI::Build> system.
455You shouldn't need to use it directly in most cases, unless you are working on L<FFI::Build>
456itself.
457
458=head1 CONSTRUCTOR
459
460=head2 new
461
462 my $platform = FFI::Build::Platform->new;
463
464Create a new instance of L<FFI::Build::Platform>.
465
466=head2 default
467
468 my $platform = FFI::Build::Platform->default;
469
470Returns the default instance of L<FFI::Build::Platform>.
471
472=head1 METHODS
473
474All of these methods may be called either as instance or classes
475methods.  If called as a class method, the default instance will
476be used.
477
478=head2 osname
479
480 my $osname = $platform->osname;
481
482The "os name" as understood by Perl.  This is the same as C<$^O>.
483
484=head2 object_suffix
485
486 my $suffix = $platform->object_suffix;
487
488The object suffix for the platform.  On UNIX this is usually C<.o>.  On Windows this
489is usually C<.obj>.
490
491=head2 library_suffix
492
493 my(@suffix) = $platform->library_suffix;
494 my $suffix  = $platform->library_suffix;
495
496The library suffix for the platform.  On Linux and some other UNIX this is often C<.so>.
497On OS X, this is C<.dylib> and C<.bundle>.  On Windows this is C<.dll>.
498
499=head2 library_prefix
500
501 my $prefix = $platform->library_prefix;
502
503The library prefix for the platform.  On Unix this is usually C<lib>, as in C<libfoo>.
504
505=head2 cc
506
507 my @cc = @{ $platform->cc };
508
509The C compiler
510
511=head2 cpp
512
513 my @cpp = @{ $platform->cpp };
514
515The C pre-processor
516
517=head2 cxx
518
519 my @cxx = @{ $platform->cxx };
520
521The C++ compiler that naturally goes with the C compiler.
522
523=head2 cxxld
524
525 my @cxxld = @{ $platform->cxxld };
526
527The C++ linker that naturally goes with the C compiler.
528
529=head2 for
530
531 my @for = @{ $platform->for };
532
533The Fortran compiler that naturally goes with the C compiler.
534
535=head2 ld
536
537 my $ld = $platform->ld;
538
539The C linker
540
541=head2 shellwords
542
543 my @words = $platform->shellwords(@strings);
544
545This is a wrapper around L<Text::ParseWords>'s C<shellwords> with some platform  workarounds
546applied.
547
548=head2 ccflags
549
550 my @ccflags = @{ $platform->cflags};
551
552The compiler flags, including those needed to compile object files that can be linked into a dynamic library.
553On Linux, for example, this is usually includes C<-fPIC>.
554
555=head2 ldflags
556
557 my @ldflags = @{ $platform->ldflags };
558
559The linker flags needed to link object files into a dynamic library.  This is NOT the C<libs> style library
560flags that specify the location and name of a library to link against, this is instead the flags that tell
561the linker to generate a dynamic library.  On Linux, for example, this is usually C<-shared>.
562
563=head2 cc_mm_works
564
565 my $bool = $platform->cc_mm_works;
566
567Returns the flags that can be passed into the C compiler to compute dependencies.
568
569=head2 flag_object_output
570
571 my @flags = $platform->flag_object_output($object_filename);
572
573Returns the flags that the compiler recognizes as being used to write out to a specific object filename.
574
575=head2 flag_library_output
576
577 my @flags = $platform->flag_library_output($library_filename);
578
579Returns the flags that the compiler recognizes as being used to write out to a specific library filename.
580
581=head2 flag_exe_output
582
583 my @flags = $platform->flag_exe_output($library_filename);
584
585Returns the flags that the compiler recognizes as being used to write out to a specific exe filename.
586
587=head2 flag_export
588
589 my @flags = $platform->flag_export(@symbols);
590
591Returns the flags that the linker recognizes for exporting functions.
592
593=head2 which
594
595 my $path = $platform->which($command);
596
597Returns the full path of the given command, if it is available, otherwise C<undef> is returned.
598
599=head2 run
600
601 $platform->run(@command);
602
603=head2 diag
604
605Diagnostic for the platform as a string.  This is for human consumption only, and the format
606may and will change over time so do not attempt to use is programmatically.
607
608=head1 AUTHOR
609
610Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
611
612Contributors:
613
614Bakkiaraj Murugesan (bakkiaraj)
615
616Dylan Cali (calid)
617
618pipcet
619
620Zaki Mughal (zmughal)
621
622Fitz Elliott (felliott)
623
624Vickenty Fesunov (vyf)
625
626Gregor Herrmann (gregoa)
627
628Shlomi Fish (shlomif)
629
630Damyan Ivanov
631
632Ilya Pavlov (Ilya33)
633
634Petr Písař (ppisar)
635
636Mohammad S Anwar (MANWAR)
637
638Håkon Hægland (hakonhagland, HAKONH)
639
640Meredith (merrilymeredith, MHOWARD)
641
642Diab Jerius (DJERIUS)
643
644Eric Brine (IKEGAMI)
645
646szTheory
647
648José Joaquín Atria (JJATRIA)
649
650Pete Houston (openstrike, HOUSTON)
651
652=head1 COPYRIGHT AND LICENSE
653
654This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
655
656This is free software; you can redistribute it and/or modify it under
657the same terms as the Perl 5 programming language system itself.
658
659=cut
660