@@ -44,6 +44,9 @@ class FormulaInstaller
4444 sig { returns ( T ::Boolean ) }
4545 attr_accessor :link_keg
4646
47+ sig { returns ( T . nilable ( Homebrew ::DownloadQueue ) ) }
48+ attr_accessor :download_queue
49+
4750 sig {
4851 params (
4952 formula : Formula ,
@@ -136,9 +139,12 @@ def initialize(
136139 @hold_locks = T . let ( false , T ::Boolean )
137140 @show_summary_heading = T . let ( false , T ::Boolean )
138141 @etc_var_preinstall = T . let ( [ ] , T ::Array [ Pathname ] )
142+ @download_queue = T . let ( nil , T . nilable ( Homebrew ::DownloadQueue ) )
139143
140144 # Take the original formula instance, which might have been swapped from an API instance to a source instance
141145 @formula = T . let ( T . must ( previously_fetched_formula ) , Formula ) if previously_fetched_formula
146+
147+ @ran_prelude_fetch = T . let ( false , T ::Boolean )
142148 end
143149
144150 sig { returns ( T ::Boolean ) }
@@ -294,7 +300,7 @@ def install_bottle_for?(dep, build)
294300 end
295301
296302 sig { void }
297- def prelude
303+ def prelude_fetch
298304 deprecate_disable_type = DeprecateDisable . type ( formula )
299305 if deprecate_disable_type . present?
300306 message = "#{ formula . full_name } has been #{ DeprecateDisable . message ( formula ) } "
@@ -312,8 +318,24 @@ def prelude
312318 end
313319 end
314320
321+ # Needs to be done before expand_dependencies for compute_dependencies
322+ fetch_bottle_tab if pour_bottle?
323+
324+ @ran_prelude_fetch = true
325+ end
326+
327+ sig { void }
328+ def prelude
329+ prelude_fetch unless @ran_prelude_fetch
330+
315331 Tab . clear_cache
316332
333+ # Setup bottle_tab_runtime_dependencies for compute_dependencies
334+ @bottle_tab_runtime_dependencies = formula . bottle_tab_attributes
335+ . fetch ( "runtime_dependencies" , [ ] ) . then { |deps | deps || [ ] }
336+ . each_with_object ( { } ) { |dep , h | h [ dep [ "full_name" ] ] = dep }
337+ . freeze
338+
317339 verify_deps_exist unless ignore_deps?
318340
319341 forbidden_license_check
@@ -778,13 +800,15 @@ def install_dependencies(deps)
778800 if deps . empty? && only_deps?
779801 puts "All dependencies for #{ formula . full_name } are satisfied."
780802 elsif !deps . empty?
781- oh1 "Installing dependencies for #{ formula . full_name } : " \
782- "#{ deps . map ( &:first ) . map { Formatter . identifier ( _1 ) } . to_sentence } " ,
783- truncate : false
803+ if deps . length > 1
804+ oh1 "Installing dependencies for #{ formula . full_name } : " \
805+ "#{ deps . map ( &:first ) . map { Formatter . identifier ( _1 ) } . to_sentence } " ,
806+ truncate : false
807+ end
784808 deps . each { |dep , options | install_dependency ( dep , options ) }
785809 end
786810
787- @show_header = true unless deps . empty?
811+ @show_header = true if deps . length > 1
788812 end
789813
790814 sig { params ( dep : Dependency ) . void }
@@ -808,6 +832,7 @@ def fetch_dependency(dep)
808832 quiet : quiet? ,
809833 verbose : verbose? ,
810834 )
835+ fi . download_queue = download_queue
811836 fi . prelude
812837 fi . fetch
813838 end
@@ -830,7 +855,7 @@ def install_dependency(dep, inherited_options)
830855 installed_keg = Keg . new ( df . prefix )
831856 tab ||= installed_keg . tab
832857 tmp_keg = Pathname . new ( "#{ installed_keg } .tmp" )
833- installed_keg . rename ( tmp_keg )
858+ installed_keg . rename ( tmp_keg ) unless tmp_keg . directory?
834859 end
835860
836861 if df . tap . present? && tab . present? && ( tab_tap = tab . source [ "tap" ] . presence ) &&
@@ -867,6 +892,7 @@ def install_dependency(dep, inherited_options)
867892 verbose : verbose? ,
868893 )
869894 oh1 "Installing #{ formula . full_name } dependency: #{ Formatter . identifier ( dep . name ) } "
895+ fi . prelude
870896 fi . install
871897 fi . finish
872898 # Handle all possible exceptions installing deps.
@@ -1337,9 +1363,13 @@ def fetch_dependencies
13371363
13381364 return if deps . empty?
13391365
1340- oh1 "Fetching dependencies for #{ formula . full_name } : " \
1341- "#{ deps . map ( &:first ) . map { Formatter . identifier ( _1 ) } . to_sentence } " ,
1342- truncate : false
1366+ unless download_queue
1367+ dependencies_string = deps . map ( &:first )
1368+ . map { Formatter . identifier ( _1 ) }
1369+ . to_sentence
1370+ oh1 "Fetching dependencies for #{ formula . full_name } : #{ dependencies_string } " ,
1371+ truncate : false
1372+ end
13431373
13441374 deps . each { |( dep , _options ) | fetch_dependency ( dep ) }
13451375 end
@@ -1360,15 +1390,18 @@ def previously_fetched_formula
13601390 def fetch_bottle_tab ( quiet : false )
13611391 return if @fetch_bottle_tab
13621392
1363- begin
1364- formula . fetch_bottle_tab ( quiet : quiet )
1365- @bottle_tab_runtime_dependencies = formula . bottle_tab_attributes
1366- . fetch ( "runtime_dependencies" , [ ] ) . then { |deps | deps || [ ] }
1367- . each_with_object ( { } ) { |dep , h | h [ dep [ "full_name" ] ] = dep }
1368- . freeze
1369- rescue DownloadError , Resource ::BottleManifest ::Error
1370- # do nothing
1393+ if ( download_queue = self . download_queue ) &&
1394+ ( bottle = formula . bottle ) &&
1395+ ( manifest_resource = bottle . github_packages_manifest_resource )
1396+ download_queue . enqueue ( manifest_resource )
1397+ else
1398+ begin
1399+ formula . fetch_bottle_tab ( quiet : quiet )
1400+ rescue DownloadError , Resource ::BottleManifest ::Error
1401+ # do nothing
1402+ end
13711403 end
1404+
13721405 @fetch_bottle_tab = T . let ( true , T . nilable ( TrueClass ) )
13731406 end
13741407
@@ -1381,7 +1414,7 @@ def fetch
13811414 return if only_deps?
13821415 return if formula . local_bottle_path . present?
13831416
1384- oh1 "Fetching #{ Formatter . identifier ( formula . full_name ) } " . strip
1417+ oh1 "Fetching #{ Formatter . identifier ( formula . full_name ) } " . strip unless download_queue
13851418
13861419 downloadable_object = downloadable
13871420 check_attestation = if pour_bottle? ( output_warning : true )
@@ -1391,19 +1424,31 @@ def fetch
13911424 else
13921425 @formula = Homebrew ::API ::Formula . source_download ( formula ) if formula . loaded_from_api?
13931426
1394- formula . fetch_patches
1395- formula . resources . each ( &:fetch )
1427+ if ( download_queue = self . download_queue )
1428+ formula . enqueue_resources_and_patches ( download_queue :)
1429+ else
1430+ formula . fetch_patches
1431+ formula . resources . each ( &:fetch )
1432+ end
1433+
13961434 downloadable_object = downloadable
13971435
13981436 false
13991437 end
1400- downloadable_object . fetch
1438+
1439+ if ( download_queue = self . download_queue )
1440+ download_queue . enqueue ( downloadable_object )
1441+ else
1442+ downloadable_object . fetch
1443+ end
14011444
14021445 # We skip `gh` to avoid a bootstrapping cycle, in the off-chance a user attempts
14031446 # to explicitly `brew install gh` without already having a version for bootstrapping.
14041447 # We also skip bottle installs from local bottle paths, as these are done in CI
14051448 # as part of the build lifecycle before attestations are produced.
14061449 if check_attestation &&
1450+ # TODO: support this for download queues at some point
1451+ download_queue . nil? &&
14071452 Homebrew ::Attestation . enabled? &&
14081453 formula . tap &.core_tap? &&
14091454 formula . name != "gh"
@@ -1489,7 +1534,10 @@ def downloadable
14891534 sig { void }
14901535 def pour
14911536 HOMEBREW_CELLAR . cd do
1492- downloadable . downloader . stage
1537+ # download queue has already done the actual staging but we'll lie about
1538+ # pouring now for nicer output
1539+ ohai "Pouring #{ downloadable . downloader . basename } "
1540+ downloadable . downloader . stage unless download_queue
14931541 end
14941542
14951543 Tab . clear_cache
0 commit comments