From 5e0b786da21564011be7df165ce20a61d639de77 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Thu, 22 Jul 2021 00:43:46 +0800 Subject: [PATCH 1/6] formula: add `deuniversalize_machos` method This method takes an optional array of `Pathnames`s or `Strings`s and extracts the native slice from the specified universal binary. If no parameter is supplied, this is done on all compatible universal binaries in a formula's keg. `deuniversalize_machos` is a no-op on Linux. I still need to look into a) error handling, and b) whether using this method requires codesigning on ARM. I've also added signatures to the methods in `extend/os/linux/formula`. --- Library/Homebrew/extend/os/linux/formula.rb | 6 ++++++ Library/Homebrew/formula.rb | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/Library/Homebrew/extend/os/linux/formula.rb b/Library/Homebrew/extend/os/linux/formula.rb index fde9216664..da2d162bdf 100644 --- a/Library/Homebrew/extend/os/linux/formula.rb +++ b/Library/Homebrew/extend/os/linux/formula.rb @@ -4,7 +4,9 @@ class Formula undef shared_library undef rpath + undef deuniversalize_machos + sig { params(name: String, version: T.nilable(T.any(String, Integer))).returns(String) } def shared_library(name, version = nil) suffix = if version == "*" || (name == "*" && version.blank?) "{,.*}" @@ -14,10 +16,14 @@ class Formula "#{name}.so#{suffix}" end + sig { returns(String) } def rpath "'$ORIGIN/../lib'" end + sig { params(targets: T.nilable(T.any(Pathname, String))).void } + def deuniversalize_machos(*targets); end + class << self undef ignore_missing_libraries diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index a368b9a532..3ce605123b 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1582,6 +1582,21 @@ class Formula end end + sig { params(targets: T.nilable(T.any(Pathname, String))).void } + def deuniversalize_machos(*targets) + if targets.blank? + targets = any_installed_keg.mach_o_files.select do |file| + file.arch == :universal && file.archs.include?(Hardware::CPU.arch) + end + end + + targets.each do |t| + macho = MachO::FatFile.new(t) + native_slice = macho.extract(Hardware::CPU.arch) + native_slice.write t + end + end + # an array of all core {Formula} names # @private def self.core_names From a8527f4c16d7df419f6539fd583302635ec98a4a Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Sat, 24 Jul 2021 19:39:47 +0800 Subject: [PATCH 2/6] Ensure writability, handle errors --- Library/Homebrew/formula.rb | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 3ce605123b..5081cc20bb 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1582,6 +1582,10 @@ class Formula end end + # Replaces a universal binary with its native slice. + # + # If called with no parameters, does this with all compatible + # universal binaries in a {Formula}'s {Keg}. sig { params(targets: T.nilable(T.any(Pathname, String))).void } def deuniversalize_machos(*targets) if targets.blank? @@ -1590,10 +1594,23 @@ class Formula end end - targets.each do |t| - macho = MachO::FatFile.new(t) + targets.each { |t| extract_slice_from(Pathname.new(t), Hardware::CPU.arch) } + end + + # @private + sig { params(file: Pathname, arch: T.nilable(Symbol)).void } + def extract_slice_from(file, arch = Hardware::CPU.arch) + odebug "Extracting #{arch} slice from #{file}" + file.ensure_writable do + macho = MachO::FatFile.new(file) native_slice = macho.extract(Hardware::CPU.arch) - native_slice.write t + native_slice.write file + rescue MachO::MachOBinaryError + onoe "#{file} is not a universal binary" + raise + rescue NoMethodError + onoe "#{file} does not contain an #{arch} slice" + raise end end From 5128b92d3ef7616a35b7c3ad4f13ded0cfa5d683 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Sun, 25 Jul 2021 11:29:33 +0800 Subject: [PATCH 3/6] Use slightly more suggestive method name for extracting slices `extract_slice_from` is still a little ambiguous, so let's try to give it a more suggestive name. --- Library/Homebrew/formula.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 5081cc20bb..aa2533de0f 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1594,12 +1594,12 @@ class Formula end end - targets.each { |t| extract_slice_from(Pathname.new(t), Hardware::CPU.arch) } + targets.each { |t| extract_macho_slice_from(Pathname.new(t), Hardware::CPU.arch) } end # @private sig { params(file: Pathname, arch: T.nilable(Symbol)).void } - def extract_slice_from(file, arch = Hardware::CPU.arch) + def extract_macho_slice_from(file, arch = Hardware::CPU.arch) odebug "Extracting #{arch} slice from #{file}" file.ensure_writable do macho = MachO::FatFile.new(file) From 542694728ca63007430f0bfa369a941669c5351d Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Sun, 25 Jul 2021 23:21:26 +0800 Subject: [PATCH 4/6] codesign binaries on ARM --- Library/Homebrew/formula.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index aa2533de0f..d2f81510d4 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1605,6 +1605,7 @@ class Formula macho = MachO::FatFile.new(file) native_slice = macho.extract(Hardware::CPU.arch) native_slice.write file + MachO.codesign! file if Hardware::CPU.arm? rescue MachO::MachOBinaryError onoe "#{file} is not a universal binary" raise From fa635db8a1bd7494c355416421d52f87cdd0491c Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 17 Aug 2021 22:32:52 +0800 Subject: [PATCH 5/6] Simplify re-definition of `targets` --- Library/Homebrew/formula.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index d2f81510d4..3a0f29fc37 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1588,10 +1588,8 @@ class Formula # universal binaries in a {Formula}'s {Keg}. sig { params(targets: T.nilable(T.any(Pathname, String))).void } def deuniversalize_machos(*targets) - if targets.blank? - targets = any_installed_keg.mach_o_files.select do |file| - file.arch == :universal && file.archs.include?(Hardware::CPU.arch) - end + targets ||= any_installed_keg.mach_o_files.select do |file| + file.arch == :universal && file.archs.include?(Hardware::CPU.arch) end targets.each { |t| extract_macho_slice_from(Pathname.new(t), Hardware::CPU.arch) } From f20d61df9a61e9074b65f09839a2e6ba59187bd7 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 17 Aug 2021 22:47:08 +0800 Subject: [PATCH 6/6] Fix typecheck error --- Library/Homebrew/formula.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 3a0f29fc37..cc8601851f 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1588,6 +1588,7 @@ class Formula # universal binaries in a {Formula}'s {Keg}. sig { params(targets: T.nilable(T.any(Pathname, String))).void } def deuniversalize_machos(*targets) + targets = nil if targets.blank? targets ||= any_installed_keg.mach_o_files.select do |file| file.arch == :universal && file.archs.include?(Hardware::CPU.arch) end