Class: Nanoc3::DependencyTracker

Inherits:
Object
  • Object
show all
Defined in:
lib/nanoc3/base/dependency_tracker.rb

Overview

Responsible for remembering dependencies between items. It is used to speed up compilation by only letting an item be recompiled when it is outdated or any of its dependencies (or dependencies’ dependencies, etc) is outdated.

The dependencies tracked by the dependency tracker are not dependencies based on an item’s content. When one item uses an attribute of another item, then this is also treated as a dependency. While dependencies based on an item’s content (handled in Compiler) cannot be mutually recursive, the more general dependencies in Nanoc3::DependencyTracker can (e.g. item A can use an attribute of item B and vice versa without problems).

The dependency tracker remembers the dependency information between runs. Dependency information is stored in the tmp/dependencies file. This file also contains a version number; when a dependencies file with an incompatible version is found, it is ignored.

Constant Summary

STORE_VERSION =

The version of the file format used to store dependencies.

2

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (DependencyTracker) initialize(items)

Creates a new dependency tracker for the given items.

should be managed

Parameters:



41
42
43
44
45
46
# File 'lib/nanoc3/base/dependency_tracker.rb', line 41

def initialize(items)
  @items          = items
  @filename       = 'tmp/dependencies'
  @graph          = Nanoc3::DirectedGraph.new([ nil ] + @items)
  @previous_items = []
end

Instance Attribute Details

- (String) filename

stored

Returns:

  • (String)

    The name of the file in which dependency information is



28
29
30
# File 'lib/nanoc3/base/dependency_tracker.rb', line 28

def filename
  @filename
end

- (Array<Nanoc3::Item>) items (readonly)

by the dependency tracker

Returns:



32
33
34
# File 'lib/nanoc3/base/dependency_tracker.rb', line 32

def items
  @items
end

Instance Method Details

- (Array<Nanoc3::Item>) direct_predecessors_of(item)

Returns the direct dependencies for item.

The direct dependencies of item include the items that, when outdated will cause item to be marked as outdated. Indirect dependencies will not be returned (e.g. if A depends on B which depends on C, then the direct dependencies of A do not include C).

predecessors

Parameters:

  • item (Nanoc3::Item)

    The item for which to fetch the direct

Returns:



95
96
97
# File 'lib/nanoc3/base/dependency_tracker.rb', line 95

def direct_predecessors_of(item)
  @graph.direct_predecessors_of(item).compact
end

- (Array<Nanoc3::Item>) direct_successors_of(item)

Returns the direct inverse dependencies for item.

The direct inverse dependencies of item include the items that will be marked as outdated when+item is outdated. Indirect dependencies will not be returned (e.g. if A depends on B which depends on C, then the direct inverse dependencies of C do not include A).

successors

Parameters:

  • item (Nanoc3::Item)

    The item for which to fetch the direct

Returns:



123
124
125
# File 'lib/nanoc3/base/dependency_tracker.rb', line 123

def direct_successors_of(item)
  @graph.direct_successors_of(item).compact
end

- (void) forget_dependencies_for(item)

This method returns an undefined value.

Empties the list of dependencies for the given item. This is necessary before recompiling the given item, because otherwise old dependencies will stick around and new dependencies will appear twice. This function removes all incoming edges for the given vertex.

Parameters:

  • item (Nanoc3::Item)

    The item for which to forget all dependencies



243
244
245
# File 'lib/nanoc3/base/dependency_tracker.rb', line 243

def forget_dependencies_for(item)
  @graph.delete_edges_to(item)
end

- (void) load_graph

This method returns an undefined value.

Loads the dependency graph from the file specified by the #filename attribute. This method will overwrite an existing dependency graph.



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/nanoc3/base/dependency_tracker.rb', line 174

def load_graph
  # Create new graph
  @graph = Nanoc3::DirectedGraph.new([ nil ] + @items)

  # Get store
  return if !File.file?(self.filename)
  store = PStore.new(self.filename)

  # Load dependencies
  store.transaction do
    # Verify version
    return if store[:version] != STORE_VERSION

    # Load vertices
    @previous_items = store[:vertices].map do |v|
      @items.find { |i| i.identifier == v }
    end

    # Load edges
    store[:edges].each do |edge|
      from_index, to_index = *edge
      from, to = @previous_items[from_index], @previous_items[to_index]
      @graph.add_edge(from, to)
    end
  end
end

- (Object) mark_outdated_items

Deprecated.


264
265
266
# File 'lib/nanoc3/base/dependency_tracker.rb', line 264

def mark_outdated_items
  propagate_outdatedness
end

- (Array<Nanoc3::Item>) predecessors_of(item)

Returns all dependencies (direct and indirect) for item.

The dependencies of item include the items that, when outdated, will cause item to be marked as outdated.

indirect predecessors

Parameters:

  • item (Nanoc3::Item)

    The item for which to fetch all direct and

Returns:



108
109
110
# File 'lib/nanoc3/base/dependency_tracker.rb', line 108

def predecessors_of(item)
  @graph.predecessors_of(item).compact
end

This method returns an undefined value.

Prints the dependency graph in human-readable form.



250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/nanoc3/base/dependency_tracker.rb', line 250

def print_graph
  @items.each do |item|
    puts "#{item.inspect} depends on:"

    predecessors = direct_predecessors_of(item)
    predecessors.each do |pred|
      puts "    #{pred.inspect}"
    end
    puts "    (nothing!)" if predecessors.empty?
    puts
  end
end

- (void) propagate_outdatedness

This method returns an undefined value.

Traverses the dependency graph and marks all items that (directly or indirectly) depend on an outdated item as outdated.



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/nanoc3/base/dependency_tracker.rb', line 205

def propagate_outdatedness
  # Unmark everything
  @items.each { |i| i.outdated_due_to_dependencies = false }

  # Mark new items as outdated
  added_items = @items - @previous_items
  added_items.each { |i| i.outdated_due_to_dependencies = true }

  # Mark successors of nil as outdated
  self.successors_of(nil).each do |i|
    i.outdated_due_to_dependencies = true
  end

  # Mark successors of outdated items as outdated
  require 'set'
  unprocessed = @items.select { |i| i.outdated? }
  seen        = Set.new(unprocessed)
  until unprocessed.empty?
    item = unprocessed.shift

    self.direct_successors_of(item).each do |successor|
      next if seen.include?(successor)
      seen << successor

      successor.outdated_due_to_dependencies = true
      unprocessed << successor
    end
  end
end

- (void) record_dependency(src, dst)

This method returns an undefined value.

Records a dependency from src to dst in the dependency graph. When dst is oudated, src will also become outdated.

that will become outdated if dst is outdated

item that will cause the source to become outdated if the destination is outdated

Parameters:

  • src (Nanoc3::Item)

    The source of the dependency, i.e. the item

  • dst (Nanoc3::Item)

    The destination of the dependency, i.e. the



151
152
153
154
# File 'lib/nanoc3/base/dependency_tracker.rb', line 151

def record_dependency(src, dst)
  # Warning! dst and src are *reversed* here!
  @graph.add_edge(dst, src) unless src == dst
end

- (void) start

This method returns an undefined value.

Starts listening for dependency messages (:visit_started and :visit_ended) and start recording dependencies.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/nanoc3/base/dependency_tracker.rb', line 52

def start
  # Initialize dependency stack. An item will be pushed onto this stack
  # when it is visited. Therefore, an item on the stack always depends on
  # all items pushed above it.
  @stack = []

  # Register start of visits
  Nanoc3::NotificationCenter.on(:visit_started, self) do |item|
    # Record possible dependency
    unless @stack.empty?
      $stderr.puts "*** Recording dependency on #{item.inspect}" if $DEBUG
      self.record_dependency(@stack[-1], item)
    end

    @stack.push(item)
  end

  # Register end of visits
  Nanoc3::NotificationCenter.on(:visit_ended, self) do |item|
    @stack.pop
  end
end

- (void) stop

This method returns an undefined value.

Stop listening for dependency messages and stop recording dependencies.



78
79
80
81
82
# File 'lib/nanoc3/base/dependency_tracker.rb', line 78

def stop
  # Unregister
  Nanoc3::NotificationCenter.remove(:visit_started, self)
  Nanoc3::NotificationCenter.remove(:visit_ended,   self)
end

- (void) store_graph

This method returns an undefined value.

Stores the dependency graph into the file specified by the #filename attribute.



160
161
162
163
164
165
166
167
168
# File 'lib/nanoc3/base/dependency_tracker.rb', line 160

def store_graph
  FileUtils.mkdir_p(File.dirname(self.filename))
  store = PStore.new(self.filename)
  store.transaction do
    store[:version]  = STORE_VERSION
    store[:vertices] = @graph.vertices.map { |i| i && i.identifier }
    store[:edges]    = @graph.edges
  end
end

- (Array<Nanoc3::Item>) successors_of(item)

Returns all inverse dependencies (direct and indirect) for item.

The inverse dependencies of item include the items that will be marked as outdated when item is outdated.

indirect successors

Parameters:

  • item (Nanoc3::Item)

    The item for which to fetch all direct and

Returns:



136
137
138
# File 'lib/nanoc3/base/dependency_tracker.rb', line 136

def successors_of(item)
  @graph.successors_of(item).compact
end