今日で株式会社ソフリットが設立されて1年です。また「会社を作って1年たったまとめ」を書こうかとも思ったんですが、あんまり面白くならなかったので、今年覚えたことのうちでいちばん重要そうなことをまとめようと思います。それは「Makefile.PLを書こう」ということ。

ではさっそく、これからHoge.pmというperlモジュールを書くとしましょう。

[danjou@guido] $ mkdir Hoge
[danjou@guido] $ cd Hoge

ここでおもむろにMakefile.PLを書き始めます。

[danjou@guido] $ vim Makefile.PL
use inc::Module::Install;

WriteAll;

とりあえずこれだけ書いて保存します。そしてperl Makefile.PLとして実行します。

[danjou@guido] $ perl Makefile.PL
include /home/danjou/tmp/20080919/Hoge/inc/Module/Install.pm
include inc/Module/Install/WriteAll.pm
include inc/Module/Install/Base.pm
include inc/Module/Install/Metadata.pm
include inc/Module/Install/Makefile.pm
Writing META.yml
No license specified, setting license = 'unknown'
include inc/Module/Install/Win32.pm
include inc/Module/Install/Can.pm
include inc/Module/Install/Fetch.pm
Warning: Guessing NAME [Hoge] from current directory name.
Writing Makefile for Hoge
[danjou@guido] $ ls
META.yml  Makefile  Makefile.PL  inc

なんとびっくり。これだけでMakefileができます。ということは、makeできますね。

[danjou@guido] $ make
[danjou@guido] $ make test
No tests defined for Hoge extension.

make して make testしてみると、テストがないよー、と言われます。テストしようとはしてくれるんですね。じゃぁ、テストを作ってみましょう。テストはtというディレクトリを作って、その中に.tという拡張子で書いていきます。

[danjou@guido] $ mkdir t
[danjou@guido] $ vim t/00_compile.t
use strict;
use warnings;
use Test::More tests => 1;

BEGIN {
    use_ok 'Hoge';
}   

これでTest::Moreを使ってHogeモジュールをuseできるかどうかのテストできます。Test::Moreが必要になったので、Makefile.PLを修正します。ついでにさっきperl Makefile.PLしたときに出てた名前とライセンスに関するワーニングに対応します。ここではperlライセンスにしました。

use inc::Module::Install;

name 'Hoge';
license 'perl';
build_requires 'Test::More';

WriteAll;

こうしてもう一度perl Makefile.PLします。

[danjou@guido] $ perl Makefile.PL
include /home/danjou/tmp/20080919/Hoge/inc/Module/Install.pm
include inc/Module/Install/Metadata.pm
include inc/Module/Install/Base.pm
include inc/Module/Install/WriteAll.pm
Writing META.yml
include inc/Module/Install/Makefile.pm
include inc/Module/Install/Win32.pm
include inc/Module/Install/Can.pm
include inc/Module/Install/Fetch.pm
Writing Makefile for Hoge

今度は何も言われずにできたみたいですね。ではもう一度make; make testしてみましょう。

[danjou@guido] $ make
[danjou@guido] $ make test
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'inc', 'blib/lib', 'blib/arch')" t/*.t
t/00_compile....1/1 
#   Failed test 'use Hoge;'

#   at t/00_compile.t line 6.

#     Tried to use 'Hoge'.

#     Error:  Can't locate Hoge.pm in @INC (@INC contains: /home/danjou/tmp/20080919/Hoge/inc /home/danjou/tmp/20080919/Hoge/blib/lib /home/danjou/tmp/20080919/Hoge/blib/arch /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi /usr/lib/perl5/site_perl/5.8.7/i386-linux-thread-multi /usr/lib/perl5/site_perl/5.8.6/i386-linux-thread-multi /usr/lib/perl5/site_perl/5.8.5/i386-linux-thread-multi /usr/lib/perl5/site_perl/5.8.8 /usr/lib/perl5/site_perl/5.8.7 /usr/lib/perl5/site_perl/5.8.6 /usr/lib/perl5/site_perl/5.8.5 /usr/lib/perl5/site_perl /usr/lib/perl5/vendor_perl/5.8.8/i386-linux-thread-multi /usr/lib/perl5/vendor_perl/5.8.7/i386-linux-thread-multi /usr/lib/perl5/vendor_perl/5.8.6/i386-linux-thread-multi /usr/lib/perl5/vendor_perl/5.8.5/i386-linux-thread-multi /usr/lib/perl5/vendor_perl/5.8.8 /usr/lib/perl5/vendor_perl/5.8.7 /usr/lib/perl5/vendor_perl/5.8.6 /usr/lib/perl5/vendor_perl/5.8.5 /usr/lib/perl5/vendor_perl /usr/lib/perl5/5.8.8/i386-linux-thread-multi /usr/lib/perl5/5.8.8 .) at (eval 2) line 2.

# BEGIN failed--compilation aborted at (eval 2) line 2.

# Looks like you failed 1 test of 1.

t/00_compile.... Dubious, test returned 1 (wstat 256, 0x100)
 Failed 1/1 subtests 

Test Summary Report
                                                                        - t/00_compile (Wstat: 256 Tests: 1 Failed: 1)
  Failed test:  1
  Non-zero exit status: 1
Files=1, Tests=1,  0 wallclock secs ( 0.01 usr  0.01 sys +  0.02 cusr  0.00 csys =  0.04 CPU)
Result: FAIL
Failed 1/1 test programs. 1/1 subtests failed.
make: *** [test_dynamic] エラー 1

こんどはテストが実行されて、Hogeモジュールが見つかんないよ!と言われました。大成功ですね。では、Hogeモジュールを書きましょう。モジュールはlibディレクトリに作ります。

[danjou@guido] $ mkdir lib
[danjou@guido] $ vim lib/Hoge.pm
package Hoge;
use strict;
use warnings;

1;

かけたので、make; make testしてみると

[danjou@guido] $ make
cp lib/Hoge.pm blib/lib/Hoge.pm
[danjou@guido] $ make test
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'inc', 'blib/lib', 'blib/arch')" t/*.t
t/00_compile....ok   
All tests successful.
Files=1, Tests=1,  0 wallclock secs ( 0.01 usr  0.00 sys +  0.02 cusr  0.00 csys =  0.03 CPU)
Result: PASS

おお。いけましたね。 では、なんか機能を付けていきましょうか。例えば、こう使うとしましょう。

#!/usr/bin/perl

# example.pl

use strict;
use warnings;
use Hoge;

my $hoge = Hoge->new;
print $hoge->fuga;

というスクリプトexample.plを書いたとして、実行すると

[danjou@guido] $ perl example.pl
fuga
[danjou@guido] $ 

こうなる感じ。ということはインターフェイスとしては

  • new
  • hoge の二つがあることになりますね。ではテストを書いておきましょう。
[danjou@guido] $ vim t/01_usage.t
use strict;
use warnings;
use Test::More tests => 2;
use Hoge;

my $hoge = Hoge->new;
isa_ok $hoge, 'Hoge';
my $fuga = $hoge->fuga;
is $fuga, "fuga\n";

テストがかけました。Hogeをnewしたら$hogeという変数にHogeのインスタンスができます。ちゃんとできてますか?というのが1個目のテスト「isa_ok」です。二個目のテスト「is」は$hogeにfugaというメソッドを実行させて、かえってきた値が”fuga\n”となっていてほしいので、それを確認しています。テストの数が2つなので、「tests => 2」と書いています。 テストがかけたので、テストしてみましょう。

[danjou@guido] $ make test
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'inc', 'blib/lib', 'blib/arch')" t/*.t
t/00_compile....ok   
t/01_usage......Can't locate object method "new" via package "Hoge" at t/01_usage.t line 6.
# Looks like your test died before it could output anything.
t/01_usage...... Dubious, test returned 255 (wstat 65280, 0xff00)
 Failed 2/2 subtests 

Test Summary Report
                                                                        - t/01_usage  (Wstat: 65280 Tests: 0 Failed: 0)
  Non-zero exit status: 255
  Parse errors: Bad plan.  You planned 2 tests but ran 0.
Files=2, Tests=1,  0 wallclock secs ( 0.01 usr  0.01 sys +  0.04 cusr  0.01 csys =  0.07 CPU)
Result: FAIL
Failed 1/2 test programs. 0/1 subtests failed.
make: *** [test_dynamic] エラー 255

Hogeにnewがないよー、と言われました。書きます。

package Hoge;
use strict;
use warnings;

sub new {
    my $class = shift;
    return bless {}, $class;
}

1;

かけたらまたテストです。

[danjou@guido] $ make test
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'inc', 'blib/lib', 'blib/arch')" t/*.t
t/00_compile....ok   
t/01_usage......1/2 Can't locate object method "fuga" via package "Hoge" at t/01_usage.t line 8.
# Looks like you planned 2 tests but only ran 1.
# Looks like your test died just after 1.
t/01_usage...... Dubious, test returned 255 (wstat 65280, 0xff00)
 Failed 1/2 subtests 

Test Summary Report
                                                                        - t/01_usage  (Wstat: 65280 Tests: 1 Failed: 0)
  Non-zero exit status: 255
  Parse errors: Bad plan.  You planned 2 tests but ran 1.
Files=2, Tests=2,  0 wallclock secs ( 0.01 usr  0.00 sys +  0.05 cusr  0.01 csys =  0.07 CPU)
Result: FAIL
Failed 1/2 test programs. 0/2 subtests failed.
make: *** [test_dynamic] エラー 255

今度はfugaってメソッドがないよー、と言われます。ということは、一個目のテスト

isa_ok $hoge, 'Hoge';

は通ったようです。前進しましたね。ではfugaも実装しましょう。

package Hoge;
use strict;
use warnings;

sub new {
    my $class = shift;
    return bless {}, $class;
}

sub fuga {
    return "fuga\n";
}

1;

もう一度テストすると、

[danjou@guido] $ make test
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'inc', 'blib/lib', 'blib/arch')" t/*.t
t/00_compile....ok   
t/01_usage......ok   
All tests successful.
Files=2, Tests=3,  0 wallclock secs ( 0.01 usr  0.00 sys +  0.05 cusr  0.01 csys =  0.07 CPU)
Result: PASS

通りました!

と言ったような流れでperlって書くんだよ、そんでまずはMakefile.PLを書くんだよ、というのが今年一番のお勉強でした。ということでそろそろおれもpmsetup書いてもいい時期。 あ、一年お世話になりました!2年目もよろしくです!あと、添削もよろしくです!