File::Spec/File::Basenameを使ったライブラリパス指定

※このエントリは Perl Advent Calendar 2016 の15日目のエントリです。

どーも、わいとんです。

早速ですが、みなさんはPerlのライブラリパスをどんな風に指定していますか?

そもそもスクリプト内で指定してない?絶対パスで直接指定?それともFindBinを使ってる?

今回はわいとんがよくやっているFile::SpecFile::Basenameを使ったライブラリパス指定の方法を紹介します。

以下のようなプロジェクトがあったとします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
./
|
+---cpanfile
|
+---bin/
| |
| +---run.pl
|
+---lib/
|
+---MyProj.pm
|
+---MyProj/
|
+---Client.pm

cpanfileは以下のような内容です。

1
2
3
4
5
6
requires 'perl', '5.008001';
requires 'LWP::UserAgent';
requires 'LWP::Protocol::https';
on 'test' => sub {
requires 'Test::More', '0.98';
};

これをcpm installなんてやった場合、local/lib/perl5配下に依存ライブラリがどんどん入ってきます。(cpmについてはskajiさんのエントリを参照してください。)

で、bin/run.plからMyProj::Client->new(...);なんてやりたいなぁって思った時に、以下のように書くと思います。

1
2
3
4
5
use strict;
use warnings;
use MyProj::Client;
my $client = MyProj::Client->new(...);
...

でもって、実行時に

1
perl -Ilib -Ilocal/lib/perl5 ./bin/run.pl

なぁんて書くと思います。別に悪くないけど、面倒ですよね。

ちょっと賢くFindBinを使う

で、use libFindBinを組み合わせて、以下のようにしたりしますよね。(しますよね?したことありません?)

1
2
3
4
5
6
7
8
9
10
use strict;
use warnings;
use FindBin;
use lib (
"$FindBin::Bin/../lib",
"$FindBin::Bin/../local/lib/perl5",
);
use MyProj::Client;
my $client = MyProj::Client->new(...);
...

実行時にはこんな感じで。

1
perl ./bin/run.pl

楽になりましたね!でもでもでもでもチョッッット待ってwww それってカレントディレクトリがプロジェクトフォルダの直下にないと動かなくないっすか?(動かないですよね?)あとそれWindowsじゃ動かなくないですか?(Windowsなんか使わない?あ、すみませんww)

File::SpecとFile::BasenameとFILEを使う

まあそこで出てくるのがFile::SpecFile::Basenameなんですね。

超雑に解説すると、File::SpecってのはOS間で異なるパス表現(¥とか/とかそういうやつ)を吸収してくれるライブラリで、File::Basenameてのは、食わせた文字列をパス表現文字列とみなしてざっくりパーズし、フォルダまでのパスとかファイル名とかをよしなに引っ張ってきてくれるライブラリです。

あと__FILE__っていうのは実は組みこみ関数でして、__FILE__が記述されているファイルのパス(つまり実行されるスクリプトのパスそのもの)を返してくれます。

んで、こいつらを使ってさっきのbin/run.plを書き換えると、こんな感じになります。

1
2
3
4
5
6
7
8
9
10
11
use strict;
use warnings;
use File::Spec;
use File::Basename 'dirname';
use lib (
File::Spec->catdir(dirname(__FILE__), qw/.. lib/),
File::Spec->catdir(dirname(__FILE__), qw/.. local lib perl5/),
);
use MyProj::Client;
my $client = MyProj::Client->new(...);
...

若干冗長ですが、実行時は常にスクリプトを正しく指定するだけでOK。

1
perl ./bin/run.pl

例えばlib/MyProj/からでも

1
perl ../../bin/run.pl

と実行できます。

少し補足。 File::Spec->catdir(...)は、引数にリストを食わせることで、実行環境に応じたパス文字列を仕立て上げてくれます。dirname(...)は与えられたパス文字列のディレクトリパスを返してくれます。

これらを組み合わせて、どの環境でどのパスから実行されても、読み取りたいライブラリパスがずれずに利用可能となる、と。こういうわけです。

何より、File::SpecFile::Basenameもコアモジュールなので、Perl5が入っていればまず使えないってことはないんじゃないでしょうか。→ツッコミ参照ください

まとめ

  • ライブラリパスをがっちりつかんで離さないスクリプトを書くための方法を紹介しました。
  • File::SpecFile::Basenameを使うぞって言いながら__FILE__も使いました。
  • きっとWindowsの人もニッコリ。
  • cpmかわいいよcpm

ツッコミもらいました

だそうです。。。skajiさん、ありがとうございますm(_ _)m