読者です 読者をやめる 読者になる 読者になる

書き捨てコードのmake(rake)

Ruby

ちょっと試したいコードを書くときに毎回コンパイラを呼ぶのは面倒。Makefileを書くのも面倒。
てなわけでRakeの習作。ソースコードにフラグを書き込んでおけば、それを利用してコンパイルしてくれる。

ソース

#======================================================================
# -*- ruby -*-
#======================================================================

require 'rake/clean'


#==================================================
# Compiler Options
#==================================================
# warning flags
C_AND_CPP_DEFAULT_FLAGS = [
  "-Wall -Wextra -Wformat=2 -Wstrict-aliasing=2",
  "-Wcast-qual -Wcast-align -Wwrite-strings -Wconversion",
  "-Wfloat-equal -Wpointer-arith -Wswitch-enum"
].join(' ')

CC       = "gcc"
CPP      = "g++"
CFLAGS   = "#{C_AND_CPP_DEFAULT_FLAGS}"
INCLUDES = ""
LIBS     = ""
CPPFLAGS = "#{C_AND_CPP_DEFAULT_FLAGS} -Woverloaded-virtual -Weffc++"
LDFLAGS  = ""

RAKE_OPTIONAL_VARIABLES = [
  :CC, :CPP, :CFLAGS, :INCLUDES, :LIBS, :CPPFLAGS, :LDFLAGS,
]


#==================================================
# Files
#==================================================
SRCS = FileList["*.c"] + FileList["*.cpp"]
OBJS = SRCS.ext('')

CLEAN.include(OBJS)


#==================================================
# Utils
#==================================================
def get_compiler_option(line)
  re_base = "\\s*:\\s*(.*)$"
  RAKE_OPTIONAL_VARIABLES.each do |option|
    return [option, $~.captures[0].strip ] if line =~ /#{option.to_s}#{re_base}/i
  end
  nil
end

def search_compiler_options(src_file)
  options = Hash.new
  File.open(src_file, 'r') do |file|
    file.each_line do |line|
      option = get_compiler_option(line)
      options[option[0]] = option[1] if option != nil
    end
  end
  options
end

def define_cpp_make_rule(src_file)
  object_file = src_file.sub(/\.cpp/, '')
  options = search_compiler_options(src_file)
  cpp      =                (options[:CPP]      ? options[:CPP]      : CPP)
  cppflags = "#{CPPFLAGS} #{(options[:CPPFLAGS] ? options[:CPPFLAGS] : '' )}"
  includes = "#{INCLUDES} #{(options[:INCLUDES] ? options[:INCLUDES] : '' )}"
  ldflags  = "#{LDFLAGS}  #{(options[:LDFLAGS]  ? options[:LDFLAGS]  : '' )}"
  libs     = "#{LIBS}     #{(options[:LIBS]     ? options[:LIBS]     : '' )}"
  file object_file => src_file do |t|
    sh "#{cpp} #{cppflags} #{includes} #{ldflags} -o #{t.name} #{t.prerequisites[0]} #{libs}"
  end
end

def define_c_make_rule(src_file)
  object_file = src_file.sub(/\.c/, '')
  options = search_compiler_options(src_file)
  cc       =                (options[:CC]       ? options[:CC]       : CC)
  cflags   = "#{CFLAGS}   #{(options[:CFLAGS]   ? options[:CFLAGS]   : '')}"
  includes = "#{INCLUDES} #{(options[:INCLUDES] ? options[:INCLUDES] : '')}"
  ldflags  = "#{LDFLAGS}  #{(options[:LDFLAGS]  ? options[:LDFLAGS]  : '')}"
  libs     = "#{LIBS}     #{(options[:LIBS]     ? options[:LIBS]     : '')}"
  file object_file => src_file do |t|
    sh "#{cc} #{cflags} #{includes} #{ldflags} -o #{t.name} #{t.prerequisites[0]} #{libs}"
  end
end


#==================================================
# Tasks
#==================================================
task :default => [:all]

task :all => [:make_all_rule] + OBJS
task :make_all_rule do
  SRCS.each do |src_file|
    case src_file
    when /\.cpp$/
      define_cpp_make_rule(src_file)
    when /\.c$/
      define_c_make_rule(src_file)
    else
      puts "no rule #{src_file}"
    end
  end
end

デフォルトのフラグはbinary hacksを参考にした。

使い方

こんなファイルを用意して

  • test.c
#if 0
ファイルのどこかにCFLAGS、LIBS、INCLUDES等を記録しておく
LIBS : -lm
#endif
#include <stdio.h>
#include <math.h>
int main(void)
{
  printf("%lf\n", sqrt(2.0));
  return 0;
}

rakeを実行

> rake
(in /your/working/directory)
gcc -Wall -Wextra -Wformat=2 -Wstrict-aliasing=2 -Wcast-qual -Wcast-align -Wwrite-strings -Wconversion\
 -Wfloat-equal -Wpointer-arith -Wswitch-enum   -o test test.c -lm