From 744e2452d86893b0caf547104e06ece3a75a3730 Mon Sep 17 00:00:00 2001 From: botantony Date: Tue, 9 Sep 2025 15:07:35 +0200 Subject: [PATCH 1/6] os/mac/keg: `typed: strict` Signed-off-by: botantony --- Library/Homebrew/os/mac/keg.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Library/Homebrew/os/mac/keg.rb b/Library/Homebrew/os/mac/keg.rb index 127633ee87..8c78c3d6d8 100644 --- a/Library/Homebrew/os/mac/keg.rb +++ b/Library/Homebrew/os/mac/keg.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true class Keg @@ -6,7 +6,7 @@ class Keg def change_dylib_id(id, file) return false if file.dylib_id == id - @require_relocation = true + @require_relocation = T.let(true, T.nilable(T::Boolean)) odebug "Changing dylib ID of #{file}\n from #{file.dylib_id}\n to #{id}" file.change_dylib_id(id, strict: false) true @@ -23,7 +23,7 @@ class Keg def change_install_name(old, new, file) return false if old == new - @require_relocation = true + @require_relocation = T.let(true, T.nilable(T::Boolean)) odebug "Changing install name in #{file}\n from #{old}\n to #{new}" file.change_install_name(old, new, strict: false) true @@ -36,10 +36,11 @@ class Keg raise end + sig { params(old: String, new: String, file: Pathname).returns(T::Boolean) } def change_rpath(old, new, file) return false if old == new - @require_relocation = true + @require_relocation = T.let(true, T.nilable(T::Boolean)) odebug "Changing rpath in #{file}\n from #{old}\n to #{new}" file.change_rpath(old, new, strict: false) true From 289fed93b4aec5abb1c0252712d28edf31c5cef8 Mon Sep 17 00:00:00 2001 From: botantony Date: Tue, 9 Sep 2025 15:07:45 +0200 Subject: [PATCH 2/6] os/mac/mach: `typed: strict` Signed-off-by: botantony --- Library/Homebrew/os/mac/mach.rb | 38 +++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/Library/Homebrew/os/mac/mach.rb b/Library/Homebrew/os/mac/mach.rb index 44ddf94636..4f29aedcad 100644 --- a/Library/Homebrew/os/mac/mach.rb +++ b/Library/Homebrew/os/mac/mach.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "macho" @@ -12,26 +12,28 @@ module MachOShim delegate [:dylib_id] => :macho + sig { params(args: T.untyped).void } def initialize(*args) - @macho = T.let(nil, T.nilable(MachO::MachOFile)) - @mach_data = T.let(nil, T.nilable(T::Array[T::Hash[Symbol, T.untyped]])) + @macho = T.let(nil, T.nilable(T.any(MachO::MachOFile, MachO::FatFile))) + @mach_data = T.let(nil, T.nilable(T::Array[T::Hash[Symbol, Symbol]])) super end + sig { returns(T.any(MachO::MachOFile, MachO::FatFile)) } def macho @macho ||= MachO.open(to_s) end private :macho - sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) } + sig { returns(T::Array[T::Hash[Symbol, Symbol]]) } def mach_data @mach_data ||= begin machos = [] mach_data = [] - if MachO::Utils.fat_magic?(macho.magic) - machos = macho.machos + if macho.is_a?(MachO::FatFile) + machos = T.cast(macho, MachO::FatFile).machos else machos << macho end @@ -68,6 +70,7 @@ module MachOShim # TODO: See if the `#write!` call can be delayed until # we know we're not making any changes to the rpaths. + sig { params(rpath: String, options: T::Boolean).void } def delete_rpath(rpath, **options) candidates = rpaths(resolve_variable_references: false).select do |r| resolve_variable_name(r) == resolve_variable_name(rpath) @@ -81,21 +84,25 @@ module MachOShim macho.write! end + sig { params(old: String, new: String, options: T::Boolean).void } def change_rpath(old, new, **options) macho.change_rpath(old, new, options) macho.write! end + sig { params(id: String, options: T::Boolean).void } def change_dylib_id(id, **options) macho.change_dylib_id(id, options) macho.write! end + sig { params(old: String, new: String, options: T::Boolean).void } def change_install_name(old, new, **options) macho.change_install_name(old, new, options) macho.write! end + sig { params(except: Symbol, resolve_variable_references: T::Boolean).returns(T::Array[String]) } def dynamically_linked_libraries(except: :none, resolve_variable_references: true) lcs = macho.dylib_load_commands lcs.reject! { |lc| lc.flag?(except) } if except != :none @@ -105,6 +112,7 @@ module MachOShim names end + sig { params(resolve_variable_references: T::Boolean).returns(T::Array[String]) } def rpaths(resolve_variable_references: true) names = macho.rpaths # Don't recursively resolve rpaths to avoid infinite loops. @@ -113,11 +121,12 @@ module MachOShim names end + sig { params(name: String, resolve_rpaths: T::Boolean).returns(String) } def resolve_variable_name(name, resolve_rpaths: true) if name.start_with? "@loader_path" - Pathname(name.sub("@loader_path", dirname)).cleanpath.to_s + Pathname(name.sub("@loader_path", dirname.to_s)).cleanpath.to_s elsif name.start_with?("@executable_path") && binary_executable? - Pathname(name.sub("@executable_path", dirname)).cleanpath.to_s + Pathname(name.sub("@executable_path", dirname.to_s)).cleanpath.to_s elsif resolve_rpaths && name.start_with?("@rpath") && (target = resolve_rpath(name)).present? target else @@ -125,6 +134,7 @@ module MachOShim end end + sig { params(name: String).returns(T.nilable(String)) } def resolve_rpath(name) target = T.let(nil, T.nilable(String)) return unless rpaths(resolve_variable_references: true).find do |rpath| @@ -134,48 +144,58 @@ module MachOShim target end + sig { returns(T::Array[Symbol]) } def archs mach_data.map { |m| m.fetch :arch } end + sig { returns(Symbol) } def arch case archs.length when 0 then :dunno - when 1 then archs.first + when 1 then archs.fetch(0) else :universal end end + sig { returns(T::Boolean) } def universal? arch == :universal end + sig { returns(T::Boolean) } def i386? arch == :i386 end + sig { returns(T::Boolean) } def x86_64? arch == :x86_64 end + sig { returns(T::Boolean) } def ppc7400? arch == :ppc7400 end + sig { returns(T::Boolean) } def ppc64? arch == :ppc64 end + sig { returns(T::Boolean) } def dylib? mach_data.any? { |m| m.fetch(:type) == :dylib } end + sig { returns(T::Boolean) } def mach_o_executable? mach_data.any? { |m| m.fetch(:type) == :executable } end alias binary_executable? mach_o_executable? + sig { returns(T::Boolean) } def mach_o_bundle? mach_data.any? { |m| m.fetch(:type) == :bundle } end From 28761d875641e231c58e0672aca522cf4b04c5b9 Mon Sep 17 00:00:00 2001 From: botantony Date: Tue, 9 Sep 2025 15:07:54 +0200 Subject: [PATCH 3/6] os/mac/sdk: `typed: strict` Signed-off-by: botantony --- Library/Homebrew/os/mac/sdk.rb | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/Library/Homebrew/os/mac/sdk.rb b/Library/Homebrew/os/mac/sdk.rb index 1584941c99..89a69d42be 100644 --- a/Library/Homebrew/os/mac/sdk.rb +++ b/Library/Homebrew/os/mac/sdk.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "system_command" @@ -22,7 +22,7 @@ module OS sig { params(version: MacOSVersion, path: T.any(String, Pathname), source: Symbol).void } def initialize(version, path, source) @version = version - @path = Pathname.new(path) + @path = T.let(Pathname.new(path), Pathname) @source = source end end @@ -36,6 +36,11 @@ module OS class NoSDKError < StandardError; end + sig { void } + def initialize + @all_sdks = T.let([], T::Array[SDK]) + end + sig { params(version: MacOSVersion).returns(SDK) } def sdk_for(version) sdk = all_sdks.find { |s| s.version == version } @@ -46,9 +51,7 @@ module OS sig { returns(T::Array[SDK]) } def all_sdks - return @all_sdks if @all_sdks - - @all_sdks = [] + return @all_sdks unless @all_sdks.empty? # Bail out if there is no SDK prefix at all return @all_sdks unless File.directory? sdk_prefix @@ -147,7 +150,7 @@ module OS sig { override.returns(String) } def sdk_prefix - @sdk_prefix ||= begin + @sdk_prefix ||= T.let(begin # 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) @@ -155,7 +158,7 @@ module OS sdk_prefix = File.join(sdk_platform_path, "Developer", "SDKs") unless File.directory? sdk_prefix sdk_prefix - end + end, T.nilable(String)) end end @@ -177,11 +180,11 @@ module OS # return `nil` SDKs for Xcode 9 and older. sig { override.returns(String) } def sdk_prefix - @sdk_prefix ||= if CLT.provides_sdk? - "#{CLT::PKG_PATH}/SDKs" - else - "" - end + @sdk_prefix ||= T.let(if CLT.provides_sdk? + "#{CLT::PKG_PATH}/SDKs" + else + "" + end, T.nilable(String)) end end end From 0adf85970d579396d45202e2b8423e8a941a5e25 Mon Sep 17 00:00:00 2001 From: botantony Date: Tue, 9 Sep 2025 15:08:02 +0200 Subject: [PATCH 4/6] os/mac/xcode: `typed: strict` Signed-off-by: botantony --- Library/Homebrew/os/mac/xcode.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Library/Homebrew/os/mac/xcode.rb b/Library/Homebrew/os/mac/xcode.rb index 2300f1489d..2cd385cb55 100644 --- a/Library/Homebrew/os/mac/xcode.rb +++ b/Library/Homebrew/os/mac/xcode.rb @@ -1,11 +1,11 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true module OS module Mac # Helper module for querying Xcode information. module Xcode - DEFAULT_BUNDLE_PATH = Pathname("/Applications/Xcode.app").freeze + DEFAULT_BUNDLE_PATH = T.let(Pathname("/Applications/Xcode.app").freeze, Pathname) BUNDLE_ID = "com.apple.dt.Xcode" OLD_BUNDLE_ID = "com.apple.Xcode" APPLE_DEVELOPER_DOWNLOAD_URL = "https://developer.apple.com/download/all/" @@ -98,7 +98,7 @@ module OS # directory or nil if Xcode.app is not installed. sig { returns(T.nilable(Pathname)) } def self.prefix - @prefix ||= begin + @prefix ||= T.let(begin dir = MacOS.active_developer_dir if dir.empty? || dir == CLT::PKG_PATH || !File.directory?(dir) @@ -108,7 +108,7 @@ module OS # Use cleanpath to avoid pathological trailing slash Pathname.new(dir).cleanpath end - end + end, T.nilable(Pathname)) end sig { returns(Pathname) } @@ -134,7 +134,7 @@ module OS sig { returns(XcodeSDKLocator) } def self.sdk_locator - @sdk_locator ||= XcodeSDKLocator.new + @sdk_locator ||= T.let(XcodeSDKLocator.new, T.nilable(OS::Mac::XcodeSDKLocator)) end sig { params(version: T.nilable(MacOSVersion)).returns(T.nilable(SDK)) } @@ -183,7 +183,7 @@ module OS # may return a version string # that is guessed based on the compiler, so do not # use it in order to check if Xcode is installed. - if @version ||= detect_version + if @version ||= T.let(detect_version, T.nilable(String)) ::Version.new @version else ::Version::NULL @@ -293,7 +293,7 @@ module OS sig { returns(CLTSDKLocator) } def self.sdk_locator - @sdk_locator ||= CLTSDKLocator.new + @sdk_locator ||= T.let(CLTSDKLocator.new, T.nilable(OS::Mac::CLTSDKLocator)) end sig { params(version: T.nilable(MacOSVersion)).returns(T.nilable(SDK)) } @@ -444,7 +444,7 @@ module OS # @api internal sig { returns(::Version) } def self.version - if @version ||= detect_version + if @version ||= T.let(detect_version, T.nilable(String)) ::Version.new @version else ::Version::NULL From bc2c12c7423d0c5414195134817bf02fa28382df Mon Sep 17 00:00:00 2001 From: botantony Date: Tue, 9 Sep 2025 18:13:13 +0200 Subject: [PATCH 5/6] os/mac/*: style corrections Signed-off-by: botantony Co-authored-by: Mike McQuaid --- Library/Homebrew/os/mac/keg.rb | 21 ++++++++++++++------- Library/Homebrew/os/mac/mach.rb | 25 ++++++++++++------------- Library/Homebrew/os/mac/sdk.rb | 17 +++++++++-------- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/Library/Homebrew/os/mac/keg.rb b/Library/Homebrew/os/mac/keg.rb index 8c78c3d6d8..30c76420b3 100644 --- a/Library/Homebrew/os/mac/keg.rb +++ b/Library/Homebrew/os/mac/keg.rb @@ -2,13 +2,20 @@ # frozen_string_literal: true class Keg + sig { params(path: Pathname).void } + def initialize(path) + super + + @require_relocation = T.let(nil, T.nilable(T::Boolean)) + end + sig { params(id: String, file: Pathname).returns(T::Boolean) } def change_dylib_id(id, file) return false if file.dylib_id == id - @require_relocation = T.let(true, T.nilable(T::Boolean)) + @require_relocation = true odebug "Changing dylib ID of #{file}\n from #{file.dylib_id}\n to #{id}" - file.change_dylib_id(id, strict: false) + file.change_dylib_id(id) true rescue MachO::MachOError onoe <<~EOS @@ -23,9 +30,9 @@ class Keg def change_install_name(old, new, file) return false if old == new - @require_relocation = T.let(true, T.nilable(T::Boolean)) + @require_relocation = true odebug "Changing install name in #{file}\n from #{old}\n to #{new}" - file.change_install_name(old, new, strict: false) + file.change_install_name(old, new) true rescue MachO::MachOError onoe <<~EOS @@ -40,9 +47,9 @@ class Keg def change_rpath(old, new, file) return false if old == new - @require_relocation = T.let(true, T.nilable(T::Boolean)) + @require_relocation = true odebug "Changing rpath in #{file}\n from #{old}\n to #{new}" - file.change_rpath(old, new, strict: false) + file.change_rpath(old, new) true rescue MachO::MachOError onoe <<~EOS @@ -56,7 +63,7 @@ class Keg sig { params(rpath: String, file: MachOShim).returns(T::Boolean) } def delete_rpath(rpath, file) odebug "Deleting rpath #{rpath} in #{file}" - file.delete_rpath(rpath, strict: false) + file.delete_rpath(rpath) true rescue MachO::MachOError onoe <<~EOS diff --git a/Library/Homebrew/os/mac/mach.rb b/Library/Homebrew/os/mac/mach.rb index 4f29aedcad..b44a13ab98 100644 --- a/Library/Homebrew/os/mac/mach.rb +++ b/Library/Homebrew/os/mac/mach.rb @@ -70,35 +70,34 @@ module MachOShim # TODO: See if the `#write!` call can be delayed until # we know we're not making any changes to the rpaths. - sig { params(rpath: String, options: T::Boolean).void } - def delete_rpath(rpath, **options) + sig { params(rpath: String).void } + def delete_rpath(rpath) candidates = rpaths(resolve_variable_references: false).select do |r| resolve_variable_name(r) == resolve_variable_name(rpath) end # Delete the last instance to avoid changing the order in which rpaths are searched. rpath_to_delete = candidates.last - options[:last] = true - macho.delete_rpath(rpath_to_delete, options) + macho.delete_rpath(rpath_to_delete, { last: true }) macho.write! end - sig { params(old: String, new: String, options: T::Boolean).void } - def change_rpath(old, new, **options) - macho.change_rpath(old, new, options) + sig { params(old: String, new: String, uniq: T::Boolean, last: T::Boolean).void } + def change_rpath(old, new, uniq: false, last: false) + macho.change_rpath(old, new, { uniq: uniq, last: last }) macho.write! end - sig { params(id: String, options: T::Boolean).void } - def change_dylib_id(id, **options) - macho.change_dylib_id(id, options) + sig { params(id: String).void } + def change_dylib_id(id) + macho.change_dylib_id(id) macho.write! end - sig { params(old: String, new: String, options: T::Boolean).void } - def change_install_name(old, new, **options) - macho.change_install_name(old, new, options) + sig { params(old: String, new: String).void } + def change_install_name(old, new) + macho.change_install_name(old, new) macho.write! end diff --git a/Library/Homebrew/os/mac/sdk.rb b/Library/Homebrew/os/mac/sdk.rb index 89a69d42be..e5ea0f2a19 100644 --- a/Library/Homebrew/os/mac/sdk.rb +++ b/Library/Homebrew/os/mac/sdk.rb @@ -22,7 +22,7 @@ module OS sig { params(version: MacOSVersion, path: T.any(String, Pathname), source: Symbol).void } def initialize(version, path, source) @version = version - @path = T.let(Pathname.new(path), Pathname) + @path = T.let(Pathname(path), Pathname) @source = source end end @@ -39,6 +39,7 @@ module OS sig { void } def initialize @all_sdks = T.let([], T::Array[SDK]) + @sdk_prefix = T.let(nil, T.nilable(String)) end sig { params(version: MacOSVersion).returns(SDK) } @@ -150,7 +151,7 @@ module OS sig { override.returns(String) } def sdk_prefix - @sdk_prefix ||= T.let(begin + @sdk_prefix ||= begin # 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) @@ -158,7 +159,7 @@ module OS sdk_prefix = File.join(sdk_platform_path, "Developer", "SDKs") unless File.directory? sdk_prefix sdk_prefix - end, T.nilable(String)) + end end end @@ -180,11 +181,11 @@ module OS # return `nil` SDKs for Xcode 9 and older. sig { override.returns(String) } def sdk_prefix - @sdk_prefix ||= T.let(if CLT.provides_sdk? - "#{CLT::PKG_PATH}/SDKs" - else - "" - end, T.nilable(String)) + @sdk_prefix ||= if CLT.provides_sdk? + "#{CLT::PKG_PATH}/SDKs" + else + "" + end end end end From 843fc7c97a508aacad8788039cdb068466acb8b8 Mon Sep 17 00:00:00 2001 From: botantony Date: Tue, 9 Sep 2025 19:51:37 +0200 Subject: [PATCH 6/6] os/mac/*: more style corrections Signed-off-by: botantony Co-authored-by: Bo Anderson --- Library/Homebrew/os/mac/keg.rb | 10 +++++----- Library/Homebrew/os/mac/mach.rb | 29 +++++++++++++++-------------- Library/Homebrew/os/mac/sdk.rb | 6 ++++-- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/Library/Homebrew/os/mac/keg.rb b/Library/Homebrew/os/mac/keg.rb index 30c76420b3..ebe0f17d7f 100644 --- a/Library/Homebrew/os/mac/keg.rb +++ b/Library/Homebrew/os/mac/keg.rb @@ -6,7 +6,7 @@ class Keg def initialize(path) super - @require_relocation = T.let(nil, T.nilable(T::Boolean)) + @require_relocation = T.let(false, T::Boolean) end sig { params(id: String, file: Pathname).returns(T::Boolean) } @@ -15,7 +15,7 @@ class Keg @require_relocation = true odebug "Changing dylib ID of #{file}\n from #{file.dylib_id}\n to #{id}" - file.change_dylib_id(id) + file.change_dylib_id(id, strict: false) true rescue MachO::MachOError onoe <<~EOS @@ -32,7 +32,7 @@ class Keg @require_relocation = true odebug "Changing install name in #{file}\n from #{old}\n to #{new}" - file.change_install_name(old, new) + file.change_install_name(old, new, strict: false) true rescue MachO::MachOError onoe <<~EOS @@ -49,7 +49,7 @@ class Keg @require_relocation = true odebug "Changing rpath in #{file}\n from #{old}\n to #{new}" - file.change_rpath(old, new) + file.change_rpath(old, new, strict: false) true rescue MachO::MachOError onoe <<~EOS @@ -63,7 +63,7 @@ class Keg sig { params(rpath: String, file: MachOShim).returns(T::Boolean) } def delete_rpath(rpath, file) odebug "Deleting rpath #{rpath} in #{file}" - file.delete_rpath(rpath) + file.delete_rpath(rpath, strict: false) true rescue MachO::MachOError onoe <<~EOS diff --git a/Library/Homebrew/os/mac/mach.rb b/Library/Homebrew/os/mac/mach.rb index b44a13ab98..5a0a27566a 100644 --- a/Library/Homebrew/os/mac/mach.rb +++ b/Library/Homebrew/os/mac/mach.rb @@ -32,8 +32,9 @@ module MachOShim machos = [] mach_data = [] - if macho.is_a?(MachO::FatFile) - machos = T.cast(macho, MachO::FatFile).machos + case (macho = self.macho) + when MachO::FatFile + machos = macho.machos else machos << macho end @@ -70,8 +71,8 @@ module MachOShim # TODO: See if the `#write!` call can be delayed until # we know we're not making any changes to the rpaths. - sig { params(rpath: String).void } - def delete_rpath(rpath) + sig { params(rpath: String, strict: T::Boolean).void } + def delete_rpath(rpath, strict: true) candidates = rpaths(resolve_variable_references: false).select do |r| resolve_variable_name(r) == resolve_variable_name(rpath) end @@ -79,25 +80,25 @@ module MachOShim # Delete the last instance to avoid changing the order in which rpaths are searched. rpath_to_delete = candidates.last - macho.delete_rpath(rpath_to_delete, { last: true }) + macho.delete_rpath(rpath_to_delete, { last: true, strict: }) macho.write! end - sig { params(old: String, new: String, uniq: T::Boolean, last: T::Boolean).void } - def change_rpath(old, new, uniq: false, last: false) - macho.change_rpath(old, new, { uniq: uniq, last: last }) + sig { params(old: String, new: String, uniq: T::Boolean, last: T::Boolean, strict: T::Boolean).void } + def change_rpath(old, new, uniq: false, last: false, strict: true) + macho.change_rpath(old, new, { uniq:, last:, strict: }) macho.write! end - sig { params(id: String).void } - def change_dylib_id(id) - macho.change_dylib_id(id) + sig { params(id: String, strict: T::Boolean).void } + def change_dylib_id(id, strict: true) + macho.change_dylib_id(id, { strict: }) macho.write! end - sig { params(old: String, new: String).void } - def change_install_name(old, new) - macho.change_install_name(old, new) + sig { params(old: String, new: String, strict: T::Boolean).void } + def change_install_name(old, new, strict: true) + macho.change_install_name(old, new, { strict: }) macho.write! end diff --git a/Library/Homebrew/os/mac/sdk.rb b/Library/Homebrew/os/mac/sdk.rb index e5ea0f2a19..498fd8e3bb 100644 --- a/Library/Homebrew/os/mac/sdk.rb +++ b/Library/Homebrew/os/mac/sdk.rb @@ -38,7 +38,7 @@ module OS sig { void } def initialize - @all_sdks = T.let([], T::Array[SDK]) + @all_sdks = T.let(nil, T.nilable(T::Array[SDK])) @sdk_prefix = T.let(nil, T.nilable(String)) end @@ -52,7 +52,9 @@ module OS sig { returns(T::Array[SDK]) } def all_sdks - return @all_sdks unless @all_sdks.empty? + return @all_sdks if @all_sdks + + @all_sdks = [] # Bail out if there is no SDK prefix at all return @all_sdks unless File.directory? sdk_prefix