Class: Rake::Pipeline::DynamicFileTask

Inherits:
FileTask
  • Object
show all
Defined in:
lib/rake-pipeline/dynamic_file_task.rb

Overview

This class extends Rake's FileTask class to add support for dynamic dependencies. Typically, Rake handles static dependencies, where a file's dependencies are known before the task is invoked. A DynamicFileTask also supports dynamic dependencies, meaning the file's dependencies can be determined just before invoking the task. Because calculating a file's dependencies at runtime may be an expensive operation (it could involve reading the file from disk and parsing it to extract dependency information, for example), the results of this calculation are stored on disk in a manifest file, and reused on subsequent runs if possible.

For example, consider this file app.c:

#include "app.h"
some_stuff();

If we have a task that compiles app.c into app.o, it needs to process app.c to look for additional dependencies specified by the file itself.

Defined Under Namespace

Classes: ManifestRequired

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from FileTask

#<=>

Instance Attribute Details

#last_manifestObject

Returns the value of attribute last_manifest


29
30
31
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 29

def last_manifest
  @last_manifest
end

#manifestObject

Returns the value of attribute manifest


29
30
31
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 29

def manifest
  @manifest
end

Instance Method Details

#dynamic(&block) ⇒ DynamicFileTask

Add a block that will return dynamic dependencies. This block can assume that all static dependencies are up to date.

Returns:


91
92
93
94
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 91

def dynamic(&block)
  @dynamic = block
  self
end

#dynamic_prerequisitesArray[String]

At runtime, we will call this to get dynamic prerequisites.

Returns:

  • (Array[String])

    an array of paths to the task's dynamic dependencies.


105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 105

def dynamic_prerequisites
  @dynamic_prerequisites ||= begin
    dynamics = if has_dynamic_block?
      dynamic_prerequisites_from_manifest || invoke_dynamic_block
    else
      []
    end

    # Make sure we don't dynamically depend on ourselves, as
    # that will create a circular reference, and that makes
    # everybody sad.
    dynamics.reject { |x| x == name }
  end
end

#has_dynamic_block?Boolean

Returns true if the task has a block to invoke for dynamic dependencies, false otherwise.

Returns:

  • (Boolean)

    true if the task has a block to invoke for dynamic dependencies, false otherwise.


33
34
35
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 33

def has_dynamic_block?
  !!@dynamic
end

#invoke(*args) ⇒ Object

Invoke this task. This method only checks to see if there is a manifest then delegates to super

Raises:


58
59
60
61
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 58

def invoke(*args)
  raise ManifestRequired if has_dynamic_block? && !manifest
  super
end

#invoke_dynamic_blockObject

Invoke the task's dynamic block.


97
98
99
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 97

def invoke_dynamic_block
  @dynamic.call(self)
end

#invoke_prerequisites(task_args, invocation_chain) ⇒ Object

Override rake's invoke_prerequisites method to invoke static prerequisites and then any dynamic prerequisites.

Raises:


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 122

def invoke_prerequisites(task_args, invocation_chain)
  super

  raise ManifestRequired if has_dynamic_block? && !manifest

  # Retrieve the dynamic prerequisites. If all goes well,
  # we will not have to invoke the dynamic block to do this.
  dynamics = dynamic_prerequisites

  # invoke dynamic prerequisites just as we would invoke
  # static prerequisites.
  dynamics.each do |prereq|
    task = lookup_prerequisite(prereq)
    prereq_args = task_args.new_scope(task.arg_names)
    task.invoke_with_call_chain(prereq_args, invocation_chain)
  end

  # Create a new manifest entry for each dynamic dependency.
  # When the pipeline finishes, these manifest entries will be written
  # to the file system.
  entry = Rake::Pipeline::ManifestEntry.new

  dynamics.each do |dynamic|
    entry.deps.merge!(dynamic => mtime_or_now(dynamic).to_i)
  end

  self.manifest_entry = entry
end

#invoke_with_call_chainObject

After invoking a task, add the mtime of the task's output to its current manifest entry.


153
154
155
156
157
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 153

def invoke_with_call_chain(*)
  super

  manifest_entry.mtime = mtime_or_now(name).to_i
end

#last_manifest_entryObject


52
53
54
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 52

def last_manifest_entry
  last_manifest[name]
end

#manifest_entryManifestEntry

Returns the manifest entry from the current manifest. This is the entry that will be written to disk after the task runs.

Returns:

  • (ManifestEntry)

    the manifest entry from the current manifest. This is the entry that will be written to disk after the task runs.


40
41
42
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 40

def manifest_entry
  manifest[name]
end

#manifest_entry=(new_entry) ⇒ ManifestEntry

Set the current manifest entry,

Parameters:

Returns:


48
49
50
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 48

def manifest_entry=(new_entry)
  manifest[name] = new_entry
end

#needed?Boolean

In addition to the regular FileTask check, a DynamicFileTask should be invoked when any of it's prerequisites are required, there is no manifest or it's dependencies are out of date.

Returns:

  • (Boolean)

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 68

def needed?
  return true if super

  return true if prerequisites_needed?

  # if we have no manifest, this file task is needed
  return true unless last_manifest_entry

  # If any of this task's dynamic dependencies have changed,
  # this file task is needed
  last_manifest_entry.deps.each do |dep, time|
    return true if File.mtime(dep).to_i > time
  end

  # Otherwise, it's not needed
  false
end