6 ==================================
7 プロジェクトは ``OMakefile`` を用いてomakeにどのようにビルドするのか指定しており、文法は ``Makefile`` と似ています。 ``OMakefile`` は3つの文法規則『変数の定義』『関数の定義』『ルールの定義』を持ち合わせています。
15 変数は以下のような文法で定義されます。変数名は任意のアルファベットとアンダースコア ``_`` 、ハイフン ``-`` を用いることができます。 ::
19 値にはリテラル文字と展開された変数が定義できます。変数の展開は ``$(name)`` のような形で表され、 ``<name>`` 変数は現在の環境下において ``<value>`` に置き換わります。いくつかの例を以下に示します。 ::
23 COMMAND = $(CC) $(CFLAGS) -O2
25 この例では、 ``COMMAND`` 変数は文字列 ``gcc -Wall -g -O2`` が格納されることとなります。
27 ``make(1)`` とは違い、変数の展開は *先行して* 行われ、 *純粋な* メカニズムです(詳細は ":ref:`label4.8`",":ref:`label6.1`" を参照してください)。これはつまり、変数の値は即座に展開され、新しい変数への束縛は古い値に影響されないことを意味しています。例えば、前回の例を以下のような変数の束縛に拡張した場合について考えてみましょう。 ::
30 COMMAND = $(COMMAND) -O3
33 この例では、変数 ``X`` の値は前回のように文字列 ``gcc -Wall -g -O2`` が定義され、変数 ``Y`` の値は ``gcc -Wall -g -O2 -O3`` となります。
39 変数はまた、既存の変数に新しい文字列を追加する演算子 ``+=`` を用いることができます。例えば、以下の2つの文は等価です。 ::
42 CFLAGS = $(CFLAGS) -Wall -g
53 配列は変数名の後ろに ``[]`` を追加し、初期値を改行を使って要素を指定することで定義できます。各々の行にスペースを入れることは重要です。例えば、以下のコードは文字列 ``c d e`` が出力されます。 ::
60 println($(nth 2, $(X)))
68 -----------------------
69 文字 ``$():,=#\`` はOMakeの特殊文字に指定されています。これらの文字を普通の文字としてOMakeで扱うためには、バックスラッシュ文字 ``\`` (訳注:日本語環境の場合は円記号 ``¥`` が用いられます)でエスケープする必要があります。 ::
73 文字列を連結させるために、改行もまたエスケープする必要があります。 ::
79 バックスラッシュは他の文字でエスケープする必要が *ない* ことに注意してください。よって以下のような例は正常に動作します(これはつまり、文字列中のバックスラッシュが正常に保たれていることを表しています)。 ::
81 DOSTARGET = C:\WINDOWS\control.ini
83 ある文章をクオーティングしたい場合は ``"#..."`` エスケープを使用します。ダブルクオーテーションの数は任意で、最も外側のクオーテーションは文字列に含まれません。 ::
85 A = $""String containing "quoted text" ""
88 The # character is not special"""
98 関数は以下のような文法を用いて定義されます。 ::
103 パラメータは識別させるためにカンマを用いて分割し、コードは関数定義からインデントした状態で、別の行に設置する必要があります。例えば、以下のコードは引数 ``a`` と ``b`` をコロンを用いて結びつける関数について定義しています。 ::
108 ``return`` は関数から値を返す命令文です。 ``return`` 文は必須ではありません。もしこの文が除外された場合、最後に関数が評価した命令文の返り値が返されます。
111 バージョン0.9.6から ``return`` 文は関数を制御する命令文となりましたので、return文が呼ばれた場合、関数はただちに値を返して終了します。以下の例ではもし引数 ``a`` が ``true`` であった場合、関数 ``f`` はただちにprint文を評価することなく値1を返します。 ::
116 println(The argument is false)
119 多くの場合、あなたは関数から直接値を返さずに、セクションやネストされたコードブロックから値を返したいと思うことがあるでしょう。このような場合に、あなたは ``value`` 演算子を使用できます。実際、 ``value`` 演算子は関数だけに限らず、値が必要となった場合はどこでも使用することができます。以下の定義では、変数 ``X`` は ``a`` の値に依存して1か2が束縛され、結果を出力し、関数から値を返します。 ::
127 println(The value of X is $(X))
130 関数はGNU-makeの文法 ``$(<name> <args))`` を用いて呼び出します。 ``<args>`` はカンマで分割された値のリストです。例えば、以下のプログラムでは、変数 ``X`` は値 ``foo:bar`` を含みます。 ::
132 X = $(ColonFun foo, bar)
134 関数は別に値を含めなくても構わず、普通の関数表記を用いて関数を呼び出すことができます。例えば、以下のプログラムでは文字列“She says: Hello world”を出力します。 ::
137 println($(name) says: Hello world)
147 コメントは ``#`` 文字から始まり、行の末尾まで続きます。
155 -----------------------------
156 ファイルのインクルードには ``include`` か ``open`` 文を使います。インクルードされたファイルはOMakefileとして、同じ文法で使用できます。 ::
158 include $(Config_file)
160 ``open`` 文は ``include`` と似ていますが、一回しかインクルードされないのが特徴です。 ::
168 もしファイル名が絶対パスで指定されていない場合、 ``include`` と ``open`` 文の両方は ``OMAKEPATH`` 変数上のパスを探します。 ``open`` 文の場合、この検索は *パースするときに実行される* ので、 ``open`` の引数に他の式を含める必要はありません。
176 -------------------------------
177 omakeのスコープはインデントのレベルで定義されます。インデントレベルが上がった場合、omakeでは新しいスコープが導入されます。
179 ``section`` 文は新しいスコープを追加したい場合に有効です。例えば、以下のコードは ``X = 2`` を出力した後で、 ``X = 1`` を出力します。 ::
188 この結果について驚くかもしれませんが─ ``section`` 内での変数の束縛は外部のスコープには影響を及ぼしていないのです。
190 6.3で説明する ``export`` 文を使えば、内部スコープの変数をエクスポートすることでこの制限から抜け出すことができます。例えば、もし私たちが前回のサンプルに ``export`` 文を追加した場合、変数 ``X`` の新しい値が返されて、 ``X = 2`` が2回出力されます。 ::
200 分離されたスコープが非常に重要な結果を及ぼす場合があります。例えば、各々の ``OMakefile`` はそれ自身のスコープで評価されます。つまり各々のプロジェクトの一部は独立した設定となっているので、一つのOMakefileで変数を定義しても、これは他の ``OMakefile`` の定義に影響を及ぼしません。
202 別の例を見てみましょう。異なったビルドターゲットを指定するために、変数を分割するほうが便利である場合を考えます。この場合の頻繁に使う慣用句として、分割されたスコープを定義する ``section`` 文を使用することが挙げられます。 ::
212 この例では、 ``foo`` サブディレクトリには ``CFLAGS`` 変数に ``-g`` オプションが追加されていますが、 ``bar`` と ``baz`` ディレクトリには追加されていません。この例の場合ですとスコープのルールは非常によく働いており、 ``foo`` サブディレクトリには新しいyaccルールが追加されていますが、 ``bar`` と ``baz`` は追加されていません。さらにいうと、この追加されたルールは現在のディレクトリに影響を及ぼしていません。
221 トップレベルでの条件分岐は以下のような形となります。 ::
230 まず ``<test>`` が評価され、もしそれが *true* の値(真偽値についての詳細は ":ref:`label9.2`" を参照してください)であるならば ``<true-clause>`` のコードが評価されます。そうでなければ、残りの節が評価されます。また、 ``if`` 文は複数の ``elseif`` 宣言句を持たせることができます。 ``elseif`` と ``else`` 宣言句はなくても構いません。ただし、新しいスコープを導入するため、それぞれの宣言句はインデントされている必要があります。
232 ``if`` 文では、もし評価する文字列が空であったり、内容が ``false`` , ``no`` , ``nil`` , ``undefined`` , ``0`` であった場合、真偽値は *false* として評価されます。それ以外はすべて *true* になります。
234 以下の例では典型的な条件分岐の使い方を示しています。 ``OSTYPE`` 変数は現在使っているマシンのアーキテクチャを表しています。 ::
237 if $(equal $(OSTYPE), Win32)
243 elseif $(mem $(OSTYPE), Unix Cygwin)
250 # 他のアーキテクチャの場合は強制終了する
251 eprintln(OS type $(OSTYPE) is not recognized)
264 パターンマッチングは ``switch`` と ``match`` 文を使って実現できます。 ::
275 ``case`` の数は任意です。 ``default`` 宣言句はなくても構いませんが、使う場合は一番最後の宣言句で用いるべきです。
277 ``switch`` の場合、文字列は ``<patterni>`` と『文字通りに』比較されます。 ::
281 println(Building on mymachine)
283 println(Building on some other machine)
285 ``<patternN>`` は定数である必要はありません。以下の関数は ``pattern1`` のマッチ、そして ``##`` デリミタを用いた ``pattern2`` のマッチを表しています。 ::
287 Switch2(s, pattern1, pattern2) =
291 case $"##$(pattern2)##"
294 println(Neither pattern matched)
296 ``match`` の場合、パターンとしてegrep(1)─正規表現─が使用できます。数値変数 ``$1, $2, ...`` は ``\(...\)`` 表現を使って値を取得できます。 ::
298 match $(NODENAME)@$(SYSNAME)@$(RELEASE)
299 case $"mymachine.*@\(.*\)@\(.*\)"
300 println(Compiling on mymachine; sysname $1 and release $2 are ignored)
302 case $".*@Linux@.*2\.4\.\(.*\)"
303 println(Compiling on a Linux 2.4 system; subrelease is $1)
306 eprintln(Machine configuration not implemented)
316 ---------------------
317 OMakeはオブジェクト指向言語です。一般的に、オブジェクトはプロパティ(\*)とメソッドを持っています。オブジェクトは変数名の最後に ``.`` を加えることで定義できます。例えば、以下のオブジェクトは2次元平面上での点(1, 5)を表しています。
319 (\*訳注: 原文では"fields"となっていましたが、日本ではプロパティが主流なので言い換えました。) ::
325 println($"$(message): the point is ($(x), $(y)")
330 # これは "Hi: the point is (1, 5)" と出力されます。
333 オブジェクトのプロパティ ``x`` と ``y`` は点の座標を表しています。 ``print`` メソッドは点の現在位置を出力します。
342 オブジェクトと同様にして *クラス* も定義できます。例えば、私たちは現在、オブジェクトを生成したり、移動したり、位置を出力するメソッドを持った ``Point`` クラスを作りたいものとしましょう。クラスはオブジェクトの作り方と似ていますが、 ``class`` 文を用いて名前を定義付ける点が違いです。 ::
364 println($"The point is ($(x), $(y)")
366 p1 = $(Point.new 1, 5)
367 p2 = $(p1.move-right)
369 # "The point is (1, 5)" と出力
372 # "The point is (2, 5)" と出力
375 変数 ``$(this)`` は現在のオブジェクトを参照していることに注目してください。また、クラスとオブジェクトは新しいオブジェクトを返す ``new`` と ``move-right`` メソッドを持っています。これは、オブジェクト ``p2`` とオブジェクト ``p1`` が別物であり、 ``p1`` はオリジナルの座標(1, 5)を保持していることを表しています。
383 クラスとオブジェクトは継承(多重継承を含む)を ``extends`` 文によってサポートしています。以下の ``Point3D`` では、 ``x`` , ``y`` , ``z`` プロパティを持ったクラスを定義しています。新しいオブジェクトは、親クラスやオブジェクトが持つすべてのプロパティやメソッドを継承します。 ::
394 println($"The 3D point is ($(x), $(y), $(z))")
396 # "new"メソッドはオーバーライドされていませんので、
397 # 下のメソッドは新しく点(1, 5, 0)を返します。
398 p = $(Point3D.new 1, 5)
404 single: ConfMsgChecking()
405 single: ConfMsgResult()
410 ``static.`` オブジェクトはOMakeが動作している間、ずっと一定の値を保持していたい場合に使うオブジェクトです。このオブジェクトはプロジェクトを設定する際に頻繁に用いられます。プロジェクトを設定する変数が何回も書き換えられるのはリスクが高いので、 ``static.`` オブジェクトは設定がちょうど一回だけ行われることを保証してくれます。以下の(どこか冗長な)サンプルでは、 ``static.`` 節がLaTeXコマンドが使用可能であるかどうか調べるために使われています。 ``$(where latex)`` 関数は ``latex`` の絶対パスか、latexコマンドが存在しない場合は ``false`` を返します。 ::
413 LATEX_ENABLED = false
414 print(--- Determining if LaTeX is installed )
420 println($'(enabled)')
422 println($'(disabled)')
424 OMakeの標準ライブラリを用いると第14章にあるような ``static.`` をプログラミングするための、多くの有用な関数を試すことができます。標準ライブラリを用いると、上のコードは以下のように書き直せます。 ::
426 open configure/Configure
428 LATEX_ENABLED = $(CheckProg latex)
430 プロジェクトの設定として使われている ``static.`` 節は、 ``ConfMsgChecking`` や ``ConfMsgResult`` 関数を使って、 ``static.`` 節でどういう動作をしているのかについて出力すべきです(もちろん、標準ライブラリにある多くの関数が、この作業を自動的に行ってくれます)。
439 *この機能はバージョン 0.9.8.5 で搭載されました。*
441 ``.STATIC`` 節の書き方は ``static.`` 節の書き方と似ています。文法は以下の3つのどれを選んでも書くことができます。::
443 # bodyで定義されたすべての変数をエクスポート
448 .STATIC: <dependencies>
451 # ファイル依存と同様に、どの変数をエクスポートしたいのか指定する場合
452 .STATIC: <vars>: <dependencies>
455 ``<vars>`` は定義する変数名、 ``<dependencies>`` はファイル依存─もし一つのファイルが変更された場合、ルールは再評価される─を指定します。 ``<vars>`` と ``<dependencies>`` はもし必要ならば除外することができ、 ``<body>`` 中で定義されたすべての変数はエクスポートされます。
457 たとえば、前回のセクションで示した最後のサンプルは以下のように改良できます。 ::
459 open configure/Configure
461 LATEX_ENABLED = $(CheckProg latex)
463 印象は ``static.`` ( ``.STATIC`` を使用する代わりに) を使った場合とほとんど似ています。しかしながら、殆どの場合において ``.STATIC`` のほうが優位です。理由は2つあります。
465 まず、 ``.STATIC`` 節は遅延評価されます。これはつまり、 ``.STATIC`` 内の変数が一つでも解決されないのならば、評価されることはないということを意味しています。例えばもし ``$(LATEX_ENABLED)`` が決して評価されない変数だとすると、 ``.STATIC`` 節は決して評価されることはありません。これは少なくとも一回はいつでも評価される ``static.`` 節とは対照的です。
467 次に、 ``.STATIC`` 節はファイル依存を指定できます。これは、 ``.STATIC`` 節に記憶性を持たせたい場合に有効です。例えば、キーと値のペアを持ったテーブルから辞書を作りたい場合を考えてみましょう。 ``.STATIC`` 節を使うことによって、omakeはこの計算を(omakeが毎回動くときに計算するのではなく)入力されたファイルが変更された場合のみ計算するようにふるまいます。以下の例では、 ``awk`` 関数がファイル ``table-file`` をパースするために用いられています。 ``awk`` 関数は ``key = value`` の形をした行を発見する度に、そのキーと値のペアを ``TABLE`` 変数に追加します。 ::
472 case $'^\([[:alnum:]]+\) *= *\(.*\)'
473 TABLE = $(TABLE.add $1, $2)
476 ルールの依存関係が変わった場合はいつでも ``.STATIC`` 節は再計算されます。このルール内での対象は、エクスポートする変数となります(この場合ですと ``TABLE`` 変数が相当します)。
484 ``.MEMO`` ルールは、その結果が独立して動いている ``omake`` インスタンス間で保存されない点を除いて、 ``.STATIC`` ルールと等価です。
494 ``.STATIC`` と ``.MEMO`` ルールはまた、計算された値とリンクしている『キー』を表す ``:key:`` を使うことができます。 ``.STATIC`` ルールを、キーと値がリンクした辞書として考えることは有用です。 ``.STATIC`` ルールが評価された場合、結果は指定されたルールによって定義された ``:key:`` がテーブル内に保存されます(もし ``:key:`` が指定されていない場合、デフォルトのキーが代わりに用いられます)。言い換えると、ルールは関数のようなものです。 ``:key:`` は関数の『引数』を表しており、ルール部分で結果を計算します。
496 これを確かめるために、 ``.MEMO`` ルールをフィボナッチ関数に改良してみましょう。 ::
501 println($"Computing fib($i)...")
503 if $(or $(eq $i, 0), $(eq $i, 1))
506 add($(fib $(sub $i, 1)), $(fib $(sub $i, 2)))
509 println($"fib(10) = $(fib 10)")
510 println($"fib(12) = $(fib 12)")
512 このスクリプトを走らせた場合、以下のような結果となります。 ::
530 フィボナッチ関数は各々の引数の場合において、一回だけしか計算されていないことに注目してください。これは普通にプログラムした場合ですと、指数関数的に計算時間が増えてしまいます。言い換えると、 ``.MEMO`` ルールは計算結果を記憶(memoization)しているからこそ、この名前なのです。もし ``.STATIC`` ルールを代わりに使った場合、すべての ``omake`` インスタンスにおいて値が保存されていることに注意してください。
532 一般的には、あなたは ``.STATIC`` か ``.MEMO`` ルールを関数内で用いる場合はいつでも、ふつう ``:key:`` を使いたくなるでしょう。しかしながら、これは必須ではありません。以下の例では、 ``.STATIC`` ルールが、何か計算時間のかかる作業を一回だけ行う場合を表しています。 ::
536 y = $(expensive-computation)
539 あなたがフィボナッチ関数のような再帰的な関数を定義する場合、さらに以下の点に注意すべきです。もし ``:key:`` を除外してしまった場合、ルールは関数自体に対して定義されてしまい、循環された依存関係で評価されてしまいます。以下は ``:key:`` を除いたフィボナッチ関数の出力結果です。 ::
550 この動作は ``i = 0 || i = 1`` の場合に達するまで ``result`` の値が保存されていないので、 ``fib`` は自身を ``fib(0)`` に達するまで再帰的に呼び出し、そして ``result`` の値は0に修正されてしまうために生じます。
552 再帰的な定義が無難に動作する場合もありますが、あなたは普通 ``:key:`` 引数をつけることで、各々の再帰的な呼び出しが異なった ``:key:`` を持つようにするでしょう。これは多くの場合において、 ``:key:`` が関数の引数すべてに含めるべきであることを示しています。
572 OMakeではいろんな方法でそれぞれの値を表すことができます。私たちはこれを以下のリストにしました。
576 * コンストラクタ: ``$(int <i>)`` (:ref:`label9.4.1`)
577 * オブジェクト: ``Int`` 12.1.4
578 * 有限の値をもった整数型で、精度はプラットフォーム上のOCamlに依存します(32ビットのプラットフォーム上では31ビット、64ビットのプラットフォーム上では63ビット)(訳注: 1ビットは正負の判定に使われます)。
579 * 詳細は ":ref:`label9.4.3`" を参照してください。
583 * コンストラクタ: ``$(float <x>)`` (:ref:`label9.4.2`)
584 * オブジェクト: ``Float`` 12.1.5
585 * 浮動小数点型で、精度は64ビットです。
589 * コンストラクタ: ``$(array <v1>, ..., <vn>)`` (:ref:`label9.3.1`)
590 * オブジェクト: ``Array`` 12.1.7
591 * 配列は有限の数の値をもったリストを表します。配列はまた以下のように定義することもできます。 ::
598 * 詳細は ":ref:`label9.3.5`", ":ref:`label9.3.8`", ":ref:`label9.3.4`" を参照してください。
602 * オブジェクト: ``String`` 12.1.8
603 * 通常、すべての文字からなるシーケンスは配列として表現され、単純にソース中に書き表すことで初期化できます。内部で文字列はいくつかの断片としてパースされます。文字列はしばしば、ホワイトスペース(訳注: ホワイトスペースはスペース、タブを含んだ空白文字のことです)によって分割された値のシーケンスとして定義されます。 ::
605 osh>S = This is a string
618 * *データ* 文字列は、ホワイトスペースが重要な意味を持つ場合に用いられます。これは単純な一つの値として定義され、配列にはなりません。コンストラクタはクオーテーション ``$"..."`` と ``$'...'`` で表現できます。 ::
620 osh>S = $'''This is a string'''
621 - : <data "This is a string"> : String
623 * 詳細は『 :ref:`label7.2` 』を参照してください。
627 * コンストラクタ: ``$(file <names>)`` (:ref:`label10.1.1`)
628 * オブジェクト: ``File`` 12.1.13
629 * ファイルオブジェクトはファイルの絶対パスを表すオブジェクトです。ファイルオブジェクトは絶対パスとして見ることができます。文字列への変換はカレントディレクトリに依存しています。 ::
631 osh>name = $(file foo)
632 - : /Users/jyh/projects/omake/0.9.8.x/foo : File
636 - : /Users/jyh/projects/omake : Dir
640 * 詳細は ":ref:`label10.6.1`" を参照してください。
644 * コンストラクタ: ``$(dir <names>)``
645 * オブジェクト: ``Dir`` 12.1.14
646 * ディレクトリオブジェクトはファイルオブジェクトと似ていますが、ディレクトリとしてふるまいます。
648 * map (dictionary) - マップ (辞書)
650 * オブジェクト: ``Map`` 12.1.2
651 * マップ/辞書オブジェクトは値と値を結びつけるテーブルです。 ``Map`` オブジェクトは空のマップです。データ構造は永続的に保持され、すべての演算は分かりやすく関数的です。特別な構文 ``$|key|`` によって文字列のキーを表現することができます。 ::
654 osh>table = $(table.add x, int)
662 * コンストラクタ: ``$(fopen <filename>, <mode>)`` (:ref:`label10.8.4`)
663 * オブジェクト: ``InChannel`` 12.1.16, ``OutChannnel`` 12.1.17
664 * チャネルオブジェクトは入力や出力のバッファとして使います。
668 * コンストラクタ: ``$(fun <params>, <body>)`` (:ref:`label9.5.1`)
669 * オブジェクト: ``Fun`` 12.1.9
670 * 関数オブジェクトはいろんな方法で定義できます。
674 $(fun i, j, $(add $i, $j))
681 * *この機能はバージョン0.9.9.0で導入されました。* 無名関数の引数 ::
683 osh>foreach(i => $(add $i, 1), 1 2 3)
684 - : <array 2 3 4> : Array
688 * オブジェクト: ``Lexer`` (:ref:`label10.11.9`)
689 * このオブジェクトは字句解析器として表現します。
693 * オブジェクト: ``Parser`` (:ref:`label10.11.13`)
694 * このオブジェクトはパーサとして表現します。