Class: Rscons::Cache

Inherits:
Object
  • Object
show all
Defined in:
lib/rscons/cache.rb

Overview

The Cache class keeps track of file checksums, build target commands and dependencies in a JSON file which persists from one invocation to the next. Example cache:

{
  "version" => "1.2.3",
  "targets" => {
    "program" => {
      "checksum" => "A1B2C3D4",
      "command" => "13543518FE",
      "deps" => [
        {
          "fname" => "program.o",
          "checksum" => "87654321",
        },
      ],
      "user_deps" => [
        {
          "fname" => "lscript.ld",
          "checksum" => "77551133",
        },
      ],
    },
    "program.o" => {
      "checksum" => "87654321",
      "command" => "98765ABCD",
      "deps" => [
        {
          "fname" => "program.c",
          "checksum" => "456789ABC",
        },
        {
          "fname" => "program.h",
          "checksum" => "7979764643",
        },
      ],
      "user_deps" => [],
    }
  },
  "directories" => {
    "build" => true,
    "build/one" => true,
    "build/two" => true,
  },
}

Constant Summary collapse

CACHE_FILE =

Name of the file to store cache information in

".rsconscache"
PHONY_PREFIX =

Prefix for phony cache entries.

":PHONY:"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCache

Create a Cache object and load in the previous contents from the cache file.


69
70
71
# File 'lib/rscons/cache.rb', line 69

def initialize
  initialize!
end

Class Method Details

.instanceObject

Access the singleton instance.


62
63
64
# File 'lib/rscons/cache.rb', line 62

def instance
  @instance ||= Cache.new
end

Instance Method Details

#clearvoid

This method returns an undefined value.

Remove the cache file.


76
77
78
79
# File 'lib/rscons/cache.rb', line 76

def clear
  FileUtils.rm_f(CACHE_FILE)
  initialize!
end

#clear_checksum_cache!void

This method returns an undefined value.

Clear the cached file checksums.


84
85
86
# File 'lib/rscons/cache.rb', line 84

def clear_checksum_cache!
  @lookup_checksums = {}
end

#directoriesArray<String>

Return a list of directories which were created as a part of the build.


242
243
244
# File 'lib/rscons/cache.rb', line 242

def directories
  @cache["directories"].keys
end

#mkdir_p(path) ⇒ void

This method returns an undefined value.

Make any needed directories and record the ones that are created for removal upon a “clean” operation.


225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/rscons/cache.rb', line 225

def mkdir_p(path)
  parts = path.split(/[\\\/]/)
  parts.each_index do |i|
    next if parts[i] == ""
    subpath = File.join(*parts[0, i + 1])
    unless File.exists?(subpath)
      FileUtils.mkdir(subpath)
      @cache["directories"][subpath] = true
      @dirty = true
    end
  end
end

#register_build(targets, command, deps, env) ⇒ void

This method returns an undefined value.

Store cache information about target(s) built by a builder.


189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/rscons/cache.rb', line 189

def register_build(targets, command, deps, env)
  Array(targets).each do |target|
    target_checksum = Rscons.phony_target?(target) ? "" : calculate_checksum(target)
    @cache["targets"][get_cache_key(target)] = {
      "command" => Digest::MD5.hexdigest(command.inspect),
      "checksum" => target_checksum,
      "deps" => deps.map do |dep|
        {
          "fname" => dep,
          "checksum" => lookup_checksum(dep),
        }
      end,
      "user_deps" => (env.get_user_deps(target) || []).map do |dep|
        {
          "fname" => dep,
          "checksum" => lookup_checksum(dep),
        }
      end,
    }
    @dirty = true
  end
end

#targetsArray<String>

Return a list of targets that have been built.


215
216
217
# File 'lib/rscons/cache.rb', line 215

def targets
  @cache["targets"].keys
end

#up_to_date?(targets, command, deps, env, options = {}) ⇒ Boolean

Check if target(s) are up to date.

Options Hash (options):

  • :strict_deps (Boolean)

    Only consider a target up to date if its list of dependencies is exactly equal (including order) to the cached list of dependencies


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/rscons/cache.rb', line 130

def up_to_date?(targets, command, deps, env, options = {})
  Array(targets).each do |target|
    cache_key = get_cache_key(target)

    unless Rscons.phony_target?(target)
      # target file must exist on disk
      return false unless File.exists?(target)
    end

    # target must be registered in the cache
    return false unless @cache["targets"].has_key?(cache_key)

    unless Rscons.phony_target?(target)
      # target must have the same checksum as when it was built last
      return false unless @cache["targets"][cache_key]["checksum"] == lookup_checksum(target)
    end

    # command used to build target must be identical
    return false unless @cache["targets"][cache_key]["command"] == Digest::MD5.hexdigest(command.inspect)

    cached_deps = @cache["targets"][cache_key]["deps"] || []
    cached_deps_fnames = cached_deps.map { |dc| dc["fname"] }
    if options[:strict_deps]
      # depedencies passed in must exactly equal those in the cache
      return false unless deps == cached_deps_fnames
    else
      # all dependencies passed in must exist in cache (but cache may have more)
      return false unless (Set.new(deps) - Set.new(cached_deps_fnames)).empty?
    end

    # set of user dependencies must match
    user_deps = env.get_user_deps(target) || []
    cached_user_deps = @cache["targets"][cache_key]["user_deps"] || []
    cached_user_deps_fnames = cached_user_deps.map { |dc| dc["fname"] }
    return false unless user_deps == cached_user_deps_fnames

    # all cached dependencies must have their checksums match
    (cached_deps + cached_user_deps).each do |dep_cache|
      return false unless dep_cache["checksum"] == lookup_checksum(dep_cache["fname"])
    end
  end

  true
end

#writevoid

This method returns an undefined value.

Write the cache to disk to be loaded next time.


91
92
93
94
95
96
97
98
99
# File 'lib/rscons/cache.rb', line 91

def write
  if @dirty || (@cache["version"] != VERSION)
    @cache["version"] = VERSION
    File.open(CACHE_FILE, "w") do |fh|
      fh.puts(JSON.dump(@cache))
    end
  end
  @dirty = false
end