File Coverage

blib/lib/InlineX/XS.pm
Criterion Covered Total %
statement 10 73 13.7
branch 0 46 0.0
condition 0 12 0.0
subroutine 4 7 57.1
pod 1 1 100.0
total 15 139 10.7


line stmt bran cond sub pod time code
1             package InlineX::XS;
2 1     1   757 use 5.006;
  1         4  
  1         35  
3 1     1   4 use strict;
  1         2  
  1         27  
4 1     1   5 use warnings;
  1         1  
  1         1056  
5              
6             our $VERSION = '0.02';
7              
8             =head1 NAME
9              
10             InlineX::XS - Auto-convert Inline::C based modules to XS
11              
12             =head1 SYNOPSIS
13              
14             package Your::Module;
15              
16             # Make sure your $VERSION is accessible at compile time for XSLoader:
17             # (yes, this is strict-safe)
18             our $VERSION = '0.01';
19             BEGIN {$VERSION = '0.01'}
20              
21             # Replace the use of Inline::C:
22             # use Inline C => <<'CODE';
23             # becomes:
24              
25             use InlineX::XS <<'CODE';
26             ... C code ...
27             CODE
28              
29             # Perl code, more C, more Perl...
30            
31             # Replace the final '1;' of your module with:
32             use InlineX::XS 'END';
33              
34             =head1 DESCRIPTION
35              
36             Make sure to read the CAVEATS section below before using this.
37             This is experimental software.
38              
39             =head2 Introduction
40              
41             Extending Perl with C was made much easier by the introduction of Ingy's
42             L or rather L module. It is possible to create
43             CPAN distributions which use C, but traditionally, writing
44             XS, the C-to-Perl glue language, by hand has been considered
45             superior in that regard because C writes its compiled shared
46             libraries to cache areas whereas the libraries compiled from XS are
47             properly installed. (I know, technically, C I
48             the fly>.)
49              
50             This module is intended to enable developers to use C and
51             have the C code converted to (static) XS code before they make
52             a release.
53              
54             =head2 How it works
55              
56             Mostly, you replace any invocation of C with C
57             as follows:
58              
59             use Inline C => <<'CODE';
60             ... C code ...
61             CODE
62              
63             becomes
64              
65             use InlineX::XS <<'CODE';
66             ... C code ...
67             CODE
68              
69             Note that most advanced usage of C
70             is currently B by C during packaging.
71             Also, C cannot read from the C<__DATA__> section of your
72             module.
73              
74             There are some other changes you need to make to your code, but
75             the above is the main difference. The other changes are shown in the
76             SYNOPSIS above.
77              
78             C will take the plain C code and first look for a loadable
79             shared object file which was compiled from XS and if that wasn't found,
80             fall back to passing the code to C.
81              
82             =head2 Packaging
83              
84             By forcing C into the packaging mode and compiling your C<.pm>
85             file with C, you can make it extract the
86             C code from your F<.pm> file into the F subdirectory. From there,
87             C will be used to generate a F<.xs> file in the current
88             directory.
89              
90             You may do so explicitly from the main distribution directory with the
91             following command:
92              
93             perl -c -MInlineX::XS=PACKAGE lib/Your/Module.pm
94              
95             You should now have a shiny new XS file F. Add it to the
96             distributions F file and you are good to go. But read on:
97              
98             =head2 Easier packaging
99              
100             More conveniently, you can just slightly modify your F if you
101             are using L and not the newer L or
102             L. It should be straightforward to do with those as well,
103             but I haven't explored that. Please contact me if you would like to give
104             a hand concerning support for other build systems.
105              
106             In the F, there is a call to C. Add a key/value
107             pair to the argument list of this call:
108              
109             dist => {
110             PREOP => 'perl -MInlineX::XS::MM=$(DISTNAME)-$(VERSION) -c lib/Your/Module.pm'
111             }
112              
113             Of course, you need to add a dependency on C. You do B
114             need a dependency on C. On the user's machine, the generated
115             XS code will be compiled and installed. C will not be used unless
116             the user removes the XS code before compilation.
117              
118             Given this modified F, you can issue the following usual commands
119             to create a release-ready package of your module:
120              
121             perl Makefile.PL
122             make dist
123              
124             C will take care of generating the XS and modifying your
125             F. Expect similar utility modules for C and
126             C in the future. (Help welcome, though.)
127              
128             An example distribution C can be found in the F
129             subdirectory.
130              
131             =head1 CAVEATS
132              
133             C isn't a drop-in replacement for C in some cases.
134             For example, it doesn't support reading from arbitrary files or getting the
135             code from code references.
136              
137             When passing the arguments through to C because no loadable
138             object was found, some of the various advanced Inline::C features work alright.
139             Once extracted as XS and compiled, those won't be available any more.
140              
141             The configuration options are only partially supported. Additionally,
142             there is one major discrepancy in behaviour:
143             Any configuration settings (i.e. C 'Config'...>
144             or C '...code...', cfg1=>'value1'...>) are applied
145             B In ordinary C code,
146             these are built up as the various inlined code sections are parsed and
147             compiled.
148              
149             Multiple modules which use C in the same distribution are
150             problematic. This isn't really an C problem but rather a general
151             issue with distributions that contain XS. It's possible, but I haven't
152             explored it fully.
153              
154             Naturally, if you use the C function from C to load
155             C routines at run-time, C can't interfere.
156              
157             Do not think you can use C like a random Inline language
158             module because it isn't one of those.
159              
160             # Cannot work and should not work:
161             use Inline XS => 'code';
162              
163             We can't declare our prerequisites in the C because
164             they're not needed by users who use modules which have been
165             compiled to XS.
166              
167             =head1 PREREQUISITES
168              
169             Depending on the mode of operation, this module may required various
170             other modules. For end-users who use modules which make use of
171             C, there are currently B prerequisites at all.
172              
173             Developers who use C in conjunction with C
174             need to install C.
175              
176             Those who generate distributions with XS code from the C
177             (or rather C) code need an installed C and
178             thus an installed C. B
179             C is required for packaging (only).>
180              
181             =cut
182              
183             our @INLINE_ARGS;
184             our $PACKAGE = 0;
185             our $PACKAGER;
186             our $DEBUG = 0;
187             our %SEEN_PKG; # used for determining packages without 'END' marker.
188              
189             =head1 CLASS METHODS
190              
191             =head2 debug
192              
193             Get or set the debugging flag. Defaults to false.
194              
195             =cut
196              
197             sub debug {
198 0     0 1   my $class = shift;
199 0 0         $DEBUG = shift if @_;
200 0           return $DEBUG;
201             }
202              
203             =head2 import
204              
205             Automatically called via C.
206              
207             =cut
208              
209             sub import {
210 0     0     my $class = shift;
211 0           my @args = @_;
212 0           my ($pkg) = caller(0);
213 0 0         $SEEN_PKG{$pkg} = {end => 0} if not exists $SEEN_PKG{$pkg};
214              
215 0 0         return if not @args;
216              
217             # special cases: PACKAGEing mode
218 0 0 0       if (@args==1 and $args[0] eq 'PACKAGE') {
    0 0        
    0          
219 0           warn "Entering PACKAGE-ing mode for package $pkg";
220 0           $PACKAGE = 1;
221 0           return 1;
222             }
223             # ... and END marker
224             elsif (@args == 1 and $args[0] eq 'END') {
225 0 0         warn 'Not generating XS because not in packaging mode.'
226             if $class->debug;
227 0           $SEEN_PKG{$pkg}{end} = 1; # have END for pkg using us.
228 0 0         return 1 unless $PACKAGE; # no XS if not in packaging mode.
229 0           warn 'C extraction complete';
230 0           _generate();
231 0           return 1;
232             }
233             # We're in packaging mode:
234             elsif ($PACKAGE) {
235 0 0         warn 'Saving arguments to Inline because we\'re in PACKAGE mode'
236             if $class->debug;
237 0           push @INLINE_ARGS, {pkg => $pkg, args => \@args};
238             }
239             else {
240 0 0         warn 'Trying to load shared obj file' if $class->debug;
241              
242 0           require XSLoader;
243 0           eval {
244 0           XSLoader::load($pkg);
245             };
246 0 0         return 1 if not $@;
247 0 0         warn "Failed to load shared obj file, resorting to inline. Reason for failure: $@"
248             if $class->debug;
249 0           eval "package $pkg; require Inline; Inline->import('C', \@args);";
250 0 0         die "Error while resorting to using Inline::C: $@" if $@;
251 0           return 1;
252             }
253             }
254              
255             sub _generate {
256 0     0     warn "Starting XS generation";
257              
258 0           require File::Spec;
259 0           require InlineX::C2XS;
260              
261 0           mkdir('src');
262 0           my %pkg;
263 0           foreach my $call (@INLINE_ARGS) {
264 0           my $pkg = $call->{pkg};
265 0           my $args = $call->{args};
266 0           my $code;
267              
268 0 0         $pkg{$pkg} = {config=>{}, code => 0} if not exists $pkg{$pkg};
269              
270             # Assume code was passed in if not in Config mode
271 0 0 0       if (@$args == 1 and $args->[0] ne 'Config') {
    0 0        
272 0           $code = $args->[0];
273             }
274             # We're in config-only ->mode!
275             elsif (@$args and $args->[0] eq 'Config') {
276 0 0         die "Uneven number of arguments to 'InlineX::XS \"Config\"'."
277             if (@$args-1)%2;
278            
279             # merge configuration for package
280 0           my %cfg = @{$args}[1..$#$args];
  0            
281 0           $pkg{$pkg}{config}{$_} = $cfg{$_} foreach keys %cfg;
282             }
283             # Code, then config
284             else {
285 0           $code = $args->[0];
286 0 0         die "Uneven number of arguments to 'InlineX::XS \"...code...\", ....'"
287             if (@$args-1)%2;
288            
289             # merge configuration for package
290 0           my %cfg = @{$args}[1..$#$args];
  0            
291 0           $pkg{$pkg}{config}{$_} = $cfg{$_} foreach keys %cfg;
292             }
293              
294 0 0         if (defined $code) {
295 0           my $file = $pkg;
296 0           $file =~ s/^(?:[^:]*::)*([^:]+)$/$1/;
297 0           $file .= '.c';
298 0 0         open my $fh, '>>', File::Spec->catfile('src', $file) or die $!;
299 0           print $fh "\n".$code;
300 0           close $fh;
301              
302 0           $pkg{$pkg}{code} = 1;
303             }
304             }
305              
306 0           foreach my $pkg (keys %pkg) {
307 0 0         next if not $pkg{$pkg}{code};
308              
309 0           InlineX::C2XS::c2xs($pkg, $pkg, '.', $pkg{$pkg}{config});
310 0 0         $PACKAGER->hook_after_c2xs($pkg) if $PACKAGER;
311             }
312             }
313              
314              
315             END {
316 1     1   712 foreach my $pkg (keys %SEEN_PKG) {
317 0 0         warn <
318             Package '$pkg' uses InlineX::XS but does not have a
319             use InlineX::XS 'END';
320             statement at the end. This is required in order for
321             InlineX::XS to work correctly.
322             HERE
323             }
324             }
325              
326             1;
327              
328             __END__