From 3cba4cbc19aa3a8782a27928a61050a6518cf1a0 Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Sat, 19 Aug 2023 15:49:16 -0700 Subject: [PATCH] dependency_helpers: rework recursive dependency resolution This is a refactor/reworking of the dependency resolution methods in the DependencyHelpers module. These methods are used by both the `brew deps` and `brew uses` commands to get a specific set of dependencies for the user based on multiple criteria. Additive Options: --include-build --include-test --include-optional Subtractive Options: --skip-recommended --missing When a user runs either command the only dependencies that are included by default are recommended and runtime dependencies. This is largely unchanged though we don't include all non-build dependencies as recommended by default anymore. The biggest change is that all installed dependencies are always removed from the list now if the --missing option is passed. This could get skipped before depending on the other options that were passed. Essentially subtractive options now will always be evaluated before additive ones (the docs will need to be updated to make this clear). Beyond that we have no special handling for the optional command anymore. We used to check that the optional dependency was not needed to build the formula but that seems redundant and confusing. Essentially, the #recursive_includes command now behaves much more like the #reject_ignores command (essentially the non-recursive version) which is a good thing for consistency's sake. --- Library/Homebrew/dependencies_helpers.rb | 48 ++++++++---------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/Library/Homebrew/dependencies_helpers.rb b/Library/Homebrew/dependencies_helpers.rb index dd2a3acd2e..390ccf65d9 100644 --- a/Library/Homebrew/dependencies_helpers.rb +++ b/Library/Homebrew/dependencies_helpers.rb @@ -8,29 +8,14 @@ require "cask_dependent" # @api private module DependenciesHelpers def args_includes_ignores(args) - includes = [] + includes = [:recommended?] # included by default + includes << :build? if args.include_build? + includes << :test? if args.include_test? + includes << :optional? if args.include_optional? + 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? - ignores << "satisfied?" if args.missing? + ignores << :recommended? if args.skip_recommended? + ignores << :satisfied? if args.missing? [includes, ignores] end @@ -41,17 +26,14 @@ module DependenciesHelpers cache_key = "recursive_includes_#{includes}_#{ignores}" klass.expand(root_dependent, cache_key: cache_key) do |dependent, dep| - if dep.recommended? - klass.prune if ignores.include?("recommended?") || dependent.build.without?(dep) - elsif dep.optional? - klass.prune if includes.exclude?("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 - elsif dep.satisfied? - klass.prune if ignores.include?("satisfied?") + klass.prune if ignores.any? { |ignore| dep.send(ignore) } + + # NOTE: Untagged dependencies are runtime dependencies and are included by default. + klass.prune if !dep.tags.empty? && includes.none? do |include| + # Ignore indirect test dependencies + next if include == :test? && dependent != root_dependent + + dep.send(include) end # If a tap isn't installed, we can't find the dependencies of one of