Refactor Formula/Cask dependencies.
This commit is contained in:
parent
96271aaa89
commit
6a1fa87191
@ -71,6 +71,15 @@ module Hbc
|
||||
@token
|
||||
end
|
||||
|
||||
def hash
|
||||
token.hash
|
||||
end
|
||||
|
||||
def eql?(other)
|
||||
token == other.token
|
||||
end
|
||||
alias == eql?
|
||||
|
||||
def dumpcask
|
||||
odebug "Cask instance dumps in YAML:"
|
||||
odebug "Cask instance toplevel:", to_yaml
|
||||
|
||||
@ -1,35 +1,36 @@
|
||||
require "delegate"
|
||||
|
||||
require "hbc/topological_hash"
|
||||
|
||||
module Hbc
|
||||
class CaskDependencies
|
||||
attr_reader :cask, :graph, :sorted
|
||||
class CaskDependencies < DelegateClass(Array)
|
||||
attr_reader :cask, :graph
|
||||
|
||||
def initialize(cask)
|
||||
@cask = cask
|
||||
@graph = graph_dependencies
|
||||
@sorted = sort
|
||||
super(sort)
|
||||
end
|
||||
|
||||
def graph_dependencies
|
||||
deps_in = ->(csk) { csk.depends_on ? csk.depends_on.cask || [] : [] }
|
||||
walk = lambda do |acc, deps|
|
||||
deps.each do |dep|
|
||||
next if acc.key?(dep)
|
||||
succs = deps_in.call CaskLoader.load(dep)
|
||||
acc[dep] = succs
|
||||
walk.call(acc, succs)
|
||||
end
|
||||
acc
|
||||
end
|
||||
private
|
||||
|
||||
graphed = walk.call({}, @cask.depends_on.cask)
|
||||
TopologicalHash[graphed]
|
||||
def graph_dependencies(cask = self.cask, acc = TopologicalHash.new)
|
||||
return acc if acc.key?(cask)
|
||||
deps = cask.depends_on.cask.map(&CaskLoader.public_method(:load))
|
||||
acc[cask] = deps
|
||||
deps.each do |dep|
|
||||
graph_dependencies(dep, acc)
|
||||
end
|
||||
acc
|
||||
end
|
||||
|
||||
def sort
|
||||
@graph.tsort
|
||||
raise CaskSelfReferencingDependencyError, cask.token if graph[cask].include?(cask)
|
||||
graph.tsort - [cask]
|
||||
rescue TSort::Cyclic
|
||||
raise CaskCyclicCaskDependencyError, @cask.token
|
||||
strongly_connected_components = graph.strongly_connected_components.sort_by(&:count)
|
||||
cyclic_dependencies = strongly_connected_components.last - [cask]
|
||||
raise CaskCyclicDependencyError.new(cask.token, cyclic_dependencies.join(", "))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -211,10 +211,10 @@ module Hbc
|
||||
|
||||
# depends_on uses a load method so that multiple stanzas can be merged
|
||||
def depends_on(*args)
|
||||
return @depends_on if args.empty?
|
||||
@depends_on ||= DSL::DependsOn.new
|
||||
return @depends_on if args.empty?
|
||||
begin
|
||||
@depends_on.load(*args) unless args.empty?
|
||||
@depends_on.load(*args)
|
||||
rescue RuntimeError => e
|
||||
raise CaskInvalidError.new(token, e)
|
||||
end
|
||||
|
||||
@ -24,6 +24,8 @@ module Hbc
|
||||
|
||||
def initialize
|
||||
@pairs ||= {}
|
||||
@cask ||= []
|
||||
@formula ||= []
|
||||
end
|
||||
|
||||
def load(pairs = {})
|
||||
@ -53,12 +55,10 @@ module Hbc
|
||||
end
|
||||
|
||||
def formula=(*args)
|
||||
@formula ||= []
|
||||
@formula.concat(args)
|
||||
end
|
||||
|
||||
def cask=(*args)
|
||||
@cask ||= []
|
||||
@cask.concat(args)
|
||||
end
|
||||
|
||||
|
||||
@ -77,9 +77,15 @@ module Hbc
|
||||
end
|
||||
end
|
||||
|
||||
class CaskCyclicCaskDependencyError < AbstractCaskErrorWithToken
|
||||
class CaskCyclicDependencyError < AbstractCaskErrorWithToken
|
||||
def to_s
|
||||
"Cask '#{token}' includes cyclic dependencies on other Casks and could not be installed."
|
||||
"Cask '#{token}' includes cyclic dependencies on other Casks" << (reason.empty? ? "." : ": #{reason}")
|
||||
end
|
||||
end
|
||||
|
||||
class CaskSelfReferencingDependencyError < CaskCyclicDependencyError
|
||||
def to_s
|
||||
"Cask '#{token}' depends on itself."
|
||||
end
|
||||
end
|
||||
|
||||
@ -91,7 +97,7 @@ module Hbc
|
||||
|
||||
class CaskInvalidError < AbstractCaskErrorWithToken
|
||||
def to_s
|
||||
"Cask '#{token}' definition is invalid" << (reason.empty? ? ".": ": #{reason}")
|
||||
"Cask '#{token}' definition is invalid" << (reason.empty? ? "." : ": #{reason}")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
require "rubygems"
|
||||
|
||||
require "formula_installer"
|
||||
|
||||
require "hbc/cask_dependencies"
|
||||
require "hbc/staged"
|
||||
require "hbc/verify"
|
||||
@ -197,7 +199,6 @@ module Hbc
|
||||
x11_dependencies
|
||||
formula_dependencies
|
||||
cask_dependencies unless skip_cask_deps?
|
||||
puts "complete"
|
||||
end
|
||||
|
||||
def macos_dependencies
|
||||
@ -234,36 +235,50 @@ module Hbc
|
||||
end
|
||||
|
||||
def formula_dependencies
|
||||
return unless @cask.depends_on.formula && !@cask.depends_on.formula.empty?
|
||||
ohai "Installing Formula dependencies from Homebrew"
|
||||
@cask.depends_on.formula.each do |dep_name|
|
||||
print "#{dep_name} ... "
|
||||
installed = @command.run(HOMEBREW_BREW_FILE,
|
||||
args: ["list", "--versions", dep_name],
|
||||
print_stderr: false).stdout.include?(dep_name)
|
||||
if installed
|
||||
puts "already installed"
|
||||
else
|
||||
@command.run!(HOMEBREW_BREW_FILE,
|
||||
args: ["install", dep_name])
|
||||
puts "done"
|
||||
formulae = @cask.depends_on.formula.map { |f| Formula[f] }
|
||||
return if formulae.empty?
|
||||
|
||||
if formulae.all?(&:any_version_installed?)
|
||||
puts "All Formula dependencies satisfied."
|
||||
return
|
||||
end
|
||||
|
||||
not_installed = formulae.reject(&:any_version_installed?)
|
||||
|
||||
ohai "Installing Formula dependencies: #{not_installed.map(&:to_s).join(", ")}"
|
||||
not_installed.each do |formula|
|
||||
begin
|
||||
old_argv = ARGV.dup
|
||||
ARGV.replace([])
|
||||
FormulaInstaller.new(formula).tap do |fi|
|
||||
fi.installed_as_dependency = true
|
||||
fi.installed_on_request = false
|
||||
fi.show_header = true
|
||||
fi.verbose = verbose?
|
||||
fi.prelude
|
||||
fi.install
|
||||
fi.finish
|
||||
end
|
||||
ensure
|
||||
ARGV.replace(old_argv)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cask_dependencies
|
||||
return unless @cask.depends_on.cask && !@cask.depends_on.cask.empty?
|
||||
ohai "Installing Cask dependencies: #{@cask.depends_on.cask.join(", ")}"
|
||||
deps = CaskDependencies.new(@cask)
|
||||
deps.sorted.each do |dep_token|
|
||||
puts "#{dep_token} ..."
|
||||
dep = CaskLoader.load(dep_token)
|
||||
if dep.installed?
|
||||
puts "already installed"
|
||||
else
|
||||
Installer.new(dep, binaries: binaries?, verbose: verbose?, skip_cask_deps: true, force: false).install
|
||||
puts "done"
|
||||
end
|
||||
return if @cask.depends_on.cask.empty?
|
||||
casks = CaskDependencies.new(@cask)
|
||||
|
||||
if casks.all?(&:installed?)
|
||||
puts "All Cask dependencies satisfied."
|
||||
return
|
||||
end
|
||||
|
||||
not_installed = casks.reject(&:installed?)
|
||||
|
||||
ohai "Installing Cask dependencies: #{not_installed.map(&:to_s).join(", ")}"
|
||||
not_installed.each do |cask|
|
||||
Installer.new(cask, binaries: binaries?, verbose: verbose?, skip_cask_deps: true, force: false).install
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ describe "Satisfy Dependencies and Requirements", :cask do
|
||||
describe "depends_on cask" do
|
||||
context "when depends_on cask is cyclic" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-cask-cyclic.rb") }
|
||||
it { is_expected.to raise_error(Hbc::CaskCyclicCaskDependencyError) }
|
||||
it { is_expected.to raise_error(Hbc::CaskCyclicDependencyError) }
|
||||
end
|
||||
|
||||
context do
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user