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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from FileTask

#<=>

Instance Attribute Details

#last_manifestObject



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

def last_manifest
  @last_manifest ||= Rake::Pipeline::Manifest.new
end

#manifestObject



36
37
38
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 36

def manifest
  @manifest ||= Rake::Pipeline::Manifest.new
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:



87
88
89
90
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 87

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.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 101

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.



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

def has_dynamic_block?
  !!@dynamic
end

#invoke_dynamic_blockObject

Invoke the task’s dynamic block.



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

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.



118
119
120
121
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
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 118

def invoke_prerequisites(task_args, invocation_chain)
  super

  # If we don't have a dynamic block, just act like a regular FileTask.
  return unless has_dynamic_block?

  # 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))
  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.



150
151
152
153
154
155
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 150

def invoke_with_call_chain(*)
  super
  return unless has_dynamic_block?

  manifest_entry.mtime = mtime_or_now(name)
end

#last_manifest_entryManifestEntry

Returns the manifest entry from the last time this task was run, usually read off the filesystem.

Returns:

  • (ManifestEntry)

    the manifest entry from the last time this task was run, usually read off the filesystem.



42
43
44
# File 'lib/rake-pipeline/dynamic_file_task.rb', line 42

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.



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

def manifest_entry
  manifest[name]
end

#manifest_entry=(new_entry) ⇒ ManifestEntry

Set the current manifest entry,

Parameters:

Returns:



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

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

#needed?Boolean

In addition to the regular FileTask check, A DynamicFileTask is needed if it has no manifest entry from a previous run, or if one of its dynamic dependencies has been modified.

Returns:

  • (Boolean)


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

def needed?
  return true if super

  # 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) > time
  end

  # Otherwise, it's not needed
  false
end