| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  | # typed: true | 
					
						
							|  |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | require "cli/parser" | 
					
						
							|  |  |  | require "formula" | 
					
						
							| 
									
										
										
										
											2021-08-06 02:30:44 -04:00
										 |  |  | require "api" | 
					
						
							| 
									
										
										
										
											2023-03-02 23:28:56 -05:00
										 |  |  | require "os/mac/xcode" | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | module Homebrew | 
					
						
							|  |  |  |   extend T::Sig | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   module_function | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { returns(CLI::Parser) } | 
					
						
							|  |  |  |   def unbottled_args | 
					
						
							|  |  |  |     Homebrew::CLI::Parser.new do | 
					
						
							| 
									
										
										
										
											2021-01-15 15:04:02 -05:00
										 |  |  |       description <<~EOS | 
					
						
							| 
									
										
										
										
											2021-01-26 15:21:24 -05:00
										 |  |  |         Show the unbottled dependents of formulae. | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |       EOS | 
					
						
							| 
									
										
										
										
											2021-01-26 15:21:24 -05:00
										 |  |  |       flag   "--tag=", | 
					
						
							|  |  |  |              description: "Use the specified bottle tag (e.g. `big_sur`) instead of the current OS." | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |       switch "--dependents", | 
					
						
							| 
									
										
										
										
											2021-01-26 15:21:24 -05:00
										 |  |  |              description: "Skip getting analytics data and sort by number of dependents instead." | 
					
						
							| 
									
										
										
										
											2022-09-05 13:57:22 +01:00
										 |  |  |       switch "--total", | 
					
						
							| 
									
										
										
										
											2021-01-26 15:21:24 -05:00
										 |  |  |              description: "Print the number of unbottled and total formulae." | 
					
						
							| 
									
										
										
										
											2022-09-05 13:57:22 +01:00
										 |  |  |       switch "--eval-all", | 
					
						
							|  |  |  |              description: "Evaluate all available formulae and casks, whether installed or not, to check them. " \ | 
					
						
							| 
									
										
										
										
											2023-02-10 23:15:40 -05:00
										 |  |  |                           "Implied if `HOMEBREW_EVAL_ALL` is set." | 
					
						
							| 
									
										
										
										
											2021-01-26 15:21:24 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-05 13:57:22 +01:00
										 |  |  |       conflicts "--dependents", "--total" | 
					
						
							| 
									
										
										
										
											2021-01-10 14:26:40 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |       named_args :formula | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { void } | 
					
						
							|  |  |  |   def unbottled | 
					
						
							|  |  |  |     args = unbottled_args.parse | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Formulary.enable_factory_cache! | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-08 13:45:15 +01:00
										 |  |  |     @bottle_tag = if (tag = args.tag) | 
					
						
							|  |  |  |       Utils::Bottles::Tag.from_symbol(tag.to_sym) | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       Utils::Bottles.tag | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-15 05:22:42 +00:00
										 |  |  |     Homebrew::SimulateSystem.os = @bottle_tag.system | 
					
						
							|  |  |  |     Homebrew::SimulateSystem.arch = if Hardware::CPU::INTEL_ARCHS.include?(@bottle_tag.arch) | 
					
						
							|  |  |  |       :intel | 
					
						
							|  |  |  |     elsif Hardware::CPU::ARM_ARCHS.include?(@bottle_tag.arch) | 
					
						
							|  |  |  |       :arm | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       raise "Unknown arch #{@bottle_tag.arch}." | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-05 13:57:22 +01:00
										 |  |  |     all = args.eval_all? | 
					
						
							|  |  |  |     if args.total? | 
					
						
							|  |  |  |       if !all && !Homebrew::EnvConfig.eval_all? | 
					
						
							| 
									
										
										
										
											2023-02-07 19:25:51 +01:00
										 |  |  |         odisabled "brew unbottled --total", "brew unbottled --total --eval-all or HOMEBREW_EVAL_ALL" | 
					
						
							| 
									
										
										
										
											2022-09-05 13:57:22 +01:00
										 |  |  |       end | 
					
						
							|  |  |  |       all = true | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2022-03-08 19:24:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |     if args.named.blank? | 
					
						
							|  |  |  |       ohai "Getting formulae..." | 
					
						
							| 
									
										
										
										
											2022-09-05 13:57:22 +01:00
										 |  |  |     elsif all | 
					
						
							| 
									
										
										
										
											2023-02-10 23:15:40 -05:00
										 |  |  |       raise UsageError, "Cannot specify formulae when using `--eval-all`/`--total`." | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-28 09:49:54 +00:00
										 |  |  |     formulae, all_formulae, formula_installs = | 
					
						
							| 
									
										
										
										
											2022-09-05 13:57:22 +01:00
										 |  |  |       formulae_all_installs_from_args(args, all) | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |     deps_hash, uses_hash = deps_uses_from_formulae(all_formulae) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if args.dependents? | 
					
						
							|  |  |  |       formula_dependents = {} | 
					
						
							|  |  |  |       formulae = formulae.sort_by do |f| | 
					
						
							|  |  |  |         dependents = uses_hash[f.name]&.length || 0
 | 
					
						
							|  |  |  |         formula_dependents[f.name] ||= dependents | 
					
						
							|  |  |  |       end.reverse | 
					
						
							| 
									
										
										
										
											2022-09-05 13:57:22 +01:00
										 |  |  |     elsif all | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |       output_total(formulae) | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     noun, hash = if args.named.present? | 
					
						
							|  |  |  |       [nil, {}] | 
					
						
							|  |  |  |     elsif args.dependents? | 
					
						
							|  |  |  |       ["dependents", formula_dependents] | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       ["installs", formula_installs] | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2020-12-28 09:49:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     output_unbottled(formulae, deps_hash, noun, hash, args.named.present?) | 
					
						
							| 
									
										
										
										
											2022-11-15 05:22:42 +00:00
										 |  |  |   ensure | 
					
						
							|  |  |  |     Homebrew::SimulateSystem.clear | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-05 13:57:22 +01:00
										 |  |  |   def formulae_all_installs_from_args(args, all) | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |     if args.named.present? | 
					
						
							|  |  |  |       formulae = all_formulae = args.named.to_formulae | 
					
						
							|  |  |  |     elsif args.dependents? | 
					
						
							| 
									
										
										
										
											2022-10-25 15:44:29 +01:00
										 |  |  |       if !args.eval_all? && !Homebrew::EnvConfig.eval_all? | 
					
						
							| 
									
										
										
										
											2023-02-07 19:25:51 +01:00
										 |  |  |         odisabled "brew unbottled --dependents", "brew unbottled --all --dependents or HOMEBREW_EVAL_ALL" | 
					
						
							| 
									
										
										
										
											2022-09-05 13:57:22 +01:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2022-01-03 14:59:10 +00:00
										 |  |  |       formulae = all_formulae = Formula.all | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-23 04:41:29 +00:00
										 |  |  |       @sort = " (sorted by number of dependents)" | 
					
						
							| 
									
										
										
										
											2022-09-05 13:57:22 +01:00
										 |  |  |     elsif all | 
					
						
							|  |  |  |       formulae = all_formulae = Formula.all | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |     else | 
					
						
							|  |  |  |       formula_installs = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ohai "Getting analytics data..." | 
					
						
							| 
									
										
										
										
											2021-08-06 02:30:44 -04:00
										 |  |  |       analytics = Homebrew::API::Analytics.fetch "install", 90
 | 
					
						
							| 
									
										
										
										
											2020-11-26 16:54:21 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if analytics.blank? | 
					
						
							|  |  |  |         raise UsageError, | 
					
						
							|  |  |  |               "default sort by analytics data requires " \ | 
					
						
							|  |  |  |               "`HOMEBREW_NO_GITHUB_API` and `HOMEBREW_NO_ANALYTICS` to be unset" | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |       formulae = analytics["items"].map do |i| | 
					
						
							|  |  |  |         f = i["formula"].split.first | 
					
						
							|  |  |  |         next if f.include?("/") | 
					
						
							|  |  |  |         next if formula_installs[f].present? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         formula_installs[f] = i["count"] | 
					
						
							|  |  |  |         begin | 
					
						
							|  |  |  |           Formula[f] | 
					
						
							|  |  |  |         rescue FormulaUnavailableError | 
					
						
							|  |  |  |           nil | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end.compact | 
					
						
							| 
									
										
										
										
											2021-02-23 04:41:29 +00:00
										 |  |  |       @sort = " (sorted by installs in the last 90 days; top 10,000 only)" | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-13 17:37:50 -04:00
										 |  |  |       all_formulae = Formula.all | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-28 09:49:54 +00:00
										 |  |  |     [formulae, all_formulae, formula_installs] | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def deps_uses_from_formulae(all_formulae) | 
					
						
							|  |  |  |     ohai "Populating dependency tree..." | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     deps_hash = {} | 
					
						
							|  |  |  |     uses_hash = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     all_formulae.each do |f| | 
					
						
							|  |  |  |       deps = f.recursive_dependencies do |_, dep| | 
					
						
							|  |  |  |         Dependency.prune if dep.optional? | 
					
						
							|  |  |  |       end.map(&:to_formula) | 
					
						
							|  |  |  |       deps_hash[f.name] = deps | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       deps.each do |dep| | 
					
						
							|  |  |  |         uses_hash[dep.name] ||= [] | 
					
						
							|  |  |  |         uses_hash[dep.name] << f | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [deps_hash, uses_hash] | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def output_total(formulae) | 
					
						
							|  |  |  |     ohai "Unbottled :#{@bottle_tag} formulae" | 
					
						
							|  |  |  |     unbottled_formulae = 0
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     formulae.each do |f| | 
					
						
							|  |  |  |       next if f.bottle_specification.tag?(@bottle_tag) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       unbottled_formulae += 1
 | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     puts "#{unbottled_formulae}/#{formulae.length} remaining." | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-28 09:49:54 +00:00
										 |  |  |   def output_unbottled(formulae, deps_hash, noun, hash, any_named_args) | 
					
						
							|  |  |  |     ohai ":#{@bottle_tag} bottle status#{@sort}" | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |     any_found = T.let(false, T::Boolean) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     formulae.each do |f| | 
					
						
							| 
									
										
										
										
											2020-12-28 09:49:54 +00:00
										 |  |  |       name = f.name.downcase | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-23 04:41:29 +00:00
										 |  |  |       if f.disabled? | 
					
						
							|  |  |  |         puts "#{Tty.bold}#{Tty.green}#{name}#{Tty.reset}: formula disabled" if any_named_args | 
					
						
							|  |  |  |         next | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       requirements = f.recursive_requirements | 
					
						
							| 
									
										
										
										
											2021-04-08 13:45:15 +01:00
										 |  |  |       if @bottle_tag.linux? | 
					
						
							| 
									
										
										
										
											2022-03-29 17:33:55 -07:00
										 |  |  |         if requirements.any? { |r| r.is_a?(MacOSRequirement) && !r.version } | 
					
						
							| 
									
										
										
										
											2020-12-28 09:49:54 +00:00
										 |  |  |           puts "#{Tty.bold}#{Tty.red}#{name}#{Tty.reset}: requires macOS" if any_named_args | 
					
						
							|  |  |  |           next | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2021-03-01 13:43:47 +00:00
										 |  |  |       elsif requirements.any?(LinuxRequirement) | 
					
						
							| 
									
										
										
										
											2020-12-28 09:49:54 +00:00
										 |  |  |         puts "#{Tty.bold}#{Tty.red}#{name}#{Tty.reset}: requires Linux" if any_named_args | 
					
						
							|  |  |  |         next | 
					
						
							| 
									
										
										
										
											2021-02-23 04:41:29 +00:00
										 |  |  |       else | 
					
						
							| 
									
										
										
										
											2021-04-08 13:45:15 +01:00
										 |  |  |         macos_version = @bottle_tag.to_macos_version | 
					
						
							| 
									
										
										
										
											2021-02-23 04:41:29 +00:00
										 |  |  |         macos_satisfied = requirements.all? do |r| | 
					
						
							|  |  |  |           case r | 
					
						
							|  |  |  |           when MacOSRequirement | 
					
						
							|  |  |  |             next true unless r.version_specified? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             macos_version.public_send(r.comparator, r.version) | 
					
						
							|  |  |  |           when XcodeRequirement | 
					
						
							|  |  |  |             next true unless r.version | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Version.new(MacOS::Xcode.latest_version(macos: macos_version)) >= r.version | 
					
						
							|  |  |  |           when ArchRequirement | 
					
						
							| 
									
										
										
										
											2021-05-31 15:24:20 +01:00
										 |  |  |             r.arch == @bottle_tag.arch | 
					
						
							| 
									
										
										
										
											2021-02-23 04:41:29 +00:00
										 |  |  |           else | 
					
						
							|  |  |  |             true | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         unless macos_satisfied | 
					
						
							|  |  |  |           puts "#{Tty.bold}#{Tty.red}#{name}#{Tty.reset}: doesn't support this macOS" if any_named_args | 
					
						
							|  |  |  |           next | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2020-12-28 09:49:54 +00:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-31 15:24:20 +01:00
										 |  |  |       if f.bottle_specification.tag?(@bottle_tag, no_older_versions: true) | 
					
						
							|  |  |  |         puts "#{Tty.bold}#{Tty.green}#{name}#{Tty.reset}: already bottled" if any_named_args | 
					
						
							|  |  |  |         next | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |       deps = Array(deps_hash[f.name]).reject do |dep| | 
					
						
							| 
									
										
										
										
											2022-05-30 14:59:14 +01:00
										 |  |  |         dep.bottle_specification.tag?(@bottle_tag, no_older_versions: true) | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-12-23 10:57:10 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if deps.blank? | 
					
						
							|  |  |  |         count = " (#{hash[f.name]} #{noun})" if noun | 
					
						
							| 
									
										
										
										
											2020-12-28 09:49:54 +00:00
										 |  |  |         puts "#{Tty.bold}#{Tty.green}#{name}#{Tty.reset}#{count}: ready to bottle" | 
					
						
							| 
									
										
										
										
											2020-12-23 10:57:10 +01:00
										 |  |  |         next | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       any_found ||= true | 
					
						
							|  |  |  |       count = " (#{hash[f.name]} #{noun})" if noun | 
					
						
							| 
									
										
										
										
											2020-12-28 09:49:54 +00:00
										 |  |  |       puts "#{Tty.bold}#{Tty.yellow}#{name}#{Tty.reset}#{count}: unbottled deps: #{deps.join(" ")}" | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  |     end | 
					
						
							|  |  |  |     return if any_found | 
					
						
							| 
									
										
										
										
											2020-12-28 09:49:54 +00:00
										 |  |  |     return if any_named_args | 
					
						
							| 
									
										
										
										
											2020-11-26 08:17:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     puts "No unbottled dependencies found!" | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |