Inline::Module::Tutorial - Make "XS" modules for CPAN. The easy way!
This tutorial will teach you how to write "extension" modules for CPAN using Inline::Module. The old way to do this is with "XS", Perl's mechanism for binding C and C++ code to Perl. Inline::Module lets you do this much easier, avoiding the need to learn XS, but delivering results that are as robust as hand-written XS.
The tutorial starts by showing you how an example module (that is actually on CPAN), was created with Inline::Module. The module is called Acme::Math::XS, and its purpose (besides trivial math functions) is to demonstrate how to do this.
No matter which framework you use to make modules (ExtUtils::MakeMaker, Dist::Zilla) etc, you'll need to add a little metadata to the controls. For now we'll just show Makefile.PL (ExtUtils::MakeMaker) way:
Makefile.PL
use lib 'inc'; use ExtUtils::MakeMaker; use Inline::Module; WriteMakefile( NAME => 'Acme::Math::XS', ... postamble => { inline => { module => 'Acme::Math::XS', stub => 'Acme::Math::XS::Inline', ilsm => 'Inline::C', }, }, );
So you need to use Inline::Module and add a postamble section with an inline section (with at least a module name) to WriteMakefile. The arguments specify the information that Inline::Module needs to do the right things. The stub and ilsm values above are the defaults, so not really needed in that example. All the values for those keys can be either a string or an array ref of strings (ie single or multiple values).
use Inline::Module
postamble
inline
module
WriteMakefile
stub
ilsm
NOTE: You also need to add inc to @INC, even if doesn't exist while you are developing and testing. It will exist when you ship it to CPAN, and the make install process needs it to work properly. The Makefile.PL or Build.PL will actually die if you don't put inc first in @INC.
inc
make install
@INC
See Alt::Acme::Math::XS::EUMM for a working example on CPAN.
Just to reiterate, whether you are using ExtUtils::MakeMaker, Module::Build, Module::Install, Dist::Zilla or Zilla::Dist, you need to specify 3 Meta values:
One or more module names that live under the lib directory and use Inline inside. This value is required in all cases (no default).
lib
One or more names of stub modules, that are referenced in the module modules listed above. The default is to add '::Inline' to each value of the module keyword.
A list of the Inline Language Support Modules (ILSMs) needed. Usually just one of Inline::C (the default) or Inline::CPP.
Each framework list above has a slightly different way to express the values, but they will all seem normal in that framework. See below.
Next we'll "inline" some C code into a Perl module called lib/Acme/Math/XS.pm. Here is the real module, but condensed a bit:
lib/Acme/Math/XS.pm
use strict; use warnings; package Acme::Math::XS; our $VERSION = '1.2.3'; use Exporter 'import'; our @EXPORT = qw(add subtract); use Acme::Math::XS::Inline C => <<'...'; long add(long a, long b) { return a + b; } long subtract(long a, long b) { return a - b; } ... 1;
Normally you use Inline::C like this:
use Inline C => '<... c code ...>';
but here we just change Inline to Acme::Math::XS::Inline. This is the key part of how Inline::Module works. Since we want to use Inline but not depend on it when the user installs this module, we do this trick. The ::Inline module is a little generated stub that knows how to do all the right magics.
Inline
Acme::Math::XS::Inline
::Inline
Next you'll need to actually generate the stub module. Run this command:
perl -MInline::Module=makestub,Acme::Math::XS::Inline
You'll get a lib/Acme/Math/XS/Inline.pm that looks like this:
lib/Acme/Math/XS/Inline.pm
use strict; use warnings; package Acme::Math::XS::Inline; use Inline::Module stub => 'v2'; 1;
The astute tutorialist will note that this module depends on Inline::Module, and that's a no-no. That's because this stub is used for author side development and testing, and another stub replaces it at module release time.
Inline::Module
The replacement stub will look like this:
use strict; use warnings; package Acme::Math::XS::Inline; use base 'DynaLoader'; bootstrap Acme::Math::XS::Inline; 1;
And everything is fine. We are just using Dynaloader, the age old loader of extension libraries. As long the shared library stuff gets built into the blib directory at user build time (and it does!) we are good to go.
blib
The stub module is generated code, and many people don't like to commit generated code. There are a couple ways to deal with this.
The first is just to commit it anyway, because it only has to change very rarely; when you install a new Inline::Module whose API version has changed. You will get an error telling you to regenerate the stub. One main advantage of this is that your project collaborators don't need to know about generating the stub.
The second way is to ignore it in your source control (ie .gitignore file).
.gitignore
There are a few ways to test stuff and I'll describe them here. They should be familiar to most module authors.
prove -l t/
This is the easiest and most common method of testing for non-XS module authors. Since Inline is involved, the compilation steps just work.
With XS, you typically need to run perl Makefile.PL && make first, and you also need to add the -b flag to prove to tell it to look in the new blib. Then you need to continually make sure to repeat this every time you change C code. With Inline, you can relax a bit.
perl Makefile.PL && make
-b
prove
perl Makefile.PL && make test
You can also use the XS style. It all works out the same.
prove -b t/
In this style, you are just invoking the blib results directly, and Inline is not involved. Use this if you want to know that no nothing is up a sleeve, but don't forget that auto-compilation can't happen this way.
Now it's time to make the final product and ship it to CPAN. The mechanics are dead simple:
perl Makefile.PL make dist
Same as any other module. Some magic is happening though to make it all work. You asked for this magic in your Makefile.PL changes!
Inline::Module modifies 2 targets in the Makefile:
distdir
This is the target that buils the distribution directory (before it is tarred up).
pure_all
This odd sounding rule is actually the primary/default rule. It gets invoked when you run:
make
without arguments. In other words, the build step.
In the distdir phase, we need to:
Add the Inline modules that control building, under inc/:
inc/
Inline::C
a few helper modules
We also need to move the test/build stub module under inc/ and put the Dynaloader version in its place under lib.
Dynaloader
The pure_all phase is simply tweaked to rearrange the exact location of things that get generated under blib. Then they are ready to be installed properly/normally by make install.
Assuming you've done all the other parts of normal CPAN modules authoring, we are done here. Upload your module and watch CPAN Testers for results on tons of different platforms.
This section will describe how to do everything we just did, using the other popular CPAN build systems, like Dist::Zilla.
For Dist::Zilla, install Dist::Zilla::Plugin::InlineModule and add this to your dist.ini file:
dist.ini
[InlineModule] module = Acme::Math::XS stub = Acme::Math::XS::Inline ilsm = Inline::C
Again, the last 2 lines are defaults. You can use any of the keywords multiple times.
See Alt::Acme::Math::XS::DistZilla for a working example on CPAN.
Install Module::Build::InlineModule and write a Build.PL file that looks like this:
Build.PL
use lib 'inc'; use Module::Build::InlineModule; Module::Build::InlineModule->new( dist_name => 'Alt-Acme-Math-XS-ModuleBuild', ... inline => { module => 'Acme::Math::XS', stub => 'Acme::Math::XS::Inline', ilsm => 'Inline::C', }, )->create_build_script();
See Alt::Acme::Math::XS::ModuleBuild for a working example on CPAN.
Install Module::Build::InlineModule and write a Makefile.PL that looks like this:
use inc::Module::Install; name 'Alt-Acme-Math-XS-ModuleInstall'; ... inline module => 'Acme::Math::XS', stub => 'Acme::Math::XS::Inline', ilsm => 'Inline::C'; WriteAll;
See Alt::Acme::Math::XS::ModuleInstall for a working example on CPAN.
Just add these lines to your Meta file:
=zild: inline: module: Acme::Math::XS stub: Acme::Math::XS::Inline ilsm: Inline::C
See Alt::Acme::Math::XS::ZillaDist for a working example on CPAN.
Putting a lot of C code in your Perl might be too messy. You can just move it to a .c file, and then change your Perl to this:
.c
use Acme::Math::XS::Inline C => 'lib/Acme/Math/XS.c';
Note: You can put the .c file anywhere you want, except at the top level. Doing that will confuse the Makefile. If you put it under lib like above, it will get installed like .pm files. This might be desirable since it opens all your source (just lie when it's actually inline).
.pm
Under the hood, Inline::Module does quite a few things different when you use Inline::CPP, but this should be mostly transparent to you. Just make sure you declare your ilsm keyword to be Inline::CPP and everything should work fine.
Inline::CPP
C
C++
It may be possible (though highly experimental) to use other Inline Language Support Modules (ILSMs), like Java or Python. Simply list your ILSM of choice in the Meta section, and see what happens.
Report any issues to: https://github.com/ingydotnet/inline-module-pm/issues
This document reflects the current state of Inline::Module. At this time, it is brand new, so please report any bugs and/or misgivings.
Ingy döt Net <ingy@cpan.org>
David Oswald <davido@cpan.org>
Copyright 2014. Ingy döt Net.
To install Inline::Module, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Inline::Module
CPAN shell
perl -MCPAN -e shell install Inline::Module
For more information on module installation, please visit the detailed CPAN module installation guide.