Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Library/Homebrew/cli/args.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,11 @@ def only_formula_or_cask
def os_arch_combinations
skip_invalid_combinations = false

oses = case (os_sym = @table[:os]&.to_sym)
# `--all-platforms` is equivalent to `--os=all --arch=all`.
all_platforms = @table[:all_platforms?]

os_sym = all_platforms ? :all : @table[:os]&.to_sym
oses = case os_sym
when nil
[SimulateSystem.current_os]
when :all
Expand All @@ -139,7 +143,8 @@ def os_arch_combinations
[os_sym]
end

arches = case (arch_sym = @table[:arch]&.to_sym)
arch_sym = all_platforms ? :all : @table[:arch]&.to_sym
arches = case arch_sym
when nil
[SimulateSystem.current_arch]
when :all
Expand Down
98 changes: 74 additions & 24 deletions Library/Homebrew/cmd/fetch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require "abstract_command"
require "formula"
require "fetch"
require "cask/config"
require "cask/download"
require "download_queue"

Expand All @@ -25,6 +26,9 @@ class FetchCmd < AbstractCommand
flag "--arch=",
description: "Download for the given CPU architecture. " \
"(Pass `all` to download for all architectures.)"
switch "--all-platforms",
description: "Download for every supported operating system and architecture, plus each " \
"language for <cask>s, fetching each distinct URL once."
flag "--bottle-tag=",
description: "Download a bottle for given tag."
switch "--HEAD",
Expand Down Expand Up @@ -65,6 +69,9 @@ class FetchCmd < AbstractCommand
conflicts "--formula", "--cask"
conflicts "--os", "--bottle-tag"
conflicts "--arch", "--bottle-tag"
conflicts "--all-platforms", "--os"
conflicts "--all-platforms", "--arch"
conflicts "--all-platforms", "--bottle-tag"

named_args [:formula, :cask], min: 1
end
Expand Down Expand Up @@ -154,30 +161,7 @@ def run
end
end
when Cask::Cask
cask = formula_or_cask
ref = cask.loaded_from_api? ? cask.full_name : cask.sourcefile_path
odie "unexpected nil cask sourcefile_path" unless ref

os_arch_combinations.each do |os, arch|
SimulateSystem.with(os:, arch:) do
cask = Cask::CaskLoader.load(ref)

if cask.url.nil? || cask.sha256.nil?
opoo "Cask #{cask} is not supported on os #{os} and arch #{arch}"
next
end

quarantine = args.quarantine?
quarantine = true if quarantine.nil?

download = Cask::Download.new(
cask,
quarantine:,
require_sha: Homebrew::EnvConfig.cask_opts_require_sha?,
)
download_queue.enqueue(download)
end
end
cask_downloads(formula_or_cask).each { |download| download_queue.enqueue(download) }
else
odie "Invalid formula or cask: #{formula_or_cask}"
end
Expand All @@ -190,6 +174,72 @@ def run

private

sig { params(cask: Cask::Cask).returns(T::Array[Cask::Download]) }
def cask_downloads(cask)
ref = cask.loaded_from_api? ? cask.full_name : cask.sourcefile_path
odie "unexpected nil cask sourcefile_path" unless ref

quarantine = args.quarantine?
quarantine = true if quarantine.nil?

if args.all_platforms? && cask.loaded_from_api?
opoo "Cask #{cask} was loaded from the API; cannot fetch all operating system and " \
"architecture variants. Set `HOMEBREW_NO_INSTALL_FROM_API=1` to fetch them all."
end

# With `--all-platforms`, a cask without `on_system` blocks resolves
# identically everywhere, so one combination covers the whole matrix.
cask_combinations = args.os_arch_combinations
cask_combinations = cask_combinations.first(1) if args.all_platforms? && !cask.on_system_blocks_exist?

downloads = T.let([], T::Array[Cask::Download])
enqueued_urls = Set.new

cask_combinations.each do |os, arch|
SimulateSystem.with(os:, arch:) do
loaded_cask = begin
Cask::CaskLoader.load(ref)
rescue Cask::CaskInvalidError, Cask::CaskUnreadableError
raise unless cask.on_system_blocks_exist?
end
if loaded_cask.nil?
opoo "Cask #{cask} is not supported on os #{os} and arch #{arch}"
next
end

languages = (loaded_cask.languages if args.all_platforms?)
languages = [nil] if languages.blank?

languages.each do |language|
localized_cask = loaded_cask
if language
# Reload per language: `Cask::Download` reads `sha256`/`url`
# lazily, so each download needs its own cask instance.
localized_cask = Cask::CaskLoader.load(ref)
localized_cask.config = localized_cask.config.merge(
Cask::Config.new(explicit: { languages: [language] }),
)
end
Comment thread
p-linnane marked this conversation as resolved.

if localized_cask.url.nil? || localized_cask.sha256.nil?
opoo "Cask #{cask} is not supported on os #{os} and arch #{arch}"
next
end

next unless enqueued_urls.add?(localized_cask.url.to_s)

downloads << Cask::Download.new(
localized_cask,
quarantine:,
require_sha: Homebrew::EnvConfig.cask_opts_require_sha?,
)
end
end
end

downloads
end

sig { returns(Integer) }
def retries
@retries ||= T.let(args.retry? ? FETCH_MAX_TRIES : 1, T.nilable(Integer))
Expand Down
3 changes: 3 additions & 0 deletions Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/fetch_cmd.rbi

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions Library/Homebrew/test/cmd/fetch_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,19 @@
expect(HOMEBREW_CACHE/"testball2--0.1.tbz").to exist
expect((HOMEBREW_CACHE/"downloads").glob("*--caffeine.zip")).not_to be_empty
end

describe "#cask_downloads", :cask do
it "collects one download per distinct URL across all platforms" do
cmd = Homebrew::Cmd::FetchCmd.new(["--cask", "--all-platforms", "sha256-os"])
basenames = cmd.send(:cask_downloads, Cask::CaskLoader.load("sha256-os"))
.map { |download| File.basename(download.url.to_s) }
expect(basenames).to contain_exactly("caffeine-arm-darwin.zip", "caffeine-intel-darwin.zip",
"caffeine-arm-linux.zip", "caffeine-intel-linux.zip")
end

it "collapses to a single download for a cask without on_system blocks" do
cmd = Homebrew::Cmd::FetchCmd.new(["--cask", "--all-platforms", "local-caffeine"])
expect(cmd.send(:cask_downloads, Cask::CaskLoader.load("local-caffeine")).length).to eq(1)
end
end
end
1 change: 1 addition & 0 deletions completions/bash/brew
Original file line number Diff line number Diff line change
Expand Up @@ -1453,6 +1453,7 @@ _brew_fetch() {
-*)
__brewcomp "
--HEAD
--all-platforms
--arch
--bottle-tag
--build-bottle
Expand Down
1 change: 1 addition & 0 deletions completions/fish/brew.fish
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,7 @@ __fish_brew_complete_arg 'extract' -a '(__fish_brew_suggest_taps_installed)'

__fish_brew_complete_cmd 'fetch' 'Download a bottle (if available) or source packages for formulae and binaries for casks'
__fish_brew_complete_arg 'fetch' -l HEAD -d 'Fetch HEAD version instead of stable version'
__fish_brew_complete_arg 'fetch' -l all-platforms -d 'Download for every supported operating system and architecture, plus each language for casks, fetching each distinct URL once'
__fish_brew_complete_arg 'fetch' -l arch -d 'Download for the given CPU architecture. (Pass `all` to download for all architectures.)'
__fish_brew_complete_arg 'fetch' -l bottle-tag -d 'Download a bottle for given tag'
__fish_brew_complete_arg 'fetch' -l build-bottle -d 'Download source packages (for eventual bottling) rather than a bottle'
Expand Down
7 changes: 4 additions & 3 deletions completions/zsh/_brew
Original file line number Diff line number Diff line change
Expand Up @@ -1274,16 +1274,17 @@ _brew_extract() {
_brew_fetch() {
_arguments \
'(--cask)--HEAD[Fetch HEAD version instead of stable version]' \
'(--bottle-tag)--arch[Download for the given CPU architecture. (Pass `all` to download for all architectures.)]' \
'(--build-from-source --build-bottle --force-bottle --cask --os --arch)--bottle-tag[Download a bottle for given tag]' \
'(--os --arch --bottle-tag)--all-platforms[Download for every supported operating system and architecture, plus each language for casks, fetching each distinct URL once]' \
'(--bottle-tag --all-platforms)--arch[Download for the given CPU architecture. (Pass `all` to download for all architectures.)]' \
'(--build-from-source --build-bottle --force-bottle --cask --os --arch --all-platforms)--bottle-tag[Download a bottle for given tag]' \
'(--build-from-source --force-bottle --bottle-tag --cask)--build-bottle[Download source packages (for eventual bottling) rather than a bottle]' \
'(--build-bottle --force-bottle --bottle-tag)--build-from-source[Download source packages rather than a bottle]' \
'--debug[Display any debugging information]' \
'(--cask)--deps[Also download dependencies for any listed formula]' \
'--force[Remove a previously cached version and re-fetch]' \
'(--build-from-source --build-bottle --bottle-tag --cask)--force-bottle[Download a bottle if it exists for the current or newest version of macOS, even if it would not be used during installation]' \
'--help[Show this message]' \
'(--bottle-tag)--os[Download for the given operating system. (Pass `all` to download for all operating systems.)]' \
'(--bottle-tag --all-platforms)--os[Download for the given operating system. (Pass `all` to download for all operating systems.)]' \
'--quiet[Make some output more quiet]' \
'--retry[Retry if downloading fails or re-download if the checksum of a previously cached version no longer matches. Tries at most 5 times with exponential backoff]' \
'--verbose[Do a verbose VCS checkout, if the URL represents a VCS. This is useful for seeing if an existing VCS cache has been updated]' \
Expand Down
5 changes: 5 additions & 0 deletions docs/Manpage.md
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,11 @@ binaries for *`cask`*s. For files, also print SHA-256 checksums.
: Download for the given CPU architecture. (Pass `all` to download for all
architectures.)

`--all-platforms`

: Download for every supported operating system and architecture, plus each
language for *`cask`*s, fetching each distinct URL once.

`--bottle-tag`

: Download a bottle for given tag.
Expand Down
3 changes: 3 additions & 0 deletions manpages/brew.1
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,9 @@ Download for the given operating system\. (Pass \fBall\fP to download for all op
\fB\-\-arch\fP
Download for the given CPU architecture\. (Pass \fBall\fP to download for all architectures\.)
.TP
\fB\-\-all\-platforms\fP
Download for every supported operating system and architecture, plus each language for \fIcask\fPs, fetching each distinct URL once\.
.TP
\fB\-\-bottle\-tag\fP
Download a bottle for given tag\.
.TP
Expand Down
Loading