File Coverage

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 is storage for the compiler flags required for multiple compilers on multiple systems and
193             an attempt to intelligently support them.
194              
195             This module is designed to be used by the L authors and contributors, rather than end users.
196              
197             =head1 METHODS
198              
199             L implements the following methods.
200              
201             =head2 cflags
202              
203             Obtain the compiler flags for the compiler and architecture suitable for passing as C to
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.
213              
214             =head2 libs
215              
216             Obtain the compiler flags for the compiler and architecture suitable for passing as C to
217             L.
218              
219             =head2 unsupported
220              
221             Report using L or L that the compiler/architecture
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 and C.
229              
230             =cut