perl -c -MModule::Inline::C=PACKAGE blib/lib/Foo/Bar.pm # (or similar)
####
package Foo::Bar;
use 5.006;
use strict;
use warnings;
our $VERSION = '0.01';
use Module::Inline::C <<'HERE';
int
fac (int x)
{
if (x <= 0) return(1);
return(x*fac(x-1));
}
HERE
# ordinary perl code and more inlined C here
# This is for triggering the XS generation if applicable:
use Module::Inline::C 'END';
1;
__END__
=head1 NAME
Foo::Bar - Perl extension for blah blah blah
=cut
####
package Module::Inline::C;
use strict;
use warnings;
# Should work for *one Inline::C-using package per distribution only*
# right now!
our @INLINE_ARGS;
our $PACKAGE = 0;
sub import {
my $class = shift;
my @args = @_;
if (@args==1 and $args[0] eq 'PACKAGE') {
warn 'Setting PACKAGE option';
# trigger packaging/XS generation mode
$PACKAGE = 1;
return 1;
}
elsif (@args == 1 and $args[0] eq 'END') {
# All C code was received. Generate XS.
return unless $PACKAGE;
_generate();
}
elsif ($PACKAGE == 1) {
warn 'Saving arguments to Inline because we\'re in PACKAGE mode';
# Write out C code
my ($pkg) = caller(0);
push @INLINE_ARGS, {pkg => $pkg, args => \@args};
}
else {
# try to load dll/so first (user mode)
warn 'Trying to load shared obj file';
my ($pkg) = caller(0);
require XSLoader;
eval {
XSLoader::load($pkg);
};
return 1 if not $@;
# Compile using Inline::C (author mode)
warn 'failed to load shared obj file, resorting to inline';
eval "package $pkg; require Inline; Inline->import('C', \@args);";
die $@ if $@;
return 1;
}
}
sub _generate {
require File::Spec;
require Inline::C2XS;
mkdir('src');
foreach my $call (@INLINE_ARGS) {
my $pkg = $call->{pkg};
if (@{$call->{args}} != 1) {
require Data::Dumper;
warn "Skipping Inline C call from package $pkg with arguments: ".Dumper($call->{args});
next;
}
my $file = $pkg;
$file =~ s/^(?:[^:]*::)*([^:]+)$/$1/;
$file .= '.c';
open my $fh, '>>', File::Spec->catfile('src', $file) or die $!;
print $fh "\n".$call->{args}[0];
close $fh;
Inline::C2XS::c2xs($pkg, $pkg);
}
}
1;