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