Rakefileでg++コンパイルするとき、ヘッダファイルの依存関係をインポートしてみる

Rakefileは便利なのでg++コンパイルMakefileの代替になればと思い、いろいろ模索中です。
ひとまず、簡単なメイクは作れるようになってきたところです。
で、問題が1つ浮上。
これまでMakefileでヘッダファイルの依存関係を定義(-MM)していましたが、それをRakefileでどう記述すればよいかわかりません。
ネットでいろいろ漁ってみたのですが、なかなか解が見つからず。。
自力でなんとかするしかないか。。。

まずは自力で。。

とりあえず、なんとなくうまくいったっぽいRakefileをメモします。
あまり、Rakefileのドキュメント見ずに書いたのでちょっぴり不安ですが。。

CXX    = "g++"
CFLAGS = "-O2 -Wall -I./"
LIBS   = "-lm"

TARGET = "run.x"

srcs = FileList["*.cpp"]
objs = srcs.ext('o')

task :default => :all
task :all => TARGET

#clean
require 'rake/clean'
CLEAN.include objs
CLOBBER.include TARGET
task :clean_all => :clobber


file TARGET => objs do |t|
  sh "#{CXX} -o #{t.name} #{t.prerequisites.join(' ')} #{LIBS}"
end

rule '.o' => ['%n.cpp', "%n.d"] do |t|
  sh "#{CXX} #{CFLAGS} -c #{t.source}"
end

# generate dependency information
srcs.each do |src|
  dep = src.sub(/\.(c|cpp)$/,".d")
  file dep => src do |t|
    tmp = `#{CXX} -MM #{CFLAGS} #{src}`
    obj,arg = tmp.gsub(/\\\n /,"").split(":")
    str = "file " + obj.inspect + " => [" + arg.split(' ').map{|m| m.inspect }.join(",") + "]"
    File.open(t.name,"w") {|f| f.write str}
  end
  import dep
  CLEAN.include dep
end


肝となるのは、以下の記述です。

srcs.each do |src|
  ・・・
end

処理の流れは、

  • "g++ -MM"で依存関係データを取得
  • それを、"file obj => [srcs]"の形式に置換
  • その文字列を、".d"ファイルに書き込む
  • 最後に"import"で".d"をインポートする

そして、".o"を生成するruleに"%n.d"を追加。

eachを使って処理できるのがrubyっぽいかも。
あっているのかなぁ、自信なし。
それと、もうちょっと綺麗に書ければなぁ。

rake/loaders/makefieを使う

よく調べたら、-MMで吐き出されたファイルをそのまま使用できることがわかりました。
ただし、拡張子は".mf"にする必要がありそうです。
改変後は以下のとおりです。

CXX    = "g++"
CFLAGS = "-O2 -Wall -I./"
LIBS   = "-lm"

TARGET = "run.x"

srcs = FileList["*.cpp"]
objs = srcs.ext('o')

task :default => :all
task :all => TARGET

#clean
require 'rake/clean'
CLEAN.include objs
CLOBBER.include TARGET
task :clean_all => :clobber


file TARGET => objs do |t|
  sh "#{CXX} -o #{t.name} #{t.prerequisites.join(' ')} #{LIBS}"
end

rule '.o' => ['%n.cpp'] do |t|
  sh "#{CXX} #{CFLAGS} -c #{t.source}"
end

# generate dependency information
require 'rake/loaders/makefile'
srcs.each do |src|
  dep = src+".mf"
  file dep => src do |t|
    sh "#{CXX} -MM #{CFLAGS} #{src} > #{t.name}"
  end
  import dep
  CLEAN.include dep
end

無駄な正規表現使わずにすっきり書くことができました。
最初、".d"っていうファイル名で依存関係のファイルを作っていたのですが、なぜかsyntaxエラーに。そこで、".mf"に変えたところ、うまく通りました。
".mf"はMakefileの記述だよ、って教えてあげているみたいですね。