From 18e83147e32b90e07993576b9be8ce5ea573b136 Mon Sep 17 00:00:00 2001 From: Jun Furuse Date: Tue, 4 Jul 2017 04:29:41 +0000 Subject: [PATCH] omake_points.md is now unified into omake.md --- omake.md | 239 +++++++++++++++++++++++++++++++++++++++++++----- omake_points.md | 135 --------------------------- 2 files changed, 216 insertions(+), 158 deletions(-) delete mode 100644 omake_points.md diff --git a/omake.md b/omake.md index c83045b..814e54a 100644 --- a/omake.md +++ b/omake.md @@ -1,51 +1,244 @@ # OMake に関するメモ -# return する関数は内部で作った dependency を export できない +# もっとも大事なこと + +## Dynamic scoping ``` -MyOCamlPPX(name, files) = - WithOCamlVeryClean() - MyOCamlGenericProgram($(name), $(files)) - export - TARGETS[]=$(name).opt $(name).run $(name)$(EXE) - return $(TARGETS) +X=1 +GETX() = + return $(X) +X=2 +eprintln($(GETX)) # 2 +``` + +## 代入がない + +``` +X=1 +F() = + eprintln($(X)) # 1 + X=2 # Xに1を代入するわけではない + eprintln($(X)) # 2 +F() +eprintln($(X)) # 1 +``` + +これは次のOCamlプログラムとだいたい同じ: +``` +let x = 1 in (* let x = ref 1 ではない *) +let f () = + print_int x; + let x = 2 in (* x := 2 ではない *) + print_int x +in +f (); +print_int x +``` + +## でも関数から外の環境をいじりたい + +変数代入もなくI/Oとdynamic scoping以外は純粋関数型っぽいが、 +ビルドルールを書いているとスコープの外の変数の値を変化させたい事が多い。 +`export`を使うとこれができる + +``` +X=1 +SETX()= + export X # X の値を上のスコープに伝える + X=2 +SETX() +eprintln($(X)) # 2 +``` + +OCamlでは「だいたい」こうなる: +``` +let x = 1 +let setx () = + let x = 2 in + x +in +let x = setx () in +print_int x +``` +「だいたい」というのは`SETX`自体が関数なので関数の返り値と`export`で出す値の +両方があるともうちょっと違った翻訳になるから。 + +`export`も外側のスコープにある変数への代入ではなく、外側のスコープでの変数定義なので、 +存在していない変数束縛でも作ることができる: + +``` +MAKEY()= + export Y + Y = 3 +MAKEY() +eprintln($(Y)) # 3 ``` -`MyOCamlGenericProgram(..)` は新しい依存性を付け加えるのだけど `export` -しているのに外に反映されない。 `return` があると駄目らしい。 +# ルールと関数は違う!そして`section`について + +いや、当たり前ですけど。たまにルール内で関数みたいなコードを書いておかしいなあとなるので。 + +``` +hello.c: + FP=1 + eprintln($(FP)) # unbound variable: global.FP + echo "hello" > hello.c +``` + +``` +FP=2 +hello.c: + FP=1 + eprintln($(FP)) # 2 !!! + echo "hello" > hello.c +``` -`value` を使って関数型ぽく書くとよい。 +ルールのボディはコマンド列であって式列ではないのですね。 +ルール中で変数を変更したい場合は、`section` を使って式列を書きましょう: ``` -MyOCamlPPX(name, files) = - WithOCamlVeryClean() - MyOCamlGenericProgram($(name), $(files)) +FP=2 +hello.c: + section + FP=1 + eprintln($(FP)) # 1 !!! + echo "hello" > hello.c +``` + +## 関数の呼び出し + + +# 次に大事なこと + +## `export` いろいろ + +Reference manual の "Exporting the environment" はよく読む必要がある。まず読んだ上で。 + +### 引数なし `export` は気をつける + +引数なし `export` は割といろんなものを勝手に外に出してくれるので便利そうだが、 +実は予期しないものも出してしまうので使わないほうが良いと思う: + +``` +f(a,b) = + x = $(add $(a), $(b)) export - TARGETS[]=$(name).opt $(name).run $(name)$(EXE) - value $(TARGETS) +``` +例えば、上の関数は`x`に`a`と`b`の和をセットするつもりだが、`a`と`b`も外側に出してしまう。 +`if`の中とかの無引数`export`は問題ないが、関数bodyのトップでの`export`は、 +一時変数まで外に出してしまうので、してはいけないと思われる。 +一時変数にすべて`private`変数を使えば大丈夫だが、いちいち`private`書くの面倒くさい。 + +### 引数なし `export` を封印するにはどうしたらよいですか + +Reference manual の "Exporting the environment" をよく読んで。 + +* `export .RULE` で implicit rule と implicit dependencies を外に出せます +* `export .PHONY` で phony target を外に出せます + +Rule, dependency や phony target の`export`も変数の`export`と同じで、上のスコープにある +同名のものをshadowするはず。 + +### 前置 `export` のほうがよさげ + +`if`でスコープを作るのでいちいち`export`しなければならない、面倒なと思う人は: + +``` +updateX(b)= + if $(b) + X=2 + export + else + X=3 + export + export X +updateX(true) +eprintln($(X)) # 2 ``` -しかしこの関数を他の関数内部で呼ぶと export の効果が無くなる。困る。 -例えば、次のコードでは `MyOCamlPPX` が作った依存が使われず失われてしまう: +`export` を前置すると捗ります: ``` -MyOCamlFindPackage(ppx_format, $(MyOCamlPPX ppx_format, $(FILES))) +updateX(b)= + export X + if $(b) + X=2 + else + X=3 +updateX(true) +eprintln($(X)) # 2 ``` -別々に書かなければならない。 +## `return` を使うと `export` が無効になる + +`return`は関数内から外に一気に脱出して値を返しますが、その際、内部での`export`宣言は無視されます。 ``` -targets=$(MyOCamlPPX ppx_format, $(FILES)) -MyOCamlFindPackage(ppx_format, $(targets) $(MyOCamlPackageExtras)) +X=1 +F(y)= + export X + X=2 + return $(y) +Z=$(F 1) +eprintln(Z=$(Z) X=$(X)) # Z=1 X=1 !!! ``` -こういうのは本当に困る。 +`return`がどうしても必要なければ `value`がおすすめ + +``` +X=1 +F(y)= + export X + X=2 + value $(y) +Z=$(F 1) +eprintln(Z=$(Z) X=$(X)) # Z=1 X=2 !!! +``` + + + +## Implicit rule -# Dependency が足りないように見える +Implicit rule は `%.ext` みたいなワイルドカードを使ったルール。よくあるのは: + +``` +%.ext1: %.ext2 + ... +``` + +こんなのも書ける: +``` +%a: %b + cp $< $@ +``` + +でもディレクトリ名を左辺には書けない +``` +x/%a: %b # rejected + cp $< $@ +``` + +右辺には書けます +``` +%a: x/%b + cp $< $@ +``` + +## Dependency が足りないように見える OCaml で `make inconsistent assumptions over implementation Xxx` が出た時。 ターゲットを `omake --show-dependency ターゲット` でビルドしてみて確認するとよい。 足りていないのを確認できる。 +# 豆知識 + +## `CREATE_SUBDIRS` + +Reference manual の "Temporary directories" に `CREATE_SUBDIRS` という秘密の変数について +さくっと書いてある。 `.SUBDIRS: ` で `dirs` が無い場合、 `CREATE_SUBDIRS=true` だと +勝手に掘ってくれるとある。ひどい。 + + diff --git a/omake_points.md b/omake_points.md deleted file mode 100644 index cb16015..0000000 --- a/omake_points.md +++ /dev/null @@ -1,135 +0,0 @@ -# OMake はまりポイント - -## スコープ - -OMake ではインデントが深くなる毎にローカルスコープができる。 - -## 名前空間 - -恐しいことに名前空間が 3 つある: private, this と global。 - -### global - -普通の変数。public とも言う。大体これを使っておけばよいと思う。 - -初期化していないものはダメ -``` -eprintln($(X)) # unbound variable: global.X -``` - -``` -X=1 -eprintln($(X)) # 1 -X=2 -eprintln($(X)) # 2 -``` - -グローバル変数なのに、ローカルスコープでの変更は `export` しないと外に伝わらない -``` -X=1 -section # ローカルスコープ - X=2 - eprintln($(X)) # 2 -eprintln($(X)) # 1 !!! -``` - -``` -X=1 -section # ローカルスコープ - X=2 - eprintln($(X)) # 2 - export # X の変更が外に反映される -eprintln($(X)) # 2 !!! -``` - -``` -section - X=2 - eprintln($(X)) # 2 -eprintln($(X)) # unbound variable: global.X -``` - - -``` -section - X=2 - eprintln($(X)) # 2 - export -eprintln($(X)) # 2 -``` - -### ルールの export を忘れる - -ローカルスコープで宣言したルールも外で有効にしたい場合は(普通したいのだが) `export` としなければいけない。 - -``` -MyOCamlTop(name, files) = - .DEFAULT: $(OCamlTop $(name), $(files)) - export -``` -これは `MyOCamlTop(name, files)` と唱え `omake` すると自動的にカスタムトップレベルを生成する -というルールを作る、が `export` を忘れると効果がなくなる。 - -# ルール - -ルール中で変数を変更しても次行に伝わらない!! - -``` -hello.c: - FP=1 - eprintln($(FP)) # unbound variable: global.FP - echo "hello" > hello.c -``` - -``` -FP=2 -hello.c: - FP=1 - eprintln($(FP)) # 2 !!! - echo "hello" > hello.c -``` - -これはどういうことか、良く判らないのだが、ルールのボディに書くのはコマンド列であって -式列ではない、ということらしい。 - -ルール中で変数を変更したい場合は、`section` を使って式列を書く: - -``` -FP=2 -hello.c: - section - FP=1 - eprintln($(FP)) # 1 !!! - echo "hello" > hello.c -``` - -# 引数なしの `export` - -引数なしの `export` が外に export する変数やルールをちゃんと理解する事が重要 - -* 全ての動的スコープの変数の値 -* カレントディレクトリ -* Unix環境変数 -* 現在の implicit rule や implicit dependencies -* 現在の "phony" ターゲット宣言 - -特に関数引数の名前が外に漏れ出るのが非常に困る。これが理由で `omake` 使えないと言われても仕方がない: - -``` -export1(foo) = - eprintln(foo=$(foo)) # foo=a - export # argument is exported to the outside!!! - -export1(a) -eprintln(foo=$(foo)) # foo=a !!! -``` - -# 引数ありの `export` - -引数がある `export` はその引数を評価後、その引数によって - -* 値が空なら引数無し `export` と同じ -* `$(export 変数..)` で作られた環境の場合、その環境 -* `.RULE`: implicit rules and implicit dependencies. -* `.PHONY`: the set of “phony” target declarations. -* その他の文字列は変数名として扱いその変数を export する