diff --git a/.travis.yml b/.travis.yml index fef0f91cad..f918a16fae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,22 @@ -language: c +language: ruby +rvm: system + cache: directories: - $HOME/Library/Caches/Homebrew/style - $HOME/Library/Caches/Homebrew/tests - Library/Homebrew/vendor/bundle + branches: only: - master + matrix: fast_finish: true include: - os: osx - compiler: clang - osx_image: xcode9.2 + osx_image: xcode9.4 - os: linux - compiler: gcc sudo: false before_install: diff --git a/Library/Homebrew/brew.sh b/Library/Homebrew/brew.sh index 166b816e41..d60da9db99 100644 --- a/Library/Homebrew/brew.sh +++ b/Library/Homebrew/brew.sh @@ -104,10 +104,8 @@ then HOMEBREW_FORCE_BREWED_GIT="1" fi - if [[ -z "$HOMEBREW_CACHE" ]] - then - HOMEBREW_CACHE="$HOME/Library/Caches/Homebrew" - fi + HOMEBREW_CACHE="${HOMEBREW_CACHE:-${HOME}/Library/Caches/Homebrew}" + HOMEBREW_SYSTEM_TEMP="/private/tmp" else HOMEBREW_PROCESSOR="$(uname -m)" HOMEBREW_PRODUCT="${HOMEBREW_SYSTEM}brew" @@ -115,17 +113,13 @@ else : "${HOMEBREW_OS_VERSION:=$(uname -r)}" HOMEBREW_OS_USER_AGENT_VERSION="$HOMEBREW_OS_VERSION" - if [[ -z "$HOMEBREW_CACHE" ]] - then - if [[ -n "$XDG_CACHE_HOME" ]] - then - HOMEBREW_CACHE="$XDG_CACHE_HOME/Homebrew" - else - HOMEBREW_CACHE="$HOME/.cache/Homebrew" - fi - fi + CACHE_HOME="${XDG_CACHE_HOME:-${HOME}/.cache}" + HOMEBREW_CACHE="${HOMEBREW_CACHE:-${CACHE_HOME}/Homebrew}" + HOMEBREW_SYSTEM_TEMP="/tmp" fi +HOMEBREW_TEMP="${HOMEBREW_TEMP:-${HOMEBREW_SYSTEM_TEMP}}" + if [[ -n "$HOMEBREW_FORCE_BREWED_CURL" && -x "$HOMEBREW_PREFIX/opt/curl/bin/curl" ]] && "$HOMEBREW_PREFIX/opt/curl/bin/curl" --version >/dev/null @@ -153,6 +147,8 @@ export HOMEBREW_BREW_FILE export HOMEBREW_PREFIX export HOMEBREW_REPOSITORY export HOMEBREW_LIBRARY +export HOMEBREW_SYSTEM_TEMP +export HOMEBREW_TEMP # Declared in brew.sh export HOMEBREW_VERSION @@ -309,6 +305,21 @@ EOS } check-run-command-as-root +check-prefix-is-not-tmpdir() { + [[ -z "${HOMEBREW_MACOS}" ]] && return + + if [[ "${HOMEBREW_PREFIX}" = "${HOMEBREW_TEMP}"* ]] + then + odie <# Do something only for clang + #
# Do something only for the system clang
   # if ENV.compiler == :clang
   #   # modify CFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS in one go:
   #   ENV.append_to_cflags "-I ./missing/includes"
@@ -301,6 +301,18 @@ module SharedEnvExtension
   # A no-op until we enable this by default again (which we may never do).
   def permit_weak_imports; end
 
+  # @private
+  def compiler_any_clang?(cc = compiler)
+    %w[clang llvm_clang].include?(cc.to_s)
+  end
+
+  # @private
+  def compiler_with_cxx11_support?(cc)
+    return if compiler_any_clang?(cc)
+    version = cc[/^gcc-(\d+(?:\.\d+)?)$/, 1]
+    version && Version.create(version) >= Version.create("4.8")
+  end
+
   private
 
   def cc=(val)
@@ -330,11 +342,6 @@ module SharedEnvExtension
     return unless homebrew_cc =~ GNU_GCC_REGEXP
     raise "Non-Apple GCC can't build universal binaries"
   end
-
-  def gcc_with_cxx11_support?(cc)
-    version = cc[/^gcc-(\d+(?:\.\d+)?)$/, 1]
-    version && Version.create(version) >= Version.create("4.8")
-  end
 end
 
 require "extend/os/extend/ENV/shared"
diff --git a/Library/Homebrew/extend/ENV/std.rb b/Library/Homebrew/extend/ENV/std.rb
index 17e0fd67a0..5a152f0d55 100644
--- a/Library/Homebrew/extend/ENV/std.rb
+++ b/Library/Homebrew/extend/ENV/std.rb
@@ -157,7 +157,7 @@ module Stdenv
     append_to_cflags Hardware::CPU.universal_archs.as_arch_flags
     append "LDFLAGS", Hardware::CPU.universal_archs.as_arch_flags
 
-    return if compiler == :clang
+    return if compiler_any_clang?
     return unless Hardware.is_32_bit?
     # Can't mix "-march" for a 32-bit CPU  with "-arch x86_64"
     replace_in_cflags(/-march=\S*/, "-Xarch_#{Hardware::CPU.arch_32_bit} \\0")
@@ -167,7 +167,7 @@ module Stdenv
     if compiler == :clang
       append "CXX", "-std=c++11"
       append "CXX", "-stdlib=libc++"
-    elsif gcc_with_cxx11_support?(compiler)
+    elsif compiler_with_cxx11_support?(compiler)
       append "CXX", "-std=c++11"
     else
       raise "The selected compiler doesn't support C++11: #{compiler}"
diff --git a/Library/Homebrew/extend/ENV/super.rb b/Library/Homebrew/extend/ENV/super.rb
index 3a23dfd7f4..aa12aa71c5 100644
--- a/Library/Homebrew/extend/ENV/super.rb
+++ b/Library/Homebrew/extend/ENV/super.rb
@@ -276,7 +276,7 @@ module Superenv
     self["HOMEBREW_ARCHFLAGS"] = Hardware::CPU.universal_archs.as_arch_flags
 
     # GCC doesn't accept "-march" for a 32-bit CPU with "-arch x86_64"
-    return if compiler == :clang
+    return if compiler_any_clang?
     return unless Hardware::CPU.is_32_bit?
     self["HOMEBREW_OPTFLAGS"] = self["HOMEBREW_OPTFLAGS"].sub(
       /-march=\S*/,
@@ -300,7 +300,7 @@ module Superenv
     if homebrew_cc == "clang"
       append "HOMEBREW_CCCFG", "x", ""
       append "HOMEBREW_CCCFG", "g", ""
-    elsif gcc_with_cxx11_support?(homebrew_cc)
+    elsif compiler_with_cxx11_support?(homebrew_cc)
       append "HOMEBREW_CCCFG", "x", ""
     else
       raise "The selected compiler doesn't support C++11: #{homebrew_cc}"
diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb
index f8106340ca..9b1e7a00b2 100644
--- a/Library/Homebrew/formula_installer.rb
+++ b/Library/Homebrew/formula_installer.rb
@@ -436,8 +436,12 @@ class FormulaInstaller
 
   def expand_requirements
     unsatisfied_reqs = Hash.new { |h, k| h[k] = [] }
-    deps = []
+    req_deps = []
     formulae = [formula]
+    formula_deps_map = Dependency.expand(formula)
+                                 .each_with_object({}) do |dep, hash|
+      hash[dep.name] = dep
+    end
 
     while f = formulae.pop
       runtime_requirements = runtime_requirements(f)
@@ -453,6 +457,8 @@ class FormulaInstaller
           next
         elsif !runtime_requirements.include?(req) && install_bottle_for_dependent
           Requirement.prune
+        elsif (dep = formula_deps_map[dependent.name]) && dep.build?
+          Requirement.prune
         else
           unsatisfied_reqs[dependent] << req
         end
@@ -460,9 +466,9 @@ class FormulaInstaller
     end
 
     # Merge the repeated dependencies, which may have different tags.
-    deps = Dependency.merge_repeats(deps)
+    req_deps = Dependency.merge_repeats(req_deps)
 
-    [unsatisfied_reqs, deps]
+    [unsatisfied_reqs, req_deps]
   end
 
   def expand_dependencies(deps)
diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb
index 112b296dc4..886f739d69 100644
--- a/Library/Homebrew/formulary.rb
+++ b/Library/Homebrew/formulary.rb
@@ -112,10 +112,10 @@ module Formulary
         resource = Resource.new(formula_name) { url bottle_name }
         resource.specs[:bottle] = true
         downloader = CurlDownloadStrategy.new resource.name, resource
-        @bottle_filename = downloader.cached_location
-        cached = @bottle_filename.exist?
+        cached = downloader.cached_location.exist?
         downloader.fetch
         ohai "Pouring the cached bottle" if cached
+        @bottle_filename = downloader.cached_location
       else
         @bottle_filename = Pathname(bottle_name).realpath
       end
diff --git a/Library/Homebrew/install.rb b/Library/Homebrew/install.rb
index f86c096a9d..fd6f7f6a95 100644
--- a/Library/Homebrew/install.rb
+++ b/Library/Homebrew/install.rb
@@ -27,12 +27,14 @@ module Homebrew
 
     def check_development_tools
       checks = Diagnostic::Checks.new
+      failed = false
       checks.fatal_development_tools_checks.each do |check|
         out = checks.send(check)
         next if out.nil?
+        failed ||= true
         ofail out
       end
-      exit 1 if Homebrew.failed?
+      exit 1 if failed
     end
 
     def check_cellar
diff --git a/Library/Homebrew/resource.rb b/Library/Homebrew/resource.rb
index 3dda50f8df..fba9328751 100644
--- a/Library/Homebrew/resource.rb
+++ b/Library/Homebrew/resource.rb
@@ -118,7 +118,7 @@ class Resource
       if block_given?
         yield ResourceStageContext.new(self, staging)
       elsif target
-        target = Pathname.new(target) unless target.is_a? Pathname
+        target = Pathname(target)
         target.install Pathname.pwd.children
       end
     end
diff --git a/Library/Homebrew/rubocops.rb b/Library/Homebrew/rubocops.rb
index f1923ee654..e0a532363a 100644
--- a/Library/Homebrew/rubocops.rb
+++ b/Library/Homebrew/rubocops.rb
@@ -1,4 +1,3 @@
-require_relative "./rubocops/bottle_block_cop"
 require_relative "./rubocops/formula_desc_cop"
 require_relative "./rubocops/components_order_cop"
 require_relative "./rubocops/components_redundancy_cop"
diff --git a/Library/Homebrew/rubocops/bottle_block_cop.rb b/Library/Homebrew/rubocops/bottle_block_cop.rb
deleted file mode 100644
index 3cfde01ad6..0000000000
--- a/Library/Homebrew/rubocops/bottle_block_cop.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require_relative "./extend/formula_cop"
-
-module RuboCop
-  module Cop
-    module FormulaAuditStrict
-      # This cop audits `bottle` block in Formulae
-      #
-      # - `rebuild` should be used instead of `revision` in `bottle` block
-
-      class BottleBlock < FormulaCop
-        MSG = "Use rebuild instead of revision in bottle block".freeze
-
-        def audit_formula(_node, _class_node, _parent_class_node, body_node)
-          bottle = find_block(body_node, :bottle)
-          return if bottle.nil? || block_size(bottle).zero?
-          problem "Use rebuild instead of revision in bottle block" if method_called_in_block?(bottle, :revision)
-        end
-
-        def autocorrect(node)
-          lambda do |corrector|
-            correction = node.source.sub("revision", "rebuild")
-            corrector.insert_before(node.source_range, correction)
-            corrector.remove(node.source_range)
-          end
-        end
-      end
-    end
-  end
-end
diff --git a/Library/Homebrew/rubocops/components_redundancy_cop.rb b/Library/Homebrew/rubocops/components_redundancy_cop.rb
index e22ec6a910..b6986db609 100644
--- a/Library/Homebrew/rubocops/components_redundancy_cop.rb
+++ b/Library/Homebrew/rubocops/components_redundancy_cop.rb
@@ -8,10 +8,12 @@ module RuboCop
       # - `url|checksum|mirror` should be inside `stable` block
       # - `head` and `head do` should not be simultaneously present
       # - `bottle :unneeded/:disable` and `bottle do` should not be simultaneously present
+      # - `stable do` should not be present without a `head` or `devel` spec
 
       class ComponentsRedundancy < FormulaCop
         HEAD_MSG = "`head` and `head do` should not be simultaneously present".freeze
         BOTTLE_MSG = "`bottle :modifier` and `bottle do` should not be simultaneously present".freeze
+        STABLE_MSG = "`stable do` should not be present without a `head` or `devel` spec".freeze
 
         def audit_formula(_node, _class_node, _parent_class_node, body_node)
           stable_block = find_block(body_node, :stable)
@@ -26,6 +28,11 @@ module RuboCop
 
           problem BOTTLE_MSG if method_called?(body_node, :bottle) &&
                                 find_block(body_node, :bottle)
+
+          return if method_called?(body_node, :head) ||
+                    find_block(body_node, :head) ||
+                    find_block(body_node, :devel)
+          problem STABLE_MSG if stable_block
         end
       end
     end
diff --git a/Library/Homebrew/rubocops/options_cop.rb b/Library/Homebrew/rubocops/options_cop.rb
index 400ea807bb..c39709d7b5 100644
--- a/Library/Homebrew/rubocops/options_cop.rb
+++ b/Library/Homebrew/rubocops/options_cop.rb
@@ -50,7 +50,6 @@ module RuboCop
         OPTION = "Formulae should not have an `option`".freeze
 
         def audit_formula(_node, _class_node, _parent_class_node, body_node)
-          return if versioned_formula?
           problem DEP_OPTION if method_called_ever?(body_node, :deprecated_option)
           return unless formula_tap == "homebrew-core"
           problem OPTION if method_called_ever?(body_node, :option)
diff --git a/Library/Homebrew/system_config.rb b/Library/Homebrew/system_config.rb
index 512ca16d54..0f7c56b1be 100644
--- a/Library/Homebrew/system_config.rb
+++ b/Library/Homebrew/system_config.rb
@@ -116,6 +116,7 @@ class SystemConfig
         HOMEBREW_CELLAR: "/usr/local/Cellar",
         HOMEBREW_CACHE: "#{ENV["HOME"]}/Library/Caches/Homebrew",
         HOMEBREW_RUBY_WARNINGS: "-W0",
+        HOMEBREW_TEMP: ENV["HOMEBREW_SYSTEM_TEMP"],
       }.freeze
       boring_keys = %w[
         HOMEBREW_BROWSER
@@ -134,6 +135,7 @@ class SystemConfig
         HOMEBREW_MACOS_VERSION
         HOMEBREW_RUBY_PATH
         HOMEBREW_SYSTEM
+        HOMEBREW_SYSTEM_TEMP
         HOMEBREW_OS_VERSION
         HOMEBREW_PATH
         HOMEBREW_PROCESSOR
@@ -155,6 +157,9 @@ class SystemConfig
       if defaults_hash[:HOMEBREW_RUBY_WARNINGS] != ENV["HOMEBREW_RUBY_WARNINGS"].to_s
         f.puts "HOMEBREW_RUBY_WARNINGS: #{ENV["HOMEBREW_RUBY_WARNINGS"]}"
       end
+      if defaults_hash[:HOMEBREW_TEMP] != HOMEBREW_TEMP.to_s
+        f.puts "HOMEBREW_TEMP: #{HOMEBREW_TEMP}"
+      end
       unless ENV["HOMEBREW_ENV"]
         ENV.sort.each do |key, value|
           next unless key.start_with?("HOMEBREW_")
diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb
index a6f7c4044f..5f7d691bff 100644
--- a/Library/Homebrew/tap.rb
+++ b/Library/Homebrew/tap.rb
@@ -284,8 +284,8 @@ class Tap
 
     link_completions_and_manpages
 
-    formula_count = formula_files.size
-    puts "Tapped #{Formatter.pluralize(formula_count, "formula")} (#{path.abv})" unless quiet
+    formatted_contents = Formatter.comma_and(*contents)&.prepend(" ")
+    puts "Tapped#{formatted_contents} (#{path.abv})." unless quiet
     Descriptions.cache_formulae(formula_names)
 
     return if options[:clone_target]
@@ -311,15 +311,18 @@ class Tap
     require "descriptions"
     raise TapUnavailableError, name unless installed?
 
-    puts "Untapping #{name}... (#{path.abv})"
+    puts "Untapping #{name}..."
+
+    abv = path.abv
+    formatted_contents = Formatter.comma_and(*contents)&.prepend(" ")
+
     unpin if pinned?
-    formula_count = formula_files.size
     Descriptions.uncache_formulae(formula_names)
     Utils::Link.unlink_manpages(path)
     Utils::Link.unlink_completions(path)
     path.rmtree
     path.parent.rmdir_if_possible
-    puts "Untapped #{Formatter.pluralize(formula_count, "formula")}"
+    puts "Untapped#{formatted_contents} (#{abv})."
     clear_cache
   end
 
@@ -343,6 +346,24 @@ class Tap
     @cask_dir ||= path/"Casks"
   end
 
+  def contents
+    contents = []
+
+    if (command_count = command_files.count).positive?
+      contents << Formatter.pluralize(command_count, "command")
+    end
+
+    if (cask_count = cask_files.count).positive?
+      contents << Formatter.pluralize(cask_count, "cask")
+    end
+
+    if (formula_count = formula_files.count).positive?
+      contents << Formatter.pluralize(formula_count, "formula")
+    end
+
+    contents
+  end
+
   # an array of all {Formula} files of this {Tap}.
   def formula_files
     @formula_files ||= if formula_dir.directory?
@@ -427,7 +448,8 @@ class Tap
 
   # an array of all commands files of this {Tap}.
   def command_files
-    @command_files ||= Pathname.glob("#{path}/cmd/brew-*").select(&:executable?)
+    @command_files ||= Pathname.glob("#{path}/cmd/brew{,cask}-*")
+                               .select { |file| file.executable? || file.extname == ".rb" }
   end
 
   # path to the pin record for this {Tap}.
diff --git a/Library/Homebrew/test/ENV_spec.rb b/Library/Homebrew/test/ENV_spec.rb
index b39ae374c7..aed0608e00 100644
--- a/Library/Homebrew/test/ENV_spec.rb
+++ b/Library/Homebrew/test/ENV_spec.rb
@@ -156,6 +156,18 @@ shared_examples EnvActivation do
       expect(subject["FOO"]).to eq "bar"
     end
   end
+
+  describe "#compiler_any_clang?" do
+    it "returns true for llvm_clang" do
+      expect(subject.compiler_any_clang?(:llvm_clang)).to be true
+    end
+  end
+
+  describe "#compiler_with_cxx11_support?" do
+    it "returns true for gcc-4.9" do
+      expect(subject.compiler_with_cxx11_support?("gcc-4.9")).to be true
+    end
+  end
 end
 
 describe Stdenv do
diff --git a/Library/Homebrew/test/Gemfile.lock b/Library/Homebrew/test/Gemfile.lock
index d0fb41096f..cf5da05187 100644
--- a/Library/Homebrew/test/Gemfile.lock
+++ b/Library/Homebrew/test/Gemfile.lock
@@ -15,7 +15,7 @@ GEM
       parallel
     parser (2.5.1.0)
       ast (~> 2.4.0)
-    powerpack (0.1.1)
+    powerpack (0.1.2)
     rainbow (3.0.0)
     rspec (3.7.0)
       rspec-core (~> 3.7.0)
@@ -37,7 +37,7 @@ GEM
     rspec-support (3.7.1)
     rspec-wait (0.0.9)
       rspec (>= 3, < 4)
-    rubocop (0.57.1)
+    rubocop (0.57.2)
       jaro_winkler (~> 1.5.1)
       parallel (~> 1.10)
       parser (>= 2.5)
@@ -51,7 +51,7 @@ GEM
       json (>= 1.8, < 3)
       simplecov-html (~> 0.10.0)
     simplecov-html (0.10.2)
-    unicode-display_width (1.3.0)
+    unicode-display_width (1.4.0)
     url (0.3.2)
 
 PLATFORMS
@@ -64,8 +64,8 @@ DEPENDENCIES
   rspec-its
   rspec-retry
   rspec-wait
-  rubocop (= 0.57.1)
+  rubocop (= 0.57.2)
   simplecov
 
 BUNDLED WITH
-   1.16.1
+   1.16.2
diff --git a/Library/Homebrew/test/cask/audit_spec.rb b/Library/Homebrew/test/cask/audit_spec.rb
index 37d4f95abc..82487982a9 100644
--- a/Library/Homebrew/test/cask/audit_spec.rb
+++ b/Library/Homebrew/test/cask/audit_spec.rb
@@ -333,10 +333,10 @@ describe Hbc::Audit, :cask do
       end
     end
 
-    describe "GitHub releases appcast check" do
-      let(:appcast_warning) { /Download uses GitHub releases/ }
+    describe "hosting with appcast checks" do
+      let(:appcast_warning) { /please add an appcast/ }
 
-      context "when the download does not use GitHub releases" do
+      context "when the download does not use hosting with an appcast" do
         let(:cask_token) { "basic-cask" }
 
         it { is_expected.not_to warn_with(appcast_warning) }
@@ -353,16 +353,6 @@ describe Hbc::Audit, :cask do
 
         it { is_expected.to warn_with(appcast_warning) }
       end
-    end
-
-    describe "SourceForge appcast check" do
-      let(:appcast_warning) { /Download is hosted on SourceForge/ }
-
-      context "when the download is not hosted on SourceForge" do
-        let(:cask_token) { "basic-cask" }
-
-        it { is_expected.not_to warn_with(appcast_warning) }
-      end
 
       context "when the download is hosted on SourceForge and has an appcast" do
         let(:cask_token) { "sourceforge-with-appcast" }
@@ -375,6 +365,30 @@ describe Hbc::Audit, :cask do
 
         it { is_expected.to warn_with(appcast_warning) }
       end
+
+      context "when the download is hosted on DevMate and has an appcast" do
+        let(:cask_token) { "devmate-with-appcast" }
+
+        it { is_expected.not_to warn_with(appcast_warning) }
+      end
+
+      context "when the download is hosted on DevMate and does not have an appcast" do
+        let(:cask_token) { "devmate-without-appcast" }
+
+        it { is_expected.to warn_with(appcast_warning) }
+      end
+
+      context "when the download is hosted on HockeyApp and has an appcast" do
+        let(:cask_token) { "hockeyapp-with-appcast" }
+
+        it { is_expected.not_to warn_with(appcast_warning) }
+      end
+
+      context "when the download is hosted on HockeyApp and does not have an appcast" do
+        let(:cask_token) { "hockeyapp-without-appcast" }
+
+        it { is_expected.to warn_with(appcast_warning) }
+      end
     end
 
     describe "latest with appcast checks" do
diff --git a/Library/Homebrew/test/cask/system_command_spec.rb b/Library/Homebrew/test/cask/system_command_spec.rb
index 6e7397931f..e9d2c4d268 100644
--- a/Library/Homebrew/test/cask/system_command_spec.rb
+++ b/Library/Homebrew/test/cask/system_command_spec.rb
@@ -80,7 +80,7 @@ describe Hbc::SystemCommand, :cask do
         options.merge!(print_stdout: true)
       end
 
-      it "echoes both STDOUT and STDERR", :focus do
+      it "echoes both STDOUT and STDERR" do
         expect { described_class.run(command, options) }
           .to output("1\n3\n5\n").to_stdout
           .and output("2\n4\n6\n").to_stderr
diff --git a/Library/Homebrew/test/formatter_spec.rb b/Library/Homebrew/test/formatter_spec.rb
index 14c63b8c3a..1c97bf6f8f 100644
--- a/Library/Homebrew/test/formatter_spec.rb
+++ b/Library/Homebrew/test/formatter_spec.rb
@@ -76,4 +76,22 @@ describe Formatter do
       expect(described_class.pluralize(2, "new formula")).to eq("2 new formulae")
     end
   end
+
+  describe "::comma_and" do
+    it "returns nil if given no arguments" do
+      expect(described_class.comma_and).to be nil
+    end
+
+    it "returns the input as string if there is only one argument" do
+      expect(described_class.comma_and(1)).to eq("1")
+    end
+
+    it "concatenates two items with “and”" do
+      expect(described_class.comma_and(1, 2)).to eq("1 and 2")
+    end
+
+    it "concatenates all items with a comma and appends the last with “and”" do
+      expect(described_class.comma_and(1, 2, 3)).to eq("1, 2 and 3")
+    end
+  end
 end
diff --git a/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb b/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb
deleted file mode 100644
index e0982dcbab..0000000000
--- a/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require_relative "../../rubocops/bottle_block_cop"
-
-describe RuboCop::Cop::FormulaAuditStrict::BottleBlock do
-  subject(:cop) { described_class.new }
-
-  context "When auditing Bottle Block" do
-    it "When there is revision in bottle block" do
-      expect_offense(<<~RUBY)
-        class Foo < Formula
-          url 'http://example.com/foo-1.0.tgz'
-          bottle do
-            cellar :any
-            revision 2
-            ^^^^^^^^^^ Use rebuild instead of revision in bottle block
-          end
-        end
-      RUBY
-    end
-  end
-
-  context "When auditing Bottle Block with auto correct" do
-    it "When there is revision in bottle block" do
-      source = <<~EOS
-        class Foo < Formula
-          url 'http://example.com/foo-1.0.tgz'
-          bottle do
-            cellar :any
-            revision 2
-          end
-        end
-      EOS
-
-      corrected_source = <<~EOS
-        class Foo < Formula
-          url 'http://example.com/foo-1.0.tgz'
-          bottle do
-            cellar :any
-            rebuild 2
-          end
-        end
-      EOS
-
-      new_source = autocorrect_source(source)
-      expect(new_source).to eq(corrected_source)
-    end
-  end
-end
diff --git a/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb b/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb
index fd325e5846..08ab0210a6 100644
--- a/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb
+++ b/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb
@@ -12,6 +12,10 @@ describe RuboCop::Cop::FormulaAudit::ComponentsRedundancy do
           stable do
             # stuff
           end
+
+          devel do
+            # stuff
+          end
         end
       RUBY
     end
@@ -40,5 +44,45 @@ describe RuboCop::Cop::FormulaAudit::ComponentsRedundancy do
         end
       RUBY
     end
+
+    it "When `stable do` is present with a `head` method" do
+      expect_no_offenses(<<~RUBY)
+        class Foo < Formula
+          head "http://example.com/foo.git"
+
+          stable do
+            # stuff
+          end
+        end
+      RUBY
+    end
+
+    it "When `stable do` is present with a `head do` block" do
+      expect_no_offenses(<<~RUBY)
+        class Foo < Formula
+          stable do
+            # stuff
+          end
+
+          head do
+            # stuff
+          end
+        end
+      RUBY
+    end
+
+    it "When `stable do` is present with a `devel` block" do
+      expect_no_offenses(<<~RUBY)
+        class Foo < Formula
+          stable do
+            # stuff
+          end
+
+          devel do
+            # stuff
+          end
+        end
+      RUBY
+    end
   end
 end
diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/devmate-with-appcast.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/devmate-with-appcast.rb
new file mode 100644
index 0000000000..d6c48d3411
--- /dev/null
+++ b/Library/Homebrew/test/support/fixtures/cask/Casks/devmate-with-appcast.rb
@@ -0,0 +1,12 @@
+cask 'devmate-with-appcast' do
+  version '1.0'
+  sha256 'a69e7357bea014f4c14ac9699274f559086844ffa46563c4619bf1addfd72ad9'
+
+  # dl.devmate.com/com.my.fancyapp was verified as official when first introduced to the cask
+  url "https://dl.devmate.com/com.my.fancyapp/app_#{version}.zip"
+  appcast 'https://updates.devmate.com/com.my.fancyapp.app.xml'
+  name 'DevMate'
+  homepage 'http://www.example.com/'
+
+  app 'DevMate.app'
+end
diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/devmate-without-appcast.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/devmate-without-appcast.rb
new file mode 100644
index 0000000000..a04d20634c
--- /dev/null
+++ b/Library/Homebrew/test/support/fixtures/cask/Casks/devmate-without-appcast.rb
@@ -0,0 +1,11 @@
+cask 'devmate-without-appcast' do
+  version '1.0'
+  sha256 'a69e7357bea014f4c14ac9699274f559086844ffa46563c4619bf1addfd72ad9'
+
+  # dl.devmate.com/com.my.fancyapp was verified as official when first introduced to the cask
+  url "https://dl.devmate.com/com.my.fancyapp/app_#{version}.zip"
+  name 'DevMate'
+  homepage 'http://www.example.com/'
+
+  app 'DevMate.app'
+end
diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/hockeyapp-with-appcast.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/hockeyapp-with-appcast.rb
new file mode 100644
index 0000000000..6a9f9c5f84
--- /dev/null
+++ b/Library/Homebrew/test/support/fixtures/cask/Casks/hockeyapp-with-appcast.rb
@@ -0,0 +1,12 @@
+cask 'hockeyapp-with-appcast' do
+  version '1.0,123'
+  sha256 'a69e7357bea014f4c14ac9699274f559086844ffa46563c4619bf1addfd72ad9'
+
+  # rink.hockeyapp.net/api/2/apps/67503a7926431872c4b6c1549f5bd6b1 was verified as official when first introduced to the cask
+  url "https://rink.hockeyapp.net/api/2/apps/67503a7926431872c4b6c1549f5bd6b1/app_versions/#{version.after_comma}?format=zip"
+  appcast 'https://rink.hockeyapp.net/api/2/apps/67503a7926431872c4b6c1549f5bd6b1'
+  name 'HockeyApp'
+  homepage 'http://www.example.com/'
+
+  app 'HockeyApp.app'
+end
diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/hockeyapp-without-appcast.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/hockeyapp-without-appcast.rb
new file mode 100644
index 0000000000..86b853bbd5
--- /dev/null
+++ b/Library/Homebrew/test/support/fixtures/cask/Casks/hockeyapp-without-appcast.rb
@@ -0,0 +1,11 @@
+cask 'hockeyapp-without-appcast' do
+  version '1.0,123'
+  sha256 'a69e7357bea014f4c14ac9699274f559086844ffa46563c4619bf1addfd72ad9'
+
+  # rink.hockeyapp.net/api/2/apps/67503a7926431872c4b6c1549f5bd6b1 was verified as official when first introduced to the cask
+  url "https://rink.hockeyapp.net/api/2/apps/67503a7926431872c4b6c1549f5bd6b1/app_versions/#{version.after_comma}?format=zip"
+  name 'HockeyApp'
+  homepage 'http://www.example.com/'
+
+  app 'HockeyApp.app'
+end
diff --git a/Library/Homebrew/test/support/lib/config.rb b/Library/Homebrew/test/support/lib/config.rb
index c83a0eabaa..cc8a8d69a2 100644
--- a/Library/Homebrew/test/support/lib/config.rb
+++ b/Library/Homebrew/test/support/lib/config.rb
@@ -14,27 +14,27 @@ end
 
 # Paths pointing into the Homebrew code base that persist across test runs
 HOMEBREW_LIBRARY_PATH  = Pathname.new(File.expand_path("../../..", __dir__))
-HOMEBREW_SHIMS_PATH    = HOMEBREW_LIBRARY_PATH.parent+"Homebrew/shims"
+HOMEBREW_SHIMS_PATH    = HOMEBREW_LIBRARY_PATH.parent/"Homebrew/shims"
 HOMEBREW_LOAD_PATH     = [
   File.expand_path(__dir__),
   HOMEBREW_LIBRARY_PATH,
-  HOMEBREW_LIBRARY_PATH.join("cask/lib"),
+  HOMEBREW_LIBRARY_PATH/"cask/lib",
 ].join(File::PATH_SEPARATOR)
 
 # Paths redirected to a temporary directory and wiped at the end of the test run
-HOMEBREW_PREFIX        = Pathname.new(TEST_TMPDIR).join("prefix")
+HOMEBREW_PREFIX        = Pathname(TEST_TMPDIR)/"prefix"
 HOMEBREW_REPOSITORY    = HOMEBREW_PREFIX
-HOMEBREW_LIBRARY       = HOMEBREW_REPOSITORY+"Library"
-HOMEBREW_CACHE         = HOMEBREW_PREFIX.parent+"cache"
-HOMEBREW_CACHE_FORMULA = HOMEBREW_PREFIX.parent+"formula_cache"
-HOMEBREW_LINKED_KEGS   = HOMEBREW_PREFIX.parent+"linked"
-HOMEBREW_PINNED_KEGS   = HOMEBREW_PREFIX.parent+"pinned"
-HOMEBREW_LOCK_DIR      = HOMEBREW_PREFIX.parent+"locks"
-HOMEBREW_CELLAR        = HOMEBREW_PREFIX.parent+"cellar"
-HOMEBREW_LOGS          = HOMEBREW_PREFIX.parent+"logs"
-HOMEBREW_TEMP          = HOMEBREW_PREFIX.parent+"temp"
+HOMEBREW_LIBRARY       = HOMEBREW_REPOSITORY/"Library"
+HOMEBREW_CACHE         = HOMEBREW_PREFIX.parent/"cache"
+HOMEBREW_CACHE_FORMULA = HOMEBREW_PREFIX.parent/"formula_cache"
+HOMEBREW_LINKED_KEGS   = HOMEBREW_PREFIX.parent/"linked"
+HOMEBREW_PINNED_KEGS   = HOMEBREW_PREFIX.parent/"pinned"
+HOMEBREW_LOCK_DIR      = HOMEBREW_PREFIX.parent/"locks"
+HOMEBREW_CELLAR        = HOMEBREW_PREFIX.parent/"cellar"
+HOMEBREW_LOGS          = HOMEBREW_PREFIX.parent/"logs"
+HOMEBREW_TEMP          = HOMEBREW_PREFIX.parent/"temp"
 
-TEST_FIXTURE_DIR = HOMEBREW_LIBRARY_PATH.join("test", "support", "fixtures")
+TEST_FIXTURE_DIR = HOMEBREW_LIBRARY_PATH/"test/support/fixtures"
 
 TESTBALL_SHA256 = "91e3f7930c98d7ccfb288e115ed52d06b0e5bc16fec7dce8bdda86530027067b".freeze
 TESTBALL_PATCHES_SHA256 = "799c2d551ac5c3a5759bea7796631a7906a6a24435b52261a317133a0bfb34d9".freeze
diff --git a/Library/Homebrew/utils/formatter.rb b/Library/Homebrew/utils/formatter.rb
index ec144bf2f8..36542d7389 100644
--- a/Library/Homebrew/utils/formatter.rb
+++ b/Library/Homebrew/utils/formatter.rb
@@ -108,4 +108,14 @@ module Formatter
 
     show_count ? "#{count} #{words}" : words
   end
+
+  def comma_and(*items)
+    # TODO: Remove when RuboCop 0.57.3 is released.
+    # False positive has been fixed and merged, but is not yet in a
+    # stable release: https://github.com/rubocop-hq/rubocop/pull/6038
+    *items, last = items.map(&:to_s) # rubocop:disable Lint/ShadowedArgument
+    return last if items.empty?
+
+    "#{items.join(", ")} and #{last}"
+  end
 end
diff --git a/Library/Homebrew/vendor/README.md b/Library/Homebrew/vendor/README.md
index d69ef4d63e..39bbdb4dbf 100644
--- a/Library/Homebrew/vendor/README.md
+++ b/Library/Homebrew/vendor/README.md
@@ -3,7 +3,7 @@ Vendored Dependencies
 
 * [plist](https://github.com/patsplat/plist), version 3.3.0
 
-* [ruby-macho](https://github.com/Homebrew/ruby-macho), version 1.2.0
+* [ruby-macho](https://github.com/Homebrew/ruby-macho), version 2.0.0
 
 * [backports](https://github.com/marcandre/backports), version 3.8.0
 
diff --git a/Library/Homebrew/vendor/macho/macho.rb b/Library/Homebrew/vendor/macho/macho.rb
index 3ba3073304..06dd39b09f 100644
--- a/Library/Homebrew/vendor/macho/macho.rb
+++ b/Library/Homebrew/vendor/macho/macho.rb
@@ -1,18 +1,18 @@
-require "#{File.dirname(__FILE__)}/macho/structure"
-require "#{File.dirname(__FILE__)}/macho/view"
-require "#{File.dirname(__FILE__)}/macho/headers"
-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/exceptions"
-require "#{File.dirname(__FILE__)}/macho/utils"
-require "#{File.dirname(__FILE__)}/macho/tools"
+require_relative "macho/structure"
+require_relative "macho/view"
+require_relative "macho/headers"
+require_relative "macho/load_commands"
+require_relative "macho/sections"
+require_relative "macho/macho_file"
+require_relative "macho/fat_file"
+require_relative "macho/exceptions"
+require_relative "macho/utils"
+require_relative "macho/tools"
 
 # The primary namespace for ruby-macho.
 module MachO
   # release version
-  VERSION = "1.2.0".freeze
+  VERSION = "2.0.0".freeze
 
   # Opens the given filename as a MachOFile or FatFile, depending on its magic.
   # @param filename [String] the file being opened
diff --git a/Library/Homebrew/vendor/macho/macho/fat_file.rb b/Library/Homebrew/vendor/macho/macho/fat_file.rb
index e46948de41..0080a5593c 100644
--- a/Library/Homebrew/vendor/macho/macho/fat_file.rb
+++ b/Library/Homebrew/vendor/macho/macho/fat_file.rb
@@ -23,21 +23,37 @@ module MachO
     # Creates a new FatFile from the given (single-arch) Mach-Os
     # @param machos [Array] the machos to combine
     # @return [FatFile] a new FatFile containing the give machos
+    # @raise [ArgumentError] if less than one Mach-O is given
     def self.new_from_machos(*machos)
-      header = Headers::FatHeader.new(Headers::FAT_MAGIC, machos.size)
+      raise ArgumentError, "expected at least one Mach-O" if machos.empty?
+
+      # put the smaller alignments further forwards in fat macho, so that we do less padding
+      machos = machos.sort_by(&:segment_alignment)
+
+      bin = +""
+
+      bin << Headers::FatHeader.new(Headers::FAT_MAGIC, machos.size).serialize
       offset = Headers::FatHeader.bytesize + (machos.size * Headers::FatArch.bytesize)
-      fat_archs = []
+
+      macho_pads = {}
+      macho_bins = {}
+
       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
+        macho_offset      = Utils.round(offset, 2**macho.segment_alignment)
+        macho_pads[macho] = Utils.padding_for(offset, 2**macho.segment_alignment)
+        macho_bins[macho] = macho.serialize
+
+        bin << Headers::FatArch.new(macho.header.cputype, macho.header.cpusubtype,
+                                    macho_offset, macho_bins[macho].bytesize,
+                                    macho.segment_alignment).serialize
+
+        offset += (macho_bins[macho].bytesize + macho_pads[macho])
       end
 
-      bin = header.serialize
-      bin << fat_archs.map(&:serialize).join
-      bin << machos.map(&:serialize).join
+      machos.each do |macho|
+        bin << Utils.nullpad(macho_pads[macho])
+        bin << macho_bins[macho]
+      end
 
       new_from_bin(bin)
     end
@@ -265,6 +281,15 @@ module MachO
       File.open(@filename, "wb") { |f| f.write(@raw_data) }
     end
 
+    # @return [Hash] a hash representation of this {FatFile}
+    def to_h
+      {
+        "header" => header.to_h,
+        "fat_archs" => fat_archs.map(&:to_h),
+        "machos" => machos.map(&:to_h),
+      }
+    end
+
     private
 
     # Obtain the fat header from raw file data.
diff --git a/Library/Homebrew/vendor/macho/macho/headers.rb b/Library/Homebrew/vendor/macho/macho/headers.rb
index 0d4463a6cb..ee8c99e84b 100644
--- a/Library/Homebrew/vendor/macho/macho/headers.rb
+++ b/Library/Homebrew/vendor/macho/macho/headers.rb
@@ -475,6 +475,15 @@ module MachO
       def serialize
         [magic, nfat_arch].pack(FORMAT)
       end
+
+      # @return [Hash] a hash representation of this {FatHeader}
+      def to_h
+        {
+          "magic" => magic,
+          "magic_sym" => MH_MAGICS[magic],
+          "nfat_arch" => nfat_arch,
+        }.merge super
+      end
     end
 
     # Fat binary header architecture structure. A Fat binary has one or more of
@@ -508,7 +517,7 @@ module MachO
       # @api private
       def initialize(cputype, cpusubtype, offset, size, align)
         @cputype = cputype
-        @cpusubtype = cpusubtype
+        @cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK
         @offset = offset
         @size = size
         @align = align
@@ -518,6 +527,19 @@ module MachO
       def serialize
         [cputype, cpusubtype, offset, size, align].pack(FORMAT)
       end
+
+      # @return [Hash] a hash representation of this {FatArch}
+      def to_h
+        {
+          "cputype" => cputype,
+          "cputype_sym" => CPU_TYPES[cputype],
+          "cpusubtype" => cpusubtype,
+          "cpusubtype_sym" => CPU_SUBTYPES[cputype][cpusubtype],
+          "offset" => offset,
+          "size" => size,
+          "align" => align,
+        }.merge super
+      end
     end
 
     # 32-bit Mach-O file header structure
@@ -639,6 +661,24 @@ module MachO
       def alignment
         magic32? ? 4 : 8
       end
+
+      # @return [Hash] a hash representation of this {MachHeader}
+      def to_h
+        {
+          "magic" => magic,
+          "magic_sym" => MH_MAGICS[magic],
+          "cputype" => cputype,
+          "cputype_sym" => CPU_TYPES[cputype],
+          "cpusubtype" => cpusubtype,
+          "cpusubtype_sym" => CPU_SUBTYPES[cputype][cpusubtype],
+          "filetype" => filetype,
+          "filetype_sym" => MH_FILETYPES[filetype],
+          "ncmds" => ncmds,
+          "sizeofcmds" => sizeofcmds,
+          "flags" => flags,
+          "alignment" => alignment,
+        }.merge super
+      end
     end
 
     # 64-bit Mach-O file header structure
@@ -660,6 +700,13 @@ module MachO
         super(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags)
         @reserved = reserved
       end
+
+      # @return [Hash] a hash representation of this {MachHeader64}
+      def to_h
+        {
+          "reserved" => reserved,
+        }.merge super
+      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 8316fccddd..08bda5a9e4 100644
--- a/Library/Homebrew/vendor/macho/macho/load_commands.rb
+++ b/Library/Homebrew/vendor/macho/macho/load_commands.rb
@@ -143,7 +143,7 @@ module MachO
       :LC_LINKER_OPTIMIZATION_HINT => "LinkeditDataCommand",
       :LC_VERSION_MIN_TVOS => "VersionMinCommand",
       :LC_VERSION_MIN_WATCHOS => "VersionMinCommand",
-      :LC_NOTE => "LoadCommand",
+      :LC_NOTE => "NoteCommand",
       :LC_BUILD_VERSION => "BuildVersionCommand",
     }.freeze
 
@@ -169,15 +169,16 @@ module MachO
       :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.
+    # The top-level Mach-O load command structure.
+    #
+    # This is the most generic load command -- only the type ID and size are
+    # represented. Used when a more specific class isn't available or isn't implemented.
     class LoadCommand < MachOStructure
-      # @return [MachO::MachOView] the raw view associated with the load command
+      # @return [MachO::MachOView, nil] the raw view associated with the load command,
+      #  or nil if the load command was created via {create}.
       attr_reader :view
 
-      # @return [Integer] the load command's identifying number
+      # @return [Integer] the load command's type ID
       attr_reader :cmd
 
       # @return [Integer] the size of the load command, in bytes
@@ -251,8 +252,8 @@ module MachO
         view.offset
       end
 
-      # @return [Symbol] a symbol representation of the load command's
-      #  identifying number
+      # @return [Symbol, nil] a symbol representation of the load command's
+      #  type ID, or nil if the ID doesn't correspond to a known load command class
       def type
         LOAD_COMMANDS[cmd]
       end
@@ -265,6 +266,17 @@ module MachO
         type.to_s
       end
 
+      # @return [Hash] a hash representation of this load command
+      # @note Children should override this to include additional information.
+      def to_h
+        {
+          "view" => view.to_h,
+          "cmd" => cmd,
+          "cmdsize" => cmdsize,
+          "type" => type,
+        }.merge super
+      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
@@ -304,6 +316,14 @@ module MachO
         def to_i
           @string_offset
         end
+
+        # @return [Hash] a hash representation of this {LCStr}.
+        def to_h
+          {
+            "string" => to_s,
+            "offset" => to_i,
+          }
+        end
       end
 
       # Represents the contextual information needed by a load command to
@@ -364,6 +384,14 @@ module MachO
 
         segs.join("-")
       end
+
+      # @return [Hash] returns a hash representation of this {UUIDCommand}
+      def to_h
+        {
+          "uuid" => uuid,
+          "uuid_string" => uuid_string,
+        }.merge super
+      end
     end
 
     # A load command indicating that part of this file is to be mapped into
@@ -398,7 +426,7 @@ module MachO
 
       # @see MachOStructure::FORMAT
       # @api private
-      FORMAT = "L=2a16L=4l=2L=2".freeze
+      FORMAT = "L=2Z16L=4l=2L=2".freeze
 
       # @see MachOStructure::SIZEOF
       # @api private
@@ -408,7 +436,7 @@ module MachO
       def initialize(view, cmd, cmdsize, segname, vmaddr, vmsize, fileoff,
                      filesize, maxprot, initprot, nsects, flags)
         super(view, cmd, cmdsize)
-        @segname = segname.delete("\x00")
+        @segname = segname
         @vmaddr = vmaddr
         @vmsize = vmsize
         @fileoff = fileoff
@@ -448,6 +476,42 @@ module MachO
         return false if flag.nil?
         flags & flag == flag
       end
+
+      # Guesses the alignment of the segment.
+      # @return [Integer] the guessed alignment, as a power of 2
+      # @note See `guess_align` in `cctools/misc/lipo.c`
+      def guess_align
+        return Sections::MAX_SECT_ALIGN if vmaddr.zero?
+
+        align = 0
+        segalign = 1
+
+        while (segalign & vmaddr).zero?
+          segalign <<= 1
+          align += 1
+        end
+
+        return 2 if align < 2
+        return Sections::MAX_SECT_ALIGN if align > Sections::MAX_SECT_ALIGN
+
+        align
+      end
+
+      # @return [Hash] a hash representation of this {SegmentCommand}
+      def to_h
+        {
+          "segname" => segname,
+          "vmaddr" => vmaddr,
+          "vmsize" => vmsize,
+          "fileoff" => fileoff,
+          "filesize" => filesize,
+          "maxprot" => maxprot,
+          "initprot" => initprot,
+          "nsects" => nsects,
+          "flags" => flags,
+          "sections" => sections.map(&:to_h),
+        }.merge super
+      end
     end
 
     # A load command indicating that part of this file is to be mapped into
@@ -455,7 +519,7 @@ module MachO
     class SegmentCommand64 < SegmentCommand
       # @see MachOStructure::FORMAT
       # @api private
-      FORMAT = "L=2a16Q=4l=2L=2".freeze
+      FORMAT = "L=2Z16Q=4l=2L=2".freeze
 
       # @see MachOStructure::SIZEOF
       # @api private
@@ -510,6 +574,16 @@ module MachO
         [cmd, cmdsize, string_offsets[:name], timestamp, current_version,
          compatibility_version].pack(format) + string_payload
       end
+
+      # @return [Hash] a hash representation of this {DylibCommand}
+      def to_h
+        {
+          "name" => name.to_h,
+          "timestamp" => timestamp,
+          "current_version" => current_version,
+          "compatibility_version" => compatibility_version,
+        }.merge super
+      end
     end
 
     # A load command representing some aspect of the dynamic linker, depending
@@ -546,6 +620,13 @@ module MachO
         cmdsize = SIZEOF + string_payload.bytesize
         [cmd, cmdsize, string_offsets[:name]].pack(format) + string_payload
       end
+
+      # @return [Hash] a hash representation of this {DylinkerCommand}
+      def to_h
+        {
+          "name" => name.to_h,
+        }.merge super
+      end
     end
 
     # A load command used to indicate dynamic libraries used in prebinding.
@@ -576,6 +657,15 @@ module MachO
         @nmodules = nmodules
         @linked_modules = linked_modules
       end
+
+      # @return [Hash] a hash representation of this {PreboundDylibCommand}
+      def to_h
+        {
+          "name" => name.to_h,
+          "nmodules" => nmodules,
+          "linked_modules" => linked_modules,
+        }.merge super
+      end
     end
 
     # A load command used to represent threads.
@@ -641,6 +731,20 @@ module MachO
         @reserved5 = reserved5
         @reserved6 = reserved6
       end
+
+      # @return [Hash] a hash representation of this {RoutinesCommand}
+      def to_h
+        {
+          "init_address" => init_address,
+          "init_module" => init_module,
+          "reserved1" => reserved1,
+          "reserved2" => reserved2,
+          "reserved3" => reserved3,
+          "reserved4" => reserved4,
+          "reserved5" => reserved5,
+          "reserved6" => reserved6,
+        }.merge super
+      end
     end
 
     # A load command containing the address of the dynamic shared library
@@ -675,6 +779,13 @@ module MachO
         super(view, cmd, cmdsize)
         @umbrella = LCStr.new(self, umbrella)
       end
+
+      # @return [Hash] a hash representation of this {SubFrameworkCommand}
+      def to_h
+        {
+          "umbrella" => umbrella.to_h,
+        }.merge super
+      end
     end
 
     # A load command signifying membership of a subumbrella containing the name
@@ -696,6 +807,13 @@ module MachO
         super(view, cmd, cmdsize)
         @sub_umbrella = LCStr.new(self, sub_umbrella)
       end
+
+      # @return [Hash] a hash representation of this {SubUmbrellaCommand}
+      def to_h
+        {
+          "sub_umbrella" => sub_umbrella.to_h,
+        }.merge super
+      end
     end
 
     # A load command signifying a sublibrary of a shared library. Corresponds
@@ -717,6 +835,13 @@ module MachO
         super(view, cmd, cmdsize)
         @sub_library = LCStr.new(self, sub_library)
       end
+
+      # @return [Hash] a hash representation of this {SubLibraryCommand}
+      def to_h
+        {
+          "sub_library" => sub_library.to_h,
+        }.merge super
+      end
     end
 
     # A load command signifying a shared library that is a subframework of
@@ -738,6 +863,13 @@ module MachO
         super(view, cmd, cmdsize)
         @sub_client = LCStr.new(self, sub_client)
       end
+
+      # @return [Hash] a hash representation of this {SubClientCommand}
+      def to_h
+        {
+          "sub_client" => sub_client.to_h,
+        }.merge super
+      end
     end
 
     # A load command containing the offsets and sizes of the link-edit 4.3BSD
@@ -749,10 +881,10 @@ module MachO
       # @return [Integer] the number of symbol table entries
       attr_reader :nsyms
 
-      # @return the string table's offset
+      # @return [Integer] the string table's offset
       attr_reader :stroff
 
-      # @return the string table size in bytes
+      # @return [Integer] the string table size in bytes
       attr_reader :strsize
 
       # @see MachOStructure::FORMAT
@@ -771,6 +903,16 @@ module MachO
         @stroff = stroff
         @strsize = strsize
       end
+
+      # @return [Hash] a hash representation of this {SymtabCommand}
+      def to_h
+        {
+          "symoff" => symoff,
+          "nsyms" => nsyms,
+          "stroff" => stroff,
+          "strsize" => strsize,
+        }.merge super
+      end
     end
 
     # A load command containing symbolic information needed to support data
@@ -864,6 +1006,30 @@ module MachO
         @locreloff = locreloff
         @nlocrel = nlocrel
       end
+
+      # @return [Hash] a hash representation of this {DysymtabCommand}
+      def to_h
+        {
+          "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,
+        }.merge super
+      end
     end
 
     # A load command containing the offset and number of hints in the two-level
@@ -895,6 +1061,15 @@ module MachO
         @table = TwolevelHintsTable.new(view, htoffset, nhints)
       end
 
+      # @return [Hash] a hash representation of this {TwolevelHintsCommand}
+      def to_h
+        {
+          "htoffset" => htoffset,
+          "nhints" => nhints,
+          "table" => table.hints.map(&:to_h),
+        }.merge super
+      end
+
       # A representation of the two-level namespace lookup hints table exposed
       # by a {TwolevelHintsCommand} (`LC_TWOLEVEL_HINTS`).
       class TwolevelHintsTable
@@ -927,6 +1102,14 @@ module MachO
             @isub_image = blob >> 24
             @itoc = blob & 0x00FFFFFF
           end
+
+          # @return [Hash] a hash representation of this {TwolevelHint}
+          def to_h
+            {
+              "isub_image" => isub_image,
+              "itoc" => itoc,
+            }
+          end
         end
       end
     end
@@ -950,6 +1133,13 @@ module MachO
         super(view, cmd, cmdsize)
         @cksum = cksum
       end
+
+      # @return [Hash] a hash representation of this {PrebindCksumCommand}
+      def to_h
+        {
+          "cksum" => cksum,
+        }.merge super
+      end
     end
 
     # A load command representing an rpath, which specifies a path that should
@@ -984,6 +1174,13 @@ module MachO
         cmdsize = SIZEOF + string_payload.bytesize
         [cmd, cmdsize, string_offsets[:path]].pack(format) + string_payload
       end
+
+      # @return [Hash] a hash representation of this {RpathCommand}
+      def to_h
+        {
+          "path" => path.to_h,
+        }.merge super
+      end
     end
 
     # A load command representing the offsets and sizes of a blob of data in
@@ -1011,6 +1208,14 @@ module MachO
         @dataoff = dataoff
         @datasize = datasize
       end
+
+      # @return [Hash] a hash representation of this {LinkeditDataCommand}
+      def to_h
+        {
+          "dataoff" => dataoff,
+          "datasize" => datasize,
+        }.merge super
+      end
     end
 
     # A load command representing the offset to and size of an encrypted
@@ -1040,20 +1245,20 @@ module MachO
         @cryptsize = cryptsize
         @cryptid = cryptid
       end
+
+      # @return [Hash] a hash representation of this {EncryptionInfoCommand}
+      def to_h
+        {
+          "cryptoff" => cryptoff,
+          "cryptsize" => cryptsize,
+          "cryptid" => cryptid,
+        }.merge super
+      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 [Integer] the offset to the encrypted segment
-      attr_reader :cryptoff
-
-      # @return [Integer] the size of the encrypted segment
-      attr_reader :cryptsize
-
-      # @return [Integer] the encryption system, or 0 if not encrypted yet
-      attr_reader :cryptid
-
+    class EncryptionInfoCommand64 < EncryptionInfoCommand
       # @return [Integer] 64-bit padding value
       attr_reader :pad
 
@@ -1067,12 +1272,16 @@ module MachO
 
       # @api private
       def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid, pad)
-        super(view, cmd, cmdsize)
-        @cryptoff = cryptoff
-        @cryptsize = cryptsize
-        @cryptid = cryptid
+        super(view, cmd, cmdsize, cryptoff, cryptsize, cryptid)
         @pad = pad
       end
+
+      # @return [Hash] a hash representation of this {EncryptionInfoCommand64}
+      def to_h
+        {
+          "pad" => pad,
+        }.merge super
+      end
     end
 
     # A load command containing the minimum OS version on which the binary
@@ -1121,6 +1330,16 @@ module MachO
 
         segs.join(".")
       end
+
+      # @return [Hash] a hash representation of this {VersionMinCommand}
+      def to_h
+        {
+          "version" => version,
+          "version_string" => version_string,
+          "sdk" => sdk,
+          "sdk_string" => sdk_string,
+        }.merge super
+      end
     end
 
     # A load command containing the minimum OS version on which
@@ -1156,6 +1375,40 @@ module MachO
         @tool_entries = ToolEntries.new(view, ntools)
       end
 
+      # A string representation of the binary's minimum OS version.
+      # @return [String] a string representing the minimum OS version.
+      def minos_string
+        binary = "%032b" % minos
+        segs = [
+          binary[0..15], binary[16..23], binary[24..31]
+        ].map { |s| s.to_i(2) }
+
+        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) }
+
+        segs.join(".")
+      end
+
+      # @return [Hash] a hash representation of this {BuildVersionCommand}
+      def to_h
+        {
+          "platform" => platform,
+          "minos" => minos,
+          "minos_string" => minos_string,
+          "sdk" => sdk,
+          "sdk_string" => sdk_string,
+          "tool_entries" => tool_entries.tools.map(&:to_h),
+        }.merge super
+      end
+
       # A representation of the tool versions exposed
       # by a {BuildVersionCommand} (`LC_BUILD_VERSION`).
       class ToolEntries
@@ -1181,37 +1434,23 @@ module MachO
           # @return [Integer] the tool's version number
           attr_reader :version
 
-          # @param tool 32-bit integer
-          # # @param version 32-bit integer
+          # @param tool [Integer] 32-bit integer
+          # @param version [Integer] 32-bit integer
           # @api private
           def initialize(tool, version)
             @tool = tool
             @version = version
           end
+
+          # @return [Hash] a hash representation of this {Tool}
+          def to_h
+            {
+              "tool" => tool,
+              "version" => version,
+            }
+          end
         end
       end
-
-      # A string representation of the binary's minimum OS version.
-      # @return [String] a string representing the minimum OS version.
-      def minos_string
-        binary = "%032b" % minos
-        segs = [
-          binary[0..15], binary[16..23], binary[24..31]
-        ].map { |s| s.to_i(2) }
-
-        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) }
-
-        segs.join(".")
-      end
     end
 
     # A load command containing the file offsets and sizes of the new
@@ -1272,6 +1511,22 @@ module MachO
         @export_off = export_off
         @export_size = export_size
       end
+
+      # @return [Hash] a hash representation of this {DyldInfoCommand}
+      def to_h
+        {
+          "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,
+        }.merge super
+      end
     end
 
     # A load command containing linker options embedded in object files.
@@ -1293,6 +1548,13 @@ module MachO
         super(view, cmd, cmdsize)
         @count = count
       end
+
+      # @return [Hash] a hash representation of this {LinkerOptionCommand}
+      def to_h
+        {
+          "count" => count,
+        }.merge super
+      end
     end
 
     # A load command specifying the offset of main(). Corresponds to LC_MAIN.
@@ -1317,6 +1579,14 @@ module MachO
         @entryoff = entryoff
         @stacksize = stacksize
       end
+
+      # @return [Hash] a hash representation of this {EntryPointCommand}
+      def to_h
+        {
+          "entryoff" => entryoff,
+          "stacksize" => stacksize,
+        }.merge super
+      end
     end
 
     # A load command specifying the version of the sources used to build the
@@ -1350,6 +1620,14 @@ module MachO
 
         segs.join(".")
       end
+
+      # @return [Hash] a hash representation of this {SourceVersionCommand}
+      def to_h
+        {
+          "version" => version,
+          "version_string" => version_string,
+        }.merge super
+      end
     end
 
     # An obsolete load command containing the offset and size of the (GNU style)
@@ -1375,6 +1653,14 @@ module MachO
         @offset = offset
         @size = size
       end
+
+      # @return [Hash] a hash representation of this {SymsegCommand}
+      def to_h
+        {
+          "offset" => offset,
+          "size" => size,
+        }.merge super
+      end
     end
 
     # An obsolete load command containing a free format string table. Each
@@ -1412,6 +1698,14 @@ module MachO
         @name = LCStr.new(self, name)
         @header_addr = header_addr
       end
+
+      # @return [Hash] a hash representation of this {FvmfileCommand}
+      def to_h
+        {
+          "name" => name.to_h,
+          "header_addr" => header_addr,
+        }.merge super
+      end
     end
 
     # An obsolete load command containing the path to a library to be loaded
@@ -1440,6 +1734,52 @@ module MachO
         @minor_version = minor_version
         @header_addr = header_addr
       end
+
+      # @return [Hash] a hash representation of this {FvmlibCommand}
+      def to_h
+        {
+          "name" => name.to_h,
+          "minor_version" => minor_version,
+          "header_addr" => header_addr,
+        }.merge super
+      end
+    end
+
+    # A load command containing an owner name and offset/size for an arbitrary data region.
+    # Corresponds to LC_NOTE.
+    class NoteCommand < LoadCommand
+      # @return [String] the name of the owner for this note
+      attr_reader :data_owner
+
+      # @return [Integer] the offset, within the file, of the note
+      attr_reader :offset
+
+      # @return [Integer] the size, in bytes, of the note
+      attr_reader :size
+
+      # @see MachOStructure::FORMAT
+      # @api private
+      FORMAT = "L=2Z16Q=2".freeze
+
+      # @see MachOStructure::SIZEOF
+      # @api private
+      SIZEOF = 48
+
+      def initialize(view, cmd, cmdsize, data_owner, offset, size)
+        super(view, cmd, cmdsize)
+        @data_owner = data_owner
+        @offset = offset
+        @size = size
+      end
+
+      # @return [Hash] a hash representation of this {NoteCommand}
+      def to_h
+        {
+          "data_owner" => data_owner,
+          "offset" => offset,
+          "size" => size,
+        }.merge super
+      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 f8c0bd1c57..3c61bf06b7 100644
--- a/Library/Homebrew/vendor/macho/macho/macho_file.rb
+++ b/Library/Homebrew/vendor/macho/macho/macho_file.rb
@@ -25,7 +25,7 @@ module MachO
     # @note load commands are provided in order of ascending offset.
     attr_reader :load_commands
 
-    # Creates a new MachOFile instance from a binary string.
+    # Creates a new instance from a binary string.
     # @param bin [String] a binary string containing raw Mach-O data
     # @return [MachOFile] a new MachOFile
     def self.new_from_bin(bin)
@@ -35,7 +35,7 @@ module MachO
       instance
     end
 
-    # Creates a new FatFile from the given filename.
+    # Creates a new instance from data read from the given filename.
     # @param filename [String] the Mach-O file to load from
     # @raise [ArgumentError] if the given file does not exist
     def initialize(filename)
@@ -219,8 +219,7 @@ module MachO
       update_sizeofcmds(sizeofcmds - lc.cmdsize)
 
       # pad the space after the load commands to preserve offsets
-      null_pad = "\x00" * lc.cmdsize
-      @raw_data.insert(header.class.bytesize + sizeofcmds - lc.cmdsize, null_pad)
+      @raw_data.insert(header.class.bytesize + sizeofcmds - lc.cmdsize, Utils.nullpad(lc.cmdsize))
 
       populate_fields if options.fetch(:repopulate, true)
     end
@@ -252,6 +251,33 @@ module MachO
       end
     end
 
+    # The segment alignment for the Mach-O. Guesses conservatively.
+    # @return [Integer] the alignment, as a power of 2
+    # @note This is **not** the same as {#alignment}!
+    # @note See `get_align` and `get_align_64` in `cctools/misc/lipo.c`
+    def segment_alignment
+      # special cases: 12 for x86/64/PPC/PP64, 14 for ARM/ARM64
+      return 12 if %i[i386 x86_64 ppc ppc64].include?(cputype)
+      return 14 if %i[arm arm64].include?(cputype)
+
+      cur_align = Sections::MAX_SECT_ALIGN
+
+      segments.each do |segment|
+        if filetype == :object
+          # start with the smallest alignment, and work our way up
+          align = magic32? ? 2 : 3
+          segment.sections.each do |section|
+            align = section.align unless section.align <= align
+          end
+        else
+          align = segment.guess_align
+        end
+        cur_align = align if align < cur_align
+      end
+
+      cur_align
+    end
+
     # The Mach-O's dylib ID, or `nil` if not a dylib.
     # @example
     #  file.dylib_id # => 'libBar.dylib'
@@ -408,6 +434,14 @@ module MachO
       File.open(@filename, "wb") { |f| f.write(@raw_data) }
     end
 
+    # @return [Hash] a hash representation of this {MachOFile}
+    def to_h
+      {
+        "header" => header.to_h,
+        "load_commands" => load_commands.map(&:to_h),
+      }
+    end
+
     private
 
     # The file's Mach-O header structure.
diff --git a/Library/Homebrew/vendor/macho/macho/sections.rb b/Library/Homebrew/vendor/macho/macho/sections.rb
index 7e96888dbb..61d646743b 100644
--- a/Library/Homebrew/vendor/macho/macho/sections.rb
+++ b/Library/Homebrew/vendor/macho/macho/sections.rb
@@ -13,6 +13,10 @@ module MachO
     # system settable attributes mask
     SECTION_ATTRIBUTES_SYS = 0x00ffff00
 
+    # maximum specifiable section alignment, as a power of 2
+    # @note see `MAXSECTALIGN` macro in `cctools/misc/lipo.c`
+    MAX_SECT_ALIGN = 15
+
     # association of section flag symbols to values
     # @api private
     SECTION_FLAGS = {
@@ -104,7 +108,7 @@ module MachO
       attr_reader :reserved2
 
       # @see MachOStructure::FORMAT
-      FORMAT = "a16a16L=9".freeze
+      FORMAT = "Z16Z16L=9".freeze
 
       # @see MachOStructure::SIZEOF
       SIZEOF = 68
@@ -125,16 +129,14 @@ module MachO
         @reserved2 = reserved2
       end
 
-      # @return [String] the section's name, with any trailing NULL characters
-      #  removed
+      # @return [String] the section's name
       def section_name
-        sectname.delete("\x00")
+        sectname
       end
 
-      # @return [String] the parent segment's name, with any trailing NULL
-      #  characters removed
+      # @return [String] the parent segment's name
       def segment_name
-        segname.delete("\x00")
+        segname
       end
 
       # @return [Boolean] whether the section is empty (i.e, {size} is 0)
@@ -151,6 +153,23 @@ module MachO
         return false if flag.nil?
         flags & flag == flag
       end
+
+      # @return [Hash] a hash representation of this {Section}
+      def to_h
+        {
+          "sectname" => sectname,
+          "segname" => segname,
+          "addr" => addr,
+          "size" => size,
+          "offset" => offset,
+          "align" => align,
+          "reloff" => reloff,
+          "nreloc" => nreloc,
+          "flags" => flags,
+          "reserved1" => reserved1,
+          "reserved2" => reserved2,
+        }.merge super
+      end
     end
 
     # Represents a section of a segment for 64-bit architectures.
@@ -159,7 +178,7 @@ module MachO
       attr_reader :reserved3
 
       # @see MachOStructure::FORMAT
-      FORMAT = "a16a16Q=2L=8".freeze
+      FORMAT = "Z16Z16Q=2L=8".freeze
 
       # @see MachOStructure::SIZEOF
       SIZEOF = 80
@@ -171,6 +190,13 @@ module MachO
           nreloc, flags, reserved1, reserved2)
         @reserved3 = reserved3
       end
+
+      # @return [Hash] a hash representation of this {Section64}
+      def to_h
+        {
+          "reserved3" => reserved3,
+        }.merge super
+      end
     end
   end
 end
diff --git a/Library/Homebrew/vendor/macho/macho/structure.rb b/Library/Homebrew/vendor/macho/macho/structure.rb
index 792aeeeac1..7bece4d704 100644
--- a/Library/Homebrew/vendor/macho/macho/structure.rb
+++ b/Library/Homebrew/vendor/macho/macho/structure.rb
@@ -26,5 +26,15 @@ module MachO
 
       new(*bin.unpack(format))
     end
+
+    # @return [Hash] a hash representation of this {MachOStructure}.
+    def to_h
+      {
+        "structure" => {
+          "format" => self.class::FORMAT,
+          "bytesize" => self.class.bytesize,
+        },
+      }
+    end
   end
 end
diff --git a/Library/Homebrew/vendor/macho/macho/utils.rb b/Library/Homebrew/vendor/macho/macho/utils.rb
index 3bc81df478..a766de23ce 100644
--- a/Library/Homebrew/vendor/macho/macho/utils.rb
+++ b/Library/Homebrew/vendor/macho/macho/utils.rb
@@ -22,6 +22,16 @@ module MachO
       round(size, alignment) - size
     end
 
+    # Returns a string of null bytes of the requested (non-negative) size
+    # @param size [Integer] the size of the nullpad
+    # @return [String] the null string (or empty string, for `size = 0`)
+    # @raise [ArgumentError] if a non-positive nullpad is requested
+    def self.nullpad(size)
+      raise ArgumentError, "size < 0: #{size}" if size.negative?
+
+      "\x00" * size
+    end
+
     # Converts an abstract (native-endian) String#unpack format to big or
     #  little.
     # @param format [String] the format string being converted
@@ -46,11 +56,11 @@ module MachO
       strings.each do |key, string|
         offsets[key] = next_offset
         payload << string
-        payload << "\x00"
+        payload << Utils.nullpad(1)
         next_offset += string.bytesize + 1
       end
 
-      payload << "\x00" * padding_for(fixed_offset + payload.bytesize, alignment)
+      payload << Utils.nullpad(padding_for(fixed_offset + payload.bytesize, alignment))
       [payload, offsets]
     end
 
diff --git a/Library/Homebrew/vendor/macho/macho/view.rb b/Library/Homebrew/vendor/macho/macho/view.rb
index c03c2580e8..5bd40e7f0e 100644
--- a/Library/Homebrew/vendor/macho/macho/view.rb
+++ b/Library/Homebrew/vendor/macho/macho/view.rb
@@ -19,5 +19,13 @@ module MachO
       @endianness = endianness
       @offset = offset
     end
+
+    # @return [Hash] a hash representation of this {MachOView}.
+    def to_h
+      {
+        "endianness" => endianness,
+        "offset" => offset,
+      }
+    end
   end
 end
diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock
index f6ac42974d..44706003db 100644
--- a/docs/Gemfile.lock
+++ b/docs/Gemfile.lock
@@ -26,10 +26,10 @@ GEM
     execjs (2.7.0)
     faraday (0.15.2)
       multipart-post (>= 1.2, < 3)
-    ffi (1.9.23)
+    ffi (1.9.25)
     forwardable-extended (2.6.0)
     gemoji (3.0.0)
-    github-pages (186)
+    github-pages (187)
       activesupport (= 4.2.10)
       github-pages-health-check (= 1.8.1)
       jekyll (= 3.7.3)
@@ -48,7 +48,7 @@ GEM
       jekyll-relative-links (= 0.5.3)
       jekyll-remote-theme (= 0.3.1)
       jekyll-sass-converter (= 1.5.2)
-      jekyll-seo-tag (= 2.4.0)
+      jekyll-seo-tag (= 2.5.0)
       jekyll-sitemap (= 1.2.0)
       jekyll-swiss (= 0.4.0)
       jekyll-theme-architect (= 0.1.1)
@@ -80,7 +80,7 @@ GEM
       octokit (~> 4.0)
       public_suffix (~> 2.0)
       typhoeus (~> 1.3)
-    html-pipeline (2.8.0)
+    html-pipeline (2.8.3)
       activesupport (>= 2)
       nokogiri (>= 1.4)
     http_parser.rb (0.6.0)
@@ -138,7 +138,7 @@ GEM
       rubyzip (>= 1.2.1, < 3.0)
     jekyll-sass-converter (1.5.2)
       sass (~> 3.4)
-    jekyll-seo-tag (2.4.0)
+    jekyll-seo-tag (2.5.0)
       jekyll (~> 3.3)
     jekyll-sitemap (1.2.0)
       jekyll (~> 3.3)
@@ -206,7 +206,7 @@ GEM
       jekyll-seo-tag (~> 2.1)
     minitest (5.11.3)
     multipart-post (2.0.0)
-    nokogiri (1.8.2)
+    nokogiri (1.8.3)
       mini_portile2 (~> 2.3.0)
     octokit (4.9.0)
       sawyer (~> 0.8.0, >= 0.5.3)
@@ -237,7 +237,7 @@ GEM
       ethon (>= 0.9.0)
     tzinfo (1.2.5)
       thread_safe (~> 0.1)
-    unicode-display_width (1.3.3)
+    unicode-display_width (1.4.0)
 
 PLATFORMS
   ruby