lib/paper_house/build_task.rb
# encoding: utf-8
require 'paper_house/auto_depends'
require 'paper_house/build_failed'
require 'paper_house/cc_options'
require 'paper_house/dependency'
require 'rake/clean'
require 'rake/tasklib'
module PaperHouse
# Common base class for *.c compilation tasks.
class BuildTask < Rake::TaskLib
# Helper class for defining CLEAN and CLOBBER.
class CleanTask
# Add +targets+ to +target_type+ (+:CLEAN+ or +:CLOBBER+).
# @example CleanTask.new %w(file1 file2), :CLEAN
def initialize(targets, target_type)
@targets = targets
@file_list = Object.const_get(target_type)
define_task
end
private
def define_task
@targets.each do |each|
next if @file_list.include?(each)
@file_list.include each
end
@file_list.existing!
end
end
include CcOptions
# Name of task.
attr_accessor :name
# @!attribute target_directory
# Directory where *.o files are created.
attr_writer :target_directory
def target_directory
@target_directory ||= '.'
end
# @!attribute cc
# C compiler name or path.
attr_writer :cc
def cc
ENV['CC'] || @cc || 'gcc'
end
# @private
def initialize(name, &block)
@name = name.to_s
block.call self if block
define
end
# Relative path to target file.
def target_path
File.join target_directory, target_file_name.to_s
end
private
def define
define_main_task
define_directory_task
define_all_c_compile_tasks
define_maybe_generate_target_task
define_clean_tasks
end
def define_main_task
path = target_path
task name => [target_directory, path]
end
def define_directory_task
directory target_directory
end
def define_all_c_compile_tasks
sources_list.zip(objects) do |source, object|
define_c_compile_task source, object
end
end
def define_c_compile_task(source, object)
task object => source do |task|
compile task.name, task.prerequisites[0]
end
end
def define_maybe_generate_target_task
file target_path => objects do |task|
next if uptodate?(task.name, task.prerequisites)
build
end
end
def build
check_sources_list
generate_target
end
def check_sources_list
fail "Cannot find sources (#{@sources})." if sources_list.empty?
end
def define_clean_tasks
CleanTask.new objects, :CLEAN
CleanTask.new clobber_targets, :CLOBBER
end
def clobber_targets
[target_path, dependency.path]
end
def objects
sources_list.pathmap File.join(target_directory, '%n.o')
end
def compile(o_file, c_file)
return if no_need_to_compile?(o_file, c_file)
auto_depends = AutoDepends.new(c_file, o_file, cc, ad_cc_options)
auto_depends.run
dependency.write o_file, auto_depends.data
end
def no_need_to_compile?(o_file, c_file)
uptodate?(o_file, dependency.read(o_file) << c_file)
end
def ad_cc_options
(cflags + ['-fPIC'] + i_options).join ' '
end
def dependency
@dependency ||= Dependency.new(@name)
end
def generate_target
command = ([cc] + cc_options).join(' ')
sh(command) do |ok, status|
ok || fail(BuildFailed.new(command, status))
end
end
end
end