Move remaining OS extensions to prepend
This commit is contained in:
parent
7c4f2c19fe
commit
eed660e784
@ -177,7 +177,6 @@ class DevelopmentTools
|
||||
"cpu_family" => Hardware::CPU.family.to_s,
|
||||
}
|
||||
end
|
||||
alias generic_build_system_info build_system_info
|
||||
end
|
||||
end
|
||||
|
||||
|
5
Library/Homebrew/extend/os/linux/cleanup.rbi
Normal file
5
Library/Homebrew/extend/os/linux/cleanup.rbi
Normal file
@ -0,0 +1,5 @@
|
||||
# typed: strict
|
||||
|
||||
# module OS::Linux::Cleanup
|
||||
# include Kernel
|
||||
# end
|
@ -1,101 +1,103 @@
|
||||
# typed: true # rubocop:disable Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "os/linux/glibc"
|
||||
|
||||
class DependencyCollector
|
||||
undef gcc_dep_if_needed
|
||||
undef glibc_dep_if_needed
|
||||
undef init_global_dep_tree_if_needed!
|
||||
module OS
|
||||
module Linux
|
||||
module DependencyCollector
|
||||
sig { params(related_formula_names: T::Set[String]).returns(T.nilable(Dependency)) }
|
||||
def gcc_dep_if_needed(related_formula_names)
|
||||
# gcc is required for libgcc_s.so.1 if glibc or gcc are too old
|
||||
return unless ::DevelopmentTools.needs_build_formulae?
|
||||
return if building_global_dep_tree?
|
||||
return if related_formula_names.include?(GCC)
|
||||
return if global_dep_tree[GCC]&.intersect?(related_formula_names)
|
||||
return unless formula_for(GCC)
|
||||
|
||||
sig { params(related_formula_names: T::Set[String]).returns(T.nilable(Dependency)) }
|
||||
def gcc_dep_if_needed(related_formula_names)
|
||||
# gcc is required for libgcc_s.so.1 if glibc or gcc are too old
|
||||
return unless DevelopmentTools.needs_build_formulae?
|
||||
return if building_global_dep_tree?
|
||||
return if related_formula_names.include?(GCC)
|
||||
return if global_dep_tree[GCC]&.intersect?(related_formula_names)
|
||||
return unless formula_for(GCC)
|
||||
Dependency.new(GCC, [:implicit])
|
||||
end
|
||||
|
||||
Dependency.new(GCC, [:implicit])
|
||||
end
|
||||
sig { params(related_formula_names: T::Set[String]).returns(T.nilable(Dependency)) }
|
||||
def glibc_dep_if_needed(related_formula_names)
|
||||
return unless ::DevelopmentTools.needs_libc_formula?
|
||||
return if building_global_dep_tree?
|
||||
return if related_formula_names.include?(GLIBC)
|
||||
return if global_dep_tree[GLIBC]&.intersect?(related_formula_names)
|
||||
return unless formula_for(GLIBC)
|
||||
|
||||
sig { params(related_formula_names: T::Set[String]).returns(T.nilable(Dependency)) }
|
||||
def glibc_dep_if_needed(related_formula_names)
|
||||
return unless DevelopmentTools.needs_libc_formula?
|
||||
return if building_global_dep_tree?
|
||||
return if related_formula_names.include?(GLIBC)
|
||||
return if global_dep_tree[GLIBC]&.intersect?(related_formula_names)
|
||||
return unless formula_for(GLIBC)
|
||||
Dependency.new(GLIBC, [:implicit])
|
||||
end
|
||||
|
||||
Dependency.new(GLIBC, [:implicit])
|
||||
end
|
||||
private
|
||||
|
||||
private
|
||||
GLIBC = "glibc"
|
||||
GCC = OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA
|
||||
|
||||
GLIBC = "glibc"
|
||||
GCC = OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA
|
||||
sig { void }
|
||||
def init_global_dep_tree_if_needed!
|
||||
return unless ::DevelopmentTools.needs_build_formulae?
|
||||
return if building_global_dep_tree?
|
||||
return unless global_dep_tree.empty?
|
||||
|
||||
sig { void }
|
||||
def init_global_dep_tree_if_needed!
|
||||
return unless DevelopmentTools.needs_build_formulae?
|
||||
return if building_global_dep_tree?
|
||||
return unless global_dep_tree.empty?
|
||||
building_global_dep_tree!
|
||||
global_dep_tree[GLIBC] = Set.new(global_deps_for(GLIBC))
|
||||
# gcc depends on glibc
|
||||
global_dep_tree[GCC] = Set.new([*global_deps_for(GCC), GLIBC, *@@global_dep_tree[GLIBC]])
|
||||
built_global_dep_tree!
|
||||
end
|
||||
|
||||
building_global_dep_tree!
|
||||
global_dep_tree[GLIBC] = Set.new(global_deps_for(GLIBC))
|
||||
# gcc depends on glibc
|
||||
global_dep_tree[GCC] = Set.new([*global_deps_for(GCC), GLIBC, *@@global_dep_tree[GLIBC]])
|
||||
built_global_dep_tree!
|
||||
end
|
||||
sig { params(name: String).returns(T.nilable(Formula)) }
|
||||
def formula_for(name)
|
||||
@formula_for ||= T.let({}, T.nilable(T::Hash[String, Formula]))
|
||||
@formula_for[name] ||= ::Formula[name]
|
||||
rescue FormulaUnavailableError
|
||||
nil
|
||||
end
|
||||
|
||||
sig { params(name: String).returns(T.nilable(Formula)) }
|
||||
def formula_for(name)
|
||||
@formula_for ||= {}
|
||||
@formula_for[name] ||= Formula[name]
|
||||
rescue FormulaUnavailableError
|
||||
nil
|
||||
end
|
||||
sig { params(name: String).returns(T::Array[String]) }
|
||||
def global_deps_for(name)
|
||||
@global_deps_for ||= T.let({}, T.nilable(T::Hash[String, T::Array[String]]))
|
||||
# Always strip out glibc and gcc from all parts of dependency tree when
|
||||
# we're calculating their dependency trees. Other parts of Homebrew will
|
||||
# catch any circular dependencies.
|
||||
@global_deps_for[name] ||= if (formula = formula_for(name))
|
||||
formula.deps.map(&:name).flat_map do |dep|
|
||||
[dep, *global_deps_for(dep)].compact
|
||||
end.uniq
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(name: String).returns(T::Array[String]) }
|
||||
def global_deps_for(name)
|
||||
@global_deps_for ||= {}
|
||||
# Always strip out glibc and gcc from all parts of dependency tree when
|
||||
# we're calculating their dependency trees. Other parts of Homebrew will
|
||||
# catch any circular dependencies.
|
||||
@global_deps_for[name] ||= if (formula = formula_for(name))
|
||||
formula.deps.map(&:name).flat_map do |dep|
|
||||
[dep, *global_deps_for(dep)].compact
|
||||
end.uniq
|
||||
else
|
||||
[]
|
||||
# Use class variables to avoid this expensive logic needing to be done more
|
||||
# than once.
|
||||
# rubocop:disable Style/ClassVars
|
||||
@@global_dep_tree = T.let({}, T::Hash[String, T::Set[String]])
|
||||
@@building_global_dep_tree = T.let(false, T::Boolean)
|
||||
|
||||
sig { returns(T::Hash[String, T::Set[String]]) }
|
||||
def global_dep_tree
|
||||
@@global_dep_tree
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def building_global_dep_tree!
|
||||
@@building_global_dep_tree = true
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def built_global_dep_tree!
|
||||
@@building_global_dep_tree = false
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def building_global_dep_tree?
|
||||
@@building_global_dep_tree.present?
|
||||
end
|
||||
# rubocop:enable Style/ClassVars
|
||||
end
|
||||
end
|
||||
|
||||
# Use class variables to avoid this expensive logic needing to be done more
|
||||
# than once.
|
||||
# rubocop:disable Style/ClassVars
|
||||
@@global_dep_tree = {}
|
||||
@@building_global_dep_tree = false
|
||||
|
||||
sig { returns(T::Hash[String, T::Set[String]]) }
|
||||
def global_dep_tree
|
||||
@@global_dep_tree
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def building_global_dep_tree!
|
||||
@@building_global_dep_tree = true
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def built_global_dep_tree!
|
||||
@@building_global_dep_tree = false
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def building_global_dep_tree?
|
||||
@@building_global_dep_tree.present?
|
||||
end
|
||||
# rubocop:enable Style/ClassVars
|
||||
end
|
||||
|
||||
DependencyCollector.prepend(OS::Linux::DependencyCollector)
|
||||
|
@ -1,54 +1,64 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DevelopmentTools
|
||||
class << self
|
||||
sig { params(tool: T.any(String, Symbol)).returns(T.nilable(Pathname)) }
|
||||
def locate(tool)
|
||||
(@locate ||= {}).fetch(tool) do |key|
|
||||
@locate[key] = if needs_build_formulae? &&
|
||||
(binutils_path = HOMEBREW_PREFIX/"opt/binutils/bin/#{tool}").executable?
|
||||
binutils_path
|
||||
elsif needs_build_formulae? && (glibc_path = HOMEBREW_PREFIX/"opt/glibc/bin/#{tool}").executable?
|
||||
glibc_path
|
||||
elsif (homebrew_path = HOMEBREW_PREFIX/"bin/#{tool}").executable?
|
||||
homebrew_path
|
||||
elsif File.executable?((system_path = "/usr/bin/#{tool}"))
|
||||
Pathname.new system_path
|
||||
module OS
|
||||
module Linux
|
||||
module DevelopmentTools
|
||||
extend T::Helpers
|
||||
|
||||
requires_ancestor { ::DevelopmentTools }
|
||||
|
||||
sig { params(tool: T.any(String, Symbol)).returns(T.nilable(Pathname)) }
|
||||
def locate(tool)
|
||||
@locate ||= T.let({}, T.nilable(T::Hash[T.any(String, Symbol), Pathname]))
|
||||
@locate.fetch(tool) do |key|
|
||||
@locate[key] = if ::DevelopmentTools.needs_build_formulae? &&
|
||||
(binutils_path = HOMEBREW_PREFIX/"opt/binutils/bin/#{tool}").executable?
|
||||
binutils_path
|
||||
elsif ::DevelopmentTools.needs_build_formulae? &&
|
||||
(glibc_path = HOMEBREW_PREFIX/"opt/glibc/bin/#{tool}").executable?
|
||||
glibc_path
|
||||
elsif (homebrew_path = HOMEBREW_PREFIX/"bin/#{tool}").executable?
|
||||
homebrew_path
|
||||
elsif File.executable?((system_path = "/usr/bin/#{tool}"))
|
||||
Pathname.new system_path
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(Symbol) }
|
||||
def default_compiler
|
||||
:gcc
|
||||
end
|
||||
sig { returns(Symbol) }
|
||||
def default_compiler = :gcc
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def needs_libc_formula?
|
||||
return @needs_libc_formula if defined? @needs_libc_formula
|
||||
sig { returns(T::Boolean) }
|
||||
def needs_libc_formula?
|
||||
return @needs_libc_formula unless @needs_libc_formula.nil?
|
||||
|
||||
@needs_libc_formula = OS::Linux::Glibc.below_ci_version?
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def needs_compiler_formula?
|
||||
return @needs_compiler_formula if defined? @needs_compiler_formula
|
||||
|
||||
gcc = "/usr/bin/gcc"
|
||||
@needs_compiler_formula = if File.exist?(gcc)
|
||||
gcc_version(gcc) < OS::LINUX_GCC_CI_VERSION
|
||||
else
|
||||
true
|
||||
@needs_libc_formula = T.let(OS::Linux::Glibc.below_ci_version?, T.nilable(T::Boolean))
|
||||
@needs_libc_formula = !!@needs_libc_formula
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[String, T.nilable(String)]) }
|
||||
def build_system_info
|
||||
generic_build_system_info.merge({
|
||||
"glibc_version" => OS::Linux::Glibc.version.to_s.presence,
|
||||
"oldest_cpu_family" => Hardware.oldest_cpu.to_s,
|
||||
})
|
||||
sig { returns(T::Boolean) }
|
||||
def needs_compiler_formula?
|
||||
return @needs_compiler_formula unless @needs_compiler_formula.nil?
|
||||
|
||||
gcc = "/usr/bin/gcc"
|
||||
@needs_compiler_formula = T.let(if File.exist?(gcc)
|
||||
::DevelopmentTools.gcc_version(gcc) < OS::LINUX_GCC_CI_VERSION
|
||||
else
|
||||
true
|
||||
end, T.nilable(T::Boolean))
|
||||
!!@needs_compiler_formula
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[String, T.nilable(String)]) }
|
||||
def build_system_info
|
||||
super.merge({
|
||||
"glibc_version" => OS::Linux::Glibc.version.to_s.presence,
|
||||
"oldest_cpu_family" => Hardware.oldest_cpu.to_s,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DevelopmentTools.singleton_class.prepend(OS::Linux::DevelopmentTools)
|
||||
|
@ -7,168 +7,174 @@ require "hardware"
|
||||
require "os/linux/glibc"
|
||||
require "os/linux/kernel"
|
||||
|
||||
module Homebrew
|
||||
module Diagnostic
|
||||
class Checks
|
||||
undef fatal_preinstall_checks, supported_configuration_checks
|
||||
module OS
|
||||
module Linux
|
||||
module Diagnostic
|
||||
module Checks
|
||||
extend T::Helpers
|
||||
|
||||
def fatal_preinstall_checks
|
||||
%w[
|
||||
check_access_directories
|
||||
check_linuxbrew_core
|
||||
check_linuxbrew_bottle_domain
|
||||
].freeze
|
||||
end
|
||||
requires_ancestor { Homebrew::Diagnostic::Checks }
|
||||
|
||||
def supported_configuration_checks
|
||||
%w[
|
||||
check_glibc_minimum_version
|
||||
check_kernel_minimum_version
|
||||
check_supported_architecture
|
||||
].freeze
|
||||
end
|
||||
|
||||
def check_tmpdir_sticky_bit
|
||||
message = generic_check_tmpdir_sticky_bit
|
||||
return if message.nil?
|
||||
|
||||
message + <<~EOS
|
||||
If you don't have administrative privileges on this machine,
|
||||
create a directory and set the HOMEBREW_TEMP environment variable,
|
||||
for example:
|
||||
install -d -m 1755 ~/tmp
|
||||
#{Utils::Shell.set_variable_in_profile("HOMEBREW_TEMP", "~/tmp")}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_tmpdir_executable
|
||||
f = Tempfile.new(%w[homebrew_check_tmpdir_executable .sh], HOMEBREW_TEMP)
|
||||
f.write "#!/bin/sh\n"
|
||||
f.chmod 0700
|
||||
f.close
|
||||
return if system T.must(f.path)
|
||||
|
||||
<<~EOS
|
||||
The directory #{HOMEBREW_TEMP} does not permit executing
|
||||
programs. It is likely mounted as "noexec". Please set HOMEBREW_TEMP
|
||||
in your #{Utils::Shell.profile} to a different directory, for example:
|
||||
export HOMEBREW_TEMP=~/tmp
|
||||
echo 'export HOMEBREW_TEMP=~/tmp' >> #{Utils::Shell.profile}
|
||||
EOS
|
||||
ensure
|
||||
f&.unlink
|
||||
end
|
||||
|
||||
def check_xdg_data_dirs
|
||||
xdg_data_dirs = ENV.fetch("HOMEBREW_XDG_DATA_DIRS", nil)
|
||||
return if xdg_data_dirs.blank?
|
||||
return if xdg_data_dirs.split(":").include?("#{HOMEBREW_PREFIX}/share")
|
||||
|
||||
<<~EOS
|
||||
Homebrew's share was not found in your XDG_DATA_DIRS but you have
|
||||
this variable set to include other locations.
|
||||
Some programs like `vapigen` may not work correctly.
|
||||
Consider adding Homebrew's share directory to XDG_DATA_DIRS like so:
|
||||
echo 'export XDG_DATA_DIRS="#{HOMEBREW_PREFIX}/share:$XDG_DATA_DIRS"' >> #{Utils::Shell.profile}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_umask_not_zero
|
||||
return unless File.umask.zero?
|
||||
|
||||
<<~EOS
|
||||
umask is currently set to 000. Directories created by Homebrew cannot
|
||||
be world-writable. This issue can be resolved by adding "umask 002" to
|
||||
your #{Utils::Shell.profile}:
|
||||
echo 'umask 002' >> #{Utils::Shell.profile}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_supported_architecture
|
||||
return if Hardware::CPU.arch == :x86_64
|
||||
|
||||
<<~EOS
|
||||
Your CPU architecture (#{Hardware::CPU.arch}) is not supported. We only support
|
||||
x86_64 CPU architectures. You will be unable to use binary packages (bottles).
|
||||
#{please_create_pull_requests}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_glibc_minimum_version
|
||||
return unless OS::Linux::Glibc.below_minimum_version?
|
||||
|
||||
<<~EOS
|
||||
Your system glibc #{OS::Linux::Glibc.system_version} is too old.
|
||||
We only support glibc #{OS::Linux::Glibc.minimum_version} or later.
|
||||
#{please_create_pull_requests}
|
||||
We recommend updating to a newer version via your distribution's
|
||||
package manager, upgrading your distribution to the latest version,
|
||||
or changing distributions.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_kernel_minimum_version
|
||||
return unless OS::Linux::Kernel.below_minimum_version?
|
||||
|
||||
<<~EOS
|
||||
Your Linux kernel #{OS.kernel_version} is too old.
|
||||
We only support kernel #{OS::Linux::Kernel.minimum_version} or later.
|
||||
You will be unable to use binary packages (bottles).
|
||||
#{please_create_pull_requests}
|
||||
We recommend updating to a newer version via your distribution's
|
||||
package manager, upgrading your distribution to the latest version,
|
||||
or changing distributions.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_linuxbrew_core
|
||||
return unless Homebrew::EnvConfig.no_install_from_api?
|
||||
return unless CoreTap.instance.linuxbrew_core?
|
||||
|
||||
<<~EOS
|
||||
Your Linux core repository is still linuxbrew-core.
|
||||
You must `brew update` to update to homebrew-core.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_linuxbrew_bottle_domain
|
||||
return unless Homebrew::EnvConfig.bottle_domain.include?("linuxbrew")
|
||||
|
||||
<<~EOS
|
||||
Your HOMEBREW_BOTTLE_DOMAIN still contains "linuxbrew".
|
||||
You must unset it (or adjust it to not contain linuxbrew
|
||||
e.g. by using homebrew instead).
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_gcc_dependent_linkage
|
||||
gcc_dependents = Formula.installed.select do |formula|
|
||||
next false unless formula.tap&.core_tap?
|
||||
|
||||
# FIXME: This includes formulae that have no runtime dependency on GCC.
|
||||
formula.recursive_dependencies.map(&:name).include? "gcc"
|
||||
rescue TapFormulaUnavailableError
|
||||
false
|
||||
def fatal_preinstall_checks
|
||||
%w[
|
||||
check_access_directories
|
||||
check_linuxbrew_core
|
||||
check_linuxbrew_bottle_domain
|
||||
].freeze
|
||||
end
|
||||
return if gcc_dependents.empty?
|
||||
|
||||
badly_linked = gcc_dependents.select do |dependent|
|
||||
keg = Keg.new(dependent.prefix)
|
||||
keg.binary_executable_or_library_files.any? do |binary|
|
||||
paths = binary.rpaths
|
||||
versioned_linkage = paths.any? { |path| path.match?(%r{lib/gcc/\d+$}) }
|
||||
unversioned_linkage = paths.any? { |path| path.match?(%r{lib/gcc/current$}) }
|
||||
def supported_configuration_checks
|
||||
%w[
|
||||
check_glibc_minimum_version
|
||||
check_kernel_minimum_version
|
||||
check_supported_architecture
|
||||
].freeze
|
||||
end
|
||||
|
||||
versioned_linkage && !unversioned_linkage
|
||||
def check_tmpdir_sticky_bit
|
||||
message = generic_check_tmpdir_sticky_bit
|
||||
return if message.nil?
|
||||
|
||||
message + <<~EOS
|
||||
If you don't have administrative privileges on this machine,
|
||||
create a directory and set the HOMEBREW_TEMP environment variable,
|
||||
for example:
|
||||
install -d -m 1755 ~/tmp
|
||||
#{Utils::Shell.set_variable_in_profile("HOMEBREW_TEMP", "~/tmp")}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_tmpdir_executable
|
||||
f = Tempfile.new(%w[homebrew_check_tmpdir_executable .sh], HOMEBREW_TEMP)
|
||||
f.write "#!/bin/sh\n"
|
||||
f.chmod 0700
|
||||
f.close
|
||||
return if system T.must(f.path)
|
||||
|
||||
<<~EOS
|
||||
The directory #{HOMEBREW_TEMP} does not permit executing
|
||||
programs. It is likely mounted as "noexec". Please set HOMEBREW_TEMP
|
||||
in your #{Utils::Shell.profile} to a different directory, for example:
|
||||
export HOMEBREW_TEMP=~/tmp
|
||||
echo 'export HOMEBREW_TEMP=~/tmp' >> #{Utils::Shell.profile}
|
||||
EOS
|
||||
ensure
|
||||
f&.unlink
|
||||
end
|
||||
|
||||
def check_xdg_data_dirs
|
||||
xdg_data_dirs = ENV.fetch("HOMEBREW_XDG_DATA_DIRS", nil)
|
||||
return if xdg_data_dirs.blank?
|
||||
return if xdg_data_dirs.split(":").include?("#{HOMEBREW_PREFIX}/share")
|
||||
|
||||
<<~EOS
|
||||
Homebrew's share was not found in your XDG_DATA_DIRS but you have
|
||||
this variable set to include other locations.
|
||||
Some programs like `vapigen` may not work correctly.
|
||||
Consider adding Homebrew's share directory to XDG_DATA_DIRS like so:
|
||||
echo 'export XDG_DATA_DIRS="#{HOMEBREW_PREFIX}/share:$XDG_DATA_DIRS"' >> #{Utils::Shell.profile}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_umask_not_zero
|
||||
return unless File.umask.zero?
|
||||
|
||||
<<~EOS
|
||||
umask is currently set to 000. Directories created by Homebrew cannot
|
||||
be world-writable. This issue can be resolved by adding "umask 002" to
|
||||
your #{Utils::Shell.profile}:
|
||||
echo 'umask 002' >> #{Utils::Shell.profile}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_supported_architecture
|
||||
return if Hardware::CPU.arch == :x86_64
|
||||
|
||||
<<~EOS
|
||||
Your CPU architecture (#{Hardware::CPU.arch}) is not supported. We only support
|
||||
x86_64 CPU architectures. You will be unable to use binary packages (bottles).
|
||||
#{please_create_pull_requests}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_glibc_minimum_version
|
||||
return unless OS::Linux::Glibc.below_minimum_version?
|
||||
|
||||
<<~EOS
|
||||
Your system glibc #{OS::Linux::Glibc.system_version} is too old.
|
||||
We only support glibc #{OS::Linux::Glibc.minimum_version} or later.
|
||||
#{please_create_pull_requests}
|
||||
We recommend updating to a newer version via your distribution's
|
||||
package manager, upgrading your distribution to the latest version,
|
||||
or changing distributions.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_kernel_minimum_version
|
||||
return unless OS::Linux::Kernel.below_minimum_version?
|
||||
|
||||
<<~EOS
|
||||
Your Linux kernel #{OS.kernel_version} is too old.
|
||||
We only support kernel #{OS::Linux::Kernel.minimum_version} or later.
|
||||
You will be unable to use binary packages (bottles).
|
||||
#{please_create_pull_requests}
|
||||
We recommend updating to a newer version via your distribution's
|
||||
package manager, upgrading your distribution to the latest version,
|
||||
or changing distributions.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_linuxbrew_core
|
||||
return unless Homebrew::EnvConfig.no_install_from_api?
|
||||
return unless CoreTap.instance.linuxbrew_core?
|
||||
|
||||
<<~EOS
|
||||
Your Linux core repository is still linuxbrew-core.
|
||||
You must `brew update` to update to homebrew-core.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_linuxbrew_bottle_domain
|
||||
return unless Homebrew::EnvConfig.bottle_domain.include?("linuxbrew")
|
||||
|
||||
<<~EOS
|
||||
Your HOMEBREW_BOTTLE_DOMAIN still contains "linuxbrew".
|
||||
You must unset it (or adjust it to not contain linuxbrew
|
||||
e.g. by using homebrew instead).
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_gcc_dependent_linkage
|
||||
gcc_dependents = ::Formula.installed.select do |formula|
|
||||
next false unless formula.tap&.core_tap?
|
||||
|
||||
# FIXME: This includes formulae that have no runtime dependency on GCC.
|
||||
formula.recursive_dependencies.map(&:name).include? "gcc"
|
||||
rescue TapFormulaUnavailableError
|
||||
false
|
||||
end
|
||||
end
|
||||
return if badly_linked.empty?
|
||||
return if gcc_dependents.empty?
|
||||
|
||||
inject_file_list badly_linked, <<~EOS
|
||||
Formulae which link to GCC through a versioned path were found. These formulae
|
||||
are prone to breaking when GCC is updated. You should `brew reinstall` these formulae:
|
||||
EOS
|
||||
badly_linked = gcc_dependents.select do |dependent|
|
||||
keg = Keg.new(dependent.prefix)
|
||||
keg.binary_executable_or_library_files.any? do |binary|
|
||||
paths = binary.rpaths
|
||||
versioned_linkage = paths.any? { |path| path.match?(%r{lib/gcc/\d+$}) }
|
||||
unversioned_linkage = paths.any? { |path| path.match?(%r{lib/gcc/current$}) }
|
||||
|
||||
versioned_linkage && !unversioned_linkage
|
||||
end
|
||||
end
|
||||
return if badly_linked.empty?
|
||||
|
||||
inject_file_list badly_linked, <<~EOS
|
||||
Formulae which link to GCC through a versioned path were found. These formulae
|
||||
are prone to breaking when GCC is updated. You should `brew reinstall` these formulae:
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Homebrew::Diagnostic::Checks.prepend(OS::Linux::Diagnostic::Checks)
|
||||
|
@ -28,7 +28,7 @@ module OS
|
||||
|
||||
sig { params(spec: SoftwareSpec).void }
|
||||
def add_global_deps_to_spec(spec)
|
||||
return unless DevelopmentTools.needs_build_formulae?
|
||||
return unless ::DevelopmentTools.needs_build_formulae?
|
||||
|
||||
@global_deps ||= begin
|
||||
dependency_collector = spec.dependency_collector
|
||||
|
@ -1,15 +1,12 @@
|
||||
# typed: true # rubocop:disable Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Homebrew
|
||||
class SimulateSystem
|
||||
class << self
|
||||
undef os
|
||||
undef simulating_or_running_on_linux?
|
||||
undef current_os
|
||||
|
||||
module OS
|
||||
module Linux
|
||||
module SimulateSystem
|
||||
sig { returns(T.nilable(Symbol)) }
|
||||
def os
|
||||
@os ||= T.let(nil, T.nilable(Symbol))
|
||||
return :macos if @os.blank? && Homebrew::EnvConfig.simulate_macos_on_linux?
|
||||
|
||||
@os
|
||||
@ -27,3 +24,5 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Homebrew::SimulateSystem.singleton_class.prepend(OS::Linux::SimulateSystem)
|
||||
|
@ -8,7 +8,7 @@ module OS
|
||||
def use_system_ruby?
|
||||
return false if Homebrew::EnvConfig.force_vendor_ruby?
|
||||
|
||||
Homebrew::EnvConfig.developer? && ENV["HOMEBREW_USE_RUBY_FROM_PATH"].present?
|
||||
::Homebrew::EnvConfig.developer? && ENV["HOMEBREW_USE_RUBY_FROM_PATH"].present?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,23 +1,26 @@
|
||||
# typed: true # rubocop:disable Sorbet/StrictSigil
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DependencyCollector
|
||||
undef git_dep_if_needed, subversion_dep_if_needed, cvs_dep_if_needed,
|
||||
xz_dep_if_needed, unzip_dep_if_needed, bzip2_dep_if_needed
|
||||
module OS
|
||||
module Mac
|
||||
module DependencyCollector
|
||||
def git_dep_if_needed(tags); end
|
||||
|
||||
def git_dep_if_needed(tags); end
|
||||
def subversion_dep_if_needed(tags)
|
||||
Dependency.new("subversion", [*tags, :implicit])
|
||||
end
|
||||
|
||||
def subversion_dep_if_needed(tags)
|
||||
Dependency.new("subversion", [*tags, :implicit])
|
||||
def cvs_dep_if_needed(tags)
|
||||
Dependency.new("cvs", [*tags, :implicit])
|
||||
end
|
||||
|
||||
def xz_dep_if_needed(tags); end
|
||||
|
||||
def unzip_dep_if_needed(tags); end
|
||||
|
||||
def bzip2_dep_if_needed(tags); end
|
||||
end
|
||||
end
|
||||
|
||||
def cvs_dep_if_needed(tags)
|
||||
Dependency.new("cvs", [*tags, :implicit])
|
||||
end
|
||||
|
||||
def xz_dep_if_needed(tags); end
|
||||
|
||||
def unzip_dep_if_needed(tags); end
|
||||
|
||||
def bzip2_dep_if_needed(tags); end
|
||||
end
|
||||
|
||||
DependencyCollector.prepend(OS::Mac::DependencyCollector)
|
||||
|
@ -1,86 +1,91 @@
|
||||
# typed: true # rubocop:disable Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "os/mac/xcode"
|
||||
|
||||
class DevelopmentTools
|
||||
class << self
|
||||
alias generic_locate locate
|
||||
undef installed?, default_compiler, curl_handles_most_https_certificates?,
|
||||
subversion_handles_most_https_certificates?
|
||||
module OS
|
||||
module Mac
|
||||
module DevelopmentTools
|
||||
extend T::Helpers
|
||||
|
||||
sig { params(tool: T.any(String, Symbol)).returns(T.nilable(Pathname)) }
|
||||
def locate(tool)
|
||||
(@locate ||= {}).fetch(tool) do |key|
|
||||
@locate[key] = if (located_tool = generic_locate(tool))
|
||||
located_tool
|
||||
else
|
||||
path = Utils.popen_read("/usr/bin/xcrun", "-no-cache", "-find", tool, err: :close).chomp
|
||||
Pathname.new(path) if File.executable?(path)
|
||||
requires_ancestor { ::DevelopmentTools }
|
||||
|
||||
sig { params(tool: T.any(String, Symbol)).returns(T.nilable(Pathname)) }
|
||||
def locate(tool)
|
||||
@locate ||= T.let({}, T.nilable(T::Hash[T.any(String, Symbol), Pathname]))
|
||||
@locate.fetch(tool) do |key|
|
||||
@locate[key] = if (located_tool = super(tool))
|
||||
located_tool
|
||||
else
|
||||
path = Utils.popen_read("/usr/bin/xcrun", "-no-cache", "-find", tool, err: :close).chomp
|
||||
Pathname.new(path) if File.executable?(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Checks if the user has any developer tools installed, either via Xcode
|
||||
# or the CLT. Convenient for guarding against formula builds when building
|
||||
# is impossible.
|
||||
sig { returns(T::Boolean) }
|
||||
def installed?
|
||||
MacOS::Xcode.installed? || MacOS::CLT.installed?
|
||||
end
|
||||
|
||||
sig { returns(Symbol) }
|
||||
def default_compiler
|
||||
:clang
|
||||
end
|
||||
|
||||
sig { returns(Version) }
|
||||
def ld64_version
|
||||
@ld64_version ||= begin
|
||||
json = Utils.popen_read("/usr/bin/ld", "-version_details")
|
||||
if $CHILD_STATUS.success?
|
||||
Version.parse(JSON.parse(json)["version"])
|
||||
else
|
||||
Version::NULL
|
||||
end
|
||||
# Checks if the user has any developer tools installed, either via Xcode
|
||||
# or the CLT. Convenient for guarding against formula builds when building
|
||||
# is impossible.
|
||||
sig { returns(T::Boolean) }
|
||||
def installed?
|
||||
MacOS::Xcode.installed? || MacOS::CLT.installed?
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def curl_handles_most_https_certificates?
|
||||
# The system Curl is too old for some modern HTTPS certificates on
|
||||
# older macOS versions.
|
||||
ENV["HOMEBREW_SYSTEM_CURL_TOO_OLD"].nil?
|
||||
end
|
||||
sig { returns(Symbol) }
|
||||
def default_compiler
|
||||
:clang
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def subversion_handles_most_https_certificates?
|
||||
# The system Subversion is too old for some HTTPS certificates on
|
||||
# older macOS versions.
|
||||
MacOS.version >= :sierra
|
||||
end
|
||||
sig { returns(Version) }
|
||||
def self.ld64_version
|
||||
@ld64_version ||= T.let(begin
|
||||
json = Utils.popen_read("/usr/bin/ld", "-version_details")
|
||||
if $CHILD_STATUS.success?
|
||||
Version.parse(JSON.parse(json)["version"])
|
||||
else
|
||||
Version::NULL
|
||||
end
|
||||
end, T.nilable(Version))
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def installation_instructions
|
||||
MacOS::CLT.installation_instructions
|
||||
end
|
||||
sig { returns(T::Boolean) }
|
||||
def curl_handles_most_https_certificates?
|
||||
# The system Curl is too old for some modern HTTPS certificates on
|
||||
# older macOS versions.
|
||||
ENV["HOMEBREW_SYSTEM_CURL_TOO_OLD"].nil?
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def custom_installation_instructions
|
||||
<<~EOS
|
||||
Install GNU's GCC:
|
||||
brew install gcc
|
||||
EOS
|
||||
end
|
||||
sig { returns(T::Boolean) }
|
||||
def subversion_handles_most_https_certificates?
|
||||
# The system Subversion is too old for some HTTPS certificates on
|
||||
# older macOS versions.
|
||||
MacOS.version >= :sierra
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[String, T.nilable(String)]) }
|
||||
def build_system_info
|
||||
build_info = {
|
||||
"xcode" => MacOS::Xcode.version.to_s.presence,
|
||||
"clt" => MacOS::CLT.version.to_s.presence,
|
||||
"preferred_perl" => MacOS.preferred_perl_version,
|
||||
}
|
||||
generic_build_system_info.merge build_info
|
||||
sig { returns(String) }
|
||||
def installation_instructions
|
||||
MacOS::CLT.installation_instructions
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def custom_installation_instructions
|
||||
<<~EOS
|
||||
Install GNU's GCC:
|
||||
brew install gcc
|
||||
EOS
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[String, T.nilable(String)]) }
|
||||
def build_system_info
|
||||
build_info = {
|
||||
"xcode" => MacOS::Xcode.version.to_s.presence,
|
||||
"clt" => MacOS::CLT.version.to_s.presence,
|
||||
"preferred_perl" => MacOS.preferred_perl_version,
|
||||
}
|
||||
super.merge build_info
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DevelopmentTools.singleton_class.prepend(OS::Mac::DevelopmentTools)
|
||||
|
@ -1,467 +1,471 @@
|
||||
# typed: true # rubocop:disable Sorbet/StrictSigil
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Homebrew
|
||||
module Diagnostic
|
||||
class Volumes
|
||||
def initialize
|
||||
@volumes = get_mounts
|
||||
end
|
||||
module OS
|
||||
module Mac
|
||||
module Diagnostic
|
||||
class Volumes
|
||||
def initialize
|
||||
@volumes = get_mounts
|
||||
end
|
||||
|
||||
def which(path)
|
||||
vols = get_mounts path
|
||||
def which(path)
|
||||
vols = get_mounts path
|
||||
|
||||
# no volume found
|
||||
return -1 if vols.empty?
|
||||
# no volume found
|
||||
return -1 if vols.empty?
|
||||
|
||||
vol_index = @volumes.index(vols[0])
|
||||
# volume not found in volume list
|
||||
return -1 if vol_index.nil?
|
||||
vol_index = @volumes.index(vols[0])
|
||||
# volume not found in volume list
|
||||
return -1 if vol_index.nil?
|
||||
|
||||
vol_index
|
||||
end
|
||||
vol_index
|
||||
end
|
||||
|
||||
def get_mounts(path = nil)
|
||||
vols = []
|
||||
# get the volume of path, if path is nil returns all volumes
|
||||
def get_mounts(path = nil)
|
||||
vols = []
|
||||
# get the volume of path, if path is nil returns all volumes
|
||||
|
||||
args = %w[/bin/df -P]
|
||||
args << path if path
|
||||
args = %w[/bin/df -P]
|
||||
args << path if path
|
||||
|
||||
Utils.popen_read(*args) do |io|
|
||||
io.each_line do |line|
|
||||
case line.chomp
|
||||
# regex matches: /dev/disk0s2 489562928 440803616 48247312 91% /
|
||||
when /^.+\s+[0-9]+\s+[0-9]+\s+[0-9]+\s+[0-9]{1,3}%\s+(.+)/
|
||||
vols << Regexp.last_match(1)
|
||||
Utils.popen_read(*args) do |io|
|
||||
io.each_line do |line|
|
||||
case line.chomp
|
||||
# regex matches: /dev/disk0s2 489562928 440803616 48247312 91% /
|
||||
when /^.+\s+[0-9]+\s+[0-9]+\s+[0-9]+\s+[0-9]{1,3}%\s+(.+)/
|
||||
vols << Regexp.last_match(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
vols
|
||||
end
|
||||
vols
|
||||
end
|
||||
end
|
||||
|
||||
class Checks
|
||||
undef fatal_preinstall_checks, fatal_build_from_source_checks,
|
||||
fatal_setup_build_environment_checks, supported_configuration_checks,
|
||||
build_from_source_checks
|
||||
|
||||
def fatal_preinstall_checks
|
||||
checks = %w[
|
||||
check_access_directories
|
||||
]
|
||||
|
||||
# We need the developer tools for `codesign`.
|
||||
checks << "check_for_installed_developer_tools" if Hardware::CPU.arm?
|
||||
|
||||
checks.freeze
|
||||
end
|
||||
|
||||
def fatal_build_from_source_checks
|
||||
%w[
|
||||
check_xcode_license_approved
|
||||
check_xcode_minimum_version
|
||||
check_clt_minimum_version
|
||||
check_if_xcode_needs_clt_installed
|
||||
check_if_supported_sdk_available
|
||||
check_broken_sdks
|
||||
].freeze
|
||||
end
|
||||
module Checks
|
||||
extend T::Helpers
|
||||
|
||||
def fatal_setup_build_environment_checks
|
||||
%w[
|
||||
check_xcode_minimum_version
|
||||
check_clt_minimum_version
|
||||
check_if_supported_sdk_available
|
||||
].freeze
|
||||
end
|
||||
requires_ancestor { Homebrew::Diagnostic::Checks }
|
||||
|
||||
def supported_configuration_checks
|
||||
%w[
|
||||
check_for_unsupported_macos
|
||||
].freeze
|
||||
end
|
||||
def fatal_preinstall_checks
|
||||
checks = %w[
|
||||
check_access_directories
|
||||
]
|
||||
|
||||
def build_from_source_checks
|
||||
%w[
|
||||
check_for_installed_developer_tools
|
||||
check_xcode_up_to_date
|
||||
check_clt_up_to_date
|
||||
].freeze
|
||||
end
|
||||
# We need the developer tools for `codesign`.
|
||||
checks << "check_for_installed_developer_tools" if ::Hardware::CPU.arm?
|
||||
|
||||
def check_for_non_prefixed_findutils
|
||||
findutils = Formula["findutils"]
|
||||
return unless findutils.any_version_installed?
|
||||
|
||||
gnubin = %W[#{findutils.opt_libexec}/gnubin #{findutils.libexec}/gnubin]
|
||||
default_names = Tab.for_name("findutils").with? "default-names"
|
||||
return if !default_names && !paths.intersect?(gnubin)
|
||||
|
||||
<<~EOS
|
||||
Putting non-prefixed findutils in your path can cause python builds to fail.
|
||||
EOS
|
||||
rescue FormulaUnavailableError
|
||||
nil
|
||||
end
|
||||
|
||||
def check_for_unsupported_macos
|
||||
return if Homebrew::EnvConfig.developer?
|
||||
return if ENV["HOMEBREW_INTEGRATION_TEST"]
|
||||
|
||||
who = +"We"
|
||||
what = if OS::Mac.version.prerelease?
|
||||
"pre-release version"
|
||||
elsif OS::Mac.version.outdated_release?
|
||||
who << " (and Apple)"
|
||||
"old version"
|
||||
checks.freeze
|
||||
end
|
||||
return if what.blank?
|
||||
|
||||
who.freeze
|
||||
def fatal_build_from_source_checks
|
||||
%w[
|
||||
check_xcode_license_approved
|
||||
check_xcode_minimum_version
|
||||
check_clt_minimum_version
|
||||
check_if_xcode_needs_clt_installed
|
||||
check_if_supported_sdk_available
|
||||
check_broken_sdks
|
||||
].freeze
|
||||
end
|
||||
|
||||
<<~EOS
|
||||
You are using macOS #{MacOS.version}.
|
||||
#{who} do not provide support for this #{what}.
|
||||
#{please_create_pull_requests(what)}
|
||||
EOS
|
||||
end
|
||||
def fatal_setup_build_environment_checks
|
||||
%w[
|
||||
check_xcode_minimum_version
|
||||
check_clt_minimum_version
|
||||
check_if_supported_sdk_available
|
||||
].freeze
|
||||
end
|
||||
|
||||
def check_xcode_up_to_date
|
||||
return unless MacOS::Xcode.outdated?
|
||||
def supported_configuration_checks
|
||||
%w[
|
||||
check_for_unsupported_macos
|
||||
].freeze
|
||||
end
|
||||
|
||||
# avoid duplicate very similar messages
|
||||
return if MacOS::Xcode.below_minimum_version?
|
||||
def build_from_source_checks
|
||||
%w[
|
||||
check_for_installed_developer_tools
|
||||
check_xcode_up_to_date
|
||||
check_clt_up_to_date
|
||||
].freeze
|
||||
end
|
||||
|
||||
# CI images are going to end up outdated so don't complain when
|
||||
# `brew test-bot` runs `brew doctor` in the CI for the Homebrew/brew
|
||||
# repository. This only needs to support whatever CI providers
|
||||
# Homebrew/brew is currently using.
|
||||
return if GitHub::Actions.env_set?
|
||||
def check_for_non_prefixed_findutils
|
||||
findutils = ::Formula["findutils"]
|
||||
return unless findutils.any_version_installed?
|
||||
|
||||
# With fake El Capitan for Portable Ruby, we are intentionally not using Xcode 8.
|
||||
# This is because we are not using the CLT and Xcode 8 has the 10.12 SDK.
|
||||
return if ENV["HOMEBREW_FAKE_MACOS"]
|
||||
gnubin = %W[#{findutils.opt_libexec}/gnubin #{findutils.libexec}/gnubin]
|
||||
default_names = Tab.for_name("findutils").with? "default-names"
|
||||
return if !default_names && !paths.intersect?(gnubin)
|
||||
|
||||
message = <<~EOS
|
||||
Your Xcode (#{MacOS::Xcode.version}) is outdated.
|
||||
Please update to Xcode #{MacOS::Xcode.latest_version} (or delete it).
|
||||
#{MacOS::Xcode.update_instructions}
|
||||
EOS
|
||||
<<~EOS
|
||||
Putting non-prefixed findutils in your path can cause python builds to fail.
|
||||
EOS
|
||||
rescue FormulaUnavailableError
|
||||
nil
|
||||
end
|
||||
|
||||
if OS::Mac.version.prerelease?
|
||||
current_path = Utils.popen_read("/usr/bin/xcode-select", "-p")
|
||||
message += <<~EOS
|
||||
If #{MacOS::Xcode.latest_version} is installed, you may need to:
|
||||
sudo xcode-select --switch /Applications/Xcode.app
|
||||
Current developer directory is:
|
||||
#{current_path}
|
||||
def check_for_unsupported_macos
|
||||
return if Homebrew::EnvConfig.developer?
|
||||
return if ENV["HOMEBREW_INTEGRATION_TEST"]
|
||||
|
||||
who = +"We"
|
||||
what = if OS::Mac.version.prerelease?
|
||||
"pre-release version"
|
||||
elsif OS::Mac.version.outdated_release?
|
||||
who << " (and Apple)"
|
||||
"old version"
|
||||
end
|
||||
return if what.blank?
|
||||
|
||||
who.freeze
|
||||
|
||||
<<~EOS
|
||||
You are using macOS #{MacOS.version}.
|
||||
#{who} do not provide support for this #{what}.
|
||||
#{please_create_pull_requests(what)}
|
||||
EOS
|
||||
end
|
||||
message
|
||||
end
|
||||
|
||||
def check_clt_up_to_date
|
||||
return unless MacOS::CLT.outdated?
|
||||
def check_xcode_up_to_date
|
||||
return unless MacOS::Xcode.outdated?
|
||||
|
||||
# avoid duplicate very similar messages
|
||||
return if MacOS::CLT.below_minimum_version?
|
||||
# avoid duplicate very similar messages
|
||||
return if MacOS::Xcode.below_minimum_version?
|
||||
|
||||
# CI images are going to end up outdated so don't complain when
|
||||
# `brew test-bot` runs `brew doctor` in the CI for the Homebrew/brew
|
||||
# repository. This only needs to support whatever CI providers
|
||||
# Homebrew/brew is currently using.
|
||||
return if GitHub::Actions.env_set?
|
||||
# CI images are going to end up outdated so don't complain when
|
||||
# `brew test-bot` runs `brew doctor` in the CI for the Homebrew/brew
|
||||
# repository. This only needs to support whatever CI providers
|
||||
# Homebrew/brew is currently using.
|
||||
return if GitHub::Actions.env_set?
|
||||
|
||||
<<~EOS
|
||||
A newer Command Line Tools release is available.
|
||||
#{MacOS::CLT.update_instructions}
|
||||
EOS
|
||||
end
|
||||
# With fake El Capitan for Portable Ruby, we are intentionally not using Xcode 8.
|
||||
# This is because we are not using the CLT and Xcode 8 has the 10.12 SDK.
|
||||
return if ENV["HOMEBREW_FAKE_MACOS"]
|
||||
|
||||
def check_xcode_minimum_version
|
||||
return unless MacOS::Xcode.below_minimum_version?
|
||||
message = <<~EOS
|
||||
Your Xcode (#{MacOS::Xcode.version}) is outdated.
|
||||
Please update to Xcode #{MacOS::Xcode.latest_version} (or delete it).
|
||||
#{MacOS::Xcode.update_instructions}
|
||||
EOS
|
||||
|
||||
xcode = MacOS::Xcode.version.to_s
|
||||
xcode += " => #{MacOS::Xcode.prefix}" unless MacOS::Xcode.default_prefix?
|
||||
|
||||
<<~EOS
|
||||
Your Xcode (#{xcode}) at #{MacOS::Xcode.bundle_path} is too outdated.
|
||||
Please update to Xcode #{MacOS::Xcode.latest_version} (or delete it).
|
||||
#{MacOS::Xcode.update_instructions}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_clt_minimum_version
|
||||
return unless MacOS::CLT.below_minimum_version?
|
||||
|
||||
<<~EOS
|
||||
Your Command Line Tools are too outdated.
|
||||
#{MacOS::CLT.update_instructions}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_if_xcode_needs_clt_installed
|
||||
return unless MacOS::Xcode.needs_clt_installed?
|
||||
|
||||
<<~EOS
|
||||
Xcode alone is not sufficient on #{MacOS.version.pretty_name}.
|
||||
#{DevelopmentTools.installation_instructions}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_xcode_prefix
|
||||
prefix = MacOS::Xcode.prefix
|
||||
return if prefix.nil?
|
||||
return unless prefix.to_s.include?(" ")
|
||||
|
||||
<<~EOS
|
||||
Xcode is installed to a directory with a space in the name.
|
||||
This will cause some formulae to fail to build.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_xcode_prefix_exists
|
||||
prefix = MacOS::Xcode.prefix
|
||||
return if prefix.nil? || prefix.exist?
|
||||
|
||||
<<~EOS
|
||||
The directory Xcode is reportedly installed to doesn't exist:
|
||||
#{prefix}
|
||||
You may need to `xcode-select` the proper path if you have moved Xcode.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_xcode_select_path
|
||||
return if MacOS::CLT.installed?
|
||||
return unless MacOS::Xcode.installed?
|
||||
return if File.file?("#{MacOS.active_developer_dir}/usr/bin/xcodebuild")
|
||||
|
||||
path = MacOS::Xcode.bundle_path
|
||||
path = "/Developer" if path.nil? || !path.directory?
|
||||
<<~EOS
|
||||
Your Xcode is configured with an invalid path.
|
||||
You should change it to the correct path:
|
||||
sudo xcode-select --switch #{path}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_xcode_license_approved
|
||||
# If the user installs Xcode-only, they have to approve the
|
||||
# license or no "xc*" tool will work.
|
||||
return unless `/usr/bin/xcrun clang 2>&1`.include?("license")
|
||||
return if $CHILD_STATUS.success?
|
||||
|
||||
<<~EOS
|
||||
You have not agreed to the Xcode license.
|
||||
Agree to the license by opening Xcode.app or running:
|
||||
sudo xcodebuild -license
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_filesystem_case_sensitive
|
||||
dirs_to_check = [
|
||||
HOMEBREW_PREFIX,
|
||||
HOMEBREW_REPOSITORY,
|
||||
HOMEBREW_CELLAR,
|
||||
HOMEBREW_TEMP,
|
||||
]
|
||||
case_sensitive_dirs = dirs_to_check.select do |dir|
|
||||
# We select the dir as being case-sensitive if either the UPCASED or the
|
||||
# downcased variant is missing.
|
||||
# Of course, on a case-insensitive fs, both exist because the os reports so.
|
||||
# In the rare situation when the user has indeed a downcased and an upcased
|
||||
# dir (e.g. /TMP and /tmp) this check falsely thinks it is case-insensitive
|
||||
# but we don't care because: 1. there is more than one dir checked, 2. the
|
||||
# check is not vital and 3. we would have to touch files otherwise.
|
||||
upcased = Pathname.new(dir.to_s.upcase)
|
||||
downcased = Pathname.new(dir.to_s.downcase)
|
||||
dir.exist? && !(upcased.exist? && downcased.exist?)
|
||||
end
|
||||
return if case_sensitive_dirs.empty?
|
||||
|
||||
volumes = Volumes.new
|
||||
case_sensitive_vols = case_sensitive_dirs.map do |case_sensitive_dir|
|
||||
volumes.get_mounts(case_sensitive_dir)
|
||||
end
|
||||
case_sensitive_vols.uniq!
|
||||
|
||||
<<~EOS
|
||||
The filesystem on #{case_sensitive_vols.join(",")} appears to be case-sensitive.
|
||||
The default macOS filesystem is case-insensitive. Please report any apparent problems.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_for_gettext
|
||||
find_relative_paths("lib/libgettextlib.dylib",
|
||||
"lib/libintl.dylib",
|
||||
"include/libintl.h")
|
||||
return if @found.empty?
|
||||
|
||||
# Our gettext formula will be caught by check_linked_keg_only_brews
|
||||
gettext = begin
|
||||
Formulary.factory("gettext")
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
if gettext&.linked_keg&.directory?
|
||||
allowlist = ["#{HOMEBREW_CELLAR}/gettext"]
|
||||
if Hardware::CPU.physical_cpu_arm64?
|
||||
allowlist += %W[
|
||||
#{HOMEBREW_MACOS_ARM_DEFAULT_PREFIX}/Cellar/gettext
|
||||
#{HOMEBREW_DEFAULT_PREFIX}/Cellar/gettext
|
||||
]
|
||||
end
|
||||
|
||||
return if @found.all? do |path|
|
||||
realpath = Pathname.new(path).realpath.to_s
|
||||
allowlist.any? { |rack| realpath.start_with?(rack) }
|
||||
end
|
||||
end
|
||||
|
||||
inject_file_list @found, <<~EOS
|
||||
gettext files detected at a system prefix.
|
||||
These files can cause compilation and link failures, especially if they
|
||||
are compiled with improper architectures. Consider removing these files:
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_for_iconv
|
||||
find_relative_paths("lib/libiconv.dylib", "include/iconv.h")
|
||||
return if @found.empty?
|
||||
|
||||
libiconv = begin
|
||||
Formulary.factory("libiconv")
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
if libiconv&.linked_keg&.directory?
|
||||
unless libiconv&.keg_only?
|
||||
<<~EOS
|
||||
A libiconv formula is installed and linked.
|
||||
This will break stuff. For serious. Unlink it.
|
||||
if OS::Mac.version.prerelease?
|
||||
current_path = Utils.popen_read("/usr/bin/xcode-select", "-p")
|
||||
message += <<~EOS
|
||||
If #{MacOS::Xcode.latest_version} is installed, you may need to:
|
||||
sudo xcode-select --switch /Applications/Xcode.app
|
||||
Current developer directory is:
|
||||
#{current_path}
|
||||
EOS
|
||||
end
|
||||
else
|
||||
inject_file_list @found, <<~EOS
|
||||
libiconv files detected at a system prefix other than /usr.
|
||||
Homebrew doesn't provide a libiconv formula and expects to link against
|
||||
the system version in /usr. libiconv in other prefixes can cause
|
||||
compile or link failure, especially if compiled with improper
|
||||
architectures. macOS itself never installs anything to /usr/local so
|
||||
it was either installed by a user or some other third party software.
|
||||
message
|
||||
end
|
||||
|
||||
tl;dr: delete these files:
|
||||
def check_clt_up_to_date
|
||||
return unless MacOS::CLT.outdated?
|
||||
|
||||
# avoid duplicate very similar messages
|
||||
return if MacOS::CLT.below_minimum_version?
|
||||
|
||||
# CI images are going to end up outdated so don't complain when
|
||||
# `brew test-bot` runs `brew doctor` in the CI for the Homebrew/brew
|
||||
# repository. This only needs to support whatever CI providers
|
||||
# Homebrew/brew is currently using.
|
||||
return if GitHub::Actions.env_set?
|
||||
|
||||
<<~EOS
|
||||
A newer Command Line Tools release is available.
|
||||
#{MacOS::CLT.update_instructions}
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
def check_for_multiple_volumes
|
||||
return unless HOMEBREW_CELLAR.exist?
|
||||
def check_xcode_minimum_version
|
||||
return unless MacOS::Xcode.below_minimum_version?
|
||||
|
||||
volumes = Volumes.new
|
||||
xcode = MacOS::Xcode.version.to_s
|
||||
xcode += " => #{MacOS::Xcode.prefix}" unless MacOS::Xcode.default_prefix?
|
||||
|
||||
# Find the volumes for the TMP folder & HOMEBREW_CELLAR
|
||||
real_cellar = HOMEBREW_CELLAR.realpath
|
||||
where_cellar = volumes.which real_cellar
|
||||
<<~EOS
|
||||
Your Xcode (#{xcode}) at #{MacOS::Xcode.bundle_path} is too outdated.
|
||||
Please update to Xcode #{MacOS::Xcode.latest_version} (or delete it).
|
||||
#{MacOS::Xcode.update_instructions}
|
||||
EOS
|
||||
end
|
||||
|
||||
begin
|
||||
tmp = Pathname.new(Dir.mktmpdir("doctor", HOMEBREW_TEMP))
|
||||
begin
|
||||
real_tmp = tmp.realpath.parent
|
||||
where_tmp = volumes.which real_tmp
|
||||
ensure
|
||||
Dir.delete tmp.to_s
|
||||
def check_clt_minimum_version
|
||||
return unless MacOS::CLT.below_minimum_version?
|
||||
|
||||
<<~EOS
|
||||
Your Command Line Tools are too outdated.
|
||||
#{MacOS::CLT.update_instructions}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_if_xcode_needs_clt_installed
|
||||
return unless MacOS::Xcode.needs_clt_installed?
|
||||
|
||||
<<~EOS
|
||||
Xcode alone is not sufficient on #{MacOS.version.pretty_name}.
|
||||
#{::DevelopmentTools.installation_instructions}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_xcode_prefix
|
||||
prefix = MacOS::Xcode.prefix
|
||||
return if prefix.nil?
|
||||
return unless prefix.to_s.include?(" ")
|
||||
|
||||
<<~EOS
|
||||
Xcode is installed to a directory with a space in the name.
|
||||
This will cause some formulae to fail to build.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_xcode_prefix_exists
|
||||
prefix = MacOS::Xcode.prefix
|
||||
return if prefix.nil? || prefix.exist?
|
||||
|
||||
<<~EOS
|
||||
The directory Xcode is reportedly installed to doesn't exist:
|
||||
#{prefix}
|
||||
You may need to `xcode-select` the proper path if you have moved Xcode.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_xcode_select_path
|
||||
return if MacOS::CLT.installed?
|
||||
return unless MacOS::Xcode.installed?
|
||||
return if File.file?("#{MacOS.active_developer_dir}/usr/bin/xcodebuild")
|
||||
|
||||
path = MacOS::Xcode.bundle_path
|
||||
path = "/Developer" if path.nil? || !path.directory?
|
||||
<<~EOS
|
||||
Your Xcode is configured with an invalid path.
|
||||
You should change it to the correct path:
|
||||
sudo xcode-select --switch #{path}
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_xcode_license_approved
|
||||
# If the user installs Xcode-only, they have to approve the
|
||||
# license or no "xc*" tool will work.
|
||||
return unless `/usr/bin/xcrun clang 2>&1`.include?("license")
|
||||
return if $CHILD_STATUS.success?
|
||||
|
||||
<<~EOS
|
||||
You have not agreed to the Xcode license.
|
||||
Agree to the license by opening Xcode.app or running:
|
||||
sudo xcodebuild -license
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_filesystem_case_sensitive
|
||||
dirs_to_check = [
|
||||
HOMEBREW_PREFIX,
|
||||
HOMEBREW_REPOSITORY,
|
||||
HOMEBREW_CELLAR,
|
||||
HOMEBREW_TEMP,
|
||||
]
|
||||
case_sensitive_dirs = dirs_to_check.select do |dir|
|
||||
# We select the dir as being case-sensitive if either the UPCASED or the
|
||||
# downcased variant is missing.
|
||||
# Of course, on a case-insensitive fs, both exist because the os reports so.
|
||||
# In the rare situation when the user has indeed a downcased and an upcased
|
||||
# dir (e.g. /TMP and /tmp) this check falsely thinks it is case-insensitive
|
||||
# but we don't care because: 1. there is more than one dir checked, 2. the
|
||||
# check is not vital and 3. we would have to touch files otherwise.
|
||||
upcased = Pathname.new(dir.to_s.upcase)
|
||||
downcased = Pathname.new(dir.to_s.downcase)
|
||||
dir.exist? && !(upcased.exist? && downcased.exist?)
|
||||
end
|
||||
rescue
|
||||
return
|
||||
return if case_sensitive_dirs.empty?
|
||||
|
||||
volumes = Volumes.new
|
||||
case_sensitive_vols = case_sensitive_dirs.map do |case_sensitive_dir|
|
||||
volumes.get_mounts(case_sensitive_dir)
|
||||
end
|
||||
case_sensitive_vols.uniq!
|
||||
|
||||
<<~EOS
|
||||
The filesystem on #{case_sensitive_vols.join(",")} appears to be case-sensitive.
|
||||
The default macOS filesystem is case-insensitive. Please report any apparent problems.
|
||||
EOS
|
||||
end
|
||||
|
||||
return if where_cellar == where_tmp
|
||||
def check_for_gettext
|
||||
find_relative_paths("lib/libgettextlib.dylib",
|
||||
"lib/libintl.dylib",
|
||||
"include/libintl.h")
|
||||
return if @found.empty?
|
||||
|
||||
<<~EOS
|
||||
Your Cellar and TEMP directories are on different volumes.
|
||||
macOS won't move relative symlinks across volumes unless the target file already
|
||||
exists. Brews known to be affected by this are Git and Narwhal.
|
||||
# Our gettext formula will be caught by check_linked_keg_only_brews
|
||||
gettext = begin
|
||||
Formulary.factory("gettext")
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
You should set the "HOMEBREW_TEMP" environment variable to a suitable
|
||||
directory on the same volume as your Cellar.
|
||||
EOS
|
||||
end
|
||||
if gettext&.linked_keg&.directory?
|
||||
allowlist = ["#{HOMEBREW_CELLAR}/gettext"]
|
||||
if ::Hardware::CPU.physical_cpu_arm64?
|
||||
allowlist += %W[
|
||||
#{HOMEBREW_MACOS_ARM_DEFAULT_PREFIX}/Cellar/gettext
|
||||
#{HOMEBREW_DEFAULT_PREFIX}/Cellar/gettext
|
||||
]
|
||||
end
|
||||
|
||||
def check_deprecated_caskroom_taps
|
||||
tapped_caskroom_taps = Tap.select { |t| t.user == "caskroom" || t.name == "phinze/cask" }
|
||||
.map(&:name)
|
||||
return if tapped_caskroom_taps.empty?
|
||||
return if @found.all? do |path|
|
||||
realpath = Pathname.new(path).realpath.to_s
|
||||
allowlist.any? { |rack| realpath.start_with?(rack) }
|
||||
end
|
||||
end
|
||||
|
||||
<<~EOS
|
||||
You have the following deprecated, cask taps tapped:
|
||||
#{tapped_caskroom_taps.join("\n ")}
|
||||
Untap them with `brew untap`.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_if_supported_sdk_available
|
||||
return unless DevelopmentTools.installed?
|
||||
return unless MacOS.sdk_root_needed?
|
||||
return if MacOS.sdk
|
||||
|
||||
locator = MacOS.sdk_locator
|
||||
|
||||
source = if locator.source == :clt
|
||||
return if MacOS::CLT.below_minimum_version? # Handled by other diagnostics.
|
||||
|
||||
update_instructions = MacOS::CLT.update_instructions
|
||||
"Command Line Tools (CLT)"
|
||||
else
|
||||
return if MacOS::Xcode.below_minimum_version? # Handled by other diagnostics.
|
||||
|
||||
update_instructions = MacOS::Xcode.update_instructions
|
||||
"Xcode"
|
||||
inject_file_list @found, <<~EOS
|
||||
gettext files detected at a system prefix.
|
||||
These files can cause compilation and link failures, especially if they
|
||||
are compiled with improper architectures. Consider removing these files:
|
||||
EOS
|
||||
end
|
||||
|
||||
<<~EOS
|
||||
Your #{source} does not support macOS #{MacOS.version}.
|
||||
It is either outdated or was modified.
|
||||
Please update your #{source} or delete it if no updates are available.
|
||||
#{update_instructions}
|
||||
EOS
|
||||
end
|
||||
def check_for_iconv
|
||||
find_relative_paths("lib/libiconv.dylib", "include/iconv.h")
|
||||
return if @found.empty?
|
||||
|
||||
# The CLT 10.x -> 11.x upgrade process on 10.14 contained a bug which broke the SDKs.
|
||||
# Notably, MacOSX10.14.sdk would indirectly symlink to MacOSX10.15.sdk.
|
||||
# This diagnostic was introduced to check for this and recommend a full reinstall.
|
||||
def check_broken_sdks
|
||||
locator = MacOS.sdk_locator
|
||||
libiconv = begin
|
||||
Formulary.factory("libiconv")
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
if libiconv&.linked_keg&.directory?
|
||||
unless libiconv&.keg_only?
|
||||
<<~EOS
|
||||
A libiconv formula is installed and linked.
|
||||
This will break stuff. For serious. Unlink it.
|
||||
EOS
|
||||
end
|
||||
else
|
||||
inject_file_list @found, <<~EOS
|
||||
libiconv files detected at a system prefix other than /usr.
|
||||
Homebrew doesn't provide a libiconv formula and expects to link against
|
||||
the system version in /usr. libiconv in other prefixes can cause
|
||||
compile or link failure, especially if compiled with improper
|
||||
architectures. macOS itself never installs anything to /usr/local so
|
||||
it was either installed by a user or some other third party software.
|
||||
|
||||
return if locator.all_sdks.all? do |sdk|
|
||||
path_version = sdk.path.basename.to_s[MacOS::SDK::VERSIONED_SDK_REGEX, 1]
|
||||
next true if path_version.blank?
|
||||
|
||||
sdk.version == MacOSVersion.new(path_version).strip_patch
|
||||
tl;dr: delete these files:
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
if locator.source == :clt
|
||||
source = "Command Line Tools (CLT)"
|
||||
path_to_remove = MacOS::CLT::PKG_PATH
|
||||
installation_instructions = MacOS::CLT.installation_instructions
|
||||
else
|
||||
source = "Xcode"
|
||||
path_to_remove = MacOS::Xcode.bundle_path
|
||||
installation_instructions = MacOS::Xcode.installation_instructions
|
||||
def check_for_multiple_volumes
|
||||
return unless HOMEBREW_CELLAR.exist?
|
||||
|
||||
volumes = Volumes.new
|
||||
|
||||
# Find the volumes for the TMP folder & HOMEBREW_CELLAR
|
||||
real_cellar = HOMEBREW_CELLAR.realpath
|
||||
where_cellar = volumes.which real_cellar
|
||||
|
||||
begin
|
||||
tmp = Pathname.new(Dir.mktmpdir("doctor", HOMEBREW_TEMP))
|
||||
begin
|
||||
real_tmp = tmp.realpath.parent
|
||||
where_tmp = volumes.which real_tmp
|
||||
ensure
|
||||
Dir.delete tmp.to_s
|
||||
end
|
||||
rescue
|
||||
return
|
||||
end
|
||||
|
||||
return if where_cellar == where_tmp
|
||||
|
||||
<<~EOS
|
||||
Your Cellar and TEMP directories are on different volumes.
|
||||
macOS won't move relative symlinks across volumes unless the target file already
|
||||
exists. Brews known to be affected by this are Git and Narwhal.
|
||||
|
||||
You should set the "HOMEBREW_TEMP" environment variable to a suitable
|
||||
directory on the same volume as your Cellar.
|
||||
EOS
|
||||
end
|
||||
|
||||
<<~EOS
|
||||
The contents of the SDKs in your #{source} installation do not match the SDK folder names.
|
||||
A clean reinstall of #{source} should fix this.
|
||||
def check_deprecated_caskroom_taps
|
||||
tapped_caskroom_taps = Tap.select { |t| t.user == "caskroom" || t.name == "phinze/cask" }
|
||||
.map(&:name)
|
||||
return if tapped_caskroom_taps.empty?
|
||||
|
||||
Remove the broken installation before reinstalling:
|
||||
sudo rm -rf #{path_to_remove}
|
||||
<<~EOS
|
||||
You have the following deprecated, cask taps tapped:
|
||||
#{tapped_caskroom_taps.join("\n ")}
|
||||
Untap them with `brew untap`.
|
||||
EOS
|
||||
end
|
||||
|
||||
#{installation_instructions}
|
||||
EOS
|
||||
def check_if_supported_sdk_available
|
||||
return unless ::DevelopmentTools.installed?
|
||||
return unless MacOS.sdk_root_needed?
|
||||
return if MacOS.sdk
|
||||
|
||||
locator = MacOS.sdk_locator
|
||||
|
||||
source = if locator.source == :clt
|
||||
return if MacOS::CLT.below_minimum_version? # Handled by other diagnostics.
|
||||
|
||||
update_instructions = MacOS::CLT.update_instructions
|
||||
"Command Line Tools (CLT)"
|
||||
else
|
||||
return if MacOS::Xcode.below_minimum_version? # Handled by other diagnostics.
|
||||
|
||||
update_instructions = MacOS::Xcode.update_instructions
|
||||
"Xcode"
|
||||
end
|
||||
|
||||
<<~EOS
|
||||
Your #{source} does not support macOS #{MacOS.version}.
|
||||
It is either outdated or was modified.
|
||||
Please update your #{source} or delete it if no updates are available.
|
||||
#{update_instructions}
|
||||
EOS
|
||||
end
|
||||
|
||||
# The CLT 10.x -> 11.x upgrade process on 10.14 contained a bug which broke the SDKs.
|
||||
# Notably, MacOSX10.14.sdk would indirectly symlink to MacOSX10.15.sdk.
|
||||
# This diagnostic was introduced to check for this and recommend a full reinstall.
|
||||
def check_broken_sdks
|
||||
locator = MacOS.sdk_locator
|
||||
|
||||
return if locator.all_sdks.all? do |sdk|
|
||||
path_version = sdk.path.basename.to_s[MacOS::SDK::VERSIONED_SDK_REGEX, 1]
|
||||
next true if path_version.blank?
|
||||
|
||||
sdk.version == MacOSVersion.new(path_version).strip_patch
|
||||
end
|
||||
|
||||
if locator.source == :clt
|
||||
source = "Command Line Tools (CLT)"
|
||||
path_to_remove = MacOS::CLT::PKG_PATH
|
||||
installation_instructions = MacOS::CLT.installation_instructions
|
||||
else
|
||||
source = "Xcode"
|
||||
path_to_remove = MacOS::Xcode.bundle_path
|
||||
installation_instructions = MacOS::Xcode.installation_instructions
|
||||
end
|
||||
|
||||
<<~EOS
|
||||
The contents of the SDKs in your #{source} installation do not match the SDK folder names.
|
||||
A clean reinstall of #{source} should fix this.
|
||||
|
||||
Remove the broken installation before reinstalling:
|
||||
sudo rm -rf #{path_to_remove}
|
||||
|
||||
#{installation_instructions}
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Homebrew::Diagnostic::Checks.prepend(OS::Mac::Diagnostic::Checks)
|
||||
|
@ -37,6 +37,6 @@ module SharedEnvExtension
|
||||
# This is supported starting Xcode 13, which ships ld64-711.
|
||||
# https://developer.apple.com/documentation/xcode-release-notes/xcode-13-release-notes
|
||||
# https://en.wikipedia.org/wiki/Xcode#Xcode_11.0_-_14.x_(since_SwiftUI_framework)_2
|
||||
DevelopmentTools.ld64_version >= 711
|
||||
OS::Mac::DevelopmentTools.ld64_version >= 711
|
||||
end
|
||||
end
|
||||
|
@ -1,117 +1,125 @@
|
||||
# typed: true # rubocop:disable Sorbet/StrictSigil
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Stdenv
|
||||
undef homebrew_extra_pkg_config_paths
|
||||
module OS
|
||||
module Mac
|
||||
module Stdenv
|
||||
extend T::Helpers
|
||||
|
||||
sig { returns(T::Array[Pathname]) }
|
||||
def homebrew_extra_pkg_config_paths
|
||||
[Pathname("#{HOMEBREW_LIBRARY}/Homebrew/os/mac/pkgconfig/#{MacOS.version}")]
|
||||
end
|
||||
private :homebrew_extra_pkg_config_paths
|
||||
requires_ancestor { ::Stdenv }
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula: T.nilable(Formula),
|
||||
cc: T.nilable(String),
|
||||
build_bottle: T.nilable(T::Boolean),
|
||||
bottle_arch: T.nilable(String),
|
||||
testing_formula: T::Boolean,
|
||||
debug_symbols: T.nilable(T::Boolean),
|
||||
).void
|
||||
}
|
||||
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false,
|
||||
debug_symbols: false)
|
||||
generic_setup_build_environment(formula:, cc:, build_bottle:, bottle_arch:,
|
||||
testing_formula:, debug_symbols:)
|
||||
sig { returns(T::Array[Pathname]) }
|
||||
def homebrew_extra_pkg_config_paths
|
||||
[Pathname("#{HOMEBREW_LIBRARY}/Homebrew/os/mac/pkgconfig/#{MacOS.version}")]
|
||||
end
|
||||
private :homebrew_extra_pkg_config_paths
|
||||
|
||||
append "LDFLAGS", "-Wl,-headerpad_max_install_names"
|
||||
sig {
|
||||
params(
|
||||
formula: T.nilable(::Formula),
|
||||
cc: T.nilable(String),
|
||||
build_bottle: T.nilable(T::Boolean),
|
||||
bottle_arch: T.nilable(String),
|
||||
testing_formula: T::Boolean,
|
||||
debug_symbols: T.nilable(T::Boolean),
|
||||
).void
|
||||
}
|
||||
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil,
|
||||
testing_formula: false, debug_symbols: false)
|
||||
generic_setup_build_environment(formula:, cc:, build_bottle:, bottle_arch:,
|
||||
testing_formula:, debug_symbols:)
|
||||
|
||||
# `sed` is strict and errors out when it encounters files with mixed character sets.
|
||||
delete("LC_ALL")
|
||||
self["LC_CTYPE"] = "C"
|
||||
append "LDFLAGS", "-Wl,-headerpad_max_install_names"
|
||||
|
||||
# Add `lib` and `include` etc. from the current `macosxsdk` to compiler flags:
|
||||
macosxsdk(formula: @formula, testing_formula:)
|
||||
# `sed` is strict and errors out when it encounters files with mixed character sets.
|
||||
delete("LC_ALL")
|
||||
self["LC_CTYPE"] = "C"
|
||||
|
||||
return unless MacOS::Xcode.without_clt?
|
||||
# Add `lib` and `include` etc. from the current `macosxsdk` to compiler flags:
|
||||
macosxsdk(formula: @formula, testing_formula:)
|
||||
|
||||
append_path "PATH", "#{MacOS::Xcode.prefix}/usr/bin"
|
||||
append_path "PATH", "#{MacOS::Xcode.toolchain_path}/usr/bin"
|
||||
end
|
||||
return unless MacOS::Xcode.without_clt?
|
||||
|
||||
def remove_macosxsdk(version = nil)
|
||||
# Clear all `lib` and `include` dirs from `CFLAGS`, `CPPFLAGS`, `LDFLAGS` that were
|
||||
# previously added by `macosxsdk`.
|
||||
remove_from_cflags(/ ?-mmacosx-version-min=\d+\.\d+/)
|
||||
delete("CPATH")
|
||||
remove "LDFLAGS", "-L#{HOMEBREW_PREFIX}/lib"
|
||||
append_path "PATH", "#{MacOS::Xcode.prefix}/usr/bin"
|
||||
append_path "PATH", "#{MacOS::Xcode.toolchain_path}/usr/bin"
|
||||
end
|
||||
|
||||
sdk = self["SDKROOT"] || MacOS.sdk_path_if_needed(version)
|
||||
return unless sdk
|
||||
def remove_macosxsdk(version = nil)
|
||||
# Clear all `lib` and `include` dirs from `CFLAGS`, `CPPFLAGS`, `LDFLAGS` that were
|
||||
# previously added by `macosxsdk`.
|
||||
remove_from_cflags(/ ?-mmacosx-version-min=\d+\.\d+/)
|
||||
delete("CPATH")
|
||||
remove "LDFLAGS", "-L#{HOMEBREW_PREFIX}/lib"
|
||||
|
||||
delete("SDKROOT")
|
||||
remove_from_cflags "-isysroot#{sdk}"
|
||||
remove "CPPFLAGS", "-isysroot#{sdk}"
|
||||
remove "LDFLAGS", "-isysroot#{sdk}"
|
||||
if HOMEBREW_PREFIX.to_s == "/usr/local"
|
||||
delete("CMAKE_PREFIX_PATH")
|
||||
else
|
||||
# It was set in `setup_build_environment`, so we have to restore it here.
|
||||
self["CMAKE_PREFIX_PATH"] = HOMEBREW_PREFIX.to_s
|
||||
sdk = self["SDKROOT"] || MacOS.sdk_path_if_needed(version)
|
||||
return unless sdk
|
||||
|
||||
delete("SDKROOT")
|
||||
remove_from_cflags "-isysroot#{sdk}"
|
||||
remove "CPPFLAGS", "-isysroot#{sdk}"
|
||||
remove "LDFLAGS", "-isysroot#{sdk}"
|
||||
if HOMEBREW_PREFIX.to_s == "/usr/local"
|
||||
delete("CMAKE_PREFIX_PATH")
|
||||
else
|
||||
# It was set in `setup_build_environment`, so we have to restore it here.
|
||||
self["CMAKE_PREFIX_PATH"] = HOMEBREW_PREFIX.to_s
|
||||
end
|
||||
remove "CMAKE_FRAMEWORK_PATH", "#{sdk}/System/Library/Frameworks"
|
||||
end
|
||||
|
||||
def macosxsdk(version = nil, formula: nil, testing_formula: false)
|
||||
# Sets all needed `lib` and `include` dirs to `CFLAGS`, `CPPFLAGS`, `LDFLAGS`.
|
||||
remove_macosxsdk
|
||||
min_version = version || MacOS.version
|
||||
append_to_cflags("-mmacosx-version-min=#{min_version}")
|
||||
self["CPATH"] = "#{HOMEBREW_PREFIX}/include"
|
||||
prepend "LDFLAGS", "-L#{HOMEBREW_PREFIX}/lib"
|
||||
|
||||
sdk = if formula
|
||||
MacOS.sdk_for_formula(formula, version, check_only_runtime_requirements: testing_formula)
|
||||
else
|
||||
MacOS.sdk(version)
|
||||
end
|
||||
return if !MacOS.sdk_root_needed? && sdk&.source != :xcode
|
||||
|
||||
Homebrew::Diagnostic.checks(:fatal_setup_build_environment_checks)
|
||||
sdk = sdk.path
|
||||
|
||||
# Extra setup to support Xcode 4.3+ without CLT.
|
||||
self["SDKROOT"] = sdk
|
||||
# Tell clang/gcc where system include's are:
|
||||
append_path "CPATH", "#{sdk}/usr/include"
|
||||
# The -isysroot is needed, too, because of the Frameworks
|
||||
append_to_cflags "-isysroot#{sdk}"
|
||||
append "CPPFLAGS", "-isysroot#{sdk}"
|
||||
# And the linker needs to find sdk/usr/lib
|
||||
append "LDFLAGS", "-isysroot#{sdk}"
|
||||
# Needed to build cmake itself and perhaps some cmake projects:
|
||||
append_path "CMAKE_PREFIX_PATH", "#{sdk}/usr"
|
||||
append_path "CMAKE_FRAMEWORK_PATH", "#{sdk}/System/Library/Frameworks"
|
||||
end
|
||||
|
||||
# Some configure scripts won't find libxml2 without help.
|
||||
# This is a no-op with macOS SDK 10.15.4 and later.
|
||||
def libxml2
|
||||
sdk = self["SDKROOT"] || MacOS.sdk_path_if_needed
|
||||
if !sdk
|
||||
append "CPPFLAGS", "-I/usr/include/libxml2"
|
||||
elsif !Pathname("#{sdk}/usr/include/libxml").directory?
|
||||
# Use the includes form the sdk
|
||||
append "CPPFLAGS", "-I#{sdk}/usr/include/libxml2"
|
||||
end
|
||||
end
|
||||
|
||||
def no_weak_imports
|
||||
append "LDFLAGS", "-Wl,-no_weak_imports" if no_weak_imports_support?
|
||||
end
|
||||
|
||||
def no_fixup_chains
|
||||
append "LDFLAGS", "-Wl,-no_fixup_chains" if no_fixup_chains_support?
|
||||
end
|
||||
end
|
||||
remove "CMAKE_FRAMEWORK_PATH", "#{sdk}/System/Library/Frameworks"
|
||||
end
|
||||
|
||||
def macosxsdk(version = nil, formula: nil, testing_formula: false)
|
||||
# Sets all needed `lib` and `include` dirs to `CFLAGS`, `CPPFLAGS`, `LDFLAGS`.
|
||||
remove_macosxsdk
|
||||
min_version = version || MacOS.version
|
||||
append_to_cflags("-mmacosx-version-min=#{min_version}")
|
||||
self["CPATH"] = "#{HOMEBREW_PREFIX}/include"
|
||||
prepend "LDFLAGS", "-L#{HOMEBREW_PREFIX}/lib"
|
||||
|
||||
sdk = if formula
|
||||
MacOS.sdk_for_formula(formula, version, check_only_runtime_requirements: testing_formula)
|
||||
else
|
||||
MacOS.sdk(version)
|
||||
end
|
||||
return if !MacOS.sdk_root_needed? && sdk&.source != :xcode
|
||||
|
||||
Homebrew::Diagnostic.checks(:fatal_setup_build_environment_checks)
|
||||
sdk = sdk.path
|
||||
|
||||
# Extra setup to support Xcode 4.3+ without CLT.
|
||||
self["SDKROOT"] = sdk
|
||||
# Tell clang/gcc where system include's are:
|
||||
append_path "CPATH", "#{sdk}/usr/include"
|
||||
# The -isysroot is needed, too, because of the Frameworks
|
||||
append_to_cflags "-isysroot#{sdk}"
|
||||
append "CPPFLAGS", "-isysroot#{sdk}"
|
||||
# And the linker needs to find sdk/usr/lib
|
||||
append "LDFLAGS", "-isysroot#{sdk}"
|
||||
# Needed to build cmake itself and perhaps some cmake projects:
|
||||
append_path "CMAKE_PREFIX_PATH", "#{sdk}/usr"
|
||||
append_path "CMAKE_FRAMEWORK_PATH", "#{sdk}/System/Library/Frameworks"
|
||||
end
|
||||
|
||||
# Some configure scripts won't find libxml2 without help.
|
||||
# This is a no-op with macOS SDK 10.15.4 and later.
|
||||
def libxml2
|
||||
sdk = self["SDKROOT"] || MacOS.sdk_path_if_needed
|
||||
if !sdk
|
||||
append "CPPFLAGS", "-I/usr/include/libxml2"
|
||||
elsif !Pathname("#{sdk}/usr/include/libxml").directory?
|
||||
# Use the includes form the sdk
|
||||
append "CPPFLAGS", "-I#{sdk}/usr/include/libxml2"
|
||||
end
|
||||
end
|
||||
|
||||
def no_weak_imports
|
||||
append "LDFLAGS", "-Wl,-no_weak_imports" if no_weak_imports_support?
|
||||
end
|
||||
|
||||
def no_fixup_chains
|
||||
append "LDFLAGS", "-Wl,-no_fixup_chains" if no_fixup_chains_support?
|
||||
end
|
||||
end
|
||||
|
||||
Stdenv.prepend(OS::Mac::Stdenv)
|
||||
|
@ -1,169 +1,175 @@
|
||||
# typed: true # rubocop:disable Sorbet/StrictSigil
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Superenv
|
||||
class << self
|
||||
# The location of Homebrew's shims on macOS.
|
||||
def shims_path
|
||||
HOMEBREW_SHIMS_PATH/"mac/super"
|
||||
end
|
||||
module OS
|
||||
module Mac
|
||||
module Superenv
|
||||
extend T::Helpers
|
||||
|
||||
undef bin
|
||||
requires_ancestor { ::Superenv }
|
||||
|
||||
def bin
|
||||
return unless DevelopmentTools.installed?
|
||||
module ClassMethods
|
||||
# The location of Homebrew's shims on macOS.
|
||||
def shims_path
|
||||
HOMEBREW_SHIMS_PATH/"mac/super"
|
||||
end
|
||||
|
||||
shims_path.realpath
|
||||
end
|
||||
end
|
||||
def bin
|
||||
return unless ::DevelopmentTools.installed?
|
||||
|
||||
undef homebrew_extra_pkg_config_paths,
|
||||
homebrew_extra_isystem_paths, homebrew_extra_library_paths,
|
||||
homebrew_extra_cmake_include_paths,
|
||||
homebrew_extra_cmake_library_paths,
|
||||
homebrew_extra_cmake_frameworks_paths,
|
||||
determine_cccfg
|
||||
|
||||
sig { returns(T::Array[Pathname]) }
|
||||
def homebrew_extra_pkg_config_paths
|
||||
[Pathname("/usr/lib/pkgconfig"), Pathname("#{HOMEBREW_LIBRARY}/Homebrew/os/mac/pkgconfig/#{MacOS.version}")]
|
||||
end
|
||||
private :homebrew_extra_pkg_config_paths
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def libxml2_include_needed?
|
||||
return false if deps.any? { |d| d.name == "libxml2" }
|
||||
return false if Pathname("#{self["HOMEBREW_SDKROOT"]}/usr/include/libxml").directory?
|
||||
|
||||
true
|
||||
end
|
||||
private :libxml2_include_needed?
|
||||
|
||||
def homebrew_extra_isystem_paths
|
||||
paths = []
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/usr/include/libxml2" if libxml2_include_needed?
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/usr/include/apache2" if MacOS::Xcode.without_clt?
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Headers"
|
||||
paths
|
||||
end
|
||||
|
||||
def homebrew_extra_library_paths
|
||||
paths = []
|
||||
if compiler == :llvm_clang
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/usr/lib"
|
||||
paths << Formula["llvm"].opt_lib.to_s
|
||||
end
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries"
|
||||
paths
|
||||
end
|
||||
|
||||
def homebrew_extra_cmake_include_paths
|
||||
paths = []
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/usr/include/libxml2" if libxml2_include_needed?
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/usr/include/apache2" if MacOS::Xcode.without_clt?
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Headers"
|
||||
paths
|
||||
end
|
||||
|
||||
def homebrew_extra_cmake_library_paths
|
||||
[Pathname("#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries")]
|
||||
end
|
||||
|
||||
def homebrew_extra_cmake_frameworks_paths
|
||||
paths = []
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks" if MacOS::Xcode.without_clt?
|
||||
paths
|
||||
end
|
||||
|
||||
def determine_cccfg
|
||||
s = +""
|
||||
# Fix issue with >= Mountain Lion apr-1-config having broken paths
|
||||
s << "a"
|
||||
s.freeze
|
||||
end
|
||||
|
||||
# @private
|
||||
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false,
|
||||
debug_symbols: false)
|
||||
sdk = formula ? MacOS.sdk_for_formula(formula) : MacOS.sdk
|
||||
is_xcode_sdk = sdk&.source == :xcode
|
||||
|
||||
if is_xcode_sdk || MacOS.sdk_root_needed?
|
||||
Homebrew::Diagnostic.checks(:fatal_setup_build_environment_checks)
|
||||
self["HOMEBREW_SDKROOT"] = sdk.path if sdk
|
||||
end
|
||||
|
||||
self["HOMEBREW_DEVELOPER_DIR"] = if is_xcode_sdk
|
||||
MacOS::Xcode.prefix.to_s
|
||||
else
|
||||
MacOS::CLT::PKG_PATH
|
||||
end
|
||||
|
||||
# This is a workaround for the missing `m4` in Xcode CLT 15.3, which was
|
||||
# reported in FB13679972. Apple has fixed this in Xcode CLT 16.0.
|
||||
# See https://github.com/Homebrew/homebrew-core/issues/165388
|
||||
if deps.none? { |d| d.name == "m4" } &&
|
||||
MacOS.active_developer_dir == MacOS::CLT::PKG_PATH &&
|
||||
!File.exist?("#{MacOS::CLT::PKG_PATH}/usr/bin/m4") &&
|
||||
(gm4 = DevelopmentTools.locate("gm4").to_s).present?
|
||||
self["M4"] = gm4
|
||||
end
|
||||
|
||||
generic_setup_build_environment(formula:, cc:, build_bottle:, bottle_arch:,
|
||||
testing_formula:, debug_symbols:)
|
||||
|
||||
# Filter out symbols known not to be defined since GNU Autotools can't
|
||||
# reliably figure this out with Xcode 8 and above.
|
||||
if MacOS.version == "10.12" && MacOS::Xcode.version >= "9.0"
|
||||
%w[fmemopen futimens open_memstream utimensat].each do |s|
|
||||
ENV["ac_cv_func_#{s}"] = "no"
|
||||
end
|
||||
elsif MacOS.version == "10.11" && MacOS::Xcode.version >= "8.0"
|
||||
%w[basename_r clock_getres clock_gettime clock_settime dirname_r
|
||||
getentropy mkostemp mkostemps timingsafe_bcmp].each do |s|
|
||||
ENV["ac_cv_func_#{s}"] = "no"
|
||||
shims_path.realpath
|
||||
end
|
||||
end
|
||||
|
||||
ENV["ac_cv_search_clock_gettime"] = "no"
|
||||
sig { returns(T::Array[Pathname]) }
|
||||
def homebrew_extra_pkg_config_paths
|
||||
[Pathname("/usr/lib/pkgconfig"), Pathname("#{HOMEBREW_LIBRARY}/Homebrew/os/mac/pkgconfig/#{MacOS.version}")]
|
||||
end
|
||||
private :homebrew_extra_pkg_config_paths
|
||||
|
||||
# works around libev.m4 unsetting ac_cv_func_clock_gettime
|
||||
ENV["ac_have_clock_syscall"] = "no"
|
||||
sig { returns(T::Boolean) }
|
||||
def libxml2_include_needed?
|
||||
return false if deps.any? { |d| d.name == "libxml2" }
|
||||
return false if Pathname("#{self["HOMEBREW_SDKROOT"]}/usr/include/libxml").directory?
|
||||
|
||||
true
|
||||
end
|
||||
private :libxml2_include_needed?
|
||||
|
||||
def homebrew_extra_isystem_paths
|
||||
paths = []
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/usr/include/libxml2" if libxml2_include_needed?
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/usr/include/apache2" if MacOS::Xcode.without_clt?
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Headers"
|
||||
paths
|
||||
end
|
||||
|
||||
def homebrew_extra_library_paths
|
||||
paths = []
|
||||
if compiler == :llvm_clang
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/usr/lib"
|
||||
paths << ::Formula["llvm"].opt_lib.to_s
|
||||
end
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries"
|
||||
paths
|
||||
end
|
||||
|
||||
def homebrew_extra_cmake_include_paths
|
||||
paths = []
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/usr/include/libxml2" if libxml2_include_needed?
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/usr/include/apache2" if MacOS::Xcode.without_clt?
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Headers"
|
||||
paths
|
||||
end
|
||||
|
||||
def homebrew_extra_cmake_library_paths
|
||||
[Pathname(
|
||||
"#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries",
|
||||
)]
|
||||
end
|
||||
|
||||
def homebrew_extra_cmake_frameworks_paths
|
||||
paths = []
|
||||
paths << "#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks" if MacOS::Xcode.without_clt?
|
||||
paths
|
||||
end
|
||||
|
||||
def determine_cccfg
|
||||
s = +""
|
||||
# Fix issue with >= Mountain Lion apr-1-config having broken paths
|
||||
s << "a"
|
||||
s.freeze
|
||||
end
|
||||
|
||||
# @private
|
||||
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil,
|
||||
testing_formula: false, debug_symbols: false)
|
||||
sdk = formula ? MacOS.sdk_for_formula(formula) : MacOS.sdk
|
||||
is_xcode_sdk = sdk&.source == :xcode
|
||||
|
||||
if is_xcode_sdk || MacOS.sdk_root_needed?
|
||||
Homebrew::Diagnostic.checks(:fatal_setup_build_environment_checks)
|
||||
self["HOMEBREW_SDKROOT"] = sdk.path if sdk
|
||||
end
|
||||
|
||||
self["HOMEBREW_DEVELOPER_DIR"] = if is_xcode_sdk
|
||||
MacOS::Xcode.prefix.to_s
|
||||
else
|
||||
MacOS::CLT::PKG_PATH
|
||||
end
|
||||
|
||||
# This is a workaround for the missing `m4` in Xcode CLT 15.3, which was
|
||||
# reported in FB13679972. Apple has fixed this in Xcode CLT 16.0.
|
||||
# See https://github.com/Homebrew/homebrew-core/issues/165388
|
||||
if deps.none? { |d| d.name == "m4" } &&
|
||||
MacOS.active_developer_dir == MacOS::CLT::PKG_PATH &&
|
||||
!File.exist?("#{MacOS::CLT::PKG_PATH}/usr/bin/m4") &&
|
||||
(gm4 = ::DevelopmentTools.locate("gm4").to_s).present?
|
||||
self["M4"] = gm4
|
||||
end
|
||||
|
||||
generic_setup_build_environment(formula:, cc:, build_bottle:, bottle_arch:,
|
||||
testing_formula:, debug_symbols:)
|
||||
|
||||
# Filter out symbols known not to be defined since GNU Autotools can't
|
||||
# reliably figure this out with Xcode 8 and above.
|
||||
if MacOS.version == "10.12" && MacOS::Xcode.version >= "9.0"
|
||||
%w[fmemopen futimens open_memstream utimensat].each do |s|
|
||||
ENV["ac_cv_func_#{s}"] = "no"
|
||||
end
|
||||
elsif MacOS.version == "10.11" && MacOS::Xcode.version >= "8.0"
|
||||
%w[basename_r clock_getres clock_gettime clock_settime dirname_r
|
||||
getentropy mkostemp mkostemps timingsafe_bcmp].each do |s|
|
||||
ENV["ac_cv_func_#{s}"] = "no"
|
||||
end
|
||||
|
||||
ENV["ac_cv_search_clock_gettime"] = "no"
|
||||
|
||||
# works around libev.m4 unsetting ac_cv_func_clock_gettime
|
||||
ENV["ac_have_clock_syscall"] = "no"
|
||||
end
|
||||
|
||||
# On macOS Sonoma (at least release candidate), iconv() is generally
|
||||
# present and working, but has a minor regression that defeats the
|
||||
# test implemented in gettext's configure script (and used by many
|
||||
# gettext dependents).
|
||||
ENV["am_cv_func_iconv_works"] = "yes" if MacOS.version == "14"
|
||||
|
||||
# The tools in /usr/bin proxy to the active developer directory.
|
||||
# This means we can use them for any combination of CLT and Xcode.
|
||||
self["HOMEBREW_PREFER_CLT_PROXIES"] = "1"
|
||||
|
||||
# Deterministic timestamping.
|
||||
# This can work on older Xcode versions, but they contain some bugs.
|
||||
# Notably, Xcode 10.2 fixes issues where ZERO_AR_DATE affected file mtimes.
|
||||
# Xcode 11.0 contains fixes for lldb reading things built with ZERO_AR_DATE.
|
||||
self["ZERO_AR_DATE"] = "1" if MacOS::Xcode.version >= "11.0" || MacOS::CLT.version >= "11.0"
|
||||
|
||||
# Pass `-no_fixup_chains` whenever the linker is invoked with `-undefined dynamic_lookup`.
|
||||
# See: https://github.com/python/cpython/issues/97524
|
||||
# https://github.com/pybind/pybind11/pull/4301
|
||||
no_fixup_chains
|
||||
|
||||
# Strip build prefixes from linker where supported, for deterministic builds.
|
||||
append_to_cccfg "o" if OS::Mac::DevelopmentTools.ld64_version >= 512
|
||||
|
||||
# Pass `-ld_classic` whenever the linker is invoked with `-dead_strip_dylibs`
|
||||
# on `ld` versions that don't properly handle that option.
|
||||
if OS::Mac::DevelopmentTools.ld64_version >= "1015.7" && OS::Mac::DevelopmentTools.ld64_version <= "1022.1"
|
||||
append_to_cccfg "c"
|
||||
end
|
||||
end
|
||||
|
||||
def no_weak_imports
|
||||
append_to_cccfg "w" if no_weak_imports_support?
|
||||
end
|
||||
|
||||
def no_fixup_chains
|
||||
append_to_cccfg "f" if no_fixup_chains_support?
|
||||
end
|
||||
end
|
||||
|
||||
# On macOS Sonoma (at least release candidate), iconv() is generally
|
||||
# present and working, but has a minor regression that defeats the
|
||||
# test implemented in gettext's configure script (and used by many
|
||||
# gettext dependents).
|
||||
ENV["am_cv_func_iconv_works"] = "yes" if MacOS.version == "14"
|
||||
|
||||
# The tools in /usr/bin proxy to the active developer directory.
|
||||
# This means we can use them for any combination of CLT and Xcode.
|
||||
self["HOMEBREW_PREFER_CLT_PROXIES"] = "1"
|
||||
|
||||
# Deterministic timestamping.
|
||||
# This can work on older Xcode versions, but they contain some bugs.
|
||||
# Notably, Xcode 10.2 fixes issues where ZERO_AR_DATE affected file mtimes.
|
||||
# Xcode 11.0 contains fixes for lldb reading things built with ZERO_AR_DATE.
|
||||
self["ZERO_AR_DATE"] = "1" if MacOS::Xcode.version >= "11.0" || MacOS::CLT.version >= "11.0"
|
||||
|
||||
# Pass `-no_fixup_chains` whenever the linker is invoked with `-undefined dynamic_lookup`.
|
||||
# See: https://github.com/python/cpython/issues/97524
|
||||
# https://github.com/pybind/pybind11/pull/4301
|
||||
no_fixup_chains
|
||||
|
||||
# Strip build prefixes from linker where supported, for deterministic builds.
|
||||
append_to_cccfg "o" if DevelopmentTools.ld64_version >= 512
|
||||
|
||||
# Pass `-ld_classic` whenever the linker is invoked with `-dead_strip_dylibs`
|
||||
# on `ld` versions that don't properly handle that option.
|
||||
append_to_cccfg "c" if DevelopmentTools.ld64_version >= "1015.7" && DevelopmentTools.ld64_version <= "1022.1"
|
||||
end
|
||||
|
||||
def no_weak_imports
|
||||
append_to_cccfg "w" if no_weak_imports_support?
|
||||
end
|
||||
|
||||
def no_fixup_chains
|
||||
append_to_cccfg "f" if no_fixup_chains_support?
|
||||
end
|
||||
end
|
||||
|
||||
Superenv.singleton_class.prepend(OS::Mac::Superenv::ClassMethods)
|
||||
Superenv.prepend(OS::Mac::Superenv)
|
||||
|
@ -97,7 +97,7 @@ module FormulaCellarChecks
|
||||
return unless formula.prefix.directory?
|
||||
return if formula.tap&.audit_exception(:flat_namespace_allowlist, formula.name)
|
||||
|
||||
keg = Keg.new(formula.prefix)
|
||||
keg = ::Keg.new(formula.prefix)
|
||||
flat_namespace_files = keg.mach_o_files.reject do |file|
|
||||
next true unless file.dylib?
|
||||
|
||||
|
@ -10,7 +10,7 @@ module OS
|
||||
|
||||
sig { params(formula: Formula).returns(T.nilable(T::Boolean)) }
|
||||
def fresh_install?(formula)
|
||||
!Homebrew::EnvConfig.developer? && !OS::Mac.version.outdated_release? &&
|
||||
!::Homebrew::EnvConfig.developer? && !OS::Mac.version.outdated_release? &&
|
||||
(!installed_as_dependency? || !formula.any_version_installed?)
|
||||
end
|
||||
end
|
||||
|
@ -3,175 +3,179 @@
|
||||
|
||||
require "macho"
|
||||
|
||||
module Hardware
|
||||
class CPU
|
||||
class << self
|
||||
undef type, family, features, sse4?
|
||||
module OS
|
||||
module Mac
|
||||
module Hardware
|
||||
module CPU
|
||||
extend T::Helpers
|
||||
|
||||
# These methods use info spewed out by sysctl.
|
||||
# Look in <mach/machine.h> for decoding info.
|
||||
def type
|
||||
case sysctl_int("hw.cputype")
|
||||
when MachO::Headers::CPU_TYPE_I386
|
||||
:intel
|
||||
when MachO::Headers::CPU_TYPE_ARM64
|
||||
:arm
|
||||
else
|
||||
:dunno
|
||||
# These methods use info spewed out by sysctl.
|
||||
# Look in <mach/machine.h> for decoding info.
|
||||
def type
|
||||
case sysctl_int("hw.cputype")
|
||||
when MachO::Headers::CPU_TYPE_I386
|
||||
:intel
|
||||
when MachO::Headers::CPU_TYPE_ARM64
|
||||
:arm
|
||||
else
|
||||
:dunno
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def family
|
||||
if arm?
|
||||
arm_family
|
||||
elsif intel?
|
||||
intel_family
|
||||
else
|
||||
:dunno
|
||||
def family
|
||||
if ::Hardware::CPU.arm?
|
||||
arm_family
|
||||
elsif ::Hardware::CPU.intel?
|
||||
intel_family
|
||||
else
|
||||
:dunno
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# True when running under an Intel-based shell via Rosetta 2 on an
|
||||
# Apple Silicon Mac. This can be detected via seeing if there's a
|
||||
# conflict between what `uname` reports and the underlying `sysctl` flags,
|
||||
# since the `sysctl` flags don't change behaviour under Rosetta 2.
|
||||
def in_rosetta2?
|
||||
sysctl_bool("sysctl.proc_translated")
|
||||
end
|
||||
|
||||
def features
|
||||
@features ||= sysctl_n(
|
||||
"machdep.cpu.features",
|
||||
"machdep.cpu.extfeatures",
|
||||
"machdep.cpu.leaf7_features",
|
||||
).split.map { |s| s.downcase.to_sym }
|
||||
end
|
||||
|
||||
def sse4?
|
||||
sysctl_bool("hw.optional.sse4_1")
|
||||
end
|
||||
|
||||
def extmodel
|
||||
sysctl_int("machdep.cpu.extmodel")
|
||||
end
|
||||
|
||||
def aes?
|
||||
sysctl_bool("hw.optional.aes")
|
||||
end
|
||||
|
||||
def altivec?
|
||||
sysctl_bool("hw.optional.altivec")
|
||||
end
|
||||
|
||||
def avx?
|
||||
sysctl_bool("hw.optional.avx1_0")
|
||||
end
|
||||
|
||||
def avx2?
|
||||
sysctl_bool("hw.optional.avx2_0")
|
||||
end
|
||||
|
||||
def sse3?
|
||||
sysctl_bool("hw.optional.sse3")
|
||||
end
|
||||
|
||||
def ssse3?
|
||||
sysctl_bool("hw.optional.supplementalsse3")
|
||||
end
|
||||
|
||||
def sse4_2?
|
||||
sysctl_bool("hw.optional.sse4_2")
|
||||
end
|
||||
|
||||
# NOTE: This is more reliable than checking `uname`. `sysctl` returns
|
||||
# the right answer even when running in Rosetta 2.
|
||||
def physical_cpu_arm64?
|
||||
sysctl_bool("hw.optional.arm64")
|
||||
end
|
||||
|
||||
def virtualized?
|
||||
sysctl_bool("kern.hv_vmm_present")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def arm_family
|
||||
case sysctl_int("hw.cpufamily")
|
||||
when 0x2c91a47e # ARMv8.0-A (Typhoon)
|
||||
:arm_typhoon
|
||||
when 0x92fb37c8 # ARMv8.0-A (Twister)
|
||||
:arm_twister
|
||||
when 0x67ceee93 # ARMv8.1-A (Hurricane, Zephyr)
|
||||
:arm_hurricane_zephyr
|
||||
when 0xe81e7ef6 # ARMv8.2-A (Monsoon, Mistral)
|
||||
:arm_monsoon_mistral
|
||||
when 0x07d34b9f # ARMv8.3-A (Vortex, Tempest)
|
||||
:arm_vortex_tempest
|
||||
when 0x462504d2 # ARMv8.4-A (Lightning, Thunder)
|
||||
:arm_lightning_thunder
|
||||
when 0x573b5eec, 0x1b588bb3 # ARMv8.4-A (Firestorm, Icestorm)
|
||||
:arm_firestorm_icestorm
|
||||
when 0xda33d83d # ARMv8.5-A (Blizzard, Avalanche)
|
||||
:arm_blizzard_avalanche
|
||||
when 0xfa33415e # ARMv8.6-A (M3, Ibiza)
|
||||
:arm_ibiza
|
||||
when 0x5f4dea93 # ARMv8.6-A (M3 Pro, Lobos)
|
||||
:arm_lobos
|
||||
when 0x72015832 # ARMv8.6-A (M3 Max, Palma)
|
||||
:arm_palma
|
||||
else
|
||||
# When adding new ARM CPU families, please also update
|
||||
# test/hardware/cpu_spec.rb to include the new families.
|
||||
:dunno
|
||||
# True when running under an Intel-based shell via Rosetta 2 on an
|
||||
# Apple Silicon Mac. This can be detected via seeing if there's a
|
||||
# conflict between what `uname` reports and the underlying `sysctl` flags,
|
||||
# since the `sysctl` flags don't change behaviour under Rosetta 2.
|
||||
def in_rosetta2?
|
||||
sysctl_bool("sysctl.proc_translated")
|
||||
end
|
||||
end
|
||||
|
||||
def intel_family(_family = nil, _cpu_model = nil)
|
||||
case sysctl_int("hw.cpufamily")
|
||||
when 0x73d67300 # Yonah: Core Solo/Duo
|
||||
:core
|
||||
when 0x426f69ef # Merom: Core 2 Duo
|
||||
:core2
|
||||
when 0x78ea4fbc # Penryn
|
||||
:penryn
|
||||
when 0x6b5a4cd2 # Nehalem
|
||||
:nehalem
|
||||
when 0x573b5eec # Westmere
|
||||
:westmere
|
||||
when 0x5490b78c # Sandy Bridge
|
||||
:sandybridge
|
||||
when 0x1f65e835 # Ivy Bridge
|
||||
:ivybridge
|
||||
when 0x10b282dc # Haswell
|
||||
:haswell
|
||||
when 0x582ed09c # Broadwell
|
||||
:broadwell
|
||||
when 0x37fc219f # Skylake
|
||||
:skylake
|
||||
when 0x0f817246 # Kaby Lake
|
||||
:kabylake
|
||||
when 0x38435547 # Ice Lake
|
||||
:icelake
|
||||
when 0x1cf8a03e # Comet Lake
|
||||
:cometlake
|
||||
else
|
||||
:dunno
|
||||
def features
|
||||
@features ||= sysctl_n(
|
||||
"machdep.cpu.features",
|
||||
"machdep.cpu.extfeatures",
|
||||
"machdep.cpu.leaf7_features",
|
||||
).split.map { |s| s.downcase.to_sym }
|
||||
end
|
||||
end
|
||||
|
||||
def sysctl_bool(key)
|
||||
sysctl_int(key) == 1
|
||||
end
|
||||
def sse4?
|
||||
sysctl_bool("hw.optional.sse4_1")
|
||||
end
|
||||
|
||||
def sysctl_int(key)
|
||||
sysctl_n(key).to_i & 0xffffffff
|
||||
end
|
||||
def extmodel
|
||||
sysctl_int("machdep.cpu.extmodel")
|
||||
end
|
||||
|
||||
def sysctl_n(*keys)
|
||||
(@properties ||= {}).fetch(keys) do
|
||||
@properties[keys] = Utils.popen_read("/usr/sbin/sysctl", "-n", *keys)
|
||||
def aes?
|
||||
sysctl_bool("hw.optional.aes")
|
||||
end
|
||||
|
||||
def altivec?
|
||||
sysctl_bool("hw.optional.altivec")
|
||||
end
|
||||
|
||||
def avx?
|
||||
sysctl_bool("hw.optional.avx1_0")
|
||||
end
|
||||
|
||||
def avx2?
|
||||
sysctl_bool("hw.optional.avx2_0")
|
||||
end
|
||||
|
||||
def sse3?
|
||||
sysctl_bool("hw.optional.sse3")
|
||||
end
|
||||
|
||||
def ssse3?
|
||||
sysctl_bool("hw.optional.supplementalsse3")
|
||||
end
|
||||
|
||||
def sse4_2?
|
||||
sysctl_bool("hw.optional.sse4_2")
|
||||
end
|
||||
|
||||
# NOTE: This is more reliable than checking `uname`. `sysctl` returns
|
||||
# the right answer even when running in Rosetta 2.
|
||||
def physical_cpu_arm64?
|
||||
sysctl_bool("hw.optional.arm64")
|
||||
end
|
||||
|
||||
def virtualized?
|
||||
sysctl_bool("kern.hv_vmm_present")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def arm_family
|
||||
case sysctl_int("hw.cpufamily")
|
||||
when 0x2c91a47e # ARMv8.0-A (Typhoon)
|
||||
:arm_typhoon
|
||||
when 0x92fb37c8 # ARMv8.0-A (Twister)
|
||||
:arm_twister
|
||||
when 0x67ceee93 # ARMv8.1-A (Hurricane, Zephyr)
|
||||
:arm_hurricane_zephyr
|
||||
when 0xe81e7ef6 # ARMv8.2-A (Monsoon, Mistral)
|
||||
:arm_monsoon_mistral
|
||||
when 0x07d34b9f # ARMv8.3-A (Vortex, Tempest)
|
||||
:arm_vortex_tempest
|
||||
when 0x462504d2 # ARMv8.4-A (Lightning, Thunder)
|
||||
:arm_lightning_thunder
|
||||
when 0x573b5eec, 0x1b588bb3 # ARMv8.4-A (Firestorm, Icestorm)
|
||||
:arm_firestorm_icestorm
|
||||
when 0xda33d83d # ARMv8.5-A (Blizzard, Avalanche)
|
||||
:arm_blizzard_avalanche
|
||||
when 0xfa33415e # ARMv8.6-A (M3, Ibiza)
|
||||
:arm_ibiza
|
||||
when 0x5f4dea93 # ARMv8.6-A (M3 Pro, Lobos)
|
||||
:arm_lobos
|
||||
when 0x72015832 # ARMv8.6-A (M3 Max, Palma)
|
||||
:arm_palma
|
||||
else
|
||||
# When adding new ARM CPU families, please also update
|
||||
# test/hardware/cpu_spec.rb to include the new families.
|
||||
:dunno
|
||||
end
|
||||
end
|
||||
|
||||
def intel_family(_family = nil, _cpu_model = nil)
|
||||
case sysctl_int("hw.cpufamily")
|
||||
when 0x73d67300 # Yonah: Core Solo/Duo
|
||||
:core
|
||||
when 0x426f69ef # Merom: Core 2 Duo
|
||||
:core2
|
||||
when 0x78ea4fbc # Penryn
|
||||
:penryn
|
||||
when 0x6b5a4cd2 # Nehalem
|
||||
:nehalem
|
||||
when 0x573b5eec # Westmere
|
||||
:westmere
|
||||
when 0x5490b78c # Sandy Bridge
|
||||
:sandybridge
|
||||
when 0x1f65e835 # Ivy Bridge
|
||||
:ivybridge
|
||||
when 0x10b282dc # Haswell
|
||||
:haswell
|
||||
when 0x582ed09c # Broadwell
|
||||
:broadwell
|
||||
when 0x37fc219f # Skylake
|
||||
:skylake
|
||||
when 0x0f817246 # Kaby Lake
|
||||
:kabylake
|
||||
when 0x38435547 # Ice Lake
|
||||
:icelake
|
||||
when 0x1cf8a03e # Comet Lake
|
||||
:cometlake
|
||||
else
|
||||
:dunno
|
||||
end
|
||||
end
|
||||
|
||||
def sysctl_bool(key)
|
||||
sysctl_int(key) == 1
|
||||
end
|
||||
|
||||
def sysctl_int(key)
|
||||
sysctl_n(key).to_i & 0xffffffff
|
||||
end
|
||||
|
||||
def sysctl_n(*keys)
|
||||
(@properties ||= {}).fetch(keys) do
|
||||
@properties[keys] = Utils.popen_read("/usr/sbin/sysctl", "-n", *keys)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Hardware::CPU.singleton_class.prepend(OS::Mac::Hardware::CPU)
|
||||
|
@ -23,101 +23,101 @@ class Keg
|
||||
GENERIC_MUST_BE_WRITABLE_DIRECTORIES +
|
||||
[HOMEBREW_PREFIX/"Frameworks"]
|
||||
).sort.uniq.freeze
|
||||
end
|
||||
|
||||
undef binary_executable_or_library_files
|
||||
module OS
|
||||
module Mac
|
||||
module Keg
|
||||
def binary_executable_or_library_files = mach_o_files
|
||||
|
||||
def binary_executable_or_library_files
|
||||
mach_o_files
|
||||
end
|
||||
def codesign_patched_binary(file)
|
||||
return if MacOS.version < :big_sur
|
||||
|
||||
def codesign_patched_binary(file)
|
||||
return if MacOS.version < :big_sur
|
||||
unless ::Hardware::CPU.arm?
|
||||
result = system_command("codesign", args: ["--verify", file], print_stderr: false)
|
||||
return unless result.stderr.match?(/invalid signature/i)
|
||||
end
|
||||
|
||||
unless Hardware::CPU.arm?
|
||||
result = system_command("codesign", args: ["--verify", file], print_stderr: false)
|
||||
return unless result.stderr.match?(/invalid signature/i)
|
||||
end
|
||||
odebug "Codesigning #{file}"
|
||||
prepare_codesign_writable_files(file) do
|
||||
# Use quiet_system to squash notifications about resigning binaries
|
||||
# which already have valid signatures.
|
||||
return if quiet_system("codesign", "--sign", "-", "--force",
|
||||
"--preserve-metadata=entitlements,requirements,flags,runtime",
|
||||
file)
|
||||
|
||||
odebug "Codesigning #{file}"
|
||||
prepare_codesign_writable_files(file) do
|
||||
# Use quiet_system to squash notifications about resigning binaries
|
||||
# which already have valid signatures.
|
||||
return if quiet_system("codesign", "--sign", "-", "--force",
|
||||
"--preserve-metadata=entitlements,requirements,flags,runtime",
|
||||
file)
|
||||
# If the codesigning fails, it may be a bug in Apple's codesign utility
|
||||
# A known workaround is to copy the file to another inode, then move it back
|
||||
# erasing the previous file. Then sign again.
|
||||
#
|
||||
# TODO: remove this once the bug in Apple's codesign utility is fixed
|
||||
Dir::Tmpname.create("workaround") do |tmppath|
|
||||
FileUtils.cp file, tmppath
|
||||
FileUtils.mv tmppath, file, force: true
|
||||
end
|
||||
|
||||
# If the codesigning fails, it may be a bug in Apple's codesign utility
|
||||
# A known workaround is to copy the file to another inode, then move it back
|
||||
# erasing the previous file. Then sign again.
|
||||
#
|
||||
# TODO: remove this once the bug in Apple's codesign utility is fixed
|
||||
Dir::Tmpname.create("workaround") do |tmppath|
|
||||
FileUtils.cp file, tmppath
|
||||
FileUtils.mv tmppath, file, force: true
|
||||
# Try signing again
|
||||
odebug "Codesigning (2nd try) #{file}"
|
||||
result = system_command("codesign", args: [
|
||||
"--sign", "-", "--force",
|
||||
"--preserve-metadata=entitlements,requirements,flags,runtime",
|
||||
file
|
||||
], print_stderr: false)
|
||||
return if result.success?
|
||||
|
||||
# If it fails again, error out
|
||||
onoe <<~EOS
|
||||
Failed applying an ad-hoc signature to #{file}:
|
||||
#{result.stderr}
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
# Try signing again
|
||||
odebug "Codesigning (2nd try) #{file}"
|
||||
result = system_command("codesign", args: [
|
||||
"--sign", "-", "--force",
|
||||
"--preserve-metadata=entitlements,requirements,flags,runtime",
|
||||
file
|
||||
], print_stderr: false)
|
||||
return if result.success?
|
||||
def prepare_codesign_writable_files(file)
|
||||
result = system_command("codesign", args: [
|
||||
"--display", "--file-list", "-", file
|
||||
], print_stderr: false)
|
||||
return unless result.success?
|
||||
|
||||
# If it fails again, error out
|
||||
onoe <<~EOS
|
||||
Failed applying an ad-hoc signature to #{file}:
|
||||
#{result.stderr}
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_codesign_writable_files(file)
|
||||
result = system_command("codesign", args: [
|
||||
"--display", "--file-list", "-", file
|
||||
], print_stderr: false)
|
||||
return unless result.success?
|
||||
|
||||
files = result.stdout.lines.map { |f| Pathname(f.chomp) }
|
||||
saved_perms = {}
|
||||
files.each do |f|
|
||||
unless f.writable?
|
||||
saved_perms[f] = f.stat.mode
|
||||
FileUtils.chmod "u+rw", f.to_path
|
||||
files = result.stdout.lines.map { |f| Pathname(f.chomp) }
|
||||
saved_perms = {}
|
||||
files.each do |f|
|
||||
unless f.writable?
|
||||
saved_perms[f] = f.stat.mode
|
||||
FileUtils.chmod "u+rw", f.to_path
|
||||
end
|
||||
end
|
||||
yield
|
||||
ensure
|
||||
saved_perms&.each do |f, p|
|
||||
f.chmod p if p
|
||||
end
|
||||
end
|
||||
end
|
||||
yield
|
||||
ensure
|
||||
saved_perms&.each do |f, p|
|
||||
f.chmod p if p
|
||||
end
|
||||
end
|
||||
|
||||
undef prepare_debug_symbols
|
||||
def prepare_debug_symbols
|
||||
binary_executable_or_library_files.each do |file|
|
||||
odebug "Extracting symbols #{file}"
|
||||
|
||||
def prepare_debug_symbols
|
||||
binary_executable_or_library_files.each do |file|
|
||||
odebug "Extracting symbols #{file}"
|
||||
result = system_command("dsymutil", args: [file], print_stderr: false)
|
||||
next if result.success?
|
||||
|
||||
result = system_command("dsymutil", args: [file], print_stderr: false)
|
||||
next if result.success?
|
||||
# If it fails again, error out
|
||||
ofail <<~EOS
|
||||
Failed to extract symbols from #{file}:
|
||||
#{result.stderr}
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
# If it fails again, error out
|
||||
ofail <<~EOS
|
||||
Failed to extract symbols from #{file}:
|
||||
#{result.stderr}
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
undef consistent_reproducible_symlink_permissions!
|
||||
|
||||
# Needed to make symlink permissions consistent on macOS and Linux for
|
||||
# reproducible bottles.
|
||||
def consistent_reproducible_symlink_permissions!
|
||||
path.find do |file|
|
||||
File.lchmod 0777, file if file.symlink?
|
||||
# Needed to make symlink permissions consistent on macOS and Linux for
|
||||
# reproducible bottles.
|
||||
def consistent_reproducible_symlink_permissions!
|
||||
path.find do |file|
|
||||
File.lchmod 0777, file if file.symlink?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Keg.prepend(OS::Mac::Keg)
|
||||
|
@ -1,255 +1,262 @@
|
||||
# typed: true # rubocop:disable Sorbet/StrictSigil
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Keg
|
||||
class << self
|
||||
undef file_linked_libraries
|
||||
module OS
|
||||
module Mac
|
||||
module Keg
|
||||
extend T::Helpers
|
||||
|
||||
def file_linked_libraries(file, string)
|
||||
# Check dynamic library linkage. Importantly, do not perform for static
|
||||
# libraries, which will falsely report "linkage" to themselves.
|
||||
if file.mach_o_executable? || file.dylib? || file.mach_o_bundle?
|
||||
file.dynamically_linked_libraries.select { |lib| lib.include? string }
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
requires_ancestor { ::Keg }
|
||||
|
||||
undef relocate_dynamic_linkage
|
||||
|
||||
def relocate_dynamic_linkage(relocation)
|
||||
mach_o_files.each do |file|
|
||||
file.ensure_writable do
|
||||
modified = T.let(false, T::Boolean)
|
||||
needs_codesigning = T.let(false, T::Boolean)
|
||||
|
||||
if file.dylib?
|
||||
id = relocated_name_for(file.dylib_id, relocation)
|
||||
modified = change_dylib_id(id, file)
|
||||
needs_codesigning ||= modified
|
||||
end
|
||||
|
||||
each_linkage_for(file, :dynamically_linked_libraries) do |old_name|
|
||||
new_name = relocated_name_for(old_name, relocation)
|
||||
modified = change_install_name(old_name, new_name, file) if new_name
|
||||
needs_codesigning ||= modified
|
||||
end
|
||||
|
||||
each_linkage_for(file, :rpaths) do |old_name|
|
||||
new_name = relocated_name_for(old_name, relocation)
|
||||
modified = change_rpath(old_name, new_name, file) if new_name
|
||||
needs_codesigning ||= modified
|
||||
end
|
||||
|
||||
# codesign the file if needed
|
||||
codesign_patched_binary(file) if needs_codesigning
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def fix_dynamic_linkage
|
||||
mach_o_files.each do |file|
|
||||
file.ensure_writable do
|
||||
modified = T.let(false, T::Boolean)
|
||||
needs_codesigning = T.let(false, T::Boolean)
|
||||
|
||||
modified = change_dylib_id(dylib_id_for(file), file) if file.dylib?
|
||||
needs_codesigning ||= modified
|
||||
|
||||
each_linkage_for(file, :dynamically_linked_libraries) do |bad_name|
|
||||
# Don't fix absolute paths unless they are rooted in the build directory.
|
||||
new_name = if bad_name.start_with?("/") && !rooted_in_build_directory?(bad_name)
|
||||
bad_name
|
||||
module ClassMethods
|
||||
def file_linked_libraries(file, string)
|
||||
# Check dynamic library linkage. Importantly, do not perform for static
|
||||
# libraries, which will falsely report "linkage" to themselves.
|
||||
if file.mach_o_executable? || file.dylib? || file.mach_o_bundle?
|
||||
file.dynamically_linked_libraries.select { |lib| lib.include? string }
|
||||
else
|
||||
fixed_name(file, bad_name)
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def relocate_dynamic_linkage(relocation)
|
||||
mach_o_files.each do |file|
|
||||
file.ensure_writable do
|
||||
modified = T.let(false, T::Boolean)
|
||||
needs_codesigning = T.let(false, T::Boolean)
|
||||
|
||||
if file.dylib?
|
||||
id = relocated_name_for(file.dylib_id, relocation)
|
||||
modified = change_dylib_id(id, file)
|
||||
needs_codesigning ||= modified
|
||||
end
|
||||
|
||||
each_linkage_for(file, :dynamically_linked_libraries) do |old_name|
|
||||
new_name = relocated_name_for(old_name, relocation)
|
||||
modified = change_install_name(old_name, new_name, file) if new_name
|
||||
needs_codesigning ||= modified
|
||||
end
|
||||
|
||||
each_linkage_for(file, :rpaths) do |old_name|
|
||||
new_name = relocated_name_for(old_name, relocation)
|
||||
modified = change_rpath(old_name, new_name, file) if new_name
|
||||
needs_codesigning ||= modified
|
||||
end
|
||||
|
||||
# codesign the file if needed
|
||||
codesign_patched_binary(file) if needs_codesigning
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def fix_dynamic_linkage
|
||||
mach_o_files.each do |file|
|
||||
file.ensure_writable do
|
||||
modified = T.let(false, T::Boolean)
|
||||
needs_codesigning = T.let(false, T::Boolean)
|
||||
|
||||
modified = change_dylib_id(dylib_id_for(file), file) if file.dylib?
|
||||
needs_codesigning ||= modified
|
||||
|
||||
each_linkage_for(file, :dynamically_linked_libraries) do |bad_name|
|
||||
# Don't fix absolute paths unless they are rooted in the build directory.
|
||||
new_name = if bad_name.start_with?("/") && !rooted_in_build_directory?(bad_name)
|
||||
bad_name
|
||||
else
|
||||
fixed_name(file, bad_name)
|
||||
end
|
||||
loader_name = loader_name_for(file, new_name)
|
||||
modified = change_install_name(bad_name, loader_name, file) if loader_name != bad_name
|
||||
needs_codesigning ||= modified
|
||||
end
|
||||
|
||||
each_linkage_for(file, :rpaths) do |bad_name|
|
||||
new_name = opt_name_for(bad_name)
|
||||
loader_name = loader_name_for(file, new_name)
|
||||
next if loader_name == bad_name
|
||||
|
||||
modified = change_rpath(bad_name, loader_name, file)
|
||||
needs_codesigning ||= modified
|
||||
end
|
||||
|
||||
# Strip duplicate rpaths and rpaths rooted in the build directory.
|
||||
# We do this separately from the rpath relocation above to avoid
|
||||
# failing to relocate an rpath whose variable duplicate we deleted.
|
||||
each_linkage_for(file, :rpaths, resolve_variable_references: true) do |bad_name|
|
||||
next if !rooted_in_build_directory?(bad_name) && file.rpaths.count(bad_name) == 1
|
||||
|
||||
modified = delete_rpath(bad_name, file)
|
||||
needs_codesigning ||= modified
|
||||
end
|
||||
|
||||
# codesign the file if needed
|
||||
codesign_patched_binary(file) if needs_codesigning
|
||||
end
|
||||
loader_name = loader_name_for(file, new_name)
|
||||
modified = change_install_name(bad_name, loader_name, file) if loader_name != bad_name
|
||||
needs_codesigning ||= modified
|
||||
end
|
||||
|
||||
each_linkage_for(file, :rpaths) do |bad_name|
|
||||
new_name = opt_name_for(bad_name)
|
||||
loader_name = loader_name_for(file, new_name)
|
||||
next if loader_name == bad_name
|
||||
generic_fix_dynamic_linkage
|
||||
end
|
||||
|
||||
modified = change_rpath(bad_name, loader_name, file)
|
||||
needs_codesigning ||= modified
|
||||
def loader_name_for(file, target)
|
||||
# Use @loader_path-relative install names for other Homebrew-installed binaries.
|
||||
if ENV["HOMEBREW_RELOCATABLE_INSTALL_NAMES"] && target.start_with?(HOMEBREW_PREFIX)
|
||||
dylib_suffix = find_dylib_suffix_from(target)
|
||||
target_dir = Pathname.new(target.delete_suffix(dylib_suffix)).cleanpath
|
||||
|
||||
"@loader_path/#{target_dir.relative_path_from(file.dirname)/dylib_suffix}"
|
||||
else
|
||||
target
|
||||
end
|
||||
end
|
||||
|
||||
# If file is a dylib or bundle itself, look for the dylib named by
|
||||
# bad_name relative to the lib directory, so that we can skip the more
|
||||
# expensive recursive search if possible.
|
||||
def fixed_name(file, bad_name)
|
||||
if bad_name.start_with? ::Keg::PREFIX_PLACEHOLDER
|
||||
bad_name.sub(::Keg::PREFIX_PLACEHOLDER, HOMEBREW_PREFIX)
|
||||
elsif bad_name.start_with? ::Keg::CELLAR_PLACEHOLDER
|
||||
bad_name.sub(::Keg::CELLAR_PLACEHOLDER, HOMEBREW_CELLAR)
|
||||
elsif (file.dylib? || file.mach_o_bundle?) && (file.dirname/bad_name).exist?
|
||||
"@loader_path/#{bad_name}"
|
||||
elsif file.mach_o_executable? && (lib/bad_name).exist?
|
||||
"#{lib}/#{bad_name}"
|
||||
elsif file.mach_o_executable? && (libexec/"lib"/bad_name).exist?
|
||||
"#{libexec}/lib/#{bad_name}"
|
||||
elsif (abs_name = find_dylib(bad_name)) && abs_name.exist?
|
||||
abs_name.to_s
|
||||
else
|
||||
opoo "Could not fix #{bad_name} in #{file}"
|
||||
bad_name
|
||||
end
|
||||
end
|
||||
|
||||
VARIABLE_REFERENCE_RX = /^@(loader_|executable_|r)path/
|
||||
|
||||
def each_linkage_for(file, linkage_type, resolve_variable_references: false, &block)
|
||||
file.public_send(linkage_type, resolve_variable_references:)
|
||||
.grep_v(VARIABLE_REFERENCE_RX)
|
||||
.each(&block)
|
||||
end
|
||||
|
||||
def dylib_id_for(file)
|
||||
# The new dylib ID should have the same basename as the old dylib ID, not
|
||||
# the basename of the file itself.
|
||||
basename = File.basename(file.dylib_id)
|
||||
relative_dirname = file.dirname.relative_path_from(path)
|
||||
(opt_record/relative_dirname/basename).to_s
|
||||
end
|
||||
|
||||
def relocated_name_for(old_name, relocation)
|
||||
old_prefix, new_prefix = relocation.replacement_pair_for(:prefix)
|
||||
old_cellar, new_cellar = relocation.replacement_pair_for(:cellar)
|
||||
|
||||
if old_name.start_with? old_cellar
|
||||
old_name.sub(old_cellar, new_cellar)
|
||||
elsif old_name.start_with? old_prefix
|
||||
old_name.sub(old_prefix, new_prefix)
|
||||
end
|
||||
end
|
||||
|
||||
# Matches framework references like `XXX.framework/Versions/YYY/XXX` and
|
||||
# `XXX.framework/XXX`, both with or without a slash-delimited prefix.
|
||||
FRAMEWORK_RX = %r{(?:^|/)(([^/]+)\.framework/(?:Versions/[^/]+/)?\2)$}
|
||||
|
||||
def find_dylib_suffix_from(bad_name)
|
||||
if (framework = bad_name.match(FRAMEWORK_RX))
|
||||
framework[1]
|
||||
else
|
||||
File.basename(bad_name)
|
||||
end
|
||||
end
|
||||
|
||||
def find_dylib(bad_name)
|
||||
return unless lib.directory?
|
||||
|
||||
suffix = "/#{find_dylib_suffix_from(bad_name)}"
|
||||
lib.find { |pn| break pn if pn.to_s.end_with?(suffix) }
|
||||
end
|
||||
|
||||
def mach_o_files
|
||||
hardlinks = Set.new
|
||||
mach_o_files = []
|
||||
path.find do |pn|
|
||||
next if pn.symlink? || pn.directory?
|
||||
next if !pn.dylib? && !pn.mach_o_bundle? && !pn.mach_o_executable?
|
||||
# if we've already processed a file, ignore its hardlinks (which have the same dev ID and inode)
|
||||
# this prevents relocations from being performed on a binary more than once
|
||||
next unless hardlinks.add? [pn.stat.dev, pn.stat.ino]
|
||||
|
||||
mach_o_files << pn
|
||||
end
|
||||
|
||||
# Strip duplicate rpaths and rpaths rooted in the build directory.
|
||||
# We do this separately from the rpath relocation above to avoid
|
||||
# failing to relocate an rpath whose variable duplicate we deleted.
|
||||
each_linkage_for(file, :rpaths, resolve_variable_references: true) do |bad_name|
|
||||
next if !rooted_in_build_directory?(bad_name) && file.rpaths.count(bad_name) == 1
|
||||
mach_o_files
|
||||
end
|
||||
|
||||
modified = delete_rpath(bad_name, file)
|
||||
needs_codesigning ||= modified
|
||||
def prepare_relocation_to_locations
|
||||
relocation = generic_prepare_relocation_to_locations
|
||||
|
||||
brewed_perl = runtime_dependencies&.any? { |dep| dep["full_name"] == "perl" && dep["declared_directly"] }
|
||||
perl_path = if brewed_perl || name == "perl"
|
||||
"#{HOMEBREW_PREFIX}/opt/perl/bin/perl"
|
||||
elsif tab.built_on.present?
|
||||
perl_path = "/usr/bin/perl#{tab.built_on["preferred_perl"]}"
|
||||
|
||||
# For `:all` bottles, we could have built this bottle with a Perl we don't have.
|
||||
# Such bottles typically don't have strict version requirements.
|
||||
perl_path = "/usr/bin/perl#{MacOS.preferred_perl_version}" unless File.exist?(perl_path)
|
||||
|
||||
perl_path
|
||||
else
|
||||
"/usr/bin/perl#{MacOS.preferred_perl_version}"
|
||||
end
|
||||
relocation.add_replacement_pair(:perl, ::Keg::PERL_PLACEHOLDER, perl_path)
|
||||
|
||||
if (openjdk = openjdk_dep_name_if_applicable)
|
||||
openjdk_path = HOMEBREW_PREFIX/"opt"/openjdk/"libexec/openjdk.jdk/Contents/Home"
|
||||
relocation.add_replacement_pair(:java, ::Keg::JAVA_PLACEHOLDER, openjdk_path.to_s)
|
||||
end
|
||||
|
||||
# codesign the file if needed
|
||||
codesign_patched_binary(file) if needs_codesigning
|
||||
relocation
|
||||
end
|
||||
|
||||
def recursive_fgrep_args
|
||||
# Don't recurse into symlinks; the man page says this is the default, but
|
||||
# it's wrong. -O is a BSD-grep-only option.
|
||||
"-lrO"
|
||||
end
|
||||
|
||||
def egrep_args
|
||||
grep_bin = "egrep"
|
||||
grep_args = "--files-with-matches"
|
||||
[grep_bin, grep_args]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
CELLAR_RX = %r{\A#{HOMEBREW_CELLAR}/(?<formula_name>[^/]+)/[^/]+}
|
||||
|
||||
# Replace HOMEBREW_CELLAR references with HOMEBREW_PREFIX/opt references
|
||||
# if the Cellar reference is to a different keg.
|
||||
def opt_name_for(filename)
|
||||
return filename unless filename.start_with?(HOMEBREW_PREFIX.to_s)
|
||||
return filename if filename.start_with?(path.to_s)
|
||||
return filename if (matches = CELLAR_RX.match(filename)).blank?
|
||||
|
||||
filename.sub(CELLAR_RX, "#{HOMEBREW_PREFIX}/opt/#{matches[:formula_name]}")
|
||||
end
|
||||
|
||||
def rooted_in_build_directory?(filename)
|
||||
# CMake normalises `/private/tmp` to `/tmp`.
|
||||
# https://gitlab.kitware.com/cmake/cmake/-/issues/23251
|
||||
return true if HOMEBREW_TEMP.to_s == "/private/tmp" && filename.start_with?("/tmp/")
|
||||
|
||||
filename.start_with?(HOMEBREW_TEMP.to_s) || filename.start_with?(HOMEBREW_TEMP.realpath.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
generic_fix_dynamic_linkage
|
||||
end
|
||||
|
||||
def loader_name_for(file, target)
|
||||
# Use @loader_path-relative install names for other Homebrew-installed binaries.
|
||||
if ENV["HOMEBREW_RELOCATABLE_INSTALL_NAMES"] && target.start_with?(HOMEBREW_PREFIX)
|
||||
dylib_suffix = find_dylib_suffix_from(target)
|
||||
target_dir = Pathname.new(target.delete_suffix(dylib_suffix)).cleanpath
|
||||
|
||||
"@loader_path/#{target_dir.relative_path_from(file.dirname)/dylib_suffix}"
|
||||
else
|
||||
target
|
||||
end
|
||||
end
|
||||
|
||||
# If file is a dylib or bundle itself, look for the dylib named by
|
||||
# bad_name relative to the lib directory, so that we can skip the more
|
||||
# expensive recursive search if possible.
|
||||
def fixed_name(file, bad_name)
|
||||
if bad_name.start_with? PREFIX_PLACEHOLDER
|
||||
bad_name.sub(PREFIX_PLACEHOLDER, HOMEBREW_PREFIX)
|
||||
elsif bad_name.start_with? CELLAR_PLACEHOLDER
|
||||
bad_name.sub(CELLAR_PLACEHOLDER, HOMEBREW_CELLAR)
|
||||
elsif (file.dylib? || file.mach_o_bundle?) && (file.dirname/bad_name).exist?
|
||||
"@loader_path/#{bad_name}"
|
||||
elsif file.mach_o_executable? && (lib/bad_name).exist?
|
||||
"#{lib}/#{bad_name}"
|
||||
elsif file.mach_o_executable? && (libexec/"lib"/bad_name).exist?
|
||||
"#{libexec}/lib/#{bad_name}"
|
||||
elsif (abs_name = find_dylib(bad_name)) && abs_name.exist?
|
||||
abs_name.to_s
|
||||
else
|
||||
opoo "Could not fix #{bad_name} in #{file}"
|
||||
bad_name
|
||||
end
|
||||
end
|
||||
|
||||
VARIABLE_REFERENCE_RX = /^@(loader_|executable_|r)path/
|
||||
|
||||
def each_linkage_for(file, linkage_type, resolve_variable_references: false, &block)
|
||||
file.public_send(linkage_type, resolve_variable_references:)
|
||||
.grep_v(VARIABLE_REFERENCE_RX)
|
||||
.each(&block)
|
||||
end
|
||||
|
||||
def dylib_id_for(file)
|
||||
# The new dylib ID should have the same basename as the old dylib ID, not
|
||||
# the basename of the file itself.
|
||||
basename = File.basename(file.dylib_id)
|
||||
relative_dirname = file.dirname.relative_path_from(path)
|
||||
(opt_record/relative_dirname/basename).to_s
|
||||
end
|
||||
|
||||
def relocated_name_for(old_name, relocation)
|
||||
old_prefix, new_prefix = relocation.replacement_pair_for(:prefix)
|
||||
old_cellar, new_cellar = relocation.replacement_pair_for(:cellar)
|
||||
|
||||
if old_name.start_with? old_cellar
|
||||
old_name.sub(old_cellar, new_cellar)
|
||||
elsif old_name.start_with? old_prefix
|
||||
old_name.sub(old_prefix, new_prefix)
|
||||
end
|
||||
end
|
||||
|
||||
# Matches framework references like `XXX.framework/Versions/YYY/XXX` and
|
||||
# `XXX.framework/XXX`, both with or without a slash-delimited prefix.
|
||||
FRAMEWORK_RX = %r{(?:^|/)(([^/]+)\.framework/(?:Versions/[^/]+/)?\2)$}
|
||||
|
||||
def find_dylib_suffix_from(bad_name)
|
||||
if (framework = bad_name.match(FRAMEWORK_RX))
|
||||
framework[1]
|
||||
else
|
||||
File.basename(bad_name)
|
||||
end
|
||||
end
|
||||
|
||||
def find_dylib(bad_name)
|
||||
return unless lib.directory?
|
||||
|
||||
suffix = "/#{find_dylib_suffix_from(bad_name)}"
|
||||
lib.find { |pn| break pn if pn.to_s.end_with?(suffix) }
|
||||
end
|
||||
|
||||
def mach_o_files
|
||||
hardlinks = Set.new
|
||||
mach_o_files = []
|
||||
path.find do |pn|
|
||||
next if pn.symlink? || pn.directory?
|
||||
next if !pn.dylib? && !pn.mach_o_bundle? && !pn.mach_o_executable?
|
||||
# if we've already processed a file, ignore its hardlinks (which have the same dev ID and inode)
|
||||
# this prevents relocations from being performed on a binary more than once
|
||||
next unless hardlinks.add? [pn.stat.dev, pn.stat.ino]
|
||||
|
||||
mach_o_files << pn
|
||||
end
|
||||
|
||||
mach_o_files
|
||||
end
|
||||
|
||||
def prepare_relocation_to_locations
|
||||
relocation = generic_prepare_relocation_to_locations
|
||||
|
||||
brewed_perl = runtime_dependencies&.any? { |dep| dep["full_name"] == "perl" && dep["declared_directly"] }
|
||||
perl_path = if brewed_perl || name == "perl"
|
||||
"#{HOMEBREW_PREFIX}/opt/perl/bin/perl"
|
||||
elsif tab.built_on.present?
|
||||
perl_path = "/usr/bin/perl#{tab.built_on["preferred_perl"]}"
|
||||
|
||||
# For `:all` bottles, we could have built this bottle with a Perl we don't have.
|
||||
# Such bottles typically don't have strict version requirements.
|
||||
perl_path = "/usr/bin/perl#{MacOS.preferred_perl_version}" unless File.exist?(perl_path)
|
||||
|
||||
perl_path
|
||||
else
|
||||
"/usr/bin/perl#{MacOS.preferred_perl_version}"
|
||||
end
|
||||
relocation.add_replacement_pair(:perl, PERL_PLACEHOLDER, perl_path)
|
||||
|
||||
if (openjdk = openjdk_dep_name_if_applicable)
|
||||
openjdk_path = HOMEBREW_PREFIX/"opt"/openjdk/"libexec/openjdk.jdk/Contents/Home"
|
||||
relocation.add_replacement_pair(:java, JAVA_PLACEHOLDER, openjdk_path.to_s)
|
||||
end
|
||||
|
||||
relocation
|
||||
end
|
||||
|
||||
def recursive_fgrep_args
|
||||
# Don't recurse into symlinks; the man page says this is the default, but
|
||||
# it's wrong. -O is a BSD-grep-only option.
|
||||
"-lrO"
|
||||
end
|
||||
|
||||
def egrep_args
|
||||
grep_bin = "egrep"
|
||||
grep_args = "--files-with-matches"
|
||||
[grep_bin, grep_args]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
CELLAR_RX = %r{\A#{HOMEBREW_CELLAR}/(?<formula_name>[^/]+)/[^/]+}
|
||||
|
||||
# Replace HOMEBREW_CELLAR references with HOMEBREW_PREFIX/opt references
|
||||
# if the Cellar reference is to a different keg.
|
||||
def opt_name_for(filename)
|
||||
return filename unless filename.start_with?(HOMEBREW_PREFIX.to_s)
|
||||
return filename if filename.start_with?(path.to_s)
|
||||
return filename if (matches = CELLAR_RX.match(filename)).blank?
|
||||
|
||||
filename.sub(CELLAR_RX, "#{HOMEBREW_PREFIX}/opt/#{matches[:formula_name]}")
|
||||
end
|
||||
|
||||
def rooted_in_build_directory?(filename)
|
||||
# CMake normalises `/private/tmp` to `/tmp`.
|
||||
# https://gitlab.kitware.com/cmake/cmake/-/issues/23251
|
||||
return true if HOMEBREW_TEMP.to_s == "/private/tmp" && filename.start_with?("/tmp/")
|
||||
|
||||
filename.start_with?(HOMEBREW_TEMP.to_s) || filename.start_with?(HOMEBREW_TEMP.realpath.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
Keg.singleton_class.prepend(OS::Mac::Keg::ClassMethods)
|
||||
Keg.prepend(OS::Mac::Keg)
|
||||
|
@ -9,7 +9,7 @@ module OS
|
||||
requires_ancestor { Kernel }
|
||||
|
||||
sig { params(tap: Tap, os_name: T.nilable(Symbol), arch: T.nilable(Symbol)).returns(T::Boolean) }
|
||||
def valid_casks?(tap, os_name: nil, arch: Hardware::CPU.type)
|
||||
def valid_casks?(tap, os_name: nil, arch: ::Hardware::CPU.type)
|
||||
return true if os_name == :linux
|
||||
|
||||
current_macos_version = if os_name.is_a?(Symbol)
|
||||
|
@ -13,7 +13,7 @@ module OS
|
||||
|
||||
sig { returns(Symbol) }
|
||||
def current_os
|
||||
Homebrew::SimulateSystem.os || MacOS.version.to_sym
|
||||
::Homebrew::SimulateSystem.os || MacOS.version.to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -3,20 +3,26 @@
|
||||
|
||||
require "system_command"
|
||||
|
||||
module OS
|
||||
module Mac
|
||||
module SystemConfig
|
||||
sig { returns(String) }
|
||||
def describe_clang
|
||||
return "N/A" if ::SystemConfig.clang.null?
|
||||
|
||||
clang_build_info = ::SystemConfig.clang_build.null? ? "(parse error)" : ::SystemConfig.clang_build
|
||||
"#{::SystemConfig.clang} build #{clang_build_info}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SystemConfig.prepend(OS::Mac::SystemConfig)
|
||||
|
||||
module SystemConfig
|
||||
class << self
|
||||
include SystemCommand::Mixin
|
||||
|
||||
undef describe_clang
|
||||
|
||||
sig { returns(String) }
|
||||
def describe_clang
|
||||
return "N/A" if clang.null?
|
||||
|
||||
clang_build_info = clang_build.null? ? "(parse error)" : clang_build
|
||||
"#{clang} build #{clang_build_info}"
|
||||
end
|
||||
|
||||
def xcode
|
||||
@xcode ||= if MacOS::Xcode.installed?
|
||||
xcode = MacOS::Xcode.version.to_s
|
||||
|
9
Library/Homebrew/hardware.rbi
Normal file
9
Library/Homebrew/hardware.rbi
Normal file
@ -0,0 +1,9 @@
|
||||
# typed: strict
|
||||
|
||||
module Hardware
|
||||
class CPU
|
||||
class << self
|
||||
include OS::Mac::Hardware::CPU
|
||||
end
|
||||
end
|
||||
end
|
5
Library/Homebrew/keg.rbi
Normal file
5
Library/Homebrew/keg.rbi
Normal file
@ -0,0 +1,5 @@
|
||||
# typed: strict
|
||||
|
||||
class Keg
|
||||
include OS::Mac::Keg
|
||||
end
|
@ -210,7 +210,6 @@ class Keg
|
||||
# for GNU grep; overridden for BSD grep on OS X
|
||||
"-lr"
|
||||
end
|
||||
alias generic_recursive_fgrep_args recursive_fgrep_args
|
||||
|
||||
def egrep_args
|
||||
grep_bin = "grep"
|
||||
|
@ -151,7 +151,7 @@ module OS
|
||||
# Xcode.prefix is pretty smart, so let's look inside to find the sdk
|
||||
sdk_prefix = "#{Xcode.prefix}/Platforms/MacOSX.platform/Developer/SDKs"
|
||||
# Finally query Xcode itself (this is slow, so check it last)
|
||||
sdk_platform_path = Utils.popen_read(DevelopmentTools.locate("xcrun"), "--show-sdk-platform-path").chomp
|
||||
sdk_platform_path = Utils.popen_read(::DevelopmentTools.locate("xcrun"), "--show-sdk-platform-path").chomp
|
||||
sdk_prefix = File.join(sdk_platform_path, "Developer", "SDKs") unless File.directory? sdk_prefix
|
||||
|
||||
sdk_prefix
|
||||
|
@ -227,7 +227,7 @@ module OS
|
||||
|
||||
sig { returns(String) }
|
||||
def self.detect_version_from_clang_version
|
||||
version = DevelopmentTools.clang_version
|
||||
version = ::DevelopmentTools.clang_version
|
||||
|
||||
return "dunno" if version.null?
|
||||
|
||||
|
@ -87,7 +87,7 @@ class AbstractTab
|
||||
"tap" => nil,
|
||||
"tap_git_head" => nil,
|
||||
},
|
||||
"built_on" => DevelopmentTools.generic_build_system_info,
|
||||
"built_on" => DevelopmentTools.build_system_info,
|
||||
}
|
||||
|
||||
new(attributes)
|
||||
|
Loading…
x
Reference in New Issue
Block a user