2011年8月9日

Waf を使う

Waf を使ってみます。 ここでの Waf とは、Web Application Framework や Web Application Firewall の略ではなく、 Python で記述されたビルドシステムのことです。

ツールの導入部分に関してはこちらの記事で紹介されています。

この記事では、何かをビルドする、というわけではなく、 アーキテクチャに依存せずに手続きを実行できるようにします。

例として、Solr をダウンロードして解凍する処理を記述します。 Linux では wgettar を使うだけですが、 Windows だとコマンドラインでダウンロード処理を記述するのが難しく、 Mac OSX Lion には wget は付属していないため curl を使うことになるでしょう。 もちろん、Windows に Cygwin をインストールしたり、Mac に wget をインストールすれば同じコマンド群を使えますが、 その手間が面倒だと感じられることが Waf を使う動機です。

とはいっても Python をインストールしておく必要があります。 そこはなんとか乗り越えてもらいたいですね。

実行環境の準備

Mac OSX Lion での設定例を示します。 Python は元から入っていますので、virtualenv を使えるようにしておきます。

$ curl -O http://python-distribute.org/distribute_setup.py
$ sudo python distribute_setup.py
$ sudo easy_install pip
$ sudo pip install virtualenv

次に、適当なディレクトリを作成し、独立した Python の実行環境を作成します。

$ virtualenv --distribute solr-test
$ cd solr-test
$ source bin/activate
$ curl http://waf.googlecode.com/files/waf-1.6.6 >bin/waf
$ chmod +x bin/waf
$ waf

これでおしまいです。 waf は単体のファイルとして配布されており、最初の実行時に展開されます。 virtualenv を使っておくと、パスの調整も必要ありませんので、便利だと思います。

Windows の場合はちょっとしたバッチファイルを用意しておくと便利かもしれません。 virtualenv で環境を作成しておくと Scripts フォルダもパスに追加されます。 Windows では拡張子が .bat のファイルは拡張子なしで実行できますので、 Scripts/waf.bat として以下のファイルを作成しておきます。

@echo off
python Scripts\waf-1.6.6 %*

Solr をダウンロードして解凍

Solr をダウンロードして解凍する処理を記述してみます。

Waf では waf コマンドを実行すると wscript ファイルを探しにいきます。 これはデフォルトのビルドスクリプトで、 make における Makefile に相当します。 いくつかのルールに注意する必要はありますが、普通の Python スクリプトも記述できます。

つまり、ファイルのダウンロードには urllib.urlretrievetar.gz の解凍には tarfileextractall を使えます。

実際に書いてみると次のようになりました。 wscript として保存し、 waf コマンドを実行すると実行できます。 (Solr 3.1 を使うのは、dotcloud のバージョンに合わせるため - DotCloud で Solr を使ってみる)

APPNAME = 'solr-test'
VERSION = '1.0.0'

top = '.'
out = '_build'

from waflib.Task import Task
import urllib
import os
import tarfile

SOLR_VERSION = '3.1.0'
SOLR_PACKAGE = 'apache-solr-%s.tgz' % (SOLR_VERSION,)
SOLR_MIRROR = 'http://ftp.riken.jp/net/apache/lucene/solr/%s/%s' % (SOLR_VERSION, SOLR_PACKAGE)

class solr(Task):

    def run(self):
        p = self.inputs[0].abspath()
        if not os.path.exists(p):
            urllib.urlretrieve(SOLR_MIRROR, p)
        d, _ = os.path.splitext(p)
        if not os.path.exists(d):
            with tarfile.open(p) as tar:
                tar.extractall()

def configure(ctx):
    print('configure "%s".' % (APPNAME,))

def build(bld):
    solr_task = solr(env=bld.env)
    solr_task.set_inputs(bld.path.find_resource(SOLR_PACKAGE))
    bld.add_to_group(solr_task)

慣れないと不思議な記述も多いですが、次の4つの変数は予約語です。 (自分ルールとして、一番上に書いておく)

  • APPNAME : ソフトウェアの名前。
  • VERSION : ソフトウェアのバージョン。
  • top : 基準とするディレクトリ。普通はカレント。
  • out : コンパイルしたファイルなどの出力先ディレクトリ。

次の2つの関数は組み込みのものを上書きしています。

  • configure : コンパイル先ディレクトリの生成など。前処理。
  • build : デフォルトのビルドターゲット。

wscript の関数は「コンテキスト」を意識して記述しなければなりません。 上記のいずれも引数オブジェクトが「コンテキスト」ですが、 実際のクラス (型) が異なります。 The Waf Book の 2.1.2 の NOTE には次のように記載されています。

The context parameter is a new object for each command executed. The classes are also different: ConfigureContext for configure, BuildContext for build, OptionContext for option, and Context for any other command.

日本語混じりにすると次のような感じでしょうか。

コンテキストパラメータは、それぞれの実行コマンドごとの新しいオブジェクトです。 クラスはコマンドごとに異なります。 configure には ConfigureContext、build には BuildContext、option には OptionContext、 それ以外のコマンドに対しては Context になっています。

個人的にはここがちょっとした嵌まりドコロで、ドキュメントの最初の方には hello やら ping, pong などがありましたので、 名前はなんでも良いのかな、と思いましたが、そうでもないようです。

関数名は waf コマンドの引数に対応しますが、 自由に付けた関数名には BuildContext が渡ってこないために迷いました。 とは言え、タスクは自作できますので、慣れれば自由に記述できそうな気がします。

終わりに

Python で記述されたビルドシステム - Waf - を使ってみました。 Python さえ利用可能ならば特別なインストールはいりません。 ここでは、環境による影響範囲を限定するため、パスの設定を簡単にするため、 この2点により virtualenv を使いました。

wscript では Python のモジュールを自由に利用できます。 もちろん Makefile でもたくさんのコマンドを使えますので、 表現力はあまり変わらないかもしれません。 しかし、Windows 環境でも使えるのは大きなアドバンテージだと思います。

Python でのビルドシステムと言えば Scons もあります。 これはこれで良いシステムだと思いますが、 virtualenv との相性がよくありません。 設定ベースで考えるなら zc.buildout もあります。 ケースバイケースで使い分けるのが良いのではないでしょうか。

この記事では取り上げていませんが、Waf も他のビルドシステムと同様に、 依存関係を定義すること、ルールを階層化して適用することができます。 ダラダラと記述して大変になってしまう可能性もありますが、 Windows でも処理の自動化を視野に入れると、知っていると便利だと思います。

蛇足ですが、モダンな Python ハッカーには Fabric が良いそうです。 Python のバージョンもモダンに限定できる場合には便利そうですね。

コメントを投稿