Check version conflicts using linkage.

Instead of refusing to install software preemptively by assuming
multiple linkage to differing versions of the same library we now make
`brew linkage --test` verify that we don't have two versions of the
same library linked at the same time.

This will be considerably more permissive whilst checking the actual
problem that we're worried about.
This commit is contained in:
Mike McQuaid 2018-08-27 10:12:02 +01:00
parent 31ab0bb391
commit ca6c75d229
No known key found for this signature in database
GPG Key ID: 48A898132FD8EE70
2 changed files with 22 additions and 27 deletions

View File

@ -155,8 +155,6 @@ class FormulaInstaller
recursive_deps = formula.recursive_dependencies recursive_deps = formula.recursive_dependencies
recursive_formulae = recursive_deps.map(&:to_formula) recursive_formulae = recursive_deps.map(&:to_formula)
recursive_runtime_formulae =
formula.runtime_formula_dependencies(undeclared: false)
recursive_dependencies = [] recursive_dependencies = []
recursive_formulae.each do |dep| recursive_formulae.each do |dep|
@ -182,25 +180,6 @@ class FormulaInstaller
EOS EOS
end end
version_hash = {}
version_conflicts = Set.new
recursive_runtime_formulae.each do |f|
name = f.name
unversioned_name, = name.split("@")
next if unversioned_name == "python"
version_hash[unversioned_name] ||= Set.new
version_hash[unversioned_name] << name
next if version_hash[unversioned_name].length < 2
version_conflicts += version_hash[unversioned_name]
end
unless version_conflicts.empty?
raise CannotInstallFormulaError, <<~EOS
#{formula.full_name} contains conflicting version recursive dependencies:
#{version_conflicts.to_a.join ", "}
View these with `brew deps --tree #{formula.full_name}`.
EOS
end
pinned_unsatisfied_deps = recursive_deps.select do |dep| pinned_unsatisfied_deps = recursive_deps.select do |dep|
dep.to_formula.pinned? && !dep.satisfied?(inherited_options_for(dep)) dep.to_formula.pinned? && !dep.satisfied?(inherited_options_for(dep))
end end

View File

@ -19,6 +19,7 @@ class LinkageChecker
@indirect_deps = [] @indirect_deps = []
@undeclared_deps = [] @undeclared_deps = []
@unnecessary_deps = [] @unnecessary_deps = []
@version_conflict_deps = []
check_dylibs(rebuild_cache: rebuild_cache) check_dylibs(rebuild_cache: rebuild_cache)
end end
@ -50,11 +51,14 @@ class LinkageChecker
def display_test_output(puts_output: true) def display_test_output(puts_output: true)
display_items "Missing libraries", @broken_dylibs, puts_output: puts_output display_items "Missing libraries", @broken_dylibs, puts_output: puts_output
display_items "Broken dependencies", @broken_deps, puts_output: puts_output display_items "Broken dependencies", @broken_deps, puts_output: puts_output
display_items "Conflicting libraries", @version_conflict_deps, puts_output: puts_output
puts "No broken library linkage" unless broken_library_linkage? puts "No broken library linkage" unless broken_library_linkage?
end end
def broken_library_linkage? def broken_library_linkage?
!@broken_dylibs.empty? || !@broken_deps.empty? !@broken_dylibs.empty? ||
!@broken_deps.empty? ||
!@version_conflict_deps.empty?
end end
private private
@ -127,8 +131,8 @@ class LinkageChecker
end end
if formula if formula
@indirect_deps, @undeclared_deps, @unnecessary_deps = @indirect_deps, @undeclared_deps, @unnecessary_deps,
check_undeclared_deps @version_conflict_deps = check_formula_deps
end end
return unless keg_files_dylibs_was_empty return unless keg_files_dylibs_was_empty
@ -136,7 +140,7 @@ class LinkageChecker
store&.update!(keg_files_dylibs: keg_files_dylibs) store&.update!(keg_files_dylibs: keg_files_dylibs)
end end
def check_undeclared_deps def check_formula_deps
filter_out = proc do |dep| filter_out = proc do |dep|
next true if dep.build? next true if dep.build?
next false unless dep.optional? || dep.recommended? next false unless dep.optional? || dep.recommended?
@ -170,13 +174,25 @@ class LinkageChecker
unnecessary_deps = declared_deps_full_names.reject do |full_name| unnecessary_deps = declared_deps_full_names.reject do |full_name|
next true if Formula[full_name].bin.directory? next true if Formula[full_name].bin.directory?
name = full_name.split("/").last name = full_name.split("/").last
@brewed_dylibs.keys.map { |x| x.split("/").last }.include?(name) @brewed_dylibs.keys.map { |l| l.split("/").last }.include?(name)
end end
missing_deps = @broken_deps.values.flatten.map { |d| dylib_to_dep(d) } missing_deps = @broken_deps.values.flatten.map { |d| dylib_to_dep(d) }
unnecessary_deps -= missing_deps unnecessary_deps -= missing_deps
[indirect_deps, undeclared_deps, unnecessary_deps] version_hash = {}
version_conflict_deps = Set.new
@brewed_dylibs.keys.each do |l|
name = l.split("/").last
unversioned_name, = name.split("@")
version_hash[unversioned_name] ||= Set.new
version_hash[unversioned_name] << name
next if version_hash[unversioned_name].length < 2
version_conflict_deps += version_hash[unversioned_name]
end
[indirect_deps, undeclared_deps,
unnecessary_deps, version_conflict_deps.to_a]
end end
def sort_by_formula_full_name!(arr) def sort_by_formula_full_name!(arr)