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"
|
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."
|
2020-11-26 08:17:20 +00:00
|
|
|
switch "--total",
|
2021-01-26 15:21:24 -05:00
|
|
|
description: "Print the number of unbottled and total formulae."
|
|
|
|
|
2020-11-26 08:17:20 +00: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
|
|
|
|
|
|
|
if args.named.blank?
|
|
|
|
ohai "Getting formulae..."
|
|
|
|
elsif args.total?
|
|
|
|
raise UsageError, "cannot specify `<formula>` and `--total`."
|
|
|
|
end
|
|
|
|
|
2020-12-28 09:49:54 +00:00
|
|
|
formulae, all_formulae, formula_installs =
|
|
|
|
formulae_all_installs_from_args(args)
|
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
|
|
|
|
end
|
|
|
|
|
|
|
|
if args.total?
|
|
|
|
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?)
|
2020-11-26 08:17:20 +00:00
|
|
|
end
|
|
|
|
|
2020-12-28 09:49:54 +00:00
|
|
|
def formulae_all_installs_from_args(args)
|
2020-11-26 08:17:20 +00:00
|
|
|
if args.named.present?
|
|
|
|
formulae = all_formulae = args.named.to_formulae
|
|
|
|
elsif args.total?
|
2022-01-03 14:59:10 +00:00
|
|
|
formulae = all_formulae = Formula.all
|
2020-11-26 08:17:20 +00:00
|
|
|
elsif args.dependents?
|
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)"
|
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
|
|
|
|
|
|
|
all_formulae = Formula
|
|
|
|
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)
|
|
|
|
next if f.bottle_unneeded?
|
|
|
|
|
|
|
|
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?
|
2021-03-01 13:43:47 +00:00
|
|
|
if requirements.any?(MacOSRequirement)
|
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
|
|
|
|
|
|
|
|
if f.bottle_unneeded? || f.bottle_disabled?
|
|
|
|
reason = if f.bottle_unneeded?
|
|
|
|
"unneeded"
|
|
|
|
else
|
|
|
|
"disabled"
|
|
|
|
end
|
|
|
|
puts "#{Tty.bold}#{Tty.red}#{name}#{Tty.reset}: bottle #{reason}" if any_named_args
|
|
|
|
next
|
|
|
|
end
|
2020-11-26 08:17:20 +00:00
|
|
|
|
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|
|
2021-04-26 11:44:35 -07:00
|
|
|
dep.bottle_specification.tag?(@bottle_tag, no_older_versions: true) || dep.bottle_unneeded?
|
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
|