Build::GraphSourceBuildGraphTask

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 walker Walker

the walker driving the graph traversal.

parameter node Node

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