とりあえずXSに触れてみる。モダンPerl入門を読みながら

xsに触れたことがないのでモダンPerl入門の8.2を読みながらとりあえず作ってみた。


本に書いてあることをほぼ丸コピーして、

package MyApp::Double

sub calc_double {
    my $x = shift;
    $x * 2;
}

と同じ動きをするモジュールを作り、

perl Makefile.PL
make
make test

がうまくいったら「やったー」で今日は終わり。

module-setupを使ってひな形作り

本の中ではExtUtils::MakeMakerというのを使ってひな形を作ってたのですけど、
色々試したかったのでmodule-setupを使ってみました。
module-setupについてはModule::Setup でらくらくモジュール作成 - JPerl Advent Calendar 2009


上のページにある通り、

module-setup --init --flavor-class=XS xs

でxsのひな形を作り、

module-setup MyApp::Double

をやってみたんですが、

Double.xs:7:20: error: ppport.h: No such file or directory

というエラーが出てしまって僕の理解を超えたので断念。
(ppport.hをローカルに作らないといけないのかな?)



なので、XSモジュール用のModule::Setupのflavorを作った - Clouder::BloggerからXSFlavor.pmを持ってきてxsのflavorにしました。

cat >> XSFlavor.pm
# (ページからコピペしたもの)

module-setup --init --flavor-class=+XSFlavor xs
module-setup MyApp::Double xs

で、MyApp-Doubleというディレクトリが作成される。

Double.xsの修正

ひな形が作成された時点で

% grep XSLoader::load lib/MyApp/Double.pm 
XSLoader::load(__PACKAGE__, $VERSION);

という感じで.soファイルを読み込むためのコードが書かれているようなので、
Double.xsにcalc_doubleを追記するだけの作業。


行末に

int
calc_double(sv)
        SV *sv;
    PREINIT:
        int x;
    CODE:
        x = SvIV(sv);
        RETVAL = x * 2;
    OUTPUT:
        RETVAL

を加えるだけ。

テストコードの作成

あとはテストコード。
t/00_compile.tでuse_okのテストをしてあるので、

% cat >> t/01_basic.t
use strict;
use Test::More;

use MyApp::Double;

my @list = (-100, 41, 3, 0, 50, 81, 100);
for my $x (@list) {
    is(MyApp::Double::calc_double($x), $x * 2, "$x + 2 = " . ($x * 2));
}

done_testing;

で完了。

結果

% perl Makefile.PL
include /Users/monmon/Documents/perl/xs/MyApp-Double/inc/Module/Install.pm
include inc/Module/Install/Metadata.pm
include inc/Module/Install/Base.pm
Cannot determine perl version info from lib/MyApp/Double.pm
include inc/Module/Install/AuthorTests.pm
include inc/Module/Install/Compiler.pm
include inc/Module/Install/Makefile.pm
include inc/Module/Install/Can.pm
include inc/Module/Install/TestBase.pm
include inc/Module/Install/Include.pm
include inc/Test/Base.pm
include inc/Test/Base/Filter.pm
include inc/Spiffy.pm
include inc/Test/More.pm
include inc/Test/Builder.pm
include inc/Test/Builder/Module.pm
include inc/Module/Install/WriteAll.pm
include inc/Module/Install/Win32.pm
include inc/Module/Install/Fetch.pm
Writing Makefile for MyApp::Double
Writing META.yml
% make
cp lib/MyApp/Double.pm blib/lib/MyApp/Double.pm
/usr/local/bin/perl "-Iinc" /usr/local/lib/perl5/5.10.1/ExtUtils/xsubpp  -typemap /usr/local/lib/perl5/5.10.1/ExtUtils/typemap -typemap typemap -typemap typemap  Double.xs > Double.xsc && mv Double.xsc Double.c
Please specify prototyping behavior for Double.xs (see perlxs manual)
cc -c  -I. -fno-common -DPERL_DARWIN -no-cpp-precomp -DDEBUGGING -fno-strict-aliasing -pipe -fstack-protector -I/opt/local/include -O3 -g   -DVERSION=\"0.01\" -DXS_VERSION=\"0.01\"  "-I/usr/local/lib/perl5/5.10.1/darwin-2level/CORE"   Double.c
Running Mkbootstrap for MyApp::Double ()
chmod 644 Double.bs
rm -f blib/arch/auto/MyApp/Double/Double.bundle
env MACOSX_DEPLOYMENT_TARGET=10.3 cc  -bundle -undefined dynamic_lookup -L/usr/local/lib -L/opt/local/lib -fstack-protector Double.o  -o blib/arch/auto/MyApp/Double/Double.bundle 	\
	     	\
	  
chmod 755 blib/arch/auto/MyApp/Double/Double.bundle
cp Double.bs blib/arch/auto/MyApp/Double/Double.bs
chmod 644 blib/arch/auto/MyApp/Double/Double.bs
Manifying blib/man3/MyApp::Double.3
% make test                               [~/Documents/perl/xs/MyApp-Double]
PERL_DL_NONLAZY=1 /usr/local/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'inc', 'blib/lib', 'blib/arch')" t/*.t xt/*.t
t/00_compile.t ...... ok   
t/01_basic.t ........ ok   
xt/01_podspell.t .... skipped: Test::Spelling is not installed.
xt/02_perlcritic.t .. skipped: Test::Perl::Critic is not installed.
xt/03_pod.t ......... ok   
All tests successful.
Files=5, Tests=9,  0 wallclock secs ( 0.05 usr  0.03 sys +  0.19 cusr  0.05 csys =  0.32 CPU)
Result: PASS

問題なく上手くいったので終了。