diff options
63 files changed, 715 insertions, 480 deletions
diff --git a/.travis.yml b/.travis.yml index 48e13983b..9cdebda83 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,9 @@ cache: - $HOME/.gem/ruby - $HOME/Library/Caches/Homebrew/style - $HOME/Library/Caches/Homebrew/tests - +branches: + only: + - master matrix: fast_finish: true include: diff --git a/Library/Homebrew/cask/lib/hbc/audit.rb b/Library/Homebrew/cask/lib/hbc/audit.rb index cee1fe807..b8bb6ab81 100644 --- a/Library/Homebrew/cask/lib/hbc/audit.rb +++ b/Library/Homebrew/cask/lib/hbc/audit.rb @@ -143,7 +143,15 @@ module Hbc def check_appcast_http_code odebug "Verifying appcast returns 200 HTTP response code" - result = @command.run("/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", URL::FAKE_USER_AGENT, "--output", "/dev/null", "--write-out", "%{http_code}", cask.appcast], print_stderr: false) + + curl_executable, *args = curl_args( + "--compressed", "--location", "--fail", + "--write-out", "%{http_code}", + "--output", "/dev/null", + cask.appcast, + user_agent: :fake + ) + result = @command.run(curl_executable, args: args, print_stderr: false) if result.success? http_code = result.stdout.chomp add_warning "unexpected HTTP response code retrieving appcast: #{http_code}" unless http_code == "200" diff --git a/Library/Homebrew/cask/lib/hbc/cask.rb b/Library/Homebrew/cask/lib/hbc/cask.rb index 672d18954..6d89a997c 100644 --- a/Library/Homebrew/cask/lib/hbc/cask.rb +++ b/Library/Homebrew/cask/lib/hbc/cask.rb @@ -7,9 +7,16 @@ module Hbc include Metadata attr_reader :token, :sourcefile_path - def initialize(token, sourcefile_path: nil, &block) + + def tap + return super if block_given? # Object#tap + @tap + end + + def initialize(token, sourcefile_path: nil, tap: nil, &block) @token = token @sourcefile_path = sourcefile_path + @tap = tap @dsl = DSL.new(@token) return unless block_given? @dsl.instance_eval(&block) diff --git a/Library/Homebrew/cask/lib/hbc/cask_loader.rb b/Library/Homebrew/cask/lib/hbc/cask_loader.rb index 8cd010ef6..dd9c61089 100644 --- a/Library/Homebrew/cask/lib/hbc/cask_loader.rb +++ b/Library/Homebrew/cask/lib/hbc/cask_loader.rb @@ -13,8 +13,8 @@ module Hbc private - def cask(header_token, &block) - Cask.new(header_token, &block) + def cask(header_token, **options, &block) + Cask.new(header_token, **options, &block) end end @@ -45,12 +45,12 @@ module Hbc private - def cask(header_token, &block) + def cask(header_token, **options, &block) if token != header_token raise CaskTokenMismatchError.new(token, header_token) end - Cask.new(header_token, sourcefile_path: path, &block) + super(header_token, **options, sourcefile_path: path, &block) end end @@ -71,7 +71,7 @@ module Hbc begin ohai "Downloading #{url}." - curl url, "-o", path + curl_download url, to: path rescue ErrorDuringExecution raise CaskUnavailableError.new(token, "Failed to download #{Formatter.url(url)}.") end @@ -80,18 +80,33 @@ module Hbc end end - class FromTapLoader < FromPathLoader + class FromTapPathLoader < FromPathLoader def self.can_load?(ref) - ref.to_s.match?(HOMEBREW_TAP_CASK_REGEX) + ref.to_s.match?(HOMEBREW_TAP_PATH_REGEX) && super end attr_reader :tap + def initialize(tap_path) + @tap = Tap.from_path(tap_path) + super tap_path + end + + private + + def cask(*args, &block) + super(*args, tap: tap, &block) + end + end + + class FromTapLoader < FromTapPathLoader + def self.can_load?(ref) + ref.to_s.match?(HOMEBREW_TAP_CASK_REGEX) + end + def initialize(tapped_name) user, repo, token = tapped_name.split("/", 3) - @tap = Tap.fetch(user, repo) - - super @tap.cask_dir/"#{token}.rb" + super Tap.fetch(user, repo).cask_dir/"#{token}.rb" end def load @@ -136,19 +151,26 @@ module Hbc [ FromURILoader, FromTapLoader, + FromTapPathLoader, FromPathLoader, ].each do |loader_class| return loader_class.new(ref) if loader_class.can_load?(ref) end - if FromPathLoader.can_load?(default_path(ref)) - return FromPathLoader.new(default_path(ref)) + if FromTapPathLoader.can_load?(default_path(ref)) + return FromTapPathLoader.new(default_path(ref)) end - possible_tap_casks = tap_paths(ref) - if possible_tap_casks.count == 1 - possible_tap_cask = possible_tap_casks.first - return FromPathLoader.new(possible_tap_cask) + case (possible_tap_casks = tap_paths(ref)).count + when 1 + return FromTapPathLoader.new(possible_tap_casks.first) + when 2..Float::INFINITY + loaders = possible_tap_casks.map(&FromTapPathLoader.method(:new)) + + raise CaskError, <<-EOS.undent + Cask #{ref} exists in multiple taps: + #{loaders.map { |loader| " #{loader.tap}/#{loader.token}" }.join("\n")} + EOS end possible_installed_cask = Cask.new(ref) diff --git a/Library/Homebrew/cask/lib/hbc/cli/search.rb b/Library/Homebrew/cask/lib/hbc/cli/search.rb index 643d18d55..e89dced92 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/search.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/search.rb @@ -15,8 +15,9 @@ module Hbc end def self.search_remote(query) - matches = GitHub.search_code("user:caskroom", "path:Casks", "filename:#{query}", "extension:rb") - [*matches].map do |match| + matches = GitHub.search_code(user: "caskroom", path: "Casks", + filename: query, extension: "rb") + matches.map do |match| tap = Tap.fetch(match["repository"]["full_name"]) next if tap.installed? "#{tap.name}/#{File.basename(match["path"], ".rb")}" diff --git a/Library/Homebrew/cask/lib/hbc/container.rb b/Library/Homebrew/cask/lib/hbc/container.rb index 93e825e03..fab3a3c1c 100644 --- a/Library/Homebrew/cask/lib/hbc/container.rb +++ b/Library/Homebrew/cask/lib/hbc/container.rb @@ -4,6 +4,7 @@ require "hbc/container/bzip2" require "hbc/container/cab" require "hbc/container/criteria" require "hbc/container/dmg" +require "hbc/container/directory" require "hbc/container/executable" require "hbc/container/generic_unar" require "hbc/container/gpg" @@ -14,6 +15,7 @@ require "hbc/container/otf" require "hbc/container/pkg" require "hbc/container/seven_zip" require "hbc/container/sit" +require "hbc/container/svn_repository" require "hbc/container/tar" require "hbc/container/ttf" require "hbc/container/rar" @@ -43,6 +45,7 @@ module Hbc Xz, # pure xz Gpg, # GnuPG signed data Executable, + SvnRepository, ] # for explicit use only (never autodetected): # Hbc::Container::Naked diff --git a/Library/Homebrew/cask/lib/hbc/container/criteria.rb b/Library/Homebrew/cask/lib/hbc/container/criteria.rb index 66ecb8c87..52f171d6a 100644 --- a/Library/Homebrew/cask/lib/hbc/container/criteria.rb +++ b/Library/Homebrew/cask/lib/hbc/container/criteria.rb @@ -13,9 +13,11 @@ module Hbc end def magic_number(regex) + return false if path.directory? + # 262: length of the longest regex (currently: Hbc::Container::Tar) - @magic_number ||= File.open(@path, "rb") { |f| f.read(262) } - @magic_number =~ regex + @magic_number ||= File.open(path, "rb") { |f| f.read(262) } + @magic_number.match?(regex) end end end diff --git a/Library/Homebrew/cask/lib/hbc/container/directory.rb b/Library/Homebrew/cask/lib/hbc/container/directory.rb new file mode 100644 index 000000000..e4bb1095b --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/container/directory.rb @@ -0,0 +1,24 @@ +require "hbc/container/base" + +module Hbc + class Container + class Directory < Base + def self.me?(*) + false + end + + def extract + @path.children.each do |child| + next if skip_path?(child) + FileUtils.cp child, @cask.staged_path + end + end + + private + + def skip_path?(*) + false + end + end + end +end diff --git a/Library/Homebrew/cask/lib/hbc/container/executable.rb b/Library/Homebrew/cask/lib/hbc/container/executable.rb index 848f6d4be..af3b36fd1 100644 --- a/Library/Homebrew/cask/lib/hbc/container/executable.rb +++ b/Library/Homebrew/cask/lib/hbc/container/executable.rb @@ -8,7 +8,7 @@ module Hbc return true if criteria.magic_number(/^#!\s*\S+/) begin - MachO.open(criteria.path).header.executable? + criteria.path.file? && MachO.open(criteria.path).header.executable? rescue MachO::MagicError false end diff --git a/Library/Homebrew/cask/lib/hbc/container/svn_repository.rb b/Library/Homebrew/cask/lib/hbc/container/svn_repository.rb new file mode 100644 index 000000000..cae613b2d --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/container/svn_repository.rb @@ -0,0 +1,15 @@ +require "hbc/container/directory" + +module Hbc + class Container + class SvnRepository < Directory + def self.me?(criteria) + criteria.path.join(".svn").directory? + end + + def skip_path?(path) + path.basename.to_s == ".svn" + end + end + end +end diff --git a/Library/Homebrew/cask/lib/hbc/download_strategy.rb b/Library/Homebrew/cask/lib/hbc/download_strategy.rb index 28ae704ee..245ad4ade 100644 --- a/Library/Homebrew/cask/lib/hbc/download_strategy.rb +++ b/Library/Homebrew/cask/lib/hbc/download_strategy.rb @@ -10,7 +10,7 @@ module Hbc class AbstractDownloadStrategy attr_reader :cask, :name, :url, :uri_object, :version - def initialize(cask, command = SystemCommand) + def initialize(cask, command: SystemCommand) @cask = cask @command = command # TODO: this excess of attributes is a function of integrating @@ -33,8 +33,8 @@ module Hbc class HbVCSDownloadStrategy < AbstractDownloadStrategy REF_TYPES = [:branch, :revision, :revisions, :tag].freeze - def initialize(cask, command = SystemCommand) - super + def initialize(*args, **options) + super(*args, **options) @ref_type, @ref = extract_ref @clone = Hbc.cache.join(cache_filename) end @@ -64,11 +64,6 @@ module Hbc end class CurlDownloadStrategy < AbstractDownloadStrategy - # TODO: should be part of url object - def mirrors - @mirrors ||= [] - end - def tarball_path @tarball_path ||= Hbc.cache.join("#{name}--#{version}#{ext}") end @@ -95,13 +90,8 @@ module Hbc end end - def downloaded_size - temporary_path.size? || 0 - end - def _fetch - odebug "Calling curl with args #{cask_curl_args}" - curl(*cask_curl_args) + curl_download url, *cask_curl_args, to: temporary_path, user_agent: uri_object.user_agent end def fetch @@ -131,33 +121,12 @@ module Hbc ignore_interrupts { temporary_path.rename(tarball_path) } end tarball_path - rescue CurlDownloadStrategyError - raise if mirrors.empty? - puts "Trying a mirror..." - @url = mirrors.shift - retry end private def cask_curl_args - default_curl_args.tap do |args| - args.concat(user_agent_args) - args.concat(cookies_args) - args.concat(referer_args) - end - end - - def default_curl_args - [url, "-C", downloaded_size, "-o", temporary_path] - end - - def user_agent_args - if uri_object.user_agent - ["-A", uri_object.user_agent] - else - [] - end + cookies_args + referer_args end def cookies_args @@ -185,14 +154,13 @@ module Hbc end def ext - Pathname.new(@url).extname + Pathname.new(@url).extname[/[^?]+/] end end class CurlPostDownloadStrategy < CurlDownloadStrategy def cask_curl_args - super - default_curl_args.concat(post_args) + super.concat(post_args) end def post_args @@ -225,8 +193,8 @@ module Hbc # super does not provide checks for already-existing downloads def fetch - if tarball_path.exist? - puts "Already downloaded: #{tarball_path}" + if cached_location.directory? + puts "Already downloaded: #{cached_location}" else @url = @url.sub(/^svn\+/, "") if @url =~ %r{^svn\+http://} ohai "Checking out #{@url}" @@ -252,9 +220,8 @@ module Hbc else fetch_repo @clone, @url end - compress end - tarball_path + cached_location end # This primary reason for redefining this method is the trust_cert @@ -288,10 +255,6 @@ module Hbc print_stderr: false) end - def tarball_path - @tarball_path ||= cached_location.dirname.join(cached_location.basename.to_s + "-#{@cask.version}.tar") - end - def shell_quote(str) # Oh god escaping shell args. # See http://notetoself.vrensk.com/2008/08/escaping-single-quotes-in-ruby-harder-than-expected/ @@ -304,35 +267,5 @@ module Hbc yield name, url end end - - private - - # TODO/UPDATE: the tar approach explained below is fragile - # against challenges such as case-sensitive filesystems, - # and must be re-implemented. - # - # Seems nutty: we "download" the contents into a tape archive. - # Why? - # * A single file is tractable to the rest of the Cask toolchain, - # * An alternative would be to create a Directory container type. - # However, some type of file-serialization trick would still be - # needed in order to enable calculating a single checksum over - # a directory. So, in that alternative implementation, the - # special cases would propagate outside this class, including - # the use of tar or equivalent. - # * SubversionDownloadStrategy.cached_location is not versioned - # * tarball_path provides a needed return value for our overridden - # fetch method. - # * We can also take this private opportunity to strip files from - # the download which are protocol-specific. - - def compress - Dir.chdir(cached_location) do - @command.run!("/usr/bin/tar", - args: ['-s/^\.//', "--exclude", ".svn", "-cf", Pathname.new(tarball_path), "--", "."], - print_stderr: false) - end - clear_cache - end end end diff --git a/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb b/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb index d302d0946..fc7e83a20 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb @@ -12,7 +12,11 @@ module Hbc end def calculate_checkpoint - result = SystemCommand.run("/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", URL::FAKE_USER_AGENT, "--fail", @uri], print_stderr: false) + curl_executable, *args = curl_args( + "--compressed", "--location", "--fail", @uri, + user_agent: :fake + ) + result = SystemCommand.run(curl_executable, args: args, print_stderr: false) checkpoint = if result.success? processed_appcast_text = result.stdout.gsub(%r{<pubDate>[^<]*</pubDate>}m, "") diff --git a/Library/Homebrew/cask/lib/hbc/pkg.rb b/Library/Homebrew/cask/lib/hbc/pkg.rb index c9aa3180f..8938268a2 100644 --- a/Library/Homebrew/cask/lib/hbc/pkg.rb +++ b/Library/Homebrew/cask/lib/hbc/pkg.rb @@ -16,19 +16,17 @@ module Hbc def uninstall unless pkgutil_bom_files.empty? odebug "Deleting pkg files" - @command.run("/usr/bin/xargs", args: ["-0", "--", "/bin/rm", "--"], input: pkgutil_bom_files.join("\0"), sudo: true) + @command.run!("/usr/bin/xargs", args: ["-0", "--", "/bin/rm", "--"], input: pkgutil_bom_files.join("\0"), sudo: true) end unless pkgutil_bom_specials.empty? odebug "Deleting pkg symlinks and special files" - @command.run("/usr/bin/xargs", args: ["-0", "--", "/bin/rm", "--"], input: pkgutil_bom_specials.join("\0"), sudo: true) + @command.run!("/usr/bin/xargs", args: ["-0", "--", "/bin/rm", "--"], input: pkgutil_bom_specials.join("\0"), sudo: true) end unless pkgutil_bom_dirs.empty? odebug "Deleting pkg directories" deepest_path_first(pkgutil_bom_dirs).each do |dir| - next if MacOS.undeletable?(dir) - with_full_permissions(dir) do clean_broken_symlinks(dir) clean_ds_store(dir) @@ -67,6 +65,7 @@ module Hbc .stdout .split("\n") .map { |path| root.join(path) } + .reject(&MacOS.public_method(:undeletable?)) end def root diff --git a/Library/Homebrew/cask/lib/hbc/system_command.rb b/Library/Homebrew/cask/lib/hbc/system_command.rb index 901617b71..b735ae4f9 100644 --- a/Library/Homebrew/cask/lib/hbc/system_command.rb +++ b/Library/Homebrew/cask/lib/hbc/system_command.rb @@ -112,11 +112,7 @@ module Hbc processed_output[:stderr], processed_status.exitstatus) end - end -end -module Hbc - class SystemCommand class Result attr_accessor :command, :stdout, :stderr, :exit_status diff --git a/Library/Homebrew/cask/lib/hbc/url.rb b/Library/Homebrew/cask/lib/hbc/url.rb index 15da2ced2..8c652657b 100644 --- a/Library/Homebrew/cask/lib/hbc/url.rb +++ b/Library/Homebrew/cask/lib/hbc/url.rb @@ -1,8 +1,6 @@ module Hbc class URL - FAKE_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10) https://caskroom.github.io".freeze - - attr_reader :using, :revision, :trust_cert, :uri, :cookies, :referer, :data + attr_reader :using, :revision, :trust_cert, :uri, :cookies, :referer, :data, :user_agent extend Forwardable def_delegators :uri, :path, :scheme, :to_s @@ -17,7 +15,7 @@ module Hbc def initialize(uri, options = {}) @uri = Hbc::UnderscoreSupportingURI.parse(uri) - @user_agent = options[:user_agent] + @user_agent = options.fetch(:user_agent, :default) @cookies = options[:cookies] @referer = options[:referer] @using = options[:using] @@ -25,10 +23,5 @@ module Hbc @trust_cert = options[:trust_cert] @data = options[:data] end - - def user_agent - return FAKE_USER_AGENT if @user_agent == :fake - @user_agent - end end end diff --git a/Library/Homebrew/cask/lib/hbc/verify/gpg.rb b/Library/Homebrew/cask/lib/hbc/verify/gpg.rb index dbb537756..f4996a5b5 100644 --- a/Library/Homebrew/cask/lib/hbc/verify/gpg.rb +++ b/Library/Homebrew/cask/lib/hbc/verify/gpg.rb @@ -33,7 +33,7 @@ module Hbc meta_dir = cached || cask.metadata_subdir("gpg", :now, true) sig_path = meta_dir.join("signature.asc") - curl(cask.gpg.signature, "-o", sig_path.to_s) unless cached || force + curl_download cask.gpg.signature, to: sig_path unless cached || force sig_path end diff --git a/Library/Homebrew/cmd/deps.rb b/Library/Homebrew/cmd/deps.rb index bbf0c1b0b..de7aa4a51 100644 --- a/Library/Homebrew/cmd/deps.rb +++ b/Library/Homebrew/cmd/deps.rb @@ -1,4 +1,4 @@ -#: * `deps` [`--1`] [`-n`] [`--union`] [`--full-name`] [`--installed`] [`--include-build`] [`--include-optional`] [`--skip-recommended`] <formulae>: +#: * `deps` [`--1`] [`-n`] [`--union`] [`--full-name`] [`--installed`] [`--include-build`] [`--include-optional`] [`--skip-recommended`] [`--include-requirements`] <formulae>: #: Show dependencies for <formulae>. When given multiple formula arguments, #: show the intersection of dependencies for <formulae>. #: @@ -19,15 +19,22 @@ #: <formulae>. To include the `:build` type dependencies, pass `--include-build`. #: Similarly, pass `--include-optional` to include `:optional` dependencies. #: To skip `:recommended` type dependencies, pass `--skip-recommended`. +#: To include requirements in addition to dependencies, pass `--include-requirements`. #: -#: * `deps` `--tree` [<filters>] (<formulae>|`--installed`): +#: * `deps` `--tree` [`--1`] [<filters>] [`--annotate`] (<formulae>|`--installed`): #: Show dependencies as a tree. When given multiple formula arguments, output #: individual trees for every formula. #: +#: If `--1` is passed, only one level of children is displayed. +#: #: If `--installed` is passed, output a tree for every installed formula. #: #: The <filters> placeholder is any combination of options `--include-build`, -#: `--include-optional`, and `--skip-recommended` as documented above. +#: `--include-optional`, `--skip-recommended`, and `--include-requirements` as +#: documented above. +#: +#: If `--annotate` is passed, the build, optional, and recommended dependencies +#: are marked as such in the output. #: #: * `deps` [<filters>] (`--installed`|`--all`): #: Show dependencies for installed or all available formulae. Every line of @@ -37,6 +44,10 @@ #: The <filters> placeholder is any combination of options `--include-build`, #: `--include-optional`, and `--skip-recommended` as documented above. +# The undocumented `--for-each` option will switch into the mode used by `deps --all`, +# but only list dependencies for specified formula, one specified formula per line. +# This is used for debugging the `--installed`/`--all` display mode. + # encoding: UTF-8 require "formula" @@ -52,20 +63,26 @@ module Homebrew all?: ARGV.include?("--all"), topo_order?: ARGV.include?("-n"), union?: ARGV.include?("--union"), + for_each?: ARGV.include?("--for-each"), ) - if mode.installed? && mode.tree? - puts_deps_tree Formula.installed + if mode.tree? + if mode.installed? + puts_deps_tree Formula.installed, !ARGV.one? + else + raise FormulaUnspecifiedError if ARGV.named.empty? + puts_deps_tree ARGV.formulae, !ARGV.one? + end elsif mode.all? puts_deps Formula - elsif mode.tree? - raise FormulaUnspecifiedError if ARGV.named.empty? - puts_deps_tree ARGV.formulae elsif ARGV.named.empty? raise FormulaUnspecifiedError unless mode.installed? puts_deps Formula.installed + elsif mode.for_each? + puts_deps ARGV.formulae else all_deps = deps_for_formulae(ARGV.formulae, !ARGV.one?, &(mode.union? ? :| : :&)) + all_deps = condense_requirements(all_deps) all_deps = all_deps.select(&:installed?) if mode.installed? all_deps = all_deps.map(&method(:dep_display_name)).uniq all_deps.sort! unless mode.topo_order? @@ -73,24 +90,59 @@ module Homebrew end end - def dep_display_name(d) - ARGV.include?("--full-name") ? d.to_formula.full_name : d.name + def condense_requirements(deps) + if ARGV.include?("--include-requirements") + deps + else + deps.map do |dep| + if dep.is_a? Dependency + dep + elsif dep.default_formula? + dep.to_dependency + end + end.compact + end + end + + def dep_display_name(dep) + str = if dep.is_a? Requirement + if ARGV.include?("--include-requirements") + if dep.default_formula? + ":#{dep.display_s} (#{dep_display_name(dep.to_dependency)})" + else + ":#{dep.display_s}" + end + elsif dep.default_formula? + dep_display_name(dep.to_dependency) + else + # This shouldn't happen, but we'll put something here to help debugging + "::#{dep.name}" + end + else + ARGV.include?("--full-name") ? dep.to_formula.full_name : dep.name + end + if ARGV.include?("--annotate") + str = "#{str} [build]" if dep.build? + str = "#{str} [optional" if dep.optional? + str = "#{str} [recommended]" if dep.recommended? + end + str end def deps_for_formula(f, recursive = false) includes = [] ignores = [] - if ARGV.include? "--include-build" + if ARGV.include?("--include-build") includes << "build?" else ignores << "build?" end - if ARGV.include? "--include-optional" + if ARGV.include?("--include-optional") includes << "optional?" else ignores << "optional?" end - ignores << "recommended?" if ARGV.include? "--skip-recommended" + ignores << "recommended?" if ARGV.include?("--skip-recommended") if recursive deps = f.recursive_dependencies do |dependent, dep| @@ -120,7 +172,7 @@ module Homebrew end end - deps + reqs.select(&:default_formula?).map(&:to_dependency) + deps + reqs.to_a end def deps_for_formulae(formulae, recursive = false, &block) @@ -129,41 +181,55 @@ module Homebrew def puts_deps(formulae) formulae.each do |f| - deps = deps_for_formula(f).sort_by(&:name).map(&method(:dep_display_name)) + deps = deps_for_formula(f) + deps = condense_requirements(deps) + deps = deps.sort_by(&:name).map(&method(:dep_display_name)) puts "#{f.full_name}: #{deps.join(" ")}" end end - def puts_deps_tree(formulae) + def puts_deps_tree(formulae, recursive = false) formulae.each do |f| - puts "#{f.full_name} (required dependencies)" - recursive_deps_tree(f, "") + puts f.full_name + @dep_stack = [] + recursive_deps_tree(f, "", recursive) puts end end - def recursive_deps_tree(f, prefix) - reqs = f.requirements.select(&:default_formula?) - deps = f.deps.default - max = reqs.length - 1 - reqs.each_with_index do |req, i| - chr = if i == max && deps.empty? + def recursive_deps_tree(f, prefix, recursive) + reqs = f.requirements + deps = f.deps + dependables = reqs + deps + dependables = dependables.reject(&:optional?) unless ARGV.include?("--include-optional") + dependables = dependables.reject(&:build?) unless ARGV.include?("--include-build") + dependables = dependables.reject(&:recommended?) if ARGV.include?("--skip-recommended") + max = dependables.length - 1 + @dep_stack.push f.name + dependables.each_with_index do |dep, i| + next if !ARGV.include?("--include-requirements") && dep.is_a?(Requirement) && !dep.default_formula? + tree_lines = if i == max "└──" else "├──" end - puts prefix + "#{chr} :#{dep_display_name(req.to_dependency)}" - end - max = deps.length - 1 - deps.each_with_index do |dep, i| - chr = if i == max - "└──" + display_s = "#{tree_lines} #{dep_display_name(dep)}" + is_circular = @dep_stack.include?(dep.name) + display_s = "#{display_s} (CIRCULAR DEPENDENCY)" if is_circular + puts "#{prefix}#{display_s}" + next if !recursive || is_circular + prefix_addition = if i == max + " " else - "├──" + "│ " + end + if dep.is_a?(Requirement) && dep.default_formula? + recursive_deps_tree(Formulary.factory(dep.to_dependency.name), prefix + prefix_addition, true) + end + if dep.is_a? Dependency + recursive_deps_tree(Formulary.factory(dep.name), prefix + prefix_addition, true) end - prefix_ext = (i == max) ? " " : "│ " - puts prefix + "#{chr} #{dep_display_name(dep)}" - recursive_deps_tree(Formulary.factory(dep.name), prefix + prefix_ext) end + @dep_stack.pop end end diff --git a/Library/Homebrew/cmd/pin.rb b/Library/Homebrew/cmd/pin.rb index c5087f6d4..5a14f853c 100644 --- a/Library/Homebrew/cmd/pin.rb +++ b/Library/Homebrew/cmd/pin.rb @@ -1,6 +1,7 @@ #: * `pin` <formulae>: #: Pin the specified <formulae>, preventing them from being upgraded when -#: issuing the `brew upgrade` command. See also `unpin`. +#: issuing the `brew upgrade <formulae>` command (but can still be upgraded +#: as dependencies for other formulae). See also `unpin`. require "formula" diff --git a/Library/Homebrew/cmd/postinstall.rb b/Library/Homebrew/cmd/postinstall.rb index f5d091227..02fd8a5f6 100644 --- a/Library/Homebrew/cmd/postinstall.rb +++ b/Library/Homebrew/cmd/postinstall.rb @@ -29,8 +29,6 @@ module Homebrew args << "--devel" end - Sandbox.print_sandbox_message if Sandbox.formula?(formula) - Utils.safe_fork do if Sandbox.formula?(formula) sandbox = Sandbox.new diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb index b2d069744..0718a3af4 100644 --- a/Library/Homebrew/cmd/search.rb +++ b/Library/Homebrew/cmd/search.rb @@ -34,7 +34,7 @@ module Homebrew elsif ARGV.include? "--opensuse" exec_browser "https://software.opensuse.org/search?q=#{ARGV.next}" elsif ARGV.include? "--fedora" - exec_browser "https://admin.fedoraproject.org/pkgdb/packages/%2A#{ARGV.next}%2A/" + exec_browser "https://apps.fedoraproject.org/packages/s/#{ARGV.next}" elsif ARGV.include? "--ubuntu" exec_browser "http://packages.ubuntu.com/search?keywords=#{ARGV.next}&searchon=names&suite=all§ion=all" elsif ARGV.include? "--desc" @@ -43,27 +43,29 @@ module Homebrew Descriptions.search(regex, :desc).print elsif ARGV.first =~ HOMEBREW_TAP_FORMULA_REGEX query = ARGV.first - user, repo, name = query.split("/", 3) begin result = Formulary.factory(query).name + results = Array(result) rescue FormulaUnavailableError - result = search_tap(user, repo, name) + _, _, name = query.split("/", 3) + results = search_taps(name) end - results = Array(result) puts Formatter.columns(results) unless results.empty? else query = ARGV.first regex = query_regexp(query) local_results = search_formulae(regex) puts Formatter.columns(local_results) unless local_results.empty? + tap_results = search_taps(query) puts Formatter.columns(tap_results) unless tap_results.empty? if $stdout.tty? count = local_results.length + tap_results.length + ohai "Searching blacklisted, migrated and deleted formulae..." if reason = Homebrew::MissingFormula.reason(query, silent: true) if count > 0 puts @@ -101,9 +103,15 @@ module Homebrew end def search_taps(query) + return [] if ENV["HOMEBREW_NO_GITHUB_API"] + + # Use stderr to avoid breaking parsed output + $stderr.puts Formatter.headline("Searching taps on GitHub...", color: :blue) + valid_dirnames = ["Formula", "HomebrewFormula", "Casks", "."].freeze - matches = GitHub.search_code("user:Homebrew", "user:caskroom", "filename:#{query}", "extension:rb") - [*matches].map do |match| + matches = GitHub.search_code(user: ["Homebrew", "caskroom"], filename: query, extension: "rb") + + matches.map do |match| dirname, filename = File.split(match["path"]) next unless valid_dirnames.include?(dirname) tap = Tap.fetch(match["repository"]["full_name"]) @@ -113,6 +121,9 @@ module Homebrew end def search_formulae(regex) + # Use stderr to avoid breaking parsed output + $stderr.puts Formatter.headline("Searching local taps...", color: :blue) + aliases = Formula.alias_full_names results = (Formula.full_names + aliases).grep(regex).sort diff --git a/Library/Homebrew/cmd/unpin.rb b/Library/Homebrew/cmd/unpin.rb index a669df1ec..e15a156ea 100644 --- a/Library/Homebrew/cmd/unpin.rb +++ b/Library/Homebrew/cmd/unpin.rb @@ -1,6 +1,6 @@ #: * `unpin` <formulae>: -#: Unpin <formulae>, allowing them to be upgraded by `brew upgrade`. See also -#: `pin`. +#: Unpin <formulae>, allowing them to be upgraded by `brew upgrade <formulae>`. +#: See also `pin`. require "formula" diff --git a/Library/Homebrew/compat/hbc/cask_loader.rb b/Library/Homebrew/compat/hbc/cask_loader.rb index e6cb65b4f..e57aea71d 100644 --- a/Library/Homebrew/compat/hbc/cask_loader.rb +++ b/Library/Homebrew/compat/hbc/cask_loader.rb @@ -1,13 +1,13 @@ module CaskLoaderCompatibilityLayer private - def cask(header_token, &block) + def cask(header_token, **options, &block) if header_token.is_a?(Hash) && header_token.key?(:v1) odeprecated %q("cask :v1 => 'token'"), %q("cask 'token'") header_token = header_token[:v1] end - super(header_token, &block) + super(header_token, **options, &block) end end diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index 7b5befaa0..170fb6d5f 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -242,12 +242,10 @@ class FormulaAuditor def self.http_content_headers_and_checksum(url, hash_needed: false, user_agent: :default) max_time = hash_needed ? "600" : "25" - args = curl_args( - extra_args: ["--connect-timeout", "15", "--include", "--max-time", max_time, url], - show_output: true, - user_agent: user_agent, + output, = curl_output( + "--connect-timeout", "15", "--include", "--max-time", max_time, "--location", url, + user_agent: user_agent ) - output = Open3.popen3(*args) { |_, stdout, _, _| stdout.read } status_code = :unknown while status_code == :unknown || status_code.to_s.start_with?("3") @@ -330,6 +328,7 @@ class FormulaAuditor valid_alias_names = [alias_name_major, alias_name_major_minor] if formula.tap && !formula.tap.core_tap? + versioned_aliases.map! { |a| "#{formula.tap}/#{a}" } valid_alias_names.map! { |a| "#{formula.tap}/#{a}" } end @@ -630,7 +629,6 @@ class FormulaAuditor end next if spec.patches.empty? - spec.patches.each { |p| patch_problems(p) if p.external? } next unless @new_formula problem "New formulae should not require patches to build. Patches should be submitted and accepted upstream first." end @@ -786,36 +784,6 @@ class FormulaAuditor end end - def patch_problems(patch) - case patch.url - when %r{https?://github\.com/.+/.+/(?:commit|pull)/[a-fA-F0-9]*.(?:patch|diff)} - unless patch.url =~ /\?full_index=\w+$/ - problem <<-EOS.undent - GitHub patches should use the full_index parameter: - #{patch.url}?full_index=1 - EOS - end - when /raw\.github\.com/, %r{gist\.github\.com/raw}, %r{gist\.github\.com/.+/raw}, - %r{gist\.githubusercontent\.com/.+/raw} - unless patch.url =~ /[a-fA-F0-9]{40}/ - problem "GitHub/Gist patches should specify a revision:\n#{patch.url}" - end - when %r{https?://patch-diff\.githubusercontent\.com/raw/(.+)/(.+)/pull/(.+)\.(?:diff|patch)} - problem <<-EOS.undent - use GitHub pull request URLs: - https://github.com/#{Regexp.last_match(1)}/#{Regexp.last_match(2)}/pull/#{Regexp.last_match(3)}.patch?full_index=1 - Rather than patch-diff: - #{patch.url} - EOS - when %r{macports/trunk} - problem "MacPorts patches should specify a revision instead of trunk:\n#{patch.url}" - when %r{^http://trac\.macports\.org} - problem "Patches from MacPorts Trac should be https://, not http:\n#{patch.url}" - when %r{^http://bugs\.debian\.org} - problem "Patches from Debian should be https://, not http:\n#{patch.url}" - end - end - def audit_text bin_names = Set.new bin_names << formula.name diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb index 1c56749a3..e9e98d450 100644 --- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb +++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb @@ -176,7 +176,10 @@ module Homebrew rsrc.version = forced_version if forced_version odie "No version specified!" unless rsrc.version rsrc_path = rsrc.fetch - if Utils.popen_read("/usr/bin/tar", "-tf", rsrc_path) =~ %r{/.*\.} + gnu_tar_gtar_path = HOMEBREW_PREFIX/"opt/gnu-tar/bin/gtar" + gnu_tar_gtar = gnu_tar_gtar_path if gnu_tar_gtar_path.executable? + tar = which("gtar") || gnu_tar_gtar || which("tar") + if Utils.popen_read(tar, "-tf", rsrc_path) =~ %r{/.*\.} new_hash = rsrc_path.sha256 elsif new_url.include? ".tar" odie "#{formula}: no url/#{hash_type} specified!" diff --git a/Library/Homebrew/dev-cmd/mirror.rb b/Library/Homebrew/dev-cmd/mirror.rb index e2492203d..6445bc34c 100644 --- a/Library/Homebrew/dev-cmd/mirror.rb +++ b/Library/Homebrew/dev-cmd/mirror.rb @@ -25,9 +25,9 @@ module Homebrew "public_download_numbers": true, "public_stats": true} EOS - curl "--silent", "--fail", "-u#{bintray_user}:#{bintray_key}", - "-H", "Content-Type: application/json", - "-d", package_blob, bintray_repo_url + curl "--silent", "--fail", "--user", "#{bintray_user}:#{bintray_key}", + "--header", "Content-Type: application/json", + "--data", package_blob, bintray_repo_url puts end @@ -40,8 +40,8 @@ module Homebrew content_url = "https://api.bintray.com/content/homebrew/mirror" content_url += "/#{bintray_package}/#{f.pkg_version}/#{filename}" content_url += "?publish=1" - curl "--silent", "--fail", "-u#{bintray_user}:#{bintray_key}", - "-T", download, content_url + curl "--silent", "--fail", "--user", "#{bintray_user}:#{bintray_key}", + "--upload-file", download, content_url puts ohai "Mirrored #{filename}!" end diff --git a/Library/Homebrew/dev-cmd/pull.rb b/Library/Homebrew/dev-cmd/pull.rb index 9681bb2bc..dd2bc6270 100644 --- a/Library/Homebrew/dev-cmd/pull.rb +++ b/Library/Homebrew/dev-cmd/pull.rb @@ -228,7 +228,7 @@ module Homebrew "https://github.com/BrewTestBot/homebrew-#{tap.repo}/compare/homebrew:master...pr-#{issue}" end - curl "--silent", "--fail", "-o", "/dev/null", "-I", bottle_commit_url + curl "--silent", "--fail", "--output", "/dev/null", "--head", bottle_commit_url safe_system "git", "checkout", "--quiet", "-B", bottle_branch, orig_revision pull_patch bottle_commit_url, "bottle commit" @@ -303,7 +303,7 @@ module Homebrew extra_msg = @description ? "(#{@description})" : nil ohai "Fetching patch #{extra_msg}" puts "Patch: #{patch_url}" - curl patch_url, "-s", "-o", patchpath + curl_download patch_url, to: patchpath end def apply_patch @@ -433,10 +433,10 @@ module Homebrew end version = info.pkg_version ohai "Publishing on Bintray: #{package} #{version}" - curl "-w", '\n', "--silent", "--fail", - "-u#{creds[:user]}:#{creds[:key]}", "-X", "POST", - "-H", "Content-Type: application/json", - "-d", '{"publish_wait_for_secs": 0}', + curl "--write-out", '\n', "--silent", "--fail", + "--user", "#{creds[:user]}:#{creds[:key]}", "--request", "POST", + "--header", "Content-Type: application/json", + "--data", '{"publish_wait_for_secs": 0}', "https://api.bintray.com/content/homebrew/#{repo}/#{package}/#{version}/publish" true rescue => e @@ -587,7 +587,7 @@ module Homebrew # We're in the cache; make sure to force re-download loop do begin - curl url, "-o", filename + curl_download url, to: filename break rescue if retry_count >= max_curl_retries @@ -606,7 +606,7 @@ module Homebrew end def check_bintray_mirror(name, url) - headers = curl_output("--connect-timeout", "15", "--head", url)[0] + headers, = curl_output("--connect-timeout", "15", "--location", "--head", url) status_code = headers.scan(%r{^HTTP\/.* (\d+)}).last.first return if status_code.start_with?("2") opoo "The Bintray mirror #{url} is not reachable (HTTP status code #{status_code})." diff --git a/Library/Homebrew/dev-cmd/test.rb b/Library/Homebrew/dev-cmd/test.rb index c678171ac..ab2b0edb0 100644 --- a/Library/Homebrew/dev-cmd/test.rb +++ b/Library/Homebrew/dev-cmd/test.rb @@ -65,8 +65,6 @@ module Homebrew args << "--devel" end - Sandbox.print_sandbox_message if Sandbox.test? - Utils.safe_fork do if Sandbox.test? sandbox = Sandbox.new diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb index 717334714..2a8b6e585 100644 --- a/Library/Homebrew/download_strategy.rb +++ b/Library/Homebrew/download_strategy.rb @@ -375,75 +375,59 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy ohai "Downloading from #{url}" end - urls = actual_urls(url) - unless urls.empty? - ohai "Downloading from #{urls.last}" - if !ENV["HOMEBREW_NO_INSECURE_REDIRECT"].nil? && url.start_with?("https://") && - urls.any? { |u| !u.start_with? "https://" } - puts "HTTPS to HTTP redirect detected & HOMEBREW_NO_INSECURE_REDIRECT is set." - raise CurlDownloadStrategyError, url - end - url = urls.last - end - - curl url, "-C", downloaded_size, "-o", temporary_path + curl_download resolved_url(url), to: temporary_path end # Curl options to be always passed to curl, - # with raw head calls (`curl -I`) or with actual `fetch`. + # with raw head calls (`curl --head`) or with actual `fetch`. def _curl_opts - copts = [] - copts << "--user" << meta.fetch(:user) if meta.key?(:user) - copts + return ["--user" << meta.fetch(:user)] if meta.key?(:user) + [] end - def actual_urls(url) - urls = [] - curl_args = _curl_opts << "-I" << "-L" << url - Utils.popen_read("curl", *curl_args).scan(/^Location: (.+)$/).map do |m| - urls << URI.join(urls.last || url, m.first.chomp).to_s + def resolved_url(url) + redirect_url, _, status = curl_output( + *_curl_opts, "--silent", "--head", + "--write-out", "%{redirect_url}", + "--output", "/dev/null", + url.to_s + ) + + return url unless status.success? + return url if redirect_url.empty? + + ohai "Downloading from #{redirect_url}" + if ENV["HOMEBREW_NO_INSECURE_REDIRECT"] && + url.start_with?("https://") && !redirect_url.start_with?("https://") + puts "HTTPS to HTTP redirect detected & HOMEBREW_NO_INSECURE_REDIRECT is set." + raise CurlDownloadStrategyError, url end - urls - end - def downloaded_size - temporary_path.size? || 0 + redirect_url end - def curl(*args) + def curl(*args, **options) args.concat _curl_opts args << "--connect-timeout" << "5" unless mirrors.empty? - super + super(*args, **options) end end # Detect and download from Apache Mirror class CurlApacheMirrorDownloadStrategy < CurlDownloadStrategy def apache_mirrors - rd, wr = IO.pipe - buf = "" - - pid = fork do - ENV.delete "HOMEBREW_CURL_VERBOSE" - rd.close - $stdout.reopen(wr) - $stderr.reopen(wr) - curl "#{@url}&asjson=1" - end - wr.close + mirrors, = Open3.capture3( + *curl_args(*_curl_opts, "--silent", "--location", "#{@url}&asjson=1"), + ) - rd.readline if ARGV.verbose? # Remove Homebrew output - buf << rd.read until rd.eof? - rd.close - Process.wait(pid) - buf + JSON.parse(mirrors) end def _fetch return super if @tried_apache_mirror @tried_apache_mirror = true - mirrors = JSON.parse(apache_mirrors) + mirrors = apache_mirrors path_info = mirrors.fetch("path_info") @url = mirrors.fetch("preferred") + path_info @mirrors |= %W[https://archive.apache.org/dist/#{path_info}] @@ -460,7 +444,7 @@ end class CurlPostDownloadStrategy < CurlDownloadStrategy def _fetch base_url, data = @url.split("?") - curl base_url, "-d", data, "-C", downloaded_size, "-o", temporary_path + curl_download base_url, "--data", data, to: temporary_path end end @@ -530,7 +514,7 @@ class S3DownloadStrategy < CurlDownloadStrategy s3url = obj.public_url end - curl s3url, "-C", downloaded_size, "-o", temporary_path + curl_download s3url, to: temporary_path end end @@ -566,7 +550,7 @@ class GitHubPrivateRepositoryDownloadStrategy < CurlDownloadStrategy end def _fetch - curl download_url, "-C", downloaded_size, "-o", temporary_path + curl_download download_url, to: temporary_path end private @@ -615,7 +599,7 @@ class GitHubPrivateRepositoryReleaseDownloadStrategy < GitHubPrivateRepositoryDo def _fetch # HTTP request header `Accept: application/octet-stream` is required. # Without this, the GitHub API will respond with metadata, not binary. - curl download_url, "-C", downloaded_size, "-o", temporary_path, "-H", "Accept: application/octet-stream" + curl_download download_url, "--header", "Accept: application/octet-stream", to: temporary_path end private @@ -915,18 +899,27 @@ class GitHubGitDownloadStrategy < GitDownloadStrategy def github_last_commit return if ENV["HOMEBREW_NO_GITHUB_API"] - output, _, status = curl_output "-H", "Accept: application/vnd.github.v3.sha", \ - "-I", "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{@ref}" + output, _, status = curl_output( + "--silent", "--head", "--location", + "-H", "Accept: application/vnd.github.v3.sha", + "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{@ref}" + ) + + return unless status.success? - commit = output[/^ETag: \"(\h+)\"/, 1] if status.success? + commit = output[/^ETag: \"(\h+)\"/, 1] version.update_commit(commit) if commit commit end def multiple_short_commits_exist?(commit) return if ENV["HOMEBREW_NO_GITHUB_API"] - output, _, status = curl_output "-H", "Accept: application/vnd.github.v3.sha", \ - "-I", "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{commit}" + + output, _, status = curl_output( + "--silent", "--head", "--location", + "-H", "Accept: application/vnd.github.v3.sha", + "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{commit}" + ) !(status.success? && output && output[/^Status: (200)/, 1] == "200") end @@ -1159,15 +1152,13 @@ class DownloadStrategyDetector SubversionDownloadStrategy when %r{^cvs://} CVSDownloadStrategy - when %r{^https?://(.+?\.)?googlecode\.com/hg} - MercurialDownloadStrategy - when %r{^hg://} + when %r{^hg://}, %r{^https?://(.+?\.)?googlecode\.com/hg} MercurialDownloadStrategy when %r{^bzr://} BazaarDownloadStrategy when %r{^fossil://} FossilDownloadStrategy - when %r{^http://svn\.apache\.org/repos/}, %r{^svn\+http://} + when %r{^svn\+http://}, %r{^http://svn\.apache\.org/repos/} SubversionDownloadStrategy when %r{^https?://(.+?\.)?sourceforge\.net/hgweb/} MercurialDownloadStrategy diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index 23a123c44..8b4cddc59 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -181,8 +181,8 @@ class TapFormulaAmbiguityError < RuntimeError @name = name @paths = paths @formulae = paths.map do |path| - path.to_s =~ HOMEBREW_TAP_PATH_REGEX - "#{Tap.fetch(Regexp.last_match(1), Regexp.last_match(2))}/#{path.basename(".rb")}" + match = path.to_s.match(HOMEBREW_TAP_PATH_REGEX) + "#{Tap.fetch(match[:user], match[:repo])}/#{path.basename(".rb")}" end super <<-EOS.undent diff --git a/Library/Homebrew/extend/ENV/super.rb b/Library/Homebrew/extend/ENV/super.rb index 692fd3623..b518c22a1 100644 --- a/Library/Homebrew/extend/ENV/super.rb +++ b/Library/Homebrew/extend/ENV/super.rb @@ -138,7 +138,6 @@ module Superenv def determine_pkg_config_libdir PATH.new( - "/usr/lib/pkgconfig", homebrew_extra_pkg_config_paths, ).existing end diff --git a/Library/Homebrew/extend/os/mac/extend/ENV/super.rb b/Library/Homebrew/extend/os/mac/extend/ENV/super.rb index f97a2dbbb..9c20cc7c6 100644 --- a/Library/Homebrew/extend/os/mac/extend/ENV/super.rb +++ b/Library/Homebrew/extend/os/mac/extend/ENV/super.rb @@ -28,7 +28,7 @@ module Superenv # @private def homebrew_extra_pkg_config_paths paths = \ - ["#{HOMEBREW_LIBRARY}/Homebrew/os/mac/pkgconfig/#{MacOS.version}"] + ["/usr/lib/pkgconfig", "#{HOMEBREW_LIBRARY}/Homebrew/os/mac/pkgconfig/#{MacOS.version}"] paths << "#{MacOS::X11.lib}/pkgconfig" << "#{MacOS::X11.share}/pkgconfig" if x11? paths end diff --git a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb index 10379c981..32e5774f6 100644 --- a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb +++ b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb @@ -6,7 +6,7 @@ module FormulaCellarChecks formula.name.start_with?(formula_name) end - return if formula.name =~ /^php\d+$/ + return if formula.name =~ /^php(@?\d+\.?\d*?)?$/ return if MacOS.version < :mavericks && formula.name.start_with?("postgresql") return if MacOS.version < :yosemite && formula.name.start_with?("memcached") @@ -67,11 +67,19 @@ module FormulaCellarChecks checker = LinkageChecker.new(keg, formula) return unless checker.broken_dylibs? - problem_if_output <<-EOS.undent - The installation was broken. - Broken dylib links found: - #{checker.broken_dylibs.to_a * "\n "} + output = <<-EOS.undent + #{formula} has broken dynamic library links: + #{checker.broken_dylibs.to_a * "\n "} EOS + tab = Tab.for_keg(keg) + if tab.poured_from_bottle + output += <<-EOS.undent + Rebuild this from source with: + brew reinstall --build-from-source #{formula} + If that's successful, file an issue#{formula.tap ? " here:\n #{formula.tap.issues_url}" : "."} + EOS + end + problem_if_output output end def audit_installed diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 5673a433f..8cea85a99 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -177,8 +177,8 @@ class Formula @tap = if path == Formulary.core_path(name) CoreTap.instance - elsif path.to_s =~ HOMEBREW_TAP_PATH_REGEX - Tap.fetch(Regexp.last_match(1), Regexp.last_match(2)) + elsif match = path.to_s.match(HOMEBREW_TAP_PATH_REGEX) + Tap.fetch(match[:user], match[:repo]) end @full_name = full_name_with_optional_tap(name) @@ -2213,7 +2213,7 @@ class Formula # depends_on :arch => :x86_64 # If this formula only builds on Intel x86 64-bit. # depends_on :arch => :ppc # Only builds on PowerPC? # depends_on :ld64 # Sometimes ld fails on `MacOS.version < :leopard`. Then use this. - # depends_on :x11 # X11/XQuartz components. Non-optional X11 deps should go in Homebrew/Homebrew-x11 + # depends_on :x11 # X11/XQuartz components. # depends_on :osxfuse # Permits the use of the upstream signed binary or our source package. # depends_on :tuntap # Does the same thing as above. This is vital for Yosemite and above. # depends_on :mysql => :recommended</pre> diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 4e4a59972..6c5b8bdab 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -46,7 +46,7 @@ class FormulaInstaller @ignore_deps = false @only_deps = false @build_from_source = ARGV.build_from_source? || ARGV.build_all_from_source? - @build_bottle = ARGV.build_bottle? + @build_bottle = false @force_bottle = ARGV.force_bottle? @interactive = false @git = false @@ -543,7 +543,6 @@ class FormulaInstaller fi.options |= inherited_options fi.options &= df.options fi.build_from_source = ARGV.build_formula_from_source?(df) - fi.build_bottle = false fi.force_bottle = false fi.verbose = verbose? fi.quieter = quieter? @@ -680,8 +679,6 @@ class FormulaInstaller #{formula.specified_path} ].concat(build_argv) - Sandbox.print_sandbox_message if Sandbox.formula?(formula) - Utils.safe_fork do # Invalidate the current sudo timestamp in case a build script calls sudo. # Travis CI's Linux sudoless workers have a weird sudo that fails here. diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 195d15cec..dd67b4f24 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -165,7 +165,7 @@ module Formulary def load_file HOMEBREW_CACHE_FORMULA.mkpath FileUtils.rm_f(path) - curl url, "-o", path + curl_download url, to: path super rescue MethodDeprecatedError => e if url =~ %r{github.com/([\w-]+)/homebrew-([\w-]+)/} diff --git a/Library/Homebrew/gpg.rb b/Library/Homebrew/gpg.rb index cb9e367df..f56473df3 100644 --- a/Library/Homebrew/gpg.rb +++ b/Library/Homebrew/gpg.rb @@ -7,8 +7,8 @@ class Gpg next unless gpg_short_version gpg_version = Version.create(gpg_short_version.to_s) @version = gpg_version - gpg_version == Version.create("2.0") || - gpg_version == Version.create("2.1") + gpg_version == Version.create("2.1") || + gpg_version == Version.create("2.0") end end @@ -20,7 +20,7 @@ class Gpg find_gpg("gpg2") end - GPG_EXECUTABLE = gpg2 || gpg + GPG_EXECUTABLE = gpg || gpg2 def self.available? File.executable?(GPG_EXECUTABLE.to_s) @@ -38,6 +38,7 @@ class Gpg Key-Length: 2048 Subkey-Type: RSA Subkey-Length: 2048 + Passphrase: '' Name-Real: Testing Name-Email: testing@foo.bar Expire-Date: 1d diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index 8fcbecfbd..92eab7ad3 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -253,6 +253,11 @@ class Keg FileUtils.rm_rf bad_tap_opt if bad_tap_opt.directory? end + aliases.each do |a| + alias_symlink = opt/a + alias_symlink.delete if alias_symlink.symlink? || alias_symlink.exist? + end + Pathname.glob("#{opt_record}@*").each do |a| a = a.basename next if aliases.include?(a) diff --git a/Library/Homebrew/os/mac/xcode.rb b/Library/Homebrew/os/mac/xcode.rb index e23a7cf3d..6f7deaa10 100644 --- a/Library/Homebrew/os/mac/xcode.rb +++ b/Library/Homebrew/os/mac/xcode.rb @@ -216,7 +216,7 @@ module OS # on the older supported platform for that Xcode release, i.e there's no # CLT package for 10.11 that contains the Clang version from Xcode 8. case MacOS.version - when "10.13" then "900.0.31" + when "10.13" then "900.0.34.1" when "10.12" then "802.0.42" when "10.11" then "800.0.42.1" when "10.10" then "700.1.81" diff --git a/Library/Homebrew/requirements/gpg2_requirement.rb b/Library/Homebrew/requirements/gpg2_requirement.rb index 97fabcca0..d570983eb 100644 --- a/Library/Homebrew/requirements/gpg2_requirement.rb +++ b/Library/Homebrew/requirements/gpg2_requirement.rb @@ -5,8 +5,8 @@ class GPG2Requirement < Requirement fatal true default_formula "gnupg" - # MacGPG2/GPGTools installs GnuPG 2.0.x as a vanilla `gpg` symlink - # pointing to `gpg2`, as do we. Ensure we're actually using a 2.0 `gpg`. - # Support both the 2.0.x "stable" and 2.1.x "modern" series. - satisfy(build_env: false) { Gpg.gpg2 || Gpg.gpg } + # GPGTools installs GnuPG 2.0.x as a vanilla `gpg` symlink + # pointing to `gpg2`. Homebrew install 2.1.x as a non-symlink `gpg`. + # We support both the 2.0.x "stable" and 2.1.x "modern" series here. + satisfy(build_env: false) { Gpg.gpg || Gpg.gpg2 } end diff --git a/Library/Homebrew/rubocops.rb b/Library/Homebrew/rubocops.rb index 4323e044c..b1144e075 100644 --- a/Library/Homebrew/rubocops.rb +++ b/Library/Homebrew/rubocops.rb @@ -6,7 +6,7 @@ require_relative "./rubocops/homepage_cop" require_relative "./rubocops/text_cop" require_relative "./rubocops/caveats_cop" require_relative "./rubocops/checksum_cop" -require_relative "./rubocops/legacy_patches_cop" +require_relative "./rubocops/patches_cop" require_relative "./rubocops/conflicts_cop" require_relative "./rubocops/options_cop" require_relative "./rubocops/urls_cop" diff --git a/Library/Homebrew/rubocops/extend/formula_cop.rb b/Library/Homebrew/rubocops/extend/formula_cop.rb index 4be0c0fe3..862664010 100644 --- a/Library/Homebrew/rubocops/extend/formula_cop.rb +++ b/Library/Homebrew/rubocops/extend/formula_cop.rb @@ -1,4 +1,5 @@ require "parser/current" +require_relative "../../extend/string" module RuboCop module Cop @@ -138,17 +139,14 @@ module RuboCop case type when :required - type_match = !node.method_args.nil? && - (node.method_args.first.str_type? || node.method_args.first.sym_type?) + type_match = required_dependency?(node) if type_match && !name_match - name_match = node_equals?(node.method_args.first, name) + name_match = required_dependency_name?(node, name) end when :build, :optional, :recommended, :run - type_match = !node.method_args.nil? && - node.method_args.first.hash_type? && - node.method_args.first.values.first.children.first == type + type_match = dependency_type_hash_match?(node, type) if type_match && !name_match - name_match = node_equals?(node.method_args.first.keys.first.children.first, name) + name_match = dependency_name_hash_match?(node, name) end else type_match = false @@ -161,6 +159,22 @@ module RuboCop type_match && name_match end + def_node_search :required_dependency?, <<-EOS.undent + (send nil :depends_on ({str sym} _)) + EOS + + def_node_search :required_dependency_name?, <<-EOS.undent + (send nil :depends_on ({str sym} %1)) + EOS + + def_node_search :dependency_type_hash_match?, <<-EOS.undent + (hash (pair ({str sym} _) ({str sym} %1))) + EOS + + def_node_search :dependency_name_hash_match?, <<-EOS.undent + (hash (pair ({str sym} %1) ({str sym} _))) + EOS + # To compare node with appropriate Ruby variable def node_equals?(node, var) node == Parser::CurrentRuby.parse(var.inspect) diff --git a/Library/Homebrew/rubocops/legacy_patches_cop.rb b/Library/Homebrew/rubocops/patches_cop.rb index e569f650e..fb14d8acc 100644 --- a/Library/Homebrew/rubocops/legacy_patches_cop.rb +++ b/Library/Homebrew/rubocops/patches_cop.rb @@ -4,9 +4,16 @@ require_relative "../extend/string" module RuboCop module Cop module FormulaAudit - # This cop checks for and audits legacy patches in Formulae - class LegacyPatches < FormulaCop + # This cop audits patches in Formulae + class Patches < FormulaCop def audit_formula(_node, _class_node, _parent_class_node, body) + external_patches = find_all_blocks(body, :patch) + external_patches.each do |patch_block| + url_node = find_every_method_call_by_name(patch_block, :url).first + url_string = parameters(url_node).first + patch_problems(url_string) + end + patches_node = find_method_def(body, :patches) return if patches_node.nil? legacy_patches = find_strings(patches_node) @@ -14,6 +21,8 @@ module RuboCop legacy_patches.each { |p| patch_problems(p) } end + private + def patch_problems(patch) patch_url = string_content(patch) gh_patch_patterns = Regexp.union([%r{/raw\.github\.com/}, @@ -30,7 +39,7 @@ module RuboCop if match_obj = regex_match_group(patch, gh_patch_diff_pattern) problem <<-EOS.undent use GitHub pull request URLs: - https://github.com/#{match_obj[1]}/#{match_obj[2]}/pull/#{match_ojb[3]}.patch + https://github.com/#{match_obj[1]}/#{match_obj[2]}/pull/#{match_obj[3]}.patch Rather than patch-diff: #{patch_url} EOS diff --git a/Library/Homebrew/sandbox.rb b/Library/Homebrew/sandbox.rb index 0de970773..8c662857e 100644 --- a/Library/Homebrew/sandbox.rb +++ b/Library/Homebrew/sandbox.rb @@ -18,12 +18,6 @@ class Sandbox !ARGV.no_sandbox? end - def self.print_sandbox_message - return if @printed_sandbox_message - ohai "Using the sandbox" - @printed_sandbox_message = true - end - def initialize @profile = SandboxProfile.new end diff --git a/Library/Homebrew/shims/super/cc b/Library/Homebrew/shims/super/cc index d894d3d69..afe72156f 100755 --- a/Library/Homebrew/shims/super/cc +++ b/Library/Homebrew/shims/super/cc @@ -43,6 +43,8 @@ class Cmd else :cc end + elsif @args.include?("-xc++-header") || @args.each_cons(2).include?(["-x", "c++-header"]) + :cxx elsif @args.include? "-E" :ccE else diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index c6e704350..49d818f0f 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -51,8 +51,18 @@ class SoftwareSpec @owner = owner @resource.owner = self resources.each_value do |r| - r.owner = self - r.version ||= (version.head? ? Version.create("HEAD") : version.dup) + r.owner = self + r.version ||= begin + if version.nil? + raise "#{full_name}: version missing for \"#{r.name}\" resource!" + end + + if version.head? + Version.create("HEAD") + else + version.dup + end + end end patches.each { |p| p.owner = self } end diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb index 84953726f..f232be428 100644 --- a/Library/Homebrew/tap.rb +++ b/Library/Homebrew/tap.rb @@ -42,9 +42,9 @@ class Tap end def self.from_path(path) - path.to_s =~ HOMEBREW_TAP_PATH_REGEX - raise "Invalid tap path '#{path}'" unless Regexp.last_match(1) - fetch(Regexp.last_match(1), Regexp.last_match(2)) + match = path.to_s.match(HOMEBREW_TAP_PATH_REGEX) + raise "Invalid tap path '#{path}'" unless match + fetch(match[:user], match[:repo]) rescue # No need to error as a nil tap is sufficient to show failure. nil diff --git a/Library/Homebrew/tap_constants.rb b/Library/Homebrew/tap_constants.rb index 773eff816..23bbc61ed 100644 --- a/Library/Homebrew/tap_constants.rb +++ b/Library/Homebrew/tap_constants.rb @@ -3,7 +3,7 @@ HOMEBREW_TAP_FORMULA_REGEX = %r{^([\w-]+)/([\w-]+)/([\w+-.@]+)$} # match taps' casks, e.g. someuser/sometap/somecask HOMEBREW_TAP_CASK_REGEX = %r{^([\w-]+)/([\w-]+)/([a-z0-9\-]+)$} # match taps' directory paths, e.g. HOMEBREW_LIBRARY/Taps/someuser/sometap -HOMEBREW_TAP_DIR_REGEX = %r{#{Regexp.escape(HOMEBREW_LIBRARY)}/Taps/([\w-]+)/([\w-]+)} +HOMEBREW_TAP_DIR_REGEX = %r{#{Regexp.escape(HOMEBREW_LIBRARY)}/Taps/(?<user>[\w-]+)/(?<repo>[\w-]+)} # match taps' formula paths, e.g. HOMEBREW_LIBRARY/Taps/someuser/sometap/someformula HOMEBREW_TAP_PATH_REGEX = Regexp.new(HOMEBREW_TAP_DIR_REGEX.source + %r{/(.*)}.source) # match the default and the versions brew-cask tap e.g. Caskroom/cask or Caskroom/versions diff --git a/Library/Homebrew/test/cask/download_strategy_spec.rb b/Library/Homebrew/test/cask/download_strategy_spec.rb index 222352c07..17da1e36e 100644 --- a/Library/Homebrew/test/cask/download_strategy_spec.rb +++ b/Library/Homebrew/test/cask/download_strategy_spec.rb @@ -26,9 +26,12 @@ describe "download strategies", :cask do downloader.fetch expect(downloader).to have_received(:curl).with( + "--location", + "--remote-time", + "--continue-at", "-", + "--output", kind_of(Pathname), cask.url.to_s, - "-C", 0, - "-o", kind_of(Pathname) + user_agent: :default ) end @@ -36,25 +39,25 @@ describe "download strategies", :cask do let(:url_options) { { user_agent: "Mozilla/25.0.1" } } it "adds the appropriate curl args" do - curl_args = [] - allow(downloader).to receive(:curl) { |*args| curl_args = args } + expect(downloader).to receive(:safe_system) { |*args| + expect(args.each_cons(2)).to include(["--user-agent", "Mozilla/25.0.1"]) + } downloader.fetch - - expect(curl_args.each_cons(2)).to include(["-A", "Mozilla/25.0.1"]) end end context "with a generalized fake user agent" do + alias_matcher :a_string_matching, :match + let(:url_options) { { user_agent: :fake } } it "adds the appropriate curl args" do - curl_args = [] - allow(downloader).to receive(:curl) { |*args| curl_args = args } + expect(downloader).to receive(:safe_system) { |*args| + expect(args.each_cons(2).to_a).to include(["--user-agent", a_string_matching(/Mozilla.*Mac OS X 10.*AppleWebKit/)]) + } downloader.fetch - - expect(curl_args.each_cons(2)).to include(["-A", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10) https://caskroom.github.io"]) end end @@ -138,7 +141,7 @@ describe "download strategies", :cask do describe Hbc::SubversionDownloadStrategy do let(:url_options) { { using: :svn } } let(:fake_system_command) { class_double(Hbc::SystemCommand) } - let(:downloader) { Hbc::SubversionDownloadStrategy.new(cask, fake_system_command) } + let(:downloader) { Hbc::SubversionDownloadStrategy.new(cask, command: fake_system_command) } before do allow(fake_system_command).to receive(:run!) end @@ -147,7 +150,7 @@ describe "download strategies", :cask do allow(downloader).to receive(:compress) allow(downloader).to receive(:fetch_repo) - expect(downloader.fetch).to equal(downloader.tarball_path) + expect(downloader.fetch).to equal(downloader.cached_location) end it "calls fetch_repo with default arguments for a simple Cask" do @@ -237,44 +240,5 @@ describe "download strategies", :cask do ) end end - - it "runs tar to serialize svn downloads" do - # sneaky stub to remake the directory, since homebrew code removes it - # before tar is called - allow(downloader).to receive(:fetch_repo) { - downloader.cached_location.mkdir - } - - downloader.fetch - - expect(fake_system_command).to have_received(:run!).with( - "/usr/bin/tar", - hash_including(args: [ - '-s/^\\.//', - "--exclude", - ".svn", - "-cf", - downloader.tarball_path, - "--", - ".", - ]), - ) - end end - - # does not work yet, because (for unknown reasons), the tar command - # returns an error code when running under the test suite - # it 'creates a tarball matching the expected checksum' do - # cask = Hbc::CaskLoader.load('svn-download-check-cask') - # downloader = Hbc::SubversionDownloadStrategy.new(cask) - # # special mocking required for tar to have something to work with - # def downloader.fetch_repo(target, url, revision = nil, ignore_externals=false) - # target.mkpath - # FileUtils.touch(target.join('empty_file.txt')) - # File.utime(1000,1000,target.join('empty_file.txt')) - # end - # expect(downloader.fetch).to equal(downloader.tarball_path) - # d = Hbc::Download.new(cask) - # d.send(:_check_sums, downloader.tarball_path, cask.sums) - # end end diff --git a/Library/Homebrew/test/cask/dsl/appcast_spec.rb b/Library/Homebrew/test/cask/dsl/appcast_spec.rb index b8903b1be..ccc6a4633 100644 --- a/Library/Homebrew/test/cask/dsl/appcast_spec.rb +++ b/Library/Homebrew/test/cask/dsl/appcast_spec.rb @@ -33,13 +33,18 @@ describe Hbc::DSL::Appcast do describe "#calculate_checkpoint" do before do - expect(Hbc::SystemCommand).to receive(:run).with(*cmd_args).and_return(cmd_result) + expect(Hbc::SystemCommand).to receive(:run) do |executable, **options| + expect(executable).to eq "/usr/bin/curl" + expect(options[:args]).to include(*cmd_args) + expect(options[:print_stderr]).to be false + cmd_result + end allow(cmd_result).to receive(:success?).and_return(cmd_success) allow(cmd_result).to receive(:stdout).and_return(cmd_stdout) end context "when server returns a successful HTTP status" do - let(:cmd_args) { ["/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", Hbc::URL::FAKE_USER_AGENT, "--fail", uri], print_stderr: false] } + let(:cmd_args) { [HOMEBREW_USER_AGENT_FAKE_SAFARI, "--compressed", "--location", "--fail", uri] } let(:cmd_result) { double("Hbc::SystemCommand::Result") } let(:cmd_success) { true } let(:cmd_stdout) { "hello world" } @@ -56,7 +61,7 @@ describe Hbc::DSL::Appcast do end context "when server returns a non-successful HTTP status" do - let(:cmd_args) { ["/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", Hbc::URL::FAKE_USER_AGENT, "--fail", uri], print_stderr: false] } + let(:cmd_args) { [HOMEBREW_USER_AGENT_FAKE_SAFARI, "--compressed", "--location", "--fail", uri] } let(:cmd_result) { double("Hbc::SystemCommand::Result") } let(:cmd_success) { false } let(:cmd_stdout) { "some error message from the server" } diff --git a/Library/Homebrew/test/cask/pkg_spec.rb b/Library/Homebrew/test/cask/pkg_spec.rb index 56061c9fd..07443e76e 100644 --- a/Library/Homebrew/test/cask/pkg_spec.rb +++ b/Library/Homebrew/test/cask/pkg_spec.rb @@ -5,16 +5,16 @@ describe Hbc::Pkg, :cask do let(:pkg) { described_class.new("my.fake.pkg", fake_system_command) } it "removes files and dirs referenced by the pkg" do - some_files = Array.new(3) { Pathname.new(Tempfile.new("testfile").path) } + some_files = Array.new(3) { Pathname.new(Tempfile.new("plain_file").path) } allow(pkg).to receive(:pkgutil_bom_files).and_return(some_files) - some_specials = Array.new(3) { Pathname.new(Tempfile.new("testfile").path) } + some_specials = Array.new(3) { Pathname.new(Tempfile.new("special_file").path) } allow(pkg).to receive(:pkgutil_bom_specials).and_return(some_specials) - some_dirs = Array.new(3) { Pathname.new(Dir.mktmpdir) } + some_dirs = Array.new(3) { mktmpdir } allow(pkg).to receive(:pkgutil_bom_dirs).and_return(some_dirs) - root_dir = Pathname.new(Dir.mktmpdir) + root_dir = Pathname.new(mktmpdir) allow(pkg).to receive(:root).and_return(root_dir) allow(pkg).to receive(:forget) @@ -55,8 +55,8 @@ describe Hbc::Pkg, :cask do end it "removes broken symlinks" do - fake_dir = Pathname.new(Dir.mktmpdir) - fake_root = Pathname.new(Dir.mktmpdir) + fake_root = mktmpdir + fake_dir = mktmpdir fake_file = fake_dir.join("ima_file").tap { |path| FileUtils.touch(path) } intact_symlink = fake_dir.join("intact_symlink").tap { |path| path.make_symlink(fake_file) } @@ -77,13 +77,13 @@ describe Hbc::Pkg, :cask do end it "snags permissions on ornery dirs, but returns them afterwards" do - fake_root = Pathname.new(Dir.mktmpdir) - fake_dir = Pathname.new(Dir.mktmpdir) - fake_file = fake_dir.join("ima_installed_file").tap { |path| FileUtils.touch(path) } + fake_root = mktmpdir + fake_dir = mktmpdir + fake_file = fake_dir.join("ima_unrelated_file").tap { |path| FileUtils.touch(path) } fake_dir.chmod(0000) allow(pkg).to receive(:pkgutil_bom_specials).and_return([]) - allow(pkg).to receive(:pkgutil_bom_files).and_return([fake_file]) + allow(pkg).to receive(:pkgutil_bom_files).and_return([]) allow(pkg).to receive(:pkgutil_bom_dirs).and_return([fake_dir]) allow(pkg).to receive(:root).and_return(fake_root) allow(pkg).to receive(:forget) @@ -91,8 +91,12 @@ describe Hbc::Pkg, :cask do pkg.uninstall expect(fake_dir).to be_a_directory - expect(fake_file).not_to be_a_file - expect((fake_dir.stat.mode % 01000).to_s(8)).to eq("0") + expect((fake_dir.stat.mode % 01000)).to eq(0) + + fake_dir.chmod(0777) + expect(fake_file).to be_a_file + + FileUtils.rm_r fake_dir end end diff --git a/Library/Homebrew/test/cmd/search_spec.rb b/Library/Homebrew/test/cmd/search_spec.rb index 06b7073d8..77c2c6352 100644 --- a/Library/Homebrew/test/cmd/search_spec.rb +++ b/Library/Homebrew/test/cmd/search_spec.rb @@ -1,6 +1,7 @@ describe "brew search", :integration_test do before(:each) do setup_test_formula "testball" + setup_remote_tap "caskroom/cask" end it "lists all available Formulae when no argument is given" do @@ -13,7 +14,7 @@ describe "brew search", :integration_test do it "supports searching by name" do expect { brew "search", "testball" } .to output(/testball/).to_stdout - .and not_to_output.to_stderr + .and output(/Searching/).to_stderr .and be_a_success end @@ -24,6 +25,13 @@ describe "brew search", :integration_test do .and be_a_success end + it "falls back to a GitHub tap search when no formula is found", :needs_network do + expect { brew "search", "caskroom/cask/firefox" } + .to output(/firefox/).to_stdout + .and output(/Searching/).to_stderr + .and be_a_success + end + describe "--desc" do let(:desc_cache) { HOMEBREW_CACHE/"desc_cache.json" } @@ -44,7 +52,7 @@ describe "brew search", :integration_test do "fink" => "http://pdb.finkproject.org/pdb/browse.php?summary=testball", "debian" => "https://packages.debian.org/search?keywords=testball&searchon=names&suite=all§ion=all", "opensuse" => "https://software.opensuse.org/search?q=testball", - "fedora" => "https://admin.fedoraproject.org/pkgdb/packages/%2Atestball%2A/", + "fedora" => "https://apps.fedoraproject.org/packages/s/testball", "ubuntu" => "http://packages.ubuntu.com/search?keywords=testball&searchon=names&suite=all§ion=all", }.each do |flag, url| specify "--#{flag}" do diff --git a/Library/Homebrew/test/formula_installer_spec.rb b/Library/Homebrew/test/formula_installer_spec.rb index c3573ae94..7365b2758 100644 --- a/Library/Homebrew/test/formula_installer_spec.rb +++ b/Library/Homebrew/test/formula_installer_spec.rb @@ -133,7 +133,7 @@ describe FormulaInstaller do }.to raise_error(CannotInstallFormulaError) end - describe "#install_requirement_formula?", :focus do + describe "#install_requirement_formula?" do before do @requirement = Python3Requirement.new @requirement_dependency = @requirement.to_dependency diff --git a/Library/Homebrew/test/rubocops/legacy_patches_cop_spec.rb b/Library/Homebrew/test/rubocops/patches_cop_spec.rb index a08fa614d..4bd79bf35 100644 --- a/Library/Homebrew/test/rubocops/legacy_patches_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/patches_cop_spec.rb @@ -1,9 +1,9 @@ require "rubocop" require "rubocop/rspec/support" require_relative "../../extend/string" -require_relative "../../rubocops/legacy_patches_cop" +require_relative "../../rubocops/patches_cop" -describe RuboCop::Cop::FormulaAudit::LegacyPatches do +describe RuboCop::Cop::FormulaAudit::Patches do subject(:cop) { described_class.new } context "When auditing legacy patches" do @@ -47,6 +47,7 @@ describe RuboCop::Cop::FormulaAudit::LegacyPatches do "https://mirrors.ustc.edu.cn/macports/trunk/", "http://trac.macports.org/export/102865/trunk/dports/mail/uudeview/files/inews.c.patch", "http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=5;filename=patch-libunac1.txt;att=1;bug=623340", + "https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch", ] patch_urls.each do |patch_url| source = <<-EOS.undent @@ -84,6 +85,15 @@ describe RuboCop::Cop::FormulaAudit::LegacyPatches do line: 5, column: 5, source: source }] + elsif patch_url =~ %r{https?://patch-diff\.githubusercontent\.com/raw/(.+)/(.+)/pull/(.+)\.(?:diff|patch)} + expected_offenses = [{ message: "use GitHub pull request URLs:\n"\ + " https://github.com/foo/foo-bar/pull/100.patch\n"\ + "Rather than patch-diff:\n"\ + " https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch\n", + severity: :convention, + line: 5, + column: 5, + source: source }] end expected_offenses.zip([cop.offenses.last]).each do |expected, actual| expect_offense(expected, actual) @@ -125,4 +135,67 @@ describe RuboCop::Cop::FormulaAudit::LegacyPatches do end end end + + context "When auditing external patches" do + it "Patch URLs" do + patch_urls = [ + "https://raw.github.com/mogaal/sendemail", + "https://mirrors.ustc.edu.cn/macports/trunk/", + "http://trac.macports.org/export/102865/trunk/dports/mail/uudeview/files/inews.c.patch", + "http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=5;filename=patch-libunac1.txt;att=1;bug=623340", + "https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch", + ] + patch_urls.each do |patch_url| + source = <<-EOS.undent + class Foo < Formula + homepage "ftp://example.com/foo" + url "http://example.com/foo-1.0.tgz" + patch do + url "#{patch_url}" + sha256 "63376b8fdd6613a91976106d9376069274191860cd58f039b29ff16de1925621" + end + end + EOS + + inspect_source(cop, source) + if patch_url =~ %r{/raw\.github\.com/} + expected_offenses = [{ message: "GitHub/Gist patches should specify a revision:\n#{patch_url}", + severity: :convention, + line: 5, + column: 16, + source: source }] + elsif patch_url =~ %r{macports/trunk} + expected_offenses = [{ message: "MacPorts patches should specify a revision instead of trunk:\n#{patch_url}", + severity: :convention, + line: 5, + column: 37, + source: source }] + elsif patch_url =~ %r{^http://trac\.macports\.org} + expected_offenses = [{ message: "Patches from MacPorts Trac should be https://, not http:\n#{patch_url}", + severity: :convention, + line: 5, + column: 9, + source: source }] + elsif patch_url =~ %r{^http://bugs\.debian\.org} + expected_offenses = [{ message: "Patches from Debian should be https://, not http:\n#{patch_url}", + severity: :convention, + line: 5, + column: 9, + source: source }] + elsif patch_url =~ %r{https?://patch-diff\.githubusercontent\.com/raw/(.+)/(.+)/pull/(.+)\.(?:diff|patch)} + expected_offenses = [{ message: "use GitHub pull request URLs:\n"\ + " https://github.com/foo/foo-bar/pull/100.patch\n"\ + "Rather than patch-diff:\n"\ + " https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch\n", + severity: :convention, + line: 5, + column: 9, + source: source }] + end + expected_offenses.zip([cop.offenses.last]).each do |expected, actual| + expect_offense(expected, actual) + end + end + end + end end diff --git a/Library/Homebrew/test/rubocops/text_cop_spec.rb b/Library/Homebrew/test/rubocops/text_cop_spec.rb index b218e9c25..ec13c4041 100644 --- a/Library/Homebrew/test/rubocops/text_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/text_cop_spec.rb @@ -7,6 +7,54 @@ describe RuboCop::Cop::FormulaAudit::Text do subject(:cop) { described_class.new } context "When auditing formula text" do + it "with both openssl and libressl optional dependencies" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + depends_on "openssl" + depends_on "libressl" => :optional + end + EOS + + expected_offenses = [{ message: "Formulae should not depend on both OpenSSL and LibreSSL (even optionally).", + severity: :convention, + line: 6, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with both openssl and libressl dependencies" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + depends_on "openssl" + depends_on "libressl" + end + EOS + + expected_offenses = [{ message: "Formulae should not depend on both OpenSSL and LibreSSL (even optionally).", + severity: :convention, + line: 6, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + it "When xcodebuild is called without SYMROOT" do source = <<-EOS.undent class Foo < Formula diff --git a/Library/Homebrew/test/utils/github_spec.rb b/Library/Homebrew/test/utils/github_spec.rb index 9b539262f..9322898ee 100644 --- a/Library/Homebrew/test/utils/github_spec.rb +++ b/Library/Homebrew/test/utils/github_spec.rb @@ -2,12 +2,38 @@ require "utils/github" describe GitHub do describe "::search_code", :needs_network do - it "searches code" do - results = subject.search_code("repo:Homebrew/brew", "path:/", "filename:readme", "language:markdown") + it "queries GitHub code with the passed paramaters" do + results = subject.search_code(repo: "Homebrew/brew", path: "/", + filename: "readme", language: "markdown") expect(results.count).to eq(1) expect(results.first["name"]).to eq("README.md") expect(results.first["path"]).to eq("README.md") end end + + describe "::query_string" do + it "builds a query with the given hash parameters formatted as key:value" do + query = subject.query_string(user: "Homebrew", repo: "brew") + expect(query).to eq("q=user%3AHomebrew+repo%3Abrew&per_page=100") + end + + it "adds a variable number of top-level string parameters to the query when provided" do + query = subject.query_string("value1", "value2", user: "Homebrew") + expect(query).to eq("q=value1+value2+user%3AHomebrew&per_page=100") + end + + it "turns array values into multiple key:value parameters" do + query = subject.query_string(user: ["Homebrew", "caskroom"]) + expect(query).to eq("q=user%3AHomebrew+user%3Acaskroom&per_page=100") + end + end + + describe "::search_issues", :needs_network do + it "queries GitHub issues with the passed parameters" do + results = subject.search_issues("brew search", repo: "Homebrew/brew", author: "avetamine", is: "closed") + expect(results).not_to be_empty + expect(results.last["title"]).to eq("brew search : 422 Unprocessable Entity") + end + end end diff --git a/Library/Homebrew/utils/curl.rb b/Library/Homebrew/utils/curl.rb index 5a40ae846..52d03c93e 100644 --- a/Library/Homebrew/utils/curl.rb +++ b/Library/Homebrew/utils/curl.rb @@ -1,42 +1,55 @@ require "pathname" require "open3" -def curl_args(options = {}) +def curl_executable curl = Pathname.new ENV["HOMEBREW_CURL"] curl = Pathname.new "/usr/bin/curl" unless curl.exist? - raise "#{curl} is not executable" unless curl.exist? && curl.executable? + return curl if curl.executable? + raise "#{curl} is not executable" +end +def curl_args(*extra_args, show_output: false, user_agent: :default) args = [ - curl.to_s, - "--remote-time", - "--location", + curl_executable.to_s, + "--show-error", ] - case options[:user_agent] - when :browser - args << "--user-agent" << HOMEBREW_USER_AGENT_FAKE_SAFARI + args << "--user-agent" << case user_agent + when :browser, :fake + HOMEBREW_USER_AGENT_FAKE_SAFARI + when :default + HOMEBREW_USER_AGENT_CURL else - args << "--user-agent" << HOMEBREW_USER_AGENT_CURL + user_agent end - unless options[:show_output] + unless show_output + args << "--fail" args << "--progress-bar" unless ARGV.verbose? args << "--verbose" if ENV["HOMEBREW_CURL_VERBOSE"] - args << "--fail" args << "--silent" if !$stdout.tty? || ENV["TRAVIS"] end - args += options[:extra_args] if options[:extra_args] - args + args + extra_args end def curl(*args) - safe_system(*curl_args(extra_args: args)) + safe_system(*curl_args(*args)) end -def curl_output(*args) - curl_args = curl_args(extra_args: args, show_output: true) - Open3.popen3(*curl_args) do |_, stdout, stderr, wait_thread| - [stdout.read, stderr.read, wait_thread.value] +def curl_download(*args, to: nil, **options) + continue_at ||= "-" + curl("--location", "--remote-time", "--continue-at", continue_at, "--output", to, *args, **options) +rescue ErrorDuringExecution + # `curl` error 33: HTTP server doesn't seem to support byte ranges. Cannot resume. + if $CHILD_STATUS.exitstatus == 33 && continue_at == "-" + continue_at = "0" + retry end + + raise +end + +def curl_output(*args, **options) + Open3.capture3(*curl_args(*args, show_output: true, **options)) end diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index 1a781cee6..a1cf5fbba 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -133,7 +133,7 @@ module GitHub def open(url, data: nil, scopes: [].freeze) # This is a no-op if the user is opting out of using the GitHub API. - return if ENV["HOMEBREW_NO_GITHUB_API"] + return block_given? ? yield({}) : {} if ENV["HOMEBREW_NO_GITHUB_API"] args = %W[--header application/vnd.github.v3+json --write-out \n%{http_code}] args += curl_args @@ -166,7 +166,7 @@ module GitHub args += ["--dump-header", headers_tmpfile.path] - output, errors, status = curl_output(url.to_s, *args) + output, errors, status = curl_output(url.to_s, "--location", *args) output, _, http_code = output.rpartition("\n") output, _, http_code = output.rpartition("\n") if http_code == "000" headers = headers_tmpfile.read @@ -227,72 +227,60 @@ module GitHub end end - def issues_matching(query, qualifiers = {}) - uri = URI.parse("#{API_URL}/search/issues") - uri.query = build_query_string(query, qualifiers) - open(uri) { |json| json["items"] } + def search_issues(query, **qualifiers) + search("issues", query, **qualifiers) end def repository(user, repo) - open(URI.parse("#{API_URL}/repos/#{user}/#{repo}")) + open(url_to("repos", user, repo)) end - def search_code(*params) - uri = URI.parse("#{API_URL}/search/code") - uri.query = "q=#{uri_escape(params.join(" "))}" - open(uri) { |json| json["items"] } - end - - def build_query_string(query, qualifiers) - s = "q=#{uri_escape(query)}+" - s << build_search_qualifier_string(qualifiers) - s << "&per_page=100" - end - - def build_search_qualifier_string(qualifiers) - { - repo: "Homebrew/homebrew-core", - in: "title", - }.update(qualifiers).map do |qualifier, value| - "#{qualifier}:#{value}" - end.join("+") - end - - def uri_escape(query) - if URI.respond_to?(:encode_www_form_component) - URI.encode_www_form_component(query) - else - require "erb" - ERB::Util.url_encode(query) - end + def search_code(**qualifiers) + search("code", **qualifiers) end def issues_for_formula(name, options = {}) tap = options[:tap] || CoreTap.instance - issues_matching(name, state: "open", repo: "#{tap.user}/homebrew-#{tap.repo}") + search_issues(name, state: "open", repo: "#{tap.user}/homebrew-#{tap.repo}") end def print_pull_requests_matching(query) - return [] if ENV["HOMEBREW_NO_GITHUB_API"] - - open_or_closed_prs = issues_matching(query, type: "pr") + open_or_closed_prs = search_issues(query, type: "pr") open_prs = open_or_closed_prs.select { |i| i["state"] == "open" } - if !open_prs.empty? + prs = if !open_prs.empty? puts "Open pull requests:" - prs = open_prs - elsif !open_or_closed_prs.empty? - puts "Closed pull requests:" - prs = open_or_closed_prs + open_prs else - return + puts "Closed pull requests:" unless open_or_closed_prs.empty? + open_or_closed_prs end prs.each { |i| puts "#{i["title"]} (#{i["html_url"]})" } end def private_repo?(full_name) - uri = URI.parse("#{API_URL}/repos/#{full_name}") + uri = url_to "repos", full_name open(uri) { |json| json["private"] } end + + def query_string(*main_params, **qualifiers) + params = main_params + + params += qualifiers.flat_map do |key, value| + Array(value).map { |v| "#{key}:#{v}" } + end + + "q=#{URI.encode_www_form_component(params.join(" "))}&per_page=100" + end + + def url_to(*subroutes) + URI.parse([API_URL, *subroutes].join("/")) + end + + def search(entity, *queries, **qualifiers) + uri = url_to "search", entity + uri.query = query_string(*queries, **qualifiers) + open(uri) { |json| json.fetch("items", []) } + end end diff --git a/completions/bash/brew b/completions/bash/brew index 03a05cb37..60c272f73 100644 --- a/completions/bash/brew +++ b/completions/bash/brew @@ -563,10 +563,10 @@ _brew() { if [[ "$i" -eq "$COMP_CWORD" ]] then - # Do not auto-complete "instal" abbreviation for "install" command. + # Do not auto-complete "*instal" or "*uninstal" aliases for "*install" commands. # Prefix newline to prevent not checking the first command. - local cmds=$'\n'"$(brew commands --quiet --include-aliases)" - __brewcomp "${cmds/$'\n'instal$'\n'/$'\n'}" + local cmds=$'\n'"$(brew commands --quiet --include-aliases | grep -v instal$)" + __brewcomp "${cmds}" return fi diff --git a/completions/zsh/_brew b/completions/zsh/_brew index 61dc26b42..bc88f402b 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -284,8 +284,8 @@ _brew_create() { ':url:_urls' } -# brew deps [--1] [-n] [--union] [--full-name] [--installed] [--include-build] [--include-optional] [--skip-recommended] formulae -# brew deps --tree [filters] (formulae|--installed) +# brew deps [--1] [-n] [--union] [--full-name] [--installed] [--include-build] [--include-optional] [--skip-recommended] [--include-requirements] formulae +# brew deps --tree [--1] [filters] (formulae|--installed) # brew deps [filters] (--installed|--all) # The filters placeholder is any combination of options --include-build, --include-optional, and --skip-recommended as documented above. _brew_deps() { @@ -294,6 +294,7 @@ _brew_deps() { '--include-build[include \:build dependencies]' \ '--include-optional[include \:optional dependencies]' \ '--skip-recommended[skip \:recommended type dependencies]' \ + '--include-requirements[include requirements]' \ '--1[only show dependencies one level down, instead of recursing]' \ '-n[show dependencies in topological order]' \ '--union[show the union of dependencies for formulae, instead of the intersection]' \ @@ -303,6 +304,11 @@ _brew_deps() { - tree-deps \ '--tree[show dependencies as a tree]' \ '(*)--installed[show dependencies for all installed formulae]' \ + '--include-build[include \:build dependencies]' \ + '--include-optional[include \:optional dependencies]' \ + '--skip-recommended[skip \:recommended type dependencies]' \ + '--include-requirements[include requirements]' \ + '--1[only show dependencies one level down, instead of recursing]' \ '(--installed)*:formulae:__brew_formulae' \ - installed-all \ '--include-build[include \:build dependencies]' \ diff --git a/docs/FAQ.md b/docs/FAQ.md index 23f53d54c..788c49626 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -26,6 +26,8 @@ To allow that formulae to update again: brew unpin <formula> +Note that pinned, outdated formulae that are depended on by another formula will be upgraded when required as we do not allow formulae to be built against non-latest versions. + ## How do I uninstall old versions of a formula? By default, Homebrew does not uninstall old versions of a formula, so over time you will accumulate old versions. To remove them, simply use: diff --git a/docs/Manpage.md b/docs/Manpage.md index 49f6bb5ae..f92991147 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -77,7 +77,7 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note a bug report, you will likely be asked for this information if you do not provide it. - * `deps` [`--1`] [`-n`] [`--union`] [`--full-name`] [`--installed`] [`--include-build`] [`--include-optional`] [`--skip-recommended`] `formulae`: + * `deps` [`--1`] [`-n`] [`--union`] [`--full-name`] [`--installed`] [`--include-build`] [`--include-optional`] [`--skip-recommended`] [`--include-requirements`] `formulae`: Show dependencies for `formulae`. When given multiple formula arguments, show the intersection of dependencies for `formulae`. @@ -98,15 +98,22 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note `formulae`. To include the `:build` type dependencies, pass `--include-build`. Similarly, pass `--include-optional` to include `:optional` dependencies. To skip `:recommended` type dependencies, pass `--skip-recommended`. + To include requirements in addition to dependencies, pass `--include-requirements`. - * `deps` `--tree` [`filters`] (`formulae`|`--installed`): + * `deps` `--tree` [`--1`] [`filters`] [`--annotate`] (`formulae`|`--installed`): Show dependencies as a tree. When given multiple formula arguments, output individual trees for every formula. + If `--1` is passed, only one level of children is displayed. + If `--installed` is passed, output a tree for every installed formula. The `filters` placeholder is any combination of options `--include-build`, - `--include-optional`, and `--skip-recommended` as documented above. + `--include-optional`, `--skip-recommended`, and `--include-requirements` as + documented above. + + If `--annotate` is passed, the build, optional, and recommended dependencies + are marked as such in the output. * `deps` [`filters`] (`--installed`|`--all`): Show dependencies for installed or all available formulae. Every line of @@ -364,7 +371,8 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note * `pin` `formulae`: Pin the specified `formulae`, preventing them from being upgraded when - issuing the `brew upgrade` command. See also `unpin`. + issuing the `brew upgrade `formulae`` command (but can still be upgraded + as dependencies for other formulae). See also `unpin`. * `postinstall` `formula`: Rerun the post-install steps for `formula`. @@ -529,8 +537,8 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note source. This is useful for creating patches for the software. * `unpin` `formulae`: - Unpin `formulae`, allowing them to be upgraded by `brew upgrade`. See also - `pin`. + Unpin `formulae`, allowing them to be upgraded by `brew upgrade `formulae``. + See also `pin`. * `untap` `tap`: Remove a tapped repository. diff --git a/manpages/brew-cask.1 b/manpages/brew-cask.1 index f11d48c09..763d78ebe 100644 --- a/manpages/brew-cask.1 +++ b/manpages/brew-cask.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BREW\-CASK" "1" "July 2017" "Homebrew" "brew-cask" +.TH "BREW\-CASK" "1" "August 2017" "Homebrew" "brew-cask" . .SH "NAME" \fBbrew\-cask\fR \- a friendly binary installer for macOS diff --git a/manpages/brew.1 b/manpages/brew.1 index 2d8ba3f09..362e884ed 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BREW" "1" "July 2017" "Homebrew" "brew" +.TH "BREW" "1" "August 2017" "Homebrew" "brew" . .SH "NAME" \fBbrew\fR \- The missing package manager for macOS @@ -88,7 +88,7 @@ If \fB\-\-quiet\fR is passed, list only the names of commands without the header Show Homebrew and system configuration useful for debugging\. If you file a bug report, you will likely be asked for this information if you do not provide it\. . .TP -\fBdeps\fR [\fB\-\-1\fR] [\fB\-n\fR] [\fB\-\-union\fR] [\fB\-\-full\-name\fR] [\fB\-\-installed\fR] [\fB\-\-include\-build\fR] [\fB\-\-include\-optional\fR] [\fB\-\-skip\-recommended\fR] \fIformulae\fR +\fBdeps\fR [\fB\-\-1\fR] [\fB\-n\fR] [\fB\-\-union\fR] [\fB\-\-full\-name\fR] [\fB\-\-installed\fR] [\fB\-\-include\-build\fR] [\fB\-\-include\-optional\fR] [\fB\-\-skip\-recommended\fR] [\fB\-\-include\-requirements\fR] \fIformulae\fR Show dependencies for \fIformulae\fR\. When given multiple formula arguments, show the intersection of dependencies for \fIformulae\fR\. . .IP @@ -107,17 +107,23 @@ If \fB\-\-full\-name\fR is passed, list dependencies by their full name\. If \fB\-\-installed\fR is passed, only list those dependencies that are currently installed\. . .IP -By default, \fBdeps\fR shows required and recommended dependencies for \fIformulae\fR\. To include the \fB:build\fR type dependencies, pass \fB\-\-include\-build\fR\. Similarly, pass \fB\-\-include\-optional\fR to include \fB:optional\fR dependencies\. To skip \fB:recommended\fR type dependencies, pass \fB\-\-skip\-recommended\fR\. +By default, \fBdeps\fR shows required and recommended dependencies for \fIformulae\fR\. To include the \fB:build\fR type dependencies, pass \fB\-\-include\-build\fR\. Similarly, pass \fB\-\-include\-optional\fR to include \fB:optional\fR dependencies\. To skip \fB:recommended\fR type dependencies, pass \fB\-\-skip\-recommended\fR\. To include requirements in addition to dependencies, pass \fB\-\-include\-requirements\fR\. . .TP -\fBdeps\fR \fB\-\-tree\fR [\fIfilters\fR] (\fIformulae\fR|\fB\-\-installed\fR) +\fBdeps\fR \fB\-\-tree\fR [\fB\-\-1\fR] [\fIfilters\fR] [\fB\-\-annotate\fR] (\fIformulae\fR|\fB\-\-installed\fR) Show dependencies as a tree\. When given multiple formula arguments, output individual trees for every formula\. . .IP +If \fB\-\-1\fR is passed, only one level of children is displayed\. +. +.IP If \fB\-\-installed\fR is passed, output a tree for every installed formula\. . .IP -The \fIfilters\fR placeholder is any combination of options \fB\-\-include\-build\fR, \fB\-\-include\-optional\fR, and \fB\-\-skip\-recommended\fR as documented above\. +The \fIfilters\fR placeholder is any combination of options \fB\-\-include\-build\fR, \fB\-\-include\-optional\fR, \fB\-\-skip\-recommended\fR, and \fB\-\-include\-requirements\fR as documented above\. +. +.IP +If \fB\-\-annotate\fR is passed, the build, optional, and recommended dependencies are marked as such in the output\. . .TP \fBdeps\fR [\fIfilters\fR] (\fB\-\-installed\fR|\fB\-\-all\fR) @@ -378,7 +384,7 @@ If \fB\-\-fetch\-HEAD\fR is passed, fetch the upstream repository to detect if t . .TP \fBpin\fR \fIformulae\fR -Pin the specified \fIformulae\fR, preventing them from being upgraded when issuing the \fBbrew upgrade\fR command\. See also \fBunpin\fR\. +Pin the specified \fIformulae\fR, preventing them from being upgraded when issuing the \fBbrew upgrade <formulae>\fR command (but can still be upgraded as dependencies for other formulae)\. See also \fBunpin\fR\. . .TP \fBpostinstall\fR \fIformula\fR @@ -549,7 +555,7 @@ If \fB\-\-git\fR (or \fB\-g\fR) is passed, a Git repository will be initialized . .TP \fBunpin\fR \fIformulae\fR -Unpin \fIformulae\fR, allowing them to be upgraded by \fBbrew upgrade\fR\. See also \fBpin\fR\. +Unpin \fIformulae\fR, allowing them to be upgraded by \fBbrew upgrade <formulae>\fR\. See also \fBpin\fR\. . .TP \fBuntap\fR \fItap\fR |
