| lib/Alien/OpenMP/configure.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 43 | 80 | 53.7 |
| branch | 15 | 34 | 44.1 |
| condition | 11 | 29 | 37.9 |
| subroutine | 12 | 14 | 85.7 |
| pod | 6 | 7 | 85.7 |
| total | 87 | 164 | 53.0 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package Alien::OpenMP::configure; | ||||||
| 2 | 4 | 4 | 216712 | use strict; | |||
| 4 | 9 | ||||||
| 4 | 167 | ||||||
| 3 | 4 | 4 | 20 | use warnings; | |||
| 4 | 10 | ||||||
| 4 | 242 | ||||||
| 4 | 4 | 4 | 44 | use Config; | |||
| 4 | 6 | ||||||
| 4 | 7386 | ||||||
| 5 | |||||||
| 6 | # ExtUtils::CBuilder uses cc not ccname | ||||||
| 7 | our $CCNAME = $ENV{CC} || $Config::Config{cc}; | ||||||
| 8 | our $OS = $^O; | ||||||
| 9 | |||||||
| 10 | my $checked = 0; | ||||||
| 11 | my $supported = { | ||||||
| 12 | gcc => { | ||||||
| 13 | cflags => ['-fopenmp'], | ||||||
| 14 | libs => ['-fopenmp'], | ||||||
| 15 | auto_include => join qq{\n}, ('#include |
||||||
| 16 | }, | ||||||
| 17 | clang => { | ||||||
| 18 | cflags => [ '-Xclang', '-fopenmp' ], | ||||||
| 19 | libs => ['-lomp'], # this could be -Xpreprocessor | ||||||
| 20 | auto_include => join qq{\n}, ('#include |
||||||
| 21 | }, | ||||||
| 22 | }; | ||||||
| 23 | |||||||
| 24 | sub auto_include { | ||||||
| 25 | 0 | 0 | 0 | 0 | shift->_update_supported; | ||
| 26 | 0 | 0 | 0 | return $supported->{$CCNAME}{auto_include} || q{}; | |||
| 27 | } | ||||||
| 28 | |||||||
| 29 | sub cflags { | ||||||
| 30 | 4 | 4 | 1 | 15 | shift->_update_supported; | ||
| 31 | 4 | 100 | 66 | 7 | return join ' ', @{$supported->{$OS}{cflags} || $supported->{$CCNAME}{cflags} || []}; | ||
| 4 | 84 | ||||||
| 32 | } | ||||||
| 33 | |||||||
| 34 | sub is_known { | ||||||
| 35 | 5 | 5 | 1 | 1176 | shift->_update_supported; | ||
| 36 | 5 | 100 | 55 | return !!(exists($supported->{$OS}) || exists($supported->{$CCNAME})); | |||
| 37 | } | ||||||
| 38 | |||||||
| 39 | 2 | 2 | 1 | 11 | sub lddlflags { __PACKAGE__->libs } | ||
| 40 | |||||||
| 41 | sub libs { | ||||||
| 42 | 4 | 4 | 1 | 14 | shift->_update_supported; | ||
| 43 | 4 | 100 | 66 | 11 | return join ' ', @{$supported->{$OS}{libs} || $supported->{$CCNAME}{libs} || []}; | ||
| 4 | 66 | ||||||
| 44 | } | ||||||
| 45 | |||||||
| 46 | sub unsupported { | ||||||
| 47 | 1 | 1 | 1 | 2199 | my ($self, $build) = (shift, shift); | ||
| 48 | |||||||
| 49 | # build an array of messages | ||||||
| 50 | 1 | 5 | my @msg = ("This version of $CCNAME does not support OpenMP"); | ||||
| 51 | 1 | 50 | 33 | 6 | if ($CCNAME eq 'gcc' and $OS ne 'darwin') { | ||
| 52 | 0 | 0 | push @msg, "This could be a bug, please record an issue https://github.com/Perl-OpenMP/p5-Alien-OpenMP/issues"; | ||||
| 53 | } | ||||||
| 54 | |||||||
| 55 | 1 | 50 | 4 | if ($OS eq 'darwin') { | |||
| 56 | 0 | 0 | push @msg, "Support can be enabled by using Homebrew or Macports (https://clang-omp.github.io)"; | ||||
| 57 | 0 | 0 | push @msg, " brew install libomp (Homebrew https://brew.sh)"; | ||||
| 58 | 0 | 0 | push @msg, " port install libomp (Macports https://www.macports.org)"; | ||||
| 59 | } | ||||||
| 60 | |||||||
| 61 | # report messages using appropriate method | ||||||
| 62 | 1 | 50 | 33 | 8 | if (ref($build)) { | ||
| 50 | |||||||
| 63 | 0 | 0 | 0 | return if $build->install_prop->{alien_openmp_compiler_has_openmp}; | |||
| 64 | 0 | 0 | unshift @msg, "phase = @{[$build->meta->{phase}]}"; | ||||
| 0 | 0 | ||||||
| 65 | 0 | 0 | $build->log($_) for @msg; | ||||
| 66 | } | ||||||
| 67 | elsif ($build && (my $log = $build->can('log'))) { | ||||||
| 68 | 0 | 0 | unshift @msg, "phase = @{[$build->meta->{phase}]}"; | ||||
| 0 | 0 | ||||||
| 69 | 0 | 0 | $log->($_) for @msg; | ||||
| 70 | } | ||||||
| 71 | else { | ||||||
| 72 | 1 | 95 | warn join q{>}, __PACKAGE__, " $_\n" for @msg; | ||||
| 73 | } | ||||||
| 74 | 1 | 36 | print "OS Unsupported\n"; | ||||
| 75 | } | ||||||
| 76 | |||||||
| 77 | sub version_from_preprocessor { | ||||||
| 78 | 2 | 2 | 1 | 5260 | my ($self, $lines) = @_; | ||
| 79 | 2 | 9 | my $define_re = qr/^(?:.*_OPENMP\s)?([0-9]+)$/; | ||||
| 80 | 2 | 4 | my %runtime; | ||||
| 81 | 2 | 77 | ($runtime{openmp_version}) = map { (my $v = $_) =~ s/$define_re/$1/; $v } grep /$define_re/, split m{$/}, $lines; | ||||
| 1 | 23 | ||||||
| 1 | 9 | ||||||
| 82 | 2 | 8 | $runtime{version} = _openmp_defined($runtime{openmp_version}); | ||||
| 83 | 2 | 9 | return \%runtime; | ||||
| 84 | } | ||||||
| 85 | |||||||
| 86 | sub _openmp_defined { | ||||||
| 87 | 2 | 2 | 5 | my $define = pop; | |||
| 88 | |||||||
| 89 | # From https://github.com/jeffhammond/HPCInfo/blob/master/docs/Preprocessor-Macros.md | ||||||
| 90 | # also https://www.openmp.org/specifications/ | ||||||
| 91 | 2 | 21 | my $versions = { | ||||
| 92 | 200505 => '2.5', | ||||||
| 93 | 200805 => '3.0', | ||||||
| 94 | 201107 => '3.1', | ||||||
| 95 | 201307 => '4.0', | ||||||
| 96 | 201511 => '4.5', | ||||||
| 97 | 201811 => '5.0', | ||||||
| 98 | 202011 => '5.1', | ||||||
| 99 | 202111 => '5.2', | ||||||
| 100 | 202411 => '6.0', | ||||||
| 101 | }; | ||||||
| 102 | 2 | 100 | 23 | return $versions->{$define || ''} || 'unknown'; | |||
| 103 | } | ||||||
| 104 | |||||||
| 105 | # test support only | ||||||
| 106 | 4 | 4 | 243498 | sub _reset { $checked = 0; } | |||
| 107 | |||||||
| 108 | sub _update_supported { | ||||||
| 109 | 13 | 100 | 13 | 48 | return if $checked; | ||
| 110 | 5 | 44 | require File::Basename; | ||||
| 111 | |||||||
| 112 | # handles situation where $CCNAME is gcc as part of a path | ||||||
| 113 | 5 | 281 | $CCNAME = File::Basename::basename($CCNAME); | ||||
| 114 | |||||||
| 115 | 5 | 50 | 32 | if ($OS eq 'darwin') { | |||
| 50 | |||||||
| 100 | |||||||
| 116 | 0 | 0 | require File::Which; | ||||
| 117 | 0 | 0 | require Path::Tiny; | ||||
| 118 | |||||||
| 119 | # The issue here is that ccname=gcc and cc=cc as an interface to clang | ||||||
| 120 | # First check if clang/gcc, then discern omp location | ||||||
| 121 | 0 | 0 | my $flavour = _compiler_flavour(); | ||||
| 122 | 0 | 0 | 0 | 0 | if ($flavour eq 'clang' || $flavour eq 'default') { | ||
| 0 | |||||||
| 123 | 0 | 0 | $supported->{darwin} = {cflags => ['-Xclang', '-fopenmp'], libs => ['-lomp'],}; | ||||
| 124 | 0 | 0 | 0 | $supported->{$CCNAME} ||= {auto_include => join qq{\n}, ('#include |
|||
| 125 | } | ||||||
| 126 | elsif ($flavour eq 'gcc') { | ||||||
| 127 | 0 | 0 | $supported->{darwin} = {cflags => ['-fopenmp'], libs => ['-lomp'],}; | ||||
| 128 | 0 | 0 | 0 | $supported->{$CCNAME} ||= {auto_include => join qq{\n}, ('#include |
|||
| 129 | } | ||||||
| 130 | else { | ||||||
| 131 | 0 | 0 | return ++$checked; | ||||
| 132 | } | ||||||
| 133 | |||||||
| 134 | 0 | 0 | 0 | if (my $mp = File::Which::which('port')) { | |||
| 135 | |||||||
| 136 | # macports /opt/local/bin/port | ||||||
| 137 | 0 | 0 | my $mp_prefix = Path::Tiny->new($mp)->parent->parent; | ||||
| 138 | 0 | 0 | push @{$supported->{darwin}{cflags}}, "-I$mp_prefix/include/libomp"; | ||||
| 0 | 0 | ||||||
| 139 | 0 | 0 | unshift @{$supported->{darwin}{libs}}, "-L$mp_prefix/lib/libomp"; | ||||
| 0 | 0 | ||||||
| 140 | } | ||||||
| 141 | else { | ||||||
| 142 | # homebrew has the headers and library in /usr/local, but is not always symlinked | ||||||
| 143 | 0 | 0 | push @{$supported->{darwin}{cflags}}, "-I/usr/local/include", "-I/opt/homebrew/opt/libomp/include"; | ||||
| 0 | 0 | ||||||
| 144 | 0 | 0 | unshift @{$supported->{darwin}{libs}}, "-L/usr/local/lib", "-L/opt/homebrew/opt/libomp/lib"; | ||||
| 0 | 0 | ||||||
| 145 | } | ||||||
| 146 | } | ||||||
| 147 | elsif ($OS eq 'freebsd') { | ||||||
| 148 | 0 | 0 | $CCNAME = 'clang'; | ||||
| 149 | } | ||||||
| 150 | # covers case where "CCNAME" is "cc" but it's still "gcc"; the alternative is to add | ||||||
| 151 | # 'cc' to the supported hash above, which seems to restrictive | ||||||
| 152 | elsif ($CCNAME eq "cc") { | ||||||
| 153 | 1 | 50 | 11 | $CCNAME = 'gcc' if defined $Config::Config{gccversion}; | |||
| 154 | } | ||||||
| 155 | 5 | 12 | $checked++; | ||||
| 156 | } | ||||||
| 157 | |||||||
| 158 | # not looking for openmp | ||||||
| 159 | sub _compiler_flavour { | ||||||
| 160 | 0 | 0 | my $defines = qx{$CCNAME -dM -E - < /dev/null}; | ||||
| 161 | 0 | 0 | return 'clang' if ($defines =~ m{^#define __clang__}m); | ||||
| 162 | 0 | 0 | 0 | return 'gcc' if ($defines =~ m{^#define __GCC_}m && $defines =~ m{^#define __APPLE__}m); | |||
| 163 | 0 | return 'default'; | |||||
| 164 | } | ||||||
| 165 | |||||||
| 166 | 1; | ||||||
| 167 | |||||||
| 168 | =encoding utf8 | ||||||
| 169 | |||||||
| 170 | =head1 NAME | ||||||
| 171 | |||||||
| 172 | Alien::OpenMP::configure - Install time configuration helper | ||||||
| 173 | |||||||
| 174 | =head1 SYNOPSIS | ||||||
| 175 | |||||||
| 176 | # alienfile | ||||||
| 177 | use Alien::OpenMP::configure; | ||||||
| 178 | |||||||
| 179 | if (!Alien::OpenMP::configure->is_known) { | ||||||
| 180 | Alien::OpenMP::configure->unsupported(__PACKAGE__); | ||||||
| 181 | exit; | ||||||
| 182 | } | ||||||
| 183 | |||||||
| 184 | plugin 'Probe::CBuilder' => ( | ||||||
| 185 | cflags => Alien::OpenMP::configure->cflags, | ||||||
| 186 | libs => Alien::OpenMP::configure->libs, | ||||||
| 187 | ... | ||||||
| 188 | ); | ||||||
| 189 | |||||||
| 190 | =head1 DESCRIPTION | ||||||
| 191 | |||||||
| 192 | L |
||||||
| 193 | an attempt to intelligently support them. | ||||||
| 194 | |||||||
| 195 | This module is designed to be used by the L |
||||||
| 196 | |||||||
| 197 | =head1 METHODS | ||||||
| 198 | |||||||
| 199 | L |
||||||
| 200 | |||||||
| 201 | =head2 cflags | ||||||
| 202 | |||||||
| 203 | Obtain the compiler flags for the compiler and architecture suitable for passing as C |
||||||
| 204 | L |
||||||
| 205 | |||||||
| 206 | =head2 is_known | ||||||
| 207 | |||||||
| 208 | Return a Boolean to indicate whether the compiler is known to this module. | ||||||
| 209 | |||||||
| 210 | =head2 lddlflags | ||||||
| 211 | |||||||
| 212 | A synonym for L"libs">. | ||||||
| 213 | |||||||
| 214 | =head2 libs | ||||||
| 215 | |||||||
| 216 | Obtain the compiler flags for the compiler and architecture suitable for passing as C |
||||||
| 217 | L |
||||||
| 218 | |||||||
| 219 | =head2 unsupported | ||||||
| 220 | |||||||
| 221 | Report using L |
||||||
| 222 | combination is unsupported and provide minimal notes on any solutions. There is little to no guarding of the actual | ||||||
| 223 | state of support in this function. | ||||||
| 224 | |||||||
| 225 | =head2 version_from_preprocessor | ||||||
| 226 | |||||||
| 227 | Parse the output from the C preprocessor, filtering for the C<#define _OPENMP> to populate a hash with both the value | ||||||
| 228 | and the equivalent decimal version. The keys of the hash are C |
||||||
| 229 | |||||||
| 230 | =cut |