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