久しぶりにGNU Makeを使いました。
$ make --version
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.This program built for i686-pc-msys
現状(2014年)の最新版は2013年リリースの4.0ですので、僕が使っているのは古いです。(GNU Makeのバージョンアップは 2006年 3.81 → 2010年 3.82 → 2013年 4.0 なので、頻繁に更新されているわけではありません。)
Unix由来のツールでは、空白を含むフィル名をうまく扱えないものがあります。なので僕は、空白を含むファイル名をいまだに使わないようにしています。しかし、既に存在するファイル名/ディレクトリ名が空白を含んでいる例はたくさんあります。GNU Makeにも、空白を含むファイル名(パス)を処理してもらいたい状況なのですよ。
とりあえず実験。
$ echo hello > foobar.txt
foo_bar.txt : foobar.txt
cp $? $@
$ make -n
cp foobar.txt foo_bar.txt
これはうまくいって当然。
さて、空白を含むファイル名の登場です。
foo bar.txt : foobar.txt
cp $? $@
$ make -n
cp foobar.txt foo
そうなりますよね、やっぱり。
バックスラッシュによるエスケープはどうか?
foo\ bar.txt : foobar.txt
cp $? $@
$ make -n
cp foobar.txt foo bar.txt
実行すると:
$ make
cp foobar.txt foo bar.txt
cp: target `bar.txt' is not a directory
make: *** [foo bar.txt] Error 1
空白はシェルによって区切り記号と解釈されるから、まー、こうなりますわな。
二重にエスケープ。
foo\\ bar.txt : foobar.txt
cp $? $@
$ make -n
cp foobar.txt foo\
なるほどね。ということは、
foo\\\ bar.txt : foobar.txt
cp $? $@
これ(↑)ならいいか。
$ make -n
cp foobar.txt foo\ bar.txt
うん、いけそうだ。
$ make
cp foobar.txt foo\ bar.txt$ cat foo\ bar.txt
hello
シェルのコマンドラインを正しく作れたようです。
他の方法は?
"foo bar.txt" : foobar.txt
cp $? $@
$ make -n
cp foobar.txt "foo
ありゃりゃ。
'foo bar.txt' : foobar.txt
cp $? $@
$ make -n
cp foobar.txt 'foo
これも同じ。クォーティングはダメ。
ファイル名のリストはどうでしょうか?
$(foreach x, foo\\\ bar.txt bar\\\ baz.txt, $(info $x))
$(info )end :
@echo Done
$ make
foo\\\
bar.txt
bar\\\
baz.txtDone
あれまー、2つのファイルのつもりが4つの名前の並びとして解釈されています。
変数を使ったらどうかな?
NIL=
SP=$(NIL) $(NIL)$(foreach x, foo$(SP)bar.txt bar$(SP)baz.txt, $(info $x))
$(info )end :
@echo Done
$ make
foo
bar.txt
bar
baz.txtDone
効果なし。
http://savannah.gnu.org/bugs/?712 によると、この問題は2002年から(実際はそれより前からでしょう)指摘されているようですが、現在も解決はされてないみたい。
「空白を含まないシンボリックリンクを使う」、「空白を代替文字(例えば'+')に置き換えて、処理の直前に戻す」、「空白を'?'に置き換えて、シェルのワイルドカード展開に任せる」とか提案されてますが、めんどくさいし問題があります。
僕も以前、空白を'%20'に置き換えてfile:スキームのURIにしてみましたが、file:スキームを解釈してくれるプログラムは少ないので実用性はない方法です。
その場しのぎのバッドノウハウはあるけど、うまい解決法はないようです。困ったなー。
[追記 date="その日"]
GNU Make 4.0のウリは、GNUのScheme処理系であるGNU Guileが組み込まれたことなんですけど、MakeがGuile上に構築されているわけじゃなくて、MakefileからGuile機能を呼び出すインターフェイスが設けられただけみたい。せっかくのSchemeインタープリタ、この使い方はもったいないよね。
GNU MakeはもともとLispっぽいし(「プログラミング言語Makeの関数型フラグメント:まとめとサンプル」参照)、いっそのことLisp処理系(Guileでいいけど)の上にビルドツールを作ってしまえばいいのに、と思います。
そうなれば、ファイル名(パス)は文字列だし、ファイル名の並びは文字列のリスト(または配列)なので、空白だろうが特殊文字だろうが平気です。ルールを容易に書くためのシンタックスシュガーをMakeから借りればいいんじゃないかと。
もちろん、これだと過去のMakefileとの構文互換性を保つのが(おそらく)不可能という大問題がありますが、完全互換じゃなくても、Makeの非互換方言くらいの位置づけもアリなのでは。
[/追記]