class Task
Represents a single unit of work within a build graph walk.
Definitions
def initialize(walker, node)
Create a new task associated with the given walker and node.
Signature
-
parameter
walkerWalker the walker driving the graph traversal.
-
parameter
nodeNode the node this task is responsible for updating.
Implementation
def initialize(walker, node)
@walker = walker
@walker.tasks[node] = self
@node = node
@fiber = nil
@error = nil
# Tasks that must be complete before finishing this task.
@children = []
@state = nil
@inputs_failed = false
end
attr :state
The state of the task, one of nil, :complete or :failed.
attr :error
The error, if the execution of the node fails.
attr :inputs_failed
A list of any inputs whose relevant tasks failed:
def visit
Derived task can override this function to provide appropriate behaviour.
Implementation
def visit
update_inputs_and_outputs
# Inforn the walker a new task is being generated for this node:
@walker.enter(self)
if @fiber
raise RuntimeError, "Task is already running!"
end
@fiber = Fiber.new do
# If all inputs were good, we can update the node.
if wait_for_inputs?
begin
yield
rescue TransientError => error
fail!(error)
end
else
fail!(InputsFailed)
end
wait_for_children!
update_outputs!
@state ||= :complete
@walker.exit(self)
@fiber = nil
end
# Schedule the work, hopefully synchronously:
@fiber.resume
# This allows the child task to be passed back to the parent when it is first invoked.
return self
end
def invoke(node)
Implementation
def invoke(node)
child_task = @walker.call(node, self)
raise ArgumentError.new("Invalid child task") unless child_task
@children << child_task
return child_task
end
def failed?
Signature
-
returns
Boolean whether the task has failed.
Implementation
def failed?
@state == :failed
end
def complete?
Signature
-
returns
Boolean whether the task has completed successfully.
Implementation
def complete?
@state == :complete
end
def dirty?
Returns true if the outputs of the task are out of date w.r.t. the inputs. Currently, does not take into account if the input is a glob and files have been added.
Implementation
def dirty?
if @outputs
@outputs.dirty?(@inputs)
else
true
end
end
def changed!
Resets the node in the walker if inputs or outputs have changed since the last run.
Implementation
def changed!
@walker.delete(@node) if (@inputs.update! or @outputs.update!)
end
def directories
Signature
-
returns
Array(String) the list of root directories for all input and output paths.
Implementation
def directories
(@inputs.roots + @outputs.roots).collect{|path| path.to_s}
end
def to_s
Signature
-
returns
String a short human-readable summary of the task.
Implementation
def to_s
"#<#{self.class} #{node_string} #{state_string}>"
end
def inspect
Signature
-
returns
String a detailed human-readable representation including object identity.
Implementation
def inspect
"\#<#{self.class}:0x#{self.object_id.to_s(16)} #{node_string} #{state_string}>"
end
def update_inputs_and_outputs
If the node inputs is a glob, this part of the process converts the glob into an actual list of files. If we are not inheriting outputs from children tasks, update our outputs now.
Implementation
def update_inputs_and_outputs
@inputs = Files::State.new(@node.inputs)
unless @node.inherit_outputs?
@outputs = Files::State.new(@node.outputs)
end
end
def children_outputs
Implementation
def children_outputs
@children.collect(&:outputs).inject(Files::Paths::NONE, &:+)
end
def update_outputs!
If the node's outputs were a glob, this checks the filesystem to figure out what files were actually generated. If it inherits the outputs of the child tasks, merge them into our own outputs.
Implementation
def update_outputs!
if @node.inherit_outputs?
@outputs = Files::State.new(self.children_outputs)
else
# After the task has finished, we update the output states:
@outputs.update!
end
end
def fail!(error)
Fail the task with the given error. Any task which is waiting on this task will also fail (eventually).
Implementation
def fail!(error)
Console.error(self, "Task failed!", exception: error)
@error = error
@state = :failed
end
def wait_for_inputs?
Implementation
def wait_for_inputs?
# Wait on any inputs, returns whether any inputs failed:
if @inputs&.any?
unless @walker.wait_on_paths(self, @inputs)
return false
end
end
return true
end
def wait_for_children?
Implementation
def wait_for_children?
if @children&.any?
unless @walker.wait_for_children(self, @children)
return false
end
end
return true
end