| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Module::Build::FFI::Fortran; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 2 |  |  | 2 |  | 103949 | use strict; | 
|  | 2 |  |  |  |  | 13 |  | 
|  | 2 |  |  |  |  | 59 |  | 
| 4 | 2 |  |  | 2 |  | 10 | use warnings; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 49 |  | 
| 5 | 2 |  |  | 2 |  | 9 | use Config; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 80 |  | 
| 6 | 2 |  |  | 2 |  | 11 | use File::Glob qw( bsd_glob ); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 220 |  | 
| 7 | 2 |  |  | 2 |  | 1081 | use File::Which qw( which ); | 
|  | 2 |  |  |  |  | 2125 |  | 
|  | 2 |  |  |  |  | 116 |  | 
| 8 | 2 |  |  | 2 |  | 1005 | use Text::ParseWords qw( shellwords ); | 
|  | 2 |  |  |  |  | 2807 |  | 
|  | 2 |  |  |  |  | 116 |  | 
| 9 | 2 |  |  | 2 |  | 15 | use File::Spec; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 51 |  | 
| 10 | 2 |  |  | 2 |  | 10 | use base qw( Module::Build::FFI ); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 1190 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | our $VERSION = '0.11'; | 
| 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 |  | 5 | return if $INC{'Module/Build/FFI/Fortran/ExtUtilsF77.pm'}; | 
| 184 | 1 |  |  | 1 |  | 59 | eval qq{ use Module::Build::FFI::Fortran::ExtUtilsF77; }; | 
|  | 1 |  |  |  |  | 613 |  | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 8 |  | 
| 185 | 1 | 50 |  |  |  | 5 | die $@ if $@; | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | sub _f77_config | 
| 189 |  |  |  |  |  |  | { | 
| 190 | 1 |  |  | 1 |  | 113 | _f77(); | 
| 191 | 1 |  |  |  |  | 7 | 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 |  |  |  |  | 4 | foreach my $compiler (qw( 90 95 )) | 
| 200 |  |  |  |  |  |  | { | 
| 201 | 2 |  |  |  |  | 7 | $config->{"f$compiler"} = $config->{f77}; | 
| 202 | 2 |  |  |  |  | 11 | $config->{"f$compiler"} =~ s/77/$compiler/; | 
| 203 |  |  |  |  |  |  | } | 
| 204 |  |  |  |  |  |  |  | 
| 205 | 1 |  |  |  |  | 4 | $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__ |