88 lines
2.9 KiB
Ruby
88 lines
2.9 KiB
Ruby
module Rscons
|
|
# Class to keep track of a set of builders to be executed.
|
|
class BuilderSet < Hash
|
|
|
|
# Create a BuilderSet.
|
|
#
|
|
# @param build_dependencies [Hash]
|
|
# Hash mapping targets to a set of build dependencies. A builder will not
|
|
# be returned as ready to run if any of its dependencies are still
|
|
# building.
|
|
# @param side_effects [Set]
|
|
# Set of side-effect files. A builder will not be returned as ready to
|
|
# run if any of its dependencies is a side-effect of another target that
|
|
# has not yet been built.
|
|
def initialize(build_dependencies, side_effects)
|
|
super()
|
|
@build_dependencies = build_dependencies
|
|
@side_effects = side_effects
|
|
end
|
|
|
|
# Add a builder to the BuilderSet.
|
|
#
|
|
# @param builder [Builder]
|
|
# The {Builder} that will produce the target.
|
|
def <<(builder)
|
|
# We allow multiple builders to be registered per target for cases like:
|
|
# env.Directory("dest")
|
|
# env.Install("dest", "bin")
|
|
# env.Install("dest", "share")
|
|
self[builder.target] ||= []
|
|
self[builder.target] << builder
|
|
end
|
|
|
|
# Return the number of remaining build steps.
|
|
#
|
|
# @return [Integer]
|
|
# The number of remaining build steps.
|
|
def build_steps_remaining
|
|
self.reduce(0) do |result, (target, builders)|
|
|
result + builders.select {|b| not b.nop?}.size
|
|
end
|
|
end
|
|
|
|
# Get the next builder that is ready to run from the BuilderSet.
|
|
#
|
|
# This method will remove the builder from the BuilderSet.
|
|
#
|
|
# @param targets_still_building [Array<String>]
|
|
# Targets that are not finished building. This is used to avoid returning
|
|
# a builder as available to run if it depends on one of the targets that
|
|
# are still building as a source.
|
|
#
|
|
# @return [nil, Builder]
|
|
# The next builder to run.
|
|
def get_next_builder_to_run(targets_still_building)
|
|
to_build = self.find do |target, builders|
|
|
deps = builders.first.sources + (@build_dependencies[target] || []).to_a
|
|
# All dependencies must have been built for this target to be ready to
|
|
# build.
|
|
deps.all? do |dep|
|
|
!(targets_still_building.include?(dep) ||
|
|
self.include?(dep) ||
|
|
@side_effects.include?(dep))
|
|
end
|
|
end
|
|
|
|
if to_build
|
|
target, builders = *to_build
|
|
builder = builders.first
|
|
if builders.size > 1
|
|
builders.slice!(0)
|
|
else
|
|
self.delete(target)
|
|
end
|
|
return builder
|
|
end
|
|
|
|
# If there is a builder to run, and nothing is still building, but we did
|
|
# not find a builder to run above, then there might be a circular
|
|
# dependency introduced by the user.
|
|
if (self.size > 0) and targets_still_building.empty?
|
|
raise "Could not find a runnable builder. Possible circular dependency for #{self.keys.first}"
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|