Module: Homebrew::Install Private

Included in:
Cmd::GistLogs
Defined in:
install.rb

Overview

This module is part of a private API. This module may only be used in the Homebrew/brew repository. Third parties should avoid using this module if possible, as it may be removed or changed without warning.

Helper module for performing (pre-)install checks.

Class Method Summary collapse

Class Method Details

.ask_casks(casks) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:



458
459
460
461
462
463
464
465
# File 'install.rb', line 458

def ask_casks(casks)
  return if casks.empty?

  puts "#{::Utils.pluralize("Cask", casks.count, plural: "s")} \
(#{casks.count}): #{casks.join(", ")}\n\n"

  ask_input
end

.ask_formulae(formulae_installer, dependants, args:) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

If asking the user is enabled, show dependency and size information.

Parameters:



437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'install.rb', line 437

def ask_formulae(formulae_installer, dependants, args:)
  return if formulae_installer.empty?

  formulae = collect_dependencies(formulae_installer, dependants)

  ohai "Looking for bottles..."

  sizes = compute_total_sizes(formulae, debug: args.debug?)

  puts "#{::Utils.pluralize("Formula", formulae.count, plural: "e")} \
(#{formulae.count}): #{formulae.join(", ")}\n\n"
  puts "Download Size: #{disk_usage_readable(sizes.fetch(:download))}"
  puts "Install Size:  #{disk_usage_readable(sizes.fetch(:installed))}"
  if (net_install_size = sizes[:net]) && net_install_size != 0
    puts "Net Install Size: #{disk_usage_readable(net_install_size)}"
  end

  ask_input
end

.check_cc_argv(cc) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:



25
26
27
28
29
30
31
32
33
34
# File 'install.rb', line 25

def check_cc_argv(cc)
  return unless cc

  @checks ||= Diagnostic::Checks.new
  opoo <<~EOS
    You passed `--cc=#{cc}`.

    #{@checks.support_tier_message(tier: 3)}
  EOS
end

.check_prefixvoid

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'install.rb', line 46

def check_prefix
  if (Hardware::CPU.intel? || Hardware::CPU.in_rosetta2?) &&
     HOMEBREW_PREFIX.to_s == HOMEBREW_MACOS_ARM_DEFAULT_PREFIX
    if Hardware::CPU.in_rosetta2?
      odie <<~EOS
        Cannot install under Rosetta 2 in ARM default prefix (#{HOMEBREW_PREFIX})!
        To rerun under ARM use:
            arch -arm64 brew install ...
        To install under x86_64, install Homebrew into #{HOMEBREW_DEFAULT_PREFIX}.
      EOS
    else
      odie "Cannot install on Intel processor in ARM default prefix (#{HOMEBREW_PREFIX})!"
    end
  elsif Hardware::CPU.arm? && HOMEBREW_PREFIX.to_s == HOMEBREW_DEFAULT_PREFIX
    odie <<~EOS
      Cannot install in Homebrew on ARM processor in Intel default prefix (#{HOMEBREW_PREFIX})!
      Please create a new installation in #{HOMEBREW_MACOS_ARM_DEFAULT_PREFIX} using one of the
      "Alternative Installs" from:
        #{Formatter.url("https://docs.brew.sh/Installation")}
      You can migrate your previously installed formula list with:
        brew bundle dump
    EOS
  end
end

.fetch_formulae(formula_installers) ⇒ Array<FormulaInstaller>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



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
# File 'install.rb', line 330

def fetch_formulae(formula_installers)
  formulae_names_to_install = formula_installers.map { |fi| fi.formula.name }
  return formula_installers if formulae_names_to_install.empty?

  formula_sentence = formulae_names_to_install.map { |name| Formatter.identifier(name) }.to_sentence
  oh1 "Fetching downloads for: #{formula_sentence}", truncate: false
  if EnvConfig.download_concurrency > 1
    download_queue = Homebrew::DownloadQueue.new(pour: true)
    formula_installers.each do |fi|
      fi.download_queue = download_queue
    end
  end

  valid_formula_installers = formula_installers.dup

  begin
    [:prelude_fetch, :prelude, :fetch].each do |step|
      valid_formula_installers.select! do |fi|
        fi.public_send(step)
        true
      rescue CannotInstallFormulaError => e
        ofail e.message
        false
      rescue UnsatisfiedRequirements, DownloadError, ChecksumMismatchError => e
        ofail "#{fi.formula}: #{e}"
        false
      end
      download_queue&.fetch
    end
  ensure
    download_queue&.shutdown
  end

  valid_formula_installers
end

.formula_installers(formulae_to_install, installed_on_request: true, installed_as_dependency: false, build_bottle: false, force_bottle: false, bottle_arch: nil, ignore_deps: false, only_deps: false, include_test_formulae: [], build_from_source_formulae: [], cc: nil, git: false, interactive: false, keep_tmp: false, debug_symbols: false, force: false, overwrite: false, debug: false, quiet: false, verbose: false, dry_run: false, skip_post_install: false, skip_link: false) ⇒ Array<FormulaInstaller>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

  • formulae_to_install (Array<Formula>)
  • installed_on_request (Boolean) (defaults to: true)
  • installed_as_dependency (Boolean) (defaults to: false)
  • build_bottle (Boolean) (defaults to: false)
  • force_bottle (Boolean) (defaults to: false)
  • bottle_arch (String, nil) (defaults to: nil)
  • ignore_deps (Boolean) (defaults to: false)
  • only_deps (Boolean) (defaults to: false)
  • include_test_formulae (Array<String>) (defaults to: [])
  • build_from_source_formulae (Array<String>) (defaults to: [])
  • cc (String, nil) (defaults to: nil)
  • git (Boolean) (defaults to: false)
  • interactive (Boolean) (defaults to: false)
  • keep_tmp (Boolean) (defaults to: false)
  • debug_symbols (Boolean) (defaults to: false)
  • force (Boolean) (defaults to: false)
  • overwrite (Boolean) (defaults to: false)
  • debug (Boolean) (defaults to: false)
  • quiet (Boolean) (defaults to: false)
  • verbose (Boolean) (defaults to: false)
  • dry_run (Boolean) (defaults to: false)
  • skip_post_install (Boolean) (defaults to: false)
  • skip_link (Boolean) (defaults to: false)

Returns:



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
# File 'install.rb', line 272

def formula_installers(
  formulae_to_install,
  installed_on_request: true,
  installed_as_dependency: false,
  build_bottle: false,
  force_bottle: false,
  bottle_arch: nil,
  ignore_deps: false,
  only_deps: false,
  include_test_formulae: [],
  build_from_source_formulae: [],
  cc: nil,
  git: false,
  interactive: false,
  keep_tmp: false,
  debug_symbols: false,
  force: false,
  overwrite: false,
  debug: false,
  quiet: false,
  verbose: false,
  dry_run: false,
  skip_post_install: false,
  skip_link: false
)
  formulae_to_install.filter_map do |formula|
    Migrator.migrate_if_needed(formula, force:, dry_run:)
    build_options = formula.build

    FormulaInstaller.new(
      formula,
      options:                    build_options.used_options,
      installed_on_request:,
      installed_as_dependency:,
      build_bottle:,
      force_bottle:,
      bottle_arch:,
      ignore_deps:,
      only_deps:,
      include_test_formulae:,
      build_from_source_formulae:,
      cc:,
      git:,
      interactive:,
      keep_tmp:,
      debug_symbols:,
      force:,
      overwrite:,
      debug:,
      quiet:,
      verbose:,
      skip_post_install:,
      skip_link:,
    )
  end
end

.global_post_installvoid

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



43
# File 'install.rb', line 43

def global_post_install; end

.install_formula(formula_installer, upgrade:) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:



468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'install.rb', line 468

def install_formula(formula_installer, upgrade:)
  formula = formula_installer.formula

  formula_installer.check_installation_already_attempted

  if upgrade
    Upgrade.print_upgrade_message(formula, formula_installer.options)

    kegs = Upgrade.outdated_kegs(formula)
    linked_kegs = kegs.select(&:linked?)
  else
    formula.print_tap_action
  end

  # first we unlink the currently active keg for this formula otherwise it is
  # possible for the existing build to interfere with the build we are about to
  # do! Seriously, it happens!
  kegs.each(&:unlink) if kegs.present?

  formula_installer.install
  formula_installer.finish
rescue FormulaInstallationAlreadyAttemptedError
  # We already attempted to upgrade f as part of the dependency tree of
  # another formula. In that case, don't generate an error, just move on.
  nil
ensure
  # restore previous installation state if build failed
  begin
    linked_kegs&.each(&:link) unless formula&.latest_version_installed?
  rescue
    nil
  end
end

.install_formula?(formula, head: false, fetch_head: false, only_dependencies: false, force: false, quiet: false, skip_link: false, overwrite: false) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

  • formula (Formula)
  • head (Boolean) (defaults to: false)
  • fetch_head (Boolean) (defaults to: false)
  • only_dependencies (Boolean) (defaults to: false)
  • force (Boolean) (defaults to: false)
  • quiet (Boolean) (defaults to: false)
  • skip_link (Boolean) (defaults to: false)
  • overwrite (Boolean) (defaults to: false)

Returns:

  • (Boolean)


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
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
# File 'install.rb', line 76

def install_formula?(
  formula,
  head: false,
  fetch_head: false,
  only_dependencies: false,
  force: false,
  quiet: false,
  skip_link: false,
  overwrite: false
)
  # head-only without --HEAD is an error
  if !head && formula.stable.nil?
    odie <<~EOS
      #{formula.full_name} is a head-only formula.
      To install it, run:
        brew install --HEAD #{formula.full_name}
    EOS
  end

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

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

  # Check if the installed formula is from a different tap
  if formula.any_version_installed? &&
     (current_tap_name = formula.tap&.name.presence) &&
     (installed_keg_tab = formula.any_installed_keg&.tab.presence) &&
     (installed_tap_name = installed_keg_tab.tap&.name.presence) &&
     installed_tap_name != current_tap_name
    odie <<~EOS
      #{formula.name} was installed from the #{Formatter.identifier(installed_tap_name)} tap
      but you are trying to install it from the #{Formatter.identifier(current_tap_name)} tap.
      Formulae with the same name from different taps cannot be installed at the same time.

      To install this version, you must first uninstall the existing formula:
        brew uninstall #{formula.name}
      Then you can install the desired version:
        brew install #{formula.full_name}
    EOS
  end

  if formula.keg_only? && formula.any_version_installed? && formula.optlinked? && !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 the --force switch is passed.
    if formula.outdated?
      if !Homebrew::EnvConfig.no_install_upgrade? && !formula.pinned?
        name = formula.name
        version = formula.linked_version
        puts "#{name} #{version} is already installed but outdated (so it will be upgraded)."
        return true
      end

      unpin_cmd_if_needed = ("brew unpin #{formula.full_name} && " if formula.pinned?)
      optlinked_version = Keg.for(formula.opt_prefix).version
      onoe <<~EOS
        #{formula.full_name} #{optlinked_version} is already installed.
        To upgrade to #{formula.version}, run:
          #{unpin_cmd_if_needed}brew upgrade #{formula.full_name}
      EOS
    elsif only_dependencies
      return true
    elsif !quiet
      opoo <<~EOS
        #{formula.full_name} #{formula.pkg_version} is already installed and up-to-date.
        To reinstall #{formula.pkg_version}, run:
          brew reinstall #{formula.name}
      EOS
    end
  elsif (head && new_head_installed) || prefix_installed
    # After we're sure the --force switch was passed for linking to opt
    # keg-only we need to be sure that the version we're attempting to
    # install is not already installed.

    installed_version = if head
      formula.latest_head_version
    else
      formula.pkg_version
    end

    msg = "#{formula.full_name} #{installed_version} is already installed"
    linked_not_equals_installed = formula.linked_version != installed_version
    if formula.linked? && linked_not_equals_installed
      msg = if quiet
        nil
      else
        <<~EOS
          #{msg}.
          The currently linked version is: #{formula.linked_version}
        EOS
      end
    elsif only_dependencies || (!formula.linked? && overwrite)
      msg = nil
      return true
    elsif !formula.linked? || formula.keg_only?
      msg = <<~EOS
        #{msg}, it's just not linked.
        To link this version, run:
          brew link #{formula}
      EOS
    else
      msg = if quiet
        nil
      else
        <<~EOS
          #{msg} and up-to-date.
          To reinstall #{formula.pkg_version}, run:
            brew reinstall #{formula.name}
        EOS
      end
    end
    opoo msg if msg
  elsif !formula.any_version_installed? && (old_formula = formula.old_installed_formulae.first)
    msg = "#{old_formula.full_name} #{old_formula.any_installed_version} already installed"
    msg = if !old_formula.linked? && !old_formula.keg_only?
      <<~EOS
        #{msg}, it's just not linked.
        To link this version, run:
          brew link #{old_formula.full_name}
      EOS
    elsif quiet
      nil
    else
      "#{msg}."
    end
    opoo msg if msg
  elsif formula.migration_needed? && !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
      #{formula.oldnames_to_migrate.first} is already installed, it's just not migrated.
      To migrate this formula, run:
        brew migrate #{formula}
      Or to force-install it, run:
        brew install #{formula} --force
    EOS
  elsif formula.linked?
    message = "#{formula.name} #{formula.linked_version} is already installed"
    if formula.outdated? && !head
      if !Homebrew::EnvConfig.no_install_upgrade? && !formula.pinned?
        puts "#{message} but outdated (so it will be upgraded)."
        return true
      end

      unpin_cmd_if_needed = ("brew unpin #{formula.full_name} && " if formula.pinned?)
      onoe <<~EOS
        #{message}
        To upgrade to #{formula.pkg_version}, run:
          #{unpin_cmd_if_needed}brew upgrade #{formula.full_name}
      EOS
    elsif only_dependencies || skip_link
      return true
    else
      onoe <<~EOS
        #{message}
        To install #{formula.pkg_version}, first run:
          brew unlink #{formula.name}
      EOS
    end
  else
    # If none of the above is true and the formula is linked, then
    # FormulaInstaller will handle this case.
    return true
  end

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

  keg = Keg.new(formula.opt_prefix.resolved_path)
  tab = keg.tab
  unless tab.installed_on_request
    tab.installed_on_request = true
    tab.write
  end

  false
end

.install_formulae(formula_installers, installed_on_request: true, installed_as_dependency: false, build_bottle: false, force_bottle: false, bottle_arch: nil, ignore_deps: false, only_deps: false, include_test_formulae: [], build_from_source_formulae: [], cc: nil, git: false, interactive: false, keep_tmp: false, debug_symbols: false, force: false, overwrite: false, debug: false, quiet: false, verbose: false, dry_run: false, skip_post_install: false, skip_link: false) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:

  • formula_installers (Array<FormulaInstaller>)
  • installed_on_request (Boolean) (defaults to: true)
  • installed_as_dependency (Boolean) (defaults to: false)
  • build_bottle (Boolean) (defaults to: false)
  • force_bottle (Boolean) (defaults to: false)
  • bottle_arch (String, nil) (defaults to: nil)
  • ignore_deps (Boolean) (defaults to: false)
  • only_deps (Boolean) (defaults to: false)
  • include_test_formulae (Array<String>) (defaults to: [])
  • build_from_source_formulae (Array<String>) (defaults to: [])
  • cc (String, nil) (defaults to: nil)
  • git (Boolean) (defaults to: false)
  • interactive (Boolean) (defaults to: false)
  • keep_tmp (Boolean) (defaults to: false)
  • debug_symbols (Boolean) (defaults to: false)
  • force (Boolean) (defaults to: false)
  • overwrite (Boolean) (defaults to: false)
  • debug (Boolean) (defaults to: false)
  • quiet (Boolean) (defaults to: false)
  • verbose (Boolean) (defaults to: false)
  • dry_run (Boolean) (defaults to: false)
  • skip_post_install (Boolean) (defaults to: false)
  • skip_link (Boolean) (defaults to: false)


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
# File 'install.rb', line 376

def install_formulae(
  formula_installers,
  installed_on_request: true,
  installed_as_dependency: false,
  build_bottle: false,
  force_bottle: false,
  bottle_arch: nil,
  ignore_deps: false,
  only_deps: false,
  include_test_formulae: [],
  build_from_source_formulae: [],
  cc: nil,
  git: false,
  interactive: false,
  keep_tmp: false,
  debug_symbols: false,
  force: false,
  overwrite: false,
  debug: false,
  quiet: false,
  verbose: false,
  dry_run: false,
  skip_post_install: false,
  skip_link: false
)
  formulae_names_to_install = formula_installers.map { |fi| fi.formula.name }
  return if formulae_names_to_install.empty?

  if dry_run
    ohai "Would install #{Utils.pluralize("formula", formulae_names_to_install.count,
                                          plural: "e", include_count: true)}:"
    puts formulae_names_to_install.join(" ")

    formula_installers.each do |fi|
      print_dry_run_dependencies(fi.formula, fi.compute_dependencies, &:name)
    end
    return
  end

  valid_formula_installers = fetch_formulae(formula_installers)

  valid_formula_installers.each do |fi|
    formula = fi.formula
    upgrade = formula.linked? && formula.outdated? && !formula.head? && !Homebrew::EnvConfig.no_install_upgrade?
    install_formula(fi, upgrade:)
    Cleanup.install_formula_clean!(formula)
  end
end

.perform_build_from_source_checks(all_fatal: false) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:

  • all_fatal (Boolean) (defaults to: false)


37
38
39
40
# File 'install.rb', line 37

def perform_build_from_source_checks(all_fatal: false)
  Diagnostic.checks(:fatal_build_from_source_checks)
  Diagnostic.checks(:build_from_source_checks, fatal: all_fatal)
end

.perform_preinstall_checks_once(all_fatal: false) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:

  • all_fatal (Boolean) (defaults to: false)


16
17
18
19
20
21
22
# File 'install.rb', line 16

def perform_preinstall_checks_once(all_fatal: false)
  @perform_preinstall_checks_once ||= {}
  @perform_preinstall_checks_once[all_fatal] ||= begin
    perform_preinstall_checks(all_fatal:)
    true
  end
end

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:



426
427
428
429
430
431
432
433
# File 'install.rb', line 426

def print_dry_run_dependencies(formula, dependencies)
  return if dependencies.empty?

  ohai "Would install #{Utils.pluralize("dependenc", dependencies.count, plural: "ies", singular: "y",
                                      include_count: true)} for #{formula.name}:"
  formula_names = dependencies.map { |(dep, _options)| yield dep.to_formula }
  puts formula_names.join(" ")
end