lib/teapot/dependency.rb in teapot-1.2.6 vs lib/teapot/dependency.rb in teapot-1.3.0
- old
+ new
@@ -1,6 +1,6 @@
-# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
+# Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
@@ -16,226 +16,10 @@
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-require 'set'
+require 'build/dependency'
module Teapot
- module Dependency
- class UnresolvedDependencyError < StandardError
- def initialize(chain)
- super "Unresolved dependency chain!"
-
- @chain = chain
- end
-
- attr :chain
- end
-
- Provision = Struct.new(:value)
- Alias = Struct.new(:dependencies)
- Resolution = Struct.new(:provider, :name)
-
- def priority= value
- @priority = value
- end
-
- def priority
- @priority || 0
- end
-
- def provides?(name)
- provisions.key? name
- end
-
- def provides(name_or_aliases, &block)
- if String === name_or_aliases || Symbol === name_or_aliases
- name = name_or_aliases
-
- provisions[name] = Provision.new(block)
- else
- aliases = name_or_aliases
-
- aliases.each do |name, dependencies|
- provisions[name] = Alias.new(Array(dependencies))
- end
- end
- end
-
- def provisions
- @provisions ||= {}
- end
-
- def depends(name)
- dependencies << name
- end
-
- def depends?(name)
- dependencies.include? name
- end
-
- def dependencies
- @dependencies ||= Set.new
- end
-
- class Chain
- def initialize(selection, dependencies, providers, options = {})
- # Explicitly selected targets which will be used when resolving ambiguity:
- @selection = Set.new(selection)
-
- # The list of dependencies that needs to be satisfied:
- @dependencies = dependencies
-
- # The available providers which match up to required dependencies:
- @providers = providers
-
- @resolved = Set.new
- @ordered = []
- @provisions = []
- @unresolved = []
- @conflicts = {}
-
- @options = options
-
- @dependencies.each do |dependency|
- expand(dependency, "<top>")
- end
- end
-
- attr :selection
- attr :dependencies
- attr :providers
-
- attr :resolved
- attr :ordered
- attr :provisions
- attr :unresolved
- attr :conflicts
-
- private
-
- def ignore_priority?
- @options[:ignore_priority]
- end
-
- def filter_by_priority(viable_providers)
- # Sort from highest priority to lowest priority:
- viable_providers = viable_providers.sort{|a,b| b.priority <=> a.priority}
-
- # The first item has the highest priority:
- highest_priority = viable_providers.first.priority
-
- # We compute all providers with the same highest priority (may be zero):
- return viable_providers.take_while{|provider| provider.priority == highest_priority}
- end
-
- def filter_by_selection(viable_providers)
- return viable_providers.select{|provider| @selection.include? provider.name}
- end
-
- def find_provider(dependency, parent)
- # Mostly, only one package will satisfy the dependency...
- viable_providers = @providers.select{|provider| provider.provides? dependency}
-
- # puts "** Found #{viable_providers.collect(&:name).join(', ')} viable providers.".color(:magenta)
-
- if viable_providers.size > 1
- # ... however in some cases (typically where aliases are being used) an explicit selection must be made for the build to work correctly.
- explicit_providers = filter_by_selection(viable_providers)
-
- # puts "** Filtering to #{explicit_providers.collect(&:name).join(', ')} explicit providers.".color(:magenta)
-
- if explicit_providers.size != 1 and !ignore_priority?
- # If we were unable to select a single package, we may use the priority to limit the number of possible options:
- explicit_providers = viable_providers if explicit_providers.empty?
-
- explicit_providers = filter_by_priority(explicit_providers)
- end
-
- if explicit_providers.size == 0
- # No provider was explicitly specified, thus we require explicit conflict resolution:
- @conflicts[dependency] = viable_providers
- return nil
- elsif explicit_providers.size == 1
- # The best outcome, a specific provider was named:
- return explicit_providers.first
- else
- # Multiple providers were explicitly mentioned that satisfy the dependency.
- @conflicts[dependency] = explicit_providers
- return nil
- end
- else
- return viable_providers.first
- end
- end
-
- def expand(dependency, parent)
- # puts "** Expanding #{dependency} from #{parent}".color(:magenta)
-
- if @resolved.include? dependency
- # puts "** Already resolved dependency!".color(:magenta)
-
- return
- end
-
- provider = find_provider(dependency, parent)
-
- if provider == nil
- # puts "** Couldn't find provider -> unresolved".color(:magenta)
- @unresolved << [dependency, parent]
- return nil
- end
-
- provision = provider.provisions[dependency]
-
- # We will now satisfy this dependency by satisfying any dependent dependencies, but we no longer need to revisit this one.
- @resolved << dependency
-
- # If the provision was an Alias, make sure to resolve the alias first:
- if Alias === provision
- # puts "** Resolving alias #{provision}".color(:magenta)
-
- provision.dependencies.each do |dependency|
- expand(dependency, provider)
- end
- end
-
- unless @resolved.include?(provider)
- # We are now satisfying the provider by expanding all its own dependencies:
- @resolved << provider
-
- # Make sure we satisfy the provider's dependencies first:
- provider.dependencies.each do |dependency|
- expand(dependency, provider)
- end
-
- # puts "** Appending #{dependency} -> ordered".color(:magenta)
-
- # Add the provider to the ordered list.
- @ordered << Resolution.new(provider, dependency)
- end
-
- # This goes here because we want to ensure 1/ that if
- unless provision == nil or Alias === provision
- # puts "** Appending #{dependency} -> provisions".color(:magenta)
-
- # Add the provision to the set of required provisions.
- @provisions << provision
- end
-
- # For both @ordered and @provisions, we ensure that for [...xs..., x, ...], x is satisfied by ...xs....
- end
- end
-
- # An `UnresolvedDependencyError` will be thrown if there are any unresolved dependencies.
- def self.chain(selection, dependencies, providers)
- chain = Chain.new(selection, dependencies, providers)
-
- if chain.unresolved.size > 0
- raise UnresolvedDependencyError.new(chain)
- end
-
- return chain
- end
- end
+ Dependency = ::Build::Dependency
end