第3回 話題騒然! 「言語内DSL」の概念とRake【前編】Ruby on Rails究極指南(2/2 ページ)

» 2007年03月20日 07時00分 公開
[角谷信太郎,ITmedia]
前のページへ 1|2       

ビルド言語の分類とRake

 DSLはスタイルによって幾つかに分類できますが、ここではプログラミング言語指向のものに注目します。プログラミング言語指向のDSLは、言語内(internal)DSLと異語外(external)DSLに大別できます。言語内DSLとは実行時に利用される言語(ホスト言語)とDSLが同じものを、言語外DSLはホスト言語とDSLが異なるものを指します。いずれも以前から行われているアプローチであり、例えば言語内DSLはLispやSmalltalkで、言語外DSLは多くのUNIXツール(例えばawkやprocmail、PostScript)で使用されています。

 マーティン・ファウラー氏は、make、Ant、Rakeの各ビルド言語をDSLのスタイルに従って次のように整理しています。

  • make:独自文法による言語外DSL
  • Ant:XMLを言語として利用した言語外DSL
  • Rake:Rubyによる言語内DSL

 DSLとしてホスト言語を利用するメリットは、学習コストの節約と、ホスト言語の文法や機能、周辺ツールを活用できる点にあります。「ドメインに特化した言語」という観点からはデメリットともなりますが、ビルドツールのようなユーザー層がプログラマーである分野では、デメリットよりもメリットの方が大きいでしょう。

Rakeの簡単な使い方

 それでは、実際にRakeを使ってみましょう。Railsが導入済みであれば、Rakeもすでにインストールされています。Rakeが正常にインストールされているかどうかは、rakeコマンドを実行することで確認できます。

$ rake --version

rake, version 0.7.2


 なお、ヘルプを表示したい場合は、「--help」オプションを指定します。

ビルドするプログラムの概要

 ここでは、Cで書かれた「Hello World」を出力するプログラムをビルドします。ソースコードの内容は省略しますが、全体の依存関係が図2のようになっているケースを想定します。

図2 図2 helloの依存関係

 コンパイル対象のソースコードとヘッダファイル、RakeのビルドファイルであるRakefileは、すべて同じディレクトリに配置しておきます。

$ ls

greet.c greet.h main.c rakefile


Rakefileの記述例

 helloプログラムをビルドするには、リスト1のようなRakefileが必要となります。Rubyの基本的な文法を理解していれば、Rakeの詳細を知らなくても処理内容が読み取れるのではないでしょうか。Rakefileの内容は次のとおりです。


 1 require 'rake/clean'
 2 CLEAN.include %w(hello *.o)
 3
 4 task :default => ['hello']  5
 6 file 'main.o' => ['main.c', 'greet.h'] do  7     sh 'cc -c -o main.o main.c'  8 end  9
10 file 'greet.o' => ['greet.c'] do 11     sh 'cc -c -o greet.o greet.c' 12 end 13
14 file 'hello' => ['main.o', 'greet.o'] do 15     sh 'cc -o hello main.o greet.o' 16 end
リスト1 Rakefile

 1行目では、Rakeがビルトインで提供しているcleanタスクを読み込んでいます。2行目では、cleanタスクで削除対象とするファイルを配列で指定しています。ここでは最終成果物であるhelloと、*.cをコンパイルして生成するオブジェクトファイルが削除対象です。

 4行目は、rakeコマンドを引数なしで起動した場合に実行されるデフォルトのタスクです。Rakeの基本的な文法は、

task タスク名 => [依存タスク] do

    処理内容

end


というものです。タスク名の指定にはシンボルを使用することに注意してください。

 6行目以降では、ビルドに必要な各処理をそれぞれタスクとして定義しています。「task」ではなく「file」と記述しているのは、ファイル生成に特化したタスクだからです。Rakeではこれを「ファイルタスク」と呼んでいます。ファイルタスクが通常のRakeタスクと異なる点は次の2つです。

  • タスク定義では「task」の代わりに「file」と記述
  • ファイルタスク名はシンボルではなく文字列で指定

 ブロックには、ファイルタスク名で指定したファイルを生成するための処理を記述します。「sh」はRakeが提供しているメソッドであり、与えられた文字列をシステムコマンドとして評価・実行します。

 なお、実際にRakeを利用する際には、タスク定義の重複個所は「ルール」と呼ばれるRakeの機能や、Rakeが提供する変数などを使ってパターン化できます。この例では説明を簡単にするため、重複個所はそのままにしてあります。

 ファイルタスクと同様の位置づけのタスクとして、ほかに「ディレクトリタスク」が提供されています。名前のとおり、ディレクトリの生成に特化したタスクです。書式は、

directory "ディレクトリ名"


であり、ディレクトリ名はファイルタスクと同様に文字列で指定します。

  • rakeコマンドによるビルドの実行

 Rakefileで定義したタスクを実行するには、Rakefileを置いたディレクトリでrakeコマンドを実行します。リスト1のRakefileではdefaultタスクを定義しているので、引数なしで起動するとhelloのファイルタスクが実行されます。

$ rake

(in /path/to/rakefile/directory)

cc -c -o main.o main.c

cc -c -o greet.o greet.c cc -o hello main.o greet.o


 生成されたファイル群を確認し、helloが実行できることを確認します。

$ ls

greet.c greet.h greet.o hello main.c main.o rakefile

$ ./hello

Hello, World


 再度rakeコマンドを実行しても、ファイルに変更がなければ何も実行されません。そこで、greet.oを削除してからrakeを実行してみると、

$ rm greet.o

$ rake

(in /path/to/rakefile/directory)

cc -c -o greet.o greet.c

cc -o hello main.o greet.o


のようにgreet.oが再生成され、greet.oに依存しているhelloが再コンパイルされます。これによって、Rakefileに記述した成果物の時系列的な依存関係がRakeによって解決されていることが分かります。

 cleanタスクを実行すると、削除対象としたファイルが削除されます。

$ rake clean

(in /path/to/rakefile/directory)

rm -r hello

rm -r main.o

rm -r greet.o

$ ls

greet.c greet.h main.c rakefile


次回は

 すでに紹介したとおり、言語内DSLはホスト言語そのものを利用してDSLを構築します。したがって、言語内DSLではホスト言語の機能をフル活用できます。Rubyで構築された言語内DSLであれば、Rubyの制御構造の利用はもちろん、独自にクラスやメソッドを定義できますし、サードパーティーのRubyライブラリを利用することも可能です。次回はこのあたりを解説します。

本記事は、オープンソースマガジン2006年3月号「Ruby on Rails 1.0の世界」を再構成したものです。


前のページへ 1|2       

Copyright © ITmedia, Inc. All Rights Reserved.

注目のテーマ