Perl入門ゼミ

テキスト処理、Linuxサーバー管理、Web開発ならPerl
  1. Perl
  2. XS
  3. here

Makefile.PLの使い方 - makeからアーカイブ作成まで

Makefile.PLは、Makefileを自動で生成するスクリプトです。XSモジュールを作成すると、自動的にMakefile.PLが作成されています。

ここでは、Makefile.PLの基本的な使い方を解説します。さらにMakefile.PLの書き方を知りたい場合は、以下の記事を参考にしてください。

Makefile.PLの基礎

Makefile.PLは、Makefileを自動で生成するためのスクリプトです。Mekefileを作成するとmakeコマンドでモジュールをコンパイルすることができます。

Makefile.PLの実行

Makefileを生成するにはMakefile.PLを実行します。

perl Makefile.PL

ディレクトリにMakefileができてることを確認できます。

Makefile

makeの実行

Makefileが生成されると、makeコマンドを実行できます。makeは、C言語のファイルやXSをコンパイルし、モジュールのインストールの準備をするために使われます。

make

makeを実行すると「blib」というディレクトリが作成されており、この中にコンパイルされたダイナミックリンクライブラリや、Perlモジュールが格納されます。

blib/

make testの実行

makeが完了すると「make test」を実行することができます。「make test」は「t」ディレクトリの中にあるPerlの自動試験のスクリプトを自動で実行してくれるコマンドです。

make test

「make test」を実行すると「t」ディレクトリの中にあるテストスクリプトが実行されます。

t/basic.t

ちなみにmakeをしないで「make test」を実行しても、自動的にmakeが直前に実行されます。

Perlにおける自動試験の書き方については以下の記事を参考にしてください。

make installの実行

「make install」を使うと、Perlのモジュールをインストールすることができます。

make install

インストールというのは、一言でいうと、必要なファイルを、Perlのライブラリのパスのある位置にコピーすることです。

僕は最近では、インストールするときに、このコマンドを使っていません。cpanmを使って「モジュール.tar.gz」ファイルをインストールするのがより簡単です。

cpanm モジュール.tar.gz

「モジュール.tar.gz」ファイルを作成する方法については以下で解説します。

make manifestの実行

モジュールを配布するためにCPANにアップロードしたいという場合があります。その場合は、配布に含めるファイルをMANIFESTファイルに記述する必要があります。

「make manifest」コマンドを使うとMANIFESTファイルに、必要なファイルを自動で追加することができます。

make manifest

不要なファイルはMANIFEST.SKIPに記述することで、MANIFESTに加えないことができます。以下はMANIFEST.SKIPのサンプルです。正規表現でスキップしたいファイルを指定できます。

\bRCS\b
\bCVS\b
^MANIFEST\.
^Makefile$
^Build$
^Build.bat$
^_build/
\.(bak|tdy|old|tmp|BAK)$
~$
^blib/
^pm_to_blib
\.cvsignore
\.gz$
^\.git
^cover_db/
^MYMETA.yml$
^MYMETA.json$
^spvm$
^solo/objs/

もし仮に間違ったファイルをMANIFESTに加えてしまった場合は「rm MANIFEST」として再度「make manifest」を実行しましょう。

make disttestの実行

MANIFESTに配布するファイルを記述したら、そのファイルだけで、試験が通るかを確認する必要があります。「make disttest」を実行すると、MANIFESTに指定したファイルだけを使って、試験を実行してくれます。

make disttest

配布前には必ず実行しましょう。

make distの実行

配布するためのアーカイブファイルを作成するには「make dist」コマンドを実行します。

make dist

アーカイブファイルはtarで固められ、gzipで圧縮され、拡張子は「tar.gz」となります。

SPVM-0.0232.tar.gz

このようにして作成したアーカイブは、CPANにアップロードすることができます。

make realcleanの実行

blibやMakefileなどの生成されたファイルを削除するには「make realclean」コマンドを使用します。

make realclean

最初からやり直したいときは「make realclean」です。「perl Makefile.PL」を実行する以前の状態に戻ることができます。

まとめ

上記の8つのコマンドを覚えておけば、CPANリリースをするのに十分です。以下にまとめておきます。

  1. perl Makefile.PL
  2. make
  3. make test
  4. make install
  5. make manifest
  6. make disttest
  7. make dist
  8. make realclean

Makefile.PLのテクニック

以下ではExtUtils::MakeMakerを使ったMakefile.PLの記述方法を解説します。

make clean,make realcleanをするときに削除するファイルを追加する

Makefile.PLは自動的にMakefileを作成しますが、このときに「make clean」と「make realclean」で実効されるコマンドが定義されます。

ときには、特定のファイルを「make clean」「make realclean」するときに、削除したいと思うときがあると思います。

そのような場合は、Makefile.PLの中のWriteMakefile関数で、以下のオプションを記述することで対応することができます。

clean => {FILES => "*.xyz foo"}
realclean => {FILES => '$(INST_ARCHAUTODIR)/*.xyz'}

FILESにおいては、makeの文法規則でファイル名を記述する必要があるので、注意してください。

XSファイルの名前を変更する方法

h2xsを使って、XSモジュールを作成すると、名前空間に「::」が入っていると、XSファイル名は、「末尾の名前.xs」というのになる。

たとえば「Some::Module」という名前でh2xsを使ってXSモジュールを作成すると、XSファイル名は「Module.xs」となる。これは少し気持ちが悪い。

自由なXSファイル名をつけるには、Makefile.PLの中で、XSファイル名を指定する必要がある。

たとえば「SomeModule.xs」という名前でXSファイルを作成したければ次のように記述する。コメントのついている「XSオプション」と「OBJECT」オプションの部分だけ見てほしい。他の部分は自動生成されたところです。

use ExtUtils::MakeMaker;

WriteMakefile(
    NAME              => 'Rstats::Element',
    VERSION_FROM      => 'lib/Rstats/Element.pm',
    PREREQ_PM         => {},
    ($] >= 5.005 ?
      (ABSTRACT_FROM  => 'lib/Rstats/Element.pm',
       AUTHOR         => 'A. U. Thor <kimoto@sakura.ne.jp>') : ()),
    LIBS              => [''],
    # XSオプション
    XS     => {'SomeModule.xs' => 'SomeModule.c'},
    DEFINE            => '',
    INC               => '-I.',
    # OBJECTオプション
    OBJECT => 'SomeModule$(OBJ_EXT)'
);

XSオプションで「SomeModule.xs」を「SomeModule.c」という名前にすると指定します。これはXSファイルをC言語ソースファイルに変換するxsubppへの命令になります。

ただしこれだけではだめで、コンパイル対象のC言語ファイル名(これはオブジェクト名によって記述)も記述する必要があります。それを、OBJECTオプションで指定します。「$(OBJ_EXT)」の部分は、makeの特別な変数のようなもので「.o」に置換されます。

このように記述すると「SomeModule.c」がコンパイル対象になります。ややこしいですね。

gccの最適化オプションを指定する

XSでgccの最適化オプションを指定するにはMakefile.PLの中のWriteMakefileのオプションとして、OPTIMIZEオプションを指定します。

OPTIMIZE => '-O3'

-O3を指定すると、一番強い最適化が行われるようです。

以下はOPTIMIZEオプションを使ったサンプルです。

use strict;
use warnings;
use ExtUtils::MakeMaker;

WriteMakefile(
    NAME                => 'Rstats',
    AUTHOR              => 'Yuki Kimoto <kimoto.yuki@gmail.com>',
    VERSION_FROM        => 'lib/Rstats.pm',
    ABSTRACT_FROM       => 'lib/Rstats.pm',
    ($ExtUtils::MakeMaker::VERSION >= 6.3002
      ? ('LICENSE'=> 'perl')
      : ()),
    PL_FILES            => {},
    PREREQ_PM => {
        'Test::More' => 0,
        'Object::Simple' => '3.10',
        'Math::Round' => '0.06',
        'Text::UnicodeTable::Simple' => '0.09'
    },
    dist                => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
    clean               => { FILES => 'Rstats-*' },
    CC =>'g++',
    OPTIMIZE => '-O3',
    LD => 'g++',
    LIBS              => [''],
    DEFINE            => '',
    INC               => '-I.',
    OBJECT            => '$(O_FILES)',
);

Makefile.PLでC/C++言語のヘッダファイルの検索パスを追加する方法

Makefile.PLを書いていると、C言語/C++のヘッダファイルの場所を変更したり、追加したくなる場合があります。ExtUtils::MakeMakerの場合ですと、WriteMakefileのINCHというオプションでそれを設定できます。

以下の設定は、「Rstats_lib/include」にC/C++のヘッダファイルをおいていることを想定しています。

    INC               => '-I. -I./Rstats_lib/include',
    H => ['ppport.h', glob('Rstats_lib/include/*.h')],

ヘッダファイルのインクルードパスの追加

ヘッダファイルのインクルードパスを追加するのに「INC」というオプションを使用します。デフォルトではカレントディレクトリの設定になっていますので、これに「./Rstats_lib/include」を追加しています。

    INC               => '-I. -I./Rstats_lib/include'

カレントディレクトリの設定は消さないでください。なぜなら「ppport.h」というヘッダファイルを読み込むために必要だからです。

これはコンパイラ(gccなど)に渡されるオプションになっています。ヘッダファイルの検索パスを複数指定した場合でも、配列のリファレンスで指定するのではなくって、「-I」というオプションを複数回記述することに注意してください。

ヘッダファイル名の一覧を指定

「H」というオプションには、ヘッダファイルの一覧を渡します。すべてのヘッダファイルを配列のリファレンスで指定してください。「ppport.h」とglob関数を使って「Rstats_lib/include」以下のすべてのヘッダファイルを指定しています。

    H => ['ppport.h', glob('Rstats_lib/include/*.h')],

この指定がないと、makeを実効したときの依存関係の解決が正しく行われませんので、必ず指定するようにしてください。「INC」オプションだけではも、初回のコンパイルは成功しますが、makeが依存関係を正しく理解できません。

Makefile.PLでC/C++言語のソースファイルの検索パスを追加する方法

XSを書くときに、C/C++言語のソースファイルを別のディレクトリに格納するには、どうすればよいのでしょうか。Makefile.PLをうまく記述するとそれを行うことができます。Makefile.PLのなかのWriteMakefileのオプションを次のように記述します。

    C => ['Rstats.c', glob('Rstats_lib/src/*.cpp')],
    OBJECT            => '$(O_FILES)',
    DEFINE => '-o $@'

これは、モジュール名がRstatsで、ソースファイルディレクトリが「Rstats_lib/src」の場合です。Rstats.xsというファイルが、トップ「/」に置かれていることを想定しています。ソースファイルの種類はC++のソースファイルです。

イメージがしやすいように、ディレクトリの構造と、Makefile.PLの全体のサンプルを見せます。

.
|-- Changes
|-- MANIFEST
|-- MANIFEST.SKIP
|-- Makefile.PL
|-- README.md
|-- Rstats.xs
|-- Rstats_lib
|   |-- include
|   |   `-- Rstats.h
|   `-- src
|       |-- Rstats_ElementFunc.cpp
|       |-- Rstats_Func.cpp
|       |-- Rstats_Main.cpp
|       |-- Rstats_Util.cpp
|       |-- Rstats_Vector.cpp
|       `-- Rstats_VectorFunc.cpp
|-- lib
|   |-- Rstats
|   |   |-- Class.pm
|   |   |-- Func.pm
|   |   |-- Object.pm
|   |   `-- Util.pm
|   `-- Rstats.pm
|-- ppport.h
`-- t

Makefile.PLのソースコードです。

use 5.010001;

use strict;
use warnings;
use ExtUtils::MakeMaker;

use Config;

# C++ compiler
my $cpp_compilers = {
  gcc => 'g++',
  clang => 'clang++',
  CC => 'CC'
};
my $cc = $cpp_compilers->{$Config{ccname}};
my $ld = $cc;

WriteMakefile(
    NAME                => 'Rstats',
    AUTHOR              => 'Yuki Kimoto <kimoto.yuki@gmail.com>',
    VERSION_FROM        => 'lib/Rstats.pm',
    ABSTRACT_FROM       => 'lib/Rstats.pm',
    ($ExtUtils::MakeMaker::VERSION >= 6.3002
      ? ('LICENSE'=> 'perl')
      : ()),
    PL_FILES            => {},
    META_MERGE   => {
      requires  => {perl => '5.010001'},
      resources => {
        license    => 'http://www.opensource.org/licenses/artistic-license-2.0',
        bugtracker => 'https://github.com/yuki-kimoto/Rstats/issues',
        repository => 'https://github.com/yuki-kimoto/Rstats.git'
      }
    },
    PREREQ_PM => {
        'Object::Simple' => '3.10',
        'Math::Round' => '0.06',
        'Text::UnicodeTable::Simple' => '0.09'
    },
    dist                => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
    clean               => { FILES => 'Rstats-*' },
    CC => $cc,
    OPTIMIZE => '-O3',
    LD => $ld,
    LIBS              => [],
    DEFINE            => '',
    INC               => '-I. -I./Rstats_lib/include',
    C => ['Rstats.c', glob('Rstats_lib/src/*.cpp')],
    OBJECT            => '$(O_FILES)',
    DEFINE => '-o $@'
);

C言語ソースファイルの記述

このままでは理解しづらいと思うので、WriteMakefileのオプションの解説をします。

    C => ['Rstats.c', glob('Rstats_lib/src/*.cpp')],
    OBJECT            => '$(O_FILES)',
    DEFINE => '-o $@'

まず「C」というオプションで、コンパイルに必要なソースコードのすべてを記述します。「Rstats.c」というのは「Rstats.xs」に対応するものです。glog関数を使って「Rstats_lib/src」以下にあるcppファイルを取得しています。もしC言語だった場合はこの部分のオプションは次のようになります。

    C => ['Rstats.c', glob('Rstats_lib/src/*.c')],

内部的なことをいうと、「C」オプションで指定されたファイルはMakefileの「C_FILES」という変数に格納されます。また「C_FILES」の拡張子を「.o」に変更した「O_FILES」という変数が自動的に作成されます。

オブジェクトファイルの出力場所の変更

「DEFINE」というオプションでは、何を記述しているのでしょうか。

  DEFINE => '-o $@'

これは、オブジェクトファイルの出力場所を変更しています。「DEFINE」というオプションを使用すれば、gccの引数を追加することができます。そして、gccの「-o」オプションを使用すれば、オブジェクトファイルの出力先を変更することができます。

「$@」はなんでしょうか。これはMakefileの文法規則で、ターゲット名が自動的に代入される変数です。ターゲット名が、オブジェクトファイル名なので、このように記述すると、オブジェクトファイルが、ソースコードファイルと同じディレクトリ内に出力されることになります。

リンカのターゲット指定

最後にリンカのターゲット指定を記述する必要があります。すべてのオブジェクトファイルを一つに合体させて、「.so」という拡張子で終わるダイナミックリンクライブラリが最終的に作成されるので、リンカにオブジェクトファイル名を知らせる必要があります。

そのためには、以下のように指定します。

    OBJECT            => '$(O_FILES)',

オブジェクトファイル名の一覧は「O_FILES」という変数に入っていたのでした。「O_FILES」を展開するには「$(O_FILES)」と記述します。

そして「OBJECT」オプションは、自動的に「LDFROM」というオプションに設定されて、リンカに渡されることになります。

XSモジュールの書き方

Makefile.PLは、XSモジュールを記述するときに書き換えることが多いですね。XSモジュールの作成方法については「XSによるC/C++バインディング入門 」をご覧ください。

  • Perlとはテキスト処理の記述性とパフォーマンスに優れ、正規表現が言語に組み込まれているプログラミング言語です。
  • Linuxサーバーでのフィルタリングプログラム、複数行の文字列を処理、ファイル内容の検索・置換などが得意
  • Perlはgitopensslなど広く普及したUnix/Linuxミドルウェアの補助ツールとして採用実績あり。後方互換性とポータビリティの高さがひとつの理由と推測。
  • 大量のテキストを扱うWeb開発も得意。ロングテールSEOを意識したWebサイト、アドテクやソーシャルゲームでの50ms以内のJSONの生成など。