yaml って Makefile を書くのに向いてるんじゃないか?

設定ファイルとして、私は yaml がお気に入りです。

なんといっても記述量が少ない。DSL もいいですが、やっぱり特化型には敵わないと思います。また、ライブラリが豊富で主要なプログラミング言語のほとんどで使えるというのも魅力です。


話は変わって「C/C++ 開発のストレスを軽減する」で触れたとおり、ビルドツールとして SCons、CMake がお気に入りです。ただし、SCons は Python なのでどうしても文字列を何らかの引用符で括る必要があるということに不満があります。CMake は専用の書式で書かなければならないということに不満があります。


例えば、SCons と CMake で hello world をビルドするときを考えてみます。

# Scons
Program(target = "hello", source = ["helloworld.c"])
# CMake
project(hello c)
add_executable(hello helloworld.c)


これくらいだとどっちも十分にシンプルなので、どっちで書いてもいいかな、という気がしますね。仮に、このプログラムが順調に育ちソースコードが増えていったとしましょう。

# Scons
Program(target = "hello", source = ["helloworld.c", "helloearth.c", "hellouniverse.c"])
# CMake
project(hello c)
add_executable(hello helloworld.c helloearth.c hellouniverse.c)


まだ大丈夫ですね。次に、プラットフォーム毎に異なるソースコードコンパイルしたくなったとしましょう。

# Scons
import os
env = Environment()
platform  = env['PLATFORM']
sources = ["helloworld.c", "helloearth.c", "hellouniverse.c"]

if platform == 'posix':
  sources.append('helloposx.c')
elif platform == 'darwin':
  sources.append('hellodarwin.c')
elif platform == 'win32':
  sources.append('hellowin32.c')

env.Program(target = "hello", source = sources)
# CMake
project(hello c)

set(sources)
if (UNIX)
  set(sources helloworld.c helloearth.c hellouniverse.c helloposix.c)
elseif (APPLE)
  set(sources helloworld.c helloearth.c hellouniverse.c hellodarwin.c)
elseif (WIN32)
  set(sources helloworld.c helloearth.c hellouniverse.c hellowin32.c)
endif

add_executable(hello ${sources})


どちらも一気に野暮ったくなりましたね*1。ここで、どういう風に書きたいかを考えてみましょう。

project:
  name: hello
  type: executable
source:
  general: [helloworld.c, helloearth.c, hellouniverse.c]
  posix: [helloposix.c]
  darwin: [hellodarwin.c]
  win32: [hellowin32.c]


ソースコードがもっと増えていくことを考えると、次のように書くべきかもしれません。

project:
  name: hello
  type: executable
source:
  general:
    - helloworld.c
    - helloearth.c
    - hellouniverse.c
  posix:
    -  helloposix.c
  darwin:
    -  hellodarwin.c
  win32:
    -  hellowin32.c

これだとすっきりしますね。ソースコードひとつにつきハイフンがいるのが厄介ですが、引用符で括るよりも楽だと思います。


さらにプログラムが大きくなると、他のライブラリを使いたくなるかもしれません。そうすると、include ディレクトリの追加とか、lib ディレクトリを追加する必要が出てきます。

project:
  name: hello
  type: executable
source:
  general:
    - helloworld.c
    - helloearth.c
    - hellouniverse.c
  posix:
    -  helloposix.c
  darwin:
    -  hellodarwin.c
  win32:
    -  hellowin32.c
incdir:
  - contrib/include
libdir:
  - contrib/lib
lib:
  - foo


条件付きコンパイルもやってみましょう。

project:
  name: hello
  type: executable
source:
  general:
    - helloworld.c
    - helloearth.c
    - hellouniverse.c
  posix:
    -  helloposix.c
  darwin:
    -  hellodarwin.c
  win32:
    -  hellowin32.c
  ENABLE_XXX:
    - xxx.c
incdir:
  - contrib/include
libdir:
  - contrib/lib
lib:
  - foo


マルチプラットフォーム向けに開発するときには、リンクするライブラリやコンパイルするソースコードはプラットフォームごとに異なるので、もう少し書き方を工夫してみます。

project:
  name: hello
  type: executable

source:
  - helloworld.c
  - helloearth.c
  - hellouniverse.c
incdir:
  - contrib/include
libdir:
  - contrib/lib
lib:
  - foo

platform_posix:
  source:
    -  helloposix.c

platform_darwin:
  source:
    -  hellodarwin.c

platform_win32:
  source:
    -  hellowin32.c


platform_xxx では、トップレベルでの incdir、libdir、lib が使えるというイメージですね。


これだと、解析も簡単なのでこれから Autotools 用のファイルを作ったり、SCons 用のファイルを作ったり、Visual Studio 用のプロジェクトファイルを作ったり、nmake 用のメイクファイルを作ったりと、夢が広がります。既存のビルドツールからの移行を考えると、ここで挙げたような作りだけでは到底機能が足りませんが、まぁなんとかなるんじゃないかな、と思います。


ここで挙げた YAML「Rubyist Magazine - プログラマーのための YAML 入門 (初級編)」の print-yaml.rb で出力を確認したので多分構文的に間違いはないと思います。

*1:もっといい書き方をご存知の方は是非ご教授を・・・