Merge branch 'master' of github.com:Homebrew/brew into update-compiler-docs

This commit is contained in:
Afnan Enayet 2018-07-05 17:23:09 -04:00
commit fd84bcbff7
No known key found for this signature in database
GPG Key ID: 9C669708328BC5A4
45 changed files with 1003 additions and 401 deletions

View File

@ -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:

View File

@ -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 <<EOS
Your HOMEBREW_PREFIX is in the Homebrew temporary directory, which Homebrew
uses to store downloads and builds. You can resolve this by installing Homebrew to
either the standard prefix (/usr/local) or to a non-standard prefix that is not
in the Homebrew temporary directory.
EOS
fi
}
check-prefix-is-not-tmpdir
if [[ "$HOMEBREW_PREFIX" = "/usr/local" &&
"$HOMEBREW_PREFIX" != "$HOMEBREW_REPOSITORY" &&
"$HOMEBREW_CELLAR" = "$HOMEBREW_REPOSITORY/Cellar" ]]

View File

@ -201,21 +201,20 @@ module Hbc
def check_hosting_with_appcast
return if cask.appcast
check_github_releases_appcast
check_sourceforge_appcast
end
def check_github_releases_appcast
return unless cask.url.to_s =~ %r{github.com/([^/]+)/([^/]+)/releases/download/(\S+)}
add_appcast = "please add an appcast. See https://github.com/Homebrew/homebrew-cask/blob/master/doc/cask_language_reference/stanzas/appcast.md"
add_warning "Download uses GitHub releases, please add an appcast. See https://github.com/Homebrew/homebrew-cask/blob/master/doc/cask_language_reference/stanzas/appcast.md"
end
def check_sourceforge_appcast
return if cask.version.latest?
return unless cask.url.to_s =~ %r{sourceforge.net/(\S+)}
add_warning "Download is hosted on SourceForge, please add an appcast. See https://github.com/Homebrew/homebrew-cask/blob/master/doc/cask_language_reference/stanzas/appcast.md"
case cask.url.to_s
when %r{github.com/([^/]+)/([^/]+)/releases/download/(\S+)}
add_warning "Download uses GitHub releases, #{add_appcast}"
when %r{sourceforge.net/(\S+)}
return if cask.version.latest?
add_warning "Download is hosted on SourceForge, #{add_appcast}"
when %r{dl.devmate.com/(\S+)}
add_warning "Download is hosted on DevMate, #{add_appcast}"
when %r{rink.hockeyapp.net/(\S+)}
add_warning "Download is hosted on HockeyApp, #{add_appcast}"
end
end
def check_url

View File

@ -62,13 +62,11 @@ module Homebrew
if tap.installed?
info += tap.pinned? ? "pinned" : "unpinned"
info += ", private" if tap.private?
if (formula_count = tap.formula_files.size).positive?
info += ", #{Formatter.pluralize(formula_count, "formula")}"
info += if (contents = tap.contents).empty?
", no commands/casks/formulae"
else
", #{contents.join(", ")}"
end
if (command_count = tap.command_files.size).positive?
info += ", #{Formatter.pluralize(command_count, "command")}"
end
info += ", no formulae/commands" if (formula_count + command_count).zero?
info += "\n#{tap.path} (#{tap.path.abv})"
info += "\nFrom: #{tap.remote.nil? ? "N/A" : tap.remote}"
else

View File

@ -39,7 +39,13 @@ HOMEBREW_CACHE_FORMULA = HOMEBREW_CACHE/"Formula"
HOMEBREW_LOGS = Pathname.new(ENV["HOMEBREW_LOGS"] || "~/Library/Logs/Homebrew/").expand_path
# Must use /tmp instead of $TMPDIR because long paths break Unix domain sockets
HOMEBREW_TEMP = Pathname.new(ENV.fetch("HOMEBREW_TEMP", "/tmp"))
HOMEBREW_TEMP = begin
# /tmp fallback is here for people auto-updating from a version where
# HOMEBREW_TEMP isn't set.
tmp = Pathname.new(ENV["HOMEBREW_TEMP"] || "/tmp")
tmp.mkpath unless tmp.exist?
tmp.realpath
end
unless defined? HOMEBREW_LIBRARY_PATH
# Root of the Homebrew code base

View File

@ -216,7 +216,7 @@ module Homebrew
def initialize(formula, options = {})
@formula = formula
@new_formula = options[:new_formula]
@new_formula = options[:new_formula] && !formula.versioned_formula?
@strict = options[:strict]
@online = options[:online]
@display_cop_names = options[:display_cop_names]
@ -236,6 +236,7 @@ module Homebrew
return unless @style_offenses
@style_offenses.each do |offense|
if offense.cop_name.start_with?("NewFormulaAudit")
next if formula.versioned_formula?
new_formula_problem offense.to_s(display_cop_name: @display_cop_names)
next
end
@ -416,7 +417,6 @@ module Homebrew
end
next unless @new_formula
next if formula.versioned_formula?
next unless @official_tap
if dep.tags.include?(:recommended) || dep.tags.include?(:optional)
new_formula_problem "Formulae should not have #{dep.tags} dependencies."
@ -571,8 +571,19 @@ module Homebrew
end
end
if @new_formula && formula.head
new_formula_problem "Formulae should not have a HEAD spec"
if formula.head || formula.devel
unstable_spec_message = "Formulae should not have an unstable spec"
if @new_formula
new_formula_problem unstable_spec_message
elsif formula.versioned_formula?
versioned_unstable_spec = %w[
bash-completion@2
imagemagick@6
openssl@1.1
python@2
]
problem unstable_spec_message unless versioned_unstable_spec.include?(formula.name)
end
end
throttled = %w[
@ -586,7 +597,7 @@ module Homebrew
throttled.each_slice(2).to_a.map do |a, b|
next if formula.stable.nil?
version = formula.stable.version.to_s.split(".").last.to_i
if @strict && a.include?(formula.name) && version.modulo(b.to_i).nonzero?
if @strict && a == formula.name && version.modulo(b.to_i).nonzero?
problem "should only be updated every #{b} releases on multiples of #{b}"
end
end

View File

@ -31,49 +31,50 @@ module Homebrew
ENV["HOMEBREW_UPDATE_TEST"] = "1"
if args.to_tag?
branch = if args.to_tag?
ENV["HOMEBREW_UPDATE_TO_TAG"] = "1"
branch = "stable"
"stable"
else
branch = "master"
"master"
end
cd HOMEBREW_REPOSITORY
start_commit = if commit = args.commit
commit
elsif date = args.before
Utils.popen_read("git", "rev-list", "-n1", "--before=#{date}", "origin/master").chomp
elsif args.to_tag?
tags = Utils.popen_read("git", "tag", "--list", "--sort=-version:refname")
previous_tag = tags.lines[1]
previous_tag ||= begin
if (HOMEBREW_REPOSITORY/".git/shallow").exist?
safe_system "git", "fetch", "--tags", "--depth=1"
tags = Utils.popen_read("git", "tag", "--list", "--sort=-version:refname")
elsif OS.linux?
tags = Utils.popen_read("git tag --list | sort -rV")
start_commit, end_commit = nil
cd HOMEBREW_REPOSITORY do
start_commit = if commit = args.commit
commit
elsif date = args.before
Utils.popen_read("git", "rev-list", "-n1", "--before=#{date}", "origin/master").chomp
elsif args.to_tag?
tags = Utils.popen_read("git", "tag", "--list", "--sort=-version:refname")
previous_tag = tags.lines[1]
previous_tag ||= begin
if (HOMEBREW_REPOSITORY/".git/shallow").exist?
safe_system "git", "fetch", "--tags", "--depth=1"
tags = Utils.popen_read("git", "tag", "--list", "--sort=-version:refname")
elsif OS.linux?
tags = Utils.popen_read("git tag --list | sort -rV")
end
tags.lines[1]
end
tags.lines[1]
previous_tag = previous_tag.to_s.chomp
odie "Could not find previous tag in:\n#{tags}" if previous_tag.empty?
previous_tag
else
Utils.popen_read("git", "rev-parse", "origin/master").chomp
end
previous_tag = previous_tag.to_s.chomp
odie "Could not find previous tag in:\n#{tags}" if previous_tag.empty?
previous_tag
else
Utils.popen_read("git", "rev-parse", "origin/master").chomp
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?
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}"
mktemp("update-test") do |staging|
staging.retain! if args.keep_tmp?
mkdir "update-test" do
curdir = Pathname.new(Dir.pwd)
oh1 "Setup test environment..."
@ -107,5 +108,7 @@ module Homebrew
EOS
end
end
ensure
FileUtils.rm_r "update-test" unless args.keep_tmp?
end
end

View File

@ -90,53 +90,6 @@ class AbstractDownloadStrategy
def quiet_safe_system(*args)
safe_system(*expand_safe_system_args(args))
end
private
def xzpath
"#{HOMEBREW_PREFIX}/opt/xz/bin/xz"
end
def lzippath
"#{HOMEBREW_PREFIX}/opt/lzip/bin/lzip"
end
def lhapath
"#{HOMEBREW_PREFIX}/opt/lha/bin/lha"
end
def cvspath
@cvspath ||= %W[
/usr/bin/cvs
#{HOMEBREW_PREFIX}/bin/cvs
#{HOMEBREW_PREFIX}/opt/cvs/bin/cvs
#{which("cvs")}
].find { |p| File.executable? p }
end
def hgpath
@hgpath ||= %W[
#{which("hg")}
#{HOMEBREW_PREFIX}/bin/hg
#{HOMEBREW_PREFIX}/opt/mercurial/bin/hg
].find { |p| File.executable? p }
end
def bzrpath
@bzrpath ||= %W[
#{which("bzr")}
#{HOMEBREW_PREFIX}/bin/bzr
#{HOMEBREW_PREFIX}/opt/bazaar/bin/bzr
].find { |p| File.executable? p }
end
def fossilpath
@fossilpath ||= %W[
#{which("fossil")}
#{HOMEBREW_PREFIX}/bin/fossil
#{HOMEBREW_PREFIX}/opt/fossil/bin/fossil
].find { |p| File.executable? p }
end
end
class VCSDownloadStrategy < AbstractDownloadStrategy
@ -224,43 +177,39 @@ end
class AbstractFileDownloadStrategy < AbstractDownloadStrategy
def stage
case type = cached_location.compression_type
path = cached_location
unpack_dir = Pathname.pwd
case type = path.compression_type
when :zip
quiet_safe_system "unzip", "-qq", cached_location
safe_system "unzip", "-qq", path, "-d", unpack_dir
chdir
when :gzip_only
buffered_write "gunzip"
FileUtils.cp path, unpack_dir, preserve: true
safe_system "gunzip", "-q", "-N", unpack_dir/path.basename
when :bzip2_only
buffered_write "bunzip2"
FileUtils.cp path, unpack_dir, preserve: true
safe_system "bunzip2", "-q", unpack_dir/path.basename
when :gzip, :bzip2, :xz, :compress, :tar
tar_flags = "x"
if type == :gzip
tar_flags << "z"
elsif type == :bzip2
tar_flags << "j"
elsif type == :xz
tar_flags << "J"
end
tar_flags << "f"
if type == :xz && DependencyCollector.tar_needs_xz_dependency?
pipe_to_tar xzpath
pipe_to_tar "#{HOMEBREW_PREFIX}/opt/xz/bin/xz", unpack_dir
else
safe_system "tar", tar_flags, cached_location
safe_system "tar", "xf", path, "-C", unpack_dir
end
chdir
when :lzip
pipe_to_tar lzippath
pipe_to_tar "#{HOMEBREW_PREFIX}/opt/lzip/bin/lzip", unpack_dir
chdir
when :lha
safe_system lhapath, "x", cached_location
safe_system "#{HOMEBREW_PREFIX}/opt/lha/bin/lha", "xq2w=#{unpack_dir}", path
when :xar
safe_system "/usr/bin/xar", "-xf", cached_location
safe_system "xar", "-x", "-f", path, "-C", unpack_dir
when :rar
quiet_safe_system "unrar", "x", "-inul", cached_location
safe_system "unrar", "x", "-inul", path, unpack_dir
when :p7zip
safe_system "7zr", "x", cached_location
safe_system "7zr", "x", "-y", "-bd", "-bso0", path, "-o#{unpack_dir}"
else
cp cached_location, basename_without_params, preserve: true
cp path, unpack_dir/basename_without_params, preserve: true
end
end
@ -278,29 +227,17 @@ class AbstractFileDownloadStrategy < AbstractDownloadStrategy
end
end
def pipe_to_tar(tool)
Utils.popen_read(tool, "-dc", cached_location.to_s) do |rd|
Utils.popen_write("tar", "xf", "-") do |wr|
def pipe_to_tar(tool, unpack_dir)
path = cached_location
Utils.popen_read(tool, "-dc", path) do |rd|
Utils.popen_write("tar", "xf", "-", "-C", unpack_dir) do |wr|
buf = ""
wr.write(buf) while rd.read(16384, buf)
end
end
end
# gunzip and bunzip2 write the output file in the same directory as the input
# file regardless of the current working directory, so we need to write it to
# the correct location ourselves.
def buffered_write(tool)
target = File.basename(basename_without_params, cached_location.extname)
Utils.popen_read(tool, "-f", cached_location.to_s, "-c") do |pipe|
File.open(target, "wb") do |f|
buf = ""
f.write(buf) while pipe.read(16384, buf)
end
end
end
def basename_without_params
# Strip any ?thing=wad out of .c?thing=wad style extensions
File.basename(@url)[/[^?]+/]
@ -672,7 +609,7 @@ class SubversionDownloadStrategy < VCSDownloadStrategy
def stage
super
quiet_safe_system "svn", "export", "--force", cached_location, Dir.pwd
safe_system "svn", "export", "--force", cached_location, Dir.pwd
end
def source_modified_time
@ -849,9 +786,9 @@ class GitDownloadStrategy < VCSDownloadStrategy
return unless @ref_type == :branch || !ref?
if !shallow_clone? && shallow_dir?
quiet_safe_system "git", "fetch", "origin", "--unshallow"
safe_system "git", "fetch", "origin", "--unshallow"
else
quiet_safe_system "git", "fetch", "origin"
safe_system "git", "fetch", "origin"
end
end
@ -866,7 +803,7 @@ class GitDownloadStrategy < VCSDownloadStrategy
def checkout
ohai "Checking out #{@ref_type} #{@ref}" if @ref_type && @ref
quiet_safe_system "git", "checkout", "-f", @ref, "--"
safe_system "git", "checkout", "-f", @ref, "--"
end
def reset_args
@ -881,12 +818,12 @@ class GitDownloadStrategy < VCSDownloadStrategy
end
def reset
quiet_safe_system "git", *reset_args
safe_system "git", *reset_args
end
def update_submodules
quiet_safe_system "git", "submodule", "foreach", "--recursive", "git submodule sync"
quiet_safe_system "git", "submodule", "update", "--init", "--recursive"
safe_system "git", "submodule", "foreach", "--recursive", "git submodule sync"
safe_system "git", "submodule", "update", "--init", "--recursive"
fix_absolute_submodule_gitdir_references!
end
@ -992,6 +929,10 @@ class CVSDownloadStrategy < VCSDownloadStrategy
end
end
def cvspath
@cvspath ||= which("cvs", PATH.new("/usr/bin", Formula["cvs"].opt_bin, ENV["PATH"]))
end
def source_modified_time
# Filter CVS's files because the timestamp for each of them is the moment
# of clone.
@ -1045,6 +986,10 @@ class MercurialDownloadStrategy < VCSDownloadStrategy
@url = @url.sub(%r{^hg://}, "")
end
def hgpath
@hgpath ||= which("hg", PATH.new(Formula["mercurial"].opt_bin, ENV["PATH"]))
end
def stage
super
@ -1082,7 +1027,9 @@ class MercurialDownloadStrategy < VCSDownloadStrategy
end
def update
cached_location.cd { quiet_safe_system hgpath, "pull", "--update" }
cached_location.cd do
safe_system hgpath, "pull", "--update"
end
end
end
@ -1093,6 +1040,10 @@ class BazaarDownloadStrategy < VCSDownloadStrategy
ENV["BZR_HOME"] = HOMEBREW_TEMP
end
def bzrpath
@bzrpath ||= which("bzr", PATH.new(Formula["bazaar"].opt_bin, ENV["PATH"]))
end
def stage
# The export command doesn't work on checkouts
# See https://bugs.launchpad.net/bzr/+bug/897511
@ -1101,13 +1052,13 @@ class BazaarDownloadStrategy < VCSDownloadStrategy
end
def source_modified_time
timestamp = Utils.popen_read("bzr", "log", "-l", "1", "--timezone=utc", cached_location.to_s)[/^timestamp: (.+)$/, 1]
timestamp = Utils.popen_read(bzrpath, "log", "-l", "1", "--timezone=utc", cached_location.to_s)[/^timestamp: (.+)$/, 1]
raise "Could not get any timestamps from bzr!" if timestamp.to_s.empty?
Time.parse timestamp
end
def last_commit
Utils.popen_read("bzr", "revno", cached_location.to_s).chomp
Utils.popen_read(bzrpath, "revno", cached_location.to_s).chomp
end
private
@ -1126,7 +1077,9 @@ class BazaarDownloadStrategy < VCSDownloadStrategy
end
def update
cached_location.cd { quiet_safe_system bzrpath, "update" }
cached_location.cd do
safe_system bzrpath, "update"
end
end
end
@ -1136,6 +1089,10 @@ class FossilDownloadStrategy < VCSDownloadStrategy
@url = @url.sub(%r{^fossil://}, "")
end
def fossilpath
@fossilpath ||= which("fossil", PATH.new(Formula["fossil"].opt_bin, ENV["PATH"]))
end
def stage
super
args = [fossilpath, "open", cached_location]
@ -1144,11 +1101,11 @@ class FossilDownloadStrategy < VCSDownloadStrategy
end
def source_modified_time
Time.parse Utils.popen_read("fossil", "info", "tip", "-R", cached_location.to_s)[/^uuid: +\h+ (.+)$/, 1]
Time.parse Utils.popen_read(fossilpath, "info", "tip", "-R", cached_location.to_s)[/^uuid: +\h+ (.+)$/, 1]
end
def last_commit
Utils.popen_read("fossil", "info", "tip", "-R", cached_location.to_s)[/^uuid: +(\h+) .+$/, 1]
Utils.popen_read(fossilpath, "info", "tip", "-R", cached_location.to_s)[/^uuid: +(\h+) .+$/, 1]
end
private

View File

@ -147,7 +147,7 @@ module SharedEnvExtension
# Outputs the current compiler.
# @return [Symbol]
# <pre># Do something only for clang
# <pre># 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"

View File

@ -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}"

View File

@ -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}"

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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_")

View File

@ -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}.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -23,21 +23,37 @@ module MachO
# 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
# @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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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