CentOS 標準搭載の Perl に負けないで強く生きる

この記事では、OS 標準の Perl で動く CLI ツールを作ってる際に CentOS でハマる点について説明しています。

# TL;DR
- Linux / Mac OS X の多くの環境では標準で Perl 5 がインストールされいる
- しかし CentOS 標準の Perl 5 の **コアモジュールは欠けている**
- OS 標準の Perl に依存した CLI ツールを作る時は注意が必要

# なぜ標準インストールの Perl を使う必要があるのか
現在使われている Linux の多くや Mac OS X には、標準で Perl 5 がインストールされています。

しかし、Perl でウェブサービスを開発する際には、それら標準でインストールされている Perl ではなく、独自でインストールやビルドした Perl を使うことになるでしょう。

しかし、日常的に Perl を使っている人向けではない CLI ツールであったとしたら話は別です。
そのツールを使うために、別途 Perl のインストールをさせるのは避けたい。

たとえば、私が開発しているコマンドラインツールである [crenv](https://github.com/pine613/crenv) のバックエンドである [crystal-build](https://github.com/pine613/crystal-build) は、上記の理由により OS 標準の Perl を使って動いています。

# `corelist` コマンド
[corelist](http://perldoc.perl.org/corelist.html) コマンドを使うと、モジュールが Perl のコアモジュールであるかどうかを調べることができます。

以下のように実行すると、`JSON::PP` は Perl v5.13.9 以降はコアモジュールであり、`JSON::XS` はコアモジュールに含まれないことが分かります。

```
$ corelist JSON::PP

JSON::PP was first released with perl v5.13.9

$ corelist JSON::XS

JSON::XS was not in CORE (or so I think)
```

つまり、`corelist` で調べてからモジュールを使えば、環境によるコアモジュール差異の問題は生じないはずです。と思っていた時代が私にもありました。

# CentOS の標準 Perl のコアモジュール
しかし CentOS の標準の Perl には、**コアモジュール** が欠けている問題があります。
具体的にどのようなモジュールが欠けているのか、簡単なスクリプトを記述して確かめてみます。

以下のスクリプトに対して `$ ./corelist.pl 5.016003` のように実行すると、ある特定のバージョンに含まれているべきコアモジュールを検査するスクリプトが書きだされます。

(CentOS の標準 Perl のコアモジュールに Module::CoreList が含まれていないため、このように遠回りな手法を取っています)

```pl:corelist.pl
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;

use Data::Dumper;
use Module::CoreList;

local $Data::Dumper::Terse = 1;

my $perl_version    = $ARGV[0];
my $module_versions = Module::CoreList::find_version($perl_version);
my @module_names    = sort keys %$module_versions;
my $data            = do { local $/; <DATA> };
my $source          = sprintf($data, Dumper(\@module_names));

print "$source\n";

__DATA__
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;

open(STDERR, '>/dev/null');

my $module_names = %s;

for my $module_name (@$module_names) {
    eval "require $module_name";

    if ($@) {
        print "$module_name ERROR\n";
    }
}
```

実行して出力したスクリプトを調べたい環境で実行すると、どのモジュールが存在しないかが分かります。

以下は、**Amazon Linux AMI 2015.09.1** の初期状態の Perl v5.16.3 で存在しないモジュールの一例です (括弧内は、コアモジュールとして存在するはずのバージョン)。

- `Archive::Tar` (>= v5.9.3)
- `JSON::PP` (>= v5.13.9)
- `Module::Load` (>= v5.9.4)
- `Test::More` (>= v5.6.2)
- `version` (>= v5.9.0)

たとえば、Perl で書かれた `nodebrew` は、コアモジュールが欠けた状態の CentOS で動作させるため [このように](https://github.com/hokaccha/nodebrew/blob/11c439b2becd7612dd4c0d82824e582e1c490841/nodebrew#L744-L752) `Archive::Tar` モジュールから `tar` コマンドへフォールバックするコードが記述されています。

## CentOS の標準 Perl で標準モジュールを有効にする方法
実は CentOS では一部の Perl コアモジュールは別のパッケージとして独立しています。
以下のようにしてインストールが可能です。

```
[ec2-user@ip-172-31-15-126 ~]$ sudo yum search perl-core
Loaded plugins: priorities, update-motd, upgrade-helper
==================== N/S matched: perl-core ====================
perl-core.x86_64 : Base perl metapackage

  Name and summary matches only, use "search all" for everything.

[ec2-user@ip-172-31-15-126 ~]$ sudo yum install perl-core -y
```

## なぜ CentOS の Perl ではコアモジュールが省かれているのか (考察)
これは、Ubuntu と違い CentOS が Perl ではなく Python に強く依存する OS であるからであると考えられます。

私は、Perl コアモジュールとして定義されているものは、どの環境にインストールされた Perl でも存在するべきであると思います。なぜならば、コアモジュールがインストールされている前提でスクリプトを記述するからです。

しかし、このような状況であるため CentOS のコアモジュールが欠けた状態を考慮してコードを書く必要がありますので、ご注意を…。

# まとめ
- Linux / Mac OS X の多くの環境では標準で Perl 5 がインストールされいる
- しかし CentOS 標準の Perl 5 の **コアモジュールは欠けている**
- OS 標準の Perl に依存した CLI ツールを作る時は注意が必要