implement Cache to store info about target dependencies and checksums across invocations

This commit is contained in:
Josh Holtrop 2013-07-14 21:51:31 -04:00
parent 9c69a45f77
commit 0bf71ae4d1
4 changed files with 81 additions and 23 deletions

View File

@ -17,19 +17,20 @@ module Rscons
end
def run(target, sources, cache)
unless cache.up_to_date?(target, sources)
vars = {
'TARGET' => target,
'SOURCES' => sources,
'DEPFILE' => target.set_suffix('.mf'),
}
@env.execute("CC #{target}", @env['CCCOM'], vars)
command = @env.build_command(@env['CCCOM'], vars)
unless cache.up_to_date?(target, command, sources)
return false unless @env.execute("CC #{target}", command)
deps = sources
if File.exists?(vars['DEPFILE'])
deps += @env.parse_makefile_deps(vars['DEPFILE'], target)
FileUtils.rm_f(vars['DEPFILE'])
end
cache.register_build(target, deps.uniq)
cache.register_build(target, command, deps.uniq)
end
target
end

View File

@ -24,13 +24,15 @@ module Rscons
builder.run(o_file, [source], cache)
end
end
unless cache.up_to_date?(target, sources)
vars = {
'TARGET' => target,
'SOURCES' => sources,
'LD' => @env['LD'] || @env['CC'], # TODO: figure out whether to use CC or CXX
}
@env.execute("LD #{target}", @env['LDCOM'], vars)
command = @env.build_command(@env['LDCOM'], vars)
unless cache.up_to_date?(target, command, sources)
return false unless @env.execute("LD #{target}", command)
cache.register_build(target, command, sources)
end
target
end

View File

@ -1,8 +1,36 @@
require 'yaml'
require 'fileutils'
require 'digest/md5'
require 'set'
module Rscons
# Example cache:
# {
# 'program' => {
# 'checksum' => 'A1B2C3D4',
# 'command' => ['gcc', '-o', 'program', 'program.o'],
# 'deps' => [
# {
# 'fname' => 'program.o',
# 'checksum' => '87654321',
# }
# ],
# }
# 'program.o' => {
# 'checksum' => '87654321',
# 'command' => ['gcc', '-c', '-o', 'program.o', 'program.c'],
# 'deps' => [
# {
# 'fname' => 'program.c',
# 'checksum' => '456789ABC',
# },
# {
# 'fname' => 'program.h',
# 'checksum' => '7979764643',
# }
# ]
# }
# }
class Cache
# Constants
CACHE_FILE = '.rsconscache'
@ -15,6 +43,7 @@ module Rscons
# Instance Methods
def initialize
@cache = YAML.load(File.read(CACHE_FILE)) rescue {}
@lookup_checksums = {}
end
def write
@ -23,20 +52,43 @@ module Rscons
end
end
def up_to_date?(file, deps = nil)
# TODO
false
def up_to_date?(target, command, deps)
# target file must exist on disk
return false unless File.exists?(target)
# target must be registered in the cache
return false unless @cache.has_key?(target)
# command line used to build target must be identical
return false unless @cache[target][:command] == command
# all dependencies passed in must exist in cache (but cache may have more)
return false unless (Set.new(deps) - Set.new(@cache[target][:deps])).empty?
# all cached dependencies must have their checksums match
@cache[target][:deps].map do |dep_cache|
dep_cache[:checksum] == lookup_checksum(dep_cache[:fname])
end.all?
end
def register_build(target, deps)
# TODO
def register_build(target, command, deps)
@cache[target] = {
command: command,
checksum: calculate_checksum(target),
deps: deps.map do |dep|
{
fname: dep,
checksum: lookup_checksum(dep),
}
end
}
end
# Private Instance Methods
private
def lookup_checksum(file)
@lookup_checksums[file] || calculate_checksum(file)
end
def calculate_checksum(file)
Digest::MD5.hexdigest(File.read(file)).encode(__ENCODING__)
@lookup_checksums[file] = Digest::MD5.hexdigest(File.read(file)).encode(__ENCODING__) rescue ''
end
end
end

View File

@ -74,8 +74,11 @@ module Rscons
cache.write
end
def execute(short_desc, command, extra_vars)
command = @varset.merge(extra_vars).expand_varref(command)
def build_command(command_template, extra_vars)
@varset.merge(extra_vars).expand_varref(command_template)
end
def execute(short_desc, command)
if @varset[:echo] == :command
puts command.map { |c| c =~ /\s/ ? "'#{c}'" : c }.join(' ')
elsif @varset[:echo] == :short