require 'yaml' require 'fileutils' require 'digest/md5' require 'set' module Rscons class Cache CACHE_FILE = '.rscons' private_class_method :new def initialize @cache = YAML.load(File.read(CACHE_FILE)) rescue {} @lookup_checksums = {} @stored_checksums = {} end def self.open @@cache ||= new end def clear FileUtils.rm_f(CACHE_FILE) initialize end def write File.open(CACHE_FILE, 'w') do |fh| fh.puts(YAML.dump(@cache)) end end def up_to_date?(file, deps = nil) return false unless File.exists?(file) stored_md5 = @cache[:checksums][file] rescue nil return false unless stored_md5 return false unless lookup_checksum(file) == stored_md5 cached_deps = @cache[:deps][file] || [] rescue [] return false if deps and Set.new(cached_deps) != Set.new(deps) cached_deps.map {|d| up_to_date?(d)}.all? end def register_build(target, deps) @cache[:deps] ||= {} @cache[:deps][target] = deps store_checksum(target) deps.each do |dep| store_checksum(dep) end # it is unfortunate to write the file here since many file writes may # occur before the final version, but I don't know where else to do so write end private def lookup_checksum(file) @lookup_checksums[file] ||= calculate_checksum(file) end def store_checksum(file) cs = calculate_checksum(file) if @stored_checksums[file] and @stored_checksums[file] != cs $stderr.puts "Warning: file #{file.inspect} changed after previously used by builder" end @cache[:checksums] ||= {} @cache[:checksums][file] = cs @lookup_checksums[file] = cs end def calculate_checksum(file) Digest::MD5.hexdigest(File.read(file)) end end end