From 0bf71ae4d1d68cd6a14450faa59c437f4e772450 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Sun, 14 Jul 2013 21:51:31 -0400 Subject: [PATCH] implement Cache to store info about target dependencies and checksums across invocations --- lib/rscons/builders/cc.rb | 17 ++++----- lib/rscons/builders/program.rb | 16 +++++---- lib/rscons/cache.rb | 64 ++++++++++++++++++++++++++++++---- lib/rscons/environment.rb | 7 ++-- 4 files changed, 81 insertions(+), 23 deletions(-) diff --git a/lib/rscons/builders/cc.rb b/lib/rscons/builders/cc.rb index fa0f7c0..8a4d15d 100644 --- a/lib/rscons/builders/cc.rb +++ b/lib/rscons/builders/cc.rb @@ -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) + vars = { + 'TARGET' => target, + 'SOURCES' => sources, + 'DEPFILE' => target.set_suffix('.mf'), + } + 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 diff --git a/lib/rscons/builders/program.rb b/lib/rscons/builders/program.rb index 00a7248..897d820 100644 --- a/lib/rscons/builders/program.rb +++ b/lib/rscons/builders/program.rb @@ -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) + vars = { + 'TARGET' => target, + 'SOURCES' => sources, + 'LD' => @env['LD'] || @env['CC'], # TODO: figure out whether to use CC or CXX + } + 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 diff --git a/lib/rscons/cache.rb b/lib/rscons/cache.rb index 9ef50db..9cca116 100644 --- a/lib/rscons/cache.rb +++ b/lib/rscons/cache.rb @@ -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 diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index 8a62374..5a3ecea 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -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