File Coverage

blib/lib/Module/Build/FFI/Fortran.pm
Criterion Covered Total %
statement 36 93 38.7
branch 2 38 5.2
condition 0 6 0.0
subroutine 11 15 73.3
pod 2 2 100.0
total 51 154 33.1


line stmt bran cond sub pod time code
1             package Module::Build::FFI::Fortran;
2              
3 2     2   140074 use strict;
  2         6  
  2         49  
4 2     2   9 use warnings;
  2         4  
  2         41  
5 2     2   11 use Config;
  2         3  
  2         64  
6 2     2   9 use File::Glob qw( bsd_glob );
  2         3  
  2         158  
7 2     2   801 use File::Which qw( which );
  2         1693  
  2         124  
8 2     2   844 use Text::ParseWords qw( shellwords );
  2         2141  
  2         105  
9 2     2   15 use File::Spec;
  2         4  
  2         47  
10 2     2   8 use base qw( Module::Build::FFI );
  2         3  
  2         902  
11              
12             our $VERSION = '0.10';
13              
14             =head1 NAME
15              
16             Module::Build::FFI::Fortran - Build Perl extensions in Fortran with FFI
17              
18             =head1 DESCRIPTION
19              
20             L variant for writing Perl extensions in Fortran with
21             FFI (sans XS).
22              
23             =head1 BASE CLASS
24              
25             All methods, properties and actions are inherited from:
26              
27             L
28              
29             =head1 METHODS
30              
31             =head2 ffi_have_compiler
32              
33             my $has_compiler = $mb->ffi_have_compiler;
34              
35             Returns true if Fortran is available.
36              
37             =cut
38              
39             sub _filter
40             {
41 0 0   0   0 grep { $_ ne '-no-cpp-precomp' && $_ !~ /^-[DI]/ } @_;
  0         0  
42             }
43              
44             sub ffi_have_compiler
45             {
46 0     0 1 0 my($self) = @_;
47            
48 0         0 my %ext;
49            
50 0         0 foreach my $dir (@{ $self->ffi_source_dir }, @{ $self->ffi_libtest_dir })
  0         0  
  0         0  
51             {
52 0 0       0 next unless -d $dir;
53 0         0 $ext{$_} = 1 for map { s/^.*\.//; $_ } bsd_glob("$dir/*.{f,for,f90,f95}");
  0         0  
  0         0  
54             }
55              
56 0 0       0 return unless %ext;
57              
58 0 0 0     0 if($ext{f} || $ext{for})
59             {
60             #warn "testing Fortran 77";
61 0 0       0 return unless $self->_f77_testcompiler;
62             }
63            
64 0 0       0 if($ext{f90})
65             {
66             #warn "testing Fortran 90";
67             # TODO: do an actual test on the compiler, not just
68             # check for it in the PATH
69 0 0       0 return unless which($self->_f77_config->{f90});
70             }
71              
72 0 0       0 if($ext{f95})
73             {
74             #warn "testing Fortran 95";
75             # TODO: do an actual test on the compiler, not just
76             # check for it in the PATH
77 0 0       0 return unless which($self->_f77_config->{f95});
78             }
79            
80 0         0 1;
81             }
82              
83             =head2 ffi_build_dynamic_lib
84              
85             my $dll_path = $mb->ffi_build_dynamic_lib($src_dir, $name, $target_dir);
86             my $dll_path = $mb->ffi_build_dynamic_lib($src_dir, $name);
87              
88             Works just like the version in the base class, except builds Fortran
89             sources.
90              
91             =cut
92              
93             sub ffi_build_dynamic_lib
94             {
95 0     0 1 0 my($self, $dirs, $name, $dest_dir) = @_;
96            
97 0   0     0 $dest_dir ||= $dirs->[0];
98            
99 0         0 my $f77_config = $self->_f77_config;
100             my @cflags = _filter (
101             shellwords($f77_config->{cflags}),
102             # hopefully the Fortran compiler understands the same flags as the C compiler
103             shellwords($Config{ccflags}),
104             shellwords($Config{cccdlflags}),
105             shellwords($Config{optimize})
106 0         0 );
107            
108 0 0       0 if($self->extra_linker_flags)
109             {
110 0 0       0 if(ref($self->extra_linker_flags))
111             {
112 0         0 push @cflags, @{ $self->extra_linker_flags };
  0         0  
113             }
114             else
115             {
116 0         0 push @cflags, shellwords($self->extra_linker_flags);
117             }
118             }
119            
120 0         0 my @obj;
121 0         0 my $count = 0;
122            
123 0         0 foreach my $dir (@$dirs)
124             {
125             push @obj, map {
126            
127 0         0 my $filename = $_;
  0         0  
128 0         0 my $obj_name = $filename;
129 0         0 $obj_name =~ s{\.(f|for|f90|f95)$}{$Config{obj_ext}};
130 0         0 my $ext = $1;
131            
132 0         0 my $source_time = (stat $filename)[9];
133 0         0 my $obj_time = (stat $obj_name)[9];
134            
135 0 0       0 unless($obj_time >= $source_time)
136             {
137 0         0 $self->add_to_cleanup($obj_name);
138            
139 0         0 my $compiler = $f77_config->{f77};
140 0 0       0 $compiler = $f77_config->{f90} if $ext eq 'f90';
141 0 0       0 $compiler = $f77_config->{f95} if $ext eq 'f95';
142            
143 0         0 my @cmd = (
144             $compiler,
145             '-c',
146             '-o' => $obj_name,
147             @cflags,
148             $filename,
149             );
150            
151 0         0 print "@cmd\n";
152 0         0 system @cmd;
153 0 0       0 exit 2 if $?;
154 0         0 $count++;
155             }
156            
157 0         0 $obj_name;
158            
159             } bsd_glob("$dir/*.{f,for,f90,f95}");
160             }
161            
162 0         0 my $b = $self->cbuilder;
163            
164 0         0 my $libfile = $b->lib_file(File::Spec->catfile($dest_dir, $b->object_file("$name.c")));
165 0 0       0 return $libfile unless $count;
166            
167 0 0       0 if($^O ne 'MSWin32')
168             {
169 0         0 return $b->link(
170             lib_file => $libfile,
171             objects => \@obj,
172             extra_linker_flags => $self->extra_linker_flags,
173             );
174             }
175             else
176             {
177 0         0 die "TODO"; # See Module::Build::FFI
178             }
179             }
180              
181             sub _f77
182             {
183 1 50   1   4 return if $INC{'Module/Build/FFI/Fortran/ExtUtilsF77.pm'};
184 1     1   51 eval qq{ use Module::Build::FFI::Fortran::ExtUtilsF77; };
  1         499  
  1         3  
  1         6  
185 1 50       4 die $@ if $@;
186             }
187              
188             sub _f77_config
189             {
190 1     1   106 _f77();
191 1         2 my $config = {
192             runtime => Module::Build::FFI::Fortran::ExtUtilsF77::runtime(),
193             trailing_underscore => Module::Build::FFI::Fortran::ExtUtilsF77::trail_(),
194             cflags => Module::Build::FFI::Fortran::ExtUtilsF77::cflags(),
195             f77 => Module::Build::FFI::Fortran::ExtUtilsF77::compiler(),
196             };
197              
198             # Just guessing...
199 1         3 foreach my $compiler (qw( 90 95 ))
200             {
201 2         7 $config->{"f$compiler"} = $config->{f77};
202 2         7 $config->{"f$compiler"} =~ s/77/$compiler/;
203             }
204            
205 1         2 $config;
206             }
207              
208             sub _f77_testcompiler
209             {
210 0     0   0 _f77();
211 0         0 Module::Build::FFI::Fortran::ExtUtilsF77::testcompiler();
212             }
213              
214             1;
215              
216             __END__