diff options
Diffstat (limited to 'Library')
157 files changed, 4629 insertions, 3366 deletions
diff --git a/Library/.rubocop.yml b/Library/.rubocop.yml index 314467ef0..a782c1117 100644 --- a/Library/.rubocop.yml +++ b/Library/.rubocop.yml @@ -11,6 +11,9 @@ require: ./Homebrew/rubocops.rb Homebrew/CorrectBottleBlock: Enabled: true +Homebrew/FormulaDesc: + Enabled: true + Metrics/AbcSize: Enabled: false diff --git a/Library/Homebrew/blacklist.rb b/Library/Homebrew/blacklist.rb deleted file mode 100644 index 93c8f81a0..000000000 --- a/Library/Homebrew/blacklist.rb +++ /dev/null @@ -1,92 +0,0 @@ -def blacklisted?(name) - case name.downcase - when "gem", /^rubygems?$/ then <<-EOS.undent - Homebrew provides gem via: `brew install ruby`. - EOS - when "tex", "tex-live", "texlive", "latex" then <<-EOS.undent - Installing TeX from source is weird and gross, requires a lot of patches, - and only builds 32-bit (and thus can't use Homebrew dependencies) - - We recommend using a MacTeX distribution: https://www.tug.org/mactex/ - - You can install it with Homebrew-Cask: - brew cask install mactex - EOS - when "pip" then <<-EOS.undent - Homebrew provides pip via: `brew install python`. However you will then - have two Pythons installed on your Mac, so alternatively you can install - pip via the instructions at: - #{Formatter.url("https://pip.readthedocs.io/en/stable/installing/")} - EOS - when "pil" then <<-EOS.undent - Instead of PIL, consider `pip install pillow` or `brew install Homebrew/python/pillow`. - EOS - when "macruby" then <<-EOS.undent - MacRuby is not packaged and is on an indefinite development hiatus. - You can read more about it at: - #{Formatter.url("https://github.com/MacRuby/MacRuby")} - EOS - when /(lib)?lzma/ - "lzma is now part of the xz formula." - when "gtest", "googletest", "google-test" then <<-EOS.undent - Installing gtest system-wide is not recommended; it should be vendored - in your projects that use it. - EOS - when "gmock", "googlemock", "google-mock" then <<-EOS.undent - Installing gmock system-wide is not recommended; it should be vendored - in your projects that use it. - EOS - when "sshpass" then <<-EOS.undent - We won't add sshpass because it makes it too easy for novice SSH users to - ruin SSH's security. - EOS - when "gsutil" then <<-EOS.undent - Install gsutil with `pip install gsutil` - EOS - when "clojure" then <<-EOS.undent - Clojure isn't really a program but a library managed as part of a - project and Leiningen is the user interface to that library. - - To install Clojure you should install Leiningen: - brew install leiningen - and then follow the tutorial: - #{Formatter.url("https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md")} - EOS - when "osmium" then <<-EOS.undent - The creator of Osmium requests that it not be packaged and that people - use the GitHub master branch instead. - EOS - when "gfortran" then <<-EOS.undent - GNU Fortran is now provided as part of GCC, and can be installed with: - brew install gcc - EOS - when "play" then <<-EOS.undent - Play 2.3 replaces the play command with activator: - brew install typesafe-activator - - You can read more about this change at: - #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Migration23")} - #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Highlights23")} - EOS - when "haskell-platform" then <<-EOS.undent - We no longer package haskell-platform. Consider installing ghc - and cabal-install instead: - brew install ghc cabal-install - - You can install with Homebrew-Cask: - brew cask install haskell-platform - EOS - when "mysqldump-secure" then <<-EOS.undent - The creator of mysqldump-secure tried to game our popularity metrics. - EOS - when "ngrok" then <<-EOS.undent - Upstream sunsetted 1.x in March 2016 and 2.x is not open-source. - - If you wish to use the 2.x release you can install with Homebrew-Cask: - brew cask install ngrok - EOS - end -end -alias generic_blacklisted? blacklisted? - -require "extend/os/blacklist" diff --git a/Library/Homebrew/build_options.rb b/Library/Homebrew/build_options.rb index d9020ecba..6c6952d71 100644 --- a/Library/Homebrew/build_options.rb +++ b/Library/Homebrew/build_options.rb @@ -47,7 +47,6 @@ class BuildOptions def bottle? include? "build-bottle" end - alias build_bottle? bottle? # True if a {Formula} is being built with {Formula.head} instead of {Formula.stable}. # <pre>args << "--some-new-stuff" if build.head?</pre> diff --git a/Library/Homebrew/cask/lib/hbc/artifact/binary.rb b/Library/Homebrew/cask/lib/hbc/artifact/binary.rb index 06bdfe157..21d123ab9 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/binary.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/binary.rb @@ -9,7 +9,12 @@ module Hbc def link super - FileUtils.chmod "+x", source + return if source.executable? + if source.writable? + FileUtils.chmod "+x", source + else + @command.run!("/bin/chmod", args: ["+x", source], sudo: true) + end end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/moved.rb b/Library/Homebrew/cask/lib/hbc/artifact/moved.rb index 01e98ac35..eaaa49e20 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/moved.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/moved.rb @@ -31,20 +31,26 @@ module Hbc ohai "Moving #{self.class.artifact_english_name} '#{source.basename}' to '#{target}'." target.dirname.mkpath - FileUtils.move(source, target) + + if target.parent.writable? + FileUtils.move(source, target) + else + SystemCommand.run("/bin/mv", args: [source, target], sudo: true) + end + add_altname_metadata target, source.basename.to_s end def delete ohai "Removing #{self.class.artifact_english_name} '#{target}'." - return unless Utils.path_occupied?(target) - raise CaskError, "Cannot remove undeletable #{self.class.artifact_english_name}." if MacOS.undeletable?(target) - if force - Utils.gain_permissions_remove(target, command: @command) - else + return unless Utils.path_occupied?(target) + + if target.parent.writable? && !force target.rmtree + else + Utils.gain_permissions_remove(target, command: @command) end end diff --git a/Library/Homebrew/cask/lib/hbc/cask.rb b/Library/Homebrew/cask/lib/hbc/cask.rb index cf5f2b37a..a193a394e 100644 --- a/Library/Homebrew/cask/lib/hbc/cask.rb +++ b/Library/Homebrew/cask/lib/hbc/cask.rb @@ -1,10 +1,12 @@ require "forwardable" require "hbc/dsl" +require "hbc/metadata" module Hbc class Cask extend Forwardable + include Metadata attr_reader :token, :sourcefile_path def initialize(token, sourcefile_path: nil, &block) @@ -20,55 +22,9 @@ module Hbc define_method(method_name) { @dsl.send(method_name) } end - METADATA_SUBDIR = ".metadata".freeze - - def metadata_master_container_path - @metadata_master_container_path ||= caskroom_path.join(METADATA_SUBDIR) - end - - def metadata_versioned_container_path - cask_version = version ? version : :unknown - metadata_master_container_path.join(cask_version.to_s) - end - - def metadata_path(timestamp = :latest, create = false) - if create && timestamp == :latest - raise CaskError, "Cannot create metadata path when timestamp is :latest" - end - path = if timestamp == :latest - Pathname.glob(metadata_versioned_container_path.join("*")).sort.last - elsif timestamp == :now - Utils.nowstamp_metadata_path(metadata_versioned_container_path) - else - metadata_versioned_container_path.join(timestamp) - end - if create - odebug "Creating metadata directory #{path}" - FileUtils.mkdir_p path - end - path - end - - def metadata_subdir(leaf, timestamp = :latest, create = false) - if create && timestamp == :latest - raise CaskError, "Cannot create metadata subdir when timestamp is :latest" - end - unless leaf.respond_to?(:length) && !leaf.empty? - raise CaskError, "Cannot create metadata subdir for empty leaf" - end - parent = metadata_path(timestamp, create) - return nil unless parent.respond_to?(:join) - subdir = parent.join(leaf) - if create - odebug "Creating metadata subdirectory #{subdir}" - FileUtils.mkdir_p subdir - end - subdir - end - def timestamped_versions - Pathname.glob(metadata_master_container_path.join("*", "*")) - .map { |p| p.relative_path_from(metadata_master_container_path) } + Pathname.glob(metadata_timestamped_path(version: "*", timestamp: "*")) + .map { |p| p.relative_path_from(p.parent.parent) } .sort_by(&:basename) # sort by timestamp .map { |p| p.split.map(&:to_s) } end diff --git a/Library/Homebrew/cask/lib/hbc/cli/doctor.rb b/Library/Homebrew/cask/lib/hbc/cli/doctor.rb index 6b2f4caab..031f78824 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/doctor.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/doctor.rb @@ -92,7 +92,7 @@ module Hbc def self.render_staging_location(path) path = Pathname.new(user_tilde(path.to_s)) if !path.exist? - "#{path} #{error_string "error: path does not exist"}}" + "#{path} #{error_string "error: path does not exist"}" elsif !path.writable? "#{path} #{error_string "error: not writable by current user"}" else diff --git a/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb index 6887aaf4f..1ee3230ad 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb @@ -12,15 +12,9 @@ module Hbc raise CaskNotInstalledError, cask unless cask.installed? || force - latest_installed_version = cask.timestamped_versions.last - - unless latest_installed_version.nil? - latest_installed_cask_file = cask.metadata_master_container_path - .join(latest_installed_version.join(File::Separator), - "Casks", "#{cask_token}.rb") - + if cask.installed? && !cask.installed_caskfile.nil? # use the same cask file that was used for installation, if possible - cask = CaskLoader.load_from_file(latest_installed_cask_file) if latest_installed_cask_file.exist? + cask = CaskLoader.load_from_file(cask.installed_caskfile) if cask.installed_caskfile.exist? end Installer.new(cask, force: force).uninstall diff --git a/Library/Homebrew/cask/lib/hbc/container.rb b/Library/Homebrew/cask/lib/hbc/container.rb index fc7246f3d..961e31968 100644 --- a/Library/Homebrew/cask/lib/hbc/container.rb +++ b/Library/Homebrew/cask/lib/hbc/container.rb @@ -4,6 +4,7 @@ require "hbc/container/bzip2" require "hbc/container/cab" require "hbc/container/criteria" require "hbc/container/dmg" +require "hbc/container/executable" require "hbc/container/generic_unar" require "hbc/container/gzip" require "hbc/container/lzma" @@ -39,6 +40,7 @@ module Hbc Gzip, # pure gzip Lzma, # pure lzma Xz, # pure xz + Executable, ] # for explicit use only (never autodetected): # Hbc::Container::Naked @@ -59,7 +61,7 @@ module Hbc begin const_get(type.to_s.split("_").map(&:capitalize).join) rescue NameError - false + nil end end end diff --git a/Library/Homebrew/cask/lib/hbc/container/executable.rb b/Library/Homebrew/cask/lib/hbc/container/executable.rb new file mode 100644 index 000000000..848f6d4be --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/container/executable.rb @@ -0,0 +1,18 @@ +require "hbc/container/naked" +require "vendor/macho/macho" + +module Hbc + class Container + class Executable < Naked + def self.me?(criteria) + return true if criteria.magic_number(/^#!\s*\S+/) + + begin + MachO.open(criteria.path).header.executable? + rescue MachO::MagicError + false + end + end + end + end +end diff --git a/Library/Homebrew/cask/lib/hbc/dsl/container.rb b/Library/Homebrew/cask/lib/hbc/dsl/container.rb index ab260c98c..caaf25bca 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/container.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/container.rb @@ -1,3 +1,5 @@ +require "hbc/container" + module Hbc class DSL class Container @@ -13,9 +15,12 @@ module Hbc @pairs = pairs pairs.each do |key, value| raise "invalid container key: '#{key.inspect}'" unless VALID_KEYS.include?(key) - writer_method = "#{key}=".to_sym - send(writer_method, value) + send(:"#{key}=", value) end + + return if type.nil? + return unless Hbc::Container.from_type(type).nil? + raise "invalid container type: #{type.inspect}" end def to_yaml diff --git a/Library/Homebrew/cask/lib/hbc/installer.rb b/Library/Homebrew/cask/lib/hbc/installer.rb index 51b0490f5..f02f07806 100644 --- a/Library/Homebrew/cask/lib/hbc/installer.rb +++ b/Library/Homebrew/cask/lib/hbc/installer.rb @@ -316,15 +316,13 @@ module Hbc end def save_caskfile - unless (old_savedirs = Pathname.glob(@cask.metadata_path("*"))).empty? - old_savedirs.each(&:rmtree) - end + old_savedir = @cask.metadata_timestamped_path return unless @cask.sourcefile_path - savedir = @cask.metadata_subdir("Casks", :now, true) - savedir.mkpath + savedir = @cask.metadata_subdir("Casks", timestamp: :now, create: true) FileUtils.copy @cask.sourcefile_path, savedir + old_savedir.rmtree unless old_savedir.nil? end def uninstall @@ -376,15 +374,15 @@ module Hbc gain_permissions_remove(@cask.staged_path) if !@cask.staged_path.nil? && @cask.staged_path.exist? # Homebrew-Cask metadata - if @cask.metadata_versioned_container_path.respond_to?(:children) && - @cask.metadata_versioned_container_path.exist? - @cask.metadata_versioned_container_path.children.each do |subdir| + if @cask.metadata_versioned_path.respond_to?(:children) && + @cask.metadata_versioned_path.exist? + @cask.metadata_versioned_path.children.each do |subdir| unless PERSISTENT_METADATA_SUBDIRS.include?(subdir.basename) gain_permissions_remove(subdir) end end end - @cask.metadata_versioned_container_path.rmdir_if_possible + @cask.metadata_versioned_path.rmdir_if_possible @cask.metadata_master_container_path.rmdir_if_possible # toplevel staged distribution diff --git a/Library/Homebrew/cask/lib/hbc/metadata.rb b/Library/Homebrew/cask/lib/hbc/metadata.rb new file mode 100644 index 000000000..344c38cee --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/metadata.rb @@ -0,0 +1,73 @@ +module Hbc + module Metadata + METADATA_SUBDIR = ".metadata".freeze + + def metadata_master_container_path + @metadata_master_container_path ||= caskroom_path.join(METADATA_SUBDIR) + end + + def metadata_versioned_path(version: self.version) + cask_version = (version || :unknown).to_s + + if cask_version.empty? + raise CaskError, "Cannot create metadata path with empty version." + end + + metadata_master_container_path.join(cask_version) + end + + def metadata_timestamped_path(version: self.version, timestamp: :latest, create: false) + if create && timestamp == :latest + raise CaskError, "Cannot create metadata path when timestamp is :latest." + end + + path = if timestamp == :latest + Pathname.glob(metadata_versioned_path(version: version).join("*")).sort.last + else + timestamp = new_timestamp if timestamp == :now + metadata_versioned_path(version: version).join(timestamp) + end + + if create && !path.directory? + odebug "Creating metadata directory #{path}." + path.mkpath + end + + path + end + + def metadata_subdir(leaf, version: self.version, timestamp: :latest, create: false) + if create && timestamp == :latest + raise CaskError, "Cannot create metadata subdir when timestamp is :latest." + end + + unless leaf.respond_to?(:empty?) && !leaf.empty? + raise CaskError, "Cannot create metadata subdir for empty leaf." + end + + parent = metadata_timestamped_path(version: version, timestamp: timestamp, create: create) + + return nil if parent.nil? + + subdir = parent.join(leaf) + + if create && !subdir.directory? + odebug "Creating metadata subdirectory #{subdir}." + subdir.mkpath + end + + subdir + end + + private + + def new_timestamp(time = Time.now) + time = time.utc + + timestamp = time.strftime("%Y%m%d%H%M%S") + fraction = format("%.3f", time.to_f - time.to_i)[1..-1] + + timestamp.concat(fraction) + end + end +end diff --git a/Library/Homebrew/cask/lib/hbc/pkg.rb b/Library/Homebrew/cask/lib/hbc/pkg.rb index 902d56449..c9aa3180f 100644 --- a/Library/Homebrew/cask/lib/hbc/pkg.rb +++ b/Library/Homebrew/cask/lib/hbc/pkg.rb @@ -63,7 +63,10 @@ module Hbc end def pkgutil_bom_all - @pkgutil_bom_all ||= info.fetch("paths").keys.map { |p| root.join(p) } + @pkgutil_bom_all ||= @command.run!("/usr/sbin/pkgutil", args: ["--files", package_id]) + .stdout + .split("\n") + .map { |path| root.join(path) } end def root @@ -71,7 +74,7 @@ module Hbc end def info - @info ||= @command.run!("/usr/sbin/pkgutil", args: ["--export-plist", package_id]) + @info ||= @command.run!("/usr/sbin/pkgutil", args: ["--pkg-info-plist", package_id]) .plist end diff --git a/Library/Homebrew/cask/lib/hbc/scopes.rb b/Library/Homebrew/cask/lib/hbc/scopes.rb index db12409e5..149c2c343 100644 --- a/Library/Homebrew/cask/lib/hbc/scopes.rb +++ b/Library/Homebrew/cask/lib/hbc/scopes.rb @@ -6,8 +6,7 @@ module Hbc module ClassMethods def all - @all_casks ||= {} - all_tokens.map { |t| @all_casks[t] ||= load(t) } + all_tokens.map(&CaskLoader.public_method(:load)) end def all_tapped_cask_dirs diff --git a/Library/Homebrew/cask/lib/hbc/system_command.rb b/Library/Homebrew/cask/lib/hbc/system_command.rb index c14079bc8..f1ec34025 100644 --- a/Library/Homebrew/cask/lib/hbc/system_command.rb +++ b/Library/Homebrew/cask/lib/hbc/system_command.rb @@ -92,17 +92,25 @@ module Hbc def each_line_from(sources) loop do - readable_sources = IO.select(sources)[0] - readable_sources.delete_if(&:eof?).first(1).each do |source| + selected_sources = IO.select(sources, [], [], 10) + + break if selected_sources.nil? + + readable_sources = selected_sources[0].delete_if(&:eof?) + + readable_sources.each do |source| type = (source == sources[0] ? :stdout : :stderr) + begin yield(type, source.readline_nonblock || "") rescue IO::WaitReadable, EOFError next end end + break if readable_sources.empty? end + sources.each(&:close_read) end diff --git a/Library/Homebrew/cask/lib/hbc/utils.rb b/Library/Homebrew/cask/lib/hbc/utils.rb index 3fc817dd5..2a5acbc88 100644 --- a/Library/Homebrew/cask/lib/hbc/utils.rb +++ b/Library/Homebrew/cask/lib/hbc/utils.rb @@ -4,8 +4,7 @@ require "stringio" require "hbc/utils/file" -PREBUG_URL = "https://github.com/caskroom/homebrew-cask/blob/master/doc/reporting_bugs/pre_bug_report.md".freeze -ISSUES_URL = "https://github.com/caskroom/homebrew-cask#reporting-bugs".freeze +BUG_REPORTS_URL = "https://github.com/caskroom/homebrew-cask#reporting-bugs".freeze # monkeypatch Object - not a great idea class Object @@ -39,7 +38,15 @@ module Hbc module Utils def self.gain_permissions_remove(path, command: SystemCommand) if path.respond_to?(:rmtree) && path.exist? - gain_permissions(path, ["-R"], command, &:rmtree) + gain_permissions(path, ["-R"], command) do |p| + if p.parent.writable? + p.rmtree + else + command.run("/bin/rm", + args: command_args + ["-r", "-f", "--", p], + sudo: true) + end + end elsif File.symlink?(path) gain_permissions(path, ["-h"], command, &FileUtils.method(:rm_f)) end @@ -96,11 +103,7 @@ module Hbc def self.error_message_with_suggestions <<-EOS.undent Follow the instructions here: - #{Formatter.url(PREBUG_URL)} - - If this doesn’t fix the problem, please report this bug: - #{Formatter.url(ISSUES_URL)} - + #{Formatter.url(BUG_REPORTS_URL)} EOS end @@ -112,16 +115,5 @@ module Hbc opoo(poo.join(" ") + "\n" + error_message_with_suggestions) end - - def self.nowstamp_metadata_path(container_path) - @timenow ||= Time.now.gmtime - return unless container_path.respond_to?(:join) - - precision = 3 - timestamp = @timenow.strftime("%Y%m%d%H%M%S") - fraction = format("%.#{precision}f", @timenow.to_f - @timenow.to_i)[1..-1] - timestamp.concat(fraction) - container_path.join(timestamp) - end end end diff --git a/Library/Homebrew/caveats.rb b/Library/Homebrew/caveats.rb index f5543cf49..b7c0a60c9 100644 --- a/Library/Homebrew/caveats.rb +++ b/Library/Homebrew/caveats.rb @@ -16,11 +16,9 @@ class Caveats f.build = build end caveats << keg_only_text - caveats << bash_completion_caveats - caveats << zsh_completion_caveats - caveats << fish_completion_caveats - caveats << zsh_function_caveats - caveats << fish_function_caveats + caveats << function_completion_caveats(:bash) + caveats << function_completion_caveats(:zsh) + caveats << function_completion_caveats(:fish) caveats << plist_caveats caveats << python_caveats caveats << elisp_caveats @@ -51,10 +49,10 @@ class Caveats if f.bin.directory? || f.sbin.directory? s << "\nIf you need to have this software first in your PATH run:\n" if f.bin.directory? - s << " #{Utils::Shell.prepend_path_in_shell_profile(f.opt_bin.to_s)}\n" + s << " #{Utils::Shell.prepend_path_in_profile(f.opt_bin.to_s)}\n" end if f.sbin.directory? - s << " #{Utils::Shell.prepend_path_in_shell_profile(f.opt_sbin.to_s)}\n" + s << " #{Utils::Shell.prepend_path_in_profile(f.opt_sbin.to_s)}\n" end end @@ -72,56 +70,35 @@ class Caveats s << "\n" end - def bash_completion_caveats + def function_completion_caveats(shell) return unless keg - return unless keg.completion_installed?(:bash) + return unless which(shell.to_s) - <<-EOS.undent - Bash completion has been installed to: - #{HOMEBREW_PREFIX}/etc/bash_completion.d - EOS - end - - def zsh_completion_caveats - return unless keg - return unless keg.completion_installed?(:zsh) - - <<-EOS.undent - zsh completion has been installed to: - #{HOMEBREW_PREFIX}/share/zsh/site-functions - EOS - end - - def fish_completion_caveats - return unless keg - return unless keg.completion_installed?(:fish) - return unless which("fish") + completion_installed = keg.completion_installed?(shell) + functions_installed = keg.functions_installed?(shell) + return unless completion_installed || functions_installed - <<-EOS.undent - fish completion has been installed to: - #{HOMEBREW_PREFIX}/share/fish/vendor_completions.d - EOS - end + installed = [] + installed << "completions" if completion_installed + installed << "functions" if functions_installed - def zsh_function_caveats - return unless keg - return unless keg.zsh_functions_installed? - - <<-EOS.undent - zsh functions have been installed to: - #{HOMEBREW_PREFIX}/share/zsh/site-functions - EOS - end - - def fish_function_caveats - return unless keg - return unless keg.fish_functions_installed? - return unless which("fish") - - <<-EOS.undent - fish functions have been installed to: - #{HOMEBREW_PREFIX}/share/fish/vendor_functions.d - EOS + case shell + when :bash + <<-EOS.undent + Bash completion has been installed to: + #{HOMEBREW_PREFIX}/etc/bash_completion.d + EOS + when :zsh + <<-EOS.undent + zsh #{installed.join(" and ")} have been installed to: + #{HOMEBREW_PREFIX}/share/zsh/site-functions + EOS + when :fish + fish_caveats = "fish #{installed.join(" and ")} have been installed to:" + fish_caveats << "\n #{HOMEBREW_PREFIX}/share/fish/vendor_completions.d" if completion_installed + fish_caveats << "\n #{HOMEBREW_PREFIX}/share/fish/vendor_functions.d" if functions_installed + fish_caveats + end end def python_caveats diff --git a/Library/Homebrew/cleanup.rb b/Library/Homebrew/cleanup.rb index 615a7ce9e..d1f0b2516 100644 --- a/Library/Homebrew/cleanup.rb +++ b/Library/Homebrew/cleanup.rb @@ -6,7 +6,9 @@ module Homebrew module Cleanup @disk_cleanup_size = 0 - def self.cleanup + module_function + + def cleanup cleanup_cellar cleanup_cache cleanup_logs @@ -15,34 +17,41 @@ module Homebrew rm_ds_store end - def self.update_disk_cleanup_size(path_size) + def update_disk_cleanup_size(path_size) @disk_cleanup_size += path_size end - def self.disk_cleanup_size + def disk_cleanup_size @disk_cleanup_size end - def self.cleanup_formula(formula) - formula.eligible_kegs_for_cleanup.each do |keg| - cleanup_path(keg) { keg.uninstall } - end + def unremovable_kegs + @unremovable_kegs ||= [] + end + + def cleanup_cellar(formulae = Formula.installed) + formulae.each(&method(:cleanup_formula)) + end + + def cleanup_formula(formula) + formula.eligible_kegs_for_cleanup.each(&method(:cleanup_keg)) end - def self.cleanup_logs + def cleanup_keg(keg) + cleanup_path(keg) { keg.uninstall } + rescue Errno::EACCES => e + opoo e.message + unremovable_kegs << keg + end + + def cleanup_logs return unless HOMEBREW_LOGS.directory? HOMEBREW_LOGS.subdirs.each do |dir| cleanup_path(dir) { dir.rmtree } if prune?(dir, days_default: 14) end end - def self.cleanup_cellar - Formula.installed.each do |formula| - cleanup_formula formula - end - end - - def self.cleanup_cache(cache = HOMEBREW_CACHE) + def cleanup_cache(cache = HOMEBREW_CACHE) return unless cache.directory? cache.children.each do |path| if path.to_s.end_with? ".incomplete" @@ -97,7 +106,7 @@ module Homebrew end end - def self.cleanup_path(path) + def cleanup_path(path) if ARGV.dry_run? puts "Would remove: #{path} (#{path.abv})" else @@ -108,7 +117,7 @@ module Homebrew update_disk_cleanup_size(path.disk_usage) end - def self.cleanup_lockfiles + def cleanup_lockfiles return unless HOMEBREW_LOCK_DIR.directory? candidates = HOMEBREW_LOCK_DIR.children lockfiles = candidates.select(&:file?) @@ -118,7 +127,7 @@ module Homebrew end end - def self.rm_ds_store + def rm_ds_store paths = Queue.new %w[Cellar Frameworks Library bin etc include lib opt sbin share var] .map { |p| HOMEBREW_PREFIX/p }.each { |p| paths << p if p.exist? } @@ -136,7 +145,7 @@ module Homebrew workers.map(&:join) end - def self.prune?(path, options = {}) + def prune?(path, options = {}) @time ||= Time.now path_modified_time = path.mtime diff --git a/Library/Homebrew/cmd/--env.rb b/Library/Homebrew/cmd/--env.rb index 323964dad..90beee89c 100644 --- a/Library/Homebrew/cmd/--env.rb +++ b/Library/Homebrew/cmd/--env.rb @@ -22,9 +22,9 @@ module Homebrew # legacy behavior shell = :bash unless $stdout.tty? elsif shell_value == "auto" - shell = Utils::Shell.parent_shell || Utils::Shell.preferred_shell + shell = Utils::Shell.parent || Utils::Shell.preferred elsif shell_value - shell = Utils::Shell.path_to_shell(shell_value) + shell = Utils::Shell.from_path(shell_value) end env_keys = build_env_keys(ENV) diff --git a/Library/Homebrew/cmd/cleanup.rb b/Library/Homebrew/cmd/cleanup.rb index 126309579..d8f669e85 100644 --- a/Library/Homebrew/cmd/cleanup.rb +++ b/Library/Homebrew/cmd/cleanup.rb @@ -21,11 +21,14 @@ module Homebrew if ARGV.named.empty? Cleanup.cleanup else - ARGV.resolved_formulae.each { |f| Cleanup.cleanup_formula f } + Cleanup.cleanup_cellar(ARGV.resolved_formulae) end - return if Cleanup.disk_cleanup_size.zero? + report_disk_usage unless Cleanup.disk_cleanup_size.zero? + report_unremovable_kegs unless Cleanup.unremovable_kegs.empty? + end + def report_disk_usage disk_space = disk_usage_readable(Cleanup.disk_cleanup_size) if ARGV.dry_run? ohai "This operation would free approximately #{disk_space} of disk space." @@ -33,4 +36,11 @@ module Homebrew ohai "This operation has freed approximately #{disk_space} of disk space." end end + + def report_unremovable_kegs + ofail <<-EOS.undent + Could not cleanup old kegs! Fix your permissions on: + #{Cleanup.unremovable_kegs.join "\n "} + EOS + end end diff --git a/Library/Homebrew/cmd/fetch.rb b/Library/Homebrew/cmd/fetch.rb index 820a27e31..006c63746 100644 --- a/Library/Homebrew/cmd/fetch.rb +++ b/Library/Homebrew/cmd/fetch.rb @@ -8,14 +8,14 @@ #: If `-v` is passed, do a verbose VCS checkout, if the URL represents a VCS. #: This is useful for seeing if an existing VCS cache has been updated. #: -#: If `--force` is passed, remove a previously cached version and re-fetch. +#: If `--force` (or `-f`) is passed, remove a previously cached version and re-fetch. #: #: If `--retry` is passed, retry if a download fails or re-download if the #: checksum of a previously cached version no longer matches. #: #: If `--deps` is passed, also download dependencies for any listed <formulae>. #: -#: If `--build-from-source` is passed, download the source rather than a +#: If `--build-from-source` (or `-s`) is passed, download the source rather than a #: bottle. #: #: If `--force-bottle` is passed, download a bottle if it exists for the diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb index b7de0005c..e7ad6821d 100644 --- a/Library/Homebrew/cmd/info.rb +++ b/Library/Homebrew/cmd/info.rb @@ -16,7 +16,7 @@ #: See the docs for examples of using the JSON output: #: <http://docs.brew.sh/Querying-Brew.html> -require "blacklist" +require "missing_formula" require "caveats" require "options" require "formula" @@ -54,10 +54,12 @@ module Homebrew else info_formula Formulary.find_with_priority(f) end - rescue FormulaUnavailableError - # No formula with this name, try a blacklist lookup - raise unless (blacklist = blacklisted?(f)) - puts blacklist + rescue FormulaUnavailableError => e + ofail e.message + # No formula with this name, try a missing formula lookup + if (reason = Homebrew::MissingFormula.reason(f)) + $stderr.puts reason + end end end end diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index c825e2796..394b31db0 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -4,7 +4,7 @@ #: <formula> is usually the name of the formula to install, but it can be specified #: in several different ways. See [SPECIFYING FORMULAE][]. #: -#: If `--debug` is passed and brewing fails, open an interactive debugging +#: If `--debug` (or `-d`) is passed and brewing fails, open an interactive debugging #: session with access to IRB or a shell inside the temporary build directory. #: #: If `--env=std` is passed, use the standard build environment instead of superenv. @@ -24,7 +24,7 @@ #: `gcc-4.2` for Apple's GCC 4.2, or `gcc-4.9` for a Homebrew-provided GCC #: 4.9. #: -#: If `--build-from-source` or `-s` is passed, compile the specified <formula> from +#: If `--build-from-source` (or `-s`) is passed, compile the specified <formula> from #: source even if a bottle is provided. Dependencies will still be installed #: from bottles if they are available. #: @@ -48,14 +48,15 @@ #: during installation. #: #: * `install` `--interactive` [`--git`] <formula>: -#: Download and patch <formula>, then open a shell. This allows the user to -#: run `./configure --help` and otherwise determine how to turn the software -#: package into a Homebrew formula. +#: If `--interactive` (or `-i`) is passed, download and patch <formula>, then +#: open a shell. This allows the user to run `./configure --help` and +#: otherwise determine how to turn the software package into a Homebrew +#: formula. #: -#: If `--git` is passed, Homebrew will create a Git repository, useful for +#: If `--git` (or `-g`) is passed, Homebrew will create a Git repository, useful for #: creating patches to the software. -require "blacklist" +require "missing_formula" require "diagnostic" require "cmd/search" require "formula_installer" @@ -193,56 +194,68 @@ module Homebrew next unless f.opt_prefix.directory? keg = Keg.new(f.opt_prefix.resolved_path) tab = Tab.for_keg(keg) - tab.installed_on_request = true - tab.write + unless tab.installed_on_request + tab.installed_on_request = true + tab.write + end end perform_preinstall_checks - formulae.each { |f| install_formula(f) } - rescue FormulaClassUnavailableError => e + formulae.each do |f| + Migrator.migrate_if_needed(f) + install_formula(f) + end + rescue FormulaUnreadableError, FormulaClassUnavailableError, + TapFormulaUnreadableError, TapFormulaClassUnavailableError => e # Need to rescue before `FormulaUnavailableError` (superclass of this) # is handled, as searching for a formula doesn't make sense here (the # formula was found, but there's a problem with its implementation). ofail e.message rescue FormulaUnavailableError => e - if (blacklist = blacklisted?(e.name)) - ofail "#{e.message}\n#{blacklist}" - elsif e.name == "updog" + if e.name == "updog" ofail "What's updog?" + return + end + + ofail e.message + if (reason = Homebrew::MissingFormula.reason(e.name)) + $stderr.puts reason + return + end + + query = query_regexp(e.name) + + ohai "Searching for similarly named formulae..." + formulae_search_results = search_formulae(query) + case formulae_search_results.length + when 0 + ofail "No similarly named formulae found." + when 1 + puts "This similarly named formula was found:" + puts formulae_search_results + puts "To install it, run:\n brew install #{formulae_search_results.first}" else - ofail e.message - query = query_regexp(e.name) - - ohai "Searching for similarly named formulae..." - formulae_search_results = search_formulae(query) - case formulae_search_results.length - when 0 - ofail "No similarly named formulae found." - when 1 - puts "This similarly named formula was found:" - puts formulae_search_results - puts "To install it, run:\n brew install #{formulae_search_results.first}" - else - puts "These similarly named formulae were found:" - puts Formatter.columns(formulae_search_results) - puts "To install one of them, run (for example):\n brew install #{formulae_search_results.first}" - end + puts "These similarly named formulae were found:" + puts Formatter.columns(formulae_search_results) + puts "To install one of them, run (for example):\n brew install #{formulae_search_results.first}" + end - ohai "Searching taps..." - taps_search_results = search_taps(query) - case taps_search_results.length - when 0 - ofail "No formulae found in taps." - when 1 - puts "This formula was found in a tap:" - puts taps_search_results - puts "To install it, run:\n brew install #{taps_search_results.first}" - else - puts "These formulae were found in taps:" - puts Formatter.columns(taps_search_results) - puts "To install one of them, run (for example):\n brew install #{taps_search_results.first}" - end + # Do not search taps if the formula name is qualified + return if e.name.include?("/") + ohai "Searching taps..." + taps_search_results = search_taps(query) + case taps_search_results.length + when 0 + ofail "No formulae found in taps." + when 1 + puts "This formula was found in a tap:" + puts taps_search_results + puts "To install it, run:\n brew install #{taps_search_results.first}" + else + puts "These formulae were found in taps:" + puts Formatter.columns(taps_search_results) + puts "To install one of them, run (for example):\n brew install #{taps_search_results.first}" end end end diff --git a/Library/Homebrew/cmd/link.rb b/Library/Homebrew/cmd/link.rb index 98cf98bf7..b8bd135e0 100644 --- a/Library/Homebrew/cmd/link.rb +++ b/Library/Homebrew/cmd/link.rb @@ -10,7 +10,7 @@ #: be linked or which would be deleted by `brew link --overwrite`, but will not #: actually link or delete any files. #: -#: If `--force` is passed, Homebrew will allow keg-only formulae to be linked. +#: If `--force` (or `-f`) is passed, Homebrew will allow keg-only formulae to be linked. require "ostruct" @@ -44,6 +44,7 @@ module Homebrew elsif keg_only && !ARGV.force? opoo "#{keg.name} is keg-only and must be linked with --force" puts "Note that doing so can interfere with building software." + puts_keg_only_path_message(keg) next elsif mode.dry_run && mode.overwrite puts "Would remove:" @@ -53,6 +54,7 @@ module Homebrew elsif mode.dry_run puts "Would link:" keg.link(mode) + puts_keg_only_path_message(keg) if keg_only next end @@ -69,10 +71,25 @@ module Homebrew else puts "#{n} symlinks created" end + + if keg_only && !ARGV.homebrew_developer? + puts_keg_only_path_message(keg) + end end end end + def puts_keg_only_path_message(keg) + bin = keg/"bin" + sbin = keg/"sbin" + return if !bin.directory? && !sbin.directory? + + opt = HOMEBREW_PREFIX/"opt/#{keg.name}" + puts "\nIf you need to have this software first in your PATH instead consider running:" + puts " #{Utils::Shell.prepend_path_in_profile(opt/"bin")}" if bin.directory? + puts " #{Utils::Shell.prepend_path_in_profile(opt/"sbin")}" if sbin.directory? + end + def keg_only?(rack) Formulary.from_rack(rack).keg_only? rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError diff --git a/Library/Homebrew/cmd/log.rb b/Library/Homebrew/cmd/log.rb index 22a3ee11d..9323c762d 100644 --- a/Library/Homebrew/cmd/log.rb +++ b/Library/Homebrew/cmd/log.rb @@ -9,20 +9,32 @@ module Homebrew def log if ARGV.named.empty? - cd HOMEBREW_REPOSITORY - git_log + git_log HOMEBREW_REPOSITORY else path = Formulary.path(ARGV.named.first) - cd path.dirname # supports taps - git_log path + tap = Tap.from_path(path) + git_log path.dirname, path, tap end end - def git_log(path = nil) - if File.exist? "#{`git rev-parse --show-toplevel`.chomp}/.git/shallow" + def git_log(cd_dir, path = nil, tap = nil) + cd cd_dir + repo = Utils.popen_read("git rev-parse --show-toplevel").chomp + if tap + name = tap.to_s + git_cd = "$(brew --repo #{tap})" + elsif cd_dir == HOMEBREW_REPOSITORY + name = "Homebrew/brew" + git_cd = "$(brew --repo)" + else + name, git_cd = cd_dir + end + + if File.exist? "#{repo}/.git/shallow" opoo <<-EOS.undent - The git repository is a shallow clone therefore the filtering may be incorrect. - Use `git fetch --unshallow` to get the full repository. + #{name} is a shallow clone so only partial output will be shown. + To get a full clone run: + git -C "#{git_cd}" fetch --unshallow EOS end args = ARGV.options_only diff --git a/Library/Homebrew/cmd/migrate.rb b/Library/Homebrew/cmd/migrate.rb index 2726b1480..951a2942e 100644 --- a/Library/Homebrew/cmd/migrate.rb +++ b/Library/Homebrew/cmd/migrate.rb @@ -2,7 +2,7 @@ #: Migrate renamed packages to new name, where <formulae> are old names of #: packages. #: -#: If `--force` is passed, then treat installed <formulae> and passed <formulae> +#: If `--force` (or `-f`) is passed, then treat installed <formulae> and passed <formulae> #: like if they are from same taps and migrate them anyway. require "migrator" diff --git a/Library/Homebrew/cmd/outdated.rb b/Library/Homebrew/cmd/outdated.rb index a18f4e399..f163212e1 100644 --- a/Library/Homebrew/cmd/outdated.rb +++ b/Library/Homebrew/cmd/outdated.rb @@ -7,7 +7,7 @@ #: If `--quiet` is passed, list only the names of outdated brews (takes #: precedence over `--verbose`). #: -#: If `--verbose` is passed, display detailed version information. +#: If `--verbose` (or `-v`) is passed, display detailed version information. #: #: If `--json=`<version> is passed, the output will be in JSON format. The only #: valid version is `v1`. @@ -64,7 +64,9 @@ module Homebrew "#{full_name} (#{kegs.map(&:version).join(", ")})" end.join(", ") - puts "#{outdated_versions} < #{current_version}" + pinned_version = " [pinned at #{f.pinned_version}]" if f.pinned? + + puts "#{outdated_versions} < #{current_version}#{pinned_version}" else puts f.full_installed_specified_name end @@ -86,7 +88,9 @@ module Homebrew json << { name: f.full_name, installed_versions: outdated_versions.collect(&:to_s), - current_version: current_version } + current_version: current_version, + pinned: f.pinned?, + pinned_version: f.pinned_version } end puts JSON.generate(json) diff --git a/Library/Homebrew/cmd/reinstall.rb b/Library/Homebrew/cmd/reinstall.rb index 586405909..c625d2d97 100644 --- a/Library/Homebrew/cmd/reinstall.rb +++ b/Library/Homebrew/cmd/reinstall.rb @@ -15,6 +15,7 @@ module Homebrew onoe "#{f.full_name} is pinned. You must unpin it to reinstall." next end + Migrator.migrate_if_needed(f) reinstall_formula(f) end end diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb index e834a00b5..20221524a 100644 --- a/Library/Homebrew/cmd/search.rb +++ b/Library/Homebrew/cmd/search.rb @@ -14,7 +14,7 @@ #: Search for <text> in the given package manager's list. require "formula" -require "blacklist" +require "missing_formula" require "utils" require "thread" require "official_taps" @@ -67,13 +67,12 @@ module Homebrew if $stdout.tty? count = local_results.length + tap_results.length - if msg = blacklisted?(query) + if reason = Homebrew::MissingFormula.reason(query, silent: true) if count > 0 puts - puts "If you meant #{query.inspect} precisely:" - puts + puts "If you meant #{query.inspect} specifically:" end - puts msg + puts reason elsif count.zero? puts "No formula found for #{query.inspect}." begin @@ -101,10 +100,8 @@ module Homebrew raise SEARCH_ERROR_QUEUE.pop unless SEARCH_ERROR_QUEUE.empty? end - SEARCHABLE_TAPS = OFFICIAL_TAPS.map { |tap| ["Homebrew", tap] } + [ - %w[Caskroom cask], - %w[Caskroom versions], - ] + SEARCHABLE_TAPS = OFFICIAL_TAPS.map { |tap| ["Homebrew", tap] } + + OFFICIAL_CASK_TAPS.map { |tap| ["caskroom", tap] } def query_regexp(query) case query @@ -127,7 +124,7 @@ module Homebrew regex = regex_or_string.is_a?(String) ? /^#{Regexp.escape(regex_or_string)}$/ : regex_or_string if (HOMEBREW_LIBRARY/"Taps/#{user.downcase}/homebrew-#{repo.downcase}").directory? && \ - user != "Caskroom" + user != "caskroom" return [] end diff --git a/Library/Homebrew/cmd/tap.rb b/Library/Homebrew/cmd/tap.rb index 114c4a8b6..2a07c1b2f 100644 --- a/Library/Homebrew/cmd/tap.rb +++ b/Library/Homebrew/cmd/tap.rb @@ -63,17 +63,4 @@ module Homebrew def full_clone? ARGV.include?("--full") || ARGV.homebrew_developer? end - - # @deprecated this method will be removed in the future, if no external commands use it. - def install_tap(user, repo, clone_target = nil) - opoo "Homebrew.install_tap is deprecated, use Tap#install." - tap = Tap.fetch(user, repo) - begin - tap.install(clone_target: clone_target, full_clone: full_clone?) - rescue TapAlreadyTappedError - false - else - true - end - end end diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index 5d02ebd1e..9c51a0d1c 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -1,7 +1,7 @@ #: * `uninstall`, `rm`, `remove` [`--force`] [`--ignore-dependencies`] <formula>: #: Uninstall <formula>. #: -#: If `--force` is passed, and there are multiple versions of <formula> +#: If `--force` (or `-f`) is passed, and there are multiple versions of <formula> #: installed, delete all installed versions. #: #: If `--ignore-dependencies` is passed, uninstalling won't fail, even if diff --git a/Library/Homebrew/cmd/unpack.rb b/Library/Homebrew/cmd/unpack.rb index 60d796d9f..89992e1f0 100644 --- a/Library/Homebrew/cmd/unpack.rb +++ b/Library/Homebrew/cmd/unpack.rb @@ -6,7 +6,7 @@ #: If `--patch` is passed, patches for <formulae> will be applied to the #: unpacked source. #: -#: If `--git` is passed, a Git repository will be initialized in the unpacked +#: If `--git` (or `-g`) is passed, a Git repository will be initialized in the unpacked #: source. This is useful for creating patches for the software. require "stringio" diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index c13d82090..aa1fd244d 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -361,7 +361,10 @@ class Reporter case status when "A", "D" - @report[status.to_sym] << tap.formula_file_to_name(src) + full_name = tap.formula_file_to_name(src) + name = full_name.split("/").last + new_tap = tap.tap_migrations[name] + @report[status.to_sym] << full_name unless new_tap when "M" begin formula = Formulary.factory(tap.path/src) @@ -499,9 +502,21 @@ class Reporter end def migrate_formula_rename - report[:R].each do |old_full_name, new_full_name| - old_name = old_full_name.split("/").last - next unless (dir = HOMEBREW_CELLAR/old_name).directory? && !dir.subdirs.empty? + Formula.installed.each do |formula| + next unless Migrator.needs_migration?(formula) + + oldname = formula.oldname + oldname_rack = HOMEBREW_CELLAR/oldname + + if oldname_rack.subdirs.empty? + oldname_rack.rmdir_if_possible + next + end + + new_name = tap.formula_renames[oldname] + next unless new_name + + new_full_name = "#{tap}/#{new_name}" begin f = Formulary.factory(new_full_name) @@ -510,13 +525,7 @@ class Reporter next end - begin - migrator = Migrator.new(f) - migrator.migrate - rescue Migrator::MigratorDifferentTapsError - rescue Exception => e - onoe e - end + Migrator.migrate_if_needed(f) end end @@ -573,14 +582,17 @@ class ReporterHub def dump_formula_report(key, title) formulae = select_formula(key).sort.map do |name, new_name| # Format list items of renamed formulae - if key == :R + case key + when :R name = pretty_installed(name) if installed?(name) new_name = pretty_installed(new_name) if installed?(new_name) "#{name} -> #{new_name}" + when :A + name unless installed?(name) else installed?(name) ? pretty_installed(name) : name end - end + end.compact return if formulae.empty? # Dump formula list. diff --git a/Library/Homebrew/cmd/update.sh b/Library/Homebrew/cmd/update.sh index 5cfdb3f46..197a99f2e 100644 --- a/Library/Homebrew/cmd/update.sh +++ b/Library/Homebrew/cmd/update.sh @@ -5,7 +5,7 @@ #: If `--merge` is specified then `git merge` is used to include updates #: (rather than `git rebase`). #: -#: If `--force` is specified then always do a slower, full update check even +#: If `--force` (or `-f`) is specified then always do a slower, full update check even #: if unnecessary. # Hide shellcheck complaint: diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb index ed36b8f33..d007ff8c8 100644 --- a/Library/Homebrew/cmd/upgrade.rb +++ b/Library/Homebrew/cmd/upgrade.rb @@ -88,6 +88,7 @@ module Homebrew end formulae_to_install.each do |f| + Migrator.migrate_if_needed(f) upgrade_formula(f) next unless ARGV.include?("--cleanup") next unless f.installed? diff --git a/Library/Homebrew/cmd/uses.rb b/Library/Homebrew/cmd/uses.rb index b1122c90a..bab174184 100644 --- a/Library/Homebrew/cmd/uses.rb +++ b/Library/Homebrew/cmd/uses.rb @@ -28,7 +28,16 @@ module Homebrew def uses raise FormulaUnspecifiedError if ARGV.named.empty? - used_formulae = ARGV.formulae + used_formulae_missing = false + used_formulae = begin + ARGV.formulae + rescue FormulaUnavailableError => e + opoo e + used_formulae_missing = true + # If the formula doesn't exist: fake the needed formula object name. + ARGV.named.map { |name| OpenStruct.new name: name, full_name: name } + end + formulae = ARGV.include?("--installed") ? Formula.installed : Formula recursive = ARGV.flag? "--recursive" includes = [] @@ -115,5 +124,6 @@ module Homebrew return if uses.empty? puts Formatter.columns(uses.map(&:full_name)) + odie "Missing formulae should not have dependents!" if used_formulae_missing end end diff --git a/Library/Homebrew/compat.rb b/Library/Homebrew/compat.rb index 92b687725..3c080f616 100644 --- a/Library/Homebrew/compat.rb +++ b/Library/Homebrew/compat.rb @@ -25,3 +25,4 @@ require "compat/tab" require "compat/ENV/shared" require "compat/ENV/std" require "compat/ENV/super" +require "compat/utils/shell" diff --git a/Library/Homebrew/compat/ARGV.rb b/Library/Homebrew/compat/ARGV.rb index 23d02ce1a..e5fa8188f 100644 --- a/Library/Homebrew/compat/ARGV.rb +++ b/Library/Homebrew/compat/ARGV.rb @@ -1,6 +1,6 @@ module HomebrewArgvExtension def build_32_bit? - # odeprecated "ARGV.build_32_bit?" + odeprecated "ARGV.build_32_bit?" include? "--32-bit" end end diff --git a/Library/Homebrew/compat/build_options.rb b/Library/Homebrew/compat/build_options.rb index 52aa9b951..73722dadb 100644 --- a/Library/Homebrew/compat/build_options.rb +++ b/Library/Homebrew/compat/build_options.rb @@ -1,6 +1,11 @@ class BuildOptions def build_32_bit? - # odeprecated "build.build_32_bit?" + odeprecated "build.build_32_bit?" include?("32-bit") && option_defined?("32-bit") end + + def build_bottle? + odeprecated "build.build_bottle?", "build.bottle?" + bottle? + end end diff --git a/Library/Homebrew/compat/dependency_collector.rb b/Library/Homebrew/compat/dependency_collector.rb index bd72c55d4..fbcf1c2a0 100644 --- a/Library/Homebrew/compat/dependency_collector.rb +++ b/Library/Homebrew/compat/dependency_collector.rb @@ -14,7 +14,6 @@ class DependencyCollector output_deprecation(spec, tags) Dependency.new(spec.to_s, tags) when :apr - # TODO: reenable in future when we've fixed a few of the audits. # output_deprecation(spec, tags, "apr-util") Dependency.new("apr-util", tags) when :libltdl diff --git a/Library/Homebrew/compat/global.rb b/Library/Homebrew/compat/global.rb index 797e9ffe5..82c452cc0 100644 --- a/Library/Homebrew/compat/global.rb +++ b/Library/Homebrew/compat/global.rb @@ -3,7 +3,7 @@ module Homebrew def method_missing(method, *args, &block) if instance_methods.include?(method) - # odeprecated "#{self}##{method}", "'module_function' or 'def self.#{method}' to convert it to a class method" + odeprecated "#{self}##{method}", "'module_function' or 'def self.#{method}' to convert it to a class method" return instance_method(method).bind(self).call(*args, &block) end super diff --git a/Library/Homebrew/compat/software_spec.rb b/Library/Homebrew/compat/software_spec.rb index 51b0f3a0b..5efd2aeb4 100644 --- a/Library/Homebrew/compat/software_spec.rb +++ b/Library/Homebrew/compat/software_spec.rb @@ -1,7 +1,5 @@ class BottleSpecification def revision(*args) - # Don't announce deprecation yet as this is quite a big change - # to a public interface. # odeprecated "BottleSpecification.revision", "BottleSpecification.rebuild" rebuild(*args) end diff --git a/Library/Homebrew/compat/tab.rb b/Library/Homebrew/compat/tab.rb index 58fdc4913..2cf71c923 100644 --- a/Library/Homebrew/compat/tab.rb +++ b/Library/Homebrew/compat/tab.rb @@ -1,6 +1,6 @@ class Tab < OpenStruct def build_32_bit? - # odeprecated "Tab.build_32_bit?" + odeprecated "Tab.build_32_bit?" include?("32-bit") end end diff --git a/Library/Homebrew/compat/tap.rb b/Library/Homebrew/compat/tap.rb index d1cf7f1d5..37b1eeac1 100644 --- a/Library/Homebrew/compat/tap.rb +++ b/Library/Homebrew/compat/tap.rb @@ -6,5 +6,3 @@ class Tap core_tap? end end - -CoreFormulaRepository = CoreTap diff --git a/Library/Homebrew/compat/utils.rb b/Library/Homebrew/compat/utils.rb index 56b0824ba..3842e8a83 100644 --- a/Library/Homebrew/compat/utils.rb +++ b/Library/Homebrew/compat/utils.rb @@ -1,18 +1,13 @@ -# return the shell profile file based on users' preference shell def shell_profile - opoo "shell_profile has been deprecated in favor of Utils::Shell.profile" - case ENV["SHELL"] - when %r{/(ba)?sh} then "~/.bash_profile" - when %r{/zsh} then "~/.zshrc" - when %r{/ksh} then "~/.kshrc" - else "~/.bash_profile" - end + # odeprecated "shell_profile", "Utils::Shell.profile" + Utils::Shell.profile end module Tty module_function def white + odeprecated "Tty.white", "Tty.reset.bold" reset.bold end end diff --git a/Library/Homebrew/compat/utils/shell.rb b/Library/Homebrew/compat/utils/shell.rb new file mode 100644 index 000000000..161f10ebb --- /dev/null +++ b/Library/Homebrew/compat/utils/shell.rb @@ -0,0 +1,8 @@ +module Utils + module Shell + def self.shell_profile + odeprecated "Utils::Shell.shell_profile", "Utils::Shell.profile" + Utils::Shell.profile + end + end +end diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index 677a52447..95d54caee 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -1,4 +1,4 @@ -#: * `audit` [`--strict`] [`--fix`] [`--online`] [`--new-formula`] [`--display-cop-names`] [`--display-filename`] [<formulae>]: +#: * `audit` [`--strict`] [`--fix`] [`--online`] [`--new-formula`] [`--display-cop-names`] [`--display-filename`] [`--only=`<method>|`--except=`<method] [<formulae>]: #: Check <formulae> for Homebrew coding style violations. This should be #: run before submitting a new formula. #: @@ -23,6 +23,10 @@ #: If `--display-filename` is passed, every line of output is prefixed with the #: name of the file or formula being audited, to make the output easy to grep. #: +#: If `--only` is passed, only the methods named `audit_<method>` will be run. +#: +#: If `--except` is passed, the methods named `audit_<method>` will not be run. +#: #: `audit` exits with a non-zero status if any errors are found. This is useful, #: for instance, for implementing pre-commit hooks. @@ -38,7 +42,7 @@ require "official_taps" require "cmd/search" require "cmd/style" require "date" -require "blacklist" +require "missing_formula" require "digest" module Homebrew @@ -331,25 +335,44 @@ class FormulaAuditor problem "File should end with a newline" unless text.trailing_newline? - versioned_formulae = Dir[formula.path.to_s.gsub(/\.rb$/, "@*.rb")] - needs_versioned_alias = !versioned_formulae.empty? && - formula.tap && - formula.aliases.grep(/.@\d/).empty? - if needs_versioned_alias - _, last_alias_version = File.basename(versioned_formulae.sort.reverse.first) - .gsub(/\.rb$/, "") - .split("@") - major, minor, = formula.version.to_s.split(".") - alias_name = if last_alias_version.split(".").length == 1 - "#{formula.name}@#{major}" - else - "#{formula.name}@#{major}.#{minor}" + if formula.versioned_formula? + unversioned_formula = begin + # build this ourselves as we want e.g. homebrew/core to be present + full_name = if formula.tap + "#{formula.tap}/#{formula.name}" + else + formula.name + end + Formulary.factory(full_name.gsub(/@.*$/, "")).path + rescue FormulaUnavailableError, TapFormulaAmbiguityError, + TapFormulaWithOldnameAmbiguityError + Pathname.new formula.path.to_s.gsub(/@.*\.rb$/, ".rb") + end + unless unversioned_formula.exist? + unversioned_name = unversioned_formula.basename(".rb") + problem "#{formula} is versioned but no #{unversioned_name} formula exists" + end + elsif ARGV.build_stable? + versioned_formulae = Dir[formula.path.to_s.gsub(/\.rb$/, "@*.rb")] + needs_versioned_alias = !versioned_formulae.empty? && + formula.tap && + formula.aliases.grep(/.@\d/).empty? + if needs_versioned_alias + _, last_alias_version = File.basename(versioned_formulae.sort.reverse.first) + .gsub(/\.rb$/, "") + .split("@") + major, minor, = formula.version.to_s.split(".") + alias_name = if last_alias_version.split(".").length == 1 + "#{formula.name}@#{major}" + else + "#{formula.name}@#{major}.#{minor}" + end + problem <<-EOS.undent + Formula has other versions so create an alias: + cd #{formula.tap.alias_dir} + ln -s #{formula.path.to_s.gsub(formula.tap.path, "..")} #{alias_name} + EOS end - problem <<-EOS.undent - Formula has other versions so create an alias: - cd #{formula.tap.alias_dir} - ln -s #{formula.path.to_s.gsub(formula.tap.path, "..")} #{alias_name} - EOS end return unless @strict @@ -399,7 +422,7 @@ class FormulaAuditor name = formula.name full_name = formula.full_name - if blacklisted?(name) + if Homebrew::MissingFormula.blacklisted_reason(name) problem "'#{name}' is blacklisted." end @@ -468,10 +491,16 @@ class FormulaAuditor end if @@aliases.include?(dep.name) && - (core_formula? || !dep_f.versioned_formula?) + (dep_f.core_formula? || !dep_f.versioned_formula?) problem "Dependency '#{dep.name}' is an alias; use the canonical name '#{dep.to_formula.full_name}'." end + if @new_formula && dep_f.keg_only_reason && + !["openssl", "apr", "apr-util"].include?(dep.name) && + [:provided_by_macos, :provided_by_osx].include?(dep_f.keg_only_reason.reason) + problem "Dependency '#{dep.name}' may be unnecessary as it is provided by macOS; try to build this formula without it." + end + dep.options.reject do |opt| next true if dep_f.option_defined?(opt) dep_f.requirements.detect do |r| @@ -572,39 +601,6 @@ class FormulaAuditor problem "New formulae should not use `deprecated_option`." end - def audit_desc - # For now, only check the description when using `--strict` - return unless @strict - - desc = formula.desc - - unless desc && !desc.empty? - problem "Formula should have a desc (Description)." - return - end - - # Make sure the formula name plus description is no longer than 80 characters - # Note full_name includes the name of the tap, while name does not - linelength = "#{formula.name}: #{desc}".length - if linelength > 80 - problem <<-EOS.undent - Description is too long. \"name: desc\" should be less than 80 characters. - Length is calculated as #{formula.name} + desc. (currently #{linelength}) - EOS - end - - if desc =~ /([Cc]ommand ?line)/ - problem "Description should use \"command-line\" instead of \"#{$1}\"" - end - - if desc =~ /^([Aa]n?)\s/ - problem "Description shouldn't start with an indefinite article (#{$1})" - end - - return unless desc.downcase.start_with? "#{formula.name} " - problem "Description shouldn't include the formula name" - end - def audit_homepage homepage = formula.homepage @@ -747,7 +743,10 @@ class FormulaAuditor } end - spec.patches.each { |p| audit_patch(p) if p.external? } + next if spec.patches.empty? + spec.patches.each { |p| patch_problems(p) if p.external? } + next unless @new_formula + problem "New formulae should not require patches to build. Patches should be submitted and accepted upstream first." end %w[Stable Devel].each do |name| @@ -775,7 +774,7 @@ class FormulaAuditor automysqlbackup 3.0-rc6 aview 1.3.0rc1 distcc 3.2rc1 - elm-format 0.5.2-alpha + elm-format 0.6.0-alpha ftgl 2.1.3-rc5 hidapi 0.8.0-rc1 libcaca 0.99b19 @@ -883,10 +882,10 @@ class FormulaAuditor return if legacy_patches.empty? problem "Use the patch DSL instead of defining a 'patches' method" - legacy_patches.each { |p| audit_patch(p) } + legacy_patches.each { |p| patch_problems(p) } end - def audit_patch(patch) + def patch_problems(patch) case patch.url when /raw\.github\.com/, %r{gist\.github\.com/raw}, %r{gist\.github\.com/.+/raw}, %r{gist\.githubusercontent\.com/.+/raw} @@ -958,7 +957,13 @@ class FormulaAuditor problem "require \"language/go\" is unnecessary unless using `go_resource`s" end - def audit_line(line, _lineno) + def audit_lines + text.without_patch.split("\n").each_with_index do |line, lineno| + line_problems(line, lineno+1) + end + end + + def line_problems(line, _lineno) if line =~ /<(Formula|AmazonWebServicesFormula|ScriptFileFormula|GithubGistFormula)/ problem "Use a space in class inheritance: class Foo < #{$1}" end @@ -1036,6 +1041,18 @@ class FormulaAuditor problem ":apr is deprecated. Usage should be \"apr-util\"" end + if line =~ /depends_on :tex/ + problem ":tex is deprecated" + end + + if line =~ /depends_on\s+['"](.+)['"]\s+=>\s+:(lua|perl|python|ruby)(\d*)/ + problem "#{$2} modules should be vendored rather than use deprecated `depends_on \"#{$1}\" => :#{$2}#{$3}`" + end + + if line =~ /depends_on\s+['"](.+)['"]\s+=>\s+.*['"](.+)['"]/ + problem "Dependency #{$1} should not use option #{$2}" + end + # Commented-out depends_on problem "Commented-out dep #{$1}" if line =~ /#\s*depends_on\s+(.+)\s*$/ @@ -1064,6 +1081,14 @@ class FormulaAuditor problem "Use ENV instead of invoking '#{$1}' to modify the environment" end + if formula.name != "wine" && line =~ /ENV\.universal_binary/ + problem "macOS has been 64-bit only since 10.6 so ENV.universal_binary is deprecated." + end + + if line =~ /build\.universal\?/ + problem "macOS has been 64-bit only so build.universal? is deprecated." + end + if line =~ /version == ['"]HEAD['"]/ problem "Use 'build.head?' instead of inspecting 'version'" end @@ -1093,11 +1118,11 @@ class FormulaAuditor end if line =~ /(not\s|!)\s*build\.with?\?/ - problem "Don't negate 'build.without?': use 'build.with?'" + problem "Don't negate 'build.with?': use 'build.without?'" end if line =~ /(not\s|!)\s*build\.without?\?/ - problem "Don't negate 'build.with?': use 'build.without?'" + problem "Don't negate 'build.without?': use 'build.with?'" end if line =~ /ARGV\.(?!(debug\?|verbose\?|value[\(\s]))/ @@ -1145,11 +1170,11 @@ class FormulaAuditor end if line =~ /depends_on :(.+) (if.+|unless.+)$/ - audit_conditional_dep($1.to_sym, $2, $&) + conditional_dep_problems($1.to_sym, $2, $&) end if line =~ /depends_on ['"](.+)['"] (if.+|unless.+)$/ - audit_conditional_dep($1, $2, $&) + conditional_dep_problems($1, $2, $&) end if line =~ /(Dir\[("[^\*{},]+")\])/ @@ -1166,7 +1191,8 @@ class FormulaAuditor problem "Use `assert_match` instead of `assert ...include?`" end - if line.include?('system "npm", "install"') && !line.include?("Language::Node") && formula.name !~ /^kibana(\d{2})?$/ + if line.include?('system "npm", "install"') && !line.include?("Language::Node") && + formula.name !~ /^kibana(\@\d+(\.\d+)?)?$/ problem "Use Language::Node for npm install args" end @@ -1174,6 +1200,10 @@ class FormulaAuditor problem "'fails_with :llvm' is now a no-op so should be removed" end + if line =~ /system\s+['"](otool|install_name_tool|lipo)/ && formula.name != "cctools" + problem "Use ruby-macho instead of calling #{$1}" + end + if formula.tap.to_s == "homebrew/core" ["OS.mac?", "OS.linux?"].each do |check| next unless line.include?(check) @@ -1232,7 +1262,7 @@ class FormulaAuditor EOS end - def audit_conditional_dep(dep, condition, line) + def conditional_dep_problems(dep, condition, line) quoted_dep = quote_dep(dep) dep = Regexp.escape(dep.to_s) @@ -1248,31 +1278,26 @@ class FormulaAuditor dep.is_a?(Symbol) ? dep.inspect : "'#{dep}'" end - def audit_check_output(output) + def problem_if_output(output) problem(output) if output end def audit - audit_file - audit_formula_name - audit_class - audit_specs - audit_revision_and_version_scheme - audit_desc - audit_homepage - audit_bottle_spec - audit_github_repository - audit_deps - audit_conflicts - audit_options - audit_legacy_patches - audit_text - audit_caveats - text.without_patch.split("\n").each_with_index { |line, lineno| audit_line(line, lineno+1) } - audit_installed - audit_prefix_has_contents - audit_reverse_migration - audit_style + only_audits = ARGV.value("only").to_s.split(",") + except_audits = ARGV.value("except").to_s.split(",") + if !only_audits.empty? && !except_audits.empty? + odie "--only and --except cannot be used simulataneously!" + end + + methods.map(&:to_s).grep(/^audit_/).each do |audit_method_name| + name = audit_method_name.gsub(/^audit_/, "") + if !only_audits.empty? + next unless only_audits.include?(name) + elsif !except_audits.empty? + next if except_audits.include?(name) + end + send(audit_method_name) + end end private @@ -1403,8 +1428,8 @@ class ResourceAuditor def audit_urls # Check GNU urls; doesn't apply to mirrors - if url =~ %r{^(?:https?|ftp)://(?!alpha).+/gnu/} - problem "Please use \"https://ftpmirror.gnu.org\" instead of #{url}." + if url =~ %r{^(?:https?|ftp)://ftpmirror.gnu.org/(.*)} + problem "Please use \"https://ftp.gnu.org/gnu/#{$1}\" instead of #{url}." end if mirrors.include?(url) diff --git a/Library/Homebrew/dev-cmd/boneyard-formula-pr.rb b/Library/Homebrew/dev-cmd/boneyard-formula-pr.rb deleted file mode 100644 index 7531ef9cf..000000000 --- a/Library/Homebrew/dev-cmd/boneyard-formula-pr.rb +++ /dev/null @@ -1,166 +0,0 @@ -#: @hide_from_man_page -#: * `boneyard-formula-pr` [`--dry-run`] [`--local`] [`--reason=<reason>`] <formula> : -#: Creates a pull request to boneyard a formula. -#: -#: If `--dry-run` is passed, print what would be done rather than doing it. -#: -#: If `--local` is passed, perform only local operations (i.e. don't push or create PR). -#: -#: If `--reason=<reason>` is passed, append this to the commit/PR message. - -require "formula" -require "json" -require "fileutils" - -begin - require "json" -rescue LoadError - puts "Homebrew does not provide Ruby dependencies; install with:" - puts " gem install json" - odie "Dependency json is not installed." -end - -module Homebrew - module_function - - def boneyard_formula_pr - local_only = ARGV.include?("--local") - formula = ARGV.formulae.first - reason = ARGV.value("reason") - odie "No formula found!" unless formula - - formula_relpath = formula.path.relative_path_from(formula.tap.path) - formula_file = "#{formula.name}.rb" - bottle_block = File.read(formula.path).include? " bottle do" - boneyard_tap = Tap.fetch("homebrew", "boneyard") - tap_migrations_path = formula.tap.path/"tap_migrations.json" - if ARGV.dry_run? - ohai "brew update" - ohai "brew tap #{boneyard_tap.name}" - ohai "cd #{formula.tap.path}" - cd formula.tap.path - ohai "cp #{formula_relpath} #{boneyard_tap.path}" - ohai "git rm #{formula_relpath}" - unless File.exist? tap_migrations_path - ohai "Creating tap_migrations.json for #{formula.tap.name}" - ohai "git add #{tap_migrations_path}" - end - ohai "Loading tap_migrations.json" - ohai "Adding #{formula.name} to tap_migrations.json" - else - safe_system HOMEBREW_BREW_FILE, "update" - safe_system HOMEBREW_BREW_FILE, "tap", boneyard_tap.name - cd formula.tap.path - cp formula_relpath, boneyard_tap.formula_dir - safe_system "git", "rm", formula_relpath - unless File.exist? tap_migrations_path - tap_migrations_path.write <<-EOS.undent - { - } - EOS - safe_system "git", "add", tap_migrations_path - end - tap_migrations = JSON.parse(File.read(tap_migrations_path)) - tap_migrations[formula.name] = boneyard_tap.name - tap_migrations = tap_migrations.sort.inject({}) { |acc, elem| acc.merge!(elem[0] => elem[1]) } - tap_migrations_path.atomic_write(JSON.pretty_generate(tap_migrations) + "\n") - end - unless which("hub") || local_only - if ARGV.dry_run? - ohai "brew install hub" - else - safe_system HOMEBREW_BREW_FILE, "install", "hub" - end - end - branch = "#{formula.name}-boneyard" - - reason = " because #{reason}" if reason - - if ARGV.dry_run? - ohai "cd #{formula.tap.path}" - ohai "git checkout --no-track -b #{branch} origin/master" - ohai "git commit --no-edit --verbose --message=\"#{formula.name}: migrate to boneyard\" -- #{formula_relpath} #{tap_migrations_path.basename}" - - unless local_only - ohai "hub fork --no-remote" - ohai "hub fork" - ohai "hub fork (to read $HUB_REMOTE)" - ohai "git push $HUB_REMOTE #{branch}:#{branch}" - ohai "hub pull-request -m $'#{formula.name}: migrate to boneyard\\n\\nCreated with `brew boneyard-formula-pr`#{reason}.'" - end - - ohai "git checkout -" - else - cd formula.tap.path - safe_system "git", "checkout", "--no-track", "-b", branch, "origin/master" - safe_system "git", "commit", "--no-edit", "--verbose", - "--message=#{formula.name}: migrate to boneyard", - "--", formula_relpath, tap_migrations_path.basename - - unless local_only - safe_system "hub", "fork", "--no-remote" - quiet_system "hub", "fork" - remote = Utils.popen_read("hub fork 2>&1")[/fatal: remote (.+) already exists\./, 1] - odie "cannot get remote from 'hub'!" unless remote - safe_system "git", "push", remote, "#{branch}:#{branch}" - pr_message = <<-EOS.undent - #{formula.name}: migrate to boneyard - - Created with `brew boneyard-formula-pr`#{reason}. - EOS - pr_url = Utils.popen_read("hub", "pull-request", "-m", pr_message).chomp - end - - safe_system "git", "checkout", "-" - end - - if ARGV.dry_run? - ohai "cd #{boneyard_tap.path}" - ohai "git checkout --no-track -b #{branch} origin/master" - if bottle_block - ohai "Removing bottle block" - else - ohai "No bottle block to remove" - end - ohai "git add #{formula_file}" - ohai "git commit --no-edit --verbose --message=\"#{formula.name}: migrate from #{formula.tap.repo}\" -- #{formula_file}" - - unless local_only - ohai "hub fork --no-remote" - ohai "hub fork" - ohai "hub fork (to read $HUB_REMOTE)" - ohai "git push $HUB_REMOTE #{branch}:#{branch}" - ohai "hub pull-request --browse -m $'#{formula.name}: migrate from #{formula.tap.repo}\\n\\nGoes together with $PR_URL\\n\\nCreated with `brew boneyard-formula-pr`#{reason}.'" - end - - ohai "git checkout -" - else - cd boneyard_tap.formula_dir - safe_system "git", "checkout", "--no-track", "-b", branch, "origin/master" - if bottle_block - Utils::Inreplace.inreplace formula_file, / bottle do.+?end\n\n/m, "" - end - safe_system "git", "add", formula_file - safe_system "git", "commit", "--no-edit", "--verbose", - "--message=#{formula.name}: migrate from #{formula.tap.repo}", - "--", formula_file - - unless local_only - safe_system "hub", "fork", "--no-remote" - quiet_system "hub", "fork" - remote = Utils.popen_read("hub fork 2>&1")[/fatal: remote (.+) already exists\./, 1] - odie "cannot get remote from 'hub'!" unless remote - safe_system "git", "push", remote, "#{branch}:#{branch}" - safe_system "hub", "pull-request", "--browse", "-m", <<-EOS.undent - #{formula.name}: migrate from #{formula.tap.repo} - - Goes together with #{pr_url}. - - Created with `brew boneyard-formula-pr`#{reason}. - EOS - end - - safe_system "git", "checkout", "-" - end - end -end diff --git a/Library/Homebrew/dev-cmd/bottle.rb b/Library/Homebrew/dev-cmd/bottle.rb index b11da5607..8d3038a5a 100644 --- a/Library/Homebrew/dev-cmd/bottle.rb +++ b/Library/Homebrew/dev-cmd/bottle.rb @@ -6,7 +6,7 @@ #: generated DSL. Passing `--keep-old` will attempt to keep it at its #: original value, while `--no-rebuild` will remove it. #: -#: If `--verbose` is passed, print the bottling commands and any warnings +#: If `--verbose` (or `-v`) is passed, print the bottling commands and any warnings #: encountered. #: #: If `--skip-relocation` is passed, do not check if the bottle can be marked diff --git a/Library/Homebrew/dev-cmd/create.rb b/Library/Homebrew/dev-cmd/create.rb index 9c58dc71a..908f65f8f 100644 --- a/Library/Homebrew/dev-cmd/create.rb +++ b/Library/Homebrew/dev-cmd/create.rb @@ -10,7 +10,8 @@ #: If `--meson` is passed, create a basic template for a Meson-style build. #: #: If `--no-fetch` is passed, Homebrew will not download <URL> to the cache and -#: will thus not add the SHA256 to the formula for you. +#: will thus not add the SHA256 to the formula for you. It will also not check +#: the GitHub API for GitHub projects (to fill out the description and homepage). #: #: The options `--set-name` and `--set-version` each take an argument and allow #: you to explicitly set the name and version of the package you are creating. @@ -19,7 +20,7 @@ #: the specified tap. require "formula" -require "blacklist" +require "missing_formula" require "digest" require "erb" @@ -28,15 +29,6 @@ module Homebrew # Create a formula from a tarball URL def create - # Allow searching MacPorts or Fink. - if ARGV.include? "--macports" - opoo "`brew create --macports` is deprecated; use `brew search --macports` instead" - exec_browser "https://www.macports.org/ports.php?by=name&substr=#{ARGV.next}" - elsif ARGV.include? "--fink" - opoo "`brew create --fink` is deprecated; use `brew search --fink` instead" - exec_browser "http://pdb.finkproject.org/pdb/browse.php?summary=#{ARGV.next}" - end - raise UsageError if ARGV.named.empty? # Ensure that the cache exists so we can fetch the tarball @@ -73,8 +65,8 @@ module Homebrew # Don't allow blacklisted formula, or names that shadow aliases, # unless --force is specified. unless ARGV.force? - if msg = blacklisted?(fc.name) - raise "#{fc.name} is blacklisted for creation.\n#{msg}\nIf you really want to create this formula use --force." + if reason = Homebrew::MissingFormula.blacklisted_reason(fc.name) + raise "#{fc.name} is blacklisted for creation.\n#{reason}\nIf you really want to create this formula use --force." end if Formula.aliases.include? fc.name @@ -100,7 +92,7 @@ module Homebrew end class FormulaCreator - attr_reader :url, :sha256 + attr_reader :url, :sha256, :desc, :homepage attr_accessor :name, :version, :tap, :path, :mode def url=(url) @@ -108,11 +100,15 @@ class FormulaCreator path = Pathname.new(url) if @name.nil? case url - when %r{github\.com/\S+/(\S+)\.git} - @name = $1 + when %r{github\.com/(\S+)/(\S+)\.git} + @user = $1 + @name = $2 @head = true - when %r{github\.com/\S+/(\S+)/archive/} - @name = $1 + @github = true + when %r{github\.com/(\S+)/(\S+)/(archive|releases)/} + @user = $1 + @name = $2 + @github = true else @name = path.basename.to_s[/(.*?)[-_.]?#{Regexp.escape(path.version.to_s)}/, 1] end @@ -131,7 +127,7 @@ class FormulaCreator end def fetch? - !head? && !ARGV.include?("--no-fetch") + !ARGV.include?("--no-fetch") end def head? @@ -145,11 +141,25 @@ class FormulaCreator opoo "Version cannot be determined from URL." puts "You'll need to add an explicit 'version' to the formula." elsif fetch? - r = Resource.new - r.url(url) - r.version(version) - r.owner = self - @sha256 = r.fetch.sha256 if r.download_strategy == CurlDownloadStrategy + unless head? + r = Resource.new + r.url(url) + r.version(version) + r.owner = self + @sha256 = r.fetch.sha256 if r.download_strategy == CurlDownloadStrategy + end + + if @user && @name + begin + metadata = GitHub.repository(@user, @name) + @desc = metadata["description"] + @homepage = metadata["homepage"] + rescue GitHub::HTTPNotFoundError + # If there was no repository found assume the network connection is at + # fault rather than the input URL. + nil + end + end end path.write ERB.new(template, nil, ">").result(binding) @@ -161,8 +171,8 @@ class FormulaCreator # PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST! class #{Formulary.class_s(name)} < Formula - desc "" - homepage "" + desc "#{desc}" + homepage "#{homepage}" <% if head? %> head "#{url}" <% else %> diff --git a/Library/Homebrew/dev-cmd/man.rb b/Library/Homebrew/dev-cmd/man.rb index 581db38ca..4e5103910 100644 --- a/Library/Homebrew/dev-cmd/man.rb +++ b/Library/Homebrew/dev-cmd/man.rb @@ -27,7 +27,7 @@ module Homebrew regenerate_man_pages - if system "git", "-C", HOMEBREW_REPOSITORY, "diff", "--quiet", "docs/brew.1.html", "manpages" + if system "git", "-C", HOMEBREW_REPOSITORY, "diff", "--quiet", "docs/Manpage.md", "manpages" puts "No changes to manpage output detected." elsif ARGV.include?("--fail-if-changed") Homebrew.failed = true @@ -38,7 +38,7 @@ module Homebrew Homebrew.install_gem_setup_path! "ronn" markup = build_man_page - convert_man_page(markup, TARGET_DOC_PATH/"brew.1.html") + convert_man_page(markup, TARGET_DOC_PATH/"Manpage.md") convert_man_page(markup, TARGET_MAN_PATH/"brew.1") cask_markup = (SOURCE_PATH/"brew-cask.1.md").read @@ -53,8 +53,7 @@ module Homebrew .grep(/^#:/) .map { |line| line.slice(2..-1) } .join - end - .reject { |s| s.strip.empty? || s.include?("@hide_from_man_page") } + end.reject { |s| s.strip.empty? || s.include?("@hide_from_man_page") } end def build_man_page @@ -107,13 +106,15 @@ module Homebrew Utils.popen(["ronn", format_flag] + shared_args, "rb+") do |ronn| ronn.write markup ronn.close_write - target.atomic_write ronn.read + ronn_output = ronn.read + ronn_output.gsub!(%r{</?var>}, "`") if format_flag == "--markdown" + target.atomic_write ronn_output end end def target_path_to_format(target) case target.basename - when /\.html?$/ then ["--fragment", "HTML fragment"] + when /\.md$/ then ["--markdown", "markdown"] when /\.\d$/ then ["--roff", "man page"] else odie "Failed to infer output format from '#{target.basename}'." diff --git a/Library/Homebrew/dev-cmd/pull.rb b/Library/Homebrew/dev-cmd/pull.rb index ec89b14b8..36c9ac27c 100644 --- a/Library/Homebrew/dev-cmd/pull.rb +++ b/Library/Homebrew/dev-cmd/pull.rb @@ -1,4 +1,5 @@ -#: * `pull` [`--bottle`] [`--bump`] [`--clean`] [`--ignore-whitespace`] [`--resolve`] [`--branch-okay`] [`--no-pbcopy`] [`--no-publish`] <patch-source> [<patch-source>]: +#: * `pull` [`--bottle`] [`--bump`] [`--clean`] [`--ignore-whitespace`] [`--resolve`] [`--branch-okay`] [`--no-pbcopy`] [`--no-publish`] [`--warn-on-publish-failure`] <patch-source> [<patch-source>]: +#: #: Gets a patch from a GitHub commit or pull request and applies it to Homebrew. #: Optionally, installs the formulae changed by the patch. #: @@ -37,6 +38,9 @@ #: clipboard. #: #: If `--no-publish` is passed, do not publish bottles to Bintray. +#: +#: If `--warn-on-publish-failure` was passed, do not exit if there's a +#: failure publishing bottles on Bintray. require "net/http" require "net/https" @@ -264,7 +268,7 @@ module Homebrew changed_formulae_names.each do |name| f = Formula[name] next if f.bottle_unneeded? || f.bottle_disabled? - publish_bottle_file_on_bintray(f, bintray_creds) + next unless publish_bottle_file_on_bintray(f, bintray_creds) published << f.full_name end else @@ -425,7 +429,7 @@ module Homebrew end unless info.bottle_info_any opoo "No bottle defined in formula #{package}" - return + return false end version = info.pkg_version ohai "Publishing on Bintray: #{package} #{version}" @@ -434,6 +438,11 @@ module Homebrew "-H", "Content-Type: application/json", "-d", '{"publish_wait_for_secs": 0}', "https://api.bintray.com/content/homebrew/#{repo}/#{package}/#{version}/publish" + true + rescue => e + raise unless ARGV.include?("--warn-on-publish-failure") + onoe e + false end # Formula info drawn from an external "brew info --json" call diff --git a/Library/Homebrew/dev-cmd/tap-new.rb b/Library/Homebrew/dev-cmd/tap-new.rb index 6e1977446..df295bf26 100644 --- a/Library/Homebrew/dev-cmd/tap-new.rb +++ b/Library/Homebrew/dev-cmd/tap-new.rb @@ -60,6 +60,7 @@ module Homebrew - git -C "$HOMEBREW_REPOSITORY" reset --hard origin/master - brew update || brew update - HOMEBREW_TAP_DIR="$(brew --repo "$TRAVIS_REPO_SLUG")" + - mkdir -p "$HOMEBREW_TAP_DIR" - rm -rf "$HOMEBREW_TAP_DIR" - ln -s "$PWD" "$HOMEBREW_TAP_DIR" - export HOMEBREW_DEVELOPER="1" diff --git a/Library/Homebrew/dev-cmd/test.rb b/Library/Homebrew/dev-cmd/test.rb index 4898629b0..288aa8a87 100644 --- a/Library/Homebrew/dev-cmd/test.rb +++ b/Library/Homebrew/dev-cmd/test.rb @@ -7,7 +7,7 @@ #: To test the development or head version of a formula, use `--devel` or #: `--HEAD`. #: -#: If `--debug` is passed and the test fails, an interactive debugger will be +#: If `--debug` (or `-d`) is passed and the test fails, an interactive debugger will be #: launched with access to IRB or a shell inside the temporary test directory. #: #: If `--keep-tmp` is passed, the temporary files created for the test are diff --git a/Library/Homebrew/dev-cmd/tests.rb b/Library/Homebrew/dev-cmd/tests.rb index 4f7f9e771..72d6143fc 100644 --- a/Library/Homebrew/dev-cmd/tests.rb +++ b/Library/Homebrew/dev-cmd/tests.rb @@ -3,7 +3,7 @@ #: `--only=`<test_script> runs only <test_script>_spec.rb, and `--seed` #: randomizes tests with the provided value instead of a random seed. #: -#: If `--verbose` is passed, print the command that runs the tests. +#: If `--verbose` (or `-v`) is passed, print the command that runs the tests. #: #: If `--coverage` is passed, also generate code coverage reports. #: @@ -28,11 +28,17 @@ module Homebrew ENV.delete("HOMEBREW_VERBOSE") ENV.delete("VERBOSE") ENV.delete("HOMEBREW_CASK_OPTS") + ENV.delete("HOMEBREW_TEMP") ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"] = "1" ENV["HOMEBREW_DEVELOPER"] = "1" ENV["HOMEBREW_NO_COMPAT"] = "1" if ARGV.include? "--no-compat" ENV["HOMEBREW_TEST_GENERIC_OS"] = "1" if ARGV.include? "--generic" - ENV["HOMEBREW_NO_GITHUB_API"] = "1" unless ARGV.include? "--online" + + if ARGV.include? "--online" + ENV["HOMEBREW_TEST_ONLINE"] = "1" + else + ENV["HOMEBREW_NO_GITHUB_API"] = "1" + end if ARGV.include? "--official-cmd-taps" ENV["HOMEBREW_TEST_OFFICIAL_CMD_TAPS"] = "1" @@ -96,6 +102,10 @@ module Homebrew files = files.reject { |p| p =~ %r{^test/(os/mac|cask)(/.*|_spec\.rb)$} } end + unless OS.linux? + files = files.reject { |p| p =~ %r{^test/os/linux(/.*|_spec\.rb)$} } + end + if parallel system "bundle", "exec", "parallel_rspec", *opts, "--", *args, "--", *files else diff --git a/Library/Homebrew/dev-cmd/update-test.rb b/Library/Homebrew/dev-cmd/update-test.rb index 2ff168669..add05bc7c 100644 --- a/Library/Homebrew/dev-cmd/update-test.rb +++ b/Library/Homebrew/dev-cmd/update-test.rb @@ -33,12 +33,24 @@ module Homebrew elsif date = ARGV.value("before") Utils.popen_read("git", "rev-list", "-n1", "--before=#{date}", "origin/master").chomp elsif ARGV.include?("--to-tag") - Utils.popen_read("git", "tag", "--list", "--sort=-version:refname").lines[1].chomp + previous_tag = + Utils.popen_read("git", "tag", "--list", "--sort=-version:refname").lines[1] + unless previous_tag + safe_system "git", "fetch", "--tags", "--depth=1" + previous_tag = + Utils.popen_read("git", "tag", "--list", "--sort=-version:refname").lines[1] + end + previous_tag.to_s.chomp else Utils.popen_read("git", "rev-parse", "origin/master").chomp end + odie "Could not find start commit!" if start_commit.empty? + start_commit = Utils.popen_read("git", "rev-parse", start_commit).chomp + odie "Could not find start commit!" if start_commit.empty? + end_commit = Utils.popen_read("git", "rev-parse", "HEAD").chomp + odie "Could not find end commit!" if end_commit.empty? puts "Start commit: #{start_commit}" puts "End commit: #{end_commit}" diff --git a/Library/Homebrew/diagnostic.rb b/Library/Homebrew/diagnostic.rb index c8c4b83d2..3002a0a67 100644 --- a/Library/Homebrew/diagnostic.rb +++ b/Library/Homebrew/diagnostic.rb @@ -460,7 +460,7 @@ module Homebrew Consider setting your PATH so that #{HOMEBREW_PREFIX}/bin occurs before /usr/bin. Here is a one-liner: - #{Utils::Shell.prepend_path_in_shell_profile("#{HOMEBREW_PREFIX}/bin")} + #{Utils::Shell.prepend_path_in_profile("#{HOMEBREW_PREFIX}/bin")} EOS end end @@ -480,7 +480,7 @@ module Homebrew <<-EOS.undent Homebrew's bin was not found in your PATH. Consider setting the PATH for example like so - #{Utils::Shell.prepend_path_in_shell_profile("#{HOMEBREW_PREFIX}/bin")} + #{Utils::Shell.prepend_path_in_profile("#{HOMEBREW_PREFIX}/bin")} EOS end @@ -495,7 +495,7 @@ module Homebrew Homebrew's sbin was not found in your PATH but you have installed formulae that put executables in #{HOMEBREW_PREFIX}/sbin. Consider setting the PATH for example like so - #{Utils::Shell.prepend_path_in_shell_profile("#{HOMEBREW_PREFIX}/sbin")} + #{Utils::Shell.prepend_path_in_profile("#{HOMEBREW_PREFIX}/sbin")} EOS end @@ -753,13 +753,14 @@ module Homebrew def check_git_version # https://help.github.com/articles/https-cloning-errors return unless Utils.git_available? - return unless Version.create(Utils.git_version) < Version.create("1.7.10") + return unless Version.create(Utils.git_version) < Version.create("1.8.5") git = Formula["git"] git_upgrade_cmd = git.any_version_installed? ? "upgrade" : "install" <<-EOS.undent An outdated version (#{Utils.git_version}) of Git was detected in your PATH. - Git 1.7.10 or newer is required to perform checkouts over HTTPS from GitHub. + Git 1.8.5 or newer is required to perform checkouts over HTTPS from GitHub and + to support the 'git -C <path>' option. Please upgrade: brew #{git_upgrade_cmd} git EOS @@ -794,31 +795,59 @@ module Homebrew EOS end - def check_git_origin + def check_brew_git_origin return if !Utils.git_available? || !(HOMEBREW_REPOSITORY/".git").exist? origin = HOMEBREW_REPOSITORY.git_origin if origin.nil? <<-EOS.undent - Missing git origin remote. + Missing Homebrew/brew git origin remote. Without a correctly configured origin, Homebrew won't update properly. You can solve this by adding the Homebrew remote: - cd #{HOMEBREW_REPOSITORY} - git remote add origin #{Formatter.url("https://github.com/Homebrew/brew.git")} + git -C "#{HOMEBREW_REPOSITORY}" remote add origin #{Formatter.url("https://github.com/Homebrew/brew.git")} EOS - elsif origin !~ %r{Homebrew/brew(\.git)?$} + elsif origin !~ %r{Homebrew/brew(\.git|/)?$} <<-EOS.undent - Suspicious git origin remote found. + Suspicious Homebrew/brew git origin remote found. With a non-standard origin, Homebrew won't pull updates from the main repository. The current git origin is: #{origin} Unless you have compelling reasons, consider setting the - origin remote to point at the main repository, located at: - #{Formatter.url("https://github.com/Homebrew/brew.git")} + origin remote to point at the main repository by running: + git -C "#{HOMEBREW_REPOSITORY}" remote add origin #{Formatter.url("https://github.com/Homebrew/brew.git")} + EOS + end + end + + def check_coretap_git_origin + coretap_path = CoreTap.instance.path + return if !Utils.git_available? || !(coretap_path/".git").exist? + + origin = coretap_path.git_origin + + if origin.nil? + <<-EOS.undent + Missing #{CoreTap.instance} git origin remote. + + Without a correctly configured origin, Homebrew won't update + properly. You can solve this by adding the Homebrew remote: + git -C "#{coretap_path}" remote add origin #{Formatter.url("https://github.com/Homebrew/homebrew-core.git")} + EOS + elsif origin !~ %r{Homebrew/homebrew-core(\.git|/)?$} + <<-EOS.undent + Suspicious #{CoreTap.instance} git origin remote found. + + With a non-standard origin, Homebrew won't pull updates from + the main repository. The current git origin is: + #{origin} + + Unless you have compelling reasons, consider setting the + origin remote to point at the main repository by running: + git -C "#{coretap_path}" remote add origin #{Formatter.url("https://github.com/Homebrew/homebrew-core.git")} EOS end end diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index 77da4489e..6751b2224 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -77,35 +77,11 @@ class FormulaUnavailableError < RuntimeError end end -class TapFormulaUnavailableError < FormulaUnavailableError - attr_reader :tap, :user, :repo - - def initialize(tap, name) - @tap = tap - @user = tap.user - @repo = tap.repo - super "#{tap}/#{name}" - end - - def to_s - s = super - s += "\nPlease tap it and then try again: brew tap #{tap}" unless tap.installed? - s - end -end - -class FormulaClassUnavailableError < FormulaUnavailableError +module FormulaClassUnavailableErrorModule attr_reader :path attr_reader :class_name attr_reader :class_list - def initialize(name, path, class_name, class_list) - @path = path - @class_name = class_name - @class_list = class_list - super name - end - def to_s s = super s += "\nIn formula file: #{path}" @@ -131,16 +107,70 @@ class FormulaClassUnavailableError < FormulaUnavailableError end end -class FormulaUnreadableError < FormulaUnavailableError +class FormulaClassUnavailableError < FormulaUnavailableError + include FormulaClassUnavailableErrorModule + + def initialize(name, path, class_name, class_list) + @path = path + @class_name = class_name + @class_list = class_list + super name + end +end + +module FormulaUnreadableErrorModule attr_reader :formula_error + def to_s + "#{name}: " + formula_error.to_s + end +end + +class FormulaUnreadableError < FormulaUnavailableError + include FormulaUnreadableErrorModule + def initialize(name, error) super(name) @formula_error = error end +end + +class TapFormulaUnavailableError < FormulaUnavailableError + attr_reader :tap, :user, :repo + + def initialize(tap, name) + @tap = tap + @user = tap.user + @repo = tap.repo + super "#{tap}/#{name}" + end def to_s - "#{name}: " + formula_error.to_s + s = super + s += "\nPlease tap it and then try again: brew tap #{tap}" unless tap.installed? + s + end +end + +class TapFormulaClassUnavailableError < TapFormulaUnavailableError + include FormulaClassUnavailableErrorModule + + attr_reader :tap + + def initialize(tap, name, path, class_name, class_list) + @path = path + @class_name = class_name + @class_list = class_list + super tap, name + end +end + +class TapFormulaUnreadableError < TapFormulaUnavailableError + include FormulaUnreadableErrorModule + + def initialize(tap, name, error) + super(tap, name) + @formula_error = error end end @@ -363,14 +393,6 @@ class BuildError < RuntimeError end end - if formula.tap && formula.tap.name == "homebrew/boneyard" - onoe <<-EOS.undent - #{formula} was moved to homebrew-boneyard because it has unfixable issues. - Please do not file any issues about this. Sorry! - EOS - return - end - if formula.tap && defined?(OS::ISSUES_URL) if formula.tap.official? puts Formatter.error(Formatter.url(OS::ISSUES_URL), label: "READ THIS") diff --git a/Library/Homebrew/extend/ENV/super.rb b/Library/Homebrew/extend/ENV/super.rb index 0935647f5..4d6d96ad3 100644 --- a/Library/Homebrew/extend/ENV/super.rb +++ b/Library/Homebrew/extend/ENV/super.rb @@ -326,9 +326,7 @@ module Superenv def set_x11_env_if_installed end - # This method does nothing in superenv since there's no custom CFLAGS API - # @private - def set_cpu_flags(*_args) + def set_cpu_flags(*) end end diff --git a/Library/Homebrew/extend/io.rb b/Library/Homebrew/extend/io.rb index 1357293cd..53bca196e 100644 --- a/Library/Homebrew/extend/io.rb +++ b/Library/Homebrew/extend/io.rb @@ -1,10 +1,17 @@ class IO def readline_nonblock(sep = $INPUT_RECORD_SEPARATOR) + line = "" buffer = "" - buffer.concat(read_nonblock(1)) while buffer[-1] != sep - buffer + + loop do + break if buffer == sep + read_nonblock(1, buffer) + line.concat(buffer) + end + + line rescue IO::WaitReadable, EOFError => e - raise e if buffer.empty? - buffer + raise e if line.empty? + line end end diff --git a/Library/Homebrew/extend/os/blacklist.rb b/Library/Homebrew/extend/os/blacklist.rb deleted file mode 100644 index 932040f82..000000000 --- a/Library/Homebrew/extend/os/blacklist.rb +++ /dev/null @@ -1,2 +0,0 @@ -require "blacklist" -require "extend/os/mac/blacklist" if OS.mac? diff --git a/Library/Homebrew/extend/os/linux/hardware/cpu.rb b/Library/Homebrew/extend/os/linux/hardware/cpu.rb index 4c8aa3f42..7c945b237 100644 --- a/Library/Homebrew/extend/os/linux/hardware/cpu.rb +++ b/Library/Homebrew/extend/os/linux/hardware/cpu.rb @@ -49,6 +49,8 @@ module Hardware :haswell when 0x3d, 0x47, 0x4f, 0x56 :broadwell + when 0x5e + :skylake when 0x8e :kabylake else diff --git a/Library/Homebrew/extend/os/linux/requirements/osxfuse_requirement.rb b/Library/Homebrew/extend/os/linux/requirements/osxfuse_requirement.rb new file mode 100644 index 000000000..3fd847bc4 --- /dev/null +++ b/Library/Homebrew/extend/os/linux/requirements/osxfuse_requirement.rb @@ -0,0 +1,39 @@ +require "requirement" + +class OsxfuseRequirement < Requirement + download "https://github.com/libfuse/libfuse" + + satisfy(build_env: false) do + next true if libfuse_formula_exists? && Formula["libfuse"].installed? + includedirs = %w[ + /usr/include + /usr/local/include + ] + next true if (includedirs.map do |dir| + File.exist? "#{dir}/fuse.h" + end).any? + false + end + + def message + msg = "libfuse is required to install this formula.\n" + if libfuse_formula_exists? + msg + <<-EOS.undent + Run "brew install libfuse" to install it. + EOS + else + msg + super + end + end + + private + + def libfuse_formula_exists? + begin + Formula["libfuse"] + rescue FormulaUnavailableError + return false + end + true + end +end diff --git a/Library/Homebrew/extend/os/mac/blacklist.rb b/Library/Homebrew/extend/os/mac/blacklist.rb deleted file mode 100644 index edff4697e..000000000 --- a/Library/Homebrew/extend/os/mac/blacklist.rb +++ /dev/null @@ -1,16 +0,0 @@ -def blacklisted?(name) - case name.downcase - when "xcode" - if MacOS.version >= :lion - <<-EOS.undent - Xcode can be installed from the App Store. - EOS - else - <<-EOS.undent - Xcode can be installed from https://developer.apple.com/xcode/downloads/ - EOS - end - else - generic_blacklisted?(name) - end -end diff --git a/Library/Homebrew/extend/os/mac/development_tools.rb b/Library/Homebrew/extend/os/mac/development_tools.rb index f54c6b8a3..caa85ffca 100644 --- a/Library/Homebrew/extend/os/mac/development_tools.rb +++ b/Library/Homebrew/extend/os/mac/development_tools.rb @@ -31,13 +31,13 @@ class DevelopmentTools elsif MacOS.version == "10.8" || MacOS.version == "10.7" <<-EOS.undent Install the Command Line Tools from - https://developer.apple.com/downloads/ + https://developer.apple.com/download/more/ or via Xcode's preferences. EOS else <<-EOS.undent Install Xcode from - https://developer.apple.com/xcode/downloads/ + https://developer.apple.com/download/more/ EOS end end diff --git a/Library/Homebrew/extend/os/mac/diagnostic.rb b/Library/Homebrew/extend/os/mac/diagnostic.rb index c7dabde2b..ff936c75a 100644 --- a/Library/Homebrew/extend/os/mac/diagnostic.rb +++ b/Library/Homebrew/extend/os/mac/diagnostic.rb @@ -81,6 +81,12 @@ module Homebrew return unless MacOS::CLT.installed? return unless MacOS::CLT.outdated? + # Travis 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 provider + # Homebrew/brew is currently using. + return if ENV["TRAVIS"] + <<-EOS.undent A newer Command Line Tools release is available. #{MacOS::CLT.update_instructions} @@ -257,7 +263,7 @@ module Homebrew SSL_CERT_DIR support was removed from Apple's curl. If fetching formulae fails you should: unset SSL_CERT_DIR - and remove it from #{Utils::Shell.shell_profile} if present. + and remove it from #{Utils::Shell.profile} if present. EOS end diff --git a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb index 5b1c648bf..10379c981 100644 --- a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb +++ b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb @@ -67,7 +67,7 @@ module FormulaCellarChecks checker = LinkageChecker.new(keg, formula) return unless checker.broken_dylibs? - audit_check_output <<-EOS.undent + problem_if_output <<-EOS.undent The installation was broken. Broken dylib links found: #{checker.broken_dylibs.to_a * "\n "} @@ -76,9 +76,9 @@ module FormulaCellarChecks def audit_installed generic_audit_installed - audit_check_output(check_shadowed_headers) - audit_check_output(check_openssl_links) - audit_check_output(check_python_framework_links(formula.lib)) + problem_if_output(check_shadowed_headers) + problem_if_output(check_openssl_links) + problem_if_output(check_python_framework_links(formula.lib)) check_linkage end end diff --git a/Library/Homebrew/extend/os/mac/hardware/cpu.rb b/Library/Homebrew/extend/os/mac/hardware/cpu.rb index f180995fb..22d118e1a 100644 --- a/Library/Homebrew/extend/os/mac/hardware/cpu.rb +++ b/Library/Homebrew/extend/os/mac/hardware/cpu.rb @@ -107,6 +107,20 @@ module Hardware end end + # Determines whether the current CPU and macOS combination + # can run an executable of the specified architecture. + # `arch` is a symbol in the same format returned by + # Hardware::CPU.family + def can_run?(arch) + if Hardware::CPU.intel? + intel_can_run? arch + elsif Hardware::CPU.ppc? + ppc_can_run? arch + else + false + end + end + def features @features ||= sysctl_n( "machdep.cpu.features", @@ -162,6 +176,35 @@ module Hardware @properties[keys] = Utils.popen_read("/usr/sbin/sysctl", "-n", *keys) end end + + def intel_can_run?(arch) + case arch + when :ppc + # Rosetta is still available + MacOS.version < :lion + when :ppc64 + # Rosetta never supported PPC64 + false + when :x86_64 + Hardware::CPU.is_64_bit? + when :i386 + true + else # dunno + false + end + end + + def ppc_can_run?(arch) + case arch + when :ppc + true + when :ppc64 + Hardware::CPU.is_64_bit? + else + # Intel is never supported + false + end + end end end end diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index 476e5da4a..0b2ecd1c9 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -78,19 +78,13 @@ class Keg end end - def filename_contains_metavariable?(fn) - fn =~ /^@(loader_|executable_|r)path/ - end - def each_install_name_for(file, &block) dylibs = file.dynamically_linked_libraries - dylibs.reject! { |fn| filename_contains_metavariable?(fn) } + dylibs.reject! { |fn| fn =~ /^@(loader_|executable_|r)path/ } dylibs.each(&block) end def dylib_id_for(file) - return file.dylib_id if filename_contains_metavariable?(file.dylib_id) - # 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) @@ -131,6 +125,12 @@ class Keg mach_o_files 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 self.file_linked_libraries(file, string) # Check dynamic library linkage. Importantly, do not perform for static # libraries, which will falsely report "linkage" to themselves. diff --git a/Library/Homebrew/extend/os/mac/missing_formula.rb b/Library/Homebrew/extend/os/mac/missing_formula.rb new file mode 100644 index 000000000..bcf0eb25d --- /dev/null +++ b/Library/Homebrew/extend/os/mac/missing_formula.rb @@ -0,0 +1,22 @@ +module Homebrew + module MissingFormula + class << self + def blacklisted_reason(name) + case name.downcase + when "xcode" + if MacOS.version >= :lion + <<-EOS.undent + Xcode can be installed from the App Store. + EOS + else + <<-EOS.undent + Xcode can be installed from #{Formatter.url("https://developer.apple.com/download/more/")}. + EOS + end + else + generic_blacklisted_reason(name) + end + end + end + end +end diff --git a/Library/Homebrew/extend/os/mac/requirements/osxfuse_requirement.rb b/Library/Homebrew/extend/os/mac/requirements/osxfuse_requirement.rb new file mode 100644 index 000000000..8c898a272 --- /dev/null +++ b/Library/Homebrew/extend/os/mac/requirements/osxfuse_requirement.rb @@ -0,0 +1,34 @@ +require "requirement" + +class OsxfuseRequirement < Requirement + cask "osxfuse" + download "https://osxfuse.github.io/" + + satisfy(build_env: false) { self.class.binary_osxfuse_installed? } + + def self.binary_osxfuse_installed? + File.exist?("/usr/local/include/osxfuse/fuse.h") && + !File.symlink?("/usr/local/include/osxfuse") + end + + env do + ENV.append_path "PKG_CONFIG_PATH", HOMEBREW_LIBRARY/"Homebrew/os/mac/pkgconfig/fuse" + + unless HOMEBREW_PREFIX.to_s == "/usr/local" + ENV.append_path "HOMEBREW_LIBRARY_PATHS", "/usr/local/lib" + ENV.append_path "HOMEBREW_INCLUDE_PATHS", "/usr/local/include/osxfuse" + end + end +end + +class NonBinaryOsxfuseRequirement < Requirement + fatal true + satisfy(build_env: false) { HOMEBREW_PREFIX.to_s != "/usr/local" || !OsxfuseRequirement.binary_osxfuse_installed? } + + def message + <<-EOS.undent + osxfuse is already installed from the binary distribution and + conflicts with this formula. + EOS + end +end diff --git a/Library/Homebrew/extend/os/missing_formula.rb b/Library/Homebrew/extend/os/missing_formula.rb new file mode 100644 index 000000000..02c59f4e4 --- /dev/null +++ b/Library/Homebrew/extend/os/missing_formula.rb @@ -0,0 +1,2 @@ +require "missing_formula" +require "extend/os/mac/missing_formula" if OS.mac? diff --git a/Library/Homebrew/extend/os/requirements/osxfuse_requirement.rb b/Library/Homebrew/extend/os/requirements/osxfuse_requirement.rb new file mode 100644 index 000000000..5f56d48c4 --- /dev/null +++ b/Library/Homebrew/extend/os/requirements/osxfuse_requirement.rb @@ -0,0 +1,7 @@ +require "requirements/osxfuse_requirement" + +if OS.mac? + require "extend/os/mac/requirements/osxfuse_requirement" +elsif OS.linux? + require "extend/os/linux/requirements/osxfuse_requirement" +end diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb index eb254c624..690979e4e 100644 --- a/Library/Homebrew/extend/pathname.rb +++ b/Library/Homebrew/extend/pathname.rb @@ -365,9 +365,8 @@ class Pathname unless method_defined?(:/) def /(other) - unless other.respond_to?(:to_str) || other.respond_to?(:to_path) - opoo "Pathname#/ called on #{inspect} with #{other.inspect} as an argument" - puts "This behavior is deprecated, please pass either a String or a Pathname" + if !other.respond_to?(:to_str) && !other.respond_to?(:to_path) + odeprecated "Pathname#/ with #{other.class}", "a String or a Pathname" end self + other.to_s end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 443619206..aec004b0b 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -252,12 +252,14 @@ class Formula public - # The alias path that was used to install this formula, if present. + # The alias path that was used to install this formula, if it exists. # Can differ from alias_path, which is the alias used to find the formula, # and is specified to this instance. def installed_alias_path path = build.source["path"] if build.is_a?(Tab) - path if path =~ %r{#{HOMEBREW_TAP_DIR_REGEX}/Aliases} + return unless path =~ %r{#{HOMEBREW_TAP_DIR_REGEX}/Aliases} + return unless File.symlink?(path) + path end def installed_alias_name @@ -1555,6 +1557,8 @@ class Formula hide.include?(d.name) || d.installed_prefixes.empty? end missing_dependencies + rescue FormulaUnavailableError + [] end # @private @@ -2392,7 +2396,6 @@ class Formula # version '4.8.1' # end</pre> def fails_with(compiler, &block) - # TODO: deprecate this in future. # odeprecated "fails_with :llvm" if compiler == :llvm specs.each { |spec| spec.fails_with(compiler, &block) } end diff --git a/Library/Homebrew/formula_cellar_checks.rb b/Library/Homebrew/formula_cellar_checks.rb index 7db5e748b..7f7d77569 100644 --- a/Library/Homebrew/formula_cellar_checks.rb +++ b/Library/Homebrew/formula_cellar_checks.rb @@ -14,7 +14,7 @@ module FormulaCellarChecks <<-EOS.undent #{prefix_bin} is not in your PATH - You can amend this by altering your #{Utils::Shell.shell_profile} file + You can amend this by altering your #{Utils::Shell.profile} file EOS end @@ -154,17 +154,17 @@ module FormulaCellarChecks end def audit_installed - audit_check_output(check_manpages) - audit_check_output(check_infopages) - audit_check_output(check_jars) - audit_check_output(check_non_libraries) - audit_check_output(check_non_executables(formula.bin)) - audit_check_output(check_generic_executables(formula.bin)) - audit_check_output(check_non_executables(formula.sbin)) - audit_check_output(check_generic_executables(formula.sbin)) - audit_check_output(check_easy_install_pth(formula.lib)) - audit_check_output(check_elisp_dirname(formula.share, formula.name)) - audit_check_output(check_elisp_root(formula.share, formula.name)) + problem_if_output(check_manpages) + problem_if_output(check_infopages) + problem_if_output(check_jars) + problem_if_output(check_non_libraries) + problem_if_output(check_non_executables(formula.bin)) + problem_if_output(check_generic_executables(formula.bin)) + problem_if_output(check_non_executables(formula.sbin)) + problem_if_output(check_generic_executables(formula.sbin)) + problem_if_output(check_easy_install_pth(formula.lib)) + problem_if_output(check_elisp_dirname(formula.share, formula.name)) + problem_if_output(check_elisp_root(formula.share, formula.name)) end alias generic_audit_installed audit_installed diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 1f91ad5c4..7fed4b24e 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -173,34 +173,22 @@ class FormulaInstaller EOS end - if ENV["HOMEBREW_CHECK_RECURSIVE_VERSION_DEPENDENCIES"] - version_hash = {} - version_conflicts = Set.new - recursive_formulae.each do |f| - name = f.name - unversioned_name, = name.split("@") - version_hash[unversioned_name] ||= Set.new - version_hash[unversioned_name] << name - next if version_hash[unversioned_name].length < 2 - version_conflicts += version_hash[unversioned_name] - end - unless version_conflicts.empty? - raise CannotInstallFormulaError, <<-EOS.undent - #{formula.full_name} contains conflicting version recursive dependencies: - #{version_conflicts.to_a.join ", "} - View these with `brew deps --tree #{formula.full_name}`. - EOS - end - end - - unless ENV["HOMEBREW_NO_CHECK_UNLINKED_DEPENDENCIES"] - unlinked_deps = recursive_formulae.select do |dep| - dep.installed? && !dep.keg_only? && !dep.linked_keg.directory? - end - - unless unlinked_deps.empty? - raise CannotInstallFormulaError, "You must `brew link #{unlinked_deps*" "}` before #{formula.full_name} can be installed" - end + version_hash = {} + version_conflicts = Set.new + recursive_formulae.each do |f| + name = f.name + unversioned_name, = name.split("@") + version_hash[unversioned_name] ||= Set.new + version_hash[unversioned_name] << name + next if version_hash[unversioned_name].length < 2 + version_conflicts += version_hash[unversioned_name] + end + unless version_conflicts.empty? + raise CannotInstallFormulaError, <<-EOS.undent + #{formula.full_name} contains conflicting version recursive dependencies: + #{version_conflicts.to_a.join ", "} + View these with `brew deps --tree #{formula.full_name}`. + EOS end pinned_unsatisfied_deps = recursive_deps.select do |dep| @@ -410,10 +398,13 @@ class FormulaInstaller end def install_requirement_formula?(req, dependent, build) - return false unless req.to_dependency + req_dependency = req.to_dependency + return false unless req_dependency return true unless req.satisfied? return false if req.run? - install_bottle_for?(dependent, build) || build_bottle? + return true if build_bottle? + return true if req.satisfied_by_formula? + install_bottle_for?(dependent, build) end def expand_requirements @@ -855,15 +846,15 @@ class FormulaInstaller tab.write end - def audit_check_output(output) + def problem_if_output(output) return unless output opoo output @show_summary_heading = true end def audit_installed - audit_check_output(check_env_path(formula.bin)) - audit_check_output(check_env_path(formula.sbin)) + problem_if_output(check_env_path(formula.bin)) + problem_if_output(check_env_path(formula.sbin)) super end diff --git a/Library/Homebrew/formula_support.rb b/Library/Homebrew/formula_support.rb index dcb995a6b..b8476f5cc 100644 --- a/Library/Homebrew/formula_support.rb +++ b/Library/Homebrew/formula_support.rb @@ -4,6 +4,8 @@ FormulaConflict = Struct.new(:name, :reason) # Used to annotate formulae that duplicate macOS provided software # or cause conflicts when linked in. class KegOnlyReason + attr_reader :reason + def initialize(reason, explanation) @reason = reason @explanation = explanation diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 4a65f8704..f5e6a2eb9 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -210,6 +210,10 @@ module Formulary def get_formula(spec, alias_path: nil) super + rescue FormulaUnreadableError => e + raise TapFormulaUnreadableError.new(tap, name, e.formula_error), "", e.backtrace + rescue FormulaClassUnavailableError => e + raise TapFormulaClassUnavailableError.new(tap, name, e.path, e.class_name, e.class_list), "", e.backtrace rescue FormulaUnavailableError => e raise TapFormulaUnavailableError.new(tap, name), "", e.backtrace end @@ -402,7 +406,7 @@ module Formulary end def self.tap_paths(name, taps = Dir["#{HOMEBREW_LIBRARY}/Taps/*/*/"]) - name = name.downcase + name = name.to_s.downcase taps.map do |tap| Pathname.glob([ "#{tap}Formula/#{name}.rb", diff --git a/Library/Homebrew/gpg.rb b/Library/Homebrew/gpg.rb index 066f67864..cb9e367df 100644 --- a/Library/Homebrew/gpg.rb +++ b/Library/Homebrew/gpg.rb @@ -5,7 +5,10 @@ class Gpg which_all(executable).detect do |gpg| gpg_short_version = Utils.popen_read(gpg, "--version")[/\d\.\d/, 0] next unless gpg_short_version - Version.create(gpg_short_version.to_s) == Version.create("2.0") + gpg_version = Version.create(gpg_short_version.to_s) + @version = gpg_version + gpg_version == Version.create("2.0") || + gpg_version == Version.create("2.1") end end @@ -23,6 +26,10 @@ class Gpg File.executable?(GPG_EXECUTABLE.to_s) end + def self.version + @version if available? + end + def self.create_test_key(path) odie "No GPG present to test against!" unless available? diff --git a/Library/Homebrew/hardware.rb b/Library/Homebrew/hardware.rb index fe07aee9d..997598def 100644 --- a/Library/Homebrew/hardware.rb +++ b/Library/Homebrew/hardware.rb @@ -80,6 +80,18 @@ module Hardware def feature?(name) features.include?(name) end + + def can_run?(arch) + if is_32_bit? + arch_32_bit == arch + elsif intel? + [:i386, :x86_64].include? arch + elsif ppc? + [:ppc, :ppc64].include? arch + else + false + end + end end end diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index 86733860c..d4b9c5d77 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -240,8 +240,9 @@ class Keg def remove_opt_record opt_record.unlink aliases.each do |a| - next if !opt_record.symlink? && !opt_record.exist? - (opt_record.parent/a).delete + alias_symlink = opt_record.parent/a + next if !alias_symlink.symlink? && !alias_symlink.exist? + alias_symlink.delete end opt_record.parent.rmdir_if_possible end @@ -302,23 +303,24 @@ class Keg dir = case shell when :bash then path.join("etc", "bash_completion.d") when :zsh - dir = path.join("share", "zsh", "site-functions") - dir if dir && dir.directory? && dir.children.any? { |f| f.basename.to_s.start_with?("_") } - when :fish then path.join("share", "fish", "vendor_completions.d") + dir = path/"share/zsh/site-functions" + dir if dir.directory? && dir.children.any? { |f| f.basename.to_s.start_with?("_") } + when :fish then path/"share/fish/vendor_completions.d" end dir && dir.directory? && !dir.children.empty? end - def zsh_functions_installed? - # Check for non completion functions (i.e. files not started with an underscore), - # since those can be checked separately - dir = path.join("share", "zsh", "site-functions") - dir && dir.directory? && dir.children.any? { |f| !f.basename.to_s.start_with?("_") } - end - - def fish_functions_installed? - dir = path.join("share", "fish", "vendor_functions.d") - dir && dir.directory? && !dir.children.empty? + def functions_installed?(shell) + case shell + when :fish + dir = path/"share/fish/vendor_functions.d" + dir.directory? && !dir.children.empty? + when :zsh + # Check for non completion functions (i.e. files not started with an underscore), + # since those can be checked separately + dir = path/"share/zsh/site-functions" + dir.directory? && dir.children.any? { |f| !f.basename.to_s.start_with?("_") } + end end def plist_installed? diff --git a/Library/Homebrew/keg_relocate.rb b/Library/Homebrew/keg_relocate.rb index 834cda768..6044426ee 100644 --- a/Library/Homebrew/keg_relocate.rb +++ b/Library/Homebrew/keg_relocate.rb @@ -16,9 +16,12 @@ class Keg link = file.readlink # Don't fix relative symlinks next unless link.absolute? - if link.to_s.start_with?(HOMEBREW_CELLAR.to_s) || link.to_s.start_with?(HOMEBREW_PREFIX.to_s) - FileUtils.ln_sf(link.relative_path_from(file.parent), file) - end + link_starts_cellar = link.to_s.start_with?(HOMEBREW_CELLAR.to_s) + link_starts_prefix = link.to_s.start_with?(HOMEBREW_PREFIX.to_s) + next if !link_starts_cellar && !link_starts_prefix + new_src = link.relative_path_from(file.parent) + file.unlink + FileUtils.ln_s(new_src, file) end end alias generic_fix_dynamic_linkage fix_dynamic_linkage @@ -96,8 +99,14 @@ class Keg [] end + def recursive_fgrep_args + # for GNU grep; overridden for BSD grep on OS X + "-lr" + end + alias generic_recursive_fgrep_args recursive_fgrep_args + def each_unique_file_matching(string) - Utils.popen_read("/usr/bin/fgrep", "-lr", string, to_s) do |io| + Utils.popen_read("/usr/bin/fgrep", recursive_fgrep_args, string, to_s) do |io| hardlinks = Set.new until io.eof? diff --git a/Library/Homebrew/language/python.rb b/Library/Homebrew/language/python.rb index 23c5246ba..0f8e3a4e6 100644 --- a/Library/Homebrew/language/python.rb +++ b/Library/Homebrew/language/python.rb @@ -53,29 +53,6 @@ module Language quiet_system python, "-c", script end - # deprecated; use system "python", *setup_install_args(prefix) instead - def self.setup_install(python, prefix, *args) - opoo <<-EOS.undent - Language::Python.setup_install is deprecated. - If you are a formula author, please use - system "python", *Language::Python.setup_install_args(prefix) - instead. - EOS - - # force-import setuptools, which monkey-patches distutils, to make - # sure that we always call a setuptools setup.py. trick borrowed from pip: - # https://github.com/pypa/pip/blob/043af83/pip/req/req_install.py#L743-L780 - shim = <<-EOS.undent - import setuptools, tokenize - __file__ = 'setup.py' - exec(compile(getattr(tokenize, 'open', open)(__file__).read() - .replace('\\r\\n', '\\n'), __file__, 'exec')) - EOS - args += %w[--single-version-externally-managed --record=installed.txt] - args << "--prefix=#{prefix}" - system python, "-c", shim, "install", *args - end - def self.setup_install_args(prefix) shim = <<-EOS.undent import setuptools, tokenize diff --git a/Library/Homebrew/migrator.rb b/Library/Homebrew/migrator.rb index a80cf0c59..3cb6c5178 100644 --- a/Library/Homebrew/migrator.rb +++ b/Library/Homebrew/migrator.rb @@ -77,13 +77,35 @@ class Migrator # path to newname cellar according to new name attr_reader :new_cellar + # true if new cellar existed at initialization time + attr_reader :new_cellar_existed + # path to newname pin attr_reader :new_pin_record # path to newname keg that will be linked if old_linked_keg isn't nil attr_reader :new_linked_keg_record - def initialize(formula) + def self.needs_migration?(formula) + oldname = formula.oldname + return false unless oldname + oldname_rack = HOMEBREW_CELLAR/oldname + return false if oldname_rack.symlink? + return false unless oldname_rack.directory? + true + end + + def self.migrate_if_needed(formula) + return unless Migrator.needs_migration?(formula) + begin + migrator = Migrator.new(formula) + migrator.migrate + rescue Exception => e + onoe e + end + end + + def initialize(formula, force: ARGV.force?) @oldname = formula.oldname @newname = formula.name raise MigratorNoOldnameError, formula unless oldname @@ -95,11 +117,12 @@ class Migrator @old_tabs = old_cellar.subdirs.map { |d| Tab.for_keg(Keg.new(d)) } @old_tap = old_tabs.first.tap - if !ARGV.force? && !from_same_taps? + if !force && !from_same_tap_user? raise MigratorDifferentTapsError.new(formula, old_tap) end @new_cellar = HOMEBREW_CELLAR/formula.name + @new_cellar_existed = @new_cellar.exist? if @old_linked_keg = linked_old_linked_keg @old_linked_keg_record = old_linked_keg.linked_keg_record if old_linked_keg.linked? @@ -121,15 +144,26 @@ class Migrator end end - def from_same_taps? - if formula.tap == old_tap + def from_same_tap_user? + formula_tap_user = formula.tap.user if formula.tap + old_tap_user = nil + + new_tap = if old_tap + old_tap_user, = old_tap.user + if migrate_tap = old_tap.tap_migrations[formula.oldname] + new_tap_user, new_tap_repo = migrate_tap.split("/") + "#{new_tap_user}/#{new_tap_repo}" + end + end + + if formula_tap_user == old_tap_user true # Homebrew didn't use to update tabs while performing tap-migrations, # so there can be INSTALL_RECEIPT's containing wrong information about tap, # so we check if there is an entry about oldname migrated to tap and if # newname's tap is the same as tap to which oldname migrated, then we # can perform migrations and the taps for oldname and newname are the same. - elsif formula.tap && old_tap && formula.tap == old_tap.tap_migrations[formula.oldname] + elsif formula.tap && old_tap && formula.tap == new_tap fix_tabs true else @@ -138,7 +172,10 @@ class Migrator end def linked_old_linked_keg - kegs = old_cellar.subdirs.map { |d| Keg.new(d) } + keg_dirs = [] + keg_dirs += new_cellar.subdirs if new_cellar.exist? + keg_dirs += old_cellar.subdirs + kegs = keg_dirs.map { |d| Keg.new(d) } kegs.detect(&:linked?) || kegs.detect(&:optlinked?) end @@ -147,38 +184,55 @@ class Migrator end def migrate - if new_cellar.exist? - onoe "#{new_cellar} already exists; remove it manually and run brew migrate #{oldname}." - return - end - - begin - oh1 "Migrating #{Formatter.identifier(oldname)} to #{Formatter.identifier(newname)}" - lock - unlink_oldname - move_to_new_directory - repin - link_oldname_cellar - link_oldname_opt - link_newname unless old_linked_keg.nil? - update_tabs - rescue Interrupt - ignore_interrupts { backup_oldname } - rescue Exception => e - onoe "Error occurred while migrating." - puts e - puts e.backtrace if ARGV.debug? - puts "Backuping..." - ignore_interrupts { backup_oldname } - ensure - unlock - end + oh1 "Migrating #{Formatter.identifier(oldname)} to #{Formatter.identifier(newname)}" + lock + unlink_oldname + unlink_newname if new_cellar.exist? + repin + move_to_new_directory + link_oldname_cellar + link_oldname_opt + link_newname unless old_linked_keg.nil? + update_tabs + rescue Interrupt + ignore_interrupts { backup_oldname } + rescue Exception => e + onoe "Error occurred while migrating." + puts e + puts e.backtrace if ARGV.debug? + puts "Backing up..." + ignore_interrupts { backup_oldname } + ensure + unlock end # move everything from Cellar/oldname to Cellar/newname def move_to_new_directory - puts "Moving to: #{new_cellar}" - FileUtils.mv(old_cellar, new_cellar) + return unless old_cellar.exist? + + if new_cellar.exist? + conflicted = false + old_cellar.each_child do |c| + next unless (new_cellar/c.basename).exist? + begin + FileUtils.rm_rf c + rescue Errno::EACCES + conflicted = true + onoe "#{new_cellar/c.basename} already exists." + end + end + + if conflicted + odie "Remove #{new_cellar} manually and run brew migrate #{oldname}." + end + end + + oh1 "Moving #{Formatter.identifier(oldname)} children" + if new_cellar.exist? + FileUtils.mv(old_cellar.children, new_cellar) + else + FileUtils.mv(old_cellar, new_cellar) + end end def repin @@ -207,6 +261,14 @@ class Migrator end end + def unlink_newname + oh1 "Unlinking #{Formatter.identifier(newname)}" + new_cellar.subdirs.each do |d| + keg = Keg.new(d) + keg.unlink + end + end + def link_newname oh1 "Linking #{Formatter.identifier(newname)}" new_keg = Keg.new(new_linked_keg_record) @@ -310,7 +372,7 @@ class Migrator new_cellar.subdirs.each do |d| newname_keg = Keg.new(d) newname_keg.unlink - newname_keg.uninstall + newname_keg.uninstall if new_cellar_existed end end diff --git a/Library/Homebrew/missing_formula.rb b/Library/Homebrew/missing_formula.rb new file mode 100644 index 000000000..eac3d7725 --- /dev/null +++ b/Library/Homebrew/missing_formula.rb @@ -0,0 +1,164 @@ +require "formulary" +require "tap" +require "utils" + +module Homebrew + module MissingFormula + class << self + def reason(name, silent: false) + blacklisted_reason(name) || tap_migration_reason(name) || + deleted_reason(name, silent: silent) + end + + def blacklisted_reason(name) + case name.downcase + when "gem", /^rubygems?$/ then <<-EOS.undent + Homebrew provides gem via: `brew install ruby`. + EOS + when "tex", "tex-live", "texlive", "latex" then <<-EOS.undent + Installing TeX from source is weird and gross, requires a lot of patches, + and only builds 32-bit (and thus can't use Homebrew dependencies) + + We recommend using a MacTeX distribution: https://www.tug.org/mactex/ + + You can install it with Homebrew-Cask: + brew cask install mactex + EOS + when "pip" then <<-EOS.undent + Homebrew provides pip via: `brew install python`. However you will then + have two Pythons installed on your Mac, so alternatively you can install + pip via the instructions at: + #{Formatter.url("https://pip.readthedocs.io/en/stable/installing/")} + EOS + when "pil" then <<-EOS.undent + Instead of PIL, consider `pip install pillow` or `brew install Homebrew/science/pillow`. + EOS + when "macruby" then <<-EOS.undent + MacRuby is not packaged and is on an indefinite development hiatus. + You can read more about it at: + #{Formatter.url("https://github.com/MacRuby/MacRuby")} + EOS + when /(lib)?lzma/ + "lzma is now part of the xz formula." + when "gtest", "googletest", "google-test" then <<-EOS.undent + Installing gtest system-wide is not recommended; it should be vendored + in your projects that use it. + EOS + when "gmock", "googlemock", "google-mock" then <<-EOS.undent + Installing gmock system-wide is not recommended; it should be vendored + in your projects that use it. + EOS + when "sshpass" then <<-EOS.undent + We won't add sshpass because it makes it too easy for novice SSH users to + ruin SSH's security. + EOS + when "gsutil" then <<-EOS.undent + Install gsutil with `pip install gsutil` + EOS + when "clojure" then <<-EOS.undent + Clojure isn't really a program but a library managed as part of a + project and Leiningen is the user interface to that library. + + To install Clojure you should install Leiningen: + brew install leiningen + and then follow the tutorial: + #{Formatter.url("https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md")} + EOS + when "gfortran" then <<-EOS.undent + GNU Fortran is now provided as part of GCC, and can be installed with: + brew install gcc + EOS + when "play" then <<-EOS.undent + Play 2.3 replaces the play command with activator: + brew install typesafe-activator + + You can read more about this change at: + #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Migration23")} + #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Highlights23")} + EOS + when "haskell-platform" then <<-EOS.undent + We no longer package haskell-platform. Consider installing ghc + and cabal-install instead: + brew install ghc cabal-install + + You can install with Homebrew-Cask: + brew cask install haskell-platform + EOS + when "mysqldump-secure" then <<-EOS.undent + The creator of mysqldump-secure tried to game our popularity metrics. + EOS + when "ngrok" then <<-EOS.undent + Upstream sunsetted 1.x in March 2016 and 2.x is not open-source. + + If you wish to use the 2.x release you can install with Homebrew-Cask: + brew cask install ngrok + EOS + end + end + alias generic_blacklisted_reason blacklisted_reason + + def tap_migration_reason(name) + message = nil + + Tap.each do |old_tap| + new_tap = old_tap.tap_migrations[name] + next unless new_tap + + new_tap_user, new_tap_repo, = new_tap.split("/") + new_tap_name = "#{new_tap_user}/#{new_tap_repo}" + + message = <<-EOS.undent + It was migrated from #{old_tap} to #{new_tap}. + You can access it again by running: + brew tap #{new_tap_name} + EOS + break + end + + message + end + + def deleted_reason(name, silent: false) + path = Formulary.path name + return if File.exist? path + tap = Tap.from_path(path) + return if tap.nil? || !File.exist?(tap.path) + relative_path = path.relative_path_from tap.path + + tap.path.cd do + ohai "Searching for a previously deleted formula..." unless silent + + # We know this may return incomplete results for shallow clones but + # we don't want to nag everyone with a shallow clone to unshallow it. + log_command = "git log --name-only --max-count=1 --format=%H\\\\n%h\\\\n%B -- #{relative_path}" + hash, short_hash, *commit_message, relative_path = + Utils.popen_read(log_command).gsub("\\n", "\n").lines.map(&:chomp) + + if hash.to_s.empty? || short_hash.to_s.empty? || + relative_path.to_s.empty? + ofail "No previously deleted formula found." unless silent + return + end + + commit_message = commit_message.reject(&:empty?).join("\n ") + + commit_message.sub!(/ \(#(\d+)\)$/, " (#{tap.issues_url}/\\1)") + commit_message.gsub!(/(Closes|Fixes) #(\d+)/, "\\1 #{tap.issues_url}/\\2") + + <<-EOS.undent + #{name} was deleted from #{tap.name} in commit #{short_hash}: + #{commit_message} + + To show the formula before removal run: + git -C "$(brew --repo #{tap})" show #{short_hash}^:#{relative_path} + + If you still use this formula consider creating your own tap: + http://docs.brew.sh/How-to-Create-and-Maintain-a-Tap.html + EOS + end + end + + require "extend/os/missing_formula" + end + end +end diff --git a/Library/Homebrew/official_taps.rb b/Library/Homebrew/official_taps.rb index c7b96ae64..dcb65d9f8 100644 --- a/Library/Homebrew/official_taps.rb +++ b/Library/Homebrew/official_taps.rb @@ -1,11 +1,13 @@ OFFICIAL_TAPS = %w[ apache - dupes - fuse nginx php science - tex +].freeze + +OFFICIAL_CASK_TAPS = %w[ + cask + versions ].freeze OFFICIAL_CMD_TAPS = { @@ -13,3 +15,19 @@ OFFICIAL_CMD_TAPS = { "homebrew/test-bot" => ["test-bot"], "homebrew/services" => ["services"], }.freeze + +DEPRECATED_OFFICIAL_TAPS = %w[ + binary + completions + devel-only + dupes + emacs + fuse + games + gui + head-only + python + tex + versions + x11 +].freeze diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index e09a9b3fd..dba2480ef 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -201,6 +201,12 @@ module OS "7.3" => { clang: "7.3", clang_build: 703 }, "7.3.1" => { clang: "7.3", clang_build: 703 }, "8.0" => { clang: "8.0", clang_build: 800 }, + "8.1" => { clang: "8.0", clang_build: 800 }, + "8.2" => { clang: "8.0", clang_build: 800 }, + "8.2.1" => { clang: "8.0", clang_build: 800 }, + "8.3" => { clang: "8.1", clang_build: 802 }, + "8.3.1" => { clang: "8.1", clang_build: 802 }, + "8.3.2" => { clang: "8.1", clang_build: 802 }, }.freeze def compilers_standard? diff --git a/Library/Homebrew/os/mac/xcode.rb b/Library/Homebrew/os/mac/xcode.rb index aa4504a17..cac0dcc30 100644 --- a/Library/Homebrew/os/mac/xcode.rb +++ b/Library/Homebrew/os/mac/xcode.rb @@ -16,13 +16,13 @@ module OS when "10.8" then "5.1.1" when "10.9" then "6.2" when "10.10" then "7.2.1" - when "10.11" then "8.2" - when "10.12" then "8.2" + when "10.11" then "8.2.1" + when "10.12" then "8.3.2" else raise "macOS '#{MacOS.version}' is invalid" unless OS::Mac.prerelease? # Default to newest known version of Xcode for unreleased macOS versions. - "8.2" + "8.3.2" end end @@ -90,7 +90,7 @@ module OS else <<-EOS.undent Xcode can be updated from - https://developer.apple.com/xcode/downloads/ + https://developer.apple.com/download/more/ EOS end end @@ -128,11 +128,10 @@ module OS end end - # The remaining logic provides a fake Xcode version for CLT-only - # systems. This behavior only exists because Homebrew used to assume - # Xcode.version would always be non-nil. This is deprecated, and will - # be removed in a future version. To remain compatible, guard usage of - # Xcode.version with an Xcode.installed? check. + # The remaining logic provides a fake Xcode version based on the + # installed CLT version. This is useful as they are packaged + # simultaneously so workarounds need to apply to both based on their + # comparable version. case (DevelopmentTools.clang_version.to_f * 10).to_i when 0 then "dunno" when 1..14 then "3.2.2" @@ -152,7 +151,8 @@ module OS when 70 then "7.0" when 73 then "7.3" when 80 then "8.0" - else "8.0" + when 81 then "8.3" + else "8.3" end end @@ -202,7 +202,7 @@ module OS elsif MacOS.version == "10.8" || MacOS.version == "10.7" <<-EOS.undent The standalone package can be obtained from - https://developer.apple.com/downloads + https://developer.apple.com/download/more/ or it can be installed via Xcode's preferences. EOS end @@ -213,8 +213,8 @@ module OS # on the older supported platform for that Xcode release, i.e there's no # CLT package for 10.11 that contains the Clang version from Xcode 8. case MacOS.version - when "10.12" then "800.0.42.1" - when "10.11" then "703.0.31" + when "10.12" then "802.0.38" + when "10.11" then "800.0.42.1" when "10.10" then "700.1.81" when "10.9" then "600.0.57" when "10.8" then "503.0.40" diff --git a/Library/Homebrew/readall.rb b/Library/Homebrew/readall.rb index 3dd7075ec..ddac58444 100644 --- a/Library/Homebrew/readall.rb +++ b/Library/Homebrew/readall.rb @@ -24,14 +24,21 @@ module Readall !failed end - def valid_aliases?(alias_dirs) + def valid_aliases?(alias_dir, formula_dir) + return true unless alias_dir.directory? + failed = false - alias_dirs.each do |alias_dir| - next unless alias_dir.directory? - alias_dir.children.each do |f| - next unless f.symlink? - next if f.file? - onoe "Broken alias: #{f}" + alias_dir.each_child do |f| + if !f.symlink? + onoe "Non-symlink alias: #{f}" + failed = true + elsif !f.file? + onoe "Non-file alias: #{f}" + failed = true + end + + if (formula_dir/"#{f.basename}.rb").exist? + onoe "Formula duplicating alias: #{f}" failed = true end end @@ -57,7 +64,7 @@ module Readall def valid_tap?(tap, options = {}) failed = false if options[:aliases] - valid_aliases = valid_aliases?([tap.alias_dir]) + valid_aliases = valid_aliases?(tap.alias_dir, tap.formula_dir) failed = true unless valid_aliases end valid_formulae = valid_formulae?(tap.formula_files) diff --git a/Library/Homebrew/requirement.rb b/Library/Homebrew/requirement.rb index 49108ca75..a4bdabdd1 100644 --- a/Library/Homebrew/requirement.rb +++ b/Library/Homebrew/requirement.rb @@ -125,6 +125,10 @@ class Requirement @formula || self.class.default_formula end + def satisfied_by_formula? + !@formula.nil? + end + def to_dependency if formula =~ HOMEBREW_TAP_FORMULA_REGEX TapDependency.new(formula, tags, method(:modify_build_environment), name) @@ -157,11 +161,9 @@ class Requirement class << self include BuildEnvironmentDSL - attr_reader :env_proc + attr_reader :env_proc, :build attr_rw :fatal, :default_formula attr_rw :cask, :download - # build is deprecated, use `depends_on <requirement> => :build` instead - attr_rw :build def satisfy(options = {}, &block) @satisfied ||= Requirement::Satisfier.new(options, &block) diff --git a/Library/Homebrew/requirements.rb b/Library/Homebrew/requirements.rb index b09dc889a..553beb2a2 100644 --- a/Library/Homebrew/requirements.rb +++ b/Library/Homebrew/requirements.rb @@ -43,7 +43,7 @@ class XcodeRequirement < Requirement EOS else message + <<-EOS.undent - Xcode can be installed from #{Formatter.url("https://developer.apple.com/xcode/downloads/")} + Xcode can be installed from #{Formatter.url("https://developer.apple.com/download/more/")}. EOS end end diff --git a/Library/Homebrew/requirements/gpg2_requirement.rb b/Library/Homebrew/requirements/gpg2_requirement.rb index 00bf4183a..97fabcca0 100644 --- a/Library/Homebrew/requirements/gpg2_requirement.rb +++ b/Library/Homebrew/requirements/gpg2_requirement.rb @@ -3,10 +3,10 @@ require "gpg" class GPG2Requirement < Requirement fatal true - default_formula "gnupg2" + default_formula "gnupg" # MacGPG2/GPGTools installs GnuPG 2.0.x as a vanilla `gpg` symlink # pointing to `gpg2`, as do we. Ensure we're actually using a 2.0 `gpg`. - # Temporarily, only support 2.0.x rather than the 2.1.x "modern" series. + # Support both the 2.0.x "stable" and 2.1.x "modern" series. satisfy(build_env: false) { Gpg.gpg2 || Gpg.gpg } end diff --git a/Library/Homebrew/requirements/java_requirement.rb b/Library/Homebrew/requirements/java_requirement.rb index 0887b69c7..653846edd 100644 --- a/Library/Homebrew/requirements/java_requirement.rb +++ b/Library/Homebrew/requirements/java_requirement.rb @@ -18,7 +18,7 @@ class JavaRequirement < Requirement def message version_string = " #{@version}" if @version - s = "Java#{version_string} is required to install this formula." + s = "Java#{version_string} is required to install this formula.\n" s += super s end diff --git a/Library/Homebrew/requirements/osxfuse_requirement.rb b/Library/Homebrew/requirements/osxfuse_requirement.rb index d5a341567..9a07209d4 100644 --- a/Library/Homebrew/requirements/osxfuse_requirement.rb +++ b/Library/Homebrew/requirements/osxfuse_requirement.rb @@ -2,34 +2,10 @@ require "requirement" class OsxfuseRequirement < Requirement fatal true - cask "osxfuse" - download "https://osxfuse.github.io/" - - satisfy(build_env: false) { self.class.binary_osxfuse_installed? } - - def self.binary_osxfuse_installed? - File.exist?("/usr/local/include/osxfuse/fuse.h") && - !File.symlink?("/usr/local/include/osxfuse") - end - - env do - ENV.append_path "PKG_CONFIG_PATH", HOMEBREW_LIBRARY/"Homebrew/os/mac/pkgconfig/fuse" - - unless HOMEBREW_PREFIX.to_s == "/usr/local" - ENV.append_path "HOMEBREW_LIBRARY_PATHS", "/usr/local/lib" - ENV.append_path "HOMEBREW_INCLUDE_PATHS", "/usr/local/include/osxfuse" - end - end end class NonBinaryOsxfuseRequirement < Requirement - fatal true - satisfy(build_env: false) { HOMEBREW_PREFIX.to_s != "/usr/local" || !OsxfuseRequirement.binary_osxfuse_installed? } - - def message - <<-EOS.undent - osxfuse is already installed from the binary distribution and - conflicts with this formula. - EOS - end + fatal false end + +require "extend/os/requirements/osxfuse_requirement" diff --git a/Library/Homebrew/requirements/ruby_requirement.rb b/Library/Homebrew/requirements/ruby_requirement.rb index 327c13170..acc655924 100644 --- a/Library/Homebrew/requirements/ruby_requirement.rb +++ b/Library/Homebrew/requirements/ruby_requirement.rb @@ -11,7 +11,7 @@ class RubyRequirement < Requirement satisfy(build_env: false) { new_enough_ruby } env do - ENV.prepend_path "PATH", new_enough_ruby + ENV.prepend_path "PATH", new_enough_ruby.dirname end def message diff --git a/Library/Homebrew/rubocops.rb b/Library/Homebrew/rubocops.rb index 1a28dd213..3625f2004 100644 --- a/Library/Homebrew/rubocops.rb +++ b/Library/Homebrew/rubocops.rb @@ -1 +1,2 @@ require_relative "./rubocops/bottle_block_cop" +require_relative "./rubocops/formula_desc_cop" diff --git a/Library/Homebrew/rubocops/bottle_block_cop.rb b/Library/Homebrew/rubocops/bottle_block_cop.rb index 55eb55152..4d7a94461 100644 --- a/Library/Homebrew/rubocops/bottle_block_cop.rb +++ b/Library/Homebrew/rubocops/bottle_block_cop.rb @@ -1,16 +1,19 @@ +require_relative "./extend/formula_cop" + module RuboCop module Cop module Homebrew - class CorrectBottleBlock < Cop - MSG = "Use rebuild instead of revision in bottle block".freeze + # This cop audits `bottle` block in Formulae + # + # - `rebuild` should be used instead of `revision` in `bottle` block - def on_block(node) - return if block_length(node).zero? - method, _args, body = *node - _keyword, method_name = *method + class CorrectBottleBlock < FormulaCop + MSG = "Use rebuild instead of revision in bottle block".freeze - return unless method_name == :bottle - check_revision?(body) + def audit_formula(_node, _class_node, _parent_class_node, formula_class_body_node) + bottle = find_block(formula_class_body_node, :bottle) + return if bottle.nil? || block_size(bottle).zero? + problem "Use rebuild instead of revision in bottle block" if method_called?(bottle, :revision) end private @@ -22,14 +25,6 @@ module RuboCop corrector.remove(node.source_range) end end - - def check_revision?(body) - body.children.each do |method_call_node| - _receiver, method_name, _args = *method_call_node - next unless method_name == :revision - add_offense(method_call_node, :expression) - end - end end end end diff --git a/Library/Homebrew/rubocops/extend/formula_cop.rb b/Library/Homebrew/rubocops/extend/formula_cop.rb new file mode 100644 index 000000000..49108bd48 --- /dev/null +++ b/Library/Homebrew/rubocops/extend/formula_cop.rb @@ -0,0 +1,144 @@ +module RuboCop + module Cop + module Homebrew + class FormulaCop < Cop + @registry = Cop.registry + + def on_class(node) + # This method is called by RuboCop and is the main entry point + file_path = processed_source.buffer.name + return unless file_path_allowed?(file_path) + class_node, parent_class_node, body = *node + return unless formula_class?(parent_class_node) + return unless respond_to?(:audit_formula) + @formula_name = class_name(class_node) + audit_formula(node, class_node, parent_class_node, body) + end + + def regex_match_group(node, pattern) + # Checks for regex match of pattern in the node and + # Sets the appropriate instance variables to report the match + string_repr = string_content(node) + match_object = string_repr.match(pattern) + return unless match_object + node_begin_pos = start_column(node) + line_begin_pos = line_start_column(node) + @column = node_begin_pos + match_object.begin(0) - line_begin_pos + 1 + @length = match_object.to_s.length + @line_no = line_number(node) + @source_buf = source_buffer(node) + @offense_source_range = source_range(@source_buf, @line_no, @column, @length) + @offensive_node = node + match_object + end + + def find_node_method_by_name(node, method_name) + # Returns method_node matching method_name + return if node.nil? + node.each_child_node(:send) do |method_node| + next unless method_node.method_name == method_name + @offensive_node = method_node + @offense_source_range = method_node.source_range + return method_node + end + # If not found then, parent node becomes the offensive node + @offensive_node = node.parent + @offense_source_range = node.parent.source_range + nil + end + + def find_block(node, block_name) + # Returns a block named block_name inside node + return if node.nil? + node.each_child_node(:block) do |block_node| + next if block_node.method_name != block_name + @offensive_node = block_node + @offense_source_range = block_node.source_range + return block_node + end + # If not found then, parent node becomes the offensive node + @offensive_node = node.parent + @offense_source_range = node.parent.source_range + nil + end + + def method_called?(node, method_name) + # Check if a method is called inside a block + block_body = node.children[2] + block_body.each_child_node(:send) do |call_node| + next unless call_node.method_name == method_name + @offensive_node = call_node + @offense_source_range = call_node.source_range + return true + end + false + end + + def parameters(method_node) + # Returns the array of arguments of the method_node + return unless method_node.send_type? + method_node.method_args + end + + def line_start_column(node) + # Returns the begin position of the node's line in source code + node.source_range.source_buffer.line_range(node.loc.line).begin_pos + end + + def start_column(node) + # Returns the begin position of the node in source code + node.source_range.begin_pos + end + + def line_number(node) + # Returns the line number of the node + node.loc.line + end + + def class_name(node) + # Returns the class node's name, nil if not a class node + @offensive_node = node + @offense_source_range = node.source_range + node.const_name + end + + def size(node) + # Returns the node size in the source code + node.source_range.size + end + + def block_size(block) + # Returns the block length of the block node + block_length(block) + end + + def source_buffer(node) + # Source buffer is required as an argument to report style violations + node.source_range.source_buffer + end + + def string_content(node) + # Returns the string representation if node is of type str + node.str_content if node.type == :str + end + + def problem(msg) + add_offense(@offensive_node, @offense_source_range, msg) + end + + private + + def formula_class?(parent_class_node) + parent_class_node && parent_class_node.const_name == "Formula" + end + + def file_path_allowed?(file_path) + paths_to_exclude = [%r{/Library/Homebrew/compat/}, + %r{/Library/Homebrew/test/}] + return true if file_path.nil? # file_path is nil when source is directly passed to the cop eg., in specs + file_path !~ Regexp.union(paths_to_exclude) + end + end + end + end +end diff --git a/Library/Homebrew/rubocops/formula_desc_cop.rb b/Library/Homebrew/rubocops/formula_desc_cop.rb new file mode 100644 index 000000000..7d69a48e7 --- /dev/null +++ b/Library/Homebrew/rubocops/formula_desc_cop.rb @@ -0,0 +1,48 @@ +require_relative "./extend/formula_cop" +require_relative "../extend/string" + +module RuboCop + module Cop + module Homebrew + # This cop audits `desc` in Formulae + # + # - Checks for existence of `desc` + # - Checks if size of `desc` > 80 + # - Checks if `desc` begins with an article + # - Checks for correct usage of `command-line` in `desc` + # - Checks if `desc` contains the formula name + class FormulaDesc < FormulaCop + def audit_formula(_node, _class_node, _parent_class_node, body) + desc_call = find_node_method_by_name(body, :desc) + + if desc_call.nil? + problem "Formula should have a desc (Description)." + return + end + + desc = parameters(desc_call).first + desc_length = "#{@formula_name}: #{string_content(desc)}".length + max_desc_length = 80 + if desc_length > max_desc_length + problem <<-EOS.undent + Description is too long. "name: desc" should be less than #{max_desc_length} characters. + Length is calculated as #{@formula_name} + desc. (currently #{desc_length}) + EOS + end + + # Check if command-line is wrongly used in formula's desc + if match = regex_match_group(desc, /(command ?line)/i) + problem "Description should use \"command-line\" instead of \"#{match}\"" + end + + if match = regex_match_group(desc, /^(an?)\s/i) + problem "Description shouldn't start with an indefinite article (#{match})" + end + + # Check if formula's name is used in formula's desc + problem "Description shouldn't include the formula name" if regex_match_group(desc, /^#{@formula_name}/i) + end + end + end + end +end diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index fb07f6c55..1d2e4bf64 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -117,8 +117,7 @@ class SoftwareSpec def option(name, description = "") opt = PREDEFINED_OPTIONS.fetch(name) do if name.is_a?(Symbol) - opoo "Passing arbitrary symbols to `option` is deprecated: #{name.inspect}" - puts "Symbols are reserved for future use, please pass a string instead" + odeprecated "passing arbitrary symbols (i.e. #{name.inspect}) to `option`" name = name.to_s end unless name.is_a?(String) @@ -172,7 +171,6 @@ class SoftwareSpec end def fails_with(compiler, &block) - # TODO: deprecate this in future. # odeprecated "fails_with :llvm" if compiler == :llvm compiler_failures << CompilerFailure.create(compiler, &block) end diff --git a/Library/Homebrew/tab.rb b/Library/Homebrew/tab.rb index fd59539df..db4b1c585 100644 --- a/Library/Homebrew/tab.rb +++ b/Library/Homebrew/tab.rb @@ -100,11 +100,14 @@ class Tab < OpenStruct def self.for_keg(keg) path = keg.join(FILENAME) - if path.exist? + tab = if path.exist? from_file(path) else empty end + + tab["tabfile"] = path + tab end # Returns a tab for the named formula's installation, diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb index ba55e3cf6..c3af73c7e 100644 --- a/Library/Homebrew/tap.rb +++ b/Library/Homebrew/tap.rb @@ -41,6 +41,15 @@ class Tap CACHE.fetch(cache_key) { |key| CACHE[key] = Tap.new(user, repo) } end + def self.from_path(path) + path.to_s =~ HOMEBREW_TAP_PATH_REGEX + raise "Invalid tap path '#{path}'" unless $1 + fetch($1, $2) + rescue + # No need to error as a nil tap is sufficient to show failure. + nil + end + extend Enumerable # The user name of this {Tap}. Usually, it's the Github username of @@ -192,6 +201,10 @@ class Tap quiet = options.fetch(:quiet, false) requested_remote = options[:clone_target] || default_remote + if official? && DEPRECATED_OFFICIAL_TAPS.include?(repo) + opoo "#{name} was deprecated. This tap is now empty as all its formulae were migrated." + end + if installed? raise TapAlreadyTappedError, name unless full_clone raise TapAlreadyUnshallowError, name unless shallow? diff --git a/Library/Homebrew/test/.bundle/config b/Library/Homebrew/test/.bundle/config index e451829e9..20549341c 100644 --- a/Library/Homebrew/test/.bundle/config +++ b/Library/Homebrew/test/.bundle/config @@ -1,3 +1,4 @@ --- BUNDLE_PATH: "../vendor/bundle" BUNDLE_DISABLE_SHARED_GEMS: "true" +BUNDLE_BIN: "../bin" diff --git a/Library/Homebrew/test/Gemfile b/Library/Homebrew/test/Gemfile index bc9bccfbc..f3c16c710 100644 --- a/Library/Homebrew/test/Gemfile +++ b/Library/Homebrew/test/Gemfile @@ -2,6 +2,7 @@ source "https://rubygems.org" gem "parallel_tests" gem "rspec" +gem "rubocop" gem "rspec-its", require: false gem "rspec-wait", require: false diff --git a/Library/Homebrew/test/Gemfile.lock b/Library/Homebrew/test/Gemfile.lock index 64561be71..4d4eefd7d 100644 --- a/Library/Homebrew/test/Gemfile.lock +++ b/Library/Homebrew/test/Gemfile.lock @@ -1,6 +1,7 @@ GEM remote: https://rubygems.org/ specs: + ast (2.3.0) codecov (0.1.9) json simplecov @@ -11,6 +12,10 @@ GEM parallel (1.10.0) parallel_tests (2.13.0) parallel + parser (2.4.0.0) + ast (~> 2.2) + powerpack (0.1.1) + rainbow (2.2.1) rspec (3.5.0) rspec-core (~> 3.5.0) rspec-expectations (~> 3.5.0) @@ -29,11 +34,19 @@ GEM rspec-support (3.5.0) rspec-wait (0.0.9) rspec (>= 3, < 4) + rubocop (0.47.1) + parser (>= 2.3.3.1, < 3.0) + powerpack (~> 0.1) + rainbow (>= 1.99.1, < 3.0) + ruby-progressbar (~> 1.7) + unicode-display_width (~> 1.0, >= 1.0.1) + ruby-progressbar (1.8.1) simplecov (0.13.0) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.0) + unicode-display_width (1.1.3) url (0.3.2) PLATFORMS @@ -45,6 +58,7 @@ DEPENDENCIES rspec rspec-its rspec-wait + rubocop simplecov BUNDLED WITH diff --git a/Library/Homebrew/test/blacklist_spec.rb b/Library/Homebrew/test/blacklist_spec.rb deleted file mode 100644 index 01882167d..000000000 --- a/Library/Homebrew/test/blacklist_spec.rb +++ /dev/null @@ -1,115 +0,0 @@ -require "blacklist" - -describe "Blacklist" do - matcher(:be_blacklisted) { match(&method(:blacklisted?)) } - - context "rubygems" do - %w[gem rubygem rubygems].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "latex" do - %w[latex tex tex-live texlive TexLive].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "pip" do - subject { "pip" } - - it { is_expected.to be_blacklisted } - end - - context "pil" do - subject { "pil" } - - it { is_expected.to be_blacklisted } - end - - context "macruby" do - subject { "MacRuby" } - - it { is_expected.to be_blacklisted } - end - - context "lzma" do - %w[lzma liblzma].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "gtest" do - %w[gtest googletest google-test].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "gmock" do - %w[gmock googlemock google-mock].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "sshpass" do - subject { "sshpass" } - - it { is_expected.to be_blacklisted } - end - - context "gsutil" do - subject { "gsutil" } - - it { is_expected.to be_blacklisted } - end - - context "clojure" do - subject { "clojure" } - - it { is_expected.to be_blacklisted } - end - - context "osmium" do - %w[osmium Osmium].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "gfortran" do - subject { "gfortran" } - - it { is_expected.to be_blacklisted } - end - - context "play" do - subject { "play" } - - it { is_expected.to be_blacklisted } - end - - context "haskell-platform" do - subject { "haskell-platform" } - - it { is_expected.to be_blacklisted } - end - - context "xcode", :needs_macos do - %w[xcode Xcode].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end -end diff --git a/Library/Homebrew/test/cask/artifact/binary_spec.rb b/Library/Homebrew/test/cask/artifact/binary_spec.rb index 69bde3438..ee62e6439 100644 --- a/Library/Homebrew/test/cask/artifact/binary_spec.rb +++ b/Library/Homebrew/test/cask/artifact/binary_spec.rb @@ -6,9 +6,7 @@ describe Hbc::Artifact::Binary, :cask do end end } - let(:expected_path) { - Hbc.binarydir.join("binary") - } + let(:expected_path) { Hbc.binarydir.join("binary") } before(:each) do Hbc.binarydir.mkpath @@ -26,15 +24,28 @@ describe Hbc::Artifact::Binary, :cask do expect(expected_path.readlink).to exist end - it "makes the binary executable" do - expect(FileUtils).to receive(:chmod).with("+x", cask.staged_path.join("binary")) + context "when the binary is not executable" do + let(:cask) { + Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-non-executable-binary.rb").tap do |cask| + shutup do + InstallHelper.install_without_artifacts(cask) + end + end + } - shutup do - Hbc::Artifact::Binary.new(cask).install_phase - end + let(:expected_path) { Hbc.binarydir.join("naked_non_executable") } - expect(expected_path).to be_a_symlink - expect(expected_path.readlink).to be_executable + it "makes the binary executable" do + expect(FileUtils).to receive(:chmod) + .with("+x", cask.staged_path.join("naked_non_executable")).and_call_original + + shutup do + Hbc::Artifact::Binary.new(cask).install_phase + end + + expect(expected_path).to be_a_symlink + expect(expected_path.readlink).to be_executable + end end it "avoids clobbering an existing binary by linking over it" do diff --git a/Library/Homebrew/test/cask/cask_spec.rb b/Library/Homebrew/test/cask/cask_spec.rb index 2ab966f82..3736f3c01 100644 --- a/Library/Homebrew/test/cask/cask_spec.rb +++ b/Library/Homebrew/test/cask/cask_spec.rb @@ -85,8 +85,8 @@ describe Hbc::Cask, :cask do it "proposes a versioned metadata directory name for each instance" do cask_token = "local-caffeine" c = Hbc::CaskLoader.load(cask_token) - metadata_path = Hbc.caskroom.join(cask_token, ".metadata", c.version) - expect(c.metadata_versioned_container_path.to_s).to eq(metadata_path.to_s) + metadata_timestamped_path = Hbc.caskroom.join(cask_token, ".metadata", c.version) + expect(c.metadata_versioned_path.to_s).to eq(metadata_timestamped_path.to_s) end end diff --git a/Library/Homebrew/test/cask/cli/style_spec.rb b/Library/Homebrew/test/cask/cli/style_spec.rb index ca17c5e46..d41636beb 100644 --- a/Library/Homebrew/test/cask/cli/style_spec.rb +++ b/Library/Homebrew/test/cask/cli/style_spec.rb @@ -81,7 +81,7 @@ describe Hbc::CLI::Style, :cask do end context "version" do - it "matches `HOMEBREW_RUBOCOP_VERSION`" do + it "matches `HOMEBREW_RUBOCOP_VERSION`", :needs_network do stdout, status = Open3.capture2("gem", "dependency", "rubocop-cask", "--version", HOMEBREW_RUBOCOP_CASK_VERSION, "--pipe", "--remote") expect(status).to be_a_success diff --git a/Library/Homebrew/test/cask/dsl_spec.rb b/Library/Homebrew/test/cask/dsl_spec.rb index 7872b42a6..7eeabcf49 100644 --- a/Library/Homebrew/test/cask/dsl_spec.rb +++ b/Library/Homebrew/test/cask/dsl_spec.rb @@ -26,8 +26,6 @@ describe Hbc::DSL, :cask do .* Unexpected method 'future_feature' called on Cask unexpected-method-cask\\. .* - https://github.com/caskroom/homebrew-cask/blob/master/doc/reporting_bugs/pre_bug_report.md - .* https://github.com/caskroom/homebrew-cask#reporting-bugs EOS diff --git a/Library/Homebrew/test/cask/installer_spec.rb b/Library/Homebrew/test/cask/installer_spec.rb index 7dd5b2bda..59d61bbdd 100644 --- a/Library/Homebrew/test/cask/installer_spec.rb +++ b/Library/Homebrew/test/cask/installer_spec.rb @@ -336,9 +336,8 @@ describe Hbc::Installer, :cask do Hbc::Installer.new(caffeine).install end - m_path = caffeine.metadata_path(:now, true) - expect(caffeine.metadata_path(:now, false)).to eq(m_path) - expect(caffeine.metadata_path(:latest)).to eq(m_path) + m_path = caffeine.metadata_timestamped_path(timestamp: :now, create: true) + expect(caffeine.metadata_timestamped_path(timestamp: :latest)).to eq(m_path) end it "generates and finds a metadata subdirectory for an installed Cask" do @@ -349,9 +348,8 @@ describe Hbc::Installer, :cask do end subdir_name = "Casks" - m_subdir = caffeine.metadata_subdir(subdir_name, :now, true) - expect(caffeine.metadata_subdir(subdir_name, :now, false)).to eq(m_subdir) - expect(caffeine.metadata_subdir(subdir_name, :latest)).to eq(m_subdir) + m_subdir = caffeine.metadata_subdir(subdir_name, timestamp: :now, create: true) + expect(caffeine.metadata_subdir(subdir_name, timestamp: :latest)).to eq(m_subdir) end end diff --git a/Library/Homebrew/test/cask/pkg_spec.rb b/Library/Homebrew/test/cask/pkg_spec.rb index 9930cd00f..e507ceda0 100644 --- a/Library/Homebrew/test/cask/pkg_spec.rb +++ b/Library/Homebrew/test/cask/pkg_spec.rb @@ -36,7 +36,12 @@ describe Hbc::Pkg, :cask do it "forgets the pkg" do allow(fake_system_command).to receive(:run!).with( "/usr/sbin/pkgutil", - args: ["--export-plist", "my.fake.pkg"], + args: ["--pkg-info-plist", "my.fake.pkg"], + ).and_return(empty_response) + + expect(fake_system_command).to receive(:run!).with( + "/usr/sbin/pkgutil", + args: ["--files", "my.fake.pkg"], ).and_return(empty_response) expect(fake_system_command).to receive(:run!).with( @@ -139,7 +144,7 @@ describe Hbc::Pkg, :cask do expect(fake_system_command).to receive(:run!).with( "/usr/sbin/pkgutil", - args: ["--export-plist", pkg_id], + args: ["--pkg-info-plist", pkg_id], ).and_return( Hbc::SystemCommand::Result.new(nil, pkg_info_plist, nil, 0), ) diff --git a/Library/Homebrew/test/cleanup_spec.rb b/Library/Homebrew/test/cleanup_spec.rb index b0e824767..2c3eddb8c 100644 --- a/Library/Homebrew/test/cleanup_spec.rb +++ b/Library/Homebrew/test/cleanup_spec.rb @@ -34,6 +34,39 @@ describe Homebrew::Cleanup do expect(ds_store).to exist end + + context "when it can't remove a keg" do + let(:f1) { Class.new(Testball) { version "0.1" }.new } + let(:f2) { Class.new(Testball) { version "0.2" }.new } + let(:unremovable_kegs) { [] } + + before(:each) do + described_class.instance_variable_set(:@unremovable_kegs, []) + shutup do + [f1, f2].each do |f| + f.brew do + f.install + end + + Tab.create(f, DevelopmentTools.default_compiler, :libcxx).write + end + end + + allow_any_instance_of(Keg) + .to receive(:uninstall) + .and_raise(Errno::EACCES) + end + + it "doesn't remove any kegs" do + shutup { described_class.cleanup_formula f2 } + expect(f1.installed_kegs.size).to eq(2) + end + + it "lists the unremovable kegs" do + shutup { described_class.cleanup_formula f2 } + expect(described_class.unremovable_kegs).to contain_exactly(f1.installed_kegs[0]) + end + end end specify "::cleanup_formula" do diff --git a/Library/Homebrew/test/cmd/bundle_spec.rb b/Library/Homebrew/test/cmd/bundle_spec.rb index 13f13485c..286ddba97 100644 --- a/Library/Homebrew/test/cmd/bundle_spec.rb +++ b/Library/Homebrew/test/cmd/bundle_spec.rb @@ -1,6 +1,6 @@ describe "brew bundle", :integration_test, :needs_test_cmd_taps do describe "check" do - it "checks if a Brewfile's dependencies are satisfied" do + it "checks if a Brewfile's dependencies are satisfied", :needs_network do setup_remote_tap "homebrew/bundle" HOMEBREW_REPOSITORY.cd do diff --git a/Library/Homebrew/test/cmd/link_spec.rb b/Library/Homebrew/test/cmd/link_spec.rb index 7b85c96dc..59ab86cc4 100644 --- a/Library/Homebrew/test/cmd/link_spec.rb +++ b/Library/Homebrew/test/cmd/link_spec.rb @@ -48,9 +48,11 @@ describe "brew link", :integration_test do expect { brew "install", "testball1" }.to be_a_success end - expect { brew "link", "testball1" } + expect { brew "link", "testball1", "SHELL" => "/bin/zsh" } .to output(/testball1 is keg-only/).to_stderr - .and output(/Note that doing so can interfere with building software\./).to_stdout + .and output(a_string_matching(/Note that doing so can interfere with building software\./) + .and(matching("If you need to have this software first in your PATH instead consider running:") + .and(including("echo 'export PATH=\"#{HOMEBREW_PREFIX}/opt/testball1/bin:$PATH\"' >> ~/.zshrc")))).to_stdout .and be_a_success end end diff --git a/Library/Homebrew/test/cmd/log_spec.rb b/Library/Homebrew/test/cmd/log_spec.rb index bdbca8912..b9e3e8d3e 100644 --- a/Library/Homebrew/test/cmd/log_spec.rb +++ b/Library/Homebrew/test/cmd/log_spec.rb @@ -33,7 +33,7 @@ describe "brew log", :integration_test do expect { brew "log", "#{shallow_tap}/testball" } .to output(/This is a test commit for Testball/).to_stdout - .and output(/Warning: The git repository is a shallow clone/).to_stderr + .and output(%r{Warning: homebrew/shallow is a shallow clone}).to_stderr .and be_a_success expect(shallow_tap.path/".git/shallow").to exist, "A shallow clone should have been created." diff --git a/Library/Homebrew/test/cmd/outdated_spec.rb b/Library/Homebrew/test/cmd/outdated_spec.rb index 2ce0825e8..65cce27c3 100644 --- a/Library/Homebrew/test/cmd/outdated_spec.rb +++ b/Library/Homebrew/test/cmd/outdated_spec.rb @@ -1,11 +1,87 @@ describe "brew outdated", :integration_test do - it "prints outdated Formulae" do - setup_test_formula "testball" - (HOMEBREW_CELLAR/"testball/0.0.1/foo").mkpath - - expect { brew "outdated" } - .to output("testball\n").to_stdout - .and not_to_output.to_stderr - .and be_a_success + context "quiet output" do + it "prints outdated Formulae" do + setup_test_formula "testball" + (HOMEBREW_CELLAR/"testball/0.0.1/foo").mkpath + + expect { brew "outdated" } + .to output("testball\n").to_stdout + .and not_to_output.to_stderr + .and be_a_success + end + end + + context "verbose output" do + it "prints out the installed and newer versions" do + setup_test_formula "testball" + (HOMEBREW_CELLAR/"testball/0.0.1/foo").mkpath + + expect { brew "outdated", "--verbose" } + .to output("testball (0.0.1) < 0.1\n").to_stdout + .and not_to_output.to_stderr + .and be_a_success + end + end + + context "pinned formula, verbose output" do + it "prints out the pinned version" do + setup_test_formula "testball" + (HOMEBREW_CELLAR/"testball/0.0.1/foo").mkpath + + shutup do + expect { brew "pin", "testball" }.to be_a_success + end + + expect { brew "outdated", "--verbose" } + .to output("testball (0.0.1) < 0.1 [pinned at 0.0.1]\n").to_stdout + .and not_to_output.to_stderr + .and be_a_success + end + end + + context "json output" do + it "includes pinned version in the json output" do + setup_test_formula "testball" + (HOMEBREW_CELLAR/"testball/0.0.1/foo").mkpath + + shutup do + expect { brew "pin", "testball" }.to be_a_success + end + + expected_json = [ + { + name: "testball", + installed_versions: ["0.0.1"], + current_version: "0.1", + pinned: true, + pinned_version: "0.0.1", + }, + ].to_json + + expect { brew "outdated", "--json=v1" } + .to output(expected_json + "\n").to_stdout + .and not_to_output.to_stderr + .and be_a_success + end + + it "has no pinned version when the formula isn't pinned" do + setup_test_formula "testball" + (HOMEBREW_CELLAR/"testball/0.0.1/foo").mkpath + + expected_json = [ + { + name: "testball", + installed_versions: ["0.0.1"], + current_version: "0.1", + pinned: false, + pinned_version: nil, + }, + ].to_json + + expect { brew "outdated", "--json=v1" } + .to output(expected_json + "\n").to_stdout + .and not_to_output.to_stderr + .and be_a_success + end end end diff --git a/Library/Homebrew/test/compiler_selector_spec.rb b/Library/Homebrew/test/compiler_selector_spec.rb index 0f6f6b5f2..18efbfd42 100644 --- a/Library/Homebrew/test/compiler_selector_spec.rb +++ b/Library/Homebrew/test/compiler_selector_spec.rb @@ -3,7 +3,7 @@ require "software_spec" describe CompilerSelector do subject { described_class.new(software_spec, versions, compilers) } - let(:compilers) { [:clang, :gcc, :llvm, :gnu] } + let(:compilers) { [:clang, :gcc, :gnu] } let(:software_spec) { SoftwareSpec.new } let(:cc) { :clang } let(:versions) do @@ -28,7 +28,6 @@ describe CompilerSelector do describe "#compiler" do it "raises an error if no matching compiler can be found" do software_spec.fails_with(:clang) - software_spec.fails_with(:llvm) software_spec.fails_with(:gcc) software_spec.fails_with(gcc: "4.8") software_spec.fails_with(gcc: "4.7") @@ -45,11 +44,6 @@ describe CompilerSelector do expect(subject.compiler).to eq(:gcc) end - it "returns clang if it fails with llvm" do - software_spec.fails_with(:llvm) - expect(subject.compiler).to eq(:clang) - end - it "returns clang if it fails with gcc" do software_spec.fails_with(:gcc) expect(subject.compiler).to eq(:clang) @@ -68,13 +62,11 @@ describe CompilerSelector do it "returns gcc if it fails with clang and llvm" do software_spec.fails_with(:clang) - software_spec.fails_with(:llvm) expect(subject.compiler).to eq(:gcc) end it "returns clang if it fails with gcc and llvm" do software_spec.fails_with(:gcc) - software_spec.fails_with(:llvm) expect(subject.compiler).to eq(:clang) end @@ -87,7 +79,6 @@ describe CompilerSelector do example "returns a lower version of gcc if it fails with the highest version" do software_spec.fails_with(:clang) software_spec.fails_with(:gcc) - software_spec.fails_with(:llvm) software_spec.fails_with(gcc: "4.8") expect(subject.compiler).to eq("gcc-4.7") end @@ -102,7 +93,6 @@ describe CompilerSelector do allow(versions).to receive(:gcc_build_version).and_return(Version::NULL) software_spec.fails_with(:clang) - software_spec.fails_with(:llvm) software_spec.fails_with(gcc: "4.8") software_spec.fails_with(gcc: "4.7") diff --git a/Library/Homebrew/test/dev-cmd/audit_spec.rb b/Library/Homebrew/test/dev-cmd/audit_spec.rb index ec1a34fb4..8a8096849 100644 --- a/Library/Homebrew/test/dev-cmd/audit_spec.rb +++ b/Library/Homebrew/test/dev-cmd/audit_spec.rb @@ -303,7 +303,7 @@ describe FormulaAuditor do end end - describe "#audit_line" do + describe "#line_problems" do specify "pkgshare" do fa = formula_auditor "foo", <<-EOS.undent, strict: true class Foo < Formula @@ -311,25 +311,25 @@ describe FormulaAuditor do end EOS - fa.audit_line 'ohai "#{share}/foo"', 3 + fa.line_problems 'ohai "#{share}/foo"', 3 expect(fa.problems.shift).to eq("Use \#{pkgshare} instead of \#{share}/foo") - fa.audit_line 'ohai "#{share}/foo/bar"', 3 + fa.line_problems 'ohai "#{share}/foo/bar"', 3 expect(fa.problems.shift).to eq("Use \#{pkgshare} instead of \#{share}/foo") - fa.audit_line 'ohai share/"foo"', 3 + fa.line_problems 'ohai share/"foo"', 3 expect(fa.problems.shift).to eq('Use pkgshare instead of (share/"foo")') - fa.audit_line 'ohai share/"foo/bar"', 3 + fa.line_problems 'ohai share/"foo/bar"', 3 expect(fa.problems.shift).to eq('Use pkgshare instead of (share/"foo")') - fa.audit_line 'ohai "#{share}/foo-bar"', 3 + fa.line_problems 'ohai "#{share}/foo-bar"', 3 expect(fa.problems).to eq([]) - fa.audit_line 'ohai share/"foo-bar"', 3 + fa.line_problems 'ohai share/"foo-bar"', 3 expect(fa.problems).to eq([]) - fa.audit_line 'ohai share/"bar"', 3 + fa.line_problems 'ohai share/"bar"', 3 expect(fa.problems).to eq([]) end @@ -344,15 +344,11 @@ describe FormulaAuditor do end EOS - fa.audit_desc - expect(fa.problems.shift) - .to eq("Description shouldn't include the formula name") - - fa.audit_line 'ohai "#{share}/foolibc++"', 3 + fa.line_problems 'ohai "#{share}/foolibc++"', 3 expect(fa.problems.shift) .to eq("Use \#{pkgshare} instead of \#{share}/foolibc++") - fa.audit_line 'ohai share/"foolibc++"', 3 + fa.line_problems 'ohai share/"foolibc++"', 3 expect(fa.problems.shift) .to eq('Use pkgshare instead of (share/"foolibc++")') end @@ -364,7 +360,7 @@ describe FormulaAuditor do end EOS - fa.audit_line "class Foo<Formula", 1 + fa.line_problems "class Foo<Formula", 1 expect(fa.problems.shift) .to eq("Use a space in class inheritance: class Foo < Formula") end @@ -372,10 +368,10 @@ describe FormulaAuditor do specify "default template" do fa = formula_auditor "foo", "class Foo < Formula; url '/foo-1.0.tgz'; end" - fa.audit_line '# system "cmake", ".", *std_cmake_args', 3 + fa.line_problems '# system "cmake", ".", *std_cmake_args', 3 expect(fa.problems.shift).to eq("Commented cmake call found") - fa.audit_line "# PLEASE REMOVE", 3 + fa.line_problems "# PLEASE REMOVE", 3 expect(fa.problems.shift).to eq("Please remove default template comments") end end @@ -413,32 +409,6 @@ describe FormulaAuditor do .to eq(["Don't recommend setuid in the caveats, suggest sudo instead."]) end - specify "#audit_desc" do - formula_descriptions = [ - { name: "foo", desc: nil, - problem: "Formula should have a desc" }, - { name: "bar", desc: "bar" * 30, - problem: "Description is too long" }, - { name: "baz", desc: "Baz commandline tool", - problem: "Description should use \"command-line\"" }, - { name: "qux", desc: "A tool called Qux", - problem: "Description shouldn't start with an indefinite article" }, - ] - - formula_descriptions.each do |formula| - content = <<-EOS.undent - class #{Formulary.class_s(formula[:name])} < Formula - url "http://example.com/#{formula[:name]}-1.0.tgz" - desc "#{formula[:desc]}" - end - EOS - - fa = formula_auditor formula[:name], content, strict: true - fa.audit_desc - expect(fa.problems.first).to match(formula[:problem]) - end - end - describe "#audit_homepage" do specify "homepage URLs" do fa = formula_auditor "foo", <<-EOS.undent, online: true diff --git a/Library/Homebrew/test/dev-cmd/pull_spec.rb b/Library/Homebrew/test/dev-cmd/pull_spec.rb index 3c0108df2..2ebe144bb 100644 --- a/Library/Homebrew/test/dev-cmd/pull_spec.rb +++ b/Library/Homebrew/test/dev-cmd/pull_spec.rb @@ -6,9 +6,7 @@ describe "brew pull", :integration_test do .and be_a_failure end - it "fetches a patch from a GitHub commit or pull request and applies it" do - skip "Requires network connection." if ENV["HOMEBREW_NO_GITHUB_API"] - + it "fetches a patch from a GitHub commit or pull request and applies it", :needs_network do CoreTap.instance.path.cd do shutup do system "git", "init" diff --git a/Library/Homebrew/test/diagnostic_spec.rb b/Library/Homebrew/test/diagnostic_spec.rb index 59560127c..c2bcdb9c0 100644 --- a/Library/Homebrew/test/diagnostic_spec.rb +++ b/Library/Homebrew/test/diagnostic_spec.rb @@ -41,6 +41,18 @@ describe Homebrew::Diagnostic::Checks do end end + specify "#check_access_lock_dir" do + begin + mode = HOMEBREW_LOCK_DIR.stat.mode & 0777 + HOMEBREW_LOCK_DIR.chmod 0555 + + expect(subject.check_access_lock_dir) + .to match("#{HOMEBREW_LOCK_DIR} isn't writable.") + ensure + HOMEBREW_LOCK_DIR.chmod mode + end + end + specify "#check_access_logs" do begin mode = HOMEBREW_LOGS.stat.mode & 0777 diff --git a/Library/Homebrew/test/formula_installer_spec.rb b/Library/Homebrew/test/formula_installer_spec.rb index 600b4560f..efe2bf5a2 100644 --- a/Library/Homebrew/test/formula_installer_spec.rb +++ b/Library/Homebrew/test/formula_installer_spec.rb @@ -134,4 +134,50 @@ describe FormulaInstaller do fi.check_install_sanity }.to raise_error(CannotInstallFormulaError) end + + describe "#install_requirement_formula?" do + before do + @requirement = Python3Requirement.new + allow(@requirement).to receive(:satisfied?).and_return(satisfied?) + allow(@requirement).to receive(:satisfied_by_formula?).and_return(satisfied_by_formula?) + allow_any_instance_of(Dependency).to receive(:installed?).and_return(installed?) + @dependent = formula do + url "foo" + version "0.1" + depends_on :python3 + end + @build = BuildOptions.new [], [] + @fi = FormulaInstaller.new(@dependent) + end + + subject { @fi.install_requirement_formula?(@requirement, @dependent, @build) } + + context "it returns false when requirement is satisfied" do + let(:satisfied?) { true } + let(:satisfied_by_formula?) { false } + let(:installed?) { false } + it { is_expected.to be false } + end + + context "it returns false when requirement is satisfied but default formula is installed" do + let(:satisfied?) { true } + let(:satisfied_by_formula?) { false } + let(:installed?) { true } + it { is_expected.to be false } + end + + context "it returns true when requirement isn't satisfied" do + let(:satisfied?) { false } + let(:satisfied_by_formula?) { false } + let(:installed?) { false } + it { is_expected.to be true } + end + + context "it returns true when requirement is satisfied by a formula" do + let(:satisfied?) { true } + let(:satisfied_by_formula?) { true } + let(:installed?) { false } + it { is_expected.to be true } + end + end end diff --git a/Library/Homebrew/test/formula_spec.rb b/Library/Homebrew/test/formula_spec.rb index 1e064912f..2309c36fb 100644 --- a/Library/Homebrew/test/formula_spec.rb +++ b/Library/Homebrew/test/formula_spec.rb @@ -129,6 +129,8 @@ describe Formula do alias_name = "bar" alias_path = "#{CoreTap.instance.alias_dir}/#{alias_name}" + CoreTap.instance.alias_dir.mkpath + FileUtils.ln_sf f.path, alias_path f.build = Tab.new(source: { "path" => alias_path }) @@ -160,6 +162,8 @@ describe Formula do alias_name = "bar" full_alias_name = "#{tap.user}/#{tap.repo}/#{alias_name}" alias_path = "#{tap.alias_dir}/#{alias_name}" + tap.alias_dir.mkpath + FileUtils.ln_sf f.path, alias_path f.build = Tab.new(source: { "path" => alias_path }) @@ -168,6 +172,8 @@ describe Formula do expect(f.full_installed_alias_name).to eq(full_alias_name) expect(f.installed_specified_name).to eq(alias_name) expect(f.full_installed_specified_name).to eq(full_alias_name) + + FileUtils.rm_rf HOMEBREW_LIBRARY/"Taps/user" end specify "#prefix" do @@ -402,6 +408,8 @@ describe Formula do url "foo-1.0" end f.build = Tab.new(source: { "path" => source_path.to_s }) + CoreTap.instance.alias_dir.mkpath + FileUtils.ln_sf f.path, source_path expect(f.alias_path).to eq(alias_path) expect(f.installed_alias_path).to eq(source_path.to_s) @@ -443,6 +451,9 @@ describe Formula do allow(described_class).to receive(:installed).and_return(formulae) + CoreTap.instance.alias_dir.mkpath + FileUtils.ln_sf formula_with_alias.path, alias_path + expect(described_class.installed_with_alias_path(alias_path)) .to eq([formula_with_alias]) end @@ -940,6 +951,9 @@ describe Formula do tab.source["path"] = alias_path stub_formula_loader(f, alias_path) + CoreTap.instance.alias_dir.mkpath + FileUtils.ln_sf f.path, alias_path + expect(f.current_installed_alias_target).to eq(f) expect(f.latest_formula).to eq(f) expect(f).not_to have_changed_installed_alias_target @@ -952,6 +966,9 @@ describe Formula do tab.source["path"] = alias_path stub_formula_loader(new_formula, alias_path) + CoreTap.instance.alias_dir.mkpath + FileUtils.ln_sf new_formula.path, alias_path + expect(f.current_installed_alias_target).to eq(new_formula) expect(f.latest_formula).to eq(new_formula) expect(f).to have_changed_installed_alias_target @@ -964,6 +981,9 @@ describe Formula do tab.source["path"] = alias_path stub_formula_loader(new_formula, alias_path) + CoreTap.instance.alias_dir.mkpath + FileUtils.ln_sf new_formula.path, alias_path + expect(new_formula.current_installed_alias_target).to eq(new_formula) expect(new_formula.latest_formula).to eq(new_formula) expect(new_formula).not_to have_changed_installed_alias_target @@ -1050,6 +1070,10 @@ describe Formula do f.follow_installed_alias = true f.build = setup_tab_for_prefix(same_prefix, path: alias_path) stub_formula_loader(new_formula, alias_path) + + CoreTap.instance.alias_dir.mkpath + FileUtils.ln_sf new_formula.path, alias_path + expect(f.outdated_kegs).not_to be_empty end @@ -1088,6 +1112,10 @@ describe Formula do tab = setup_tab_for_prefix(old_alias_target_prefix, path: alias_path) old_formula.build = tab allow(described_class).to receive(:installed).and_return([old_formula]) + + CoreTap.instance.alias_dir.mkpath + FileUtils.ln_sf f.path, alias_path + expect(f.outdated_kegs).not_to be_empty end diff --git a/Library/Homebrew/test/gpg2_requirement_spec.rb b/Library/Homebrew/test/gpg2_requirement_spec.rb index d7767abd3..a5501c84e 100644 --- a/Library/Homebrew/test/gpg2_requirement_spec.rb +++ b/Library/Homebrew/test/gpg2_requirement_spec.rb @@ -9,7 +9,7 @@ describe GPG2Requirement do ENV["PATH"] = dir/"bin" (dir/"bin/gpg").write <<-EOS.undent #!/bin/bash - echo 2.0.30 + echo 2.1.20 EOS FileUtils.chmod 0755, dir/"bin/gpg" diff --git a/Library/Homebrew/test/gpg_spec.rb b/Library/Homebrew/test/gpg_spec.rb index aa00d79f5..160e55379 100644 --- a/Library/Homebrew/test/gpg_spec.rb +++ b/Library/Homebrew/test/gpg_spec.rb @@ -13,7 +13,12 @@ describe Gpg do shutup do subject.create_test_key(dir) end - expect(dir/".gnupg/secring.gpg").to exist + + if subject.version == Version.create("2.0") + expect(dir/".gnupg/secring.gpg").to be_a_file + else + expect(dir/".gnupg/pubring.kbx").to be_a_file + end end end end diff --git a/Library/Homebrew/test/hardware_spec.rb b/Library/Homebrew/test/hardware_spec.rb index c5f8daf4e..de8d77e68 100644 --- a/Library/Homebrew/test/hardware_spec.rb +++ b/Library/Homebrew/test/hardware_spec.rb @@ -36,5 +36,52 @@ module Hardware ).to include(described_class.family) end end + + describe "::can_run?" do + it "reports that Intel machines can run Intel executables" do + allow(Hardware::CPU).to receive(:type).and_return :intel + allow(Hardware::CPU).to receive(:bits).and_return 64 + expect(Hardware::CPU.can_run?(:i386)).to be true + expect(Hardware::CPU.can_run?(:x86_64)).to be true + end + + it "reports that PowerPC machines can run PowerPC executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + allow(Hardware::CPU).to receive(:bits).and_return 64 + expect(Hardware::CPU.can_run?(:ppc)).to be true + expect(Hardware::CPU.can_run?(:ppc64)).to be true + end + + it "reports that 32-bit Intel machines can't run x86_64 executables" do + allow(Hardware::CPU).to receive(:type).and_return :intel + allow(Hardware::CPU).to receive(:bits).and_return 32 + expect(Hardware::CPU.can_run?(:x86_64)).to be false + end + + it "reports that 32-bit PowerPC machines can't run ppc64 executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + allow(Hardware::CPU).to receive(:bits).and_return 32 + expect(Hardware::CPU.can_run?(:ppc64)).to be false + end + + it "identifies that Intel and PowerPC machines can't run each others' executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + expect(Hardware::CPU.can_run?(:i386)).to be false + expect(Hardware::CPU.can_run?(:x86_64)).to be false + + allow(Hardware::CPU).to receive(:type).and_return :intel + expect(Hardware::CPU.can_run?(:ppc)).to be false + expect(Hardware::CPU.can_run?(:ppc64)).to be false + end + + it "returns false for unknown CPU types" do + allow(Hardware::CPU).to receive(:type).and_return :dunno + expect(Hardware::CPU.can_run?(:i386)).to be false + end + + it "returns false for unknown arches" do + expect(Hardware::CPU.can_run?(:blah)).to be false + end + end end end diff --git a/Library/Homebrew/test/missing_formula_spec.rb b/Library/Homebrew/test/missing_formula_spec.rb new file mode 100644 index 000000000..69bb3e70d --- /dev/null +++ b/Library/Homebrew/test/missing_formula_spec.rb @@ -0,0 +1,171 @@ +require "missing_formula" + +describe Homebrew::MissingFormula do + context "::reason" do + subject { described_class.reason("gem") } + + it { is_expected.to_not be_nil } + end + + context "::blacklisted_reason" do + matcher(:be_blacklisted) do + match(&Homebrew::MissingFormula.method(:blacklisted_reason)) + end + + context "rubygems" do + %w[gem rubygem rubygems].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "latex" do + %w[latex tex tex-live texlive TexLive].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "pip" do + subject { "pip" } + + it { is_expected.to be_blacklisted } + end + + context "pil" do + subject { "pil" } + + it { is_expected.to be_blacklisted } + end + + context "macruby" do + subject { "MacRuby" } + + it { is_expected.to be_blacklisted } + end + + context "lzma" do + %w[lzma liblzma].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "gtest" do + %w[gtest googletest google-test].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "gmock" do + %w[gmock googlemock google-mock].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "sshpass" do + subject { "sshpass" } + + it { is_expected.to be_blacklisted } + end + + context "gsutil" do + subject { "gsutil" } + + it { is_expected.to be_blacklisted } + end + + context "clojure" do + subject { "clojure" } + + it { is_expected.to be_blacklisted } + end + + context "gfortran" do + subject { "gfortran" } + + it { is_expected.to be_blacklisted } + end + + context "play" do + subject { "play" } + + it { is_expected.to be_blacklisted } + end + + context "haskell-platform" do + subject { "haskell-platform" } + + it { is_expected.to be_blacklisted } + end + + context "xcode", :needs_macos do + %w[xcode Xcode].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + end + + context "::tap_migration_reason" do + subject { described_class.tap_migration_reason(formula) } + + before do + Tap.clear_cache + tap_path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" + tap_path.mkpath + (tap_path/"tap_migrations.json").write <<-EOS.undent + { "migrated-formula": "homebrew/bar" } + EOS + end + + context "with a migrated formula" do + let(:formula) { "migrated-formula" } + it { is_expected.to_not be_nil } + end + + context "with a missing formula" do + let(:formula) { "missing-formula" } + it { is_expected.to be_nil } + end + end + + context "::deleted_reason" do + subject { described_class.deleted_reason(formula, silent: true) } + + before do + Tap.clear_cache + tap_path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" + tap_path.mkpath + (tap_path/"deleted-formula.rb").write "placeholder" + + tap_path.cd do + shutup do + system "git", "init" + system "git", "add", "--all" + system "git", "commit", "-m", "initial state" + system "git", "rm", "deleted-formula.rb" + system "git", "commit", "-m", "delete formula 'deleted-formula'" + end + end + end + + context "with a deleted formula" do + let(:formula) { "homebrew/foo/deleted-formula" } + it { is_expected.to_not be_nil } + end + + context "with a formula that never existed" do + let(:formula) { "homebrew/foo/missing-formula" } + it { is_expected.to be_nil } + end + end +end diff --git a/Library/Homebrew/test/os/linux/osxfuse_requirement_spec.rb b/Library/Homebrew/test/os/linux/osxfuse_requirement_spec.rb new file mode 100644 index 000000000..c45af2fa7 --- /dev/null +++ b/Library/Homebrew/test/os/linux/osxfuse_requirement_spec.rb @@ -0,0 +1,9 @@ +require "requirements/osxfuse_requirement" + +describe OsxfuseRequirement do + subject { described_class.new([]) } + + describe "#message" do + its(:message) { is_expected.to match("libfuse is required to install this formula") } + end +end diff --git a/Library/Homebrew/test/os/mac/hardware_spec.rb b/Library/Homebrew/test/os/mac/hardware_spec.rb new file mode 100644 index 000000000..fa577ba7d --- /dev/null +++ b/Library/Homebrew/test/os/mac/hardware_spec.rb @@ -0,0 +1,56 @@ +require "hardware" +require "extend/os/mac/hardware/cpu" + +describe Hardware::CPU do + describe "::can_run?" do + it "reports that Intel Macs can run Intel executables" do + allow(Hardware::CPU).to receive(:type).and_return :intel + allow(Hardware::CPU).to receive(:bits).and_return 64 + expect(Hardware::CPU.can_run?(:i386)).to be true + expect(Hardware::CPU.can_run?(:x86_64)).to be true + end + + it "reports that PowerPC Macs can run PowerPC executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + allow(Hardware::CPU).to receive(:bits).and_return 64 + expect(Hardware::CPU.can_run?(:ppc)).to be true + expect(Hardware::CPU.can_run?(:ppc64)).to be true + end + + it "reports that 32-bit Intel Macs can't run x86_64 executables" do + allow(Hardware::CPU).to receive(:type).and_return :intel + allow(Hardware::CPU).to receive(:bits).and_return 32 + expect(Hardware::CPU.can_run?(:x86_64)).to be false + end + + it "reports that 32-bit PowerPC Macs can't run ppc64 executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + allow(Hardware::CPU).to receive(:bits).and_return 32 + expect(Hardware::CPU.can_run?(:ppc64)).to be false + end + + it "reports that Intel Macs can only run 32-bit PowerPC executables on 10.6 and older" do + allow(Hardware::CPU).to receive(:type).and_return :intel + allow(OS::Mac).to receive(:version).and_return OS::Mac::Version.new "10.6" + expect(Hardware::CPU.can_run?(:ppc)).to be true + + allow(OS::Mac).to receive(:version).and_return OS::Mac::Version.new "10.7" + expect(Hardware::CPU.can_run?(:ppc)).to be false + end + + it "reports that PowerPC Macs can't run Intel executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + expect(Hardware::CPU.can_run?(:i386)).to be false + expect(Hardware::CPU.can_run?(:x86_64)).to be false + end + + it "returns false for unknown CPU types" do + allow(Hardware::CPU).to receive(:type).and_return :dunno + expect(Hardware::CPU.can_run?(:i386)).to be false + end + + it "returns false for unknown arches" do + expect(Hardware::CPU.can_run?(:blah)).to be false + end + end +end diff --git a/Library/Homebrew/test/os/mac/osxfuse_requirement_spec.rb b/Library/Homebrew/test/os/mac/osxfuse_requirement_spec.rb new file mode 100644 index 000000000..06d3d885e --- /dev/null +++ b/Library/Homebrew/test/os/mac/osxfuse_requirement_spec.rb @@ -0,0 +1,36 @@ +require "requirements/osxfuse_requirement" + +describe OsxfuseRequirement do + subject { described_class.new([]) } + + describe "::binary_osxfuse_installed?" do + it "returns false if fuse.h does not exist" do + allow(File).to receive(:exist?).and_return(false) + expect(described_class).not_to be_binary_osxfuse_installed + end + + it "returns false if osxfuse include directory is a symlink" do + allow(File).to receive(:exist?).and_return(true) + allow(File).to receive(:symlink?).and_return(true) + expect(described_class).not_to be_binary_osxfuse_installed + end + end + + describe "environment" do + it "adds the fuse directories to the appropriate paths" do + expect(ENV).to receive(:append_path).with("PKG_CONFIG_PATH", any_args) + expect(ENV).to receive(:append_path).with("HOMEBREW_LIBRARY_PATHS", any_args) + expect(ENV).to receive(:append_path).with("HOMEBREW_INCLUDE_PATHS", any_args) + subject.modify_build_environment + end + end +end + +describe NonBinaryOsxfuseRequirement do + subject { described_class.new([]) } + + describe "#message" do + msg = /osxfuse is already installed from the binary distribution/ + its(:message) { is_expected.to match(msg) } + end +end diff --git a/Library/Homebrew/test/requirement_spec.rb b/Library/Homebrew/test/requirement_spec.rb index 110a7ac4f..959041cf4 100644 --- a/Library/Homebrew/test/requirement_spec.rb +++ b/Library/Homebrew/test/requirement_spec.rb @@ -146,17 +146,13 @@ describe Requirement do end describe "#build?" do - context "#build true is specified" do - let(:klass) do - Class.new(described_class) do - build true - end - end + context ":build tag is specified" do + subject { described_class.new([:build]) } it { is_expected.to be_a_build_requirement } end - context "#build ommitted" do + context "#build omitted" do it { is_expected.not_to be_a_build_requirement } end end diff --git a/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb b/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb new file mode 100644 index 000000000..5be2d6cf5 --- /dev/null +++ b/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb @@ -0,0 +1,67 @@ +require "rubocop" +require "rubocop/rspec/support" +require_relative "../../extend/string" +require_relative "../../rubocops/bottle_block_cop" + +describe RuboCop::Cop::Homebrew::CorrectBottleBlock do + subject(:cop) { described_class.new } + + context "When auditing Bottle Block" do + it "When there is revision in bottle block" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + bottle do + cellar :any + revision 2 + end + end + EOS + + expected_offenses = [{ message: "Use rebuild instead of revision in bottle block", + severity: :convention, + line: 5, + column: 4, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + def expect_offense(expected, actual) + expect(actual.message).to eq(expected[:message]) + expect(actual.severity).to eq(expected[:severity]) + expect(actual.line).to eq(expected[:line]) + expect(actual.column).to eq(expected[:column]) + end + end + + context "When auditing Bottle Block with auto correct" do + it "When there is revision in bottle block" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + bottle do + cellar :any + revision 2 + end + end + EOS + corrected_source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + bottle do + cellar :any + rebuild 2 + end + end + EOS + + new_source = autocorrect_source(cop, source) + expect(new_source).to eq(corrected_source) + end + end +end diff --git a/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb b/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb new file mode 100644 index 000000000..04c4c27da --- /dev/null +++ b/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb @@ -0,0 +1,121 @@ +require "rubocop" +require "rubocop/rspec/support" +require_relative "../../extend/string" +require_relative "../../rubocops/formula_desc_cop" + +describe RuboCop::Cop::Homebrew::FormulaDesc do + subject(:cop) { described_class.new } + + context "When auditing formula desc" do + it "When there is no desc" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + end + EOS + + expected_offenses = [{ message: "Formula should have a desc (Description).", + severity: :convention, + line: 1, + column: 0, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When desc is too long" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + desc '#{"bar"*30}' + end + EOS + + msg = <<-EOS.undent + Description is too long. "name: desc" should be less than 80 characters. + Length is calculated as Foo + desc. (currently 95) + EOS + expected_offenses = [{ message: msg, + severity: :convention, + line: 3, + column: 2, + source: source }] + + inspect_source(cop, source) + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When wrong \"command-line\" usage in desc" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + desc 'command line' + end + EOS + + expected_offenses = [{ message: "Description should use \"command-line\" instead of \"command line\"", + severity: :convention, + line: 3, + column: 8, + source: source }] + + inspect_source(cop, source) + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When an article is used in desc" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + desc 'An ' + end + EOS + + expected_offenses = [{ message: "Description shouldn't start with an indefinite article (An )", + severity: :convention, + line: 3, + column: 8, + source: source }] + + inspect_source(cop, source) + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When formula name is in desc" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + desc 'Foo' + end + EOS + + expected_offenses = [{ message: "Description shouldn't include the formula name", + severity: :convention, + line: 3, + column: 8, + source: source }] + + inspect_source(cop, source) + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + def expect_offense(expected, actual) + expect(actual.message).to eq(expected[:message]) + expect(actual.severity).to eq(expected[:severity]) + expect(actual.line).to eq(expected[:line]) + expect(actual.column).to eq(expected[:column]) + end + end +end diff --git a/Library/Homebrew/test/spec_helper.rb b/Library/Homebrew/test/spec_helper.rb index 2f6274fd1..005c6d2fb 100644 --- a/Library/Homebrew/test/spec_helper.rb +++ b/Library/Homebrew/test/spec_helper.rb @@ -61,6 +61,10 @@ RSpec.configure do |config| skip "Python not installed." unless which("python") end + config.before(:each, :needs_network) do + skip "Requires network connection." unless ENV["HOMEBREW_TEST_ONLINE"] + end + config.around(:each) do |example| begin TEST_DIRECTORIES.each(&:mkpath) diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/with-non-executable-binary.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/with-non-executable-binary.rb new file mode 100644 index 000000000..4bd2f0882 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/with-non-executable-binary.rb @@ -0,0 +1,9 @@ +cask 'with-non-executable-binary' do + version '1.2.3' + sha256 'd5b2dfbef7ea28c25f7a77cd7fa14d013d82b626db1d82e00e25822464ba19e2' + + url "file://#{TEST_FIXTURE_DIR}/cask/naked_non_executable" + homepage 'http://example.com/with-binary' + + binary "naked_non_executable" +end diff --git a/Library/Homebrew/test/support/fixtures/cask/naked_non_executable b/Library/Homebrew/test/support/fixtures/cask/naked_non_executable new file mode 100644 index 000000000..039e4d006 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/naked_non_executable @@ -0,0 +1,2 @@ +#!/bin/sh +exit 0 diff --git a/Library/Homebrew/test/tab_spec.rb b/Library/Homebrew/test/tab_spec.rb index fec390c28..1b0836c93 100644 --- a/Library/Homebrew/test/tab_spec.rb +++ b/Library/Homebrew/test/tab_spec.rb @@ -258,7 +258,7 @@ describe Tab do it "can create a Tab for a non-existant Keg" do f.prefix.mkpath - expect(subject.tabfile).to be nil + expect(subject.tabfile).to eq(f_tab_path) end end diff --git a/Library/Homebrew/test/utils/shell_spec.rb b/Library/Homebrew/test/utils/shell_spec.rb index c44bd8253..d32f9928f 100644 --- a/Library/Homebrew/test/utils/shell_spec.rb +++ b/Library/Homebrew/test/utils/shell_spec.rb @@ -1,92 +1,92 @@ require "utils/shell" describe Utils::Shell do - describe "::shell_profile" do + describe "::profile" do it "returns ~/.bash_profile by default" do ENV["SHELL"] = "/bin/another_shell" - expect(subject.shell_profile).to eq("~/.bash_profile") + expect(subject.profile).to eq("~/.bash_profile") end it "returns ~/.bash_profile for Sh" do ENV["SHELL"] = "/bin/another_shell" - expect(subject.shell_profile).to eq("~/.bash_profile") + expect(subject.profile).to eq("~/.bash_profile") end it "returns ~/.bash_profile for Bash" do ENV["SHELL"] = "/bin/bash" - expect(subject.shell_profile).to eq("~/.bash_profile") + expect(subject.profile).to eq("~/.bash_profile") end it "returns ~/.zshrc for Zsh" do ENV["SHELL"] = "/bin/zsh" - expect(subject.shell_profile).to eq("~/.zshrc") + expect(subject.profile).to eq("~/.zshrc") end it "returns ~/.kshrc for Ksh" do ENV["SHELL"] = "/bin/ksh" - expect(subject.shell_profile).to eq("~/.kshrc") + expect(subject.profile).to eq("~/.kshrc") end end - describe "::path_to_shell" do + describe "::from_path" do it "supports a raw command name" do - expect(subject.path_to_shell("bash")).to eq(:bash) + expect(subject.from_path("bash")).to eq(:bash) end it "supports full paths" do - expect(subject.path_to_shell("/bin/bash")).to eq(:bash) + expect(subject.from_path("/bin/bash")).to eq(:bash) end it "supports versions" do - expect(subject.path_to_shell("zsh-5.2")).to eq(:zsh) + expect(subject.from_path("zsh-5.2")).to eq(:zsh) end it "strips newlines" do - expect(subject.path_to_shell("zsh-5.2\n")).to eq(:zsh) + expect(subject.from_path("zsh-5.2\n")).to eq(:zsh) end it "returns nil when input is invalid" do - expect(subject.path_to_shell("")).to be nil - expect(subject.path_to_shell("@@@@@@")).to be nil - expect(subject.path_to_shell("invalid_shell-4.2")).to be nil + expect(subject.from_path("")).to be nil + expect(subject.from_path("@@@@@@")).to be nil + expect(subject.from_path("invalid_shell-4.2")).to be nil end end specify "::sh_quote" do - expect(subject.sh_quote("")).to eq("''") - expect(subject.sh_quote("\\")).to eq("\\\\") - expect(subject.sh_quote("\n")).to eq("'\n'") - expect(subject.sh_quote("$")).to eq("\\$") - expect(subject.sh_quote("word")).to eq("word") + expect(subject.send(:sh_quote, "")).to eq("''") + expect(subject.send(:sh_quote, "\\")).to eq("\\\\") + expect(subject.send(:sh_quote, "\n")).to eq("'\n'") + expect(subject.send(:sh_quote, "$")).to eq("\\$") + expect(subject.send(:sh_quote, "word")).to eq("word") end specify "::csh_quote" do - expect(subject.csh_quote("")).to eq("''") - expect(subject.csh_quote("\\")).to eq("\\\\") + expect(subject.send(:csh_quote, "")).to eq("''") + expect(subject.send(:csh_quote, "\\")).to eq("\\\\") # note this test is different than for sh - expect(subject.csh_quote("\n")).to eq("'\\\n'") - expect(subject.csh_quote("$")).to eq("\\$") - expect(subject.csh_quote("word")).to eq("word") + expect(subject.send(:csh_quote, "\n")).to eq("'\\\n'") + expect(subject.send(:csh_quote, "$")).to eq("\\$") + expect(subject.send(:csh_quote, "word")).to eq("word") end - describe "::prepend_path_in_shell_profile" do + describe "::prepend_path_in_profile" do let(:path) { "/my/path" } it "supports Tcsh" do ENV["SHELL"] = "/bin/tcsh" - expect(subject.prepend_path_in_shell_profile(path)) + expect(subject.prepend_path_in_profile(path)) .to start_with("echo 'setenv PATH #{path}:$") end it "supports Bash" do ENV["SHELL"] = "/bin/bash" - expect(subject.prepend_path_in_shell_profile(path)) + expect(subject.prepend_path_in_profile(path)) .to start_with("echo 'export PATH=\"#{path}:$") end it "supports Fish" do ENV["SHELL"] = "/usr/local/bin/fish" - expect(subject.prepend_path_in_shell_profile(path)) + expect(subject.prepend_path_in_profile(path)) .to start_with("echo 'set -g fish_user_paths \"#{path}\" $fish_user_paths' >>") end end diff --git a/Library/Homebrew/test/utils_spec.rb b/Library/Homebrew/test/utils_spec.rb index 314c299a8..dd7ea20de 100644 --- a/Library/Homebrew/test/utils_spec.rb +++ b/Library/Homebrew/test/utils_spec.rb @@ -189,7 +189,13 @@ describe "globally-scoped helper methods" do specify "#which_editor" do ENV["HOMEBREW_EDITOR"] = "vemate" - expect(which_editor).to eq("vemate") + ENV["HOMEBREW_PATH"] = dir + + editor = dir/"vemate" + FileUtils.touch editor + FileUtils.chmod 0755, editor + + expect(which_editor).to eql editor end specify "#gzip" do diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index 46a8cc68e..f37b777ee 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -320,21 +320,21 @@ def which_all(cmd, path = ENV["PATH"]) end def which_editor - editor = ENV.values_at("HOMEBREW_EDITOR", "VISUAL", "EDITOR").compact.first - return editor unless editor.nil? + editor = ENV.values_at("HOMEBREW_EDITOR", "VISUAL").compact.reject(&:empty?).first + return which(editor, ENV["HOMEBREW_PATH"]) unless editor.nil? # Find Textmate - editor = "mate" if which "mate" + editor = which("mate", ENV["HOMEBREW_PATH"]) # Find BBEdit / TextWrangler - editor ||= "edit" if which "edit" + editor ||= which("edit", ENV["HOMEBREW_PATH"]) # Find vim - editor ||= "vim" if which "vim" + editor ||= which("vim", ENV["HOMEBREW_PATH"]) # Default to standard vim editor ||= "/usr/bin/vim" opoo <<-EOS.undent Using #{editor} because no editor was set in the environment. - This may change in the future, so we recommend setting EDITOR, VISUAL, + This may change in the future, so we recommend setting EDITOR, or HOMEBREW_EDITOR to your preferred text editor. EOS diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index 5f961974c..a5ed5394a 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -268,7 +268,6 @@ module GitHub def print_pull_requests_matching(query) return [] if ENV["HOMEBREW_NO_GITHUB_API"] - ohai "Searching pull requests..." open_or_closed_prs = issues_matching(query, type: "pr") diff --git a/Library/Homebrew/utils/shell.rb b/Library/Homebrew/utils/shell.rb index 302167d47..5327f6ecf 100644 --- a/Library/Homebrew/utils/shell.rb +++ b/Library/Homebrew/utils/shell.rb @@ -1,62 +1,24 @@ module Utils - SHELL_PROFILE_MAP = { - bash: "~/.bash_profile", - csh: "~/.cshrc", - fish: "~/.config/fish/config.fish", - ksh: "~/.kshrc", - sh: "~/.bash_profile", - tcsh: "~/.tcshrc", - zsh: "~/.zshrc", - }.freeze - module Shell - UNSAFE_SHELL_CHAR = %r{([^A-Za-z0-9_\-.,:/@\n])} + module_function # take a path and heuristically convert it # to a shell name, return nil if there's no match - def path_to_shell(path) + def from_path(path) # we only care about the basename shell_name = File.basename(path) # handle possible version suffix like `zsh-5.2` shell_name.sub!(/-.*\z/m, "") shell_name.to_sym if %w[bash csh fish ksh sh tcsh zsh].include?(shell_name) end - module_function :path_to_shell - - def preferred_shell - path_to_shell(ENV.fetch("SHELL", "")) - end - module_function :preferred_shell - def parent_shell - path_to_shell(`ps -p #{Process.ppid} -o ucomm=`.strip) + def preferred + from_path(ENV.fetch("SHELL", "")) end - module_function :parent_shell - def csh_quote(str) - # ruby's implementation of shell_escape - str = str.to_s - return "''" if str.empty? - str = str.dup - # anything that isn't a known safe character is padded - str.gsub!(UNSAFE_SHELL_CHAR, "\\\\" + "\\1") - # newlines have to be specially quoted in csh - str.gsub!(/\n/, "'\\\n'") - str + def parent + from_path(`ps -p #{Process.ppid} -o ucomm=`.strip) end - module_function :csh_quote - - def sh_quote(str) - # ruby's implementation of shell_escape - str = str.to_s - return "''" if str.empty? - str = str.dup - # anything that isn't a known safe character is padded - str.gsub!(UNSAFE_SHELL_CHAR, "\\\\" + "\\1") - str.gsub!(/\n/, "'\n'") - str - end - module_function :sh_quote # quote values. quoting keys is overkill def export_value(shell, key, value) @@ -72,24 +34,60 @@ module Utils "setenv #{key} #{csh_quote(value)};" end end - module_function :export_value # return the shell profile file based on users' preferred shell - def shell_profile - SHELL_PROFILE_MAP.fetch(preferred_shell, "~/.bash_profile") + def profile + SHELL_PROFILE_MAP.fetch(preferred, "~/.bash_profile") end - module_function :shell_profile - def prepend_path_in_shell_profile(path) - case preferred_shell + def prepend_path_in_profile(path) + case preferred when :bash, :ksh, :sh, :zsh, nil - "echo 'export PATH=\"#{sh_quote(path)}:$PATH\"' >> #{shell_profile}" + "echo 'export PATH=\"#{sh_quote(path)}:$PATH\"' >> #{profile}" when :csh, :tcsh - "echo 'setenv PATH #{csh_quote(path)}:$PATH' >> #{shell_profile}" + "echo 'setenv PATH #{csh_quote(path)}:$PATH' >> #{profile}" when :fish - "echo 'set -g fish_user_paths \"#{sh_quote(path)}\" $fish_user_paths' >> #{shell_profile}" + "echo 'set -g fish_user_paths \"#{sh_quote(path)}\" $fish_user_paths' >> #{profile}" end end - module_function :prepend_path_in_shell_profile + + private + + SHELL_PROFILE_MAP = { + bash: "~/.bash_profile", + csh: "~/.cshrc", + fish: "~/.config/fish/config.fish", + ksh: "~/.kshrc", + sh: "~/.bash_profile", + tcsh: "~/.tcshrc", + zsh: "~/.zshrc", + }.freeze + + UNSAFE_SHELL_CHAR = %r{([^A-Za-z0-9_\-.,:/@\n])} + + module_function + + def csh_quote(str) + # ruby's implementation of shell_escape + str = str.to_s + return "''" if str.empty? + str = str.dup + # anything that isn't a known safe character is padded + str.gsub!(UNSAFE_SHELL_CHAR, "\\\\" + "\\1") + # newlines have to be specially quoted in csh + str.gsub!(/\n/, "'\\\n'") + str + end + + def sh_quote(str) + # ruby's implementation of shell_escape + str = str.to_s + return "''" if str.empty? + str = str.dup + # anything that isn't a known safe character is padded + str.gsub!(UNSAFE_SHELL_CHAR, "\\\\" + "\\1") + str.gsub!(/\n/, "'\n'") + str + end end end diff --git a/Library/Homebrew/vendor/README.md b/Library/Homebrew/vendor/README.md index f998d26c5..906d42918 100644 --- a/Library/Homebrew/vendor/README.md +++ b/Library/Homebrew/vendor/README.md @@ -3,7 +3,7 @@ Vendored Dependencies * [plist](https://github.com/bleything/plist), version 3.1.0 -* [ruby-macho](https://github.com/Homebrew/ruby-macho), version 0.2.6 +* [ruby-macho](https://github.com/Homebrew/ruby-macho), version 1.1.0 ## Licenses: @@ -33,7 +33,7 @@ Vendored Dependencies ### ruby-macho > The MIT License -> Copyright (c) 2015, 2016 William Woodruff <william @ tuffbizz.com> +> Copyright (c) 2015, 2016, 2017 William Woodruff <william @ tuffbizz.com> > > Permission is hereby granted, free of charge, to any person obtaining a copy > of this software and associated documentation files (the "Software"), to deal diff --git a/Library/Homebrew/vendor/macho/macho.rb b/Library/Homebrew/vendor/macho/macho.rb index de1d4ff43..b7f20ea19 100644 --- a/Library/Homebrew/vendor/macho/macho.rb +++ b/Library/Homebrew/vendor/macho/macho.rb @@ -5,7 +5,6 @@ require "#{File.dirname(__FILE__)}/macho/load_commands" require "#{File.dirname(__FILE__)}/macho/sections" require "#{File.dirname(__FILE__)}/macho/macho_file" require "#{File.dirname(__FILE__)}/macho/fat_file" -require "#{File.dirname(__FILE__)}/macho/open" require "#{File.dirname(__FILE__)}/macho/exceptions" require "#{File.dirname(__FILE__)}/macho/utils" require "#{File.dirname(__FILE__)}/macho/tools" @@ -13,5 +12,29 @@ require "#{File.dirname(__FILE__)}/macho/tools" # The primary namespace for ruby-macho. module MachO # release version - VERSION = "0.2.6".freeze + VERSION = "1.1.0".freeze + + # Opens the given filename as a MachOFile or FatFile, depending on its magic. + # @param filename [String] the file being opened + # @return [MachOFile] if the file is a Mach-O + # @return [FatFile] if the file is a Fat file + # @raise [ArgumentError] if the given file does not exist + # @raise [TruncatedFileError] if the file is too small to have a valid header + # @raise [MagicError] if the file's magic is not valid Mach-O magic + def self.open(filename) + raise ArgumentError, "#{filename}: no such file" unless File.file?(filename) + raise TruncatedFileError unless File.stat(filename).size >= 4 + + magic = File.open(filename, "rb") { |f| f.read(4) }.unpack("N").first + + if Utils.fat_magic?(magic) + file = FatFile.new(filename) + elsif Utils.magic?(magic) + file = MachOFile.new(filename) + else + raise MagicError, magic + end + + file + end end diff --git a/Library/Homebrew/vendor/macho/macho/exceptions.rb b/Library/Homebrew/vendor/macho/macho/exceptions.rb index 262c195a3..14c2c22ae 100644 --- a/Library/Homebrew/vendor/macho/macho/exceptions.rb +++ b/Library/Homebrew/vendor/macho/macho/exceptions.rb @@ -80,7 +80,8 @@ module MachO # @param cputype [Fixnum] the CPU type of the unknown pair # @param cpusubtype [Fixnum] the CPU sub-type of the unknown pair def initialize(cputype, cpusubtype) - super "Unrecognized CPU sub-type: 0x#{"%08x" % cpusubtype} (for CPU type: 0x#{"%08x" % cputype})" + super "Unrecognized CPU sub-type: 0x#{"%08x" % cpusubtype}" \ + " (for CPU type: 0x#{"%08x" % cputype})" end end @@ -108,13 +109,15 @@ module MachO end end - # Raised when the number of arguments used to create a load command manually is wrong. + # Raised when the number of arguments used to create a load command manually + # is wrong. class LoadCommandCreationArityError < MachOError # @param cmd_sym [Symbol] the load command's symbol # @param expected_arity [Fixnum] the number of arguments expected # @param actual_arity [Fixnum] the number of arguments received def initialize(cmd_sym, expected_arity, actual_arity) - super "Expected #{expected_arity} arguments for #{cmd_sym} creation, got #{actual_arity}" + super "Expected #{expected_arity} arguments for #{cmd_sym} creation," \ + " got #{actual_arity}" end end @@ -130,7 +133,8 @@ module MachO class LCStrMalformedError < MachOError # @param lc [MachO::LoadCommand] the load command containing the string def initialize(lc) - super "Load command #{lc.type} at offset #{lc.view.offset} contains a malformed string" + super "Load command #{lc.type} at offset #{lc.view.offset} contains a" \ + " malformed string" end end diff --git a/Library/Homebrew/vendor/macho/macho/fat_file.rb b/Library/Homebrew/vendor/macho/macho/fat_file.rb index 9f29922e6..351be5ac6 100644 --- a/Library/Homebrew/vendor/macho/macho/fat_file.rb +++ b/Library/Homebrew/vendor/macho/macho/fat_file.rb @@ -1,24 +1,50 @@ +require "forwardable" + module MachO # Represents a "Fat" file, which contains a header, a listing of available # architectures, and one or more Mach-O binaries. # @see https://en.wikipedia.org/wiki/Mach-O#Multi-architecture_binaries - # @see MachO::MachOFile + # @see MachOFile class FatFile + extend Forwardable + # @return [String] the filename loaded from, or nil if loaded from a binary string attr_accessor :filename - # @return [MachO::FatHeader] the file's header + # @return [Headers::FatHeader] the file's header attr_reader :header - # @return [Array<MachO::FatArch>] an array of fat architectures + # @return [Array<Headers::FatArch>] an array of fat architectures attr_reader :fat_archs - # @return [Array<MachO::MachOFile>] an array of Mach-O binaries + # @return [Array<MachOFile>] an array of Mach-O binaries attr_reader :machos + # Creates a new FatFile from the given (single-arch) Mach-Os + # @param machos [Array<MachOFile>] the machos to combine + # @return [FatFile] a new FatFile containing the give machos + def self.new_from_machos(*machos) + header = Headers::FatHeader.new(Headers::FAT_MAGIC, machos.size) + offset = Headers::FatHeader.bytesize + (machos.size * Headers::FatArch.bytesize) + fat_archs = [] + machos.each do |macho| + fat_archs << Headers::FatArch.new(macho.header.cputype, + macho.header.cpusubtype, + offset, macho.serialize.bytesize, + macho.alignment) + offset += macho.serialize.bytesize + end + + bin = header.serialize + bin << fat_archs.map(&:serialize).join + bin << machos.map(&:serialize).join + + new_from_bin(bin) + end + # Creates a new FatFile instance from a binary string. # @param bin [String] a binary string containing raw Mach-O data - # @return [MachO::FatFile] a new FatFile + # @return [FatFile] a new FatFile def self.new_from_bin(bin) instance = allocate instance.initialize_from_bin(bin) @@ -38,7 +64,7 @@ module MachO end # Initializes a new FatFile instance from a binary string. - # @see MachO::FatFile.new_from_bin + # @see new_from_bin # @api private def initialize_from_bin(bin) @filename = nil @@ -52,70 +78,41 @@ module MachO @raw_data end - # @return [Boolean] true if the file is of type `MH_OBJECT`, false otherwise - def object? - machos.first.object? - end - - # @return [Boolean] true if the file is of type `MH_EXECUTE`, false otherwise - def executable? - machos.first.executable? - end - - # @return [Boolean] true if the file is of type `MH_FVMLIB`, false otherwise - def fvmlib? - machos.first.fvmlib? - end - - # @return [Boolean] true if the file is of type `MH_CORE`, false otherwise - def core? - machos.first.core? - end - - # @return [Boolean] true if the file is of type `MH_PRELOAD`, false otherwise - def preload? - machos.first.preload? - end - - # @return [Boolean] true if the file is of type `MH_DYLIB`, false otherwise - def dylib? - machos.first.dylib? - end - - # @return [Boolean] true if the file is of type `MH_DYLINKER`, false otherwise - def dylinker? - machos.first.dylinker? - end - - # @return [Boolean] true if the file is of type `MH_BUNDLE`, false otherwise - def bundle? - machos.first.bundle? - end - - # @return [Boolean] true if the file is of type `MH_DSYM`, false otherwise - def dsym? - machos.first.dsym? - end - - # @return [Boolean] true if the file is of type `MH_KEXT_BUNDLE`, false otherwise - def kext? - machos.first.kext? - end - - # @return [Fixnum] the file's magic number - def magic - header.magic - end + # @!method object? + # @return (see MachO::MachOFile#object?) + # @!method executable? + # @return (see MachO::MachOFile#executable?) + # @!method fvmlib? + # @return (see MachO::MachOFile#fvmlib?) + # @!method core? + # @return (see MachO::MachOFile#core?) + # @!method preload? + # @return (see MachO::MachOFile#preload?) + # @!method dylib? + # @return (see MachO::MachOFile#dylib?) + # @!method dylinker? + # @return (see MachO::MachOFile#dylinker?) + # @!method bundle? + # @return (see MachO::MachOFile#bundle?) + # @!method dsym? + # @return (see MachO::MachOFile#dsym?) + # @!method kext? + # @return (see MachO::MachOFile#kext?) + # @!method filetype + # @return (see MachO::MachOFile#filetype) + # @!method dylib_id + # @return (see MachO::MachOFile#dylib_id) + def_delegators :canonical_macho, :object?, :executable?, :fvmlib?, + :core?, :preload?, :dylib?, :dylinker?, :bundle?, + :dsym?, :kext?, :filetype, :dylib_id + + # @!method magic + # @return (see MachO::Headers::FatHeader#magic) + def_delegators :header, :magic # @return [String] a string representation of the file's magic number def magic_string - MH_MAGICS[magic] - end - - # The file's type. Assumed to be the same for every Mach-O within. - # @return [Symbol] the filetype - def filetype - machos.first.filetype + Headers::MH_MAGICS[magic] end # Populate the instance's fields with the raw Fat Mach-O data. @@ -128,21 +125,13 @@ module MachO end # All load commands responsible for loading dylibs in the file's Mach-O's. - # @return [Array<MachO::DylibCommand>] an array of DylibCommands + # @return [Array<LoadCommands::DylibCommand>] an array of DylibCommands def dylib_load_commands machos.map(&:dylib_load_commands).flatten end - # The file's dylib ID. If the file is not a dylib, returns `nil`. - # @example - # file.dylib_id # => 'libBar.dylib' - # @return [String, nil] the file's dylib ID - # @see MachO::MachOFile#linked_dylibs - def dylib_id - machos.first.dylib_id - end - - # Changes the file's dylib ID to `new_id`. If the file is not a dylib, does nothing. + # Changes the file's dylib ID to `new_id`. If the file is not a dylib, + # does nothing. # @example # file.change_dylib_id('libFoo.dylib') # @param new_id [String] the new dylib ID @@ -151,7 +140,7 @@ module MachO # if false, fail only if all slices fail. # @return [void] # @raise [ArgumentError] if `new_id` is not a String - # @see MachO::MachOFile#linked_dylibs + # @see MachOFile#linked_dylibs def change_dylib_id(new_id, options = {}) raise ArgumentError, "argument must be a String" unless new_id.is_a?(String) return unless machos.all?(&:dylib?) @@ -167,7 +156,7 @@ module MachO # All shared libraries linked to the file's Mach-Os. # @return [Array<String>] an array of all shared libraries - # @see MachO::MachOFile#linked_dylibs + # @see MachOFile#linked_dylibs def linked_dylibs # Individual architectures in a fat binary can link to different subsets # of libraries, but at this point we want to have the full picture, i.e. @@ -175,8 +164,9 @@ module MachO machos.map(&:linked_dylibs).flatten.uniq end - # Changes all dependent shared library install names from `old_name` to `new_name`. - # In a fat file, this changes install names in all internal Mach-Os. + # Changes all dependent shared library install names from `old_name` to + # `new_name`. In a fat file, this changes install names in all internal + # Mach-Os. # @example # file.change_install_name('/usr/lib/libFoo.dylib', '/usr/lib/libBar.dylib') # @param old_name [String] the shared library name being changed @@ -185,7 +175,7 @@ module MachO # @option options [Boolean] :strict (true) if true, fail if one slice fails. # if false, fail only if all slices fail. # @return [void] - # @see MachO::MachOFile#change_install_name + # @see MachOFile#change_install_name def change_install_name(old_name, new_name, options = {}) each_macho(options) do |macho| macho.change_install_name(old_name, new_name, options) @@ -198,7 +188,7 @@ module MachO # All runtime paths associated with the file's Mach-Os. # @return [Array<String>] an array of all runtime paths - # @see MachO::MachOFile#rpaths + # @see MachOFile#rpaths def rpaths # Can individual architectures have different runtime paths? machos.map(&:rpaths).flatten.uniq @@ -211,7 +201,7 @@ module MachO # @option options [Boolean] :strict (true) if true, fail if one slice fails. # if false, fail only if all slices fail. # @return [void] - # @see MachO::MachOFile#change_rpath + # @see MachOFile#change_rpath def change_rpath(old_path, new_path, options = {}) each_macho(options) do |macho| macho.change_rpath(old_path, new_path, options) @@ -226,7 +216,7 @@ module MachO # @option options [Boolean] :strict (true) if true, fail if one slice fails. # if false, fail only if all slices fail. # @return [void] - # @see MachO::MachOFile#add_rpath + # @see MachOFile#add_rpath def add_rpath(path, options = {}) each_macho(options) do |macho| macho.add_rpath(path, options) @@ -241,7 +231,7 @@ module MachO # @option options [Boolean] :strict (true) if true, fail if one slice fails. # if false, fail only if all slices fail. # @return void - # @see MachO::MachOFile#delete_rpath + # @see MachOFile#delete_rpath def delete_rpath(path, options = {}) each_macho(options) do |macho| macho.delete_rpath(path, options) @@ -254,20 +244,21 @@ module MachO # @example # file.extract(:i386) # => MachO::MachOFile # @param cputype [Symbol] the CPU type of the Mach-O being extracted - # @return [MachO::MachOFile, nil] the extracted Mach-O or nil if no Mach-O has the given CPU type + # @return [MachOFile, nil] the extracted Mach-O or nil if no Mach-O has the given CPU type def extract(cputype) machos.select { |macho| macho.cputype == cputype }.first end # Write all (fat) data to the given filename. # @param filename [String] the file to write to + # @return [void] def write(filename) File.open(filename, "wb") { |f| f.write(@raw_data) } end # Write all (fat) data to the file used to initialize the instance. # @return [void] - # @raise [MachO::MachOError] if the instance was initialized without a file + # @raise [MachOError] if the instance was initialized without a file # @note Overwrites all data in the file! def write! if filename.nil? @@ -280,17 +271,18 @@ module MachO private # Obtain the fat header from raw file data. - # @return [MachO::FatHeader] the fat header - # @raise [MachO::TruncatedFileError] if the file is too small to have a valid header - # @raise [MachO::MagicError] if the magic is not valid Mach-O magic - # @raise [MachO::MachOBinaryError] if the magic is for a non-fat Mach-O file - # @raise [MachO::JavaClassFileError] if the file is a Java classfile + # @return [Headers::FatHeader] the fat header + # @raise [TruncatedFileError] if the file is too small to have a + # valid header + # @raise [MagicError] if the magic is not valid Mach-O magic + # @raise [MachOBinaryError] if the magic is for a non-fat Mach-O file + # @raise [JavaClassFileError] if the file is a Java classfile # @api private def populate_fat_header # the smallest fat Mach-O header is 8 bytes raise TruncatedFileError if @raw_data.size < 8 - fh = FatHeader.new_from_bin(:big, @raw_data[0, FatHeader.bytesize]) + fh = Headers::FatHeader.new_from_bin(:big, @raw_data[0, Headers::FatHeader.bytesize]) raise MagicError, fh.magic unless Utils.magic?(fh.magic) raise MachOBinaryError unless Utils.fat_magic?(fh.magic) @@ -308,22 +300,22 @@ module MachO end # Obtain an array of fat architectures from raw file data. - # @return [Array<MachO::FatArch>] an array of fat architectures + # @return [Array<Headers::FatArch>] an array of fat architectures # @api private def populate_fat_archs archs = [] - fa_off = FatHeader.bytesize - fa_len = FatArch.bytesize + fa_off = Headers::FatHeader.bytesize + fa_len = Headers::FatArch.bytesize header.nfat_arch.times do |i| - archs << FatArch.new_from_bin(:big, @raw_data[fa_off + (fa_len * i), fa_len]) + archs << Headers::FatArch.new_from_bin(:big, @raw_data[fa_off + (fa_len * i), fa_len]) end archs end # Obtain an array of Mach-O blobs from raw file data. - # @return [Array<MachO::MachOFile>] an array of Mach-Os + # @return [Array<MachOFile>] an array of Mach-Os # @api private def populate_machos machos = [] @@ -351,7 +343,7 @@ module MachO # @option options [Boolean] :strict (true) whether or not to fail loudly # with an exception if at least one Mach-O raises an exception. If false, # only raises an exception if *all* Mach-Os raise exceptions. - # @raise [MachO::RecoverableModificationError] under the conditions of + # @raise [RecoverableModificationError] under the conditions of # the `:strict` option above. # @api private def each_macho(options = {}) @@ -373,5 +365,13 @@ module MachO # Non-strict mode: Raise first error if *all* Mach-O slices failed. raise errors.first if errors.size == machos.size end + + # Return a single-arch Mach-O that represents this fat Mach-O for purposes + # of delegation. + # @return [MachOFile] the Mach-O file + # @api private + def canonical_macho + machos.first + end end end diff --git a/Library/Homebrew/vendor/macho/macho/headers.rb b/Library/Homebrew/vendor/macho/macho/headers.rb index 7272503af..08a4b80c4 100644 --- a/Library/Homebrew/vendor/macho/macho/headers.rb +++ b/Library/Homebrew/vendor/macho/macho/headers.rb @@ -1,587 +1,665 @@ module MachO - # big-endian fat magic - # @api private - FAT_MAGIC = 0xcafebabe - - # little-endian fat magic - # this is defined, but should never appear in ruby-macho code because - # fat headers are always big-endian and therefore always unpacked as such. - # @api private - FAT_CIGAM = 0xbebafeca - - # 32-bit big-endian magic - # @api private - MH_MAGIC = 0xfeedface - - # 32-bit little-endian magic - # @api private - MH_CIGAM = 0xcefaedfe - - # 64-bit big-endian magic - # @api private - MH_MAGIC_64 = 0xfeedfacf - - # 64-bit little-endian magic - # @api private - MH_CIGAM_64 = 0xcffaedfe - - # association of magic numbers to string representations - # @api private - MH_MAGICS = { - FAT_MAGIC => "FAT_MAGIC", - MH_MAGIC => "MH_MAGIC", - MH_CIGAM => "MH_CIGAM", - MH_MAGIC_64 => "MH_MAGIC_64", - MH_CIGAM_64 => "MH_CIGAM_64", - }.freeze - - # mask for CPUs with 64-bit architectures (when running a 64-bit ABI?) - # @api private - CPU_ARCH_ABI64 = 0x01000000 - - # any CPU (unused?) - # @api private - CPU_TYPE_ANY = -1 - - # m68k compatible CPUs - # @api private - CPU_TYPE_MC680X0 = 0x06 - - # i386 and later compatible CPUs - # @api private - CPU_TYPE_I386 = 0x07 - - # x86_64 (AMD64) compatible CPUs - # @api private - CPU_TYPE_X86_64 = (CPU_TYPE_I386 | CPU_ARCH_ABI64) - - # 32-bit ARM compatible CPUs - # @api private - CPU_TYPE_ARM = 0x0c - - # m88k compatible CPUs - # @api private - CPU_TYPE_MC88000 = 0xd - - # 64-bit ARM compatible CPUs - # @api private - CPU_TYPE_ARM64 = (CPU_TYPE_ARM | CPU_ARCH_ABI64) - - # PowerPC compatible CPUs - # @api private - CPU_TYPE_POWERPC = 0x12 - - # PowerPC64 compatible CPUs - # @api private - CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | CPU_ARCH_ABI64) - - # association of cpu types to symbol representations - # @api private - CPU_TYPES = { - CPU_TYPE_ANY => :any, - CPU_TYPE_I386 => :i386, - CPU_TYPE_X86_64 => :x86_64, - CPU_TYPE_ARM => :arm, - CPU_TYPE_ARM64 => :arm64, - CPU_TYPE_POWERPC => :ppc, - CPU_TYPE_POWERPC64 => :ppc64, - }.freeze - - # mask for CPU subtype capabilities - # @api private - CPU_SUBTYPE_MASK = 0xff000000 - - # 64-bit libraries (undocumented!) - # @see http://llvm.org/docs/doxygen/html/Support_2MachO_8h_source.html - # @api private - CPU_SUBTYPE_LIB64 = 0x80000000 - - # the lowest common sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_I386 = 3 - - # the i486 sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_486 = 4 - - # the i486SX sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_486SX = 132 - - # the i586 (P5, Pentium) sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_586 = 5 - - # @see CPU_SUBTYPE_586 - # @api private - CPU_SUBTYPE_PENT = CPU_SUBTYPE_586 - - # the Pentium Pro (P6) sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_PENTPRO = 22 - - # the Pentium II (P6, M3?) sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_PENTII_M3 = 54 - - # the Pentium II (P6, M5?) sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_PENTII_M5 = 86 - - # the Pentium 4 (Netburst) sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_PENTIUM_4 = 10 - - # the lowest common sub-type for `CPU_TYPE_MC680X0` - # @api private - CPU_SUBTYPE_MC680X0_ALL = 1 - - # @see CPU_SUBTYPE_MC680X0_ALL - # @api private - CPU_SUBTYPE_MC68030 = CPU_SUBTYPE_MC680X0_ALL - - # the 040 subtype for `CPU_TYPE_MC680X0` - # @api private - CPU_SUBTYPE_MC68040 = 2 - - # the 030 subtype for `CPU_TYPE_MC680X0` - # @api private - CPU_SUBTYPE_MC68030_ONLY = 3 - - # the lowest common sub-type for `CPU_TYPE_X86_64` - # @api private - CPU_SUBTYPE_X86_64_ALL = CPU_SUBTYPE_I386 - - # the Haskell sub-type for `CPU_TYPE_X86_64` - # @api private - CPU_SUBTYPE_X86_64_H = 8 - - # the lowest common sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_ALL = 0 - - # the v4t sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V4T = 5 - - # the v6 sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V6 = 6 - - # the v5 sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V5TEJ = 7 - - # the xscale (v5 family) sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_XSCALE = 8 - - # the v7 sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7 = 9 - - # the v7f (Cortex A9) sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7F = 10 + # Classes and constants for parsing the headers of Mach-O binaries. + module Headers + # big-endian fat magic + # @api private + FAT_MAGIC = 0xcafebabe - # the v7s ("Swift") sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7S = 11 + # little-endian fat magic + # this is defined, but should never appear in ruby-macho code because + # fat headers are always big-endian and therefore always unpacked as such. + # @api private + FAT_CIGAM = 0xbebafeca - # the v7k ("Kirkwood40") sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7K = 12 - - # the v6m sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V6M = 14 - - # the v7m sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7M = 15 - - # the v7em sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7EM = 16 - - # the v8 sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V8 = 13 - - # the lowest common sub-type for `CPU_TYPE_ARM64` - # @api private - CPU_SUBTYPE_ARM64_ALL = 0 - - # the v8 sub-type for `CPU_TYPE_ARM64` - # @api private - CPU_SUBTYPE_ARM64_V8 = 1 - - # the lowest common sub-type for `CPU_TYPE_MC88000` - # @api private - CPU_SUBTYPE_MC88000_ALL = 0 - - # @see CPU_SUBTYPE_MC88000_ALL - # @api private - CPU_SUBTYPE_MMAX_JPC = CPU_SUBTYPE_MC88000_ALL - - # the 100 sub-type for `CPU_TYPE_MC88000` - # @api private - CPU_SUBTYPE_MC88100 = 1 - - # the 110 sub-type for `CPU_TYPE_MC88000` - # @api private - CPU_SUBTYPE_MC88110 = 2 - - # the lowest common sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_ALL = 0 - - # the 601 sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_601 = 1 - - # the 602 sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_602 = 2 - - # the 603 sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_603 = 3 - - # the 603e (G2) sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_603E = 4 - - # the 603ev sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_603EV = 5 - - # the 604 sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_604 = 6 - - # the 604e sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_604E = 7 - - # the 620 sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_620 = 8 - - # the 750 (G3) sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_750 = 9 - - # the 7400 (G4) sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_7400 = 10 - - # the 7450 (G4 "Voyager") sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_7450 = 11 - - # the 970 (G5) sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_970 = 100 - - # any CPU sub-type for CPU type `CPU_TYPE_POWERPC64` - # @api private - CPU_SUBTYPE_POWERPC64_ALL = CPU_SUBTYPE_POWERPC_ALL - - # association of CPU types/subtype pairs to symbol representations in - # (very) roughly descending order of commonness - # @see https://opensource.apple.com/source/cctools/cctools-877.8/libstuff/arch.c - # @api private - CPU_SUBTYPES = { - CPU_TYPE_I386 => { - CPU_SUBTYPE_I386 => :i386, - CPU_SUBTYPE_486 => :i486, - CPU_SUBTYPE_486SX => :i486SX, - CPU_SUBTYPE_586 => :i586, # also "pentium" in arch(3) - CPU_SUBTYPE_PENTPRO => :i686, # also "pentpro" in arch(3) - CPU_SUBTYPE_PENTII_M3 => :pentIIm3, - CPU_SUBTYPE_PENTII_M5 => :pentIIm5, - CPU_SUBTYPE_PENTIUM_4 => :pentium4, - }.freeze, - CPU_TYPE_X86_64 => { - CPU_SUBTYPE_X86_64_ALL => :x86_64, - CPU_SUBTYPE_X86_64_H => :x86_64h, - }.freeze, - CPU_TYPE_ARM => { - CPU_SUBTYPE_ARM_ALL => :arm, - CPU_SUBTYPE_ARM_V4T => :armv4t, - CPU_SUBTYPE_ARM_V6 => :armv6, - CPU_SUBTYPE_ARM_V5TEJ => :armv5, - CPU_SUBTYPE_ARM_XSCALE => :xscale, - CPU_SUBTYPE_ARM_V7 => :armv7, - CPU_SUBTYPE_ARM_V7F => :armv7f, - CPU_SUBTYPE_ARM_V7S => :armv7s, - CPU_SUBTYPE_ARM_V7K => :armv7k, - CPU_SUBTYPE_ARM_V6M => :armv6m, - CPU_SUBTYPE_ARM_V7M => :armv7m, - CPU_SUBTYPE_ARM_V7EM => :armv7em, - CPU_SUBTYPE_ARM_V8 => :armv8, - }.freeze, - CPU_TYPE_ARM64 => { - CPU_SUBTYPE_ARM64_ALL => :arm64, - CPU_SUBTYPE_ARM64_V8 => :arm64v8, - }.freeze, - CPU_TYPE_POWERPC => { - CPU_SUBTYPE_POWERPC_ALL => :ppc, - CPU_SUBTYPE_POWERPC_601 => :ppc601, - CPU_SUBTYPE_POWERPC_603 => :ppc603, - CPU_SUBTYPE_POWERPC_603E => :ppc603e, - CPU_SUBTYPE_POWERPC_603EV => :ppc603ev, - CPU_SUBTYPE_POWERPC_604 => :ppc604, - CPU_SUBTYPE_POWERPC_604E => :ppc604e, - CPU_SUBTYPE_POWERPC_750 => :ppc750, - CPU_SUBTYPE_POWERPC_7400 => :ppc7400, - CPU_SUBTYPE_POWERPC_7450 => :ppc7450, - CPU_SUBTYPE_POWERPC_970 => :ppc970, - }.freeze, - CPU_TYPE_POWERPC64 => { - CPU_SUBTYPE_POWERPC64_ALL => :ppc64, - # apparently the only exception to the naming scheme - CPU_SUBTYPE_POWERPC_970 => :ppc970_64, - }.freeze, - CPU_TYPE_MC680X0 => { - CPU_SUBTYPE_MC680X0_ALL => :m68k, - CPU_SUBTYPE_MC68030 => :mc68030, - CPU_SUBTYPE_MC68040 => :mc68040, - }, - CPU_TYPE_MC88000 => { - CPU_SUBTYPE_MC88000_ALL => :m88k, - }, - }.freeze - - # relocatable object file - # @api private - MH_OBJECT = 0x1 - - # demand paged executable file - # @api private - MH_EXECUTE = 0x2 - - # fixed VM shared library file - # @api private - MH_FVMLIB = 0x3 - - # core dump file - # @api private - MH_CORE = 0x4 - - # preloaded executable file - # @api private - MH_PRELOAD = 0x5 - - # dynamically bound shared library - # @api private - MH_DYLIB = 0x6 - - # dynamic link editor - # @api private - MH_DYLINKER = 0x7 - - # dynamically bound bundle file - # @api private - MH_BUNDLE = 0x8 - - # shared library stub for static linking only, no section contents - # @api private - MH_DYLIB_STUB = 0x9 - - # companion file with only debug sections - # @api private - MH_DSYM = 0xa - - # x86_64 kexts - # @api private - MH_KEXT_BUNDLE = 0xb - - # association of filetypes to Symbol representations - # @api private - MH_FILETYPES = { - MH_OBJECT => :object, - MH_EXECUTE => :execute, - MH_FVMLIB => :fvmlib, - MH_CORE => :core, - MH_PRELOAD => :preload, - MH_DYLIB => :dylib, - MH_DYLINKER => :dylinker, - MH_BUNDLE => :bundle, - MH_DYLIB_STUB => :dylib_stub, - MH_DSYM => :dsym, - MH_KEXT_BUNDLE => :kext_bundle, - }.freeze - - # association of mach header flag symbols to values - # @api private - MH_FLAGS = { - :MH_NOUNDEFS => 0x1, - :MH_INCRLINK => 0x2, - :MH_DYLDLINK => 0x4, - :MH_BINDATLOAD => 0x8, - :MH_PREBOUND => 0x10, - :MH_SPLIT_SEGS => 0x20, - :MH_LAZY_INIT => 0x40, - :MH_TWOLEVEL => 0x80, - :MH_FORCE_FLAT => 0x100, - :MH_NOMULTIDEFS => 0x200, - :MH_NOPREFIXBINDING => 0x400, - :MH_PREBINDABLE => 0x800, - :MH_ALLMODSBOUND => 0x1000, - :MH_SUBSECTIONS_VIA_SYMBOLS => 0x2000, - :MH_CANONICAL => 0x4000, - :MH_WEAK_DEFINES => 0x8000, - :MH_BINDS_TO_WEAK => 0x10000, - :MH_ALLOW_STACK_EXECUTION => 0x20000, - :MH_ROOT_SAFE => 0x40000, - :MH_SETUID_SAFE => 0x80000, - :MH_NO_REEXPORTED_DYLIBS => 0x100000, - :MH_PIE => 0x200000, - :MH_DEAD_STRIPPABLE_DYLIB => 0x400000, - :MH_HAS_TLV_DESCRIPTORS => 0x800000, - :MH_NO_HEAP_EXECUTION => 0x1000000, - :MH_APP_EXTENSION_SAFE => 0x02000000, - }.freeze - - # Fat binary header structure - # @see MachO::FatArch - class FatHeader < MachOStructure - # @return [Fixnum] the magic number of the header (and file) - attr_reader :magic - - # @return [Fixnum] the number of fat architecture structures following the header - attr_reader :nfat_arch - - # always big-endian - # @see MachOStructure::FORMAT - # @api private - FORMAT = "N2".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 8 - - # @api private - def initialize(magic, nfat_arch) - @magic = magic - @nfat_arch = nfat_arch - end - end + # 32-bit big-endian magic + # @api private + MH_MAGIC = 0xfeedface - # Fat binary header architecture structure. A Fat binary has one or more of - # these, representing one or more internal Mach-O blobs. - # @see MachO::FatHeader - class FatArch < MachOStructure - # @return [Fixnum] the CPU type of the Mach-O - attr_reader :cputype + # 32-bit little-endian magic + # @api private + MH_CIGAM = 0xcefaedfe - # @return [Fixnum] the CPU subtype of the Mach-O - attr_reader :cpusubtype + # 64-bit big-endian magic + # @api private + MH_MAGIC_64 = 0xfeedfacf - # @return [Fixnum] the file offset to the beginning of the Mach-O data - attr_reader :offset + # 64-bit little-endian magic + # @api private + MH_CIGAM_64 = 0xcffaedfe - # @return [Fixnum] the size, in bytes, of the Mach-O data - attr_reader :size + # association of magic numbers to string representations + # @api private + MH_MAGICS = { + FAT_MAGIC => "FAT_MAGIC", + MH_MAGIC => "MH_MAGIC", + MH_CIGAM => "MH_CIGAM", + MH_MAGIC_64 => "MH_MAGIC_64", + MH_CIGAM_64 => "MH_CIGAM_64", + }.freeze + + # mask for CPUs with 64-bit architectures (when running a 64-bit ABI?) + # @api private + CPU_ARCH_ABI64 = 0x01000000 - # @return [Fixnum] the alignment, as a power of 2 - attr_reader :align + # any CPU (unused?) + # @api private + CPU_TYPE_ANY = -1 - # always big-endian - # @see MachOStructure::FORMAT + # m68k compatible CPUs # @api private - FORMAT = "N5".freeze + CPU_TYPE_MC680X0 = 0x06 - # @see MachOStructure::SIZEOF + # i386 and later compatible CPUs # @api private - SIZEOF = 20 + CPU_TYPE_I386 = 0x07 + # x86_64 (AMD64) compatible CPUs # @api private - def initialize(cputype, cpusubtype, offset, size, align) - @cputype = cputype - @cpusubtype = cpusubtype - @offset = offset - @size = size - @align = align - end - end + CPU_TYPE_X86_64 = (CPU_TYPE_I386 | CPU_ARCH_ABI64) - # 32-bit Mach-O file header structure - class MachHeader < MachOStructure - # @return [Fixnum] the magic number - attr_reader :magic + # 32-bit ARM compatible CPUs + # @api private + CPU_TYPE_ARM = 0x0c - # @return [Fixnum] the CPU type of the Mach-O - attr_reader :cputype + # m88k compatible CPUs + # @api private + CPU_TYPE_MC88000 = 0xd - # @return [Fixnum] the CPU subtype of the Mach-O - attr_reader :cpusubtype + # 64-bit ARM compatible CPUs + # @api private + CPU_TYPE_ARM64 = (CPU_TYPE_ARM | CPU_ARCH_ABI64) - # @return [Fixnum] the file type of the Mach-O - attr_reader :filetype + # PowerPC compatible CPUs + # @api private + CPU_TYPE_POWERPC = 0x12 - # @return [Fixnum] the number of load commands in the Mach-O - attr_reader :ncmds + # PowerPC64 compatible CPUs + # @api private + CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | CPU_ARCH_ABI64) - # @return [Fixnum] the size of all load commands, in bytes, in the Mach-O - attr_reader :sizeofcmds + # association of cpu types to symbol representations + # @api private + CPU_TYPES = { + CPU_TYPE_ANY => :any, + CPU_TYPE_I386 => :i386, + CPU_TYPE_X86_64 => :x86_64, + CPU_TYPE_ARM => :arm, + CPU_TYPE_ARM64 => :arm64, + CPU_TYPE_POWERPC => :ppc, + CPU_TYPE_POWERPC64 => :ppc64, + }.freeze + + # mask for CPU subtype capabilities + # @api private + CPU_SUBTYPE_MASK = 0xff000000 - # @return [Fixnum] the header flags associated with the Mach-O - attr_reader :flags + # 64-bit libraries (undocumented!) + # @see http://llvm.org/docs/doxygen/html/Support_2MachO_8h_source.html + # @api private + CPU_SUBTYPE_LIB64 = 0x80000000 - # @see MachOStructure::FORMAT + # the lowest common sub-type for `CPU_TYPE_I386` # @api private - FORMAT = "L=7".freeze + CPU_SUBTYPE_I386 = 3 - # @see MachOStructure::SIZEOF + # the i486 sub-type for `CPU_TYPE_I386` # @api private - SIZEOF = 28 + CPU_SUBTYPE_486 = 4 + # the i486SX sub-type for `CPU_TYPE_I386` # @api private - def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, - flags) - @magic = magic - @cputype = cputype - # For now we're not interested in additional capability bits also to be - # found in the `cpusubtype` field. We only care about the CPU sub-type. - @cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK - @filetype = filetype - @ncmds = ncmds - @sizeofcmds = sizeofcmds - @flags = flags - end + CPU_SUBTYPE_486SX = 132 - # @example - # puts "this mach-o has position-independent execution" if header.flag?(:MH_PIE) - # @param flag [Symbol] a mach header flag symbol - # @return [Boolean] true if `flag` is present in the header's flag section - def flag?(flag) - flag = MH_FLAGS[flag] - return false if flag.nil? - flags & flag == flag - end - end + # the i586 (P5, Pentium) sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_586 = 5 + + # @see CPU_SUBTYPE_586 + # @api private + CPU_SUBTYPE_PENT = CPU_SUBTYPE_586 + + # the Pentium Pro (P6) sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_PENTPRO = 22 + + # the Pentium II (P6, M3?) sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_PENTII_M3 = 54 + + # the Pentium II (P6, M5?) sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_PENTII_M5 = 86 + + # the Pentium 4 (Netburst) sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_PENTIUM_4 = 10 + + # the lowest common sub-type for `CPU_TYPE_MC680X0` + # @api private + CPU_SUBTYPE_MC680X0_ALL = 1 + + # @see CPU_SUBTYPE_MC680X0_ALL + # @api private + CPU_SUBTYPE_MC68030 = CPU_SUBTYPE_MC680X0_ALL + + # the 040 subtype for `CPU_TYPE_MC680X0` + # @api private + CPU_SUBTYPE_MC68040 = 2 + + # the 030 subtype for `CPU_TYPE_MC680X0` + # @api private + CPU_SUBTYPE_MC68030_ONLY = 3 + + # the lowest common sub-type for `CPU_TYPE_X86_64` + # @api private + CPU_SUBTYPE_X86_64_ALL = CPU_SUBTYPE_I386 + + # the Haskell sub-type for `CPU_TYPE_X86_64` + # @api private + CPU_SUBTYPE_X86_64_H = 8 + + # the lowest common sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_ALL = 0 + + # the v4t sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V4T = 5 + + # the v6 sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V6 = 6 + + # the v5 sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V5TEJ = 7 + + # the xscale (v5 family) sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_XSCALE = 8 + + # the v7 sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7 = 9 + + # the v7f (Cortex A9) sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7F = 10 + + # the v7s ("Swift") sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7S = 11 + + # the v7k ("Kirkwood40") sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7K = 12 + + # the v6m sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V6M = 14 + + # the v7m sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7M = 15 - # 64-bit Mach-O file header structure - class MachHeader64 < MachHeader - # @return [void] - attr_reader :reserved + # the v7em sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7EM = 16 + + # the v8 sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V8 = 13 + + # the lowest common sub-type for `CPU_TYPE_ARM64` + # @api private + CPU_SUBTYPE_ARM64_ALL = 0 + + # the v8 sub-type for `CPU_TYPE_ARM64` + # @api private + CPU_SUBTYPE_ARM64_V8 = 1 + + # the lowest common sub-type for `CPU_TYPE_MC88000` + # @api private + CPU_SUBTYPE_MC88000_ALL = 0 + + # @see CPU_SUBTYPE_MC88000_ALL + # @api private + CPU_SUBTYPE_MMAX_JPC = CPU_SUBTYPE_MC88000_ALL + + # the 100 sub-type for `CPU_TYPE_MC88000` + # @api private + CPU_SUBTYPE_MC88100 = 1 + + # the 110 sub-type for `CPU_TYPE_MC88000` + # @api private + CPU_SUBTYPE_MC88110 = 2 + + # the lowest common sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_ALL = 0 + + # the 601 sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_601 = 1 + + # the 602 sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_602 = 2 + + # the 603 sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_603 = 3 + + # the 603e (G2) sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_603E = 4 + + # the 603ev sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_603EV = 5 + + # the 604 sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_604 = 6 + + # the 604e sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_604E = 7 + + # the 620 sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_620 = 8 + + # the 750 (G3) sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_750 = 9 - # @see MachOStructure::FORMAT + # the 7400 (G4) sub-type for `CPU_TYPE_POWERPC` # @api private - FORMAT = "L=8".freeze + CPU_SUBTYPE_POWERPC_7400 = 10 - # @see MachOStructure::SIZEOF + # the 7450 (G4 "Voyager") sub-type for `CPU_TYPE_POWERPC` # @api private - SIZEOF = 32 + CPU_SUBTYPE_POWERPC_7450 = 11 + # the 970 (G5) sub-type for `CPU_TYPE_POWERPC` # @api private - def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, - flags, reserved) - super(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags) - @reserved = reserved + CPU_SUBTYPE_POWERPC_970 = 100 + + # any CPU sub-type for CPU type `CPU_TYPE_POWERPC64` + # @api private + CPU_SUBTYPE_POWERPC64_ALL = CPU_SUBTYPE_POWERPC_ALL + + # association of CPU types/subtype pairs to symbol representations in + # (very) roughly descending order of commonness + # @see https://opensource.apple.com/source/cctools/cctools-877.8/libstuff/arch.c + # @api private + CPU_SUBTYPES = { + CPU_TYPE_I386 => { + CPU_SUBTYPE_I386 => :i386, + CPU_SUBTYPE_486 => :i486, + CPU_SUBTYPE_486SX => :i486SX, + CPU_SUBTYPE_586 => :i586, # also "pentium" in arch(3) + CPU_SUBTYPE_PENTPRO => :i686, # also "pentpro" in arch(3) + CPU_SUBTYPE_PENTII_M3 => :pentIIm3, + CPU_SUBTYPE_PENTII_M5 => :pentIIm5, + CPU_SUBTYPE_PENTIUM_4 => :pentium4, + }.freeze, + CPU_TYPE_X86_64 => { + CPU_SUBTYPE_X86_64_ALL => :x86_64, + CPU_SUBTYPE_X86_64_H => :x86_64h, + }.freeze, + CPU_TYPE_ARM => { + CPU_SUBTYPE_ARM_ALL => :arm, + CPU_SUBTYPE_ARM_V4T => :armv4t, + CPU_SUBTYPE_ARM_V6 => :armv6, + CPU_SUBTYPE_ARM_V5TEJ => :armv5, + CPU_SUBTYPE_ARM_XSCALE => :xscale, + CPU_SUBTYPE_ARM_V7 => :armv7, + CPU_SUBTYPE_ARM_V7F => :armv7f, + CPU_SUBTYPE_ARM_V7S => :armv7s, + CPU_SUBTYPE_ARM_V7K => :armv7k, + CPU_SUBTYPE_ARM_V6M => :armv6m, + CPU_SUBTYPE_ARM_V7M => :armv7m, + CPU_SUBTYPE_ARM_V7EM => :armv7em, + CPU_SUBTYPE_ARM_V8 => :armv8, + }.freeze, + CPU_TYPE_ARM64 => { + CPU_SUBTYPE_ARM64_ALL => :arm64, + CPU_SUBTYPE_ARM64_V8 => :arm64v8, + }.freeze, + CPU_TYPE_POWERPC => { + CPU_SUBTYPE_POWERPC_ALL => :ppc, + CPU_SUBTYPE_POWERPC_601 => :ppc601, + CPU_SUBTYPE_POWERPC_603 => :ppc603, + CPU_SUBTYPE_POWERPC_603E => :ppc603e, + CPU_SUBTYPE_POWERPC_603EV => :ppc603ev, + CPU_SUBTYPE_POWERPC_604 => :ppc604, + CPU_SUBTYPE_POWERPC_604E => :ppc604e, + CPU_SUBTYPE_POWERPC_750 => :ppc750, + CPU_SUBTYPE_POWERPC_7400 => :ppc7400, + CPU_SUBTYPE_POWERPC_7450 => :ppc7450, + CPU_SUBTYPE_POWERPC_970 => :ppc970, + }.freeze, + CPU_TYPE_POWERPC64 => { + CPU_SUBTYPE_POWERPC64_ALL => :ppc64, + # apparently the only exception to the naming scheme + CPU_SUBTYPE_POWERPC_970 => :ppc970_64, + }.freeze, + CPU_TYPE_MC680X0 => { + CPU_SUBTYPE_MC680X0_ALL => :m68k, + CPU_SUBTYPE_MC68030 => :mc68030, + CPU_SUBTYPE_MC68040 => :mc68040, + }, + CPU_TYPE_MC88000 => { + CPU_SUBTYPE_MC88000_ALL => :m88k, + }, + }.freeze + + # relocatable object file + # @api private + MH_OBJECT = 0x1 + + # demand paged executable file + # @api private + MH_EXECUTE = 0x2 + + # fixed VM shared library file + # @api private + MH_FVMLIB = 0x3 + + # core dump file + # @api private + MH_CORE = 0x4 + + # preloaded executable file + # @api private + MH_PRELOAD = 0x5 + + # dynamically bound shared library + # @api private + MH_DYLIB = 0x6 + + # dynamic link editor + # @api private + MH_DYLINKER = 0x7 + + # dynamically bound bundle file + # @api private + MH_BUNDLE = 0x8 + + # shared library stub for static linking only, no section contents + # @api private + MH_DYLIB_STUB = 0x9 + + # companion file with only debug sections + # @api private + MH_DSYM = 0xa + + # x86_64 kexts + # @api private + MH_KEXT_BUNDLE = 0xb + + # association of filetypes to Symbol representations + # @api private + MH_FILETYPES = { + MH_OBJECT => :object, + MH_EXECUTE => :execute, + MH_FVMLIB => :fvmlib, + MH_CORE => :core, + MH_PRELOAD => :preload, + MH_DYLIB => :dylib, + MH_DYLINKER => :dylinker, + MH_BUNDLE => :bundle, + MH_DYLIB_STUB => :dylib_stub, + MH_DSYM => :dsym, + MH_KEXT_BUNDLE => :kext_bundle, + }.freeze + + # association of mach header flag symbols to values + # @api private + MH_FLAGS = { + :MH_NOUNDEFS => 0x1, + :MH_INCRLINK => 0x2, + :MH_DYLDLINK => 0x4, + :MH_BINDATLOAD => 0x8, + :MH_PREBOUND => 0x10, + :MH_SPLIT_SEGS => 0x20, + :MH_LAZY_INIT => 0x40, + :MH_TWOLEVEL => 0x80, + :MH_FORCE_FLAT => 0x100, + :MH_NOMULTIDEFS => 0x200, + :MH_NOPREFIXBINDING => 0x400, + :MH_PREBINDABLE => 0x800, + :MH_ALLMODSBOUND => 0x1000, + :MH_SUBSECTIONS_VIA_SYMBOLS => 0x2000, + :MH_CANONICAL => 0x4000, + :MH_WEAK_DEFINES => 0x8000, + :MH_BINDS_TO_WEAK => 0x10000, + :MH_ALLOW_STACK_EXECUTION => 0x20000, + :MH_ROOT_SAFE => 0x40000, + :MH_SETUID_SAFE => 0x80000, + :MH_NO_REEXPORTED_DYLIBS => 0x100000, + :MH_PIE => 0x200000, + :MH_DEAD_STRIPPABLE_DYLIB => 0x400000, + :MH_HAS_TLV_DESCRIPTORS => 0x800000, + :MH_NO_HEAP_EXECUTION => 0x1000000, + :MH_APP_EXTENSION_SAFE => 0x02000000, + }.freeze + + # Fat binary header structure + # @see MachO::FatArch + class FatHeader < MachOStructure + # @return [Fixnum] the magic number of the header (and file) + attr_reader :magic + + # @return [Fixnum] the number of fat architecture structures following the header + attr_reader :nfat_arch + + # always big-endian + # @see MachOStructure::FORMAT + # @api private + FORMAT = "N2".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 8 + + # @api private + def initialize(magic, nfat_arch) + @magic = magic + @nfat_arch = nfat_arch + end + + # @return [String] the serialized fields of the fat header + def serialize + [magic, nfat_arch].pack(FORMAT) + end + end + + # Fat binary header architecture structure. A Fat binary has one or more of + # these, representing one or more internal Mach-O blobs. + # @see MachO::Headers::FatHeader + class FatArch < MachOStructure + # @return [Fixnum] the CPU type of the Mach-O + attr_reader :cputype + + # @return [Fixnum] the CPU subtype of the Mach-O + attr_reader :cpusubtype + + # @return [Fixnum] the file offset to the beginning of the Mach-O data + attr_reader :offset + + # @return [Fixnum] the size, in bytes, of the Mach-O data + attr_reader :size + + # @return [Fixnum] the alignment, as a power of 2 + attr_reader :align + + # always big-endian + # @see MachOStructure::FORMAT + # @api private + FORMAT = "N5".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 20 + + # @api private + def initialize(cputype, cpusubtype, offset, size, align) + @cputype = cputype + @cpusubtype = cpusubtype + @offset = offset + @size = size + @align = align + end + + # @return [String] the serialized fields of the fat arch + def serialize + [cputype, cpusubtype, offset, size, align].pack(FORMAT) + end + end + + # 32-bit Mach-O file header structure + class MachHeader < MachOStructure + # @return [Fixnum] the magic number + attr_reader :magic + + # @return [Fixnum] the CPU type of the Mach-O + attr_reader :cputype + + # @return [Fixnum] the CPU subtype of the Mach-O + attr_reader :cpusubtype + + # @return [Fixnum] the file type of the Mach-O + attr_reader :filetype + + # @return [Fixnum] the number of load commands in the Mach-O + attr_reader :ncmds + + # @return [Fixnum] the size of all load commands, in bytes, in the Mach-O + attr_reader :sizeofcmds + + # @return [Fixnum] the header flags associated with the Mach-O + attr_reader :flags + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=7".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 28 + + # @api private + def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, + flags) + @magic = magic + @cputype = cputype + # For now we're not interested in additional capability bits also to be + # found in the `cpusubtype` field. We only care about the CPU sub-type. + @cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK + @filetype = filetype + @ncmds = ncmds + @sizeofcmds = sizeofcmds + @flags = flags + end + + # @example + # puts "this mach-o has position-independent execution" if header.flag?(:MH_PIE) + # @param flag [Symbol] a mach header flag symbol + # @return [Boolean] true if `flag` is present in the header's flag section + def flag?(flag) + flag = MH_FLAGS[flag] + return false if flag.nil? + flags & flag == flag + end + + # @return [Boolean] whether or not the file is of type `MH_OBJECT` + def object? + filetype == Headers::MH_OBJECT + end + + # @return [Boolean] whether or not the file is of type `MH_EXECUTE` + def executable? + filetype == Headers::MH_EXECUTE + end + + # @return [Boolean] whether or not the file is of type `MH_FVMLIB` + def fvmlib? + filetype == Headers::MH_FVMLIB + end + + # @return [Boolean] whether or not the file is of type `MH_CORE` + def core? + filetype == Headers::MH_CORE + end + + # @return [Boolean] whether or not the file is of type `MH_PRELOAD` + def preload? + filetype == Headers::MH_PRELOAD + end + + # @return [Boolean] whether or not the file is of type `MH_DYLIB` + def dylib? + filetype == Headers::MH_DYLIB + end + + # @return [Boolean] whether or not the file is of type `MH_DYLINKER` + def dylinker? + filetype == Headers::MH_DYLINKER + end + + # @return [Boolean] whether or not the file is of type `MH_BUNDLE` + def bundle? + filetype == Headers::MH_BUNDLE + end + + # @return [Boolean] whether or not the file is of type `MH_DSYM` + def dsym? + filetype == Headers::MH_DSYM + end + + # @return [Boolean] whether or not the file is of type `MH_KEXT_BUNDLE` + def kext? + filetype == Headers::MH_KEXT_BUNDLE + end + + # @return [Boolean] true if the Mach-O has 32-bit magic, false otherwise + def magic32? + Utils.magic32?(magic) + end + + # @return [Boolean] true if the Mach-O has 64-bit magic, false otherwise + def magic64? + Utils.magic64?(magic) + end + + # @return [Fixnum] the file's internal alignment + def alignment + magic32? ? 4 : 8 + end + end + + # 64-bit Mach-O file header structure + class MachHeader64 < MachHeader + # @return [void] + attr_reader :reserved + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=8".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 32 + + # @api private + def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, + flags, reserved) + super(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags) + @reserved = reserved + end end end end diff --git a/Library/Homebrew/vendor/macho/macho/load_commands.rb b/Library/Homebrew/vendor/macho/macho/load_commands.rb index 205110801..be4319ee2 100644 --- a/Library/Homebrew/vendor/macho/macho/load_commands.rb +++ b/Library/Homebrew/vendor/macho/macho/load_commands.rb @@ -1,1314 +1,1350 @@ module MachO - # load commands added after OS X 10.1 need to be bitwise ORed with - # LC_REQ_DYLD to be recognized by the dynamic linder (dyld) - # @api private - LC_REQ_DYLD = 0x80000000 - - # association of load commands to symbol representations - # @api private - LOAD_COMMANDS = { - 0x1 => :LC_SEGMENT, - 0x2 => :LC_SYMTAB, - 0x3 => :LC_SYMSEG, - 0x4 => :LC_THREAD, - 0x5 => :LC_UNIXTHREAD, - 0x6 => :LC_LOADFVMLIB, - 0x7 => :LC_IDFVMLIB, - 0x8 => :LC_IDENT, - 0x9 => :LC_FVMFILE, - 0xa => :LC_PREPAGE, - 0xb => :LC_DYSYMTAB, - 0xc => :LC_LOAD_DYLIB, - 0xd => :LC_ID_DYLIB, - 0xe => :LC_LOAD_DYLINKER, - 0xf => :LC_ID_DYLINKER, - 0x10 => :LC_PREBOUND_DYLIB, - 0x11 => :LC_ROUTINES, - 0x12 => :LC_SUB_FRAMEWORK, - 0x13 => :LC_SUB_UMBRELLA, - 0x14 => :LC_SUB_CLIENT, - 0x15 => :LC_SUB_LIBRARY, - 0x16 => :LC_TWOLEVEL_HINTS, - 0x17 => :LC_PREBIND_CKSUM, - (0x18 | LC_REQ_DYLD) => :LC_LOAD_WEAK_DYLIB, - 0x19 => :LC_SEGMENT_64, - 0x1a => :LC_ROUTINES_64, - 0x1b => :LC_UUID, - (0x1c | LC_REQ_DYLD) => :LC_RPATH, - 0x1d => :LC_CODE_SIGNATURE, - 0x1e => :LC_SEGMENT_SPLIT_INFO, - (0x1f | LC_REQ_DYLD) => :LC_REEXPORT_DYLIB, - 0x20 => :LC_LAZY_LOAD_DYLIB, - 0x21 => :LC_ENCRYPTION_INFO, - 0x22 => :LC_DYLD_INFO, - (0x22 | LC_REQ_DYLD) => :LC_DYLD_INFO_ONLY, - (0x23 | LC_REQ_DYLD) => :LC_LOAD_UPWARD_DYLIB, - 0x24 => :LC_VERSION_MIN_MACOSX, - 0x25 => :LC_VERSION_MIN_IPHONEOS, - 0x26 => :LC_FUNCTION_STARTS, - 0x27 => :LC_DYLD_ENVIRONMENT, - (0x28 | LC_REQ_DYLD) => :LC_MAIN, - 0x29 => :LC_DATA_IN_CODE, - 0x2a => :LC_SOURCE_VERSION, - 0x2b => :LC_DYLIB_CODE_SIGN_DRS, - 0x2c => :LC_ENCRYPTION_INFO_64, - 0x2d => :LC_LINKER_OPTION, - 0x2e => :LC_LINKER_OPTIMIZATION_HINT, - 0x2f => :LC_VERSION_MIN_TVOS, - 0x30 => :LC_VERSION_MIN_WATCHOS, - }.freeze - - # association of symbol representations to load command constants - # @api private - LOAD_COMMAND_CONSTANTS = LOAD_COMMANDS.invert.freeze - - # load commands responsible for loading dylibs - # @api private - DYLIB_LOAD_COMMANDS = [ - :LC_LOAD_DYLIB, - :LC_LOAD_WEAK_DYLIB, - :LC_REEXPORT_DYLIB, - :LC_LAZY_LOAD_DYLIB, - :LC_LOAD_UPWARD_DYLIB, - ].freeze - - # load commands that can be created manually via {LoadCommand.create} - # @api private - CREATABLE_LOAD_COMMANDS = DYLIB_LOAD_COMMANDS + [ - :LC_ID_DYLIB, - :LC_RPATH, - :LC_LOAD_DYLINKER, - ].freeze - - # association of load command symbols to string representations of classes - # @api private - LC_STRUCTURES = { - :LC_SEGMENT => "SegmentCommand", - :LC_SYMTAB => "SymtabCommand", - :LC_SYMSEG => "SymsegCommand", # obsolete - :LC_THREAD => "ThreadCommand", # seems obsolete, but not documented as such - :LC_UNIXTHREAD => "ThreadCommand", - :LC_LOADFVMLIB => "FvmlibCommand", # obsolete - :LC_IDFVMLIB => "FvmlibCommand", # obsolete - :LC_IDENT => "IdentCommand", # obsolete - :LC_FVMFILE => "FvmfileCommand", # reserved for internal use only - :LC_PREPAGE => "LoadCommand", # reserved for internal use only, no public struct - :LC_DYSYMTAB => "DysymtabCommand", - :LC_LOAD_DYLIB => "DylibCommand", - :LC_ID_DYLIB => "DylibCommand", - :LC_LOAD_DYLINKER => "DylinkerCommand", - :LC_ID_DYLINKER => "DylinkerCommand", - :LC_PREBOUND_DYLIB => "PreboundDylibCommand", - :LC_ROUTINES => "RoutinesCommand", - :LC_SUB_FRAMEWORK => "SubFrameworkCommand", - :LC_SUB_UMBRELLA => "SubUmbrellaCommand", - :LC_SUB_CLIENT => "SubClientCommand", - :LC_SUB_LIBRARY => "SubLibraryCommand", - :LC_TWOLEVEL_HINTS => "TwolevelHintsCommand", - :LC_PREBIND_CKSUM => "PrebindCksumCommand", - :LC_LOAD_WEAK_DYLIB => "DylibCommand", - :LC_SEGMENT_64 => "SegmentCommand64", - :LC_ROUTINES_64 => "RoutinesCommand64", - :LC_UUID => "UUIDCommand", - :LC_RPATH => "RpathCommand", - :LC_CODE_SIGNATURE => "LinkeditDataCommand", - :LC_SEGMENT_SPLIT_INFO => "LinkeditDataCommand", - :LC_REEXPORT_DYLIB => "DylibCommand", - :LC_LAZY_LOAD_DYLIB => "DylibCommand", - :LC_ENCRYPTION_INFO => "EncryptionInfoCommand", - :LC_DYLD_INFO => "DyldInfoCommand", - :LC_DYLD_INFO_ONLY => "DyldInfoCommand", - :LC_LOAD_UPWARD_DYLIB => "DylibCommand", - :LC_VERSION_MIN_MACOSX => "VersionMinCommand", - :LC_VERSION_MIN_IPHONEOS => "VersionMinCommand", - :LC_FUNCTION_STARTS => "LinkeditDataCommand", - :LC_DYLD_ENVIRONMENT => "DylinkerCommand", - :LC_MAIN => "EntryPointCommand", - :LC_DATA_IN_CODE => "LinkeditDataCommand", - :LC_SOURCE_VERSION => "SourceVersionCommand", - :LC_DYLIB_CODE_SIGN_DRS => "LinkeditDataCommand", - :LC_ENCRYPTION_INFO_64 => "EncryptionInfoCommand64", - :LC_LINKER_OPTION => "LinkerOptionCommand", - :LC_LINKER_OPTIMIZATION_HINT => "LinkeditDataCommand", - :LC_VERSION_MIN_TVOS => "VersionMinCommand", - :LC_VERSION_MIN_WATCHOS => "VersionMinCommand", - }.freeze - - # association of segment name symbols to names - # @api private - SEGMENT_NAMES = { - :SEG_PAGEZERO => "__PAGEZERO", - :SEG_TEXT => "__TEXT", - :SEG_DATA => "__DATA", - :SEG_OBJC => "__OBJC", - :SEG_ICON => "__ICON", - :SEG_LINKEDIT => "__LINKEDIT", - :SEG_UNIXSTACK => "__UNIXSTACK", - :SEG_IMPORT => "__IMPORT", - }.freeze - - # association of segment flag symbols to values - # @api private - SEGMENT_FLAGS = { - :SG_HIGHVM => 0x1, - :SG_FVMLIB => 0x2, - :SG_NORELOC => 0x4, - :SG_PROTECTED_VERSION_1 => 0x8, - }.freeze - - # Mach-O load command structure - # This is the most generic load command - only cmd ID and size are - # represented, and no actual data. Used when a more specific class - # isn't available/implemented. - class LoadCommand < MachOStructure - # @return [MachO::MachOView] the raw view associated with the load command - attr_reader :view - - # @return [Fixnum] the load command's identifying number - attr_reader :cmd - - # @return [Fixnum] the size of the load command, in bytes - attr_reader :cmdsize - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2".freeze + # Classes and constants for parsing load commands in Mach-O binaries. + module LoadCommands + # load commands added after OS X 10.1 need to be bitwise ORed with + # LC_REQ_DYLD to be recognized by the dynamic linker (dyld) + # @api private + LC_REQ_DYLD = 0x80000000 + + # association of load commands to symbol representations + # @api private + LOAD_COMMANDS = { + 0x1 => :LC_SEGMENT, + 0x2 => :LC_SYMTAB, + 0x3 => :LC_SYMSEG, + 0x4 => :LC_THREAD, + 0x5 => :LC_UNIXTHREAD, + 0x6 => :LC_LOADFVMLIB, + 0x7 => :LC_IDFVMLIB, + 0x8 => :LC_IDENT, + 0x9 => :LC_FVMFILE, + 0xa => :LC_PREPAGE, + 0xb => :LC_DYSYMTAB, + 0xc => :LC_LOAD_DYLIB, + 0xd => :LC_ID_DYLIB, + 0xe => :LC_LOAD_DYLINKER, + 0xf => :LC_ID_DYLINKER, + 0x10 => :LC_PREBOUND_DYLIB, + 0x11 => :LC_ROUTINES, + 0x12 => :LC_SUB_FRAMEWORK, + 0x13 => :LC_SUB_UMBRELLA, + 0x14 => :LC_SUB_CLIENT, + 0x15 => :LC_SUB_LIBRARY, + 0x16 => :LC_TWOLEVEL_HINTS, + 0x17 => :LC_PREBIND_CKSUM, + (0x18 | LC_REQ_DYLD) => :LC_LOAD_WEAK_DYLIB, + 0x19 => :LC_SEGMENT_64, + 0x1a => :LC_ROUTINES_64, + 0x1b => :LC_UUID, + (0x1c | LC_REQ_DYLD) => :LC_RPATH, + 0x1d => :LC_CODE_SIGNATURE, + 0x1e => :LC_SEGMENT_SPLIT_INFO, + (0x1f | LC_REQ_DYLD) => :LC_REEXPORT_DYLIB, + 0x20 => :LC_LAZY_LOAD_DYLIB, + 0x21 => :LC_ENCRYPTION_INFO, + 0x22 => :LC_DYLD_INFO, + (0x22 | LC_REQ_DYLD) => :LC_DYLD_INFO_ONLY, + (0x23 | LC_REQ_DYLD) => :LC_LOAD_UPWARD_DYLIB, + 0x24 => :LC_VERSION_MIN_MACOSX, + 0x25 => :LC_VERSION_MIN_IPHONEOS, + 0x26 => :LC_FUNCTION_STARTS, + 0x27 => :LC_DYLD_ENVIRONMENT, + (0x28 | LC_REQ_DYLD) => :LC_MAIN, + 0x29 => :LC_DATA_IN_CODE, + 0x2a => :LC_SOURCE_VERSION, + 0x2b => :LC_DYLIB_CODE_SIGN_DRS, + 0x2c => :LC_ENCRYPTION_INFO_64, + 0x2d => :LC_LINKER_OPTION, + 0x2e => :LC_LINKER_OPTIMIZATION_HINT, + 0x2f => :LC_VERSION_MIN_TVOS, + 0x30 => :LC_VERSION_MIN_WATCHOS, + }.freeze + + # association of symbol representations to load command constants + # @api private + LOAD_COMMAND_CONSTANTS = LOAD_COMMANDS.invert.freeze + + # load commands responsible for loading dylibs + # @api private + DYLIB_LOAD_COMMANDS = [ + :LC_LOAD_DYLIB, + :LC_LOAD_WEAK_DYLIB, + :LC_REEXPORT_DYLIB, + :LC_LAZY_LOAD_DYLIB, + :LC_LOAD_UPWARD_DYLIB, + ].freeze + + # load commands that can be created manually via {LoadCommand.create} + # @api private + CREATABLE_LOAD_COMMANDS = DYLIB_LOAD_COMMANDS + [ + :LC_ID_DYLIB, + :LC_RPATH, + :LC_LOAD_DYLINKER, + ].freeze + + # association of load command symbols to string representations of classes + # @api private + LC_STRUCTURES = { + :LC_SEGMENT => "SegmentCommand", + :LC_SYMTAB => "SymtabCommand", + # "obsolete" + :LC_SYMSEG => "SymsegCommand", + # seems obsolete, but not documented as such + :LC_THREAD => "ThreadCommand", + :LC_UNIXTHREAD => "ThreadCommand", + # "obsolete" + :LC_LOADFVMLIB => "FvmlibCommand", + # "obsolete" + :LC_IDFVMLIB => "FvmlibCommand", + # "obsolete" + :LC_IDENT => "IdentCommand", + # "reserved for internal use only" + :LC_FVMFILE => "FvmfileCommand", + # "reserved for internal use only", no public struct + :LC_PREPAGE => "LoadCommand", + :LC_DYSYMTAB => "DysymtabCommand", + :LC_LOAD_DYLIB => "DylibCommand", + :LC_ID_DYLIB => "DylibCommand", + :LC_LOAD_DYLINKER => "DylinkerCommand", + :LC_ID_DYLINKER => "DylinkerCommand", + :LC_PREBOUND_DYLIB => "PreboundDylibCommand", + :LC_ROUTINES => "RoutinesCommand", + :LC_SUB_FRAMEWORK => "SubFrameworkCommand", + :LC_SUB_UMBRELLA => "SubUmbrellaCommand", + :LC_SUB_CLIENT => "SubClientCommand", + :LC_SUB_LIBRARY => "SubLibraryCommand", + :LC_TWOLEVEL_HINTS => "TwolevelHintsCommand", + :LC_PREBIND_CKSUM => "PrebindCksumCommand", + :LC_LOAD_WEAK_DYLIB => "DylibCommand", + :LC_SEGMENT_64 => "SegmentCommand64", + :LC_ROUTINES_64 => "RoutinesCommand64", + :LC_UUID => "UUIDCommand", + :LC_RPATH => "RpathCommand", + :LC_CODE_SIGNATURE => "LinkeditDataCommand", + :LC_SEGMENT_SPLIT_INFO => "LinkeditDataCommand", + :LC_REEXPORT_DYLIB => "DylibCommand", + :LC_LAZY_LOAD_DYLIB => "DylibCommand", + :LC_ENCRYPTION_INFO => "EncryptionInfoCommand", + :LC_DYLD_INFO => "DyldInfoCommand", + :LC_DYLD_INFO_ONLY => "DyldInfoCommand", + :LC_LOAD_UPWARD_DYLIB => "DylibCommand", + :LC_VERSION_MIN_MACOSX => "VersionMinCommand", + :LC_VERSION_MIN_IPHONEOS => "VersionMinCommand", + :LC_FUNCTION_STARTS => "LinkeditDataCommand", + :LC_DYLD_ENVIRONMENT => "DylinkerCommand", + :LC_MAIN => "EntryPointCommand", + :LC_DATA_IN_CODE => "LinkeditDataCommand", + :LC_SOURCE_VERSION => "SourceVersionCommand", + :LC_DYLIB_CODE_SIGN_DRS => "LinkeditDataCommand", + :LC_ENCRYPTION_INFO_64 => "EncryptionInfoCommand64", + :LC_LINKER_OPTION => "LinkerOptionCommand", + :LC_LINKER_OPTIMIZATION_HINT => "LinkeditDataCommand", + :LC_VERSION_MIN_TVOS => "VersionMinCommand", + :LC_VERSION_MIN_WATCHOS => "VersionMinCommand", + }.freeze + + # association of segment name symbols to names + # @api private + SEGMENT_NAMES = { + :SEG_PAGEZERO => "__PAGEZERO", + :SEG_TEXT => "__TEXT", + :SEG_DATA => "__DATA", + :SEG_OBJC => "__OBJC", + :SEG_ICON => "__ICON", + :SEG_LINKEDIT => "__LINKEDIT", + :SEG_UNIXSTACK => "__UNIXSTACK", + :SEG_IMPORT => "__IMPORT", + }.freeze + + # association of segment flag symbols to values + # @api private + SEGMENT_FLAGS = { + :SG_HIGHVM => 0x1, + :SG_FVMLIB => 0x2, + :SG_NORELOC => 0x4, + :SG_PROTECTED_VERSION_1 => 0x8, + }.freeze + + # Mach-O load command structure + # This is the most generic load command - only cmd ID and size are + # represented, and no actual data. Used when a more specific class + # isn't available/implemented. + class LoadCommand < MachOStructure + # @return [MachO::MachOView] the raw view associated with the load command + attr_reader :view + + # @return [Fixnum] the load command's identifying number + attr_reader :cmd + + # @return [Fixnum] the size of the load command, in bytes + attr_reader :cmdsize + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 8 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 8 - # Instantiates a new LoadCommand given a view into its origin Mach-O - # @param view [MachO::MachOView] the load command's raw view - # @return [MachO::LoadCommand] the new load command - # @api private - def self.new_from_bin(view) - bin = view.raw_data.slice(view.offset, bytesize) - format = Utils.specialize_format(self::FORMAT, view.endianness) + # Instantiates a new LoadCommand given a view into its origin Mach-O + # @param view [MachO::MachOView] the load command's raw view + # @return [LoadCommand] the new load command + # @api private + def self.new_from_bin(view) + bin = view.raw_data.slice(view.offset, bytesize) + format = Utils.specialize_format(self::FORMAT, view.endianness) - new(view, *bin.unpack(format)) - end + new(view, *bin.unpack(format)) + end - # Creates a new (viewless) command corresponding to the symbol provided - # @param cmd_sym [Symbol] the symbol of the load command being created - # @param args [Array] the arguments for the load command being created - def self.create(cmd_sym, *args) - raise LoadCommandNotCreatableError, cmd_sym unless CREATABLE_LOAD_COMMANDS.include?(cmd_sym) + # Creates a new (viewless) command corresponding to the symbol provided + # @param cmd_sym [Symbol] the symbol of the load command being created + # @param args [Array] the arguments for the load command being created + def self.create(cmd_sym, *args) + raise LoadCommandNotCreatableError, cmd_sym unless CREATABLE_LOAD_COMMANDS.include?(cmd_sym) - klass = MachO.const_get LC_STRUCTURES[cmd_sym] - cmd = LOAD_COMMAND_CONSTANTS[cmd_sym] + klass = LoadCommands.const_get LC_STRUCTURES[cmd_sym] + cmd = LOAD_COMMAND_CONSTANTS[cmd_sym] - # cmd will be filled in, view and cmdsize will be left unpopulated - klass_arity = klass.instance_method(:initialize).arity - 3 + # cmd will be filled in, view and cmdsize will be left unpopulated + klass_arity = klass.instance_method(:initialize).arity - 3 - raise LoadCommandCreationArityError.new(cmd_sym, klass_arity, args.size) if klass_arity != args.size + raise LoadCommandCreationArityError.new(cmd_sym, klass_arity, args.size) if klass_arity != args.size - klass.new(nil, cmd, nil, *args) - end + klass.new(nil, cmd, nil, *args) + end - # @param view [MachO::MachOView] the load command's raw view - # @param cmd [Fixnum] the load command's identifying number - # @param cmdsize [Fixnum] the size of the load command in bytes - # @api private - def initialize(view, cmd, cmdsize) - @view = view - @cmd = cmd - @cmdsize = cmdsize - end + # @param view [MachO::MachOView] the load command's raw view + # @param cmd [Fixnum] the load command's identifying number + # @param cmdsize [Fixnum] the size of the load command in bytes + # @api private + def initialize(view, cmd, cmdsize) + @view = view + @cmd = cmd + @cmdsize = cmdsize + end - # @return [Boolean] true if the load command can be serialized, false otherwise - def serializable? - CREATABLE_LOAD_COMMANDS.include?(LOAD_COMMANDS[cmd]) - end + # @return [Boolean] whether the load command can be serialized + def serializable? + CREATABLE_LOAD_COMMANDS.include?(LOAD_COMMANDS[cmd]) + end - # @param context [MachO::LoadCommand::SerializationContext] the context - # to serialize into - # @return [String, nil] the serialized fields of the load command, or nil - # if the load command can't be serialized - # @api private - def serialize(context) - raise LoadCommandNotSerializableError, LOAD_COMMANDS[cmd] unless serializable? - format = Utils.specialize_format(FORMAT, context.endianness) - [cmd, SIZEOF].pack(format) - end + # @param context [SerializationContext] the context + # to serialize into + # @return [String, nil] the serialized fields of the load command, or nil + # if the load command can't be serialized + # @api private + def serialize(context) + raise LoadCommandNotSerializableError, LOAD_COMMANDS[cmd] unless serializable? + format = Utils.specialize_format(FORMAT, context.endianness) + [cmd, SIZEOF].pack(format) + end - # @return [Fixnum] the load command's offset in the source file - # @deprecated use {#view} instead - def offset - view.offset - end + # @return [Fixnum] the load command's offset in the source file + # @deprecated use {#view} instead + def offset + view.offset + end - # @return [Symbol] a symbol representation of the load command's identifying number - def type - LOAD_COMMANDS[cmd] - end + # @return [Symbol] a symbol representation of the load command's + # identifying number + def type + LOAD_COMMANDS[cmd] + end - alias to_sym type + alias to_sym type - # @return [String] a string representation of the load command's identifying number - def to_s - type.to_s - end + # @return [String] a string representation of the load command's + # identifying number + def to_s + type.to_s + end - # Represents a Load Command string. A rough analogue to the lc_str - # struct used internally by OS X. This class allows ruby-macho to - # pretend that strings stored in LCs are immediately available without - # explicit operations on the raw Mach-O data. - class LCStr - # @param lc [MachO::LoadCommand] the load command - # @param lc_str [Fixnum, String] the offset to the beginning of the string, - # or the string itself if not being initialized with a view. - # @raise [MachO::LCStrMalformedError] if the string is malformed - # @todo devise a solution such that the `lc_str` parameter is not - # interpreted differently depending on `lc.view`. The current behavior - # is a hack to allow viewless load command creation. - # @api private - def initialize(lc, lc_str) - view = lc.view - - if view - lc_str_abs = view.offset + lc_str - lc_end = view.offset + lc.cmdsize - 1 - raw_string = view.raw_data.slice(lc_str_abs..lc_end) - @string, null_byte, _padding = raw_string.partition("\x00") - raise LCStrMalformedError, lc if null_byte.empty? - @string_offset = lc_str - else - @string = lc_str - @string_offset = 0 + # Represents a Load Command string. A rough analogue to the lc_str + # struct used internally by OS X. This class allows ruby-macho to + # pretend that strings stored in LCs are immediately available without + # explicit operations on the raw Mach-O data. + class LCStr + # @param lc [LoadCommand] the load command + # @param lc_str [Fixnum, String] the offset to the beginning of the + # string, or the string itself if not being initialized with a view. + # @raise [MachO::LCStrMalformedError] if the string is malformed + # @todo devise a solution such that the `lc_str` parameter is not + # interpreted differently depending on `lc.view`. The current behavior + # is a hack to allow viewless load command creation. + # @api private + def initialize(lc, lc_str) + view = lc.view + + if view + lc_str_abs = view.offset + lc_str + lc_end = view.offset + lc.cmdsize - 1 + raw_string = view.raw_data.slice(lc_str_abs..lc_end) + @string, null_byte, _padding = raw_string.partition("\x00") + raise LCStrMalformedError, lc if null_byte.empty? + @string_offset = lc_str + else + @string = lc_str + @string_offset = 0 + end end - end - # @return [String] a string representation of the LCStr - def to_s - @string + # @return [String] a string representation of the LCStr + def to_s + @string + end + + # @return [Fixnum] the offset to the beginning of the string in the + # load command + def to_i + @string_offset + end end - # @return [Fixnum] the offset to the beginning of the string in the load command - def to_i - @string_offset + # Represents the contextual information needed by a load command to + # serialize itself correctly into a binary string. + class SerializationContext + # @return [Symbol] the endianness of the serialized load command + attr_reader :endianness + + # @return [Fixnum] the constant alignment value used to pad the + # serialized load command + attr_reader :alignment + + # @param macho [MachO::MachOFile] the file to contextualize + # @return [SerializationContext] the + # resulting context + def self.context_for(macho) + new(macho.endianness, macho.alignment) + end + + # @param endianness [Symbol] the endianness of the context + # @param alignment [Fixnum] the alignment of the context + # @api private + def initialize(endianness, alignment) + @endianness = endianness + @alignment = alignment + end end end - # Represents the contextual information needed by a load command to - # serialize itself correctly into a binary string. - class SerializationContext - # @return [Symbol] the endianness of the serialized load command - attr_reader :endianness + # A load command containing a single 128-bit unique random number + # identifying an object produced by static link editor. Corresponds to + # LC_UUID. + class UUIDCommand < LoadCommand + # @return [Array<Fixnum>] the UUID + attr_reader :uuid - # @return [Fixnum] the constant alignment value used to pad the serialized load command - attr_reader :alignment + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2a16".freeze - # @param macho [MachO::MachOFile] the file to contextualize - # @return [MachO::LoadCommand::SerializationContext] the resulting context - def self.context_for(macho) - new(macho.endianness, macho.alignment) - end + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 24 - # @param endianness [Symbol] the endianness of the context - # @param alignment [Fixnum] the alignment of the context # @api private - def initialize(endianness, alignment) - @endianness = endianness - @alignment = alignment + def initialize(view, cmd, cmdsize, uuid) + super(view, cmd, cmdsize) + @uuid = uuid.unpack("C16") # re-unpack for the actual UUID array end - end - end - - # A load command containing a single 128-bit unique random number identifying - # an object produced by static link editor. Corresponds to LC_UUID. - class UUIDCommand < LoadCommand - # @return [Array<Fixnum>] the UUID - attr_reader :uuid - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2a16".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 24 + # @return [String] a string representation of the UUID + def uuid_string + hexes = uuid.map { |e| "%02x" % e } + segs = [ + hexes[0..3].join, hexes[4..5].join, hexes[6..7].join, + hexes[8..9].join, hexes[10..15].join + ] - # @api private - def initialize(view, cmd, cmdsize, uuid) - super(view, cmd, cmdsize) - @uuid = uuid.unpack("C16") # re-unpack for the actual UUID array + segs.join("-") + end end - # @return [String] a string representation of the UUID - def uuid_string - hexes = uuid.map { |e| "%02x" % e } - segs = [ - hexes[0..3].join, hexes[4..5].join, hexes[6..7].join, - hexes[8..9].join, hexes[10..15].join - ] + # A load command indicating that part of this file is to be mapped into + # the task's address space. Corresponds to LC_SEGMENT. + class SegmentCommand < LoadCommand + # @return [String] the name of the segment + attr_reader :segname - segs.join("-") - end - end + # @return [Fixnum] the memory address of the segment + attr_reader :vmaddr - # A load command indicating that part of this file is to be mapped into - # the task's address space. Corresponds to LC_SEGMENT. - class SegmentCommand < LoadCommand - # @return [String] the name of the segment - attr_reader :segname + # @return [Fixnum] the memory size of the segment + attr_reader :vmsize - # @return [Fixnum] the memory address of the segment - attr_reader :vmaddr + # @return [Fixnum] the file offset of the segment + attr_reader :fileoff - # @return [Fixnum] the memory size of the segment - attr_reader :vmsize + # @return [Fixnum] the amount to map from the file + attr_reader :filesize - # @return [Fixnum] the file offset of the segment - attr_reader :fileoff + # @return [Fixnum] the maximum VM protection + attr_reader :maxprot - # @return [Fixnum] the amount to map from the file - attr_reader :filesize + # @return [Fixnum] the initial VM protection + attr_reader :initprot - # @return [Fixnum] the maximum VM protection - attr_reader :maxprot + # @return [Fixnum] the number of sections in the segment + attr_reader :nsects - # @return [Fixnum] the initial VM protection - attr_reader :initprot + # @return [Fixnum] any flags associated with the segment + attr_reader :flags - # @return [Fixnum] the number of sections in the segment - attr_reader :nsects + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2a16L=4l=2L=2".freeze - # @return [Fixnum] any flags associated with the segment - attr_reader :flags + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 56 - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2a16L=4l=2L=2".freeze + # @api private + def initialize(view, cmd, cmdsize, segname, vmaddr, vmsize, fileoff, + filesize, maxprot, initprot, nsects, flags) + super(view, cmd, cmdsize) + @segname = segname.delete("\x00") + @vmaddr = vmaddr + @vmsize = vmsize + @fileoff = fileoff + @filesize = filesize + @maxprot = maxprot + @initprot = initprot + @nsects = nsects + @flags = flags + end - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 56 + # All sections referenced within this segment. + # @return [Array<MachO::Sections::Section>] if the Mach-O is 32-bit + # @return [Array<MachO::Sections::Section64>] if the Mach-O is 64-bit + def sections + klass = case self + when SegmentCommand64 + MachO::Sections::Section64 + when SegmentCommand + MachO::Sections::Section + end - # @api private - def initialize(view, cmd, cmdsize, segname, vmaddr, vmsize, fileoff, - filesize, maxprot, initprot, nsects, flags) - super(view, cmd, cmdsize) - @segname = segname.delete("\x00") - @vmaddr = vmaddr - @vmsize = vmsize - @fileoff = fileoff - @filesize = filesize - @maxprot = maxprot - @initprot = initprot - @nsects = nsects - @flags = flags - end + offset = view.offset + self.class.bytesize + length = nsects * klass.bytesize - # All sections referenced within this segment. - # @return [Array<MachO::Section>] if the Mach-O is 32-bit - # @return [Array<MachO::Section64>] if the Mach-O is 64-bit - def sections - klass = case self - when MachO::SegmentCommand64 - MachO::Section64 - when MachO::SegmentCommand - MachO::Section + bins = view.raw_data[offset, length] + bins.unpack("a#{klass.bytesize}" * nsects).map do |bin| + klass.new_from_bin(view.endianness, bin) + end end - bins = view.raw_data[view.offset + self.class.bytesize, nsects * klass.bytesize] - bins.unpack("a#{klass.bytesize}" * nsects).map do |bin| - klass.new_from_bin(view.endianness, bin) + # @example + # puts "this segment relocated in/to it" if sect.flag?(:SG_NORELOC) + # @param flag [Symbol] a segment flag symbol + # @return [Boolean] true if `flag` is present in the segment's flag field + def flag?(flag) + flag = SEGMENT_FLAGS[flag] + return false if flag.nil? + flags & flag == flag end end - # @example - # puts "this segment relocated in/to it" if sect.flag?(:SG_NORELOC) - # @param flag [Symbol] a segment flag symbol - # @return [Boolean] true if `flag` is present in the segment's flag field - def flag?(flag) - flag = SEGMENT_FLAGS[flag] - return false if flag.nil? - flags & flag == flag - end - end - - # A load command indicating that part of this file is to be mapped into - # the task's address space. Corresponds to LC_SEGMENT_64. - class SegmentCommand64 < SegmentCommand - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2a16Q=4l=2L=2".freeze + # A load command indicating that part of this file is to be mapped into + # the task's address space. Corresponds to LC_SEGMENT_64. + class SegmentCommand64 < SegmentCommand + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2a16Q=4l=2L=2".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 72 - end + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 72 + end - # A load command representing some aspect of shared libraries, depending - # on filetype. Corresponds to LC_ID_DYLIB, LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, - # and LC_REEXPORT_DYLIB. - class DylibCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the library's path name as an LCStr - attr_reader :name + # A load command representing some aspect of shared libraries, depending + # on filetype. Corresponds to LC_ID_DYLIB, LC_LOAD_DYLIB, + # LC_LOAD_WEAK_DYLIB, and LC_REEXPORT_DYLIB. + class DylibCommand < LoadCommand + # @return [LCStr] the library's path + # name as an LCStr + attr_reader :name - # @return [Fixnum] the library's build time stamp - attr_reader :timestamp + # @return [Fixnum] the library's build time stamp + attr_reader :timestamp - # @return [Fixnum] the library's current version number - attr_reader :current_version + # @return [Fixnum] the library's current version number + attr_reader :current_version - # @return [Fixnum] the library's compatibility version number - attr_reader :compatibility_version + # @return [Fixnum] the library's compatibility version number + attr_reader :compatibility_version - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=6".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=6".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 24 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 24 - # @api private - def initialize(view, cmd, cmdsize, name, timestamp, current_version, compatibility_version) - super(view, cmd, cmdsize) - @name = LCStr.new(self, name) - @timestamp = timestamp - @current_version = current_version - @compatibility_version = compatibility_version - end + # @api private + def initialize(view, cmd, cmdsize, name, timestamp, current_version, + compatibility_version) + super(view, cmd, cmdsize) + @name = LCStr.new(self, name) + @timestamp = timestamp + @current_version = current_version + @compatibility_version = compatibility_version + end - # @param context [MachO::LoadCcommand::SerializationContext] the context - # @return [String] the serialized fields of the load command - # @api private - def serialize(context) - format = Utils.specialize_format(FORMAT, context.endianness) - string_payload, string_offsets = Utils.pack_strings(SIZEOF, context.alignment, :name => name.to_s) - cmdsize = SIZEOF + string_payload.bytesize - [cmd, cmdsize, string_offsets[:name], timestamp, current_version, - compatibility_version].pack(format) + string_payload + # @param context [SerializationContext] + # the context + # @return [String] the serialized fields of the load command + # @api private + def serialize(context) + format = Utils.specialize_format(FORMAT, context.endianness) + string_payload, string_offsets = Utils.pack_strings(SIZEOF, + context.alignment, + :name => name.to_s) + cmdsize = SIZEOF + string_payload.bytesize + [cmd, cmdsize, string_offsets[:name], timestamp, current_version, + compatibility_version].pack(format) + string_payload + end end - end - # A load command representing some aspect of the dynamic linker, depending - # on filetype. Corresponds to LC_ID_DYLINKER, LC_LOAD_DYLINKER, and - # LC_DYLD_ENVIRONMENT. - class DylinkerCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the dynamic linker's path name as an LCStr - attr_reader :name + # A load command representing some aspect of the dynamic linker, depending + # on filetype. Corresponds to LC_ID_DYLINKER, LC_LOAD_DYLINKER, and + # LC_DYLD_ENVIRONMENT. + class DylinkerCommand < LoadCommand + # @return [LCStr] the dynamic linker's + # path name as an LCStr + attr_reader :name - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 - # @api private - def initialize(view, cmd, cmdsize, name) - super(view, cmd, cmdsize) - @name = LCStr.new(self, name) - end + # @api private + def initialize(view, cmd, cmdsize, name) + super(view, cmd, cmdsize) + @name = LCStr.new(self, name) + end - # @param context [MachO::LoadCcommand::SerializationContext] the context - # @return [String] the serialized fields of the load command - # @api private - def serialize(context) - format = Utils.specialize_format(FORMAT, context.endianness) - string_payload, string_offsets = Utils.pack_strings(SIZEOF, context.alignment, :name => name.to_s) - cmdsize = SIZEOF + string_payload.bytesize - [cmd, cmdsize, string_offsets[:name]].pack(format) + string_payload + # @param context [SerializationContext] + # the context + # @return [String] the serialized fields of the load command + # @api private + def serialize(context) + format = Utils.specialize_format(FORMAT, context.endianness) + string_payload, string_offsets = Utils.pack_strings(SIZEOF, + context.alignment, + :name => name.to_s) + cmdsize = SIZEOF + string_payload.bytesize + [cmd, cmdsize, string_offsets[:name]].pack(format) + string_payload + end end - end - # A load command used to indicate dynamic libraries used in prebinding. - # Corresponds to LC_PREBOUND_DYLIB. - class PreboundDylibCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the library's path name as an LCStr - attr_reader :name + # A load command used to indicate dynamic libraries used in prebinding. + # Corresponds to LC_PREBOUND_DYLIB. + class PreboundDylibCommand < LoadCommand + # @return [LCStr] the library's path + # name as an LCStr + attr_reader :name - # @return [Fixnum] the number of modules in the library - attr_reader :nmodules + # @return [Fixnum] the number of modules in the library + attr_reader :nmodules - # @return [Fixnum] a bit vector of linked modules - attr_reader :linked_modules + # @return [Fixnum] a bit vector of linked modules + attr_reader :linked_modules - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=5".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=5".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 20 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 20 - # @api private - def initialize(view, cmd, cmdsize, name, nmodules, linked_modules) - super(view, cmd, cmdsize) - @name = LCStr.new(self, name) - @nmodules = nmodules - @linked_modules = linked_modules + # @api private + def initialize(view, cmd, cmdsize, name, nmodules, linked_modules) + super(view, cmd, cmdsize) + @name = LCStr.new(self, name) + @nmodules = nmodules + @linked_modules = linked_modules + end end - end - # A load command used to represent threads. - # @note cctools-870 has all fields of thread_command commented out except common ones (cmd, cmdsize) - class ThreadCommand < LoadCommand - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2".freeze + # A load command used to represent threads. + # @note cctools-870 and onwards have all fields of thread_command commented + # out except the common ones (cmd, cmdsize) + class ThreadCommand < LoadCommand + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 8 - end + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 8 + end - # A load command containing the address of the dynamic shared library - # initialization routine and an index into the module table for the module - # that defines the routine. Corresponds to LC_ROUTINES. - class RoutinesCommand < LoadCommand - # @return [Fixnum] the address of the initialization routine - attr_reader :init_address + # A load command containing the address of the dynamic shared library + # initialization routine and an index into the module table for the module + # that defines the routine. Corresponds to LC_ROUTINES. + class RoutinesCommand < LoadCommand + # @return [Fixnum] the address of the initialization routine + attr_reader :init_address - # @return [Fixnum] the index into the module table that the init routine is defined in - attr_reader :init_module + # @return [Fixnum] the index into the module table that the init routine + # is defined in + attr_reader :init_module - # @return [void] - attr_reader :reserved1 + # @return [void] + attr_reader :reserved1 - # @return [void] - attr_reader :reserved2 + # @return [void] + attr_reader :reserved2 - # @return [void] - attr_reader :reserved3 + # @return [void] + attr_reader :reserved3 - # @return [void] - attr_reader :reserved4 + # @return [void] + attr_reader :reserved4 - # @return [void] - attr_reader :reserved5 + # @return [void] + attr_reader :reserved5 - # @return [void] - attr_reader :reserved6 + # @return [void] + attr_reader :reserved6 - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=10".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=10".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 40 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 40 - # @api private - def initialize(view, cmd, cmdsize, init_address, init_module, reserved1, - reserved2, reserved3, reserved4, reserved5, reserved6) - super(view, cmd, cmdsize) - @init_address = init_address - @init_module = init_module - @reserved1 = reserved1 - @reserved2 = reserved2 - @reserved3 = reserved3 - @reserved4 = reserved4 - @reserved5 = reserved5 - @reserved6 = reserved6 + # @api private + def initialize(view, cmd, cmdsize, init_address, init_module, reserved1, + reserved2, reserved3, reserved4, reserved5, reserved6) + super(view, cmd, cmdsize) + @init_address = init_address + @init_module = init_module + @reserved1 = reserved1 + @reserved2 = reserved2 + @reserved3 = reserved3 + @reserved4 = reserved4 + @reserved5 = reserved5 + @reserved6 = reserved6 + end end - end - # A load command containing the address of the dynamic shared library - # initialization routine and an index into the module table for the module - # that defines the routine. Corresponds to LC_ROUTINES_64. - class RoutinesCommand64 < RoutinesCommand - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2Q=8".freeze + # A load command containing the address of the dynamic shared library + # initialization routine and an index into the module table for the module + # that defines the routine. Corresponds to LC_ROUTINES_64. + class RoutinesCommand64 < RoutinesCommand + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2Q=8".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 72 - end + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 72 + end - # A load command signifying membership of a subframework containing the name - # of an umbrella framework. Corresponds to LC_SUB_FRAMEWORK. - class SubFrameworkCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the umbrella framework name as an LCStr - attr_reader :umbrella + # A load command signifying membership of a subframework containing the name + # of an umbrella framework. Corresponds to LC_SUB_FRAMEWORK. + class SubFrameworkCommand < LoadCommand + # @return [LCStr] the umbrella framework name as an LCStr + attr_reader :umbrella - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 - # @api private - def initialize(view, cmd, cmdsize, umbrella) - super(view, cmd, cmdsize) - @umbrella = LCStr.new(self, umbrella) + # @api private + def initialize(view, cmd, cmdsize, umbrella) + super(view, cmd, cmdsize) + @umbrella = LCStr.new(self, umbrella) + end end - end - # A load command signifying membership of a subumbrella containing the name - # of an umbrella framework. Corresponds to LC_SUB_UMBRELLA. - class SubUmbrellaCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the subumbrella framework name as an LCStr - attr_reader :sub_umbrella + # A load command signifying membership of a subumbrella containing the name + # of an umbrella framework. Corresponds to LC_SUB_UMBRELLA. + class SubUmbrellaCommand < LoadCommand + # @return [LCStr] the subumbrella framework name as an LCStr + attr_reader :sub_umbrella - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 - # @api private - def initialize(view, cmd, cmdsize, sub_umbrella) - super(view, cmd, cmdsize) - @sub_umbrella = LCStr.new(self, sub_umbrella) + # @api private + def initialize(view, cmd, cmdsize, sub_umbrella) + super(view, cmd, cmdsize) + @sub_umbrella = LCStr.new(self, sub_umbrella) + end end - end - # A load command signifying a sublibrary of a shared library. Corresponds - # to LC_SUB_LIBRARY. - class SubLibraryCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the sublibrary name as an LCStr - attr_reader :sub_library + # A load command signifying a sublibrary of a shared library. Corresponds + # to LC_SUB_LIBRARY. + class SubLibraryCommand < LoadCommand + # @return [LCStr] the sublibrary name as an LCStr + attr_reader :sub_library - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 - # @api private - def initialize(view, cmd, cmdsize, sub_library) - super(view, cmd, cmdsize) - @sub_library = LCStr.new(self, sub_library) + # @api private + def initialize(view, cmd, cmdsize, sub_library) + super(view, cmd, cmdsize) + @sub_library = LCStr.new(self, sub_library) + end end - end - # A load command signifying a shared library that is a subframework of - # an umbrella framework. Corresponds to LC_SUB_CLIENT. - class SubClientCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the subclient name as an LCStr - attr_reader :sub_client + # A load command signifying a shared library that is a subframework of + # an umbrella framework. Corresponds to LC_SUB_CLIENT. + class SubClientCommand < LoadCommand + # @return [LCStr] the subclient name as an LCStr + attr_reader :sub_client - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 - # @api private - def initialize(view, cmd, cmdsize, sub_client) - super(view, cmd, cmdsize) - @sub_client = LCStr.new(self, sub_client) + # @api private + def initialize(view, cmd, cmdsize, sub_client) + super(view, cmd, cmdsize) + @sub_client = LCStr.new(self, sub_client) + end end - end - # A load command containing the offsets and sizes of the link-edit 4.3BSD - # "stab" style symbol table information. Corresponds to LC_SYMTAB. - class SymtabCommand < LoadCommand - # @return [Fixnum] the symbol table's offset - attr_reader :symoff + # A load command containing the offsets and sizes of the link-edit 4.3BSD + # "stab" style symbol table information. Corresponds to LC_SYMTAB. + class SymtabCommand < LoadCommand + # @return [Fixnum] the symbol table's offset + attr_reader :symoff - # @return [Fixnum] the number of symbol table entries - attr_reader :nsyms + # @return [Fixnum] the number of symbol table entries + attr_reader :nsyms - # @return the string table's offset - attr_reader :stroff + # @return the string table's offset + attr_reader :stroff - # @return the string table size in bytes - attr_reader :strsize + # @return the string table size in bytes + attr_reader :strsize - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=6".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=6".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 24 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 24 - # @api private - def initialize(view, cmd, cmdsize, symoff, nsyms, stroff, strsize) - super(view, cmd, cmdsize) - @symoff = symoff - @nsyms = nsyms - @stroff = stroff - @strsize = strsize + # @api private + def initialize(view, cmd, cmdsize, symoff, nsyms, stroff, strsize) + super(view, cmd, cmdsize) + @symoff = symoff + @nsyms = nsyms + @stroff = stroff + @strsize = strsize + end end - end - # A load command containing symbolic information needed to support data - # structures used by the dynamic link editor. Corresponds to LC_DYSYMTAB. - class DysymtabCommand < LoadCommand - # @return [Fixnum] the index to local symbols - attr_reader :ilocalsym + # A load command containing symbolic information needed to support data + # structures used by the dynamic link editor. Corresponds to LC_DYSYMTAB. + class DysymtabCommand < LoadCommand + # @return [Fixnum] the index to local symbols + attr_reader :ilocalsym - # @return [Fixnum] the number of local symbols - attr_reader :nlocalsym + # @return [Fixnum] the number of local symbols + attr_reader :nlocalsym - # @return [Fixnum] the index to externally defined symbols - attr_reader :iextdefsym + # @return [Fixnum] the index to externally defined symbols + attr_reader :iextdefsym - # @return [Fixnum] the number of externally defined symbols - attr_reader :nextdefsym + # @return [Fixnum] the number of externally defined symbols + attr_reader :nextdefsym - # @return [Fixnum] the index to undefined symbols - attr_reader :iundefsym + # @return [Fixnum] the index to undefined symbols + attr_reader :iundefsym - # @return [Fixnum] the number of undefined symbols - attr_reader :nundefsym + # @return [Fixnum] the number of undefined symbols + attr_reader :nundefsym - # @return [Fixnum] the file offset to the table of contents - attr_reader :tocoff + # @return [Fixnum] the file offset to the table of contents + attr_reader :tocoff - # @return [Fixnum] the number of entries in the table of contents - attr_reader :ntoc + # @return [Fixnum] the number of entries in the table of contents + attr_reader :ntoc - # @return [Fixnum] the file offset to the module table - attr_reader :modtaboff + # @return [Fixnum] the file offset to the module table + attr_reader :modtaboff - # @return [Fixnum] the number of entries in the module table - attr_reader :nmodtab + # @return [Fixnum] the number of entries in the module table + attr_reader :nmodtab - # @return [Fixnum] the file offset to the referenced symbol table - attr_reader :extrefsymoff + # @return [Fixnum] the file offset to the referenced symbol table + attr_reader :extrefsymoff - # @return [Fixnum] the number of entries in the referenced symbol table - attr_reader :nextrefsyms + # @return [Fixnum] the number of entries in the referenced symbol table + attr_reader :nextrefsyms - # @return [Fixnum] the file offset to the indirect symbol table - attr_reader :indirectsymoff + # @return [Fixnum] the file offset to the indirect symbol table + attr_reader :indirectsymoff - # @return [Fixnum] the number of entries in the indirect symbol table - attr_reader :nindirectsyms + # @return [Fixnum] the number of entries in the indirect symbol table + attr_reader :nindirectsyms - # @return [Fixnum] the file offset to the external relocation entries - attr_reader :extreloff + # @return [Fixnum] the file offset to the external relocation entries + attr_reader :extreloff - # @return [Fixnum] the number of external relocation entries - attr_reader :nextrel + # @return [Fixnum] the number of external relocation entries + attr_reader :nextrel - # @return [Fixnum] the file offset to the local relocation entries - attr_reader :locreloff + # @return [Fixnum] the file offset to the local relocation entries + attr_reader :locreloff - # @return [Fixnum] the number of local relocation entries - attr_reader :nlocrel + # @return [Fixnum] the number of local relocation entries + attr_reader :nlocrel - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=20".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=20".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 80 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 80 - # ugh - # @api private - def initialize(view, cmd, cmdsize, ilocalsym, nlocalsym, iextdefsym, - nextdefsym, iundefsym, nundefsym, tocoff, ntoc, modtaboff, - nmodtab, extrefsymoff, nextrefsyms, indirectsymoff, - nindirectsyms, extreloff, nextrel, locreloff, nlocrel) - super(view, cmd, cmdsize) - @ilocalsym = ilocalsym - @nlocalsym = nlocalsym - @iextdefsym = iextdefsym - @nextdefsym = nextdefsym - @iundefsym = iundefsym - @nundefsym = nundefsym - @tocoff = tocoff - @ntoc = ntoc - @modtaboff = modtaboff - @nmodtab = nmodtab - @extrefsymoff = extrefsymoff - @nextrefsyms = nextrefsyms - @indirectsymoff = indirectsymoff - @nindirectsyms = nindirectsyms - @extreloff = extreloff - @nextrel = nextrel - @locreloff = locreloff - @nlocrel = nlocrel + # ugh + # @api private + def initialize(view, cmd, cmdsize, ilocalsym, nlocalsym, iextdefsym, + nextdefsym, iundefsym, nundefsym, tocoff, ntoc, modtaboff, + nmodtab, extrefsymoff, nextrefsyms, indirectsymoff, + nindirectsyms, extreloff, nextrel, locreloff, nlocrel) + super(view, cmd, cmdsize) + @ilocalsym = ilocalsym + @nlocalsym = nlocalsym + @iextdefsym = iextdefsym + @nextdefsym = nextdefsym + @iundefsym = iundefsym + @nundefsym = nundefsym + @tocoff = tocoff + @ntoc = ntoc + @modtaboff = modtaboff + @nmodtab = nmodtab + @extrefsymoff = extrefsymoff + @nextrefsyms = nextrefsyms + @indirectsymoff = indirectsymoff + @nindirectsyms = nindirectsyms + @extreloff = extreloff + @nextrel = nextrel + @locreloff = locreloff + @nlocrel = nlocrel + end end - end - # A load command containing the offset and number of hints in the two-level - # namespace lookup hints table. Corresponds to LC_TWOLEVEL_HINTS. - class TwolevelHintsCommand < LoadCommand - # @return [Fixnum] the offset to the hint table - attr_reader :htoffset + # A load command containing the offset and number of hints in the two-level + # namespace lookup hints table. Corresponds to LC_TWOLEVEL_HINTS. + class TwolevelHintsCommand < LoadCommand + # @return [Fixnum] the offset to the hint table + attr_reader :htoffset - # @return [Fixnum] the number of hints in the hint table - attr_reader :nhints + # @return [Fixnum] the number of hints in the hint table + attr_reader :nhints - # @return [MachO::TwolevelHintsCommand::TwolevelHintTable] the hint table - attr_reader :table + # @return [TwolevelHintsTable] + # the hint table + attr_reader :table - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=4".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=4".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 - # @api private - def initialize(view, cmd, cmdsize, htoffset, nhints) - super(view, cmd, cmdsize) - @htoffset = htoffset - @nhints = nhints - @table = TwolevelHintsTable.new(view, htoffset, nhints) - end + # @api private + def initialize(view, cmd, cmdsize, htoffset, nhints) + super(view, cmd, cmdsize) + @htoffset = htoffset + @nhints = nhints + @table = TwolevelHintsTable.new(view, htoffset, nhints) + end - # A representation of the two-level namespace lookup hints table exposed - # by a {TwolevelHintsCommand} (`LC_TWOLEVEL_HINTS`). - class TwolevelHintsTable - # @return [Array<MachO::TwoLevelHintsTable::TwoLevelHint>] all hints in the table - attr_reader :hints + # A representation of the two-level namespace lookup hints table exposed + # by a {TwolevelHintsCommand} (`LC_TWOLEVEL_HINTS`). + class TwolevelHintsTable + # @return [Array<TwolevelHint>] all hints in the table + attr_reader :hints - # @param view [MachO::MachOView] the view into the current Mach-O - # @param htoffset [Fixnum] the offset of the hints table - # @param nhints [Fixnum] the number of two-level hints in the table - # @api private - def initialize(view, htoffset, nhints) - format = Utils.specialize_format("L=#{nhints}", view.endianness) - raw_table = view.raw_data[htoffset, nhints * 4] - blobs = raw_table.unpack(format) + # @param view [MachO::MachOView] the view into the current Mach-O + # @param htoffset [Fixnum] the offset of the hints table + # @param nhints [Fixnum] the number of two-level hints in the table + # @api private + def initialize(view, htoffset, nhints) + format = Utils.specialize_format("L=#{nhints}", view.endianness) + raw_table = view.raw_data[htoffset, nhints * 4] + blobs = raw_table.unpack(format) - @hints = blobs.map { |b| TwolevelHint.new(b) } - end + @hints = blobs.map { |b| TwolevelHint.new(b) } + end - # An individual two-level namespace lookup hint. - class TwolevelHint - # @return [Fixnum] the index into the sub-images - attr_reader :isub_image + # An individual two-level namespace lookup hint. + class TwolevelHint + # @return [Fixnum] the index into the sub-images + attr_reader :isub_image - # @return [Fixnum] the index into the table of contents - attr_reader :itoc + # @return [Fixnum] the index into the table of contents + attr_reader :itoc - # @param blob [Fixnum] the 32-bit number containing the lookup hint - # @api private - def initialize(blob) - @isub_image = blob >> 24 - @itoc = blob & 0x00FFFFFF + # @param blob [Fixnum] the 32-bit number containing the lookup hint + # @api private + def initialize(blob) + @isub_image = blob >> 24 + @itoc = blob & 0x00FFFFFF + end end end end - end - # A load command containing the value of the original checksum for prebound - # files, or zero. Corresponds to LC_PREBIND_CKSUM. - class PrebindCksumCommand < LoadCommand - # @return [Fixnum] the checksum or 0 - attr_reader :cksum + # A load command containing the value of the original checksum for prebound + # files, or zero. Corresponds to LC_PREBIND_CKSUM. + class PrebindCksumCommand < LoadCommand + # @return [Fixnum] the checksum or 0 + attr_reader :cksum - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 - # @api private - def initialize(view, cmd, cmdsize, cksum) - super(view, cmd, cmdsize) - @cksum = cksum + # @api private + def initialize(view, cmd, cmdsize, cksum) + super(view, cmd, cmdsize) + @cksum = cksum + end end - end - # A load command representing an rpath, which specifies a path that should - # be added to the current run path used to find @rpath prefixed dylibs. - # Corresponds to LC_RPATH. - class RpathCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the path to add to the run path as an LCStr - attr_reader :path + # A load command representing an rpath, which specifies a path that should + # be added to the current run path used to find @rpath prefixed dylibs. + # Corresponds to LC_RPATH. + class RpathCommand < LoadCommand + # @return [LCStr] the path to add to the run path as an LCStr + attr_reader :path - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 - # @api private - def initialize(view, cmd, cmdsize, path) - super(view, cmd, cmdsize) - @path = LCStr.new(self, path) - end + # @api private + def initialize(view, cmd, cmdsize, path) + super(view, cmd, cmdsize) + @path = LCStr.new(self, path) + end - # @param context [MachO::LoadCcommand::SerializationContext] the context - # @return [String] the serialized fields of the load command - # @api private - def serialize(context) - format = Utils.specialize_format(FORMAT, context.endianness) - string_payload, string_offsets = Utils.pack_strings(SIZEOF, context.alignment, :path => path.to_s) - cmdsize = SIZEOF + string_payload.bytesize - [cmd, cmdsize, string_offsets[:path]].pack(format) + string_payload + # @param context [SerializationContext] the context + # @return [String] the serialized fields of the load command + # @api private + def serialize(context) + format = Utils.specialize_format(FORMAT, context.endianness) + string_payload, string_offsets = Utils.pack_strings(SIZEOF, + context.alignment, + :path => path.to_s) + cmdsize = SIZEOF + string_payload.bytesize + [cmd, cmdsize, string_offsets[:path]].pack(format) + string_payload + end end - end - # A load command representing the offsets and sizes of a blob of data in - # the __LINKEDIT segment. Corresponds to LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, - # LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS, and LC_LINKER_OPTIMIZATION_HINT. - class LinkeditDataCommand < LoadCommand - # @return [Fixnum] offset to the data in the __LINKEDIT segment - attr_reader :dataoff + # A load command representing the offsets and sizes of a blob of data in + # the __LINKEDIT segment. Corresponds to LC_CODE_SIGNATURE, + # LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, + # LC_DYLIB_CODE_SIGN_DRS, and LC_LINKER_OPTIMIZATION_HINT. + class LinkeditDataCommand < LoadCommand + # @return [Fixnum] offset to the data in the __LINKEDIT segment + attr_reader :dataoff - # @return [Fixnum] size of the data in the __LINKEDIT segment - attr_reader :datasize + # @return [Fixnum] size of the data in the __LINKEDIT segment + attr_reader :datasize - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=4".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=4".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 - # @api private - def initialize(view, cmd, cmdsize, dataoff, datasize) - super(view, cmd, cmdsize) - @dataoff = dataoff - @datasize = datasize + # @api private + def initialize(view, cmd, cmdsize, dataoff, datasize) + super(view, cmd, cmdsize) + @dataoff = dataoff + @datasize = datasize + end end - end - # A load command representing the offset to and size of an encrypted - # segment. Corresponds to LC_ENCRYPTION_INFO. - class EncryptionInfoCommand < LoadCommand - # @return [Fixnum] the offset to the encrypted segment - attr_reader :cryptoff + # A load command representing the offset to and size of an encrypted + # segment. Corresponds to LC_ENCRYPTION_INFO. + class EncryptionInfoCommand < LoadCommand + # @return [Fixnum] the offset to the encrypted segment + attr_reader :cryptoff - # @return [Fixnum] the size of the encrypted segment - attr_reader :cryptsize + # @return [Fixnum] the size of the encrypted segment + attr_reader :cryptsize - # @return [Fixnum] the encryption system, or 0 if not encrypted yet - attr_reader :cryptid + # @return [Fixnum] the encryption system, or 0 if not encrypted yet + attr_reader :cryptid - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=5".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=5".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 20 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 20 - # @api private - def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid) - super(view, cmd, cmdsize) - @cryptoff = cryptoff - @cryptsize = cryptsize - @cryptid = cryptid + # @api private + def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid) + super(view, cmd, cmdsize) + @cryptoff = cryptoff + @cryptsize = cryptsize + @cryptid = cryptid + end end - end - # A load command representing the offset to and size of an encrypted - # segment. Corresponds to LC_ENCRYPTION_INFO_64. - class EncryptionInfoCommand64 < LoadCommand - # @return [Fixnum] the offset to the encrypted segment - attr_reader :cryptoff + # A load command representing the offset to and size of an encrypted + # segment. Corresponds to LC_ENCRYPTION_INFO_64. + class EncryptionInfoCommand64 < LoadCommand + # @return [Fixnum] the offset to the encrypted segment + attr_reader :cryptoff - # @return [Fixnum] the size of the encrypted segment - attr_reader :cryptsize + # @return [Fixnum] the size of the encrypted segment + attr_reader :cryptsize - # @return [Fixnum] the encryption system, or 0 if not encrypted yet - attr_reader :cryptid + # @return [Fixnum] the encryption system, or 0 if not encrypted yet + attr_reader :cryptid - # @return [Fixnum] 64-bit padding value - attr_reader :pad + # @return [Fixnum] 64-bit padding value + attr_reader :pad - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=6".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=6".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 24 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 24 - # @api private - def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid, pad) - super(view, cmd, cmdsize) - @cryptoff = cryptoff - @cryptsize = cryptsize - @cryptid = cryptid - @pad = pad + # @api private + def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid, pad) + super(view, cmd, cmdsize) + @cryptoff = cryptoff + @cryptsize = cryptsize + @cryptid = cryptid + @pad = pad + end end - end - # A load command containing the minimum OS version on which the binary - # was built to run. Corresponds to LC_VERSION_MIN_MACOSX and LC_VERSION_MIN_IPHONEOS. - class VersionMinCommand < LoadCommand - # @return [Fixnum] the version X.Y.Z packed as x16.y8.z8 - attr_reader :version + # A load command containing the minimum OS version on which the binary + # was built to run. Corresponds to LC_VERSION_MIN_MACOSX and + # LC_VERSION_MIN_IPHONEOS. + class VersionMinCommand < LoadCommand + # @return [Fixnum] the version X.Y.Z packed as x16.y8.z8 + attr_reader :version - # @return [Fixnum] the SDK version X.Y.Z packed as x16.y8.z8 - attr_reader :sdk + # @return [Fixnum] the SDK version X.Y.Z packed as x16.y8.z8 + attr_reader :sdk - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=4".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=4".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 - # @api private - def initialize(view, cmd, cmdsize, version, sdk) - super(view, cmd, cmdsize) - @version = version - @sdk = sdk - end + # @api private + def initialize(view, cmd, cmdsize, version, sdk) + super(view, cmd, cmdsize) + @version = version + @sdk = sdk + end - # A string representation of the binary's minimum OS version. - # @return [String] a string representing the minimum OS version. - def version_string - binary = "%032b" % version - segs = [ - binary[0..15], binary[16..23], binary[24..31] - ].map { |s| s.to_i(2) } + # A string representation of the binary's minimum OS version. + # @return [String] a string representing the minimum OS version. + def version_string + binary = "%032b" % version + segs = [ + binary[0..15], binary[16..23], binary[24..31] + ].map { |s| s.to_i(2) } - segs.join(".") - end + segs.join(".") + end - # A string representation of the binary's SDK version. - # @return [String] a string representing the SDK version. - def sdk_string - binary = "%032b" % sdk - segs = [ - binary[0..15], binary[16..23], binary[24..31] - ].map { |s| s.to_i(2) } + # A string representation of the binary's SDK version. + # @return [String] a string representing the SDK version. + def sdk_string + binary = "%032b" % sdk + segs = [ + binary[0..15], binary[16..23], binary[24..31] + ].map { |s| s.to_i(2) } - segs.join(".") + segs.join(".") + end end - end - # A load command containing the file offsets and sizes of the new - # compressed form of the information dyld needs to load the image. - # Corresponds to LC_DYLD_INFO and LC_DYLD_INFO_ONLY. - class DyldInfoCommand < LoadCommand - # @return [Fixnum] the file offset to the rebase information - attr_reader :rebase_off + # A load command containing the file offsets and sizes of the new + # compressed form of the information dyld needs to load the image. + # Corresponds to LC_DYLD_INFO and LC_DYLD_INFO_ONLY. + class DyldInfoCommand < LoadCommand + # @return [Fixnum] the file offset to the rebase information + attr_reader :rebase_off - # @return [Fixnum] the size of the rebase information - attr_reader :rebase_size + # @return [Fixnum] the size of the rebase information + attr_reader :rebase_size - # @return [Fixnum] the file offset to the binding information - attr_reader :bind_off + # @return [Fixnum] the file offset to the binding information + attr_reader :bind_off - # @return [Fixnum] the size of the binding information - attr_reader :bind_size + # @return [Fixnum] the size of the binding information + attr_reader :bind_size - # @return [Fixnum] the file offset to the weak binding information - attr_reader :weak_bind_off + # @return [Fixnum] the file offset to the weak binding information + attr_reader :weak_bind_off - # @return [Fixnum] the size of the weak binding information - attr_reader :weak_bind_size + # @return [Fixnum] the size of the weak binding information + attr_reader :weak_bind_size - # @return [Fixnum] the file offset to the lazy binding information - attr_reader :lazy_bind_off + # @return [Fixnum] the file offset to the lazy binding information + attr_reader :lazy_bind_off - # @return [Fixnum] the size of the lazy binding information - attr_reader :lazy_bind_size + # @return [Fixnum] the size of the lazy binding information + attr_reader :lazy_bind_size - # @return [Fixnum] the file offset to the export information - attr_reader :export_off + # @return [Fixnum] the file offset to the export information + attr_reader :export_off - # @return [Fixnum] the size of the export information - attr_reader :export_size + # @return [Fixnum] the size of the export information + attr_reader :export_size - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=12".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=12".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 48 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 48 - # @api private - def initialize(view, cmd, cmdsize, rebase_off, rebase_size, bind_off, - bind_size, weak_bind_off, weak_bind_size, lazy_bind_off, - lazy_bind_size, export_off, export_size) - super(view, cmd, cmdsize) - @rebase_off = rebase_off - @rebase_size = rebase_size - @bind_off = bind_off - @bind_size = bind_size - @weak_bind_off = weak_bind_off - @weak_bind_size = weak_bind_size - @lazy_bind_off = lazy_bind_off - @lazy_bind_size = lazy_bind_size - @export_off = export_off - @export_size = export_size + # @api private + def initialize(view, cmd, cmdsize, rebase_off, rebase_size, bind_off, + bind_size, weak_bind_off, weak_bind_size, lazy_bind_off, + lazy_bind_size, export_off, export_size) + super(view, cmd, cmdsize) + @rebase_off = rebase_off + @rebase_size = rebase_size + @bind_off = bind_off + @bind_size = bind_size + @weak_bind_off = weak_bind_off + @weak_bind_size = weak_bind_size + @lazy_bind_off = lazy_bind_off + @lazy_bind_size = lazy_bind_size + @export_off = export_off + @export_size = export_size + end end - end - # A load command containing linker options embedded in object files. - # Corresponds to LC_LINKER_OPTION. - class LinkerOptionCommand < LoadCommand - # @return [Fixnum] the number of strings - attr_reader :count + # A load command containing linker options embedded in object files. + # Corresponds to LC_LINKER_OPTION. + class LinkerOptionCommand < LoadCommand + # @return [Fixnum] the number of strings + attr_reader :count - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 - # @api private - def initialize(view, cmd, cmdsize, count) - super(view, cmd, cmdsize) - @count = count + # @api private + def initialize(view, cmd, cmdsize, count) + super(view, cmd, cmdsize) + @count = count + end end - end - # A load command specifying the offset of main(). Corresponds to LC_MAIN. - class EntryPointCommand < LoadCommand - # @return [Fixnum] the file (__TEXT) offset of main() - attr_reader :entryoff + # A load command specifying the offset of main(). Corresponds to LC_MAIN. + class EntryPointCommand < LoadCommand + # @return [Fixnum] the file (__TEXT) offset of main() + attr_reader :entryoff - # @return [Fixnum] if not 0, the initial stack size. - attr_reader :stacksize + # @return [Fixnum] if not 0, the initial stack size. + attr_reader :stacksize - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2Q=2".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2Q=2".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 24 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 24 - # @api private - def initialize(view, cmd, cmdsize, entryoff, stacksize) - super(view, cmd, cmdsize) - @entryoff = entryoff - @stacksize = stacksize + # @api private + def initialize(view, cmd, cmdsize, entryoff, stacksize) + super(view, cmd, cmdsize) + @entryoff = entryoff + @stacksize = stacksize + end end - end - # A load command specifying the version of the sources used to build the - # binary. Corresponds to LC_SOURCE_VERSION. - class SourceVersionCommand < LoadCommand - # @return [Fixnum] the version packed as a24.b10.c10.d10.e10 - attr_reader :version + # A load command specifying the version of the sources used to build the + # binary. Corresponds to LC_SOURCE_VERSION. + class SourceVersionCommand < LoadCommand + # @return [Fixnum] the version packed as a24.b10.c10.d10.e10 + attr_reader :version - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2Q=1".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2Q=1".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 - # @api private - def initialize(view, cmd, cmdsize, version) - super(view, cmd, cmdsize) - @version = version - end + # @api private + def initialize(view, cmd, cmdsize, version) + super(view, cmd, cmdsize) + @version = version + end - # A string representation of the sources used to build the binary. - # @return [String] a string representation of the version - def version_string - binary = "%064b" % version - segs = [ - binary[0..23], binary[24..33], binary[34..43], binary[44..53], - binary[54..63] - ].map { |s| s.to_i(2) } + # A string representation of the sources used to build the binary. + # @return [String] a string representation of the version + def version_string + binary = "%064b" % version + segs = [ + binary[0..23], binary[24..33], binary[34..43], binary[44..53], + binary[54..63] + ].map { |s| s.to_i(2) } - segs.join(".") + segs.join(".") + end end - end - # An obsolete load command containing the offset and size of the (GNU style) - # symbol table information. Corresponds to LC_SYMSEG. - class SymsegCommand < LoadCommand - # @return [Fixnum] the offset to the symbol segment - attr_reader :offset + # An obsolete load command containing the offset and size of the (GNU style) + # symbol table information. Corresponds to LC_SYMSEG. + class SymsegCommand < LoadCommand + # @return [Fixnum] the offset to the symbol segment + attr_reader :offset - # @return [Fixnum] the size of the symbol segment in bytes - attr_reader :size + # @return [Fixnum] the size of the symbol segment in bytes + attr_reader :size - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=4".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=4".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 - # @api private - def initialize(view, cmd, cmdsize, offset, size) - super(view, cmd, cmdsize) - @offset = offset - @size = size + # @api private + def initialize(view, cmd, cmdsize, offset, size) + super(view, cmd, cmdsize) + @offset = offset + @size = size + end end - end - # An obsolete load command containing a free format string table. Each string - # is null-terminated and the command is zero-padded to a multiple of 4. - # Corresponds to LC_IDENT. - class IdentCommand < LoadCommand - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2".freeze + # An obsolete load command containing a free format string table. Each + # string is null-terminated and the command is zero-padded to a multiple of + # 4. Corresponds to LC_IDENT. + class IdentCommand < LoadCommand + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 8 - end + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 8 + end - # An obsolete load command containing the path to a file to be loaded into - # memory. Corresponds to LC_FVMFILE. - class FvmfileCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the pathname of the file being loaded - attr_reader :name + # An obsolete load command containing the path to a file to be loaded into + # memory. Corresponds to LC_FVMFILE. + class FvmfileCommand < LoadCommand + # @return [LCStr] the pathname of the file being loaded + attr_reader :name - # @return [Fixnum] the virtual address being loaded at - attr_reader :header_addr + # @return [Fixnum] the virtual address being loaded at + attr_reader :header_addr - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=4".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=4".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 - def initialize(view, cmd, cmdsize, name, header_addr) - super(view, cmd, cmdsize) - @name = LCStr.new(self, name) - @header_addr = header_addr + def initialize(view, cmd, cmdsize, name, header_addr) + super(view, cmd, cmdsize) + @name = LCStr.new(self, name) + @header_addr = header_addr + end end - end - # An obsolete load command containing the path to a library to be loaded into - # memory. Corresponds to LC_LOADFVMLIB and LC_IDFVMLIB. - class FvmlibCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the library's target pathname - attr_reader :name + # An obsolete load command containing the path to a library to be loaded + # into memory. Corresponds to LC_LOADFVMLIB and LC_IDFVMLIB. + class FvmlibCommand < LoadCommand + # @return [LCStr] the library's target pathname + attr_reader :name - # @return [Fixnum] the library's minor version number - attr_reader :minor_version + # @return [Fixnum] the library's minor version number + attr_reader :minor_version - # @return [Fixnum] the library's header address - attr_reader :header_addr + # @return [Fixnum] the library's header address + attr_reader :header_addr - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=5".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=5".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 20 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 20 - def initialize(view, cmd, cmdsize, name, minor_version, header_addr) - super(view, cmd, cmdsize) - @name = LCStr.new(self, name) - @minor_version = minor_version - @header_addr = header_addr + def initialize(view, cmd, cmdsize, name, minor_version, header_addr) + super(view, cmd, cmdsize) + @name = LCStr.new(self, name) + @minor_version = minor_version + @header_addr = header_addr + end end end end diff --git a/Library/Homebrew/vendor/macho/macho/macho_file.rb b/Library/Homebrew/vendor/macho/macho/macho_file.rb index 8ece62672..7693ab0dd 100644 --- a/Library/Homebrew/vendor/macho/macho/macho_file.rb +++ b/Library/Homebrew/vendor/macho/macho/macho_file.rb @@ -1,27 +1,33 @@ +require "forwardable" + module MachO # Represents a Mach-O file, which contains a header and load commands # as well as binary executable instructions. Mach-O binaries are # architecture specific. # @see https://en.wikipedia.org/wiki/Mach-O - # @see MachO::FatFile + # @see FatFile class MachOFile - # @return [String] the filename loaded from, or nil if loaded from a binary string + extend Forwardable + + # @return [String] the filename loaded from, or nil if loaded from a binary + # string attr_accessor :filename # @return [Symbol] the endianness of the file, :big or :little attr_reader :endianness - # @return [MachO::MachHeader] if the Mach-O is 32-bit - # @return [MachO::MachHeader64] if the Mach-O is 64-bit + # @return [Headers::MachHeader] if the Mach-O is 32-bit + # @return [Headers::MachHeader64] if the Mach-O is 64-bit attr_reader :header - # @return [Array<MachO::LoadCommand>] an array of the file's load commands + # @return [Array<LoadCommands::LoadCommand>] an array of the file's load + # commands # @note load commands are provided in order of ascending offset. attr_reader :load_commands # Creates a new MachOFile instance from a binary string. # @param bin [String] a binary string containing raw Mach-O data - # @return [MachO::MachOFile] a new MachOFile + # @return [MachOFile] a new MachOFile def self.new_from_bin(bin) instance = allocate instance.initialize_from_bin(bin) @@ -55,109 +61,63 @@ module MachO @raw_data end - # @return [Boolean] true if the Mach-O has 32-bit magic, false otherwise - def magic32? - Utils.magic32?(header.magic) - end - - # @return [Boolean] true if the Mach-O has 64-bit magic, false otherwise - def magic64? - Utils.magic64?(header.magic) - end - - # @return [Fixnum] the file's internal alignment - def alignment - magic32? ? 4 : 8 - end - - # @return [Boolean] true if the file is of type `MH_OBJECT`, false otherwise - def object? - header.filetype == MH_OBJECT - end - - # @return [Boolean] true if the file is of type `MH_EXECUTE`, false otherwise - def executable? - header.filetype == MH_EXECUTE - end - - # @return [Boolean] true if the file is of type `MH_FVMLIB`, false otherwise - def fvmlib? - header.filetype == MH_FVMLIB - end - - # @return [Boolean] true if the file is of type `MH_CORE`, false otherwise - def core? - header.filetype == MH_CORE - end - - # @return [Boolean] true if the file is of type `MH_PRELOAD`, false otherwise - def preload? - header.filetype == MH_PRELOAD - end - - # @return [Boolean] true if the file is of type `MH_DYLIB`, false otherwise - def dylib? - header.filetype == MH_DYLIB - end - - # @return [Boolean] true if the file is of type `MH_DYLINKER`, false otherwise - def dylinker? - header.filetype == MH_DYLINKER - end - - # @return [Boolean] true if the file is of type `MH_BUNDLE`, false otherwise - def bundle? - header.filetype == MH_BUNDLE - end - - # @return [Boolean] true if the file is of type `MH_DSYM`, false otherwise - def dsym? - header.filetype == MH_DSYM - end - - # @return [Boolean] true if the file is of type `MH_KEXT_BUNDLE`, false otherwise - def kext? - header.filetype == MH_KEXT_BUNDLE - end - - # @return [Fixnum] the file's magic number - def magic - header.magic - end + # @!method magic + # @return (see MachO::Headers::MachHeader#magic) + # @!method ncmds + # @return (see MachO::Headers::MachHeader#ncmds) + # @!method sizeofcmds + # @return (see MachO::Headers::MachHeader#sizeofcmds) + # @!method flags + # @return (see MachO::Headers::MachHeader#flags) + # @!method object? + # @return (see MachO::Headers::MachHeader#object?) + # @!method executable? + # @return (see MachO::Headers::MachHeader#executable?) + # @!method fvmlib? + # @return (see MachO::Headers::MachHeader#fvmlib?) + # @!method core? + # @return (see MachO::Headers::MachHeader#core?) + # @!method preload? + # @return (see MachO::Headers::MachHeader#preload?) + # @!method dylib? + # @return (see MachO::Headers::MachHeader#dylib?) + # @!method dylinker? + # @return (see MachO::Headers::MachHeader#dylinker?) + # @!method bundle? + # @return (see MachO::Headers::MachHeader#bundle?) + # @!method dsym? + # @return (see MachO::Headers::MachHeader#dsym?) + # @!method kext? + # @return (see MachO::Headers::MachHeader#kext?) + # @!method magic32? + # @return (see MachO::Headers::MachHeader#magic32?) + # @!method magic64? + # @return (see MachO::Headers::MachHeader#magic64?) + # @!method alignment + # @return (see MachO::Headers::MachHeader#alignment) + def_delegators :header, :magic, :ncmds, :sizeofcmds, :flags, :object?, + :executable?, :fvmlib?, :core?, :preload?, :dylib?, + :dylinker?, :bundle?, :dsym?, :kext?, :magic32?, :magic64?, + :alignment # @return [String] a string representation of the file's magic number def magic_string - MH_MAGICS[magic] + Headers::MH_MAGICS[magic] end # @return [Symbol] a string representation of the Mach-O's filetype def filetype - MH_FILETYPES[header.filetype] + Headers::MH_FILETYPES[header.filetype] end # @return [Symbol] a symbol representation of the Mach-O's CPU type def cputype - CPU_TYPES[header.cputype] + Headers::CPU_TYPES[header.cputype] end # @return [Symbol] a symbol representation of the Mach-O's CPU subtype def cpusubtype - CPU_SUBTYPES[header.cputype][header.cpusubtype] - end - - # @return [Fixnum] the number of load commands in the Mach-O's header - def ncmds - header.ncmds - end - - # @return [Fixnum] the size of all load commands, in bytes - def sizeofcmds - header.sizeofcmds - end - - # @return [Fixnum] execution flags set by the linker - def flags - header.flags + Headers::CPU_SUBTYPES[header.cputype][header.cpusubtype] end # All load commands of a given name. @@ -165,7 +125,8 @@ module MachO # file.command("LC_LOAD_DYLIB") # file[:LC_LOAD_DYLIB] # @param [String, Symbol] name the load command ID - # @return [Array<MachO::LoadCommand>] an array of LoadCommands corresponding to `name` + # @return [Array<LoadCommands::LoadCommand>] an array of load commands + # corresponding to `name` def command(name) load_commands.select { |lc| lc.type == name.to_sym } end @@ -174,16 +135,16 @@ module MachO # Inserts a load command at the given offset. # @param offset [Fixnum] the offset to insert at - # @param lc [MachO::LoadCommand] the load command to insert + # @param lc [LoadCommands::LoadCommand] the load command to insert # @param options [Hash] # @option options [Boolean] :repopulate (true) whether or not to repopulate # the instance fields - # @raise [MachO::OffsetInsertionError] if the offset is not in the load command region - # @raise [MachO::HeaderPadError] if the new command exceeds the header pad buffer + # @raise [OffsetInsertionError] if the offset is not in the load command region + # @raise [HeaderPadError] if the new command exceeds the header pad buffer # @note Calling this method with an arbitrary offset in the load command # region **will leave the object in an inconsistent state**. def insert_command(offset, lc, options = {}) - context = LoadCommand::SerializationContext.context_for(self) + context = LoadCommands::LoadCommand::SerializationContext.context_for(self) cmd_raw = lc.serialize(context) if offset < header.class.bytesize || offset + cmd_raw.bytesize > low_fileoff @@ -207,14 +168,14 @@ module MachO end # Replace a load command with another command in the Mach-O, preserving location. - # @param old_lc [MachO::LoadCommand] the load command being replaced - # @param new_lc [MachO::LoadCommand] the load command being added + # @param old_lc [LoadCommands::LoadCommand] the load command being replaced + # @param new_lc [LoadCommands::LoadCommand] the load command being added # @return [void] - # @raise [MachO::HeaderPadError] if the new command exceeds the header pad buffer - # @see {#insert_command} + # @raise [HeaderPadError] if the new command exceeds the header pad buffer + # @see #insert_command # @note This is public, but methods like {#dylib_id=} should be preferred. def replace_command(old_lc, new_lc) - context = LoadCommand::SerializationContext.context_for(self) + context = LoadCommands::LoadCommand::SerializationContext.context_for(self) cmd_raw = new_lc.serialize(context) new_sizeofcmds = sizeofcmds + cmd_raw.bytesize - old_lc.cmdsize if header.class.bytesize + new_sizeofcmds > low_fileoff @@ -226,12 +187,12 @@ module MachO end # Appends a new load command to the Mach-O. - # @param lc [MachO::LoadCommand] the load command being added + # @param lc [LoadCommands::LoadCommand] the load command being added # @param options [Hash] # @option options [Boolean] :repopulate (true) whether or not to repopulate # the instance fields # @return [void] - # @see {#insert_command} + # @see #insert_command # @note This is public, but methods like {#add_rpath} should be preferred. # Setting `repopulate` to false **will leave the instance in an # inconsistent state** unless {#populate_fields} is called **immediately** @@ -241,7 +202,7 @@ module MachO end # Delete a load command from the Mach-O. - # @param lc [MachO::LoadCommand] the load command being deleted + # @param lc [LoadCommands::LoadCommand] the load command being deleted # @param options [Hash] # @option options [Boolean] :repopulate (true) whether or not to repopulate # the instance fields @@ -275,14 +236,14 @@ module MachO end # All load commands responsible for loading dylibs. - # @return [Array<MachO::DylibCommand>] an array of DylibCommands + # @return [Array<LoadCommands::DylibCommand>] an array of DylibCommands def dylib_load_commands - load_commands.select { |lc| DYLIB_LOAD_COMMANDS.include?(lc.type) } + load_commands.select { |lc| LoadCommands::DYLIB_LOAD_COMMANDS.include?(lc.type) } end # All segment load commands in the Mach-O. - # @return [Array<MachO::SegmentCommand>] if the Mach-O is 32-bit - # @return [Array<MachO::SegmentCommand64>] if the Mach-O is 64-bit + # @return [Array<LoadCommands::SegmentCommand>] if the Mach-O is 32-bit + # @return [Array<LoadCommands::SegmentCommand64>] if the Mach-O is 64-bit def segments if magic32? command(:LC_SEGMENT) @@ -319,10 +280,10 @@ module MachO old_lc = command(:LC_ID_DYLIB).first raise DylibIdMissingError unless old_lc - new_lc = LoadCommand.create(:LC_ID_DYLIB, new_id, - old_lc.timestamp, - old_lc.current_version, - old_lc.compatibility_version) + new_lc = LoadCommands::LoadCommand.create(:LC_ID_DYLIB, new_id, + old_lc.timestamp, + old_lc.current_version, + old_lc.compatibility_version) replace_command(old_lc, new_lc) end @@ -341,22 +302,22 @@ module MachO # Changes the shared library `old_name` to `new_name` # @example - # file.change_install_name("/usr/lib/libWhatever.dylib", "/usr/local/lib/libWhatever2.dylib") + # file.change_install_name("abc.dylib", "def.dylib") # @param old_name [String] the shared library's old name # @param new_name [String] the shared library's new name # @param _options [Hash] # @return [void] - # @raise [MachO::DylibUnknownError] if no shared library has the old name + # @raise [DylibUnknownError] if no shared library has the old name # @note `_options` is currently unused and is provided for signature # compatibility with {MachO::FatFile#change_install_name} def change_install_name(old_name, new_name, _options = {}) old_lc = dylib_load_commands.find { |d| d.name.to_s == old_name } raise DylibUnknownError, old_name if old_lc.nil? - new_lc = LoadCommand.create(old_lc.type, new_name, - old_lc.timestamp, - old_lc.current_version, - old_lc.compatibility_version) + new_lc = LoadCommands::LoadCommand.create(old_lc.type, new_name, + old_lc.timestamp, + old_lc.current_version, + old_lc.compatibility_version) replace_command(old_lc, new_lc) end @@ -376,8 +337,8 @@ module MachO # @param new_path [String] the new runtime path # @param _options [Hash] # @return [void] - # @raise [MachO::RpathUnknownError] if no such old runtime path exists - # @raise [MachO::RpathExistsError] if the new runtime path already exists + # @raise [RpathUnknownError] if no such old runtime path exists + # @raise [RpathExistsError] if the new runtime path already exists # @note `_options` is currently unused and is provided for signature # compatibility with {MachO::FatFile#change_rpath} def change_rpath(old_path, new_path, _options = {}) @@ -385,7 +346,7 @@ module MachO raise RpathUnknownError, old_path if old_lc.nil? raise RpathExistsError, new_path if rpaths.include?(new_path) - new_lc = LoadCommand.create(:LC_RPATH, new_path) + new_lc = LoadCommands::LoadCommand.create(:LC_RPATH, new_path) delete_rpath(old_path) insert_command(old_lc.view.offset, new_lc) @@ -399,13 +360,13 @@ module MachO # @param path [String] the new runtime path # @param _options [Hash] # @return [void] - # @raise [MachO::RpathExistsError] if the runtime path already exists + # @raise [RpathExistsError] if the runtime path already exists # @note `_options` is currently unused and is provided for signature # compatibility with {MachO::FatFile#add_rpath} def add_rpath(path, _options = {}) raise RpathExistsError, path if rpaths.include?(path) - rpath_cmd = LoadCommand.create(:LC_RPATH, path) + rpath_cmd = LoadCommands::LoadCommand.create(:LC_RPATH, path) add_command(rpath_cmd) end @@ -417,7 +378,7 @@ module MachO # @param path [String] the runtime path to delete # @param _options [Hash] # @return void - # @raise [MachO::RpathUnknownError] if no such runtime path exists + # @raise [RpathUnknownError] if no such runtime path exists # @note `_options` is currently unused and is provided for signature # compatibility with {MachO::FatFile#delete_rpath} def delete_rpath(path, _options = {}) @@ -431,15 +392,6 @@ module MachO populate_fields end - # All sections of the segment `segment`. - # @param segment [MachO::SegmentCommand, MachO::SegmentCommand64] the segment being inspected - # @return [Array<MachO::Section>] if the Mach-O is 32-bit - # @return [Array<MachO::Section64>] if the Mach-O is 64-bit - # @deprecated use {MachO::SegmentCommand#sections} instead - def sections(segment) - segment.sections - end - # Write all Mach-O data to the given filename. # @param filename [String] the file to write to # @return [void] @@ -449,7 +401,7 @@ module MachO # Write all Mach-O data to the file used to initialize the instance. # @return [void] - # @raise [MachO::MachOError] if the instance was initialized without a file + # @raise [MachOError] if the instance was initialized without a file # @note Overwrites all data in the file! def write! if @filename.nil? @@ -462,16 +414,16 @@ module MachO private # The file's Mach-O header structure. - # @return [MachO::MachHeader] if the Mach-O is 32-bit - # @return [MachO::MachHeader64] if the Mach-O is 64-bit - # @raise [MachO::TruncatedFileError] if the file is too small to have a valid header + # @return [Headers::MachHeader] if the Mach-O is 32-bit + # @return [Headers::MachHeader64] if the Mach-O is 64-bit + # @raise [TruncatedFileError] if the file is too small to have a valid header # @api private def populate_mach_header # the smallest Mach-O header is 28 bytes raise TruncatedFileError if @raw_data.size < 28 magic = populate_and_check_magic - mh_klass = Utils.magic32?(magic) ? MachHeader : MachHeader64 + mh_klass = Utils.magic32?(magic) ? Headers::MachHeader : Headers::MachHeader64 mh = mh_klass.new_from_bin(endianness, @raw_data[0, mh_klass.bytesize]) check_cputype(mh.cputype) @@ -483,8 +435,8 @@ module MachO # Read just the file's magic number and check its validity. # @return [Fixnum] the magic - # @raise [MachO::MagicError] if the magic is not valid Mach-O magic - # @raise [MachO::FatBinaryError] if the magic is for a Fat file + # @raise [MagicError] if the magic is not valid Mach-O magic + # @raise [FatBinaryError] if the magic is for a Fat file # @api private def populate_and_check_magic magic = @raw_data[0..3].unpack("N").first @@ -499,32 +451,32 @@ module MachO # Check the file's CPU type. # @param cputype [Fixnum] the CPU type - # @raise [MachO::CPUTypeError] if the CPU type is unknown + # @raise [CPUTypeError] if the CPU type is unknown # @api private def check_cputype(cputype) - raise CPUTypeError, cputype unless CPU_TYPES.key?(cputype) + raise CPUTypeError, cputype unless Headers::CPU_TYPES.key?(cputype) end # Check the file's CPU type/subtype pair. # @param cpusubtype [Fixnum] the CPU subtype - # @raise [MachO::CPUSubtypeError] if the CPU sub-type is unknown + # @raise [CPUSubtypeError] if the CPU sub-type is unknown # @api private def check_cpusubtype(cputype, cpusubtype) # Only check sub-type w/o capability bits (see `populate_mach_header`). - raise CPUSubtypeError.new(cputype, cpusubtype) unless CPU_SUBTYPES[cputype].key?(cpusubtype) + raise CPUSubtypeError.new(cputype, cpusubtype) unless Headers::CPU_SUBTYPES[cputype].key?(cpusubtype) end # Check the file's type. # @param filetype [Fixnum] the file type - # @raise [MachO::FiletypeError] if the file type is unknown + # @raise [FiletypeError] if the file type is unknown # @api private def check_filetype(filetype) - raise FiletypeError, filetype unless MH_FILETYPES.key?(filetype) + raise FiletypeError, filetype unless Headers::MH_FILETYPES.key?(filetype) end # All load commands in the file. - # @return [Array<MachO::LoadCommand>] an array of load commands - # @raise [MachO::LoadCommandError] if an unknown load command is encountered + # @return [Array<LoadCommands::LoadCommand>] an array of load commands + # @raise [LoadCommandError] if an unknown load command is encountered # @api private def populate_load_commands offset = header.class.bytesize @@ -533,13 +485,13 @@ module MachO header.ncmds.times do fmt = Utils.specialize_format("L=", endianness) cmd = @raw_data.slice(offset, 4).unpack(fmt).first - cmd_sym = LOAD_COMMANDS[cmd] + cmd_sym = LoadCommands::LOAD_COMMANDS[cmd] raise LoadCommandError, cmd if cmd_sym.nil? # why do I do this? i don't like declaring constants below # classes, and i need them to resolve... - klass = MachO.const_get LC_STRUCTURES[cmd_sym] + klass = LoadCommands.const_get LoadCommands::LC_STRUCTURES[cmd_sym] view = MachOView.new(@raw_data, endianness, offset) command = klass.new_from_bin(view) diff --git a/Library/Homebrew/vendor/macho/macho/open.rb b/Library/Homebrew/vendor/macho/macho/open.rb deleted file mode 100644 index 103f61741..000000000 --- a/Library/Homebrew/vendor/macho/macho/open.rb +++ /dev/null @@ -1,25 +0,0 @@ -module MachO - # Opens the given filename as a MachOFile or FatFile, depending on its magic. - # @param filename [String] the file being opened - # @return [MachO::MachOFile] if the file is a Mach-O - # @return [MachO::FatFile] if the file is a Fat file - # @raise [ArgumentError] if the given file does not exist - # @raise [MachO::TruncatedFileError] if the file is too small to have a valid header - # @raise [MachO::MagicError] if the file's magic is not valid Mach-O magic - def self.open(filename) - raise ArgumentError, "#{filename}: no such file" unless File.file?(filename) - raise TruncatedFileError unless File.stat(filename).size >= 4 - - magic = File.open(filename, "rb") { |f| f.read(4) }.unpack("N").first - - if Utils.fat_magic?(magic) - file = FatFile.new(filename) - elsif Utils.magic?(magic) - file = MachOFile.new(filename) - else - raise MagicError, magic - end - - file - end -end diff --git a/Library/Homebrew/vendor/macho/macho/sections.rb b/Library/Homebrew/vendor/macho/macho/sections.rb index bd6218bb5..1e69e0b61 100644 --- a/Library/Homebrew/vendor/macho/macho/sections.rb +++ b/Library/Homebrew/vendor/macho/macho/sections.rb @@ -1,170 +1,176 @@ module MachO - # type mask - SECTION_TYPE = 0x000000ff - - # attributes mask - SECTION_ATTRIBUTES = 0xffffff00 - - # user settable attributes mask - SECTION_ATTRIBUTES_USR = 0xff000000 - - # system settable attributes mask - SECTION_ATTRIBUTES_SYS = 0x00ffff00 - - # association of section flag symbols to values - # @api private - SECTION_FLAGS = { - :S_REGULAR => 0x0, - :S_ZEROFILL => 0x1, - :S_CSTRING_LITERALS => 0x2, - :S_4BYTE_LITERALS => 0x3, - :S_8BYTE_LITERALS => 0x4, - :S_LITERAL_POINTERS => 0x5, - :S_NON_LAZY_SYMBOL_POINTERS => 0x6, - :S_LAZY_SYMBOL_POINTERS => 0x7, - :S_SYMBOL_STUBS => 0x8, - :S_MOD_INIT_FUNC_POINTERS => 0x9, - :S_MOD_TERM_FUNC_POINTERS => 0xa, - :S_COALESCED => 0xb, - :S_GB_ZEROFILE => 0xc, - :S_INTERPOSING => 0xd, - :S_16BYTE_LITERALS => 0xe, - :S_DTRACE_DOF => 0xf, - :S_LAZY_DYLIB_SYMBOL_POINTERS => 0x10, - :S_THREAD_LOCAL_REGULAR => 0x11, - :S_THREAD_LOCAL_ZEROFILL => 0x12, - :S_THREAD_LOCAL_VARIABLES => 0x13, - :S_THREAD_LOCAL_VARIABLE_POINTERS => 0x14, - :S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => 0x15, - :S_ATTR_PURE_INSTRUCTIONS => 0x80000000, - :S_ATTR_NO_TOC => 0x40000000, - :S_ATTR_STRIP_STATIC_SYMS => 0x20000000, - :S_ATTR_NO_DEAD_STRIP => 0x10000000, - :S_ATTR_LIVE_SUPPORT => 0x08000000, - :S_ATTR_SELF_MODIFYING_CODE => 0x04000000, - :S_ATTR_DEBUG => 0x02000000, - :S_ATTR_SOME_INSTRUCTIONS => 0x00000400, - :S_ATTR_EXT_RELOC => 0x00000200, - :S_ATTR_LOC_RELOC => 0x00000100, - }.freeze - - # association of section name symbols to names - # @api private - SECTION_NAMES = { - :SECT_TEXT => "__text", - :SECT_FVMLIB_INIT0 => "__fvmlib_init0", - :SECT_FVMLIB_INIT1 => "__fvmlib_init1", - :SECT_DATA => "__data", - :SECT_BSS => "__bss", - :SECT_COMMON => "__common", - :SECT_OBJC_SYMBOLS => "__symbol_table", - :SECT_OBJC_MODULES => "__module_info", - :SECT_OBJC_STRINGS => "__selector_strs", - :SECT_OBJC_REFS => "__selector_refs", - :SECT_ICON_HEADER => "__header", - :SECT_ICON_TIFF => "__tiff", - }.freeze - - # Represents a section of a segment for 32-bit architectures. - class Section < MachOStructure - # @return [String] the name of the section, including null pad bytes - attr_reader :sectname - - # @return [String] the name of the segment's section, including null pad bytes - attr_reader :segname - - # @return [Fixnum] the memory address of the section - attr_reader :addr - - # @return [Fixnum] the size, in bytes, of the section - attr_reader :size - - # @return [Fixnum] the file offset of the section - attr_reader :offset - - # @return [Fixnum] the section alignment (power of 2) of the section - attr_reader :align - - # @return [Fixnum] the file offset of the section's relocation entries - attr_reader :reloff - - # @return [Fixnum] the number of relocation entries - attr_reader :nreloc - - # @return [Fixnum] flags for type and attributes of the section - attr_reader :flags - - # @return [void] reserved (for offset or index) - attr_reader :reserved1 - - # @return [void] reserved (for count or sizeof) - attr_reader :reserved2 - - # @see MachOStructure::FORMAT - FORMAT = "a16a16L=9".freeze - - # @see MachOStructure::SIZEOF - SIZEOF = 68 + # Classes and constants for parsing sections in Mach-O binaries. + module Sections + # type mask + SECTION_TYPE = 0x000000ff - # @api private - def initialize(sectname, segname, addr, size, offset, align, reloff, - nreloc, flags, reserved1, reserved2) - @sectname = sectname - @segname = segname - @addr = addr - @size = size - @offset = offset - @align = align - @reloff = reloff - @nreloc = nreloc - @flags = flags - @reserved1 = reserved1 - @reserved2 = reserved2 - end + # attributes mask + SECTION_ATTRIBUTES = 0xffffff00 - # @return [String] the section's name, with any trailing NULL characters removed - def section_name - sectname.delete("\x00") - end - - # @return [String] the parent segment's name, with any trailing NULL characters removed - def segment_name - segname.delete("\x00") - end + # user settable attributes mask + SECTION_ATTRIBUTES_USR = 0xff000000 - # @return [Boolean] true if the section has no contents (i.e, `size` is 0) - def empty? - size.zero? - end + # system settable attributes mask + SECTION_ATTRIBUTES_SYS = 0x00ffff00 - # @example - # puts "this section is regular" if sect.flag?(:S_REGULAR) - # @param flag [Symbol] a section flag symbol - # @return [Boolean] true if `flag` is present in the section's flag field - def flag?(flag) - flag = SECTION_FLAGS[flag] - return false if flag.nil? - flags & flag == flag + # association of section flag symbols to values + # @api private + SECTION_FLAGS = { + :S_REGULAR => 0x0, + :S_ZEROFILL => 0x1, + :S_CSTRING_LITERALS => 0x2, + :S_4BYTE_LITERALS => 0x3, + :S_8BYTE_LITERALS => 0x4, + :S_LITERAL_POINTERS => 0x5, + :S_NON_LAZY_SYMBOL_POINTERS => 0x6, + :S_LAZY_SYMBOL_POINTERS => 0x7, + :S_SYMBOL_STUBS => 0x8, + :S_MOD_INIT_FUNC_POINTERS => 0x9, + :S_MOD_TERM_FUNC_POINTERS => 0xa, + :S_COALESCED => 0xb, + :S_GB_ZEROFILE => 0xc, + :S_INTERPOSING => 0xd, + :S_16BYTE_LITERALS => 0xe, + :S_DTRACE_DOF => 0xf, + :S_LAZY_DYLIB_SYMBOL_POINTERS => 0x10, + :S_THREAD_LOCAL_REGULAR => 0x11, + :S_THREAD_LOCAL_ZEROFILL => 0x12, + :S_THREAD_LOCAL_VARIABLES => 0x13, + :S_THREAD_LOCAL_VARIABLE_POINTERS => 0x14, + :S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => 0x15, + :S_ATTR_PURE_INSTRUCTIONS => 0x80000000, + :S_ATTR_NO_TOC => 0x40000000, + :S_ATTR_STRIP_STATIC_SYMS => 0x20000000, + :S_ATTR_NO_DEAD_STRIP => 0x10000000, + :S_ATTR_LIVE_SUPPORT => 0x08000000, + :S_ATTR_SELF_MODIFYING_CODE => 0x04000000, + :S_ATTR_DEBUG => 0x02000000, + :S_ATTR_SOME_INSTRUCTIONS => 0x00000400, + :S_ATTR_EXT_RELOC => 0x00000200, + :S_ATTR_LOC_RELOC => 0x00000100, + }.freeze + + # association of section name symbols to names + # @api private + SECTION_NAMES = { + :SECT_TEXT => "__text", + :SECT_FVMLIB_INIT0 => "__fvmlib_init0", + :SECT_FVMLIB_INIT1 => "__fvmlib_init1", + :SECT_DATA => "__data", + :SECT_BSS => "__bss", + :SECT_COMMON => "__common", + :SECT_OBJC_SYMBOLS => "__symbol_table", + :SECT_OBJC_MODULES => "__module_info", + :SECT_OBJC_STRINGS => "__selector_strs", + :SECT_OBJC_REFS => "__selector_refs", + :SECT_ICON_HEADER => "__header", + :SECT_ICON_TIFF => "__tiff", + }.freeze + + # Represents a section of a segment for 32-bit architectures. + class Section < MachOStructure + # @return [String] the name of the section, including null pad bytes + attr_reader :sectname + + # @return [String] the name of the segment's section, including null + # pad bytes + attr_reader :segname + + # @return [Fixnum] the memory address of the section + attr_reader :addr + + # @return [Fixnum] the size, in bytes, of the section + attr_reader :size + + # @return [Fixnum] the file offset of the section + attr_reader :offset + + # @return [Fixnum] the section alignment (power of 2) of the section + attr_reader :align + + # @return [Fixnum] the file offset of the section's relocation entries + attr_reader :reloff + + # @return [Fixnum] the number of relocation entries + attr_reader :nreloc + + # @return [Fixnum] flags for type and attributes of the section + attr_reader :flags + + # @return [void] reserved (for offset or index) + attr_reader :reserved1 + + # @return [void] reserved (for count or sizeof) + attr_reader :reserved2 + + # @see MachOStructure::FORMAT + FORMAT = "a16a16L=9".freeze + + # @see MachOStructure::SIZEOF + SIZEOF = 68 + + # @api private + def initialize(sectname, segname, addr, size, offset, align, reloff, + nreloc, flags, reserved1, reserved2) + @sectname = sectname + @segname = segname + @addr = addr + @size = size + @offset = offset + @align = align + @reloff = reloff + @nreloc = nreloc + @flags = flags + @reserved1 = reserved1 + @reserved2 = reserved2 + end + + # @return [String] the section's name, with any trailing NULL characters + # removed + def section_name + sectname.delete("\x00") + end + + # @return [String] the parent segment's name, with any trailing NULL + # characters removed + def segment_name + segname.delete("\x00") + end + + # @return [Boolean] whether the section is empty (i.e, {size} is 0) + def empty? + size.zero? + end + + # @example + # puts "this section is regular" if sect.flag?(:S_REGULAR) + # @param flag [Symbol] a section flag symbol + # @return [Boolean] whether the flag is present in the section's {flags} + def flag?(flag) + flag = SECTION_FLAGS[flag] + return false if flag.nil? + flags & flag == flag + end end - end - # Represents a section of a segment for 64-bit architectures. - class Section64 < Section - # @return [void] reserved - attr_reader :reserved3 + # Represents a section of a segment for 64-bit architectures. + class Section64 < Section + # @return [void] reserved + attr_reader :reserved3 - # @see MachOStructure::FORMAT - FORMAT = "a16a16Q=2L=8".freeze + # @see MachOStructure::FORMAT + FORMAT = "a16a16Q=2L=8".freeze - # @see MachOStructure::SIZEOF - SIZEOF = 80 + # @see MachOStructure::SIZEOF + SIZEOF = 80 - # @api private - def initialize(sectname, segname, addr, size, offset, align, reloff, - nreloc, flags, reserved1, reserved2, reserved3) - super(sectname, segname, addr, size, offset, align, reloff, - nreloc, flags, reserved1, reserved2) - @reserved3 = reserved3 + # @api private + def initialize(sectname, segname, addr, size, offset, align, reloff, + nreloc, flags, reserved1, reserved2, reserved3) + super(sectname, segname, addr, size, offset, align, reloff, + nreloc, flags, reserved1, reserved2) + @reserved3 = reserved3 + end end end end diff --git a/Library/Homebrew/vendor/macho/macho/structure.rb b/Library/Homebrew/vendor/macho/macho/structure.rb index 43f7bc84e..bac5342d4 100644 --- a/Library/Homebrew/vendor/macho/macho/structure.rb +++ b/Library/Homebrew/vendor/macho/macho/structure.rb @@ -19,7 +19,7 @@ module MachO # @param endianness [Symbol] either `:big` or `:little` # @param bin [String] the string to be unpacked into the new structure - # @return [MachO::MachOStructure] a new MachOStructure initialized with `bin` + # @return [MachO::MachOStructure] the resulting structure # @api private def self.new_from_bin(endianness, bin) format = Utils.specialize_format(self::FORMAT, endianness) diff --git a/Library/Homebrew/vendor/macho/macho/tools.rb b/Library/Homebrew/vendor/macho/macho/tools.rb index f34d75dc1..b49626d9d 100644 --- a/Library/Homebrew/vendor/macho/macho/tools.rb +++ b/Library/Homebrew/vendor/macho/macho/tools.rb @@ -1,5 +1,6 @@ module MachO - # A collection of convenient methods for common operations on Mach-O and Fat binaries. + # A collection of convenient methods for common operations on Mach-O and Fat + # binaries. module Tools # @param filename [String] the Mach-O or Fat binary being read # @return [Array<String>] an array of all dylibs linked to the binary @@ -9,7 +10,8 @@ module MachO file.linked_dylibs end - # Changes the dylib ID of a Mach-O or Fat binary, overwriting the source file. + # Changes the dylib ID of a Mach-O or Fat binary, overwriting the source + # file. # @param filename [String] the Mach-O or Fat binary being modified # @param new_id [String] the new dylib ID for the binary # @param options [Hash] @@ -23,7 +25,8 @@ module MachO file.write! end - # Changes a shared library install name in a Mach-O or Fat binary, overwriting the source file. + # Changes a shared library install name in a Mach-O or Fat binary, + # overwriting the source file. # @param filename [String] the Mach-O or Fat binary being modified # @param old_name [String] the old shared library name # @param new_name [String] the new shared library name @@ -38,7 +41,8 @@ module MachO file.write! end - # Changes a runtime path in a Mach-O or Fat binary, overwriting the source file. + # Changes a runtime path in a Mach-O or Fat binary, overwriting the source + # file. # @param filename [String] the Mach-O or Fat binary being modified # @param old_path [String] the old runtime path # @param new_path [String] the new runtime path @@ -67,7 +71,8 @@ module MachO file.write! end - # Delete a runtime path from a Mach-O or Fat binary, overwriting the source file. + # Delete a runtime path from a Mach-O or Fat binary, overwriting the source + # file. # @param filename [String] the Mach-O or Fat binary being modified # @param old_path [String] the old runtime path # @param options [Hash] @@ -80,5 +85,24 @@ module MachO file.delete_rpath(old_path, options) file.write! end + + # Merge multiple Mach-Os into one universal (Fat) binary. + # @param filename [String] the fat binary to create + # @param files [Array<MachO::MachOFile, MachO::FatFile>] the files to merge + # @return [void] + def self.merge_machos(filename, *files) + machos = files.map do |file| + macho = MachO.open(file) + case macho + when MachO::MachOFile + macho + else + macho.machos + end + end.flatten + + fat_macho = MachO::FatFile.new_from_machos(*machos) + fat_macho.write(filename) + end end end diff --git a/Library/Homebrew/vendor/macho/macho/utils.rb b/Library/Homebrew/vendor/macho/macho/utils.rb index cedd0bc1c..ecfc8390b 100644 --- a/Library/Homebrew/vendor/macho/macho/utils.rb +++ b/Library/Homebrew/vendor/macho/macho/utils.rb @@ -5,7 +5,7 @@ module MachO # @param value [Fixnum] the number being rounded # @param round [Fixnum] the number being rounded with # @return [Fixnum] the rounded value - # @see https://www.opensource.apple.com/source/cctools/cctools-870/libstuff/rnd.c + # @see http://www.opensource.apple.com/source/cctools/cctools-870/libstuff/rnd.c def self.round(value, round) round -= 1 value += round @@ -13,7 +13,8 @@ module MachO value end - # Returns the number of bytes needed to pad the given size to the given alignment. + # Returns the number of bytes needed to pad the given size to the given + # alignment. # @param size [Fixnum] the unpadded size # @param alignment [Fixnum] the number to alignment the size with # @return [Fixnum] the number of pad bytes required @@ -21,7 +22,8 @@ module MachO round(size, alignment) - size end - # Converts an abstract (native-endian) String#unpack format to big or little. + # Converts an abstract (native-endian) String#unpack format to big or + # little. # @param format [String] the format string being converted # @param endianness [Symbol] either `:big` or `:little` # @return [String] the converted string @@ -31,7 +33,8 @@ module MachO end # Packs tagged strings into an aligned payload. - # @param fixed_offset [Fixnum] the baseline offset for the first packed string + # @param fixed_offset [Fixnum] the baseline offset for the first packed + # string # @param alignment [Fixnum] the alignment value to use for packing # @param strings [Hash] the labeled strings to pack # @return [Array<String, Hash>] the packed string and labeled offsets @@ -53,44 +56,44 @@ module MachO # Compares the given number to valid Mach-O magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid Mach-O magic number, false otherwise + # @return [Boolean] whether `num` is a valid Mach-O magic number def self.magic?(num) - MH_MAGICS.key?(num) + Headers::MH_MAGICS.key?(num) end # Compares the given number to valid Fat magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid Fat magic number, false otherwise + # @return [Boolean] whether `num` is a valid Fat magic number def self.fat_magic?(num) - num == FAT_MAGIC + num == Headers::FAT_MAGIC end # Compares the given number to valid 32-bit Mach-O magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid 32-bit magic number, false otherwise + # @return [Boolean] whether `num` is a valid 32-bit magic number def self.magic32?(num) - num == MH_MAGIC || num == MH_CIGAM + num == Headers::MH_MAGIC || num == Headers::MH_CIGAM end # Compares the given number to valid 64-bit Mach-O magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid 64-bit magic number, false otherwise + # @return [Boolean] whether `num` is a valid 64-bit magic number def self.magic64?(num) - num == MH_MAGIC_64 || num == MH_CIGAM_64 + num == Headers::MH_MAGIC_64 || num == Headers::MH_CIGAM_64 end # Compares the given number to valid little-endian magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid little-endian magic number, false otherwise + # @return [Boolean] whether `num` is a valid little-endian magic number def self.little_magic?(num) - num == MH_CIGAM || num == MH_CIGAM_64 + num == Headers::MH_CIGAM || num == Headers::MH_CIGAM_64 end # Compares the given number to valid big-endian magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid big-endian magic number, false otherwise + # @return [Boolean] whether `num` is a valid big-endian magic number def self.big_magic?(num) - num == MH_CIGAM || num == MH_CIGAM_64 + num == Headers::MH_CIGAM || num == Headers::MH_CIGAM_64 end end end diff --git a/Library/Homebrew/vendor/plist/plist/parser.rb b/Library/Homebrew/vendor/plist/plist/parser.rb index de441fcc5..7d8bfab07 100644..100755 --- a/Library/Homebrew/vendor/plist/plist/parser.rb +++ b/Library/Homebrew/vendor/plist/plist/parser.rb @@ -69,6 +69,11 @@ module Plist @xml = plist_data_or_file end + # TODO: Update vendored `plist` parser when + # https://github.com/patsplat/plist/pull/38 + # is merged. + @xml.force_encoding("UTF-8") + @listener = listener end |
