mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
197 lines
5.8 KiB
Ruby
197 lines
5.8 KiB
Ruby
# typed: true
|
|
# frozen_string_literal: true
|
|
|
|
require "tab"
|
|
|
|
module Utils
|
|
# Helper functions for bottles.
|
|
#
|
|
# @api private
|
|
module Bottles
|
|
class << self
|
|
extend T::Sig
|
|
|
|
def tag
|
|
@tag ||= "#{ENV["HOMEBREW_PROCESSOR"]}_#{ENV["HOMEBREW_SYSTEM"]}".downcase.to_sym
|
|
end
|
|
|
|
def built_as?(f)
|
|
return false unless f.latest_version_installed?
|
|
|
|
tab = Tab.for_keg(f.latest_installed_prefix)
|
|
tab.built_as_bottle
|
|
end
|
|
|
|
def file_outdated?(f, file)
|
|
filename = file.basename.to_s
|
|
return unless f.bottle && filename.match(Pathname::BOTTLE_EXTNAME_RX)
|
|
|
|
bottle_ext = filename[native_regex, 1]
|
|
bottle_url_ext = f.bottle.url[native_regex, 1]
|
|
|
|
bottle_ext && bottle_url_ext && bottle_ext != bottle_url_ext
|
|
end
|
|
|
|
sig { returns(Regexp) }
|
|
def native_regex
|
|
/(\.#{Regexp.escape(tag.to_s)}\.bottle\.(\d+\.)?tar\.gz)$/o
|
|
end
|
|
|
|
def receipt_path(bottle_file)
|
|
path = Utils.popen_read("tar", "-tzf", bottle_file).lines.map(&:chomp).find do |line|
|
|
line =~ %r{.+/.+/INSTALL_RECEIPT.json}
|
|
end
|
|
raise "This bottle does not contain the file INSTALL_RECEIPT.json: #{bottle_file}" unless path
|
|
|
|
path
|
|
end
|
|
|
|
def resolve_formula_names(bottle_file)
|
|
receipt_file_path = receipt_path bottle_file
|
|
receipt_file = Utils.popen_read("tar", "-xOzf", bottle_file, receipt_file_path)
|
|
name = receipt_file_path.split("/").first
|
|
tap = Tab.from_file_content(receipt_file, "#{bottle_file}/#{receipt_file_path}").tap
|
|
|
|
full_name = if tap.nil? || tap.core_tap?
|
|
name
|
|
else
|
|
"#{tap}/#{name}"
|
|
end
|
|
|
|
[name, full_name]
|
|
end
|
|
|
|
def resolve_version(bottle_file)
|
|
PkgVersion.parse receipt_path(bottle_file).split("/").second
|
|
end
|
|
|
|
def formula_contents(bottle_file,
|
|
name: resolve_formula_names(bottle_file)[0])
|
|
bottle_version = resolve_version bottle_file
|
|
formula_path = "#{name}/#{bottle_version}/.brew/#{name}.rb"
|
|
contents = Utils.popen_read "tar", "-xOzf", bottle_file, formula_path
|
|
raise BottleFormulaUnavailableError.new(bottle_file, formula_path) unless $CHILD_STATUS.success?
|
|
|
|
contents
|
|
end
|
|
|
|
def add_bottle_stanza!(formula_contents, bottle_output)
|
|
Homebrew.install_bundler_gems!
|
|
require "rubocop-ast"
|
|
|
|
ruby_version = Version.new(HOMEBREW_REQUIRED_RUBY_VERSION).major_minor.to_f
|
|
processed_source = RuboCop::AST::ProcessedSource.new(formula_contents, ruby_version)
|
|
root_node = processed_source.ast
|
|
|
|
class_node = if root_node.class_type?
|
|
root_node
|
|
elsif root_node.begin_type?
|
|
root_node.children.find do |n|
|
|
n.class_type? && n.parent_class&.const_name == "Formula"
|
|
end
|
|
end
|
|
|
|
odie "Could not find formula class!" if class_node.nil?
|
|
|
|
body_node = class_node.body
|
|
odie "Formula class is empty!" if body_node.nil?
|
|
|
|
node_before_bottle = if body_node.begin_type?
|
|
body_node.children.compact.reduce do |previous_child, current_child|
|
|
break previous_child unless component_before_bottle_block? current_child
|
|
|
|
current_child
|
|
end
|
|
else
|
|
body_node
|
|
end
|
|
node_before_bottle = node_before_bottle.last_argument if node_before_bottle.send_type?
|
|
|
|
expr_before_bottle = node_before_bottle.location.expression
|
|
processed_source.comments.each do |comment|
|
|
comment_expr = comment.location.expression
|
|
distance = comment_expr.first_line - expr_before_bottle.first_line
|
|
case distance
|
|
when 0
|
|
if comment_expr.last_line > expr_before_bottle.last_line ||
|
|
comment_expr.end_pos > expr_before_bottle.end_pos
|
|
expr_before_bottle = comment_expr
|
|
end
|
|
when 1
|
|
expr_before_bottle = comment_expr
|
|
end
|
|
end
|
|
|
|
tree_rewriter = Parser::Source::TreeRewriter.new(processed_source.buffer)
|
|
tree_rewriter.insert_after(expr_before_bottle, "\n\n#{bottle_output.chomp}")
|
|
formula_contents.replace(tree_rewriter.process)
|
|
end
|
|
|
|
private
|
|
|
|
def component_before_bottle_block?(node)
|
|
require "rubocops/components_order"
|
|
|
|
RuboCop::Cop::FormulaAudit::ComponentsOrder::COMPONENT_PRECEDENCE_LIST.each do |components|
|
|
components.each do |component|
|
|
return false if component[:name] == :bottle && component[:type] == :block_call
|
|
|
|
case component[:type]
|
|
when :method_call
|
|
return true if node.send_type? && node.method_name == component[:name]
|
|
when :block_call
|
|
return true if node.block_type? && node.method_name == component[:name]
|
|
end
|
|
end
|
|
end
|
|
false
|
|
end
|
|
end
|
|
|
|
# Helper functions for bottles hosted on Bintray.
|
|
module Bintray
|
|
def self.package(formula_name)
|
|
package_name = formula_name.to_s.dup
|
|
package_name.tr!("+", "x")
|
|
package_name.sub!(/(.)@(\d)/, "\\1:\\2") # Handle foo@1.2 style formulae.
|
|
package_name
|
|
end
|
|
|
|
def self.repository(tap = nil)
|
|
if tap.nil? || tap.core_tap?
|
|
"bottles"
|
|
else
|
|
"bottles-#{tap.repo}"
|
|
end
|
|
end
|
|
end
|
|
|
|
# Collector for bottle specifications.
|
|
class Collector
|
|
extend T::Sig
|
|
|
|
extend Forwardable
|
|
|
|
def_delegators :@checksums, :keys, :[], :[]=, :key?, :each_key
|
|
|
|
sig { void }
|
|
def initialize
|
|
@checksums = {}
|
|
end
|
|
|
|
def fetch_checksum_for(tag)
|
|
tag = find_matching_tag(tag)
|
|
return self[tag], tag if tag
|
|
end
|
|
|
|
private
|
|
|
|
def find_matching_tag(tag)
|
|
tag if key?(tag)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
require "extend/os/bottles"
|