Help us understand the problem. What is going on with this article?

ビルドツールの裏側にいるMakefileを覗いてみる

More than 1 year has passed since last update.

この記事は1日遅れですが、Perl Advent Calendar 2017の22日目の記事です。

21日目は@booksさん今年Perlで困ったことでした。地道に困ったことと、解決策を記録していくって、意外とできないんですが、実は凄く大事なことですよね!


先日、VOYAGE GROUPの方とお話した際に、環境セットアップは色々なツールをmakeコマンドでラップしているので、全てmake installで完結するようになっている、ということを伺って、改めてmakeコマンドについて調べてみました。

ちょっと古いですが、1012年のVOYAGE GROUPのテックブログにmakeを使っている、という話題が載っています(あとはajitofmでも聴いたような…)。

超便利!Makefileを作ってmakeするのは想像よりもずっと簡単だった

そんなmakeですが、PerlのビルドツールであるExtUtils::MakeMakerも内部ではmakeコマンドを使っています。

今回は、どんなふうにmakeが使われているか、その裏側を覗いてみます。

Perlのモジュールインストールをおさらい

まず、そもそもビルドツールがどこで使われているか、そこからおさらいします。

Perlのモジュールのインストールといえば、最近ではcpanコマンド(cpanmや、cpmも有りますね)で実行するものになっていて、コマンド1つやるもの、という状況ですが、今でも歴史あるモジュールのREADMEのインストール方法にはtar.gzのアーカイブファイルをダウンロードしてきて、以下のコマンドを実行するように書かれています。

$ perl Makefile.PL
$ make
$ make test
$ make install

これはPerlのビルドツールであるExtUtils::MakeMakerがモジュールのビルド/インストールに必要なMakefileを生成し、あとはmakeコマンドにまかせて進める、という仕組みによるものです(だからperlは一回しか出てこない)。

一方で、pure perlで書かれたビルドツールであるModule::Buildだと、モジュールのインストール方法として以下のように書かれているはずです。

$ perl Build.PL
$ ./build
$ ./build test
$ ./build install

Makefileを生成せず、perlで書かれたbuildを生成し、そのコマンドからビルド/インストールを実行しています。

この辺の事情は、以下の記事が参考になるでしょう。

第23回 Module::Build:MakeMakerの後継者を目指して:モダンPerlの世界へようこそ

ただし、Module::Buildは、コアモジュールのスリム化の一環で現在(Perl 5.22以降)では、コアモジュールから外れています(当然、メンテナンスは継続しています)。

生成されたMakefileの中身を見る

元々ExtUtils::MakeMakerはC言語で書かれた拡張(XSモジュール)をビルドして、インストールするために作られた、という経緯がありますので、まずはXSモジュールで見てみましょう。

JSON::XS

pure perl版とXS版が別ディストリビューションになっている方が分かりやすいので、ここではJSON::XSを例に見てみます。

アーカイブファイルをダウンロードし、展開したら、展開したディレクトリへ移ります。

$ curl -O https://cpan.metacpan.org/authors/id/M/ML/MLEHMANN/JSON-XS-3.04.tar.gz
$ tar xvf JSON-XS-3.04.tar.gz
$ cd JSON-XS-3.04

Makefile.PLの中身がMakefileの元となる情報ですが、意外と情報量は少なめです。

WriteMakefile(
    dist         => {
                     PREOP      => 'pod2text XS.pm | tee README >$(DISTVNAME)/README; chmod -R u=rwX,go=rX . ;',
                     COMPRESS   => 'gzip -9v',
                     SUFFIX     => '.gz',
                    },
    EXE_FILES    => [ "bin/json_xs" ],
    VERSION_FROM => "XS.pm",
    NAME         => "JSON::XS",
    PREREQ_PM    => {
       common::sense     => 0,
       Types::Serialiser => 0,
    },
    CONFIGURE_REQUIRES => { ExtUtils::MakeMaker => 6.52, Canary::Stability => 0 },
);

distはディストリビューションとしてパッケージングする時に使用する情報なので、インストールに使う情報としては実行コマンドをインストールするためのEXE_FILES程度です(REQUIRESは依存モジュールの事前チェック用で、インストール自体には影響しません)。

あとは、ディレクトリに配置されているファイルをイイ感じに解析して、必要なコマンドを生成してくれるようになっています。

では$ Perl MakefileMakefileを生成してみましょう(途中でCanary::Stabilityモジュールのメッセージが出ますが、yで先に進んで下さい)。

Makefileの中を見始めると、さまざまな変数定義が続いていて、どこから読み進めればいいのかわかりづらいですが、ターゲット部分はこのあたりから読み始めると良いでしょう。

引数無しのmakeコマンドで実行されるのはallというターゲットで、更に呼び出されているpure_allというターゲットの中でblibディレクトリへのモジュールのコピーなどが行われます。

all :: pure_all manifypods
...
pure_all :: config pm_to_blib subdirs linkext

実際のコンパイルなどは(XSの構造までは説明しませんが)、xs_o sectionxs_c sectionに書かれています。使われている変数が設定されている箇所と共に読み進めていくと、どうやってコンパイラを呼び出しているか?といった所が理解できると思います。

# --- MakeMaker xs_c section:

.xs.c:
        $(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $(XSUBPP_EXTRA_ARGS) $*.xs > $*.xsc
        $(MV) $*.xsc $*.c


# --- MakeMaker xs_o section:
.xs$(OBJ_EXT) :
        $(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $*.xs > $*.xsc
        $(MV) $*.xsc $*.c
        $(CCCMD) $(CCCDLFLAGS) "-I$(PERL_INC)" $(PASTHRU_DEFINE) $(DEFINE) $*.c

なお、JSON::XSでは使われませんが、c_o sectionには通常のC言語や、C++でのコンパイルの設定が定義されています。

JSON::PP

次にPure Perl版の実装であるJSON::PPを見てみましょう。

$ curl https://cpan.metacpan.org/authors/id/I/IS/ISHIGAKI/JSON-PP-2.97001.tar.gz
$ tar xvf JSON-PP-2.97001.tar.gz
$ cd JSON-PP-2.97001

ExtUtils::MakeMakerのバージョンごとの差異を吸収するためのコードが随所に入っている関係で長く見えてしまいますが、中心となる箇所は以下の通りでJSON::XSとあまり変わりません。

WriteMakefile(
    'NAME'          => 'JSON::PP',
    'VERSION_FROM'  => 'lib/JSON/PP.pm', # finds $VERSION
    'PREREQ_PM'     => {
              'Test::More'  => 0,
              %prereq,
    },
    'EXE_FILES' => [ 'bin/json_pp' ],

ここでもpure_allを見てみます。

# --- MakeMaker top_targets section:
all :: pure_all manifypods
...
pure_all :: config pm_to_blib subdirs linkext

先ほどのXS版と変わらないですね。ただし、その先で、先ほどのXS版に出てきたようなc_o sectionxs_o sectionxs_c sectionという箇所は全て空っぽになっています。pure perlではコンパイルが不要なので、単にモジュールをコピーするだけで終わっています。

# --- MakeMaker c_o section:


# --- MakeMaker xs_c section:


# --- MakeMaker xs_o section:

おわりに

最初の入り口だけでしたが、Perlのビルドツールの裏側を覗いてみました。ビルドツールは特にさまざまな小さなツールと(バッド)ノウハウの積み重ねでできているので、読み解くのが大変ですが、先人達の知恵が詰まっているので、読んでみてください。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away