diff --git a/Library/Homebrew/cmd/deps.rb b/Library/Homebrew/cmd/deps.rb index 2d7863255f..4cb93befde 100644 --- a/Library/Homebrew/cmd/deps.rb +++ b/Library/Homebrew/cmd/deps.rb @@ -4,7 +4,7 @@ require "formula" require "ostruct" require "cli/parser" require "cask/caskroom" -require "cask_dependent" +require "dependencies_helpers" module Homebrew extend DependenciesHelpers @@ -110,16 +110,6 @@ module Homebrew puts all_deps end - def dependents(formulae_or_casks) - formulae_or_casks.map do |formula_or_cask| - if formula_or_cask.is_a?(Formula) - formula_or_cask - else - CaskDependent.new(formula_or_cask) - end - end - end - def sorted_dependents(formulae_or_casks) dependents(formulae_or_casks).sort_by(&:name) end diff --git a/Library/Homebrew/cmd/uses.rb b/Library/Homebrew/cmd/uses.rb index ba71e0972d..91e58e9dbd 100644 --- a/Library/Homebrew/cmd/uses.rb +++ b/Library/Homebrew/cmd/uses.rb @@ -6,6 +6,8 @@ require "formula" require "cli/parser" +require "cask/caskroom" +require "dependencies_helpers" module Homebrew extend DependenciesHelpers @@ -68,39 +70,22 @@ module Homebrew !args.include_optional? && !args.skip_recommended? + recursive = args.recursive? + includes, ignores = args_includes_ignores(args) + uses = if use_runtime_dependents && !used_formulae_missing used_formulae.map(&:runtime_installed_formula_dependents) .reduce(&:&) - .select(&:any_version_installed?) + .select(&:any_version_installed?) + + select_used_dependents(dependents(Cask::Caskroom.casks), used_formulae, recursive, includes, ignores) else - formulae = args.installed? ? Formula.installed : Formula - recursive = args.recursive? - includes, ignores = args_includes_ignores(args) - - formulae.select do |f| - deps = if recursive - recursive_includes(Dependency, f, includes, ignores) - else - reject_ignores(f.deps, ignores, includes) - end - - used_formulae.all? do |ff| - deps.any? do |dep| - match = begin - dep.to_formula.full_name == ff.full_name if dep.name.include?("/") - rescue - nil - end - next match unless match.nil? - - dep.name == ff.name - end - rescue FormulaUnavailableError - # Silently ignore this case as we don't care about things used in - # taps that aren't currently tapped. - next - end + deps = if args.installed? + dependents(Formula.installed + Cask::Caskroom.casks) + else + dependents(Formula.to_a + Cask::Cask.to_a) end + + select_used_dependents(deps, used_formulae, recursive, includes, ignores) end return if uses.empty? @@ -108,4 +93,31 @@ module Homebrew puts Formatter.columns(uses.map(&:full_name).sort) odie "Missing formulae should not have dependents!" if used_formulae_missing end + + def select_used_dependents(dependents, used_formulae, recursive, includes, ignores) + dependents.select do |d| + deps = if recursive + recursive_includes(Dependency, d, includes, ignores) + else + reject_ignores(d.deps, ignores, includes) + end + + used_formulae.all? do |ff| + deps.any? do |dep| + match = begin + dep.to_formula.full_name == ff.full_name if dep.name.include?("/") + rescue + nil + end + next match unless match.nil? + + dep.name == ff.name + end + rescue FormulaUnavailableError + # Silently ignore this case as we don't care about things used in + # taps that aren't currently tapped. + next + end + end + end end diff --git a/Library/Homebrew/dependencies.rb b/Library/Homebrew/dependencies.rb index 1ece9e5340..476d1b53ec 100644 --- a/Library/Homebrew/dependencies.rb +++ b/Library/Homebrew/dependencies.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "delegate" +require "cask_dependent" class Dependencies < DelegateClass(Array) def initialize(*args) @@ -55,71 +56,3 @@ class Requirements < DelegateClass(Set) "#<#{self.class.name}: {#{to_a.join(", ")}}>" end end - -module DependenciesHelpers - def args_includes_ignores(args) - includes = [] - ignores = [] - - if args.include_build? - includes << "build?" - else - ignores << "build?" - end - - if args.include_test? - includes << "test?" - else - ignores << "test?" - end - - if args.include_optional? - includes << "optional?" - else - ignores << "optional?" - end - - ignores << "recommended?" if args.skip_recommended? - - [includes, ignores] - end - - def recursive_includes(klass, root_dependent, includes, ignores) - type = if klass == Dependency - :dependencies - elsif klass == Requirement - :requirements - else - raise ArgumentError, "Invalid class argument: #{klass}" - end - - root_dependent.send("recursive_#{type}") do |dependent, dep| - if dep.recommended? - klass.prune if ignores.include?("recommended?") || dependent.build.without?(dep) - elsif dep.optional? - klass.prune if !includes.include?("optional?") && !dependent.build.with?(dep) - elsif dep.build? || dep.test? - keep = false - keep ||= dep.test? && includes.include?("test?") && dependent == root_dependent - keep ||= dep.build? && includes.include?("build?") - klass.prune unless keep - end - - # If a tap isn't installed, we can't find the dependencies of one of - # its formulae, and an exception will be thrown if we try. - if type == :dependencies && - dep.is_a?(TapDependency) && - !dep.tap.installed? - Dependency.keep_but_prune_recursive_deps - end - end - end - - def reject_ignores(dependables, ignores, includes) - dependables.reject do |dep| - next false unless ignores.any? { |ignore| dep.send(ignore) } - - includes.none? { |include| dep.send(include) } - end - end -end diff --git a/Library/Homebrew/dependencies_helpers.rb b/Library/Homebrew/dependencies_helpers.rb new file mode 100644 index 0000000000..fb6f13e9d1 --- /dev/null +++ b/Library/Homebrew/dependencies_helpers.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require "cask_dependent" + +module DependenciesHelpers + def args_includes_ignores(args) + includes = [] + ignores = [] + + if args.include_build? + includes << "build?" + else + ignores << "build?" + end + + if args.include_test? + includes << "test?" + else + ignores << "test?" + end + + if args.include_optional? + includes << "optional?" + else + ignores << "optional?" + end + + ignores << "recommended?" if args.skip_recommended? + + [includes, ignores] + end + + def recursive_includes(klass, root_dependent, includes, ignores) + type = if klass == Dependency + :dependencies + elsif klass == Requirement + :requirements + else + raise ArgumentError, "Invalid class argument: #{klass}" + end + + root_dependent.send("recursive_#{type}") do |dependent, dep| + if dep.recommended? + klass.prune if ignores.include?("recommended?") || dependent.build.without?(dep) + elsif dep.optional? + klass.prune if !includes.include?("optional?") && !dependent.build.with?(dep) + elsif dep.build? || dep.test? + keep = false + keep ||= dep.test? && includes.include?("test?") && dependent == root_dependent + keep ||= dep.build? && includes.include?("build?") + klass.prune unless keep + end + + # If a tap isn't installed, we can't find the dependencies of one of + # its formulae, and an exception will be thrown if we try. + if type == :dependencies && + dep.is_a?(TapDependency) && + !dep.tap.installed? + Dependency.keep_but_prune_recursive_deps + end + end + end + + def reject_ignores(dependables, ignores, includes) + dependables.reject do |dep| + next false unless ignores.any? { |ignore| dep.send(ignore) } + + includes.none? { |include| dep.send(include) } + end + end + + def dependents(formulae_or_casks) + formulae_or_casks.map do |formula_or_cask| + if formula_or_cask.is_a?(Formula) + formula_or_cask + else + CaskDependent.new(formula_or_cask) + end + end + end + module_function :dependents +end diff --git a/Library/Homebrew/test/dependencies_helpers_spec.rb b/Library/Homebrew/test/dependencies_helpers_spec.rb new file mode 100644 index 0000000000..4686084bce --- /dev/null +++ b/Library/Homebrew/test/dependencies_helpers_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "dependencies_helpers" + +describe DependenciesHelpers do + specify "#dependents" do + foo = formula "foo" do + url "foo" + version "1.0" + end + + foo_cask = Cask::CaskLoader.load(+<<-RUBY) + cask "foo_cask" do + end + RUBY + + bar = formula "bar" do + url "bar-url" + version "1.0" + end + + bar_cask = Cask::CaskLoader.load(+<<-RUBY) + cask "bar-cask" do + end + RUBY + + methods = [ + :name, + :full_name, + :runtime_dependencies, + :deps, + :requirements, + :recursive_dependencies, + :recursive_requirements, + :any_version_installed?, + ] + + dependents = described_class.dependents([foo, foo_cask, bar, bar_cask]) + + dependents.each do |dependent| + methods.each do |method| + expect(dependent.respond_to?(method)) + .to be true + end + end + end +end