From 160434b9037f94d7701d2130e8caac39092fd31e Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Tue, 15 Apr 2014 14:02:26 -0400 Subject: [PATCH] Cache: change to a Singleton object so it is only loaded from disk once --- lib/rscons.rb | 4 +-- lib/rscons/cache.rb | 44 +++++++++++++++++++-------------- lib/rscons/environment.rb | 3 ++- spec/rscons/cache_spec.rb | 25 ++++++++++++------- spec/rscons/environment_spec.rb | 9 ++++--- spec/rscons_spec.rb | 4 +-- 6 files changed, 54 insertions(+), 35 deletions(-) diff --git a/lib/rscons.rb b/lib/rscons.rb index 93e54dd..81d6135 100644 --- a/lib/rscons.rb +++ b/lib/rscons.rb @@ -23,7 +23,7 @@ module Rscons # Remove all generated files def self.clean - cache = Cache.new + cache = Cache.instance # remove all built files cache.targets.each do |target| FileUtils.rm_f(target) @@ -35,7 +35,7 @@ module Rscons Dir.rmdir(directory) rescue nil end end - Cache.clear + cache.clear end # Return whether the given path is an absolute filesystem path or not diff --git a/lib/rscons/cache.rb b/lib/rscons/cache.rb index c937131..a2de5ed 100644 --- a/lib/rscons/cache.rb +++ b/lib/rscons/cache.rb @@ -2,6 +2,7 @@ require "digest/md5" require "fileutils" require "json" require "set" +require "singleton" require "rscons/version" module Rscons @@ -50,30 +51,25 @@ module Rscons # }, # } class Cache - #### Constants + include Singleton # Name of the file to store cache information in CACHE_FILE = ".rsconscache" - #### Class Methods - - # Remove the cache file - def self.clear - FileUtils.rm_f(CACHE_FILE) - end - - #### Instance Methods - # Create a Cache object and load in the previous contents from the cache # file. def initialize - @cache = JSON.load(File.read(CACHE_FILE)) rescue {} - unless @cache.is_a?(Hash) - $stderr.puts "Warning: #{CACHE_FILE} was corrupt. Contents:\n#{@cache.inspect}" - @cache = {} - end - @cache["targets"] ||= {} - @cache["directories"] ||= {} + initialize! + end + + # Remove the cache file. + def clear + FileUtils.rm_f(CACHE_FILE) + initialize! + end + + # Clear the cached file checksums. + def clear_checksum_cache! @lookup_checksums = {} end @@ -194,9 +190,21 @@ module Rscons @cache["directories"].keys end - # Private Instance Methods private + # Create a Cache object and load in the previous contents from the cache + # file. + def initialize! + @cache = JSON.load(File.read(CACHE_FILE)) rescue {} + unless @cache.is_a?(Hash) + $stderr.puts "Warning: #{CACHE_FILE} was corrupt. Contents:\n#{@cache.inspect}" + @cache = {} + end + @cache["targets"] ||= {} + @cache["directories"] ||= {} + @lookup_checksums = {} + end + # Return a file's checksum, or the previously calculated checksum for # the same file # @param file [String] The file name. diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index a851ba4..4cdebd7 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -172,7 +172,8 @@ module Rscons # called after the block returns. def process clean_target_paths! - cache = Cache.new + cache = Cache.instance + cache.clear_checksum_cache! targets_processed = {} process_target = proc do |target| targets_processed[target] ||= begin diff --git a/spec/rscons/cache_spec.rb b/spec/rscons/cache_spec.rb index a87e674..5b30a8c 100644 --- a/spec/rscons/cache_spec.rb +++ b/spec/rscons/cache_spec.rb @@ -5,14 +5,11 @@ module Rscons end def build_from(cache) - JSON.should_receive(:load).and_return(cache) - Cache.new - end - - describe ".clear" do - it "removes the cache file" do - FileUtils.should_receive(:rm_f).with(Cache::CACHE_FILE) - Cache.clear + JSON.stub(:load) do + cache + end + Cache.instance.tap do |cache| + cache.send(:initialize!) end end @@ -21,11 +18,21 @@ module Rscons it "prints a warning and defaults to an empty hash" do JSON.should_receive(:load).and_return("string") $stderr.should_receive(:puts).with(/Warning:.*was.corrupt/) - Cache.new.instance_variable_get(:@cache).is_a?(Hash).should be_true + c = Cache.instance + c.send(:initialize!) + c.instance_variable_get(:@cache).is_a?(Hash).should be_true end end end + describe "#clear" do + it "removes the cache file" do + FileUtils.should_receive(:rm_f).with(Cache::CACHE_FILE) + JSON.stub(:load) {{}} + Cache.instance.clear + end + end + describe "#write" do it "should fill in 'version' and write to file" do cache = {} diff --git a/spec/rscons/environment_spec.rb b/spec/rscons/environment_spec.rb index 1dc8bc8..09b9347 100644 --- a/spec/rscons/environment_spec.rb +++ b/spec/rscons/environment_spec.rb @@ -140,7 +140,8 @@ module Rscons env.Program("a.out", "main.c") cache = "cache" - Cache.should_receive(:new).and_return(cache) + Cache.should_receive(:instance).and_return(cache) + cache.should_receive(:clear_checksum_cache!) env.should_receive(:run_builder).with(anything, "a.out", ["main.c"], cache, {}).and_return(true) cache.should_receive(:write) @@ -153,7 +154,8 @@ module Rscons env.Object("main.o", "other.cc") cache = "cache" - Cache.should_receive(:new).and_return(cache) + Cache.should_receive(:instance).and_return(cache) + cache.should_receive(:clear_checksum_cache!) env.should_receive(:run_builder).with(anything, "main.o", ["other.cc"], cache, {}).and_return("main.o") env.should_receive(:run_builder).with(anything, "a.out", ["main.o"], cache, {}).and_return("a.out") cache.should_receive(:write) @@ -167,7 +169,8 @@ module Rscons env.Object("main.o", "other.cc") cache = "cache" - Cache.should_receive(:new).and_return(cache) + Cache.should_receive(:instance).and_return(cache) + cache.should_receive(:clear_checksum_cache!) env.should_receive(:run_builder).with(anything, "main.o", ["other.cc"], cache, {}).and_return(false) cache.should_receive(:write) diff --git a/spec/rscons_spec.rb b/spec/rscons_spec.rb index 9fa2bfe..6345094 100644 --- a/spec/rscons_spec.rb +++ b/spec/rscons_spec.rb @@ -2,7 +2,7 @@ describe Rscons do describe ".clean" do it "removes all build targets and created directories" do cache = "cache" - Rscons::Cache.should_receive(:new).and_return(cache) + Rscons::Cache.should_receive(:instance).and_return(cache) cache.should_receive(:targets).and_return(["build/a.out", "build/main.o"]) FileUtils.should_receive(:rm_f).with("build/a.out") FileUtils.should_receive(:rm_f).with("build/main.o") @@ -18,7 +18,7 @@ describe Rscons do Dir.should_receive(:rmdir).with("build") File.should_receive(:directory?).with("other").and_return(true) Dir.should_receive(:entries).with("other").and_return([".", "..", "other.file"]) - Rscons::Cache.should_receive(:clear) + cache.should_receive(:clear) Rscons.clean end