モジュールのバージョン番号について考えてみました(2)

モジュールのバージョン番号について考えてみました(1) - ヒルズで働く@robarioの技ログの続きです。
ちなみにperl -v は This is perl, v5.8.7 built for cygwin-thread-multi-64int です。

versionオブジェクトの生成

version.pm(0.6701 XS版)では、versionオブジェクトを生成するために2つのメソッドnewとqvがあります。qvに与えられた引数は必ずExtended Versionsであるとみなされます。newに与えられた引数はExtended Versionsに見える場合はそのように、Numeric Versionsに見える場合はそのように扱われます。(参考:version - metacpan.org
なお、Numeric Versionsは 1.28 や 1.002003 のように通常の数値に見えるような表記であり、またExtended Versionsは1.2.3やv1.2.3のようなバージョン表現に特化した表記です。

以下は何が言いたいのか良く分からなくなってしまったテストスクリプトです。

$ cat version-test.pl
use version;
my @args = qw(2.86 2.86_01 2.102 2.2248 2.224.8);
print "\n* new without v-strings\n";
printf "version->new( %-7s) = %s\n", $_, version->new($_)->normal foreach (@args);
print "\n* new with v-strings\n";
printf "version->new(v%-7s) = %s\n", $_, version->new("v$_")->normal foreach (@args);
print "\n* qv without v-strings\n";
printf "          qv( %-7s) = %s\n", $_, qv($_)->normal foreach (@args);
print "\n* qv with v-strings\n";
printf "          qv(v%-7s) = %s\n",  $_, qv("v$_")->normal foreach (@args);

$ perl version-test.pl

* new without v-strings
version->new( 2.86   ) = v2.860.0
version->new( 2.86_01) = v2.860_100
version->new( 2.102  ) = v2.102.0
version->new( 2.2248 ) = v2.224.800
version->new( 2.224.8) = v2.224.8

* new with v-strings
version->new(v2.86   ) = v2.86.0
version->new(v2.86_01) = v2.86_1
version->new(v2.102  ) = v2.102.0
version->new(v2.2248 ) = v2.2248.0
version->new(v2.224.8) = v2.224.8

* qv without v-strings
          qv( 2.86   ) = v2.86.0
          qv( 2.86_01) = v2.86_1
          qv( 2.102  ) = v2.102.0
          qv( 2.2248 ) = v2.2248.0
          qv( 2.224.8) = v2.224.8

* qv with v-strings
          qv(v2.86   ) = v2.86.0
          qv(v2.86_01) = v2.86_1
          qv(v2.102  ) = v2.102.0
          qv(v2.2248 ) = v2.2248.0
          qv(v2.224.8) = v2.224.8

new with not v-stringsの結果のみが、他の場合と異なっていることに注目してください。前回のエントリで「v-stringsを使えば気にしなくて良い」と言いましたが、更に「qvを使っていればv-stringsかどうかも気にしなくて良い」ということが言いかったようです。
混乱を防ぐためにも自分は特に理由が無い限りqvで統一したいと思います。


Numeric Versionsは曲者で解釈違いを起こし易い表現です。2.102(2.102)より2.86(2.860)の方が大きいという、直感とは逆の結果が得られたりします。また、Numeric Alpha Versionsの区切りは_(アンダースコア)で表すのですが、perlでは数値に含まれる_(アンダースコア)は無視されてしまうため、必ずquoteする必要があります。quoteしないと 2.86_01 は 2.8601 と解釈されます。
よって自分はNumeric Versionsはなるべく使わないでいきたいと思います。


オレバージョニング(俺的なSubversion管理下でのバージョン番号自動更新方法)

バージョン管理ツールSubversionの機能を使ってPerlモジュールのバージョンを自動更新する方法について自分なりに整理してみます。CVSを使うのは小学生までなので、今回はとりあえず無視します。

ではまず、満たすべき条件を列挙します。

  1. ExtUtils::MakeMakerのために、$VERSIONの定義は1行で完結させる必要があります。(use version;を別行に書くのもダメです。)
  2. perldataによると、Version Strings(v-strings)は推奨しないそうなので、v-stringsは使いません。Numeric Versionsか、v-stringsを除くExtended Versionsを使用します。

次に、満たすと良いと思われる条件を列挙します。

  1. version.pmを使います。
  2. version.pmを使う場合、newではなくqvを使います。
  3. Numeric Versionsは使いません。(が、qvを使うので特に気にしません。)

最後に俺好み条件を列挙します。

  1. Perl::Criticの-severity 1に従います。(つまりPerl::Tidyにも従います。)よって、$VERSIONの定義を短くまとめる必要があります。
  2. v137.0.0のようにあまりにも大きい値になるのは嫌なので、小数点以下2〜3桁ぐらいは0以外が良いです。
  3. v0.0.137のようにあまりにも小さい値になるのは嫌なので、基本的にv0.137.0のように最後を0とします。(最後のはAlpha Versionsではありません。Alpha Versionsは更に後ろに_(アンダースコア)を付けて書きます。)
  4. version.pmや最近のperlの流行りを見ると、基本的にバージョン番号は3桁区切りにした方が良さそうな気がします(気のせい?)。なので、左端(Major Versions?)以外は3桁の数値が良いです。
  5. メジャーバージョン・アルファバージョンは手動で付けたい。

以上を可能な限り満たす書き方は、2通りあります。まず1つは

use version; our $VERSION = qv( (qw$Revision: 102 $)[1] / 1000 );
use version; our $VERSION = qv( sprintf q{%.3f}, (qw$Revision: 100 $)[1] / 1000);

で、これは俺好みの5番目(メジャーバージョンを手動で付けたい)を満たせていません。知らないうちにメジャーバージョンが勝手に上がってしまいます。
リビジョン102,2228はそれぞれ以下のようになります。

$ perl -Mversion -e 'print qv( (qw$Revision: 102 $)[1] / 1000 )->normal;'
v0.102.0
$ perl -Mversion -e 'print qv( (qw$Revision: 2228 $)[1] / 1000 )->normal;'
v2.228.0
% perl -Mversion -e 'print qv( sprintf q{%.3f}, (qw$Revision: 102 $)[1] / 1000);'
v0.102.0
% perl -Mversion -e 'print qv( sprintf q{%.3f}, (qw$Revision: 2228 $)[1] / 1000);'
v2.228.0

3つ目(Beta Versions?)やAlpha Versionsを付けるには末尾に文字列を手動でくっつけます。

$ perl -Mversion -e 'print qv( (qw$Revision: 2228 $)[1] / 1000 . ".3_1")->normal;'
v2.228.3_1
% perl -Mversion -e 'print qv( sprintf q{%.3f.3_1}, (qw$Revision: 2228 $)[1] / 1000);'
v2.228.3_1

もう1つの方法は

use version; our $VERSION = qv( '0.' . (qw$Revision: 102 $)[1] . '.0');
# or
# use version; our $VERSION = qv( sprintf '0.%s.0', (qw$Revision: 102 $)[1] );

で、これは俺好みの4番目(3桁の数値にしたい)を満たせません。


自分はとりあえず1000で割る前者の方法で行くことにしました。「こうするべき」と言っているつもりはありませんのでご注意ください。

おまけ

以上、2回のエントリーに分けて書きましたが、あまり納得いっていません。このエントリーはコロコロ書き換わっていく気がします。

ろくにドキュメント読まずに書いているので、大きな間違いを犯している可能性が高いと思います。トラックバックなりコメントなりで指摘してくださると非常に助かります。

Subversionの場合、リビジョン番号がリポジトリ内で共有されてしまうため、必ずしも1から始まるとは限りませんし、バージョン番号が飛び飛びになります。あまり真剣に考えてもどうしようもない、という話もあります。


バージョン絡みなんですが、誰かこれ→$VERSION = eval $VERSION;? - ヒルズで働く@robarioの技ログ教えてください(P口`q。)". Perl::Critic::DEVELOPER - How to make new Perl::Critic::Policy modules - metacpan.orgにも載っているのですが、理由がイマイチ理解できません。。。