Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

View File

@@ -0,0 +1,357 @@
package FFI::Build::File::Base;
use strict;
use warnings;
use 5.008004;
use Carp ();
use FFI::Temp;
use File::Basename ();
use FFI::Build::Platform;
use overload '""' => sub { $_[0]->path }, bool => sub { 1 }, fallback => 1;
# ABSTRACT: Base class for File::Build files
our $VERSION = '1.34'; # VERSION
sub new
{
my($class, $content, %config) = @_;
my $base = $config{base} || 'ffi_build_';
my $dir = $config{dir};
my $build = $config{build};
my $platform = $config{platform} || FFI::Build::Platform->new;
my $self = bless {
platform => $platform,
build => $build,
}, $class;
if(!defined $content)
{
Carp::croak("content is required");
}
elsif(ref($content) eq 'ARRAY')
{
$self->{path} = File::Spec->catfile(@$content);
}
elsif(ref($content) eq 'SCALAR')
{
my %args;
$args{TEMPLATE} = "${base}XXXXXX";
$args{DIR} = $dir if $dir;
$args{SUFFIX} = $self->default_suffix;
$args{UNLINK} = 0;
my $fh = $self->{fh} = FFI::Temp->new(%args);
binmode( $fh, $self->default_encoding );
print $fh $$content;
close $fh;
$self->{path} = $fh->filename;
$self->{temp} = 1;
}
elsif(ref($content) eq '')
{
$self->{path} = $content;
}
if($self->platform->osname eq 'MSWin32')
{
$self->{native} = File::Spec->catfile($self->{path});
$self->{path} =~ s{\\}{/}g;
}
$self;
}
sub default_suffix { die "must define a default extension in subclass" }
sub default_encoding { die "must define an encoding" }
sub accept_suffix { () }
sub path { shift->{path} }
sub basename { File::Basename::basename shift->{path} }
sub dirname { File::Basename::dirname shift->{path} }
sub is_temp { shift->{temp} }
sub platform { shift->{platform} }
sub build { shift->{build} }
sub native {
my($self) = @_;
$self->platform->osname eq 'MSWin32'
? $self->{native}
: $self->{path};
}
sub slurp
{
my($self) = @_;
my $fh;
open($fh, '<', $self->path) || Carp::croak "Error opening @{[ $self->path ]} for read $!";
binmode($fh, $self->default_encoding);
my $content = do { local $/; <$fh> };
close $fh;
$content;
}
sub keep
{
delete shift->{temp};
}
sub build_item
{
Carp::croak("Not implemented!");
}
sub needs_rebuild
{
my($self, @source) = @_;
# if the target doesn't exist, then we definitely
# need a rebuild.
return 1 unless -f $self->path;
my $target_time = [stat $self->path]->[9];
foreach my $source (@source)
{
my $source_time = [stat "$source"]->[9];
return 1 if ! defined $source_time;
return 1 if $source_time > $target_time;
}
return 0;
}
sub ld
{
return undef;
}
sub DESTROY
{
my($self) = @_;
if($self->{temp})
{
unlink($self->path);
}
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Build::File::Base - Base class for File::Build files
=head1 VERSION
version 1.34
=head1 SYNOPSIS
Create your own file class
package FFI::Build::File::Foo;
use base qw( FFI::Build::File::Base );
use constant default_suffix => '.foo';
use constant default_encoding => ':utf8';
Use it:
# use an existing file in the filesystem
my $file = FFI::Build::File::Foo->new('src/myfile.foo');
# generate a temp file with provided content
# file will be deletd when $file falls out of scope.
my $file = FFI::Build::File::Foo->new(\'content for a temp foo');
=head1 DESCRIPTION
This class is the base class for other L<FFI::Build::File> classes.
=head1 CONSTRUCTOR
=head2 new
my $file = FFI::Build::File::Base->new(\$content, %options);
my $file = FFI::Build::File::Base->new($filename, %options);
Create a new instance of the file class. You may provide either the
content of the file as a scalar reference, or the path to an existing
filename. Options:
=over 4
=item base
The base name for any temporary file C<ffi_build_> by default.
=item build
The L<FFI::Build> instance to use.
=item dir
The directory to store any temporary file.
=item platform
The L<FFI::Build::Platform> instance to use.
=back
=head1 METHODS
=head2 default_suffix
my $suffix = $file->default_suffix;
B<MUST> be overridden in the subclass. This is the standard extension for the file type. C<.c> for a C file, C<.o> or C<.obj> for an object file depending on platform. etc.
=head2 default_encoding
my $encoding = $file->default_encoding;
B<MUST> be overridden in the subclass. This is the passed to C<binmode> when the file is opened for reading or writing.
=head2 accept_suffix
my @suffix_list = $file->accept_suffix;
Returns a list of regexes that recognize the file type.
=head2 path
my $path = $file->path;
The full or relative path to the file.
=head2 basename
my $basename = $file->basename;
The base filename part of the path.
=head2 dirname
my $dir = $file->dirname;
The directory part of the path.
=head2 is_temp
my $bool = $file->is_temp;
Returns true if the file is temporary, that is, it will be deleted when the file object falls out of scope.
You can call C<keep>, to keep the file.
=head2 platform
my $platform = $file->platform;
The L<FFI::Build::Platform> instance used for this file object.
=head2 build
my $build = $file->build;
The L<FFI::Build> instance used for this file object, if any.
=head2 native
my $path = $file->native;
Returns the operating system native version of the filename path. On Windows, this means that forward slash C<\> is
used instead of backslash C</>.
=head2 slurp
my $content = $file->slurp;
Returns the content of the file.
=head2 keep
$file->keep;
Turns off the temporary flag on the file object, meaning it will not automatically be deleted when the
file object is deallocated or falls out of scope.
=head2 build_item
$file->build_item;
Builds the file into its natural output type, usually an object file. It returns a new file instance,
or if the file is an object file then it returns empty list.
=head2 build_all
$file->build_all;
If implemented the file in question can directly create a shared or dynamic library
without needing a link step. This is useful for languages that have their own build
systems.
=head2 needs_rebuild
my $bool = $file->needs_rebuild
=head2 ld
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,244 @@
package FFI::Build::File::C;
use strict;
use warnings;
use 5.008004;
use base qw( FFI::Build::File::Base );
use constant default_suffix => '.c';
use constant default_encoding => ':utf8';
use Capture::Tiny ();
use File::Path ();
use FFI::Build::File::Object;
# ABSTRACT: Class to track C source file in FFI::Build
our $VERSION = '1.34'; # VERSION
sub accept_suffix
{
(qr/\.(c|i)$/)
}
sub build_item
{
my($self) = @_;
my $oname = $self->basename;
$oname =~ s/\.(c(xx|pp)|i)?$//;
$oname .= $self->platform->object_suffix;
my $buildname = '_build';
$buildname = $self->build->buildname if $self->build;
my $object = FFI::Build::File::Object->new(
[ $self->dirname, $buildname, $oname ],
platform => $self->platform,
build => $self->build,
);
return $object if -f $object->path && !$object->needs_rebuild($self->_deps);
File::Path::mkpath($object->dirname, { verbose => 0, mode => oct(700) });
my @cmd = (
$self->_base_args,
-c => $self->path,
$self->platform->flag_object_output($object->path),
);
my($out, $exit) = Capture::Tiny::capture_merged(sub {
$self->platform->run(@cmd);
});
if($exit || !-f $object->path)
{
print $out;
die "error building $object from $self";
}
elsif($self->build && $self->build->verbose >= 2)
{
print $out;
}
elsif($self->build && $self->build->verbose >= 1)
{
print "CC @{[ $self->path ]}\n";
}
$object;
}
sub cc
{
my($self) = @_;
$self->platform->cc;
}
sub _base_args
{
my($self) = @_;
my @cmd = ($self->cc);
push @cmd, $self->build->cflags_I if $self->build;
push @cmd, $self->platform->ccflags;
push @cmd, @{ $self->build->cflags } if $self->build;
@cmd;
}
sub _base_args_cpp
{
my($self) = @_;
my @cmd = ($self->platform->cpp);
push @cmd, $self->build->cflags_I if $self->build;
push @cmd, grep /^-[DI]/, $self->platform->ccflags;
push @cmd, grep /^-D/, @{ $self->build->cflags } if $self->build;
@cmd;
}
sub build_item_cpp
{
my($self) = @_;
my $oname = $self->basename;
$oname =~ s/\.(c(xx|pp)|i)$?$//;
$oname .= '.i';
my $buildname = '_build';
$buildname = $self->build->buildname if $self->build;
my $ifile = FFI::Build::File::C->new(
[ $self->dirname, $buildname, $oname ],
platform => $self->platform,
build => $self->build,
);
File::Path::mkpath($ifile->dirname, { verbose => 0, mode => oct(700) });
my @cmd = (
$self->_base_args_cpp,
$self->path,
);
my($out, $err, $exit) = Capture::Tiny::capture(sub {
$self->platform->run(@cmd);
});
if($exit)
{
print "[out]\n$out\n" if defined $out && $out ne '';
print "[err]\n$err\n" if defined $err && $err ne '';
die "error building $ifile from $self";
}
else
{
my $fh;
open($fh, '>', $ifile->path);
print $fh $out;
close $fh;
}
$ifile;
}
sub _deps
{
my($self) = @_;
return $self->path unless $self->platform->cc_mm_works;
my @cmd = (
$self->_base_args,
'-MM',
$self->path,
);
my($out,$err,$exit) = Capture::Tiny::capture(sub {
$self->platform->run(@cmd);
});
if($exit)
{
print $out;
print $err;
warn "error computing dependencies for $self";
return ($self->path);
}
else
{
$out =~ s/^\+.*\n//; # remove the command line
# which on windows could have an confusing :
my(undef, $deps) = split /:/, $out, 2;
$deps =~ s/^\s+//;
$deps =~ s/\s+$//;
return grep !/^\\$/, split /\s+/, $deps;
}
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Build::File::C - Class to track C source file in FFI::Build
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Build::File::C;
my $c = FFI::Build::File::C->new('src/foo.c');
=head1 DESCRIPTION
File class for C source files.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,100 @@
package FFI::Build::File::CXX;
use strict;
use warnings;
use 5.008004;
use base qw( FFI::Build::File::C );
use constant default_suffix => '.cxx';
use constant default_encoding => ':utf8';
# ABSTRACT: Class to track C source file in FFI::Build
our $VERSION = '1.34'; # VERSION
sub accept_suffix
{
(qr/\.c(xx|pp)$/)
}
sub cc
{
my($self) = @_;
$self->platform->cxx;
}
sub ld
{
my($self) = @_;
$self->platform->cxx;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Build::File::CXX - Class to track C source file in FFI::Build
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Build::File::CXX;
my $c = FFI::Build::File::CXX->new('src/foo.cxx');
=head1 DESCRIPTION
File class for C++ source files.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,101 @@
package FFI::Build::File::Library;
use strict;
use warnings;
use 5.008004;
use base qw( FFI::Build::File::Base );
use constant default_encoding => ':raw';
# ABSTRACT: Class to track object file in FFI::Build
our $VERSION = '1.34'; # VERSION
sub default_suffix
{
shift->platform->library_suffix;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Build::File::Library - Class to track object file in FFI::Build
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Build;
my $build = FFI::Build->new(source => 'src/*.c');
# $lib is an instance of FFI::Build::File::Library
my $lib = $build->build;
=head1 DESCRIPTION
This is a class to track a library generated by L<FFI::Build>.
This is returned by L<FFI::Build>'s build method. This class
is a subclass of L<FFI::Build::File::Base>. The most important
method is probably C<path>, which returns the path to the library
which can be passed into L<FFI::Platypus> for immediate use.
=head1 METHODS
=head2 path
my $path = $lib->path;
Returns the path of the library.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,98 @@
package FFI::Build::File::Object;
use strict;
use warnings;
use 5.008004;
use base qw( FFI::Build::File::Base );
use constant default_encoding => ':raw';
use Carp ();
# ABSTRACT: Class to track object file in FFI::Build
our $VERSION = '1.34'; # VERSION
sub default_suffix
{
shift->platform->object_suffix;
}
sub build_item
{
my($self) = @_;
unless(-f $self->path)
{
Carp::croak "File not built"
}
return;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Build::File::Object - Class to track object file in FFI::Build
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Build::File::Object;
my $o = FFI::Build::File::Object->new('src/_build/foo.o');
=head1 DESCRIPTION
This class represents an object file. You normally do not need
to use it directly.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

440
database/perl/vendor/lib/FFI/Build/MM.pm vendored Normal file
View File

@@ -0,0 +1,440 @@
package FFI::Build::MM;
use strict;
use warnings;
use 5.008004;
use Carp ();
use FFI::Build;
use JSON::PP ();
use File::Glob ();
use File::Basename ();
use File::Path ();
use File::Copy ();
use ExtUtils::MakeMaker 7.12;
# ABSTRACT: FFI::Build installer code for ExtUtils::MakeMaker
our $VERSION = '1.34'; # VERSION
sub new
{
my($class, %opt) = @_;
my $save = defined $opt{save} ? $opt{save} : 1;
my $self = bless { save => $save }, $class;
$self->load_prop;
$self;
}
sub mm_args
{
my($self, %args) = @_;
if($args{DISTNAME})
{
$self->{prop}->{distname} ||= $args{DISTNAME};
$self->{prop}->{share} ||= "blib/lib/auto/share/dist/@{[ $self->distname ]}";
$self->{prop}->{arch} ||= "blib/arch/auto/@{[ join '/', split /-/, $self->distname ]}";
$self->save_prop;
}
else
{
Carp::croak "DISTNAME is required";
}
if(my $build = $self->build)
{
foreach my $alien (@{ $build->alien })
{
next if ref $alien;
$args{BUILD_REQUIRES}->{$alien} ||= 0;
}
}
if(my $test = $self->test)
{
foreach my $alien (@{ $test->alien })
{
next if ref $alien;
$args{TEST_REQUIRES}->{$alien} ||= 0;
}
}
%args;
}
sub distname { shift->{prop}->{distname} }
sub sharedir
{
my($self, $new) = @_;
if(defined $new)
{
$self->{prop}->{share} = $new;
$self->save_prop;
}
$self->{prop}->{share};
}
sub archdir
{
my($self, $new) = @_;
if(defined $new)
{
$self->{prop}->{arch} = $new;
$self->save_prop;
}
$self->{prop}->{arch};
}
sub load_build
{
my($self, $dir, $name, $install) = @_;
return unless -d $dir;
my($fbx) = File::Glob::bsd_glob("./$dir/*.fbx");
my $options;
my $platform = FFI::Build::Platform->default;
if($fbx)
{
$name = File::Basename::basename($fbx);
$name =~ s/\.fbx$//;
$options = do {
package FFI::Build::MM::FBX;
our $DIR = $dir;
our $PLATFORM = $platform;
# make sure we catch all of the errors
# code copied from `perldoc -f do`
my $return = do $fbx;
unless ( $return ) {
Carp::croak( "couldn't parse $fbx: $@" ) if $@;
Carp::croak( "couldn't do $fbx: $!" ) unless defined $return;
Carp::croak( "couldn't run $fbx" ) unless $return;
}
$return;
};
}
else
{
$name ||= $self->distname;
$options = {
source => ["$dir/*.c", "$dir/*.cxx", "$dir/*.cpp"],
};
# if we see a Go, Rust control file then we assume the
# ffi mod is written in that language.
foreach my $control_file ("$dir/Cargo.toml", "$dir/go.mod")
{
if(-f $control_file)
{
$options->{source} = [$control_file];
last;
}
}
}
$options->{platform} ||= $platform;
$options->{dir} ||= ref $install ? $install->($options) : $install;
$options->{verbose} = 1 unless defined $options->{verbose};
FFI::Build->new($name, %$options);
}
sub build
{
my($self) = @_;
$self->{build} ||= $self->load_build('ffi', undef, $self->sharedir . "/lib");
}
sub test
{
my($self) = @_;
$self->{test} ||= $self->load_build('t/ffi', 'test', sub {
my($opt) = @_;
my $buildname = $opt->{buildname} || '_build';
"t/ffi/$buildname";
});
}
sub save_prop
{
my($self) = @_;
return unless $self->{save};
open my $fh, '>', 'fbx.json';
print $fh JSON::PP::encode_json($self->{prop});
close $fh;
}
sub load_prop
{
my($self) = @_;
return unless $self->{save};
unless(-f 'fbx.json')
{
$self->{prop} = {};
return;
}
open my $fh, '<', 'fbx.json';
$self->{prop} = JSON::PP::decode_json(do { local $/; <$fh> });
close $fh;
}
sub clean
{
my($self) = @_;
foreach my $stage (qw( build test ))
{
my $build = $self->$stage;
$build->clean if $build;
}
unlink 'fbx.json' if -f 'fbx.json';
}
sub mm_postamble
{
my($self) = @_;
my $postamble = ".PHONY: fbx_build ffi fbx_test ffi-test fbx_clean ffi-clean\n\n";
# make fbx_realclean ; make clean
$postamble .= "realclean :: fbx_clean\n" .
"\n" .
"fbx_clean ffi-clean:\n" .
"\t\$(FULLPERL) -MFFI::Build::MM=cmd -e fbx_clean\n\n";
# make fbx_build; make
$postamble .= "pure_all :: fbx_build\n" .
"\n" .
"fbx_build ffi:\n" .
"\t\$(FULLPERL) -MFFI::Build::MM=cmd -e fbx_build\n\n";
# make fbx_test; make test
$postamble .= "subdirs-test_dynamic subdirs-test_static subdirs-test :: fbx_test\n" .
"\n" .
"fbx_test ffi-test:\n" .
"\t\$(FULLPERL) -MFFI::Build::MM=cmd -e fbx_test\n\n";
$postamble;
}
sub action_build
{
my($self) = @_;
my $build = $self->build;
if($build)
{
my $lib = $build->build;
if($self->archdir)
{
File::Path::mkpath($self->archdir, 0, oct(755));
my $archfile = File::Spec->catfile($self->archdir, File::Basename::basename($self->archdir) . ".txt");
open my $fh, '>', $archfile;
my $lib_path = $lib->path;
$lib_path =~ s/^blib\/lib\///;
print $fh "FFI::Build\@$lib_path\n";
close $fh;
}
}
return;
}
sub action_test
{
my($self) = @_;
my $build = $self->test;
$build->build if $build;
}
sub action_clean
{
my($self) = @_;
my $build = $self->clean;
();
}
sub import
{
my(undef, @args) = @_;
foreach my $arg (@args)
{
if($arg eq 'cmd')
{
package main;
my $mm = sub {
my($action) = @_;
my $build = FFI::Build::MM->new;
$build->$action;
};
no warnings 'once';
*fbx_build = sub {
$mm->('action_build');
};
*fbx_test = sub {
$mm->('action_test');
};
*fbx_clean = sub {
$mm->('action_clean');
};
}
}
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Build::MM - FFI::Build installer code for ExtUtils::MakeMaker
=head1 VERSION
version 1.34
=head1 SYNOPSIS
In your Makefile.PL:
use ExtUtils::MakeMaker;
use FFI::Build::MM;
my $fbmm = FFI::Build::MM->new;
WriteMakefile($fbmm->mm_args(
ABSTRACT => 'My FFI extension',
DISTNAME => 'Foo-Bar-Baz-FFI',
NAME => 'Foo::Bar::Baz::FFI',
VERSION_FROM => 'lib/Foo/Bar/Baz/FFI.pm',
...
));
sub MY::postamble {
$fbmm->mm_postamble;
}
Then put the C, C++ or Fortran files in C<./ffi> for your runtime library
and C<./t/ffi> for your test time library.
=head1 DESCRIPTION
This module provides a thin layer between L<FFI::Build> and L<ExtUtils::MakeMaker>.
Its interface is influenced by the design of L<Alien::Build::MM>. The idea is that
for your distribution you throw some C, C++ or Fortran source files into a directory
called C<ffi> and these files will be compiled and linked into a library that can
be used by your module. There is a control file C<ffi/*.fbx> which can be used to
control the compiler and linker options. (options passed directly into L<FFI::Build>).
The interface for this file is still under development.
=head1 CONSTRUCTOR
=head2 new
my $fbmm = FFI::Build::MM->new;
Create a new instance of L<FFI::Build::MM>.
=head1 METHODS
=head2 mm_args
my %new_args = $fbmm->mm_args(%old_args);
This method does two things:
=over 4
=item reads the arguments to determine sensible defaults (library name, install location, etc).
=item adjusts the arguments as necessary and returns an updated set of arguments.
=back
=head2 mm_postamble
my $postamble = $fbmm->mm_postamble;
This returns the Makefile postamble used by L<ExtUtils::MakeMaker>. The synopsis above for
how to invoke it properly. It adds the following Make targets:
=over 4
=item fbx_build / ffi
build the main runtime library in C<./ffi>.
=item fbx_test / ffi-test
Build the test library in C<./t/ffi>.
=item fbx_clean / ffi-clean
Clean any runtime or test libraries already built.
=back
Normally you do not need to build these targets manually, they will be built automatically
at the appropriate stage.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,623 @@
package FFI::Build::Platform;
use strict;
use warnings;
use 5.008004;
use Carp ();
use Text::ParseWords ();
use FFI::Temp;
use Capture::Tiny ();
use File::Spec;
use FFI::Platypus::ShareConfig;
# ABSTRACT: Platform specific configuration.
our $VERSION = '1.34'; # VERSION
sub new
{
my($class, $config) = @_;
$config ||= do {
require Config;
\%Config::Config;
};
my $self = bless {
config => $config,
}, $class;
$self;
}
my $default;
sub default
{
$default ||= FFI::Build::Platform->new;
}
sub _self
{
my($self) = @_;
ref $self ? $self : $self->default;
}
sub osname
{
_self(shift)->{config}->{osname};
}
sub object_suffix
{
_self(shift)->{config}->{obj_ext};
}
sub library_suffix
{
my $self = _self(shift);
my $osname = $self->osname;
my @suffix;
if($osname eq 'darwin')
{
push @suffix, '.dylib', '.bundle';
}
elsif($osname =~ /^(MSWin32|msys|cygwin)$/)
{
push @suffix, '.dll';
}
else
{
push @suffix, '.' . $self->{config}->{dlext};
}
wantarray ? @suffix : $suffix[0]; ## no critic (Freenode::Wantarray)
}
sub library_prefix
{
my $self = _self(shift);
# this almost certainly requires refinement.
if($self->osname eq 'cygwin')
{
return 'cyg';
}
elsif($self->osname eq 'msys')
{
return 'msys-';
}
elsif($self->osname eq 'MSWin32')
{
return '';
}
else
{
return 'lib';
}
}
sub cc
{
my $self = _self(shift);
my $cc = $self->{config}->{cc};
[$self->shellwords($cc)];
}
sub cpp
{
my $self = _self(shift);
my $cpp = $self->{config}->{cpprun};
[$self->shellwords($cpp)];
}
sub cxx
{
my $self = _self(shift);
my @cc = @{ $self->cc };
if($self->{config}->{ccname} eq 'gcc')
{
if($cc[0] =~ /gcc$/)
{
my @maybe = @cc;
$maybe[0] =~ s/gcc$/g++/;
return \@maybe if $self->which($maybe[0]);
}
if($cc[0] =~ /clang/)
{
my @maybe = @cc;
$maybe[0] =~ s/clang/clang++/;
return \@maybe if $self->which($maybe[0]);
}
# TODO: there are probably situations, eg solaris
# where we don't want to try c++ in the case of
# a ccname = gcc ?
my @maybe = qw( c++ g++ clang++ );
foreach my $maybe (@maybe)
{
return [$maybe] if $self->which($maybe);
}
}
elsif($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
{
# TODO: see https://github.com/PerlFFI/FFI-Platypus/issues/203
#return \@cc;
}
Carp::croak("unable to detect corresponding C++ compiler");
}
sub for
{
my $self = _self(shift);
my @cc = @{ $self->cc };
if($self->{config}->{ccname} eq 'gcc')
{
if($cc[0] =~ /gcc$/)
{
my @maybe = @cc;
$maybe[0] =~ s/gcc$/gfortran/;
return \@maybe if $self->which($maybe[0]);
}
foreach my $maybe (qw( gfortran ))
{
return [$maybe] if $self->which($maybe);
}
}
else
{
Carp::croak("unable to detect correspnding Fortran Compiler");
}
}
sub ld
{
my($self) = @_;
my $ld = $self->{config}->{ld};
[$self->shellwords($ld)];
}
sub shellwords
{
my $self = _self(shift);
my $win = !!($self->osname eq 'MSWin32');
grep { defined $_ } map {
ref $_
# if we have an array ref then it has already been shellworded
? @$_
: do {
# remove leading whitespace, confuses some older versions of shellwords
my $str = /^\s*(.*)$/ && $1;
# escape things on windows
$str =~ s,\\,\\\\,g if $win;
Text::ParseWords::shellwords($str);
}
} @_;
}
sub ccflags
{
my $self = _self(shift);
my @ccflags;
push @ccflags, $self->shellwords($self->{config}->{cccdlflags});
push @ccflags, $self->shellwords($self->{config}->{ccflags});
push @ccflags, $self->shellwords($self->{config}->{optimize});
my $dist_include = eval { File::Spec->catdir(FFI::Platypus::ShareConfig::dist_dir('FFI-Platypus'), 'include') };
push @ccflags, "-I$dist_include" unless $@;
\@ccflags;
}
sub ldflags
{
my $self = _self(shift);
my @ldflags = $self->shellwords($self->{config}->{lddlflags});
if($self->osname eq 'cygwin')
{
no warnings 'qw';
# doesn't appear to be necessary, Perl has this in lddlflags already on cygwin
#push @ldflags, qw( -Wl,--enable-auto-import -Wl,--export-all-symbols -Wl,--enable-auto-image-base );
}
elsif($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
{
push @ldflags, qw( -dll );
@ldflags = grep !/^-nodefaultlib$/, @ldflags;
}
elsif($self->osname eq 'MSWin32')
{
no warnings 'qw';
push @ldflags, qw( -Wl,--enable-auto-import -Wl,--export-all-symbols -Wl,--enable-auto-image-base );
}
elsif($self->osname eq 'darwin')
{
# we want to build a .dylib instead of a .bundle
@ldflags = map { $_ eq '-bundle' ? '-shared' : $_ } @ldflags;
}
\@ldflags;
}
sub cc_mm_works
{
my $self = _self(shift);
my $verbose = shift;
$verbose ||= 0;
unless(defined $self->{cc_mm_works})
{
require FFI::Build::File::C;
my $c = FFI::Build::File::C->new(\"#include \"foo.h\"\n");
my $dir = FFI::Temp->newdir;
{
open my $fh, '>', "$dir/foo.h";
print $fh "\n";
close $fh;
}
my @cmd = (
$self->cc,
$self->ccflags,
"-I$dir",
'-MM',
$c->path,
);
my($out, $exit) = Capture::Tiny::capture_merged(sub {
$self->run(@cmd);
});
if($verbose >= 2)
{
print $out;
}
elsif($verbose >= 1)
{
print "CC (checkfor -MM)\n";
}
if(!$exit && $out =~ /foo\.h/)
{
$self->{cc_mm_works} = '-MM';
}
else
{
$self->{cc_mm_works} = 0;
}
}
$self->{cc_mm_works};
}
sub flag_object_output
{
my $self = _self(shift);
my $file = shift;
if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
{
return ("-Fo$file");
}
else
{
return ('-o' => $file);
}
}
sub flag_library_output
{
my $self = _self(shift);
my $file = shift;
if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
{
return ("-OUT:$file");
}
elsif($self->osname eq 'darwin')
{
return ('-install_name' => "\@rpath/$file", -o => $file);
}
else
{
return ('-o' => $file);
}
}
sub flag_exe_output
{
my $self = _self(shift);
my $file = shift;
if($self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl')
{
my $file = File::Spec->rel2abs($file);
return ("/Fe:$file");
}
else
{
return ('-o' => $file);
}
}
sub flag_export
{
my $self = _self(shift);
return () unless $self->osname eq 'MSWin32' && $self->{config}->{ccname} eq 'cl';
return map { "/EXPORT:$_" } @_;
}
sub which
{
my(undef, $command) = @_;
require IPC::Cmd;
my @command = ref $command ? @$command : ($command);
IPC::Cmd::can_run($command[0]);
}
sub run
{
my $self = shift;
my @command = map { ref $_ ? @$_ : $_ } grep { defined $_ } @_;
print "+@command\n";
system @command;
$?;
}
sub _c { join ',', @_ }
sub _l { join ' ', map { ref $_ ? @$_ : $_ } @_ }
sub diag
{
my $self = _self(shift);
my @diag;
push @diag, "osname : ". _c($self->osname);
push @diag, "cc : ". _l($self->cc);
push @diag, "cxx : ". (eval { _l($self->cxx) } || '---' );
push @diag, "for : ". (eval { _l($self->for) } || '---' );
push @diag, "ld : ". _l($self->ld);
push @diag, "ccflags : ". _l($self->ccflags);
push @diag, "ldflags : ". _l($self->ldflags);
push @diag, "object suffix : ". _c($self->object_suffix);
push @diag, "library prefix : ". _c($self->library_prefix);
push @diag, "library suffix : ". _c($self->library_suffix);
push @diag, "cc mm works : ". $self->cc_mm_works;
join "\n", @diag;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Build::Platform - Platform specific configuration.
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Build::Platform;
=head1 DESCRIPTION
This class is used to abstract out the platform specific parts of the L<FFI::Build> system.
You shouldn't need to use it directly in most cases, unless you are working on L<FFI::Build>
itself.
=head1 CONSTRUCTOR
=head2 new
my $platform = FFI::Build::Platform->new;
Create a new instance of L<FFI::Build::Platform>.
=head2 default
my $platform = FFI::Build::Platform->default;
Returns the default instance of L<FFI::Build::Platform>.
=head1 METHODS
All of these methods may be called either as instance or classes
methods. If called as a class method, the default instance will
be used.
=head2 osname
my $osname = $platform->osname;
The "os name" as understood by Perl. This is the same as C<$^O>.
=head2 object_suffix
my $suffix = $platform->object_suffix;
The object suffix for the platform. On UNIX this is usually C<.o>. On Windows this
is usually C<.obj>.
=head2 library_suffix
my(@suffix) = $platform->library_suffix;
my $suffix = $platform->library_suffix;
The library suffix for the platform. On Linux and some other UNIX this is often C<.so>.
On OS X, this is C<.dylib> and C<.bundle>. On Windows this is C<.dll>.
=head2 library_prefix
my $prefix = $platform->library_prefix;
The library prefix for the platform. On Unix this is usually C<lib>, as in C<libfoo>.
=head2 cc
my @cc = @{ $platform->cc };
The C compiler
=head2 cpp
my @cpp = @{ $platform->cpp };
The C pre-processor
=head2 cxx
my @cxx = @{ $platform->cxx };
The C++ compiler that naturally goes with the C compiler.
=head2 for
my @for = @{ $platform->for };
The Fortran compiler that naturally goes with the C compiler.
=head2 ld
my $ld = $platform->ld;
The C linker
=head2 shellwords
my @words = $platform->shellwords(@strings);
This is a wrapper around L<Text::ParseWords>'s C<shellwords> with some platform workarounds
applied.
=head2 ccflags
my @ccflags = @{ $platform->cflags};
The compiler flags, including those needed to compile object files that can be linked into a dynamic library.
On Linux, for example, this is usually includes C<-fPIC>.
=head2 ldflags
my @ldflags = @{ $platform->ldflags };
The linker flags needed to link object files into a dynamic library. This is NOT the C<libs> style library
flags that specify the location and name of a library to link against, this is instead the flags that tell
the linker to generate a dynamic library. On Linux, for example, this is usually C<-shared>.
=head2 cc_mm_works
my $bool = $platform->cc_mm_works;
Returns the flags that can be passed into the C compiler to compute dependencies.
=head2 flag_object_output
my @flags = $platform->flag_object_output($object_filename);
Returns the flags that the compiler recognizes as being used to write out to a specific object filename.
=head2 flag_library_output
my @flags = $platform->flag_library_output($library_filename);
Returns the flags that the compiler recognizes as being used to write out to a specific library filename.
=head2 flag_exe_output
my @flags = $platform->flag_exe_output($library_filename);
Returns the flags that the compiler recognizes as being used to write out to a specific exe filename.
=head2 flag_export
my @flags = $platform->flag_export(@symbols);
Returns the flags that the linker recognizes for exporting functions.
=head2 which
my $path = $platform->which($command);
Returns the full path of the given command, if it is available, otherwise C<undef> is returned.
=head2 run
$platform->run(@command);
=head2 diag
Diagnostic for the platform as a string. This is for human consumption only, and the format
may and will change over time so do not attempt to use is programmatically.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut