このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

GNU Makeと空白を含むファイル名

久しぶりに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.txt

Done

あれまー、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.txt

Done

効果なし。


http://savannah.gnu.org/bugs/?712 によると、この問題は2002年から(実際はそれより前からでしょう)指摘されているようですが、現在も解決はされてないみたい。

「空白を含まないシンボリックリンクを使う」、「空白を代替文字(例えば'+')に置き換えて、処理の直前に戻す」、「空白を'?'に置き換えて、シェルのワイルドカード展開に任せる」とか提案されてますが、めんどくさいし問題があります。

僕も以前、空白を'%20'に置き換えてfile:スキームのURIにしてみましたが、file:スキームを解釈してくれるプログラムは少ないので実用性はない方法です。

その場しのぎのバッドノウハウはあるけど、うまい解決法はないようです。困ったなー。



[追記 date="その日"]

GNU Make 4.0のウリは、GNUScheme処理系であるGNU Guileが組み込まれたことなんですけど、MakeがGuile上に構築されているわけじゃなくて、MakefileからGuile機能を呼び出すインターフェイスが設けられただけみたい。せっかくのSchemeインタープリタ、この使い方はもったいないよね。

GNU MakeはもともとLispっぽいし(「プログラミング言語Makeの関数型フラグメント:まとめとサンプル」参照)、いっそのことLisp処理系(Guileでいいけど)の上にビルドツールを作ってしまえばいいのに、と思います。

そうなれば、ファイル名(パス)は文字列だし、ファイル名の並びは文字列のリスト(または配列)なので、空白だろうが特殊文字だろうが平気です。ルールを容易に書くためのシンタックスシュガーをMakeから借りればいいんじゃないかと。

もちろん、これだと過去のMakefileとの構文互換性を保つのが(おそらく)不可能という大問題がありますが、完全互換じゃなくても、Makeの非互換方言くらいの位置づけもアリなのでは。

[/追記]