File Coverage

blib/lib/Module/XSOrPP.pm
Criterion Covered Total %
statement 63 84 75.0
branch 28 60 46.6
condition 3 6 50.0
subroutine 9 9 100.0
pod 3 3 100.0
total 106 162 65.4


line stmt bran cond sub pod time code
1             package Module::XSOrPP;
2              
3             our $DATE = '2016-01-15'; # DATE
4             our $VERSION = '0.11'; # VERSION
5              
6 1     1   762 use 5.010001;
  1         4  
7 1     1   4 use strict;
  1         2  
  1         19  
8 1     1   5 use warnings;
  1         1  
  1         26  
9              
10 1     1   790 use Dist::Util qw(packlist_for);
  1         956  
  1         71  
11 1     1   912 use Module::Path::More qw(module_path);
  1         1066  
  1         160  
12              
13             require Exporter;
14             our @ISA = qw(Exporter);
15             our @EXPORT_OK = qw(
16             is_xs
17             is_pp
18             xs_or_pp
19             );
20              
21             our @XS_OR_PP_MODULES = qw(
22             DateTime
23             List::MoreUtils
24             Params::Util
25             Params::Validate
26             );
27              
28             our @XS_MODULES = qw(
29             Scalar::Util
30             );
31              
32             our @PP_MODULES = qw(
33             );
34              
35             sub xs_or_pp {
36 1     1   781 use experimental 'smartmatch';
  1         4212  
  1         7  
37              
38 13     13 1 29 my ($mod, $opts) = @_;
39 13 50       37 die "Please specify module\n" unless $mod;
40              
41 13 50       40 if ($mod =~ m!/!) {
42 0         0 $mod =~ s!/!::!g;
43 0         0 $mod =~ s/\.pm$//;
44             }
45              
46 13   50     65 $opts //= {};
47 13   50     69 $opts->{warn} //= 0;
48 13         25 my $warn = $opts->{warn};
49 13   50     53 $opts->{debug} //= 0;
50 13         23 my $debug = $opts->{debug};
51              
52 13 100       62 if ($mod ~~ @XS_OR_PP_MODULES) {
53 3 50       15 warn "$mod is xs_or_pp (from list)\n" if $debug;
54 3         14 return "xs_or_pp";
55             }
56              
57 10 100       36 if ($mod ~~ @XS_MODULES) {
58 3 50       9 warn "$mod is xs (from list)\n" if $debug;
59 3         14 return "xs";
60             }
61              
62 7 50       17 if ($mod ~~ @PP_MODULES) {
63 0 0       0 warn "$mod is pp (from list)\n" if $debug;
64 0         0 return "pp";
65             }
66              
67 7         24 my $path = packlist_for($mod);
68             {
69 7 50       3629 last unless $path;
  7         24  
70 0         0 my $fh;
71 0 0       0 unless (open $fh, '<', $path) {
72 0 0       0 warn "Can't open .packlist $path: $!\n" if $warn;
73 0         0 last;
74             }
75 0         0 while (my $line = <$fh>) {
76 0         0 chomp $line;
77 0 0       0 if ($line =~ /\.(bs|so|[Dd][Ll][Ll])\z/) {
78 0 0       0 warn "$mod is XS because the .packlist contains .{bs,so,dll} files\n" if $debug;
79 0         0 return "xs";
80             }
81             }
82 0 0       0 warn "$mod is PP because the .packlist doesn't contain any .{bs,so,dll} files\n" if $debug;
83 0         0 return "pp";
84             }
85              
86 7         23 $path = module_path(module=>$mod);
87             {
88 7 100       21 last unless $path;
89 6         19 local $/;
90 6         7 my $fh;
91 6 50       286 unless (open $fh, '<', $path) {
92 0 0       0 warn "Can't open module file $path: $!" if $warn;
93 0         0 last;
94             }
95 6         243 while (my $content = <$fh>) {
96 6 100       2011 if ($content =~ m!^\s*(use|require) \s+ (DynaLoader|XSLoader)\b!mx) {
97 3 50       9 warn "$mod is XS because the source contains 'use {DynaLoader,XSLoader}' statement\n" if $debug;
98 3         45 return "xs";
99             }
100             }
101 3 50       11 warn "$mod is PP because the source code doesn't contain any 'use {DynaLoader,XSLoader}' statement\n" if $debug;
102 3         41 return "pp";
103             }
104              
105             {
106 7         3434 my $mod = $mod;
  1         2  
  1         3  
107 1 50       4 unless ($mod =~ /\.pm\z/) { $mod =~ s!::!/!g; $mod .= ".pm" }
  1         3  
  1         2  
108              
109 1 50       13 if ($mod =~ m!/XS\.pm\z|/[^/]+_(xs|XS)\.pm\z!) {
    50          
110 0 0       0 warn "$mod is probably XS because its name contains XS" if $debug;
111 0         0 return "xs";
112             } elsif ($mod =~ m!/PP\.pm\z|/[^/]+_(pp|PP)\.pm\z!) {
113 0 0       0 warn "$mod is probably PP because its name contains PP" if $debug;
114 0         0 return "pp";
115             }
116             }
117              
118 1 50       4 warn "Can't determine whether $mod is XS: all methods tried\n" if $warn;
119 1         5 undef;
120             }
121              
122             sub is_xs {
123 5     5 1 15 my ($mod, $opts) = @_;
124 5         13 my $res = xs_or_pp($mod, $opts);
125 5 100       21 return undef unless defined($res);
126 4 100       31 $res eq 'xs' || $res eq 'xs_or_pp';
127             }
128              
129             sub is_pp {
130 4     4 1 12 my ($mod, $opts) = @_;
131 4         13 my $res = xs_or_pp($mod, $opts);
132 4 50       15 return undef unless defined($res);
133 4 100       49 $res eq 'pp' || $res eq 'xs_or_pp';
134             }
135              
136             1;
137             # ABSTRACT: Determine if an installed module is XS or pure-perl
138              
139             __END__
140              
141             =pod
142              
143             =encoding UTF-8
144              
145             =head1 NAME
146              
147             Module::XSOrPP - Determine if an installed module is XS or pure-perl
148              
149             =head1 VERSION
150              
151             This document describes version 0.11 of Module::XSOrPP (from Perl distribution Module-XSOrPP), released on 2016-01-15.
152              
153             =head1 SYNOPSIS
154              
155             use Module::XSOrPP qw(
156             is_xs is_pp xs_or_pp
157             );
158              
159             say "Class::XSAccessor is an XS module" if is_xs("Class/XSAccessor.pm");
160             say "JSON::PP is a pure-Perl module" if is_pp("JSON::PP");
161             say "Params::Util is an XS module with PP fallback" if xs_or_pp("Class/XSAccessor.pm") =~ /^(xs|xs_or_pp)$/;
162              
163             =head1 DESCRIPTION
164              
165             =head1 FUNCTIONS
166              
167             =head2 xs_or_pp($mod, \%opts) => str
168              
169             Return either "xs", "pp", or "xs_or_pp" (XS with a PP fallback). Return undef if
170             can't determine which. C<$mod> value can be in the form of C<Package/SubPkg.pm>
171             or C<Package::SubPkg>. The following ways are tried, in order:
172              
173             =over
174              
175             =item * Predetermined list
176              
177             Some CPAN modules are XS with a PP fallback. This module maintains the list.
178              
179             =item * Looking at the C<.packlist>
180              
181             If a .{bs,so,dll} file is listed in the C<.packlist>, then it is assumed to be
182             an XS module. This method will fail if there is no C<.packlist> available (e.g.
183             core or uninstalled or when the package management strips the packlist), or if a
184             dist contains both pure-Perl and XS.
185              
186             =item * Looking at the source file for usage of C<XSLoader> or C<DynaLoader>
187              
188             If the module source code has something like C<use XSLoader;> or <use
189             DynaLoader;> then it is assumed to be an XS module. This is currently
190             implemented using a simple regex, so it is somewhat brittle.
191              
192             =item * Guessing from the name
193              
194             If the module has "XS" in its name then it's assumed to be an XS module. If the
195             module has "PP" in its name, it's assumed to be a pure-Perl module.
196              
197             Known false positives will be prevented in the future.
198              
199             =back
200              
201             Other methods will be added in the future (e.g. a database like in
202             L<Module::CoreList>, consulting MetaCPAN, etc).
203              
204             Options:
205              
206             =over
207              
208             =item * warn => BOOL (default: 0)
209              
210             If set to true, will warn to STDERR if fail to determine.
211              
212             =item * debug => BOOL (default: 0)
213              
214             If set to true will print debugging message to STDERR.
215              
216             =back
217              
218             =head2 is_xs($mod, \%opts) => BOOL
219              
220             Return true if module C<$mod> is an XS module, false if a pure Perl module, or
221             undef if can't determine either. See C<xs_or_pp> for more details.
222              
223             =head2 is_pp($mod, \%opts) => BOOL
224              
225             Return true if module C<$mod> is a pure Perl module or XS module with a PP
226             fallback. See C<is_xs> for more details. See C<xs_or_pp> for more details.
227              
228             =head1 HOMEPAGE
229              
230             Please visit the project's homepage at L<https://metacpan.org/release/Module-XSOrPP>.
231              
232             =head1 SOURCE
233              
234             Source repository is at L<https://github.com/sharyanto/perl-SHARYANTO-Module-Util>.
235              
236             =head1 BUGS
237              
238             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Module-XSOrPP>
239              
240             When submitting a bug or request, please include a test-file or a
241             patch to an existing test-file that illustrates the bug or desired
242             feature.
243              
244             =head1 AUTHOR
245              
246             perlancar <perlancar@cpan.org>
247              
248             =head1 COPYRIGHT AND LICENSE
249              
250             This software is copyright (c) 2016 by perlancar@cpan.org.
251              
252             This is free software; you can redistribute it and/or modify it under
253             the same terms as the Perl 5 programming language system itself.
254              
255             =cut