Rakefileで快適コンパイル生活
コンパイルしたいときはたった四文字打つだけ。
$ rake
Rubyをよく使うのでC言語のコンパイル処理をRakefileに書いた。
require 'rake/clean' CLEAN.include("**/*.o") CLOBBER.include("*.out") CC = "gcc" GFLAGS = "-g -Wall" OPT = "-O3" INCDIR = "lib" SRCS = FileList["**/*.c"] OBJS = SRCS.ext('.o') PROGRAMS_DIR = FileList.new("**/*.c").exclude("lib/*").ext('.out') PROGRAMS = PROGRAMS_DIR.map{|p| File.basename(p)} DATA_DIR = "data" directory DATA_DIR task default: PROGRAMS desc "初期化" task init: [DATA_DIR, :reset] desc "中間ファイルを削除してからビルド" task refresh: [:clean, :default] desc "実行ファイルを削除してからビルド" task reset: [:clobber, :default] desc "依存ファイルのビルド" task dist: OBJS def dep_obj program headers = [program.ext('.o')] open(program.ext('.c')){ |f| headers.push "/#{$1.ext('.o')}" if $_ =~ /#include\s+"(.+)"/ while f.gets } OBJS.select{|obj| obj =~ Regexp.union(headers) } end PROGRAMS_DIR.each do |program| desc "#{File.basename(program)}をビルド" file File.basename(program) => dep_obj(program) do |t| sh "#{CC} #{OPT} #{GFLAGS} -o #{t.name} #{t.prerequisites.join(' ')}" end end rule '.o' => '.c' do |t| sh "#{CC} #{OPT} #{GFLAGS} -c #{t.source} -o #{t.name} -I#{INCDIR}" end
固定タスクは init
と refresh
と reset
で文字通りのことしてる。rake/cleanを require することでcleanとclobberというタスクが追加される。
CLEAN.include("**/*.o") CLOBBER.include("*.out")
このようにして削除したいファイルをincludeしておくとタスクを実行したときに消してくれる。いちおうcleanは中間ファイル、clobberをclean + 実行ファイルを消すためのものらしいけど、違いはいまいち分からない。
PROGRAMS_DIR = FileList.new("**/*.c").exclude("lib/*").ext('.out') PROGRAMS = PROGRAMS_DIR.map{|p| File.basename(p)}
defaultタスクは配下のすべてのプログラムをコンパイルしている。libを除いたフォルダの中のC言語をそれぞれ実行ファイル形式(.out)にコンパイルしている。FileListでCファイルのPATHを求めて実行ファイル名を作成している。ext()はFileListの関数で拡張子を任意の文字列に変換できる。
DATA_DIR = "data" directory DATA_DIR ...ruby task init: [DATA_DIR, :reset]
directoryはFileUtilsの関数だけど[1]タスクのように指定することでディレクトリを作成してくれる。
PROGRAMS_DIR.each do |program| desc "#{File.basename(program)}をビルド" file File.basename(program) => dep_obj(program) do |t| sh "#{CC} #{OPT} #{GFLAGS} -o #{t.name} #{t.prerequisites.join(' ')}" end end
動的に実行ファイルのコンパイルタスクを生成している。
def dep_obj program headers = [program.ext('.o')] open(program.ext('.c')){ |f| headers.push "/#{$1.ext('.o')}" if $_ =~ /#include\s+"(.+)"/ while f.gets } OBJS.select{|obj| obj =~ Regexp.union(headers) } end
Cファイルを開きincludeされているものを読んでいる。 if $ =~ /#include\s+"(.+)"/
は$が暗黙の変数なので if /#include\s+"(.+)"/
でもOK。でもwarningが出るので極力やめよう。
rule '.o' => '.c' do |t| sh "#{CC} #{OPT} #{GFLAGS} -c #{t.source} -o #{t.name} -I#{INCDIR}" end
rakeのruleでOファイルが同名のCファイルから作成できることを書いた。