Module: Homebrew

Extended by:
FileUtils, Search
Defined in:
brew/Library/Homebrew/help.rb,
brew/Library/Homebrew/fetch.rb,
brew/Library/Homebrew/style.rb,
brew/Library/Homebrew/utils.rb,
brew/Library/Homebrew/cmd/sh.rb,
brew/Library/Homebrew/global.rb,
brew/Library/Homebrew/search.rb,
brew/Library/Homebrew/cleanup.rb,
brew/Library/Homebrew/cmd/cat.rb,
brew/Library/Homebrew/cmd/diy.rb,
brew/Library/Homebrew/cmd/log.rb,
brew/Library/Homebrew/cmd/pin.rb,
brew/Library/Homebrew/cmd/tap.rb,
brew/Library/Homebrew/install.rb,
brew/Library/Homebrew/options.rb,
brew/Library/Homebrew/cli/args.rb,
brew/Library/Homebrew/cmd/cask.rb,
brew/Library/Homebrew/cmd/deps.rb,
brew/Library/Homebrew/cmd/desc.rb,
brew/Library/Homebrew/cmd/help.rb,
brew/Library/Homebrew/cmd/home.rb,
brew/Library/Homebrew/cmd/info.rb,
brew/Library/Homebrew/cmd/link.rb,
brew/Library/Homebrew/cmd/list.rb,
brew/Library/Homebrew/cmd/uses.rb,
brew/Library/Homebrew/cmd/--env.rb,
brew/Library/Homebrew/cmd/fetch.rb,
brew/Library/Homebrew/cmd/style.rb,
brew/Library/Homebrew/cmd/unpin.rb,
brew/Library/Homebrew/cmd/untap.rb,
brew/Library/Homebrew/reinstall.rb,
brew/Library/Homebrew/cli/parser.rb,
brew/Library/Homebrew/cmd/config.rb,
brew/Library/Homebrew/cmd/doctor.rb,
brew/Library/Homebrew/cmd/leaves.rb,
brew/Library/Homebrew/cmd/search.rb,
brew/Library/Homebrew/cmd/switch.rb,
brew/Library/Homebrew/cmd/unlink.rb,
brew/Library/Homebrew/cmd/unpack.rb,
brew/Library/Homebrew/diagnostic.rb,
brew/Library/Homebrew/utils/gems.rb,
brew/Library/Homebrew/cmd/--cache.rb,
brew/Library/Homebrew/cmd/cleanup.rb,
brew/Library/Homebrew/cmd/command.rb,
brew/Library/Homebrew/cmd/install.rb,
brew/Library/Homebrew/cmd/migrate.rb,
brew/Library/Homebrew/cmd/missing.rb,
brew/Library/Homebrew/cmd/options.rb,
brew/Library/Homebrew/cmd/readall.rb,
brew/Library/Homebrew/cmd/tap-pin.rb,
brew/Library/Homebrew/cmd/upgrade.rb,
brew/Library/Homebrew/dev-cmd/irb.rb,
brew/Library/Homebrew/dev-cmd/man.rb,
brew/Library/Homebrew/cmd/--cellar.rb,
brew/Library/Homebrew/cmd/--prefix.rb,
brew/Library/Homebrew/cmd/commands.rb,
brew/Library/Homebrew/cmd/outdated.rb,
brew/Library/Homebrew/cmd/tap-info.rb,
brew/Library/Homebrew/dependencies.rb,
brew/Library/Homebrew/dev-cmd/edit.rb,
brew/Library/Homebrew/dev-cmd/prof.rb,
brew/Library/Homebrew/dev-cmd/pull.rb,
brew/Library/Homebrew/dev-cmd/ruby.rb,
brew/Library/Homebrew/dev-cmd/test.rb,
brew/Library/Homebrew/cmd/--version.rb,
brew/Library/Homebrew/cmd/analytics.rb,
brew/Library/Homebrew/cmd/gist-logs.rb,
brew/Library/Homebrew/cmd/reinstall.rb,
brew/Library/Homebrew/cmd/tap-unpin.rb,
brew/Library/Homebrew/cmd/uninstall.rb,
brew/Library/Homebrew/dev-cmd/audit.rb,
brew/Library/Homebrew/dev-cmd/tests.rb,
brew/Library/Homebrew/dev-cmd/bottle.rb,
brew/Library/Homebrew/dev-cmd/create.rb,
brew/Library/Homebrew/dev-cmd/mirror.rb,
brew/Library/Homebrew/cmd/postinstall.rb,
brew/Library/Homebrew/dev-cmd/extract.rb,
brew/Library/Homebrew/dev-cmd/formula.rb,
brew/Library/Homebrew/dev-cmd/linkage.rb,
brew/Library/Homebrew/dev-cmd/pr-pull.rb,
brew/Library/Homebrew/dev-cmd/tap-new.rb,
brew/Library/Homebrew/formula_creator.rb,
brew/Library/Homebrew/missing_formula.rb,
brew/Library/Homebrew/os/linux/global.rb,
brew/Library/Homebrew/cmd/--repository.rb,
brew/Library/Homebrew/build_environment.rb,
brew/Library/Homebrew/cmd/update-report.rb,
brew/Library/Homebrew/formula_free_port.rb,
brew/Library/Homebrew/dev-cmd/pr-publish.rb,
brew/Library/Homebrew/formula_assertions.rb,
brew/Library/Homebrew/dev-cmd/update-test.rb,
brew/Library/Homebrew/dev-cmd/vendor-gems.rb,
brew/Library/Homebrew/os/linux/diagnostic.rb,
brew/Library/Homebrew/extend/os/mac/search.rb,
brew/Library/Homebrew/dev-cmd/bump-revision.rb,
brew/Library/Homebrew/dev-cmd/release-notes.rb,
brew/Library/Homebrew/dev-cmd/bump-formula-pr.rb,
brew/Library/Homebrew/extend/os/linux/install.rb,
brew/Library/Homebrew/extend/os/mac/diagnostic.rb,
brew/Library/Homebrew/extend/os/linux/diagnostic.rb,
brew/Library/Homebrew/dev-cmd/install-bundler-gems.rb,
brew/Library/Homebrew/extend/os/mac/missing_formula.rb

Defined Under Namespace

Modules: Assertions, CLI, Diagnostic, Fetch, FreePort, Help, Install, MissingFormula, Search, Style Classes: Cleanup, DependentsMessage, DeveloperDependentsMessage, FormulaAuditor, FormulaCreator, FormulaText, NondeveloperDependentsMessage, PatchPuller, ResourceAuditor

Constant Summary collapse

DEFAULT_CELLAR =
"#{DEFAULT_PREFIX}/Cellar"
DEFAULT_REPOSITORY =
"#{DEFAULT_PREFIX}/Homebrew"
VALID_DAYS =
%w[30 90 365].freeze
VALID_FORMULA_CATEGORIES =
%w[install install-on-request build-error].freeze
VALID_CATEGORIES =
(VALID_FORMULA_CATEGORIES + %w[cask-install os-version]).freeze
UNBREWED_EXCLUDE_FILES =
%w[.DS_Store].freeze
UNBREWED_EXCLUDE_PATHS =
%w[
  .github/*
  bin/brew
  completions/zsh/_brew
  docs/*
  lib/gdk-pixbuf-2.0/*
  lib/gio/*
  lib/node_modules/*
  lib/python[23].[0-9]/*
  lib/pypy/*
  lib/pypy3/*
  lib/ruby/gems/[12].*
  lib/ruby/site_ruby/[12].*
  lib/ruby/vendor_ruby/[12].*
  manpages/brew.1
  manpages/brew-cask.1
  share/pypy/*
  share/pypy3/*
  share/info/dir
  share/man/whatis
].freeze
PACKAGE_MANAGERS =
{
  macports: ->(query) { "https://www.macports.org/ports.php?by=name&substr=#{query}" },
  fink:     ->(query) { "http://pdb.finkproject.org/pdb/browse.php?summary=#{query}" },
  opensuse: ->(query) { "https://software.opensuse.org/search?q=#{query}" },
  fedora:   ->(query) { "https://apps.fedoraproject.org/packages/s/#{query}" },
  debian:   lambda { |query|
    "https://packages.debian.org/search?keywords=#{query}&searchon=names&suite=all&section=all"
  },
  ubuntu:   lambda { |query|
    "https://packages.ubuntu.com/search?keywords=#{query}&searchon=names&suite=all&section=all"
  },
}.freeze
HOMEBREW_BUNDLER_VERSION =

Keep in sync with the Gemfile.lock’s BUNDLED WITH.

"1.17.2"
SOURCE_PATH =
(HOMEBREW_LIBRARY_PATH/"manpages").freeze
TARGET_MAN_PATH =
(HOMEBREW_REPOSITORY/"manpages").freeze
TARGET_DOC_PATH =
(HOMEBREW_REPOSITORY/"docs").freeze

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Search

query_regexp, search_casks, search_descriptions, search_formulae, search_taps

Class Attribute Details

.argsObject



86
87
88
# File 'brew/Library/Homebrew/global.rb', line 86

def args
  @args ||= CLI::Args.new(argv: ARGV)
end

.auditing=(value) ⇒ Object (writeonly)

Sets the attribute auditing

Parameters:

  • value

    the value to set the attribute auditing to.



75
76
77
# File 'brew/Library/Homebrew/global.rb', line 75

def auditing=(value)
  @auditing = value
end

.failed=(value) ⇒ Object (writeonly)

Sets the attribute failed

Parameters:

  • value

    the value to set the attribute failed to.



75
76
77
# File 'brew/Library/Homebrew/global.rb', line 75

def failed=(value)
  @failed = value
end

.raise_deprecation_exceptions=(value) ⇒ Object (writeonly)

Sets the attribute raise_deprecation_exceptions

Parameters:

  • value

    the value to set the attribute raise_deprecation_exceptions to.



75
76
77
# File 'brew/Library/Homebrew/global.rb', line 75

def raise_deprecation_exceptions=(value)
  @raise_deprecation_exceptions = value
end

Class Method Details

.__cacheObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'brew/Library/Homebrew/cmd/--cache.rb', line 26

def __cache
  __cache_args.parse

  if args.no_named?
    puts HOMEBREW_CACHE
  else
    args.formulae.each do |f|
      if Fetch.fetch_bottle?(f)
        puts f.bottle.cached_download
      else
        puts f.cached_download
      end
    end
  end
end

.__cache_argsObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'brew/Library/Homebrew/cmd/--cache.rb', line 9

def __cache_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `--cache` [<options>] [<formula>]

      Display Homebrew's download cache. See also `HOMEBREW_CACHE`.

      If <formula> is provided, display the file or directory used to cache <formula>.
    EOS
    switch "-s", "--build-from-source",
           description: "Show the cache file used when building from source."
    switch "--force-bottle",
           description: "Show the cache file used when pouring a bottle."
    conflicts "--build-from-source", "--force-bottle"
  end
end

.__cellarObject



22
23
24
25
26
27
28
29
30
# File 'brew/Library/Homebrew/cmd/--cellar.rb', line 22

def __cellar
  __cellar_args.parse

  if args.no_named?
    puts HOMEBREW_CELLAR
  else
    puts args.resolved_formulae.map(&:rack)
  end
end

.__cellar_argsObject



8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'brew/Library/Homebrew/cmd/--cellar.rb', line 8

def __cellar_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `--cellar` [<formula>]

      Display Homebrew's Cellar path. *Default:* `$(brew --prefix)/Cellar`, or if
      that directory doesn't exist, `$(brew --repository)/Cellar`.

      If <formula> is provided, display the location in the cellar where <formula>
      would be installed, without any sort of versioned directory as the last path.
    EOS
  end
end

.__envObject



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'brew/Library/Homebrew/cmd/--env.rb', line 29

def __env
  __env_args.parse

  ENV.activate_extensions!
  ENV.deps = args.formulae if superenv?
  ENV.setup_build_environment

  shell = if args.plain?
    nil
  elsif args.shell.nil?    # legacy behavior

    :bash unless $stdout.tty?
  elsif args.shell == "auto"
    Utils::Shell.parent || Utils::Shell.preferred
  elsif args.shell
    Utils::Shell.from_path(args.shell)
  end

  env_keys = build_env_keys(ENV)
  if shell.nil?
    dump_build_env ENV
  else
    env_keys.each do |key|
      puts Utils::Shell.export_value(key, ENV[key], shell)
    end
  end
end

.__env_argsObject



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'brew/Library/Homebrew/cmd/--env.rb', line 11

def __env_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `--env` [<options>] [<formula>]

      Summarise Homebrew's build environment as a plain list.

      If the command's output is sent through a pipe and no shell is specified,
      the list is formatted for export to `bash`(1) unless `--plain` is passed.
    EOS
    flag   "--shell=",
           description: "Generate a list of environment variables for the specified shell, " \
                        "or `--shell=auto` to detect the current shell."
    switch "--plain",
           description: "Generate plain output even when piped."
  end
end

.__getsObject



131
132
133
134
# File 'brew/Library/Homebrew/dev-cmd/create.rb', line 131

def __gets
  gots = $stdin.gets.chomp
  gots.empty? ? nil : gots
end

.__prefixObject



22
23
24
25
26
27
28
29
30
31
32
# File 'brew/Library/Homebrew/cmd/--prefix.rb', line 22

def __prefix
  __prefix_args.parse

  if args.no_named?
    puts HOMEBREW_PREFIX
  else
    puts args.resolved_formulae.map { |f|
      f.opt_prefix.exist? ? f.opt_prefix : f.installed_prefix
    }
  end
end

.__prefix_argsObject



8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'brew/Library/Homebrew/cmd/--prefix.rb', line 8

def __prefix_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `--prefix` [<formula>]

      Display Homebrew's install path. *Default:* `/usr/local` on macOS and
      `/home/linuxbrew/.linuxbrew` on Linux.

      If <formula> is provided, display the location in the cellar where <formula>
      is or would be installed.
    EOS
  end
end

.__repositoryObject



20
21
22
23
24
25
26
27
28
# File 'brew/Library/Homebrew/cmd/--repository.rb', line 20

def __repository
  __repository_args.parse

  if args.no_named?
    puts HOMEBREW_REPOSITORY
  else
    puts args.named.map { |tap| Tap.fetch(tap).path }
  end
end

.__repository_argsObject



8
9
10
11
12
13
14
15
16
17
18
# File 'brew/Library/Homebrew/cmd/--repository.rb', line 8

def __repository_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `--repository`, `--repo` [<user>`/`<repo>]

      Display where Homebrew's `.git` directory is located.

      If <user>`/`<repo> are provided, display where tap <user>`/`<repo>'s directory is located.
    EOS
  end
end

.__versionObject



20
21
22
23
24
25
26
# File 'brew/Library/Homebrew/cmd/--version.rb', line 20

def __version
  __version_args.parse

  puts "Homebrew #{HOMEBREW_VERSION}"
  puts "#{CoreTap.instance.full_name} #{CoreTap.instance.version_string}"
  puts "#{Tap.default_cask_tap.full_name} #{Tap.default_cask_tap.version_string}" if Tap.default_cask_tap.installed?
end

.__version_argsObject



8
9
10
11
12
13
14
15
16
17
18
# File 'brew/Library/Homebrew/cmd/--version.rb', line 8

def __version_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `--version`

      Print the version numbers of Homebrew, Homebrew/homebrew-core and Homebrew/homebrew-cask
      (if tapped) to standard output.
    EOS
    max_named 0
  end
end

._system(cmd, *args, **options) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'brew/Library/Homebrew/utils.rb', line 22

def _system(cmd, *args, **options)
  pid = fork do
    yield if block_given?
    args.map!(&:to_s)
    begin
      exec(cmd, *args, **options)
    rescue
      nil
    end
    exit! 1 # never gets here unless exec failed
  end
  Process.wait(pid)
  $CHILD_STATUS.success?
end

.alias_update_pair(formula, new_formula_version) ⇒ Object



506
507
508
509
510
511
512
513
514
515
516
# File 'brew/Library/Homebrew/dev-cmd/bump-formula-pr.rb', line 506

def alias_update_pair(formula, new_formula_version)
  versioned_alias = formula.aliases.grep(/^.*@\d+(\.\d+)?$/).first
  return if versioned_alias.nil?

  name, old_alias_version = versioned_alias.split("@")
  new_alias_regex = (old_alias_version.split(".").length == 1) ? /^\d+/ : /^\d+\.\d+/
  new_alias_version, = *new_formula_version.to_s.match(new_alias_regex)
  return if Version.create(new_alias_version) <= Version.create(old_alias_version)

  [versioned_alias, "#{name}@#{new_alias_version}"]
end

.analyticsObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'brew/Library/Homebrew/cmd/analytics.rb', line 26

def analytics
  analytics_args.parse

  case args.named.first
  when nil, "state"
    if Utils::Analytics.disabled?
      puts "Analytics are disabled."
    else
      puts "Analytics are enabled."
      puts "UUID: #{Utils::Analytics.uuid}" if Utils::Analytics.uuid.present?
    end
  when "on"
    Utils::Analytics.enable!
  when "off"
    Utils::Analytics.disable!
  when "regenerate-uuid"
    Utils::Analytics.regenerate_uuid!
  else
    raise UsageError, "unknown subcommand"
  end
end

.analytics_argsObject



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'brew/Library/Homebrew/cmd/analytics.rb', line 8

def analytics_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `analytics` [<subcommand>]

      If `on` or `off` is passed, turn Homebrew's analytics on or off respectively.

      If `state` is passed, display the current anonymous user behaviour analytics state.
      Read more at <https://docs.brew.sh/Analytics>.

      If `regenerate-uuid` is passed, regenerate the UUID used in Homebrew's analytics.
    EOS
    switch :verbose
    switch :debug
    max_named 1
  end
end

.argv_includes_ignores(argv) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'brew/Library/Homebrew/dependencies.rb', line 62

def argv_includes_ignores(argv)
  includes = []
  ignores = []

  if argv.include? "--include-build"
    includes << "build?"
  else
    ignores << "build?"
  end

  if argv.include? "--include-test"
    includes << "test?"
  else
    ignores << "test?"
  end

  if argv.include? "--include-optional"
    includes << "optional?"
  else
    ignores << "optional?"
  end

  ignores << "recommended?" if ARGV.include? "--skip-recommended"

  [includes, ignores]
end

.auditObject



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'brew/Library/Homebrew/dev-cmd/audit.rb', line 65

def audit
  audit_args.parse

  Homebrew.auditing = true
  inject_dump_stats!(FormulaAuditor, /^audit_/) if args.audit_debug?

  formula_count = 0
  problem_count = 0
  corrected_problem_count = 0
  new_formula_problem_count = 0
  new_formula = args.new_formula?
  strict = new_formula || args.strict?
  online = new_formula || args.online?

  ENV.activate_extensions!
  ENV.setup_build_environment

  if args.no_named?
    ff = Formula
    files = Tap.map(&:formula_dir)
  else
    ff = args.resolved_formulae
    files = args.resolved_formulae.map(&:path)
  end

  only_cops = args.only_cops
  except_cops = args.except_cops
  options = { fix: args.fix? }

  if only_cops
    options[:only_cops] = only_cops
  elsif args.new_formula?
    nil
  elsif strict
    options[:except_cops] = [:NewFormulaAudit]
  elsif except_cops
    options[:except_cops] = except_cops
  elsif !strict
    options[:only_cops] = [:FormulaAudit]
  end

  # Check style in a single batch run up front for performance
  style_results = Style.check_style_json(files, options)

  new_formula_problem_lines = []
  ff.sort.each do |f|
    only = only_cops ? ["style"] : args.only
    options = { new_formula: new_formula, strict: strict, online: online, only: only, except: args.except }
    options[:style_offenses] = style_results.file_offenses(f.path)
    options[:display_cop_names] = args.display_cop_names?

    fa = FormulaAuditor.new(f, options)
    fa.audit
    next if fa.problems.empty? && fa.new_formula_problems.empty?

    fa.problems
    formula_count += 1
    problem_count += fa.problems.size
    problem_lines = format_problem_lines(fa.problems)
    corrected_problem_count = options[:style_offenses].count(&:corrected?)
    new_formula_problem_lines = format_problem_lines(fa.new_formula_problems)
    if args.display_filename?
      puts problem_lines.map { |s| "#{f.path}: #{s}" }
    else
      puts "#{f.full_name}:", problem_lines.map { |s| "  #{s}" }
    end
  end

  created_pr_comment = false
  if new_formula && !new_formula_problem_lines.empty?
    begin
      created_pr_comment = true if GitHub.create_issue_comment(new_formula_problem_lines.join("\n"))
    rescue *GitHub.api_errors => e
      opoo "Unable to create issue comment: #{e.message}"
    end
  end

  unless created_pr_comment
    new_formula_problem_count += new_formula_problem_lines.size
    puts new_formula_problem_lines.map { |s| "  #{s}" }
  end

  total_problems_count = problem_count + new_formula_problem_count
  problem_plural = "#{total_problems_count} #{"problem".pluralize(total_problems_count)}"
  formula_plural = "#{formula_count} #{"formula".pluralize(formula_count)}"
  corrected_problem_plural = "#{corrected_problem_count} #{"problem".pluralize(corrected_problem_count)}"
  errors_summary = "#{problem_plural} in #{formula_plural} detected"
  errors_summary += ", #{corrected_problem_plural} corrected" if corrected_problem_count.positive?

  if problem_count.positive? ||
     (new_formula_problem_count.positive? && !created_pr_comment)
    ofail errors_summary
  end
end

.audit_argsObject



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'brew/Library/Homebrew/dev-cmd/audit.rb', line 18

def audit_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `audit` [<options>] [<formula>]

      Check <formula> for Homebrew coding style violations. This should be run before
      submitting a new formula. If no <formula> are provided, check all locally
      available formulae. Will exit with a non-zero status if any errors are
      found, which can be useful for implementing pre-commit hooks.
    EOS
    switch "--strict",
           description: "Run additional style checks, including RuboCop style checks."
    switch "--online",
           description: "Run additional slower style checks that require a network connection."
    switch "--new-formula",
           description: "Run various additional style checks to determine if a new formula is eligible "\
                        "for Homebrew. This should be used when creating new formula and implies "\
                        "`--strict` and `--online`."
    switch "--fix",
           description: "Fix style violations automatically using RuboCop's auto-correct feature."
    switch "--display-cop-names",
           description: "Include the RuboCop cop name for each violation in the output."
    switch "--display-filename",
           description: "Prefix every line of output with the file or formula name being audited, to "\
                        "make output easy to grep."
    switch "-D", "--audit-debug",
           description: "Enable debugging and profiling of audit methods."
    comma_array "--only",
                description: "Specify a comma-separated <method> list to only run the methods named "\
                             "`audit_`<method>."
    comma_array "--except",
                description: "Specify a comma-separated <method> list to skip running the methods named "\
                             "`audit_`<method>."
    comma_array "--only-cops",
                description: "Specify a comma-separated <cops> list to check for violations of only the listed "\
                             "RuboCop cops."
    comma_array "--except-cops",
                description: "Specify a comma-separated <cops> list to skip checking for violations of the listed "\
                             "RuboCop cops."
    switch :verbose
    switch :debug
    conflicts "--only", "--except"
    conflicts "--only-cops", "--except-cops", "--strict"
    conflicts "--only-cops", "--except-cops", "--only"
  end
end

.auditing?Boolean

Returns:

  • (Boolean)


98
99
100
# File 'brew/Library/Homebrew/global.rb', line 98

def auditing?
  @auditing == true
end

.backup(keg) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
# File 'brew/Library/Homebrew/reinstall.rb', line 60

def backup(keg)
  keg.unlink
  begin
    keg.rename backup_path(keg)
  rescue Errno::EACCES, Errno::ENOTEMPTY
    odie <<~EOS
      Could not rename #{keg.name} keg! Check/fix its permissions:
        sudo chown -R $(whoami) #{keg}
    EOS
  end
end

.backup_path(path) ⇒ Object



83
84
85
# File 'brew/Library/Homebrew/reinstall.rb', line 83

def backup_path(path)
  Pathname.new "#{path}.reinstall"
end

.bottleObject



86
87
88
89
90
91
92
93
94
95
# File 'brew/Library/Homebrew/dev-cmd/bottle.rb', line 86

def bottle
  bottle_args.parse

  return merge if args.merge?

  ensure_relocation_formulae_installed! unless args.skip_relocation?
  args.resolved_formulae.each do |f|
    bottle_formula f
  end
end

.bottle_argsObject



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'brew/Library/Homebrew/dev-cmd/bottle.rb', line 42

def bottle_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `bottle` [<options>] <formula>

      Generate a bottle (binary package) from a formula that was installed with
      `--build-bottle`.
      If the formula specifies a rebuild version, it will be incremented in the
      generated DSL. Passing `--keep-old` will attempt to keep it at its original
      value, while `--no-rebuild` will remove it.
    EOS
    switch "--skip-relocation",
           description: "Do not check if the bottle can be marked as relocatable."
    switch "--force-core-tap",
           description: "Build a bottle even if <formula> is not in `homebrew/core` or any installed taps."
    switch "--no-rebuild",
           description: "If the formula specifies a rebuild version, remove it from the generated DSL."
    switch "--keep-old",
           description: "If the formula specifies a rebuild version, attempt to preserve its value in the "\
                        "generated DSL."
    switch "--json",
           description: "Write bottle information to a JSON file, which can be used as the value for "\
                        "`--merge`."
    switch "--merge",
           description: "Generate an updated bottle block for a formula and optionally merge it into the "\
                        "formula file. Instead of a formula name, requires the path to a JSON file generated with "\
                        "`brew bottle --json` <formula>."
    switch "--write",
           depends_on:  "--merge",
           description: "Write changes to the formula file. A new commit will be generated unless "\
                        "`--no-commit` is passed."
    switch "--no-commit",
           depends_on:  "--write",
           description: "When passed with `--write`, a new commit will not generated after writing changes "\
                        "to the formula file."
    flag   "--root-url=",
           description: "Use the specified <URL> as the root of the bottle's URL instead of Homebrew's default."
    switch :verbose
    switch :debug
    conflicts "--no-rebuild", "--keep-old"
    min_named 1
  end
end

.bottle_formula(f) ⇒ Object



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'brew/Library/Homebrew/dev-cmd/bottle.rb', line 214

def bottle_formula(f)
  return ofail "Formula not installed or up-to-date: #{f.full_name}" unless f.latest_version_installed?

  unless tap = f.tap
    return ofail "Formula not from core or any installed taps: #{f.full_name}" unless args.force_core_tap?

    tap = CoreTap.instance
  end

  if f.bottle_disabled?
    ofail "Formula has disabled bottle: #{f.full_name}"
    puts f.bottle_disable_reason
    return
  end

  return ofail "Formula was not installed with --build-bottle: #{f.full_name}" unless Utils::Bottles.built_as? f

  return ofail "Formula has no stable version: #{f.full_name}" unless f.stable

  if args.no_rebuild? || !f.tap
    rebuild = 0
  elsif args.keep_old?
    rebuild = f.bottle_specification.rebuild
  else
    ohai "Determining #{f.full_name} bottle rebuild..."
    versions = FormulaVersions.new(f)
    rebuilds = versions.bottle_version_map("origin/master")[f.pkg_version]
    rebuilds.pop if rebuilds.last.to_i.positive?
    rebuild = rebuilds.empty? ? 0 : rebuilds.max.to_i + 1
  end

  filename = Bottle::Filename.create(f, Utils::Bottles.tag, rebuild)
  bottle_path = Pathname.pwd/filename

  tar_filename = filename.to_s.sub(/.gz$/, "")
  tar_path = Pathname.pwd/tar_filename

  prefix = HOMEBREW_PREFIX.to_s
  repository = HOMEBREW_REPOSITORY.to_s
  cellar = HOMEBREW_CELLAR.to_s

  ohai "Bottling #{filename}..."

  formula_and_runtime_deps_names = [f.name] + f.runtime_dependencies.map(&:name)
  keg = Keg.new(f.prefix)
  relocatable = false
  skip_relocation = false

  keg.lock do
    original_tab = nil
    changed_files = nil

    begin
      keg.delete_pyc_files!

      changed_files = keg.replace_locations_with_placeholders unless args.skip_relocation?

      Formula.clear_cache
      Keg.clear_cache
      Tab.clear_cache
      tab = Tab.for_keg(keg)
      original_tab = tab.dup
      tab.poured_from_bottle = false
      tab.HEAD = nil
      tab.time = nil
      tab.changed_files = changed_files
      tab.write

      keg.find do |file|
        if file.symlink?          # Ruby does not support `File.lutime` yet.
          # Shellout using `touch` to change modified time of symlink itself.

          system "/usr/bin/touch", "-h",
                 "-t", tab.source_modified_time.strftime("%Y%m%d%H%M.%S"), file
        else
          file.utime(tab.source_modified_time, tab.source_modified_time)
        end
      end

      cd cellar do
        sudo_purge
        safe_system "tar", "cf", tar_path, "#{f.name}/#{f.pkg_version}"
        sudo_purge
        tar_path.utime(tab.source_modified_time, tab.source_modified_time)
        relocatable_tar_path = "#{f}-bottle.tar"
        mv tar_path, relocatable_tar_path        # Use gzip, faster to compress than bzip2, faster to uncompress than bzip2
        # or an uncompressed tarball (and more bandwidth friendly).

        safe_system "gzip", "-f", relocatable_tar_path
        sudo_purge
        mv "#{relocatable_tar_path}.gz", bottle_path
      end

      ohai "Detecting if #{filename} is relocatable..." if bottle_path.size > 1 * 1024 * 1024

      prefix_check = if Homebrew.default_prefix?(prefix)
        File.join(prefix, "opt")
      else
        prefix
      end

      # Ignore matches to source code, which is not required at run time.
      # These matches may be caused by debugging symbols.
      ignores = [%r{/include/|\.(c|cc|cpp|h|hpp)$}]
      any_go_deps = f.deps.any? do |dep|
        dep.name =~ Version.formula_optionally_versioned_regex(:go)
      end
      if any_go_deps
        go_regex =
          Version.formula_optionally_versioned_regex(:go, full: false)
        ignores << %r{#{Regexp.escape(HOMEBREW_CELLAR)}/#{go_regex}/[\d\.]+/libexec}
      end

      relocatable = true
      if args.skip_relocation?
        skip_relocation = true
      else
        relocatable = false if keg_contain?(prefix_check, keg, ignores, formula_and_runtime_deps_names)
        relocatable = false if keg_contain?(repository, keg, ignores)
        relocatable = false if keg_contain?(cellar, keg, ignores, formula_and_runtime_deps_names)
        if prefix != prefix_check
          relocatable = false if keg_contain_absolute_symlink_starting_with?(prefix, keg)
          relocatable = false if keg_contain?("#{prefix}/etc", keg, ignores)
          relocatable = false if keg_contain?("#{prefix}/var", keg, ignores)
          relocatable = false if keg_contain?("#{prefix}/share/vim", keg, ignores)
        end
        skip_relocation = relocatable && !keg.require_relocation?
      end
      puts if !relocatable && args.verbose?
    rescue Interrupt
      ignore_interrupts { bottle_path.unlink if bottle_path.exist? }
      raise
    ensure
      ignore_interrupts do
        original_tab&.write
        keg.replace_placeholders_with_locations changed_files unless args.skip_relocation?
      end
    end
  end

  root_url = args.root_url

  bottle = BottleSpecification.new
  bottle.tap = tap
  bottle.root_url(root_url) if root_url
  if relocatable
    if skip_relocation
      bottle.cellar :any_skip_relocation
    else
      bottle.cellar :any
    end
  else
    bottle.cellar cellar
    bottle.prefix prefix
  end
  bottle.rebuild rebuild
  sha256 = bottle_path.sha256
  bottle.sha256 sha256 => Utils::Bottles.tag

  old_spec = f.bottle_specification
  if args.keep_old? && !old_spec.checksums.empty?
    mismatches = [:root_url, :prefix, :cellar, :rebuild].reject do |key|
      old_spec.send(key) == bottle.send(key)
    end
    if (old_spec.cellar == :any && bottle.cellar == :any_skip_relocation) ||
       (old_spec.cellar == cellar &&
        [:any, :any_skip_relocation].include?(bottle.cellar))
      mismatches.delete(:cellar)
      bottle.cellar old_spec.cellar
    end
    unless mismatches.empty?
      bottle_path.unlink if bottle_path.exist?

      mismatches.map! do |key|
        old_value = old_spec.send(key).inspect
        value = bottle.send(key).inspect
        "#{key}: old: #{old_value}, new: #{value}"
      end

      odie <<~EOS
        --keep-old was passed but there are changes in:
        #{mismatches.join("\n")}
      EOS
    end
  end

  output = bottle_output bottle

  puts "./#{filename}"
  puts output

  return unless args.json?

  json = {
    f.full_name => {
      "formula" => {
        "pkg_version" => f.pkg_version.to_s,
        "path"        => f.path.to_s.delete_prefix("#{HOMEBREW_REPOSITORY}/"),
      },
      "bottle"  => {
        "root_url" => bottle.root_url,
        "prefix"   => bottle.prefix,
        "cellar"   => bottle.cellar.to_s,
        "rebuild"  => bottle.rebuild,
        "tags"     => {
          Utils::Bottles.tag.to_s => {
            "filename"       => filename.bintray,
            "local_filename" => filename.to_s,
            "sha256"         => sha256,
          },
        },
      },
      "bintray" => {
        "package"    => Utils::Bottles::Bintray.package(f.name),
        "repository" => Utils::Bottles::Bintray.repository(tap),
      },
    },
  }
  File.open(filename.json, "w") do |file|
    file.write JSON.generate json
  end
end

.bottle_output(bottle) ⇒ Object



203
204
205
206
# File 'brew/Library/Homebrew/dev-cmd/bottle.rb', line 203

def bottle_output(bottle)
  erb = ERB.new BOTTLE_ERB
  erb.result(bottle.instance_eval { binding }).gsub(/^\s*$\n/, "")
end

.brief_build_info(f) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
# File 'brew/Library/Homebrew/cmd/gist-logs.rb', line 78

def brief_build_info(f)
  build_time_str = f.logs.ctime.strftime("%Y-%m-%d %H:%M:%S")
  s = +<<~EOS
    Homebrew build logs for #{f.full_name} on #{OS_VERSION}
  EOS
  if args.with_hostname?
    hostname = Socket.gethostname
    s << "Host: #{hostname}\n"
  end
  s << "Build date: #{build_time_str}\n"
  s.freeze
end

.build_env_keys(env) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'brew/Library/Homebrew/build_environment.rb', line 37

def build_env_keys(env)
  %w[
    CC CXX LD OBJC OBJCXX
    HOMEBREW_CC HOMEBREW_CXX
    CFLAGS CXXFLAGS CPPFLAGS LDFLAGS SDKROOT MAKEFLAGS
    CMAKE_PREFIX_PATH CMAKE_INCLUDE_PATH CMAKE_LIBRARY_PATH CMAKE_FRAMEWORK_PATH
    MACOSX_DEPLOYMENT_TARGET PKG_CONFIG_PATH PKG_CONFIG_LIBDIR
    HOMEBREW_DEBUG HOMEBREW_MAKE_JOBS HOMEBREW_VERBOSE
    HOMEBREW_SVN HOMEBREW_GIT
    HOMEBREW_SDKROOT
    MAKE GIT CPP
    ACLOCAL_PATH PATH CPATH
    LD_LIBRARY_PATH LD_RUN_PATH LD_PRELOAD LIBRARY_PATH
  ].select { |key| env.key?(key) }
end

.build_man_pageObject



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'brew/Library/Homebrew/dev-cmd/man.rb', line 58

def build_man_page
  template = (SOURCE_PATH/"brew.1.md.erb").read
  variables = OpenStruct.new

  variables[:commands] = generate_cmd_manpages(Commands.internal_commands_paths)
  variables[:developer_commands] = generate_cmd_manpages(Commands.internal_developer_commands_paths)
  variables[:official_external_commands] = generate_cmd_manpages(Commands.official_external_commands_paths)
  variables[:global_options] = global_options_manpage

  readme = HOMEBREW_REPOSITORY/"README.md"
  variables[:lead] =
    readme.read[/(Homebrew's \[Project Leader.*\.)/, 1]
          .gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
  variables[:plc] =
    readme.read[/(Homebrew's \[Project Leadership Committee.*\.)/, 1]
          .gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
  variables[:tsc] =
    readme.read[/(Homebrew's \[Technical Steering Committee.*\.)/, 1]
          .gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
  variables[:linux] =
    readme.read[%r{(Homebrew/brew's Linux maintainers .*\.)}, 1]
          .gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
  variables[:maintainers] =
    readme.read[/(Homebrew's other current maintainers .*\.)/, 1]
          .gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
  variables[:alumni] =
    readme.read[/(Former maintainers .*\.)/, 1]
          .gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')

  ERB.new(template, trim_mode: ">").result(variables.instance_eval { binding })
end

.bump_formula_prObject



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'brew/Library/Homebrew/dev-cmd/bump-formula-pr.rb', line 109

def bump_formula_pr
  bump_formula_pr_args.parse

  # As this command is simplifying user run commands then let's just use a
  # user path, too.
  ENV["PATH"] = ENV["HOMEBREW_PATH"]

  # Use the user's browser, too.
  ENV["BROWSER"] = ENV["HOMEBREW_BROWSER"]

  formula = args.formulae.first

  if formula
    tap_full_name, origin_branch, previous_branch = use_correct_linux_tap(formula)
    check_for_duplicate_pull_requests(formula, tap_full_name)
    checked_for_duplicates = true
  end

  new_url = args.url
  if new_url && !formula    # Split the new URL on / and find any formulae that have the same URL
    # except for the last component, but don't try to match any more than the
    # first five components since sometimes the last component isn't the only
    # one to change.

    new_url_split = new_url.split("/")
    maximum_url_components_to_match = 5
    components_to_match = [new_url_split.count - 1, maximum_url_components_to_match].min
    base_url = new_url_split.first(components_to_match).join("/")
    base_url = /#{Regexp.escape(base_url)}/
    is_devel = args.devel?
    guesses = []
    Formula.each do |f|
      if is_devel && f.devel && f.devel.url && f.devel.url.match(base_url)
        guesses << f
      elsif f.stable&.url && f.stable.url.match(base_url)
        guesses << f
      end
    end
    if guesses.count == 1
      formula = guesses.shift
    elsif guesses.count > 1
      odie "Couldn't guess formula for sure; could be one of these:\n#{guesses.map(&:name).join(", ")}"
    end
  end
  raise FormulaUnspecifiedError unless formula

  check_for_duplicate_pull_requests(formula, tap_full_name) unless checked_for_duplicates

  requested_spec, formula_spec = if args.devel?
    devel_message = " (devel)"
    [:devel, formula.devel]
  else
    [:stable, formula.stable]
  end
  odie "#{formula}: no #{requested_spec} specification found!" unless formula_spec

  hash_type, old_hash = if (checksum = formula_spec.checksum)
    [checksum.hash_type, checksum.hexdigest]
  end

  new_hash = args[hash_type] if hash_type
  new_tag = args.tag
  new_revision = args.revision
  new_mirrors ||= args.mirror
  new_mirror ||= case new_url
  when requested_spec != :devel && %r{.*ftp.gnu.org/gnu.*}
    new_url.sub "ftp.gnu.org/gnu", "ftpmirror.gnu.org"
  when %r{.*download.savannah.gnu.org/*}
    new_url.sub "download.savannah.gnu.org", "download-mirror.savannah.gnu.org"
  when %r{.*www.apache.org/dyn/closer.lua\?path=.*}
    new_url.sub "www.apache.org/dyn/closer.lua?path=", "archive.apache.org/dist/"
  when %r{.*mirrors.ocf.berkeley.edu/debian.*}
    new_url.sub "mirrors.ocf.berkeley.edu/debian", "mirrorservice.org/sites/ftp.debian.org/debian"
  end
  new_mirrors ||= [new_mirror] unless new_mirror.nil?
  forced_version = args.version
  new_url_hash = if new_url && new_hash
    true
  elsif new_tag && new_revision
    false
  elsif !hash_type
    odie "#{formula}: no --tag=/--revision= arguments specified!"
  elsif !new_url
    odie "#{formula}: no --url= argument specified!"
  else
    resource = Resource.new { @url = new_url }
    resource.download_strategy = DownloadStrategyDetector.detect_from_url(new_url)
    resource.owner = Resource.new(formula.name)
    if forced_version
      if forced_version == resource.version
        forced_version = nil
      else
        resource.version = forced_version
      end
    end
    odie "No --version= argument specified!" unless resource.version
    resource_path = resource.fetch
    tar_file_extensions = %w[.tar .tb2 .tbz .tbz2 .tgz .tlz .txz .tZ]
    if tar_file_extensions.any? { |extension| new_url.include? extension }
      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", resource_path).match?(%r{/.*\.})
        new_hash = resource_path.sha256
      else
        odie "#{resource_path} is not a valid tar file!"
      end
    else
      new_hash = resource_path.sha256
    end
  end

  old_formula_version = formula_version(formula, requested_spec)

  replacement_pairs = []
  if requested_spec == :stable && formula.revision.nonzero?
    replacement_pairs << [
      /^  revision \d+\n(\n(  head "))?/m,
      "\\2",
    ]
  end

  replacement_pairs += formula_spec.mirrors.map do |mirror|
    [
      / +mirror \"#{Regexp.escape(mirror)}\"\n/m,
      "",
    ]
  end

  replacement_pairs += if new_url_hash
    [
      [
        /#{Regexp.escape(formula_spec.url)}/,
        new_url,
      ],
      [
        old_hash,
        new_hash,
      ],
    ]
  else
    [
      [
        formula_spec.specs[:tag],
        new_tag,
      ],
      [
        formula_spec.specs[:revision],
        new_revision,
      ],
    ]
  end

  backup_file = File.read(formula.path) unless args.dry_run?

  if new_mirrors
    replacement_pairs << [
      /^( +)(url \"#{Regexp.escape(new_url)}\"\n)/m,
      "\\1\\2\\1mirror \"#{new_mirrors.join("\"\n\\1mirror \"")}\"\n",
    ]
  end

  # When bumping a linux-only formula, one needs to also delete the
  # sha256 linux bottle line if it exists. That's because of running
  # test-bot with --keep-old option in linuxbrew-core.
  formula_contents = formula.path.read
  if formula_contents.include?("depends_on :linux") && formula_contents.include?("=> :x86_64_linux")
    replacement_pairs << [
      /^    sha256 ".+" => :x86_64_linux\n/m,
      "\\2",
    ]
  end

  if forced_version && forced_version != "0"
    if requested_spec == :stable
      replacement_pairs << if File.read(formula.path).include?("version \"#{old_formula_version}\"")
        [
          old_formula_version.to_s,
          forced_version,
        ]
      elsif new_mirrors
        [
          /^( +)(mirror \"#{Regexp.escape(new_mirrors.last)}\"\n)/m,
          "\\1\\2\\1version \"#{forced_version}\"\n",
        ]
      else
        [
          /^( +)(url \"#{Regexp.escape(new_url)}\"\n)/m,
          "\\1\\2\\1version \"#{forced_version}\"\n",
        ]
      end
    elsif requested_spec == :devel
      replacement_pairs << [
        /(  devel do.+?version \")#{old_formula_version}(\"\n.+?end\n)/m,
        "\\1#{forced_version}\\2",
      ]
    end
  elsif forced_version && forced_version == "0"
    if requested_spec == :stable
      replacement_pairs << [
        /^  version \"[\w\.\-\+]+\"\n/m,
        "",
      ]
    elsif requested_spec == :devel
      replacement_pairs << [
        /(  devel do.+?)^ +version \"[^\n]+\"\n(.+?end\n)/m,
        "\\1\\2",
      ]
    end
  end
  new_contents = inreplace_pairs(formula.path, replacement_pairs)

  new_formula_version = formula_version(formula, requested_spec, new_contents)

  if !new_mirrors && !formula_spec.mirrors.empty?
    if args.force?
      opoo "#{formula}: Removing all mirrors because a --mirror= argument was not specified."
    else
      odie <<~EOS
        #{formula}: a --mirror= argument for updating the mirror URL was not specified.
        Use --force to remove all mirrors.
      EOS
    end
  end

  if new_formula_version < old_formula_version
    formula.path.atomic_write(backup_file) unless args.dry_run?
    odie <<~EOS
      You probably need to bump this formula manually since changing the
      version from #{old_formula_version} to #{new_formula_version} would be a downgrade.
    EOS
  elsif new_formula_version == old_formula_version
    formula.path.atomic_write(backup_file) unless args.dry_run?
    odie <<~EOS
      You probably need to bump this formula manually since the new version
      and old version are both #{new_formula_version}.
    EOS
  end

  alias_rename = alias_update_pair(formula, new_formula_version)
  if alias_rename.present?
    ohai "renaming alias #{alias_rename.first} to #{alias_rename.last}"
    alias_rename.map! { |a| formula.tap.alias_dir/a }
  end

  run_audit(formula, alias_rename, backup_file)

  formula.path.parent.cd do
    branch = "#{formula.name}-#{new_formula_version}"
    git_dir = Utils.popen_read("git rev-parse --git-dir").chomp
    shallow = !git_dir.empty? && File.exist?("#{git_dir}/shallow")
    changed_files = [formula.path]
    changed_files += alias_rename if alias_rename.present?

    if args.dry_run?
      ohai "try to fork repository with GitHub API" unless args.no_fork?
      ohai "git fetch --unshallow origin" if shallow
      ohai "git add #{alias_rename.first} #{alias_rename.last}" if alias_rename.present?
      ohai "git checkout --no-track -b #{branch} #{origin_branch}"
      ohai "git commit --no-edit --verbose --message='#{formula.name} " \
           "#{new_formula_version}#{devel_message}' -- #{changed_files.join(" ")}"
      ohai "git push --set-upstream $HUB_REMOTE #{branch}:#{branch}"
      ohai "git checkout --quiet #{previous_branch}"
      ohai "create pull request with GitHub API"
    else

      if args.no_fork?
        remote_url = Utils.popen_read("git remote get-url --push origin").chomp
        username = formula.tap.user
      else
        remote_url, username = forked_repo_info(formula, tap_full_name, backup_file)
      end

      safe_system "git", "fetch", "--unshallow", "origin" if shallow
      safe_system "git", "add", *alias_rename if alias_rename.present?
      safe_system "git", "checkout", "--no-track", "-b", branch, origin_branch
      safe_system "git", "commit", "--no-edit", "--verbose",
                  "--message=#{formula.name} #{new_formula_version}#{devel_message}",
                  "--", *changed_files
      safe_system "git", "push", "--set-upstream", remote_url, "#{branch}:#{branch}"
      safe_system "git", "checkout", "--quiet", previous_branch
      pr_message = <<~EOS
        Created with `brew bump-formula-pr`.
      EOS
      user_message = args.message
      if user_message
        pr_message += "\n" + <<~EOS
          ---

          #{user_message}
        EOS
      end
      pr_title = "#{formula.name} #{new_formula_version}#{devel_message}"

      begin
        url = GitHub.create_pull_request(tap_full_name, pr_title,
                                         "#{username}:#{branch}", "master", pr_message)["html_url"]
        if args.no_browse?
          puts url
        else
          exec_browser url
        end
      rescue *GitHub.api_errors => e
        odie "Unable to open pull request: #{e.message}!"
      end
    end
  end
end

.bump_formula_pr_argsObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'brew/Library/Homebrew/dev-cmd/bump-formula-pr.rb', line 9

def bump_formula_pr_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `bump-formula-pr` [<options>] [<formula>]

      Create a pull request to update <formula> with a new URL or a new tag.

      If a <URL> is specified, the <SHA-256> checksum of the new download should also
      be specified. A best effort to determine the <SHA-256> and <formula> name will
      be made if either or both values are not supplied by the user.

      If a <tag> is specified, the Git commit <revision> corresponding to that tag
      must also be specified.

      *Note:* this command cannot be used to transition a formula from a
      URL-and-SHA-256 style specification into a tag-and-revision style specification,
      nor vice versa. It must use whichever style specification the formula already uses.
    EOS
    switch "--devel",
           description: "Bump the development rather than stable version. The development spec must already exist."
    switch "-n", "--dry-run",
           description: "Print what would be done rather than doing it."
    switch "--write",
           depends_on:  "--dry-run",
           description: "When passed along with `--dry-run`, perform a not-so-dry run by making the expected "\
                        "file modifications but not taking any Git actions."
    switch "--no-audit",
           description: "Don't run `brew audit` before opening the PR."
    switch "--strict",
           description: "Run `brew audit --strict` before opening the PR."
    switch "--no-browse",
           description: "Print the pull request URL instead of opening in a browser."
    switch "--no-fork",
           description: "Don't try to fork the repository."
    comma_array "--mirror",
                description: "Use the specified <URL> as a mirror URL. If <URL> is a comma-separated list "\
                             "of URLs, multiple mirrors will be added."
    flag   "--version=",
           description: "Use the specified <version> to override the value parsed from the URL or tag. Note "\
                        "that `--version=0` can be used to delete an existing version override from a "\
                        "formula if it has become redundant."
    flag   "--message=",
           description: "Append <message> to the default pull request message."
    flag   "--url=",
           description: "Specify the <URL> for the new download. If a <URL> is specified, the <SHA-256> "\
                        "checksum of the new download should also be specified."
    flag   "--sha256=",
           depends_on:  "--url=",
           description: "Specify the <SHA-256> checksum of the new download."
    flag   "--tag=",
           description: "Specify the new git commit <tag> for the formula."
    flag   "--revision=",
           required_for: "--tag=",
           description:  "Specify the new git commit <revision> corresponding to the specified <tag>."
    switch :force
    switch :quiet
    switch :verbose
    switch :debug
    conflicts "--no-audit", "--strict"
    conflicts "--url", "--tag"
    max_named 1
  end
end

.bump_revisionObject



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'brew/Library/Homebrew/dev-cmd/bump-revision.rb', line 29

def bump_revision
  bump_revision_args.parse

  # As this command is simplifying user run commands then let's just use a
  # user path, too.
  ENV["PATH"] = ENV["HOMEBREW_PATH"]

  formulae = args.formulae
  raise FormulaUnspecifiedError if formulae.empty?

  formula = formulae.first
  current_revision = formula.revision

  if current_revision.zero?
    formula_spec = formula.stable
    hash_type, old_hash = if (checksum = formula_spec.checksum)
      [checksum.hash_type, checksum.hexdigest]
    end

    old = if hash_type      # insert replacement revision after hash

      <<~EOS
        #{hash_type} "#{old_hash}"
      EOS
    else
      # insert replacement revision after :revision
      <<~EOS
        :revision => "#{formula_spec.specs[:revision]}"
      EOS
    end
    replacement = old + "  revision 1\n"

  else
    old = "revision #{current_revision}"
    replacement = "revision #{current_revision+1}"
  end

  if args.dry_run?
    ohai "replace #{old.inspect} with #{replacement.inspect}" unless args.quiet?
  else
    Utils::Inreplace.inreplace(formula.path) do |s|
      s.gsub!(old, replacement)
    end
  end

  message = "#{formula.name}: revision bump #{args.message}"
  if args.dry_run?
    ohai "git commit --no-edit --verbose --message=#{message} -- #{formula.path}"
  else
    formula.path.parent.cd do
      safe_system "git", "commit", "--no-edit", "--verbose",
                  "--message=#{message}", "--", formula.path
    end
  end
end

.bump_revision_argsObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'brew/Library/Homebrew/dev-cmd/bump-revision.rb', line 9

def bump_revision_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `bump-revision` [<options>] <formula>

      Create a commit to increment the revision of <formula>. If no revision is
      present, "revision 1" will be added.
    EOS
    switch "-n", "--dry-run",
           description: "Print what would be done rather than doing it."
    flag   "--message=",
           description: "Append <message> to the default commit message."
    switch :force
    switch :quiet
    switch :verbose
    switch :debug
    max_named 1
  end
end

.caskObject



8
9
10
# File 'brew/Library/Homebrew/cmd/cask.rb', line 8

def cask
  Cask::Cmd.run(*ARGV)
end

.catObject



19
20
21
22
23
24
25
26
27
28
29
# File 'brew/Library/Homebrew/cmd/cat.rb', line 19

def cat
  cat_args.parse

  cd HOMEBREW_REPOSITORY
  pager = if ENV["HOMEBREW_BAT"].nil?
    "cat"
  else
    "#{HOMEBREW_PREFIX}/bin/bat"
  end
  safe_system pager, args.formulae.first.path, *args.passthrough
end

.cat_argsObject



8
9
10
11
12
13
14
15
16
17
# File 'brew/Library/Homebrew/cmd/cat.rb', line 8

def cat_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `cat` <formula>

      Display the source of <formula>.
    EOS
    named :formula
  end
end

.check_bintray_mirror(name, url) ⇒ Object



462
463
464
465
466
467
468
469
# File 'brew/Library/Homebrew/dev-cmd/pull.rb', line 462

def check_bintray_mirror(name, url)
  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})."
  opoo "Do you need to upload it with `brew mirror #{name}`?"
end

.check_branch(path, ref) ⇒ Object



109
110
111
112
113
114
115
# File 'brew/Library/Homebrew/dev-cmd/pr-pull.rb', line 109

def check_branch(path, ref)
  branch = Utils.popen_read("git", "-C", path, "symbolic-ref", "--short", "HEAD").strip

  return if branch == ref || args.clean? || args.branch_okay?

  opoo "Current branch is #{branch}: do you need to pull inside #{ref}?"
end

.check_bumps(patch_changes) ⇒ Object



273
274
275
276
277
278
279
280
281
# File 'brew/Library/Homebrew/dev-cmd/pull.rb', line 273

def check_bumps(patch_changes)
  if patch_changes[:formulae].empty?
    odie "No changed formulae found to bump"
  elsif patch_changes[:formulae].length > 1
    odie "Can only bump one changed formula; bumped #{patch_changes[:formulae]}"
  elsif !patch_changes[:others].empty?
    odie "Cannot bump if non-formula files are changed"
  end
end

.check_dependents(formulae_to_install) ⇒ Object



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'brew/Library/Homebrew/cmd/upgrade.rb', line 234

def check_dependents(formulae_to_install)
  return if formulae_to_install.empty?

  oh1 "Checking for dependents of upgraded formulae..." unless args.dry_run?
  outdated_dependents =
    formulae_to_install.flat_map(&:runtime_installed_formula_dependents)
                       .select(&:outdated?)
  if outdated_dependents.blank?
    ohai "No dependents found!" unless args.dry_run?
    return
  end
  outdated_dependents -= formulae_to_install if args.dry_run?

  upgradeable_dependents =
    outdated_dependents.reject(&:pinned?)
                       .sort { |a, b| depends_on(a, b) }
  pinned_dependents =
    outdated_dependents.select(&:pinned?)
                       .sort { |a, b| depends_on(a, b) }

  if pinned_dependents.present?
    plural = "dependent".pluralize(pinned_dependents.count)
    ohai "Not upgrading #{pinned_dependents.count} pinned #{plural}:"
    puts(pinned_dependents.map do |f|
      "#{f.full_specified_name} #{f.pkg_version}"
    end.join(", "))
  end

  # Print the upgradable dependents.
  if upgradeable_dependents.blank?
    ohai "No outdated dependents to upgrade!" unless args.dry_run?
  else
    plural = "dependent".pluralize(upgradeable_dependents.count)
    verb = args.dry_run? ? "Would upgrade" : "Upgrading"
    ohai "#{verb} #{upgradeable_dependents.count} #{plural}:"
    formulae_upgrades = upgradeable_dependents.map do |f|
      name = f.full_specified_name
      if f.optlinked?
        "#{name} #{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
      else
        "#{name} #{f.pkg_version}"
      end
    end
    puts formulae_upgrades.join(", ")
  end

  upgrade_formulae(upgradeable_dependents)

  # Assess the dependents tree again now we've upgraded.
  oh1 "Checking for dependents of upgraded formulae..." unless args.dry_run?
  broken_dependents = CacheStoreDatabase.use(:linkage) do |db|
    formulae_to_install.flat_map(&:runtime_installed_formula_dependents)
                       .select do |f|
      keg = f.opt_or_installed_prefix_keg
      next unless keg

      LinkageChecker.new(keg, cache_db: db)
                    .broken_library_linkage?
    end.compact
  end
  if broken_dependents.blank?
    if args.dry_run?
      ohai "No currently broken dependents found!"
      opoo "If they are broken by the upgrade they will also be upgraded or reinstalled."
    else
      ohai "No broken dependents found!"
    end
    return
  end

  reinstallable_broken_dependents =
    broken_dependents.reject(&:outdated?)
                     .reject(&:pinned?)
                     .sort { |a, b| depends_on(a, b) }
  outdated_pinned_broken_dependents =
    broken_dependents.select(&:outdated?)
                     .select(&:pinned?)
                     .sort { |a, b| depends_on(a, b) }

  # Print the pinned dependents.
  if outdated_pinned_broken_dependents.present?
    count = outdated_pinned_broken_dependents.count
    plural = "dependent".pluralize(outdated_pinned_broken_dependents.count)
    onoe "Not reinstalling #{count} broken and outdated, but pinned #{plural}:"
    $stderr.puts(outdated_pinned_broken_dependents.map do |f|
      "#{f.full_specified_name} #{f.pkg_version}"
    end.join(", "))
  end

  # Print the broken dependents.
  if reinstallable_broken_dependents.blank?
    ohai "No broken dependents to reinstall!"
  else
    count = reinstallable_broken_dependents.count
    plural = "dependent".pluralize(reinstallable_broken_dependents.count)
    ohai "Reinstalling #{count} broken #{plural} from source:"
    puts reinstallable_broken_dependents.map(&:full_specified_name)
                                        .join(", ")
  end

  return if args.dry_run?

  reinstallable_broken_dependents.each do |f|
    reinstall_formula(f, build_from_source: true)
  rescue FormulaInstallationAlreadyAttemptedError    # We already attempted to reinstall f as part of the dependency tree of
    # another formula. In that case, don't generate an error, just move on.

    nil
  rescue CannotInstallFormulaError => e
    ofail e
  rescue BuildError => e
    e.dump
    puts
    Homebrew.failed = true
  rescue DownloadError => e
    ofail e
  end
end

.check_for_dependents(kegs) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
# File 'brew/Library/Homebrew/cmd/uninstall.rb', line 110

def check_for_dependents(kegs)
  return false unless result = Keg.find_some_installed_dependents(kegs)

  if ARGV.homebrew_developer?
    DeveloperDependentsMessage.new(*result).output
  else
    NondeveloperDependentsMessage.new(*result).output
  end

  true
end

.check_for_duplicate_pull_requests(formula, tap_full_name) ⇒ Object



484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# File 'brew/Library/Homebrew/dev-cmd/bump-formula-pr.rb', line 484

def check_for_duplicate_pull_requests(formula, tap_full_name)
  pull_requests = fetch_pull_requests(formula, tap_full_name)
  return unless pull_requests
  return if pull_requests.empty?

  duplicates_message = <<~EOS
    These open pull requests may be duplicates:
    #{pull_requests.map { |pr| "#{pr["title"]} #{pr["html_url"]}" }.join("\n")}
  EOS
  error_message = "Duplicate PRs should not be opened. Use --force to override this error."
  if args.force? && !args.quiet?
    opoo duplicates_message
  elsif !args.force? && args.quiet?
    odie error_message
  elsif !args.force?
    odie <<~EOS
      #{duplicates_message.chomp}
      #{error_message}
    EOS
  end
end

.cherry_pick_pr!(pr, path: ".", dry_run: false) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'brew/Library/Homebrew/dev-cmd/pr-pull.rb', line 81

def cherry_pick_pr!(pr, path: ".", dry_run: false)
  if dry_run
    puts <<~EOS
      git fetch --force origin +refs/pull/#{pr}/head
      git merge-base HEAD FETCH_HEAD
      git cherry-pick --ff --allow-empty $merge_base..FETCH_HEAD
    EOS
  else
    safe_system "git", "-C", path, "fetch", "--quiet", "--force", "origin", "+refs/pull/#{pr}/head"
    merge_base = Utils.popen_read("git", "-C", path, "merge-base", "HEAD", "FETCH_HEAD").strip
    commit_count = Utils.popen_read("git", "-C", path, "rev-list", "#{merge_base}..FETCH_HEAD").lines.count

    # git cherry-pick unfortunately has no quiet option
    ohai "Cherry-picking #{commit_count} commit#{"s" unless commit_count == 1} from ##{pr}"
    cherry_pick_args = "git", "-C", path, "cherry-pick", "--ff", "--allow-empty", "#{merge_base}..FETCH_HEAD"
    result = Homebrew.args.verbose? ? system(*cherry_pick_args) : quiet_system(*cherry_pick_args)

    unless result
      if Homebrew.args.resolve?
        odie "Cherry-pick failed: try to resolve it."
      else
        system "git", "-C", path, "cherry-pick", "--abort"
        odie "Cherry-pick failed!"
      end
    end
  end
end

.cleanupObject



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'brew/Library/Homebrew/cmd/cleanup.rb', line 33

def cleanup
  cleanup_args.parse

  cleanup = Cleanup.new(*args.named, dry_run: args.dry_run?, scrub: args.s?, days: args.prune&.to_i)
  if args.prune_prefix?
    cleanup.prune_prefix_symlinks_and_directories
    return
  end

  cleanup.clean!

  unless cleanup.disk_cleanup_size.zero?
    disk_space = disk_usage_readable(cleanup.disk_cleanup_size)
    if args.dry_run?
      ohai "This operation would free approximately #{disk_space} of disk space."
    else
      ohai "This operation has freed approximately #{disk_space} of disk space."
    end
  end

  return if cleanup.unremovable_kegs.empty?

  ofail <<~EOS
    Could not cleanup old kegs! Fix your permissions on:
      #{cleanup.unremovable_kegs.join "\n  "}
  EOS
end

.cleanup_argsObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'brew/Library/Homebrew/cmd/cleanup.rb', line 9

def cleanup_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `cleanup` [<options>] [<formula>|<cask>]

      Remove stale lock files and outdated downloads for all formulae and casks,
      and remove old versions of installed formulae. If arguments are specified,
      only do this for the given formulae and casks.
    EOS
    flag   "--prune=",
           description: "Remove all cache files older than specified <days>."
    switch "-n", "--dry-run",
           description: "Show what would be removed, but do not actually remove anything."
    switch "-s",
           description: "Scrub the cache, including downloads for even the latest versions. "\
                        "Note downloads for any installed formulae or casks will still not be deleted. "\
                        "If you want to delete those too: `rm -rf \"$(brew --cache)\"`"
    switch "--prune-prefix",
           description: "Only prune the symlinks and directories from the prefix and remove no other files."
    switch :verbose
    switch :debug
  end
end

.cmd_comment_manpage_lines(cmd_path) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'brew/Library/Homebrew/dev-cmd/man.rb', line 176

def cmd_comment_manpage_lines(cmd_path)
  comment_lines = cmd_path.read.lines.grep(/^#:/)
  return if comment_lines.empty?
  return if comment_lines.first.include?("@hide_from_man_page")

  lines = [format_usage_banner(comment_lines.first).chomp]
  comment_lines.slice(1..-1)
               .each do |line|
    line = line.slice(4..-2)
    unless line
      lines.last << "\n"
      next
    end

    # Omit the common global_options documented separately in the man page.
    next if line.match?(/--(debug|force|help|quiet|verbose) /)

    # Format one option or a comma-separated pair of short and long options.
    lines << line.gsub(/^ +(-+[a-z-]+), (-+[a-z-]+) +/, "* `\\1`, `\\2`:\n  ")
                 .gsub(/^ +(-+[a-z-]+) +/, "* `\\1`:\n  ")
  end
  lines.last << "\n"
  lines
end

.cmd_parser_manpage_lines(cmd_parser) ⇒ Object



166
167
168
169
170
171
172
173
174
# File 'brew/Library/Homebrew/dev-cmd/man.rb', line 166

def cmd_parser_manpage_lines(cmd_parser)
  lines = [format_usage_banner(cmd_parser.usage_banner_text)]
  lines += cmd_parser.processed_options.map do |short, long, _, desc|
    next if !long.nil? && cmd_parser.global_option?(cmd_parser.option_to_name(long), desc)

    generate_option_doc(short, long, desc)
  end.reject(&:blank?)
  lines
end

.commandObject



22
23
24
25
26
27
28
29
30
# File 'brew/Library/Homebrew/cmd/command.rb', line 22

def command
  command_args.parse

  args.named.each do |cmd|
    path = Commands.path(cmd)
    odie "Unknown command: #{cmd}" unless path
    puts path
  end
end

.command_argsObject



9
10
11
12
13
14
15
16
17
18
19
20
# File 'brew/Library/Homebrew/cmd/command.rb', line 9

def command_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `command` <cmd>

      Display the path to the file being used when invoking `brew` <cmd>.
    EOS
    switch :verbose
    switch :debug
    min_named 1
  end
end

.commandsObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'brew/Library/Homebrew/cmd/commands.rb', line 26

def commands
  commands_args.parse

  if args.quiet?
    puts Formatter.columns(Commands.commands(aliases: args.include_aliases?))
    return
  end

  ohai "Built-in commands", Formatter.columns(Commands.internal_commands)
  puts
  ohai "Built-in developer commands", Formatter.columns(Commands.internal_developer_commands)

  external_commands = Commands.external_commands
  return if external_commands.blank?

  puts
  ohai "External commands", Formatter.columns(external_commands)
end

.commands_argsObject



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'brew/Library/Homebrew/cmd/commands.rb', line 8

def commands_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `commands` [<options>]

      Show lists of built-in and external commands.
    EOS
    switch :quiet,
           description: "List only the names of commands without category headers."
    switch "--include-aliases",
           depends_on:  "--quiet",
           description: "Include aliases of internal commands."
    switch :verbose
    switch :debug
    max_named 0
  end
end

.condense_requirements(deps) ⇒ Object



106
107
108
109
110
# File 'brew/Library/Homebrew/cmd/deps.rb', line 106

def condense_requirements(deps)
  return deps if args.include_requirements?

  deps.select { |dep| dep.is_a? Dependency }
end

.configObject



23
24
25
26
27
# File 'brew/Library/Homebrew/cmd/config.rb', line 23

def config
  config_args.parse

  SystemConfig.dump_verbose_config
end

.config_argsObject



9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'brew/Library/Homebrew/cmd/config.rb', line 9

def config_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `config`

      Show Homebrew and system configuration info useful for debugging. If you file
      a bug report, you will be required to provide this information.
    EOS
    switch :verbose
    switch :debug
    max_named 0
  end
end

.convert_man_page(markup, target) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'brew/Library/Homebrew/dev-cmd/man.rb', line 95

def convert_man_page(markup, target)
  manual = target.basename(".1")
  organisation = "Homebrew"

  # Set the manpage date to the existing one if we're checking for changes.
  # This avoids the only change being e.g. a new date.
  date = if args.fail_if_changed? &&
            target.extname == ".1" && target.exist?
    /"(\d{1,2})" "([A-Z][a-z]+) (\d{4})" "#{organisation}" "#{manual}"/ =~ target.read
    Date.parse("#{Regexp.last_match(1)} #{Regexp.last_match(2)} #{Regexp.last_match(3)}")
  else
    Date.today
  end
  date = date.strftime("%Y-%m-%d")

  shared_args = %W[
    --pipe
    --organization=#{organisation}
    --manual=#{target.basename(".1")}
    --date=#{date}
  ]

  format_flag, format_desc = target_path_to_format(target)

  puts "Writing #{format_desc} to #{target}"
  Utils.popen(["ronn", format_flag] + shared_args, "rb+") do |ronn|
    ronn.write markup
    ronn.close_write
    ronn_output = ronn.read
    odie "Got no output from ronn!" if ronn_output.blank?
    if format_flag == "--markdown"
      ronn_output = ronn_output.gsub(%r{<var>(.*?)</var>}, "*`\\1`*")
                               .gsub(/\n\n\n+/, "\n\n")
    elsif format_flag == "--roff"
      ronn_output = ronn_output.gsub(%r{<code>(.*?)</code>}, "\\fB\\1\\fR")
                               .gsub(%r{<var>(.*?)</var>}, "\\fI\\1\\fR")
                               .gsub(/(^\[?\\fB.+): /, "\\1\n    ")
    end
    target.atomic_write ronn_output
  end
end

.createObject

Create a formula from a tarball URL



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'brew/Library/Homebrew/dev-cmd/create.rb', line 59

def create
  create_args.parse

  # Ensure that the cache exists so we can fetch the tarball
  HOMEBREW_CACHE.mkpath

  url = args.named.first # Pull the first (and only) url from ARGV

  version = args.set_version
  name = args.set_name
  tap = args.tap

  fc = FormulaCreator.new
  fc.name = name
  fc.version = version
  fc.tap = Tap.fetch(tap || "homebrew/core")
  raise TapUnavailableError, tap unless fc.tap.installed?

  fc.url = url

  fc.mode = if args.cmake?
    :cmake
  elsif args.autotools?
    :autotools
  elsif args.meson?
    :meson
  elsif args.go?
    :go
  elsif args.perl?
    :perl
  elsif args.python?
    :python
  elsif args.ruby?
    :ruby
  elsif args.rust?
    :rust
  end

  if fc.name.nil? || fc.name.strip.empty?
    stem = Pathname.new(url).stem
    print "Formula name [#{stem}]: "
    fc.name = __gets || stem
    fc.update_path
  end

  # Don't allow blacklisted formula, or names that shadow aliases,
  # unless --force is specified.
  unless args.force?
    if reason = MissingFormula.blacklisted_reason(fc.name)
      raise <<~EOS
        #{fc.name} is blacklisted for creation.
        #{reason}
        If you really want to create this formula use --force.
      EOS
    end

    if Formula.aliases.include? fc.name
      realname = Formulary.canonical_name(fc.name)
      raise <<~EOS
        The formula #{realname} is already aliased to #{fc.name}
        Please check that you are not creating a duplicate.
        To force creation use --force.
      EOS
    end
  end

  fc.generate!

  puts "Please run `brew audit --new-formula #{fc.name}` before submitting, thanks."
  exec_editor fc.path
end

.create_argsObject



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'brew/Library/Homebrew/dev-cmd/create.rb', line 11

def create_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `create` [<options>] <URL>

      Generate a formula for the downloadable file at <URL> and open it in the editor.
      Homebrew will attempt to automatically derive the formula name and version, but
      if it fails, you'll have to make your own template. The `wget` formula serves as
      a simple example. For the complete API, see:
      <http://www.rubydoc.info/github/Homebrew/brew/master/Formula>
    EOS
    switch "--autotools",
           description: "Create a basic template for an Autotools-style build."
    switch "--cmake",
           description: "Create a basic template for a CMake-style build."
    switch "--go",
           description: "Create a basic template for a Go build."
    switch "--meson",
           description: "Create a basic template for a Meson-style build."
    switch "--perl",
           description: "Create a basic template for a Perl build."
    switch "--python",
           description: "Create a basic template for a Python build."
    switch "--ruby",
           description: "Create a basic template for a Ruby build."
    switch "--rust",
           description: "Create a basic template for a Rust build."
    switch "--no-fetch",
           description: "Homebrew will not download <URL> to the cache and will thus not add its SHA-256 "\
                        "to the formula for you, nor will it check the GitHub API for GitHub projects "\
                        "(to fill out its description and homepage)."
    switch "--HEAD",
           description: "Indicate that <URL> points to the package's repository rather than a file."
    flag   "--set-name=",
           description: "Explicitly set the <name> of the new formula."
    flag   "--set-version=",
           description: "Explicitly set the <version> of the new formula."
    flag   "--tap=",
           description: "Generate the new formula within the given tap, specified as <user>`/`<repo>."
    switch :force
    switch :verbose
    switch :debug
    conflicts "--autotools", "--cmake", "--go", "--meson", "--perl", "--python", "--rust"
    named 1
  end
end

.create_gist(files, description) ⇒ Object



128
129
130
131
132
133
# File 'brew/Library/Homebrew/cmd/gist-logs.rb', line 128

def create_gist(files, description)
  url = "https://api.github.com/gists"
  data = { "public" => !create_private?, "files" => files, "description" => description }
  scopes = GitHub::CREATE_GIST_SCOPES
  GitHub.open_api(url, data: data, scopes: scopes)["html_url"]
end

.create_issue(repo, title, body) ⇒ Object



135
136
137
138
139
140
# File 'brew/Library/Homebrew/cmd/gist-logs.rb', line 135

def create_issue(repo, title, body)
  url = "https://api.github.com/repos/#{repo}/issues"
  data = { "title" => title, "body" => body }
  scopes = GitHub::CREATE_ISSUE_FORK_OR_PR_SCOPES
  GitHub.open_api(url, data: data, scopes: scopes)["html_url"]
end

.create_private?Boolean

Returns:

  • (Boolean)


124
125
126
# File 'brew/Library/Homebrew/cmd/gist-logs.rb', line 124

def create_private?
  args.private?
end

.current_versions_from_info_external(formula_name) ⇒ Object

Get current formula versions without loading formula definition in this process. Returns info as a hash (type => version), for pull.rb’s internal use. Uses special key :nonexistent => true for nonexistent formulae.



406
407
408
409
410
411
412
413
414
415
416
417
# File 'brew/Library/Homebrew/dev-cmd/pull.rb', line 406

def current_versions_from_info_external(formula_name)
  info = FormulaInfo.lookup(formula_name)
  versions = {}
  if info
    [:stable, :devel, :head].each do |spec_type|
      versions[spec_type] = info.version(spec_type)
    end
  else
    versions[:nonexistent] = true
  end
  versions
end

.decorate_dependencies(dependencies) ⇒ Object



247
248
249
250
251
252
253
254
255
256
# File 'brew/Library/Homebrew/cmd/info.rb', line 247

def decorate_dependencies(dependencies)
  deps_status = dependencies.map do |dep|
    if dep.satisfied?([])
      pretty_installed(dep_display_s(dep))
    else
      pretty_uninstalled(dep_display_s(dep))
    end
  end
  deps_status.join(", ")
end

.decorate_requirements(requirements) ⇒ Object



258
259
260
261
262
263
264
# File 'brew/Library/Homebrew/cmd/info.rb', line 258

def decorate_requirements(requirements)
  req_status = requirements.map do |req|
    req_s = req.display_s
    req.satisfied? ? pretty_installed(req_s) : pretty_uninstalled(req_s)
  end
  req_status.join(", ")
end

.default_prefix?(prefix = HOMEBREW_PREFIX) ⇒ Boolean

Returns:

  • (Boolean)


77
78
79
# File 'brew/Library/Homebrew/global.rb', line 77

def Homebrew.default_prefix?(prefix = HOMEBREW_PREFIX)
  prefix.to_s == DEFAULT_PREFIX
end

.dep_display_name(dep) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'brew/Library/Homebrew/cmd/deps.rb', line 112

def dep_display_name(dep)
  str = if dep.is_a? Requirement
    if args.include_requirements?
      ":#{dep.display_s}"
    else
      # This shouldn't happen, but we'll put something here to help debugging
      "::#{dep.name}"
    end
  elsif args.full_name?
    dep.to_formula.full_name
  else
    dep.name
  end

  if args.annotate?
    str = "#{str} " if args.tree?
    str = "#{str} [build]" if dep.build?
    str = "#{str} [test]" if dep.test?
    str = "#{str} [optional]" if dep.optional?
    str = "#{str} [recommended]" if dep.recommended?
  end

  str
end

.dep_display_s(dep) ⇒ Object



266
267
268
269
270
# File 'brew/Library/Homebrew/cmd/info.rb', line 266

def dep_display_s(dep)
  return dep.name if dep.option_tags.empty?

  "#{dep.name} #{dep.option_tags.map { |o| "--#{o}" }.join(" ")}"
end

.depsObject



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'brew/Library/Homebrew/cmd/deps.rb', line 59

def deps
  deps_args.parse

  Formulary.enable_factory_cache!

  recursive = !args.send("1?")
  installed = args.installed? || args.formulae.all?(&:opt_or_installed_prefix_keg)

  @use_runtime_dependencies = installed && recursive &&
                              !args.include_build? &&
                              !args.include_test? &&
                              !args.include_optional? &&
                              !args.skip_recommended?

  if args.tree?
    if args.installed?
      puts_deps_tree Formula.installed.sort, recursive
    else
      raise FormulaUnspecifiedError if args.no_named?

      puts_deps_tree args.formulae, recursive
    end
    return
  elsif args.all?
    puts_deps Formula.sort, recursive
    return
  elsif !args.no_named? && args.for_each?
    puts_deps args.formulae, recursive
    return
  end

  if args.no_named?
    raise FormulaUnspecifiedError unless args.installed?

    puts_deps Formula.installed.sort, recursive
    return
  end

  all_deps = deps_for_formulae(args.formulae, recursive, &(args.union? ? :| : :&))
  all_deps = condense_requirements(all_deps)
  all_deps.select!(&:installed?) if args.installed?
  all_deps.map!(&method(:dep_display_name))
  all_deps.uniq!
  all_deps.sort! unless args.n?
  puts all_deps
end

.deps_argsObject



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'brew/Library/Homebrew/cmd/deps.rb', line 10

def deps_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `deps` [<options>] [<formula>]

      Show dependencies for <formula>. Additional options specific to <formula>
      may be appended to the command. When given multiple formula arguments,
      show the intersection of dependencies for each formula.
    EOS
    switch "-n",
           description: "Sort dependencies in topological order."
    switch "--1",
           description: "Only show dependencies one level down, instead of recursing."
    switch "--union",
           description: "Show the union of dependencies for multiple <formula>, instead of the intersection."
    switch "--full-name",
           description: "List dependencies by their full name."
    switch "--include-build",
           description: "Include `:build` dependencies for <formula>."
    switch "--include-optional",
           description: "Include `:optional` dependencies for <formula>."
    switch "--include-test",
           description: "Include `:test` dependencies for <formula> (non-recursive)."
    switch "--skip-recommended",
           description: "Skip `:recommended` dependencies for <formula>."
    switch "--include-requirements",
           description: "Include requirements in addition to dependencies for <formula>."
    switch "--tree",
           description: "Show dependencies as a tree. When given multiple formula arguments, "\
                        "show individual trees for each formula."
    switch "--annotate",
           description: "Mark any build, test, optional, or recommended dependencies as "\
                        "such in the output."
    switch "--installed",
           description: "List dependencies for formulae that are currently installed. If <formula> is "\
                        "specified, list only its dependencies that are currently installed."
    switch "--all",
           description: "List dependencies for all available formulae."
    switch "--for-each",
           description: "Switch into the mode used by the `--all` option, but only list dependencies "\
                        "for each provided <formula>, one formula per line. This is used for "\
                        "debugging the `--installed`/`--all` display mode."
    switch :verbose
    switch :debug
    conflicts "--installed", "--all"
    formula_options
  end
end

.deps_for_formula(f, recursive = false) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'brew/Library/Homebrew/cmd/deps.rb', line 137

def deps_for_formula(f, recursive = false)
  includes, ignores = argv_includes_ignores(ARGV)

  deps = f.runtime_dependencies if @use_runtime_dependencies

  if recursive
    deps ||= recursive_includes(Dependency,  f, includes, ignores)
    reqs   = recursive_includes(Requirement, f, includes, ignores)
  else
    deps ||= reject_ignores(f.deps, ignores, includes)
    reqs   = reject_ignores(f.requirements, ignores, includes)
  end

  deps + reqs.to_a
end

.deps_for_formulae(formulae, recursive = false, &block) ⇒ Object



153
154
155
# File 'brew/Library/Homebrew/cmd/deps.rb', line 153

def deps_for_formulae(formulae, recursive = false, &block)
  formulae.map { |f| deps_for_formula(f, recursive) }.reduce(&block)
end

.descObject



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'brew/Library/Homebrew/cmd/desc.rb', line 37

def desc
  desc_args.parse

  search_type = if args.search?
    :either
  elsif args.name?
    :name
  elsif args.description?
    :desc
  end

  results = if search_type.nil?
    desc = {}
    args.formulae.each { |f| desc[f.full_name] = f.desc }
    Descriptions.new(desc)
  else
    query = args.named.join(" ")
    string_or_regex = query_regexp(query)
    CacheStoreDatabase.use(:descriptions) do |db|
      cache_store = DescriptionCacheStore.new(db)
      Descriptions.search(string_or_regex, search_type, cache_store)
    end
  end

  results.print
end

.desc_argsObject



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'brew/Library/Homebrew/cmd/desc.rb', line 13

def desc_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `desc` [<options>] (<text>|`/`<text>`/`|<formula>)

      Display <formula>'s name and one-line description.
      Formula descriptions are cached; the cache is created on the
      first search, making that search slower than subsequent ones.
    EOS
    switch "-s", "--search",
           description: "Search both names and descriptions for <text>. If <text> is flanked by "\
                        "slashes, it is interpreted as a regular expression."
    switch "-n", "--name",
           description: "Search just names for <text>. If <text> is flanked by slashes, it is "\
                        "interpreted as a regular expression."
    switch "-d", "--description",
           description: "Search just descriptions for <text>. If <text> is flanked by slashes, "\
                        "it is interpreted as a regular expression."
    switch :verbose
    conflicts "--search", "--name", "--description"
    min_named 1
  end
end

.detect_name(path, version) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'brew/Library/Homebrew/cmd/diy.rb', line 56

def detect_name(path, version)
  basename = path.basename.to_s
  detected_name = basename[/(.*?)-?#{Regexp.escape(version)}/, 1] || basename
  canonical_name = Formulary.canonical_name(detected_name)

  odie <<~EOS if detected_name != canonical_name
    The detected name #{detected_name.inspect} exists in Homebrew as an alias
    of #{canonical_name.inspect}. Consider using the canonical name instead:
      brew diy --name=#{canonical_name}

    To continue using the detected name, pass it explicitly:
      brew diy --name=#{detected_name}
  EOS

  detected_name
end

.detect_version(path) ⇒ Object



49
50
51
52
53
54
# File 'brew/Library/Homebrew/cmd/diy.rb', line 49

def detect_version(path)
  version = path.version.to_s
  raise "Couldn't determine version, set it with --version=<version>" if version.empty?

  version
end

.diyObject



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'brew/Library/Homebrew/cmd/diy.rb', line 28

def diy
  diy_args.parse

  path = Pathname.getwd

  version = args.version || detect_version(path)
  name = args.name || detect_name(path, version)

  prefix = HOMEBREW_CELLAR/name/version

  if File.file? "CMakeLists.txt"
    puts "-DCMAKE_INSTALL_PREFIX=#{prefix}"
  elsif File.file? "configure"
    puts "--prefix=#{prefix}"
  elsif File.file? "meson.build"
    puts "-Dprefix=#{prefix}"
  else
    raise "Couldn't determine build system. You can manually put files into #{prefix}"
  end
end

.diy_argsObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'brew/Library/Homebrew/cmd/diy.rb', line 9

def diy_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `diy` [<options>]

      Automatically determine the installation prefix for non-Homebrew software.
      Using the output from this command, you can install your own software into
      the Cellar and then link it into Homebrew's prefix with `brew link`.
    EOS
    flag   "--name=",
           description: "Explicitly set the <name> of the package being installed."
    flag   "--version=",
           description: "Explicitly set the <version> of the package being installed."
    switch :verbose
    switch :debug
    max_named 0
  end
end

.doctorObject



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'brew/Library/Homebrew/cmd/doctor.rb', line 30

def doctor
  doctor_args.parse

  inject_dump_stats!(Diagnostic::Checks, /^check_*/) if args.audit_debug?

  checks = Diagnostic::Checks.new

  if args.list_checks?
    puts checks.all.sort
    exit
  end

  if args.no_named?
    slow_checks = %w[
      check_for_broken_symlinks
      check_missing_deps
    ]
    methods = (checks.all.sort - slow_checks) + slow_checks
  else
    methods = args.named
  end

  first_warning = true
  methods.each do |method|
    $stderr.puts Formatter.headline("Checking #{method}", color: :magenta) if args.debug?
    unless checks.respond_to?(method)
      Homebrew.failed = true
      puts "No check available by the name: #{method}"
      next
    end

    out = checks.send(method)
    next if out.nil? || out.empty?

    if first_warning
      $stderr.puts <<~EOS
        #{Tty.bold}Please note that these warnings are just used to help the Homebrew maintainers
        with debugging if you file an issue. If everything you use Homebrew for is
        working fine: please don't worry or file an issue; just ignore this. Thanks!#{Tty.reset}
      EOS
    end

    $stderr.puts
    opoo out
    Homebrew.failed = true
    first_warning = false
  end

  puts "Your system is ready to brew." unless Homebrew.failed?
end

.doctor_argsObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'brew/Library/Homebrew/cmd/doctor.rb', line 9

def doctor_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `doctor` [<options>]

      Check your system for potential problems. Will exit with a non-zero status
      if any potential problems are found. Please note that these warnings are just
      used to help the Homebrew maintainers with debugging if you file an issue. If
      everything you use Homebrew for is working fine: please don't worry or file
      an issue; just ignore this.
    EOS
    switch "--list-checks",
           description: "List all audit methods, which can be run individually "\
                        "if provided as arguments."
    switch "-D", "--audit-debug",
           description: "Enable debugging and profiling of audit methods."
    switch :verbose
    switch :debug
  end
end

.dump_build_env(env, f = $stdout) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'brew/Library/Homebrew/build_environment.rb', line 53

def dump_build_env(env, f = $stdout)
  keys = build_env_keys(env)
  keys -= %w[CC CXX OBJC OBJCXX] if env["CC"] == env["HOMEBREW_CC"]

  keys.each do |key|
    value = env[key]
    s = +"#{key}: #{value}"
    case key
    when "CC", "CXX", "LD"
      s << " => #{Pathname.new(value).realpath}" if File.symlink?(value)
    end
    s.freeze
    f.puts s
  end
end

.dump_options_for_formula(f) ⇒ Object



120
121
122
123
124
125
126
# File 'brew/Library/Homebrew/options.rb', line 120

def dump_options_for_formula(f)
  f.options.sort_by(&:flag).each do |opt|
    puts "#{opt.flag}\n\t#{opt.description}"
  end
  puts "--devel\n\tInstall development version #{f.devel.version}" if f.devel
  puts "--HEAD\n\tInstall HEAD version" if f.head
end

.editObject



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'brew/Library/Homebrew/dev-cmd/edit.rb', line 23

def edit
  edit_args.parse

  unless (HOMEBREW_REPOSITORY/".git").directory?
    raise <<~EOS
      Changes will be lost!
      The first time you `brew update`, all local changes will be lost; you should
      thus `brew update` before you `brew edit`!
    EOS
  end

  # If no brews are listed, open the project root in an editor.
  paths = [HOMEBREW_REPOSITORY] if args.no_named?

  # Don't use args.formulae as that will throw if the file doesn't parse
  paths ||= args.named.map do |name|
    path = Formulary.path(name)
    raise FormulaUnavailableError, name if !path.file? && !args.force?

    path
  end

  exec_editor(*paths)
end

.edit_argsObject



9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'brew/Library/Homebrew/dev-cmd/edit.rb', line 9

def edit_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `edit` [<formula>]

      Open <formula> in the editor set by `EDITOR` or `HOMEBREW_EDITOR`, or open the
      Homebrew repository for editing if no formula is provided.
    EOS
    switch :force
    switch :verbose
    switch :debug
  end
end

.ensure_relocation_formulae_installed!Object



97
98
99
100
101
102
103
104
# File 'brew/Library/Homebrew/dev-cmd/bottle.rb', line 97

def ensure_relocation_formulae_installed!
  Keg.relocation_formulae.each do |f|
    next if Formula[f].latest_version_installed?

    ohai "Installing #{f}..."
    safe_system HOMEBREW_BREW_FILE, "install", f
  end
end

.extractObject



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'brew/Library/Homebrew/dev-cmd/extract.rb', line 98

def extract
  extract_args.parse

  if args.named.first !~ HOMEBREW_TAP_FORMULA_REGEX
    name = args.named.first.downcase
    source_tap = CoreTap.instance
  else
    name = Regexp.last_match(3).downcase
    source_tap = Tap.fetch(Regexp.last_match(1), Regexp.last_match(2))
    raise TapFormulaUnavailableError.new(source_tap, name) unless source_tap.installed?
  end

  destination_tap = Tap.fetch(args.named.second)
  unless ARGV.homebrew_developer?
    odie "Cannot extract formula to homebrew/core!" if destination_tap.core_tap?
    odie "Cannot extract formula to the same tap!" if destination_tap == source_tap
  end
  destination_tap.install unless destination_tap.installed?

  repo = source_tap.path
  pattern = if source_tap.core_tap?
    [repo/"Formula/#{name}.rb"]
  else
    # A formula can technically live in the root directory of a tap or in any of its subdirectories
    [repo/"#{name}.rb", repo/"**/#{name}.rb"]
  end

  if args.version
    ohai "Searching repository history"
    version = args.version
    version_segments = Gem::Version.new(version).segments if Gem::Version.correct?(version)
    rev = nil
    test_formula = nil
    result = ""
    loop do
      rev = rev.nil? ? "HEAD" : "#{rev}~1"
      rev, (path,) = Git.last_revision_commit_of_files(repo, pattern, before_commit: rev)
      odie "Could not find #{name}! The formula or version may not have existed." if rev.nil?

      file = repo/path
      result = Git.last_revision_of_file(repo, file, before_commit: rev)
      if result.empty?
        odebug "Skipping revision #{rev} - file is empty at this revision"
        next
      end

      test_formula = formula_at_revision(repo, name, file, rev)
      break if test_formula.nil? || test_formula.version == version

      if version_segments && Gem::Version.correct?(test_formula.version)
        test_formula_version_segments = Gem::Version.new(test_formula.version).segments
        if version_segments.length < test_formula_version_segments.length
          odebug "Apply semantic versioning with #{test_formual_version_segments}"
          break if version_segments == test_formula_version_segments.first(version_segments.length)
        end
      end

      odebug "Trying #{test_formula.version} from revision #{rev} against desired #{version}"
    end
    odie "Could not find #{name}! The formula or version may not have existed." if test_formula.nil?
  else
    # Search in the root directory of <repo> as well as recursively in all of its subdirectories
    files = Dir[repo/"{,**/}"].map do |dir|
      Pathname.glob(["#{dir}/#{name}.rb"]).find(&:file?)
    end.compact

    if files.empty?
      ohai "Searching repository history"
      rev, (path,) = Git.last_revision_commit_of_files(repo, pattern)
      odie "Could not find #{name}! The formula or version may not have existed." if rev.nil?
      file = repo/path
      version = formula_at_revision(repo, name, file, rev).version
      result = Git.last_revision_of_file(repo, file)
    else
      file = files.first.realpath
      rev = "HEAD"
      version = Formulary.factory(file).version
      result = File.read(file)
    end
  end

  # The class name has to be renamed to match the new filename,
  # e.g. Foo version 1.2.3 becomes FooAT123 and resides in Foo@1.2.3.rb.
  class_name = Formulary.class_s(name)

  # Remove any existing version suffixes, as a new one will be added later
  name.sub!(/\b@(.*)\z\b/i, "")
  versioned_name = Formulary.class_s("#{name}@#{version}")
  result.gsub!("class #{class_name} < Formula", "class #{versioned_name} < Formula")

  path = destination_tap.path/"Formula/#{name}@#{version}.rb"
  if path.exist?
    unless args.force?
      odie <<~EOS
        Destination formula already exists: #{path}
        To overwrite it and continue anyways, run:
          brew extract --force --version=#{version} #{name} #{destination_tap.name}
      EOS
    end
    odebug "Overwriting existing formula at #{path}"
    path.delete
  end
  ohai "Writing formula for #{name} from revision #{rev} to #{path}"
  path.write result
end

.extract_argsObject



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'brew/Library/Homebrew/dev-cmd/extract.rb', line 79

def extract_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `extract` [<options>] <formula> <tap>

      Look through repository history to find the most recent version of <formula> and
      create a copy in <tap>`/Formula/`<formula>`@`<version>`.rb`. If the tap is not
      installed yet, attempt to install/clone the tap before continuing. To extract
      a formula from a tap that is not `homebrew/core` use its fully-qualified form of
      <user>`/`<repo>`/`<formula>.
    EOS
    flag   "--version=",
           description: "Extract the specified <version> of <formula> instead of the most recent."
    switch :force
    switch :debug
    named 2
  end
end

.failed?Boolean

Returns:

  • (Boolean)


81
82
83
84
# File 'brew/Library/Homebrew/global.rb', line 81

def failed?
  @failed ||= false
  @failed == true
end

.fetchObject



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'brew/Library/Homebrew/cmd/fetch.rb', line 46

def fetch
  fetch_args.parse

  if args.deps?
    bucket = []
    args.formulae.each do |f|
      bucket << f
      bucket.concat f.recursive_dependencies.map(&:to_formula)
    end
    bucket.uniq!
  else
    bucket = args.formulae
  end

  puts "Fetching: #{bucket * ", "}" if bucket.size > 1
  bucket.each do |f|
    f.print_tap_action verb: "Fetching"

    fetched_bottle = false
    if Fetch.fetch_bottle?(f)
      begin
        fetch_formula(f.bottle)
      rescue Interrupt
        raise
      rescue => e
        raise if ARGV.homebrew_developer?

        fetched_bottle = false
        onoe e.message
        opoo "Bottle fetch failed: fetching the source."
      else
        fetched_bottle = true
      end
    end

    next if fetched_bottle

    fetch_formula(f)

    f.resources.each do |r|
      fetch_resource(r)
      r.patches.each { |p| fetch_patch(p) if p.external? }
    end

    f.patchlist.each { |p| fetch_patch(p) if p.external? }
  end
end

.fetch_argsObject



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'brew/Library/Homebrew/cmd/fetch.rb', line 10

def fetch_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `fetch` [<options>] <formula>

      Download a bottle (if available) or source packages for <formula>.
      For tarballs, also print SHA-256 checksums.
    EOS
    switch "--HEAD",
           description: "Fetch HEAD version instead of stable version."
    switch "--devel",
           description: "Fetch development version instead of stable version."
    switch :force,
           description: "Remove a previously cached version and re-fetch."
    switch :verbose,
           description: "Do a verbose VCS checkout, if the URL represents a VCS. This is useful for "\
                        "seeing if an existing VCS cache has been updated."
    switch "--retry",
           description: "Retry if downloading fails or re-download if the checksum of a previously cached "\
                        "version no longer matches."
    switch "--deps",
           description: "Also download dependencies for any listed <formula>."
    switch "-s", "--build-from-source",
           description: "Download source packages rather than a bottle."
    switch "--build-bottle",
           description: "Download source packages (for eventual bottling) rather than a bottle."
    switch "--force-bottle",
           description: "Download a bottle if it exists for the current or newest version of macOS, "\
                        "even if it would not be used during installation."
    switch :debug
    conflicts "--devel", "--HEAD"
    conflicts "--build-from-source", "--build-bottle", "--force-bottle"
    min_named :formula
  end
end

.fetch_bottles_patch(bottle_commit_url, args, bottle_branch, branch, orig_revision) ⇒ Object



295
296
297
298
299
300
301
302
# File 'brew/Library/Homebrew/dev-cmd/pull.rb', line 295

def fetch_bottles_patch(bottle_commit_url, args, bottle_branch, branch, orig_revision)
  safe_system "git", "checkout", "--quiet", "-B", bottle_branch, orig_revision
  PatchPuller.new(bottle_commit_url, args, "bottle commit").pull_patch
  safe_system "git", "rebase", "--quiet", branch
  safe_system "git", "checkout", "--quiet", branch
  safe_system "git", "merge", "--quiet", "--ff-only", "--no-edit", bottle_branch
  safe_system "git", "branch", "--quiet", "-D", bottle_branch
end

.fetch_fetchable(f) ⇒ Object



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'brew/Library/Homebrew/cmd/fetch.rb', line 128

def fetch_fetchable(f)
  f.clear_cache if args.force?

  already_fetched = f.cached_download.exist?

  begin
    download = f.fetch(verify_download_integrity: false)
  rescue DownloadError
    retry if retry_fetch? f
    raise
  end

  return unless download.file?

  puts "Downloaded to: #{download}" unless already_fetched
  puts Checksum::TYPES.map { |t| "#{t.to_s.upcase}: #{download.send(t)}" }

  f.verify_download_integrity(download)
end

.fetch_formula(f) ⇒ Object



102
103
104
105
106
107
# File 'brew/Library/Homebrew/cmd/fetch.rb', line 102

def fetch_formula(f)
  fetch_fetchable f
rescue ChecksumMismatchError => e
  retry if retry_fetch? f
  opoo "Formula reports different #{e.hash_type}: #{e.expected}"
end

.fetch_merge_patch(url, args, issue) ⇒ Object



304
305
306
# File 'brew/Library/Homebrew/dev-cmd/pull.rb', line 304

def fetch_merge_patch(url, args, issue)
  PatchPuller.new(url, args, "merge commit").pull_merge_commit(issue)
end

.fetch_patch(p) ⇒ Object



109
110
111
112
113
114
# File 'brew/Library/Homebrew/cmd/fetch.rb', line 109

def fetch_patch(p)
  fetch_fetchable p
rescue ChecksumMismatchError => e
  Homebrew.failed = true
  opoo "Patch reports different #{e.hash_type}: #{e.expected}"
end

.fetch_pull_requests(formula, tap_full_name) ⇒ Object



474
475
476
477
478
479
480
481
482
# File 'brew/Library/Homebrew/dev-cmd/bump-formula-pr.rb', line 474

def fetch_pull_requests(formula, tap_full_name)
  GitHub.issues_for_formula(formula.name, tap_full_name: tap_full_name).select do |pr|
    pr["html_url"].include?("/pull/") &&
      /(^|\s)#{Regexp.quote(formula.name)}(:|\s|$)/i =~ pr["title"]
  end
rescue GitHub::RateLimitExceededError => e
  opoo e.message
  []
end

.fetch_resource(r) ⇒ Object



94
95
96
97
98
99
100
# File 'brew/Library/Homebrew/cmd/fetch.rb', line 94

def fetch_resource(r)
  puts "Resource: #{r.name}"
  fetch_fetchable r
rescue ChecksumMismatchError => e
  retry if retry_fetch? r
  opoo "Resource #{r.name} reports different #{e.hash_type}: #{e.expected}"
end

.files_changed_in_patch(patchfile, tap) ⇒ Object

List files changed by a patch, partitioned in to those that are (probably) formula definitions, and those which aren’t. Only applies to patches on Homebrew core or taps, based simply on relative pathnames of affected files.



385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'brew/Library/Homebrew/dev-cmd/pull.rb', line 385

def files_changed_in_patch(patchfile, tap)
  files = []
  formulae = []
  others = []
  File.foreach(patchfile) do |line|
    files << Regexp.last_match(1) if line =~ %r{^\+\+\+ b/(.*)}
  end
  files.each do |file|
    if tap&.formula_file?(file)
      formula_name = File.basename(file, ".rb")
      formulae << formula_name unless formulae.include?(formula_name)
    else
      others << file
    end
  end
  { files: files, formulae: formulae, others: others }
end

.filtered_listObject



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'brew/Library/Homebrew/cmd/list.rb', line 125

def filtered_list
  names = if args.no_named?
    Formula.racks
  else
    racks = args.named.map { |n| Formulary.to_rack(n) }
    racks.select do |rack|
      Homebrew.failed = true unless rack.exist?
      rack.exist?
    end
  end
  if args.pinned?
    pinned_versions = {}
    names.sort.each do |d|
      keg_pin = (HOMEBREW_PINNED_KEGS/d.basename.to_s)
      pinned_versions[d] = keg_pin.readlink.basename.to_s if keg_pin.exist? || keg_pin.symlink?
    end
    pinned_versions.each do |d, version|
      puts d.basename.to_s.concat(args.versions? ? " #{version}" : "")
    end
  else # --versions without --pinned
    names.sort.each do |d|
      versions = d.subdirs.map { |pn| pn.basename.to_s }
      next if args.multiple? && versions.length < 2

      puts "#{d.basename} #{versions * " "}"
    end
  end
end

.find_in_path(executable) ⇒ Object



83
84
85
86
87
# File 'brew/Library/Homebrew/utils/gems.rb', line 83

def find_in_path(executable)
  ENV["PATH"].split(":").find do |path|
    File.executable?("#{path}/#{executable}")
  end
end

.force_auto_update?Boolean

Returns:

  • (Boolean)


67
68
69
70
# File 'brew/Library/Homebrew/cmd/tap.rb', line 67

def force_auto_update?  # if no relevant flag is present, return nil, meaning "no change"

  true if args.force_auto_update?
end

.forked_repo_info(formula, tap_full_name, backup_file) ⇒ Object



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'brew/Library/Homebrew/dev-cmd/bump-formula-pr.rb', line 418

def forked_repo_info(formula, tap_full_name, backup_file)
  response = GitHub.create_fork(tap_full_name)
rescue GitHub::AuthenticationFailedError, *GitHub.api_errors => e
  formula.path.atomic_write(backup_file)
  odie "Unable to fork: #{e.message}!"
else
  # GitHub API responds immediately but fork takes a few seconds to be ready.
  sleep 1 until GitHub.check_fork_exists(tap_full_name)
  remote_url = if system("git", "config", "--local", "--get-regexp", "remote\..*\.url", "git@github.com:.*")
    response.fetch("ssh_url")
  else
    url = response.fetch("clone_url")
    url.gsub!(%r{^https://github\.com/}, "https://#{GitHub.env_token}@github.com/") if GitHub.env_token
    url
  end
  username = response.fetch("owner").fetch("login")
  [remote_url, username]
end

.format_long_opt(opt) ⇒ Object



219
220
221
# File 'brew/Library/Homebrew/dev-cmd/man.rb', line 219

def format_long_opt(opt)
  "`#{opt}`" unless opt.nil?
end

.format_problem_lines(problems) ⇒ Object



160
161
162
# File 'brew/Library/Homebrew/dev-cmd/audit.rb', line 160

def format_problem_lines(problems)
  problems.uniq.map { |p| "* #{p.chomp.gsub("\n", "\n    ")}" }
end

.format_short_opt(opt) ⇒ Object



215
216
217
# File 'brew/Library/Homebrew/dev-cmd/man.rb', line 215

def format_short_opt(opt)
  "`#{opt}`" unless opt.nil?
end

.format_usage_banner(usage_banner) ⇒ Object



223
224
225
# File 'brew/Library/Homebrew/dev-cmd/man.rb', line 223

def format_usage_banner(usage_banner)
  usage_banner&.sub(/^(#: *\* )?/, "### ")
end

.formulaObject



22
23
24
25
26
# File 'brew/Library/Homebrew/dev-cmd/formula.rb', line 22

def formula
  formula_args.parse

  args.resolved_formulae.each { |f| puts f.path }
end

.formula_argsObject



9
10
11
12
13
14
15
16
17
18
19
20
# File 'brew/Library/Homebrew/dev-cmd/formula.rb', line 9

def formula_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `formula` <formula>

      Display the path where <formula> is located.
    EOS
    switch :verbose
    switch :debug
    min_named :formula
  end
end

.formula_version(formula, spec, contents = nil) ⇒ Object



464
465
466
467
468
469
470
471
472
# File 'brew/Library/Homebrew/dev-cmd/bump-formula-pr.rb', line 464

def formula_version(formula, spec, contents = nil)
  name = formula.name
  path = formula.path
  if contents
    Formulary.from_contents(name, path, contents, spec).version
  else
    Formulary::FormulaLoader.new(name, path).get_formula(spec).version
  end
end

.gem_user_bindirObject



19
20
21
22
# File 'brew/Library/Homebrew/utils/gems.rb', line 19

def gem_user_bindir
  require "rubygems"
  "#{Gem.user_dir}/bin"
end

.generate_cmd_manpages(cmd_paths) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'brew/Library/Homebrew/dev-cmd/man.rb', line 146

def generate_cmd_manpages(cmd_paths)
  man_page_lines = []
  man_args = Homebrew.args  # preserve existing manpage order

  cmd_paths.sort_by(&method(:sort_key_for_path))
           .each do |cmd_path|
    cmd_man_page_lines = if cmd_parser = CLI::Parser.from_cmd_path(cmd_path)
      next if cmd_parser.hide_from_man_page

      cmd_parser_manpage_lines(cmd_parser).join
    else
      cmd_comment_manpage_lines(cmd_path)
    end

    man_page_lines << cmd_man_page_lines
  end
  Homebrew.args = man_args
  man_page_lines.compact.join("\n")
end

.generate_option_doc(short, long, desc) ⇒ Object



210
211
212
213
# File 'brew/Library/Homebrew/dev-cmd/man.rb', line 210

def generate_option_doc(short, long, desc)
  comma = (short && long) ? ", " : ""
  "* #{format_short_opt(short)}" + comma + "#{format_long_opt(long)}:" + "\n  " + desc + "\n"
end

.gist_logsObject



142
143
144
145
146
147
148
149
150
# File 'brew/Library/Homebrew/cmd/gist-logs.rb', line 142

def gist_logs
  gist_logs_args.parse

  raise FormulaUnspecifiedError if args.resolved_formulae.length != 1

  Install.perform_preinstall_checks(all_fatal: true)
  Install.perform_build_from_source_checks(all_fatal: true)
  gistify_logs(args.resolved_formulae.first)
end

.gist_logs_argsObject



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'brew/Library/Homebrew/cmd/gist-logs.rb', line 13

def gist_logs_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `gist-logs` [<options>] <formula>

      Upload logs for a failed build of <formula> to a new Gist. Presents an
      error message if no logs are found.
    EOS
    switch "--with-hostname",
           description: "Include the hostname in the Gist."
    switch "-n", "--new-issue",
           description: "Automatically create a new issue in the appropriate GitHub repository "\
                        "after creating the Gist."
    switch "-p", "--private",
           description: "The Gist will be marked private and will not appear in listings but will "\
                        "be accessible with its link."
    switch :verbose
    switch :debug
    max_named 1
  end
end

.gistify_logs(f) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'brew/Library/Homebrew/cmd/gist-logs.rb', line 35

def gistify_logs(f)
  files = load_logs(f.logs)
  build_time = f.logs.ctime
  timestamp = build_time.strftime("%Y-%m-%d_%H-%M-%S")

  s = StringIO.new
  SystemConfig.dump_verbose_config s  # Dummy summary file, asciibetically first, to control display title of gist

  files["# #{f.name} - #{timestamp}.txt"] = { content: brief_build_info(f) }
  files["00.config.out"] = { content: s.string }
  files["00.doctor.out"] = { content: Utils.popen_read("#{HOMEBREW_PREFIX}/bin/brew", "doctor", err: :out) }
  unless f.core_formula?
    tap = <<~EOS
      Formula: #{f.name}
          Tap: #{f.tap}
         Path: #{f.path}
    EOS
    files["00.tap.out"] = { content: tap }
  end

  if GitHub.api_credentials_type == :none
    puts <<~EOS
      You can create a new personal access token:
        #{GitHub::ALL_SCOPES_URL}
      #{Utils::Shell.set_variable_in_profile("HOMEBREW_GITHUB_API_TOKEN", "your_token_here")}

    EOS
    login!
  end

  # Description formatted to work well as page title when viewing gist
  descr = if f.core_formula?
    "#{f.name} on #{OS_VERSION} - Homebrew build logs"
  else
    "#{f.name} (#{f.full_name}) on #{OS_VERSION} - Homebrew build logs"
  end
  url = create_gist(files, descr)

  url = create_issue(f.tap, "#{f.name} failed to build on #{MacOS.full_version}", url) if args.new_issue?

  puts url if url
end

.git_log(cd_dir, path = nil, tap = nil) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'brew/Library/Homebrew/cmd/log.rb', line 45

def git_log(cd_dir, path = nil, tap = nil)
  cd cd_dir
  repo = Utils.popen_read("git rev-parse --show-toplevel").chomp
  if tap
    name = tap.to_s
    git_cd = "$(brew --repo #{tap})"
  elsif cd_dir == HOMEBREW_REPOSITORY
    name = "Homebrew/brew"
    git_cd = "$(brew --repo)"
  else
    name, git_cd = cd_dir
  end

  if File.exist? "#{repo}/.git/shallow"
    opoo <<~EOS
      #{name} is a shallow clone so only partial output will be shown.
      To get a full clone run:
        git -C "#{git_cd}" fetch --unshallow
    EOS
  end
  system_args = args.options_only
  system_args += ["--follow", "--", path] if path.present?
  system "git", "log", *system_args
end

.github_info(f) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
# File 'brew/Library/Homebrew/cmd/info.rb', line 153

def github_info(f)
  if f.tap
    if remote = f.tap.remote
      path = f.path.relative_path_from(f.tap.path)
      github_remote_path(remote, path)
    else
      f.path
    end
  else
    f.path
  end
end

.github_remote_path(remote, path) ⇒ Object



145
146
147
148
149
150
151
# File 'brew/Library/Homebrew/cmd/info.rb', line 145

def github_remote_path(remote, path)
  if remote =~ %r{^(?:https?://|git(?:@|://))github\.com[:/](.+)/(.+?)(?:\.git)?$}
    "https://github.com/#{Regexp.last_match(1)}/#{Regexp.last_match(2)}/blob/master/#{path}"
  else
    "#{remote}/#{path}"
  end
end

.global_options_manpageObject



201
202
203
204
205
206
207
208
# File 'brew/Library/Homebrew/dev-cmd/man.rb', line 201

def global_options_manpage
  lines = ["These options are applicable across multiple subcommands.\n"]
  lines += Homebrew::CLI::Parser.global_options.values.map do |names, _, desc|
    short, long = names
    generate_option_doc(short, long, desc)
  end
  lines.join("\n")
end

.handle_unsatisfied_dependents(kegs_by_rack) ⇒ Object



100
101
102
103
104
105
106
107
108
# File 'brew/Library/Homebrew/cmd/uninstall.rb', line 100

def handle_unsatisfied_dependents(kegs_by_rack)
  return if args.ignore_dependencies?

  all_kegs = kegs_by_rack.values.flatten(1)
  check_for_dependents all_kegs
rescue MethodDeprecatedError  # Silently ignore deprecations when uninstalling.

  nil
end

.head_revision(_url, fetched) ⇒ Object



291
292
293
# File 'brew/Library/Homebrew/dev-cmd/pull.rb', line 291

def head_revision(_url, fetched)
  Utils.popen_read("git", "rev-parse", fetched ? "FETCH_HEAD" : "HEAD").strip
end

.homeObject



20
21
22
23
24
25
26
27
28
# File 'brew/Library/Homebrew/cmd/home.rb', line 20

def home
  home_args.parse

  if args.no_named?
    exec_browser HOMEBREW_WWW
  else
    exec_browser(*args.formulae.map(&:homepage))
  end
end

.home_argsObject



8
9
10
11
12
13
14
15
16
17
18
# File 'brew/Library/Homebrew/cmd/home.rb', line 8

def home_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `home` [<formula>]

      Open <formula>'s homepage in a browser, or open Homebrew's own homepage
      if no formula is provided.
    EOS
    switch :debug
  end
end

.infoObject



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'brew/Library/Homebrew/cmd/info.rb', line 62

def info
  info_args.parse

  if args.days.present?
    raise UsageError, "--days must be one of #{VALID_DAYS.join(", ")}" unless VALID_DAYS.include?(args.days)
  end

  if args.category.present?
    if args.named.present? && !VALID_FORMULA_CATEGORIES.include?(args.category)
      raise UsageError, "--category must be one of #{VALID_FORMULA_CATEGORIES.join(", ")} when querying formulae"
    end

    unless VALID_CATEGORIES.include?(args.category)
      raise UsageError, "--category must be one of #{VALID_CATEGORIES.join(", ")}"
    end
  end

  if args.json
    raise UsageError, "invalid JSON version: #{args.json}" unless ["v1", true].include? args.json

    if !(args.all? || args.installed?) && args.no_named?
      raise FormulaUnspecifiedError if args.no_named?
    end

    print_json
  elsif args.github?
    raise FormulaUnspecifiedError if args.no_named?

    exec_browser(*args.formulae.map { |f| github_info(f) })
  else
    print_info
  end
end

.info_argsObject



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'brew/Library/Homebrew/cmd/info.rb', line 19

def info_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `info` [<options>] [<formula>]

      Display brief statistics for your Homebrew installation.

      If <formula> is provided, show summary of information about <formula>.
    EOS
    switch "--analytics",
           description: "List global Homebrew analytics data or, if specified, installation and "\
                        "build error data for <formula> (provided neither `HOMEBREW_NO_ANALYTICS` "\
                        "nor `HOMEBREW_NO_GITHUB_API` are set)."
    flag   "--days",
           depends_on:  "--analytics",
           description: "How many days of analytics data to retrieve. "\
                        "The value for <days> must be `30`, `90` or `365`. The default is `30`."
    flag   "--category",
           depends_on:  "--analytics",
           description: "Which type of analytics data to retrieve. "\
                        "The value for <category> must be `install`, `install-on-request` or `build-error`; "\
                        "`cask-install` or `os-version` may be specified if <formula> is not. "\
                        "The default is `install`."
    switch "--github",
           description: "Open the GitHub source page for <formula> in a browser. "\
                        "To view formula history locally: `brew log -p` <formula>"
    flag   "--json",
           description: "Print a JSON representation of <formula>. Currently the default and only accepted "\
                        "value for <version> is `v1`. See the docs for examples of using the JSON "\
                        "output: <https://docs.brew.sh/Querying-Brew>"
    switch "--installed",
           depends_on:  "--json",
           description: "Print JSON of formulae that are currently installed."
    switch "--all",
           depends_on:  "--json",
           description: "Print JSON of all available formulae."
    switch :verbose,
           description: "Show more verbose analytics data for <formula>."
    switch :debug
    conflicts "--installed", "--all"
  end
end

.info_formula(f) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'brew/Library/Homebrew/cmd/info.rb', line 166

def info_formula(f)
  specs = []

  if stable = f.stable
    s = "stable #{stable.version}"
    s += " (bottled)" if stable.bottled?
    specs << s
  end

  if devel = f.devel
    specs << "devel #{devel.version}"
  end

  specs << "HEAD" if f.head

  attrs = []
  attrs << "pinned at #{f.pinned_version}" if f.pinned?
  attrs << "keg-only" if f.keg_only?

  puts "#{f.full_name}: #{specs * ", "}#{" [#{attrs * ", "}]" unless attrs.empty?}"
  puts f.desc if f.desc
  puts Formatter.url(f.homepage) if f.homepage

  conflicts = f.conflicts.map do |c|
    reason = " (because #{c.reason})" if c.reason
    "#{c.name}#{reason}"
  end.sort!
  unless conflicts.empty?
    puts <<~EOS
      Conflicts with:
        #{conflicts.join("\n  ")}
    EOS
  end

  kegs = f.installed_kegs
  heads, versioned = kegs.partition { |k| k.version.head? }
  kegs = [
    *heads.sort_by { |k| -Tab.for_keg(k).time.to_i },
    *versioned.sort_by(&:version),
  ]
  if kegs.empty?
    puts "Not installed"
  else
    kegs.each do |keg|
      puts "#{keg} (#{keg.abv})#{" *" if keg.linked?}"
      tab = Tab.for_keg(keg).to_s
      puts "  #{tab}" unless tab.empty?
    end
  end

  puts "From: #{Formatter.url(github_info(f))}"

  unless f.deps.empty?
    ohai "Dependencies"
    %w[build required recommended optional].map do |type|
      deps = f.deps.send(type).uniq
      puts "#{type.capitalize}: #{decorate_dependencies deps}" unless deps.empty?
    end
  end

  unless f.requirements.to_a.empty?
    ohai "Requirements"
    %w[build required recommended optional].map do |type|
      reqs = f.requirements.select(&:"#{type}?")
      next if reqs.to_a.empty?

      puts "#{type.capitalize}: #{decorate_requirements(reqs)}"
    end
  end

  if !f.options.empty? || f.head || f.devel
    ohai "Options"
    Homebrew.dump_options_for_formula f
  end

  caveats = Caveats.new(f)
  ohai "Caveats", caveats.to_s unless caveats.empty?

  Utils::Analytics.formula_output(f)
end

.inject_dump_stats!(the_module, pattern) ⇒ Object

rubocop:disable Style/GlobalVars



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'brew/Library/Homebrew/utils.rb', line 46

def inject_dump_stats!(the_module, pattern)
  @injected_dump_stat_modules ||= {}
  @injected_dump_stat_modules[the_module] ||= []
  injected_methods = @injected_dump_stat_modules[the_module]
  the_module.module_eval do
    instance_methods.grep(pattern).each do |name|
      next if injected_methods.include? name

      method = instance_method(name)
      define_method(name) do |*args, &block|
        time = Time.now
        method.bind(self).call(*args, &block)
      ensure
        $times[name] ||= 0
        $times[name] += Time.now - time
      end
    end
  end

  return unless $times.nil?

  $times = {}
  at_exit do
    col_width = [$times.keys.map(&:size).max.to_i + 2, 15].max
    $times.sort_by { |_k, v| v }.each do |method, time|
      puts format("%<method>-#{col_width}s %<time>0.4f sec", method: "#{method}:", time: time)
    end
  end
end

.inreplace_pairs(path, replacement_pairs) ⇒ Object



437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'brew/Library/Homebrew/dev-cmd/bump-formula-pr.rb', line 437

def inreplace_pairs(path, replacement_pairs)
  if args.dry_run?
    contents = path.open("r") { |f| Formulary.ensure_utf8_encoding(f).read }
    contents.extend(StringInreplaceExtension)
    replacement_pairs.each do |old, new|
      ohai "replace #{old.inspect} with #{new.inspect}" unless args.quiet?
      raise "No old value for new value #{new}! Did you pass the wrong arguments?" unless old

      contents.gsub!(old, new)
    end
    raise Utils::InreplaceError, path => contents.errors unless contents.errors.empty?

    path.atomic_write(contents) if args.write?
    contents
  else
    Utils::Inreplace.inreplace(path) do |s|
      replacement_pairs.each do |old, new|
        ohai "replace #{old.inspect} with #{new.inspect}" unless args.quiet?
        raise "No old value for new value #{new}! Did you pass the wrong arguments?" unless old

        s.gsub!(old, new)
      end
    end
    path.open("r") { |f| Formulary.ensure_utf8_encoding(f).read }
  end
end

.installObject



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'brew/Library/Homebrew/cmd/install.rb', line 95

def install
  install_args.parse

  args.named.each do |name|
    next if File.exist?(name)
    next if name !~ HOMEBREW_TAP_FORMULA_REGEX && name !~ HOMEBREW_CASK_TAP_CASK_REGEX

    tap = Tap.fetch(Regexp.last_match(1), Regexp.last_match(2))
    tap.install unless tap.installed?
  end

  if args.ignore_dependencies?
    opoo <<~EOS
      #{Tty.bold}--ignore-dependencies is an unsupported Homebrew developer flag!#{Tty.reset}
      Adjust your PATH to put any preferred versions of applications earlier in the
      PATH rather than using this unsupported flag!

    EOS
  end

  formulae = []

  unless ARGV.casks.empty?
    cask_args = []
    cask_args << "--force" if args.force?
    cask_args << "--debug" if args.debug?
    cask_args << "--verbose" if args.verbose?

    ARGV.casks.each do |c|
      ohai "brew cask install #{c} #{cask_args.join " "}"
      system("#{HOMEBREW_PREFIX}/bin/brew", "cask", "install", c, *cask_args)
    end
  end

  # if the user's flags will prevent bottle only-installations when no
  # developer tools are available, we need to stop them early on
  FormulaInstaller.prevent_build_flags unless DevelopmentTools.installed?

  args.formulae.each do |f|
    # head-only without --HEAD is an error
    if !args.HEAD? && f.stable.nil? && f.devel.nil?
      raise <<~EOS
        #{f.full_name} is a head-only formula
        Install with `brew install --HEAD #{f.full_name}`
      EOS
    end

    # devel-only without --devel is an error
    if !args.devel? && f.stable.nil? && f.head.nil?
      raise <<~EOS
        #{f.full_name} is a devel-only formula
        Install with `brew install --devel #{f.full_name}`
      EOS
    end

    if !(args.HEAD? || args.devel?) && f.stable.nil?
      raise "#{f.full_name} has no stable download, please choose --devel or --HEAD"
    end

    # --HEAD, fail with no head defined
    raise "No head is defined for #{f.full_name}" if args.head? && f.head.nil?

    # --devel, fail with no devel defined
    raise "No devel block is defined for #{f.full_name}" if args.devel? && f.devel.nil?

    installed_head_version = f.latest_head_version
    if installed_head_version &&
       !f.head_version_outdated?(installed_head_version, fetch_head: args.fetch_HEAD?)
      new_head_installed = true
    end
    prefix_installed = f.prefix.exist? && !f.prefix.children.empty?

    if f.keg_only? && f.any_version_installed? && f.optlinked? && !args.force?      # keg-only install is only possible when no other version is
      # linked to opt, because installing without any warnings can break
      # dependencies. Therefore before performing other checks we need to be
      # sure --force flag is passed.

      if f.outdated?
        optlinked_version = Keg.for(f.opt_prefix).version
        onoe <<~EOS
          #{f.full_name} #{optlinked_version} is already installed
          To upgrade to #{f.version}, run `brew upgrade #{f.full_name}`
        EOS
      elsif args.only_dependencies?
        formulae << f
      else
        opoo <<~EOS
          #{f.full_name} #{f.pkg_version} is already installed and up-to-date
          To reinstall #{f.pkg_version}, run `brew reinstall #{f.name}`
        EOS
      end
    elsif (args.HEAD? && new_head_installed) || prefix_installed      # After we're sure that --force flag is passed for linked to opt
      # keg-only we need to be sure that the version we're attempting to
      # install is not already installed.


      installed_version = if args.HEAD?
        f.latest_head_version
      else
        f.pkg_version
      end

      msg = "#{f.full_name} #{installed_version} is already installed"
      linked_not_equals_installed = f.linked_version != installed_version
      if f.linked? && linked_not_equals_installed
        msg = <<~EOS
          #{msg}
          The currently linked version is #{f.linked_version}
          You can use `brew switch #{f} #{installed_version}` to link this version.
        EOS
      elsif !f.linked? || f.keg_only?
        msg = <<~EOS
          #{msg}, it's just not linked
          You can use `brew link #{f}` to link this version.
        EOS
      elsif args.only_dependencies?
        msg = nil
        formulae << f
      else
        msg = <<~EOS
          #{msg} and up-to-date
          To reinstall #{f.pkg_version}, run `brew reinstall #{f.name}`
        EOS
      end
      opoo msg if msg
    elsif !f.any_version_installed? && old_formula = f.old_installed_formulae.first
      msg = "#{old_formula.full_name} #{old_formula.installed_version} already installed"
      if !old_formula.linked? && !old_formula.keg_only?
        msg = <<~EOS
          #{msg}, it's just not linked.
          You can use `brew link #{old_formula.full_name}` to link this version.
        EOS
      end
      opoo msg
    elsif f.migration_needed? && !args.force?      # Check if the formula we try to install is the same as installed
      # but not migrated one. If --force is passed then install anyway.

      opoo <<~EOS
        #{f.oldname} is already installed, it's just not migrated
        You can migrate this formula with `brew migrate #{f}`
        Or you can force install it with `brew install #{f} --force`
      EOS
    else
      # If none of the above is true and the formula is linked, then
      # FormulaInstaller will handle this case.
      formulae << f
    end

    # Even if we don't install this formula mark it as no longer just
    # installed as a dependency.
    next unless f.opt_prefix.directory?

    keg = Keg.new(f.opt_prefix.resolved_path)
    tab = Tab.for_keg(keg)
    unless tab.installed_on_request
      tab.installed_on_request = true
      tab.write
    end
  end

  return if formulae.empty?

  Install.perform_preinstall_checks

  formulae.each do |f|
    Migrator.migrate_if_needed(f)
    install_formula(f)
    Cleanup.install_formula_clean!(f)
  end
  Homebrew.messages.display_messages
rescue FormulaUnreadableError, FormulaClassUnavailableError,
       TapFormulaUnreadableError, TapFormulaClassUnavailableError => e  # Need to rescue before `FormulaUnavailableError` (superclass of this)
  # is handled, as searching for a formula doesn't make sense here (the
  # formula was found, but there's a problem with its implementation).

  ofail e.message
rescue FormulaUnavailableError => e
  if e.name == "updog"
    ofail "What's updog?"
    return
  end

  ofail e.message
  if (reason = MissingFormula.reason(e.name))
    $stderr.puts reason
    return
  end

  ohai "Searching for similarly named formulae..."
  formulae_search_results = search_formulae(e.name)
  case formulae_search_results.length
  when 0
    ofail "No similarly named formulae found."
  when 1
    puts "This similarly named formula was found:"
    puts formulae_search_results
    puts "To install it, run:\n  brew install #{formulae_search_results.first}"
  else
    puts "These similarly named formulae were found:"
    puts Formatter.columns(formulae_search_results)
    puts "To install one of them, run (for example):\n  brew install #{formulae_search_results.first}"
  end

  # Do not search taps if the formula name is qualified
  return if e.name.include?("/")

  ohai "Searching taps..."
  taps_search_results = search_taps(e.name)[:formulae]
  case taps_search_results.length
  when 0
    ofail "No formulae found in taps."
  when 1
    puts "This formula was found in a tap:"
    puts taps_search_results
    puts "To install it, run:\n  brew install #{taps_search_results.first}"
  else
    puts "These formulae were found in taps:"
    puts Formatter.columns(taps_search_results)
    puts "To install one of them, run (for example):\n  brew install #{taps_search_results.first}"
  end
end

.install_argsObject



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'brew/Library/Homebrew/cmd/install.rb', line 16

def install_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `install` [<options>] <formula>

      Install <formula>. Additional options specific to <formula> may be appended to the command.

      Unless `HOMEBREW_NO_INSTALL_CLEANUP` is set, `brew cleanup` will then be run for the
      installed formulae or, every 30 days, for all formulae.
    EOS
    switch :debug,
           description: "If brewing fails, open an interactive debugging session with access to IRB "\
                        "or a shell inside the temporary build directory."
    flag   "--env=",
           description: "If `std` is passed, use the standard build environment instead of superenv. "\
                        "If `super` is passed, use superenv even if the formula specifies the "\
                        "standard build environment."
    switch "--ignore-dependencies",
           description: "An unsupported Homebrew development flag to skip installing any dependencies of "\
                        "any kind. If the dependencies are not already present, the formula will have issues. "\
                        "If you're not developing Homebrew, consider adjusting your PATH rather than "\
                        "using this flag."
    switch "--only-dependencies",
           description: "Install the dependencies with specified options but do not install the "\
                        "formula itself."
    flag   "--cc=",
           description: "Attempt to compile using the specified <compiler>, which should be the "\
                        "name of the compiler's executable, e.g. `gcc-7` for GCC 7. "\
                        "In order to use LLVM's clang, specify `llvm_clang`. To use the "\
                        "Apple-provided clang, specify `clang`. This option will only accept "\
                        "compilers that are provided by Homebrew or bundled with macOS. "\
                        "Please do not file issues if you encounter errors while using this option."
    switch "-s", "--build-from-source",
           description: "Compile <formula> from source even if a bottle is provided. "\
                        "Dependencies will still be installed from bottles if they are available."
    switch "--force-bottle",
           description: "Install from a bottle if it exists for the current or newest version of "\
                        "macOS, even if it would not normally be used for installation."
    switch "--include-test",
           description: "Install testing dependencies required to run `brew test` <formula>."
    switch "--devel",
           description: "If <formula> defines it, install the development version."
    switch "--HEAD",
           description: "If <formula> defines it, install the HEAD version, aka. master, trunk, unstable."
    switch "--fetch-HEAD",
           description: "Fetch the upstream repository to detect if the HEAD installation of the "\
                        "formula is outdated. Otherwise, the repository's HEAD will only be checked for "\
                        "updates when a new stable or development version has been released."
    switch "--keep-tmp",
           description: "Retain the temporary files created during installation."
    switch "--build-bottle",
           description: "Prepare the formula for eventual bottling during installation, skipping any "\
                        "post-install steps."
    flag   "--bottle-arch=",
           depends_on:  "--build-bottle",
           description: "Optimise bottles for the specified architecture rather than the oldest "\
                        "architecture supported by the version of macOS the bottles are built on."
    switch :force,
           description: "Install without checking for previously installed keg-only or "\
                        "non-migrated versions."
    switch :verbose,
           description: "Print the verification and postinstall steps."
    switch "--display-times",
           env:         :display_install_times,
           description: "Print install times for each formula at the end of the run."
    switch "-i", "--interactive",
           description: "Download and patch <formula>, then open a shell. This allows the user to "\
                        "run `./configure --help` and otherwise determine how to turn the software "\
                        "package into a Homebrew package."
    switch "-g", "--git",
           description: "Create a Git repository, useful for creating patches to the software."
    conflicts "--ignore-dependencies", "--only-dependencies"
    conflicts "--devel", "--HEAD"
    conflicts "--build-from-source", "--build-bottle", "--force-bottle"
    formula_options
    min_named :formula
  end
end

.install_bundler!Object



89
90
91
92
93
94
95
96
97
98
# File 'brew/Library/Homebrew/utils/gems.rb', line 89

def install_bundler!
  require "rubygems"
  setup_gem_environment!(gem_home: Gem.user_dir, gem_bindir: gem_user_bindir)
  install_gem_setup_path!(
    "bundler",
    version:               HOMEBREW_BUNDLER_VERSION,
    executable:            "bundle",
    setup_gem_environment: false,
  )
end

.install_bundler_gemsObject



21
22
23
24
25
# File 'brew/Library/Homebrew/dev-cmd/install-bundler-gems.rb', line 21

def install_bundler_gems
  install_bundler_gems_args.parse

  Homebrew.install_bundler_gems!
end

.install_bundler_gems!Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'brew/Library/Homebrew/utils/gems.rb', line 100

def install_bundler_gems!
  install_bundler!

  ENV["BUNDLE_GEMFILE"] = "#{ENV["HOMEBREW_LIBRARY"]}/Homebrew/Gemfile"
  @bundle_installed ||= begin
    bundle = "#{find_in_path(:bundle)}/bundle"
    bundle_check_output = `#{bundle} check 2>&1`
    bundle_check_failed = !$CHILD_STATUS.success?

    # for some reason sometimes the exit code lies so check the output too.
    if bundle_check_failed || bundle_check_output.include?("Install missing gems")
      unless system bundle, "install"
        odie_if_defined <<~EOS
          failed to run `#{bundle} install`!
        EOS
      end
    else
      true
    end
  end

  setup_gem_environment!
end

.install_bundler_gems_argsObject



9
10
11
12
13
14
15
16
17
18
19
# File 'brew/Library/Homebrew/dev-cmd/install-bundler-gems.rb', line 9

def install_bundler_gems_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `install-bundler-gems`

      Install Homebrew's Bundler gems.
    EOS
    switch :debug
    max_named 0
  end
end

.install_core_tap_if_necessaryObject



140
141
142
143
144
145
146
147
148
149
150
# File 'brew/Library/Homebrew/cmd/update-report.rb', line 140

def install_core_tap_if_necessary
  return if ENV["HOMEBREW_UPDATE_TEST"]

  core_tap = CoreTap.instance
  return if core_tap.installed?

  CoreTap.ensure_installed!
  revision = core_tap.git_head
  ENV["HOMEBREW_UPDATE_BEFORE_HOMEBREW_HOMEBREW_CORE"] = revision
  ENV["HOMEBREW_UPDATE_AFTER_HOMEBREW_HOMEBREW_CORE"] = revision
end

.install_formula(f) ⇒ Object



317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'brew/Library/Homebrew/cmd/install.rb', line 317

def install_formula(f)
  f.print_tap_action
  build_options = f.build

  fi = FormulaInstaller.new(f)
  fi.options              = build_options.used_options
  fi.ignore_deps          = args.ignore_dependencies?
  fi.only_deps            = args.only_dependencies?
  fi.build_bottle         = args.build_bottle?
  fi.interactive          = args.interactive?
  fi.git                  = args.git?
  fi.prelude
  fi.install
  fi.finish
rescue FormulaInstallationAlreadyAttemptedError  # We already attempted to install f as part of the dependency tree of
  # another formula. In that case, don't generate an error, just move on.

  nil
rescue CannotInstallFormulaError => e
  ofail e.message
end

.install_gem!(name, version: nil, setup_gem_environment: true) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
# File 'brew/Library/Homebrew/utils/gems.rb', line 60

def install_gem!(name, version: nil, setup_gem_environment: true)
  setup_gem_environment! if setup_gem_environment
  return unless Gem::Specification.find_all_by_name(name, version).empty?

  # Shell out to `gem` to avoid RubyGems requires for e.g. loading JSON.
  ohai_if_defined "Installing '#{name}' gem"
  install_args = %W[--no-document #{name}]
  install_args << "--version" << version if version
  return if system "#{ruby_bindir}/gem", "install", *install_args

  odie_if_defined "failed to install the '#{name}' gem."
end

.install_gem_setup_path!(name, version: nil, executable: name, setup_gem_environment: true) ⇒ Object



73
74
75
76
77
78
79
80
81
# File 'brew/Library/Homebrew/utils/gems.rb', line 73

def install_gem_setup_path!(name, version: nil, executable: name, setup_gem_environment: true)
  install_gem!(name, version: version, setup_gem_environment: setup_gem_environment)
  return if find_in_path(executable)

  odie_if_defined <<~EOS
    the '#{name}' gem is installed but couldn't find '#{executable}' in the PATH:
      #{ENV["PATH"]}
  EOS
end

.irbObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'brew/Library/Homebrew/dev-cmd/irb.rb', line 35

def irb  # work around IRB modifying ARGV.

  irb_args.parse(ARGV.dup)

  if args.examples?
    puts "'v8'.f # => instance of the v8 formula"
    puts ":hub.f.installed?"
    puts ":lua.f.methods - 1.methods"
    puts ":mpd.f.recursive_dependencies.reject(&:installed?)"
    return
  end

  if args.pry?
    Homebrew.install_gem_setup_path! "pry"
    require "pry"
    Pry.config.prompt_name = "brew"
  else
    require "irb"
  end

  require "formula"
  require "keg"
  require "cask/all"

  ohai "Interactive Homebrew Shell"
  puts "Example commands available with: brew irb --examples"
  if args.pry?
    Pry.start
  else
    IRB.start
  end
end

.irb_argsObject



20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'brew/Library/Homebrew/dev-cmd/irb.rb', line 20

def irb_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `irb` [<options>]

      Enter the interactive Homebrew Ruby shell.
    EOS
    switch "--examples",
           description: "Show several examples."
    switch "--pry",
           env:         :pry,
           description: "Use Pry instead of IRB. Implied if `HOMEBREW_PRY` is set."
  end
end

.keg_contain?(string, keg, ignores, formula_and_runtime_deps_names = nil) ⇒ Boolean

Returns:

  • (Boolean)


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'brew/Library/Homebrew/dev-cmd/bottle.rb', line 106

def keg_contain?(string, keg, ignores, formula_and_runtime_deps_names = nil)
  @put_string_exists_header, @put_filenames = nil

  print_filename = lambda do |str, filename|
    unless @put_string_exists_header
      opoo "String '#{str}' still exists in these files:"
      @put_string_exists_header = true
    end

    @put_filenames ||= []

    return if @put_filenames.include?(filename)

    puts Formatter.error(filename.to_s)
    @put_filenames << filename
  end

  result = false

  keg.each_unique_file_matching(string) do |file|
    next if Metafiles::EXTENSIONS.include?(file.extname) # Skip document files.

    linked_libraries = Keg.file_linked_libraries(file, string)
    result ||= !linked_libraries.empty?

    if args.verbose?
      print_filename.call(string, file) unless linked_libraries.empty?
      linked_libraries.each do |lib|
        puts " #{Tty.bold}-->#{Tty.reset} links to #{lib}"
      end
    end

    text_matches = []

    # Use strings to search through the file for each string
    Utils.popen_read("strings", "-t", "x", "-", file.to_s) do |io|
      until io.eof?
        str = io.readline.chomp
        next if ignores.any? { |i| i =~ str }
        next unless str.include? string

        offset, match = str.split(" ", 2)
        next if linked_libraries.include? match # Don't bother reporting a string if it was found by otool

        # Do not report matches to files that do not exist.
        next unless File.exist? match

        # Do not report matches to build dependencies.
        if formula_and_runtime_deps_names.present?
          begin
            keg_name = Keg.for(Pathname.new(match)).name
            next unless formula_and_runtime_deps_names.include? keg_name
          rescue NotAKegError
            nil
          end
        end

        result = true
        text_matches << [match, offset]
      end
    end

    next unless args.verbose? && !text_matches.empty?

    print_filename.call(string, file)
    text_matches.first(MAXIMUM_STRING_MATCHES).each do |match, offset|
      puts " #{Tty.bold}-->#{Tty.reset} match '#{match}' at offset #{Tty.bold}0x#{offset}#{Tty.reset}"
    end

    if text_matches.size > MAXIMUM_STRING_MATCHES
      puts "Only the first #{MAXIMUM_STRING_MATCHES} matches were output."
    end
  end

  keg_contain_absolute_symlink_starting_with?(string, keg) || result
end

Returns:

  • (Boolean)


183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'brew/Library/Homebrew/dev-cmd/bottle.rb', line 183

def keg_contain_absolute_symlink_starting_with?(string, keg)
  absolute_symlinks_start_with_string = []
  keg.find do |pn|
    next unless pn.symlink? && (link = pn.readlink).absolute?

    absolute_symlinks_start_with_string << pn if link.to_s.start_with?(string)
  end

  if args.verbose?
    unless absolute_symlinks_start_with_string.empty?
      opoo "Absolute symlink starting with #{string}:"
      absolute_symlinks_start_with_string.each do |pn|
        puts "  #{pn} -> #{pn.resolved_path}"
      end
    end
  end

  !absolute_symlinks_start_with_string.empty?
end

.leavesObject



22
23
24
25
26
27
28
29
# File 'brew/Library/Homebrew/cmd/leaves.rb', line 22

def leaves
  leaves_args.parse

  installed = Formula.installed.sort
  deps_of_installed = installed.flat_map(&:runtime_formula_dependencies)
  leaves = installed.map(&:full_name) - deps_of_installed.map(&:full_name)
  leaves.each(&method(:puts))
end

.leaves_argsObject



10
11
12
13
14
15
16
17
18
19
20
# File 'brew/Library/Homebrew/cmd/leaves.rb', line 10

def leaves_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `leaves`

      List installed formulae that are not dependencies of another installed formula.
    EOS
    switch :debug
    max_named 0
  end
end


32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'brew/Library/Homebrew/cmd/link.rb', line 32

def link
  link_args.parse

  mode = OpenStruct.new

  mode.overwrite = true if args.overwrite?
  mode.dry_run = true if args.dry_run?

  args.kegs.each do |keg|
    keg_only = Formulary.keg_only?(keg.rack)

    if keg.linked?
      opoo "Already linked: #{keg}"
      name_and_flag = if keg_only
        "--force #{keg.name}"
      else
        keg.name
      end
      puts <<~EOS
        To relink:
          brew unlink #{keg.name} && brew link #{name_and_flag}
      EOS
      next
    end

    if mode.dry_run
      if mode.overwrite
        puts "Would remove:"
      else
        puts "Would link:"
      end
      keg.link(mode)
      puts_keg_only_path_message(keg) if keg_only
      next
    end

    if keg_only
      if Homebrew.default_prefix?
        f = keg.to_formula
        if f.keg_only_reason.reason == :provided_by_macos
          caveats = Caveats.new(f)
          opoo <<~EOS
            Refusing to link macOS-provided software: #{keg.name}
            #{caveats.keg_only_text(skip_reason: true).strip}
          EOS
          next
        end
      end

      unless args.force?
        opoo "#{keg.name} is keg-only and must be linked with --force"
        puts_keg_only_path_message(keg)
        next
      end
    end

    keg.lock do
      print "Linking #{keg}... "
      puts if args.verbose?

      begin
        n = keg.link(mode)
      rescue Keg::LinkError
        puts
        raise
      else
        puts "#{n} symlinks created"
      end

      puts_keg_only_path_message(keg) if keg_only && !ARGV.homebrew_developer?
    end
  end
end


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'brew/Library/Homebrew/cmd/link.rb', line 10

def link_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `link`, `ln` [<options>] <formula>

      Symlink all of <formula>'s installed files into Homebrew's prefix. This
      is done automatically when you install formulae but can be useful for DIY
      installations.
    EOS
    switch "--overwrite",
           description: "Delete files that already exist in the prefix while linking."
    switch "-n", "--dry-run",
           description: "List files which would be linked or deleted by "\
                        "`brew link --overwrite` without actually linking or deleting any files."
    switch :force,
           description: "Allow keg-only formulae to be linked."
    switch :verbose
    switch :debug
    min_named :keg
  end
end


152
153
154
155
156
157
158
159
160
161
162
# File 'brew/Library/Homebrew/cmd/update-report.rb', line 152

def link_completions_manpages_and_docs(repository = HOMEBREW_REPOSITORY)
  command = "brew update"
  Utils::Link.link_completions(repository, command)
  Utils::Link.link_manpages(repository, command)
  Utils::Link.link_docs(repository, command)
rescue => e
  ofail <<~EOS
    Failed to link all completions, docs and manpages:
      #{e}
  EOS
end

.linkageObject



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'brew/Library/Homebrew/dev-cmd/linkage.rb', line 32

def linkage
  linkage_args.parse

  CacheStoreDatabase.use(:linkage) do |db|
    kegs = if args.kegs.empty?
      Formula.installed.map(&:opt_or_installed_prefix_keg).reject(&:nil?)
    else
      args.kegs
    end
    kegs.each do |keg|
      ohai "Checking #{keg.name} linkage" if kegs.size > 1

      result = LinkageChecker.new(keg, cache_db: db)

      if args.test?
        result.display_test_output
        Homebrew.failed = true if result.broken_library_linkage?
      elsif args.reverse?
        result.display_reverse_output
      else
        result.display_normal_output
      end
    end
  end
end

.linkage_argsObject



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'brew/Library/Homebrew/dev-cmd/linkage.rb', line 10

def linkage_args
  Homebrew::CLI::Parser.new do
    usage_banner <<~EOS
      `linkage` [<options>] [<formula>]

      Check the library links from the given <formula> kegs. If no <formula> are
      provided, check all kegs. Raises an error if run on uninstalled formulae.
    EOS
    switch "--test",
           description: "Show only missing libraries and exit with a non-zero status if any missing "\
                        "libraries are found."
    switch "--reverse",
           description: "For every library that a keg references, print its dylib path followed by the "\
                        "binaries that link to it."
    switch "--cached",
           description: "Print the cached linkage values stored in `HOMEBREW_CACHE`, set by a previous "\
                        "`brew linkage` run."
    switch :verbose
    switch :debug
  end
end

.listObject



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'brew/Library/Homebrew/cmd/list.rb', line 50

def list
  list_args.parse

  return list_unbrewed if args.unbrewed?

  # Unbrewed uses the PREFIX, which will exist
  # Things below use the CELLAR, which doesn't until the first formula is installed.
  unless HOMEBREW_CELLAR.exist?
    raise NoSuchKegError, Hombrew.args.named.first if args.named.present?

    return
  end

  if args.pinned? || args.versions?
    filtered_list
  elsif args.no_named?
    if args.full_name?
      full_names = Formula