diff --git a/Library/Homebrew/extend/os/mac/keg.rb b/Library/Homebrew/extend/os/mac/keg.rb index 504b5c134f..f41cd6c9e7 100644 --- a/Library/Homebrew/extend/os/mac/keg.rb +++ b/Library/Homebrew/extend/os/mac/keg.rb @@ -19,4 +19,10 @@ class Keg GENERIC_MUST_BE_WRITABLE_DIRECTORIES + [HOMEBREW_PREFIX/"Frameworks"] ).sort.uniq.freeze + + undef binary_executable_or_library_files + + def binary_executable_or_library_files + mach_o_files + end end diff --git a/Library/Homebrew/formula_cellar_checks.rb b/Library/Homebrew/formula_cellar_checks.rb index d1601f924d..a317967731 100644 --- a/Library/Homebrew/formula_cellar_checks.rb +++ b/Library/Homebrew/formula_cellar_checks.rb @@ -284,6 +284,36 @@ module FormulaCellarChecks "Service command does not exist" unless File.exist?(formula.service.command.first) end + def check_cpuid_instruction(formula) + return unless formula.prefix.directory? + # TODO: add methods to `utils/ast` to allow checking for method use + return unless formula.path.read.include? "ENV.runtime_cpu_detection" + # Checking for `cpuid` only makes sense on Intel: + # https://en.wikipedia.org/wiki/CPUID + return unless Hardware::CPU.intel? + + # macOS `objdump` is a bit slow, so we prioritise llvm's `llvm-objdump` (~5.7x faster) + # or binutils' `objdump` (~1.8x faster) if they are installed. + objdump = Formula["llvm"].opt_bin/"llvm-objdump" if Formula["llvm"].any_version_installed? + objdump ||= Formula["binutils"].opt_bin/"objdump" if Formula["binutils"].any_version_installed? + objdump ||= which("objdump") + objdump ||= which("objdump", ENV["HOMEBREW_PATH"]) + + unless objdump + return <<~EOS + No `objdump` found, so cannot check for a `cpuid` instruction. Install `objdump` with + brew install binutils + EOS + end + + keg = Keg.new(formula.prefix) + return if keg.binary_executable_or_library_files.any? do |file| + cpuid_instruction?(file, objdump) + end + + "No `cpuid` instruction detected. #{formula} should not use `ENV.runtime_cpu_detection`." + end + def audit_installed @new_formula ||= false @@ -303,6 +333,7 @@ module FormulaCellarChecks problem_if_output(check_shim_references(formula.prefix)) problem_if_output(check_plist(formula.prefix, formula.plist)) problem_if_output(check_python_symlinks(formula.name, formula.keg_only?)) + problem_if_output(check_cpuid_instruction(formula)) end alias generic_audit_installed audit_installed @@ -311,6 +342,26 @@ module FormulaCellarChecks def relative_glob(dir, pattern) File.directory?(dir) ? Dir.chdir(dir) { Dir[pattern] } : [] end + + def cpuid_instruction?(file, objdump = "objdump") + @instruction_column_index ||= {} + @instruction_column_index[objdump] ||= if Utils.popen_read(objdump, "--version").include? "LLVM" + 1 # `llvm-objdump` or macOS `objdump` + else + 2 # GNU binutils `objdump` + end + + has_cpuid_instruction = false + Utils.popen_read(objdump, "--disassemble", file) do |io| + until io.eof? + instruction = io.readline.split("\t")[@instruction_column_index[objdump]]&.strip + has_cpuid_instruction = instruction == "cpuid" if instruction.present? + break if has_cpuid_instruction + end + end + + has_cpuid_instruction + end end require "extend/os/formula_cellar_checks" diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index 7680dab013..6287d60708 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -525,6 +525,10 @@ class Keg find { |pn| FileUtils.rm_rf pn if pn.basename.to_s == "__pycache__" } end + def binary_executable_or_library_files + elf_files + end + private def resolve_any_conflicts(dst, dry_run: false, verbose: false, overwrite: false)