@@ -66,6 +66,7 @@ class RebuildContext:
6666 missing_packages : dict = field (default_factory = lambda : collections .defaultdict (set ))
6767 unresolvable_components : set = field (default_factory = set )
6868 prerel_abi_blocked_components : set = field (default_factory = set )
69+ component_availability : dict = field (default_factory = dict )
6970
7071
7172def _query_packages_by_deps (sack_getter , deps , excluded_components ):
@@ -208,7 +209,15 @@ def _update_blocker_statistics(blocking_components, blocker_counter, loop_detect
208209 blocker_counter ['single' ][next (iter (blocking_components ))] += 1
209210 elif 1 < len (blocking_components ) < 10 : # this is an arbitrarily chosen number to avoid cruft
210211 blocker_counter ['combinations' ][tuple (sorted (blocking_components ))] += 1
211- loop_detector [component ] = sorted (blocking_components )
212+ known_blocking_components = set (loop_detector .get (component , []))
213+ if not known_blocking_components :
214+ loop_detector [component ] = sorted (blocking_components )
215+ else :
216+ if blocking_components <= known_blocking_components :
217+ pass
218+ else :
219+ # can this ever happen?
220+ loop_detector [component ] = sorted (known_blocking_components | blocking_components )
212221
213222
214223def are_all_done (component , packages_to_check , ctx ):
@@ -486,31 +495,45 @@ def check_bcond_build(component, bcond_config, number_of_resolved, ctx):
486495 return ready_to_rebuild
487496
488497
498+ def extract_bcond_identifier (s ):
499+ non_empty_parts = [p for p in s .split (':' ) if p ]
500+ return non_empty_parts [1 ] if len (non_empty_parts ) >= 2 else None
501+
502+
489503def check_bcond_builds (component , number_of_resolved , ctx ):
490504 """
491505 Check all bcond builds for a component that isn't ready for regular build.
492-
506+
493507 Args:
494508 component: Name of the component to check
495509 number_of_resolved: Number of packages in regular build (for comparison)
496510 ctx: RebuildContext with shared data and state
497-
511+
498512 Prints any bcond build identifiers that are ready to rebuild.
513+
514+ Returns:
515+ tuple: (buildable_bconds, non_buildable_bconds)
499516 """
500517 from bconds import bcond_cache_identifier
501-
502- if component not in CONFIG ['bconds' ]:
503- return
504-
505- for bcond_config in CONFIG ['bconds' ][component ]:
506- bcond_config ['id' ] = bcond_cache_identifier (component , bcond_config )
507- log (f'• { component } not ready and { bcond_config ["id" ]} bcond found, will check that one' )
508-
509- ready_to_rebuild = check_bcond_build (component , bcond_config , number_of_resolved , ctx )
510-
511- if ready_to_rebuild :
512- if should_print_component (component , ctx .components_done ):
513- print (bcond_config ['id' ])
518+
519+ buildable_bconds = []
520+ non_buildable_bconds = []
521+
522+ if component in CONFIG ['bconds' ]:
523+ for bcond_config in CONFIG ['bconds' ][component ]:
524+ bcond_config ['id' ] = bcond_cache_identifier (component , bcond_config )
525+ log (f'• { component } not ready and { bcond_config ["id" ]} bcond found, will check that one' )
526+
527+ ready_to_rebuild = check_bcond_build (component , bcond_config , number_of_resolved , ctx )
528+
529+ if ready_to_rebuild :
530+ if should_print_component (component , ctx .components_done ):
531+ print (bcond_config ['id' ])
532+ buildable_bconds .append (extract_bcond_identifier (bcond_config ['id' ]))
533+ else :
534+ non_buildable_bconds .append (extract_bcond_identifier (bcond_config ['id' ]))
535+
536+ return sorted (buildable_bconds ), sorted (non_buildable_bconds )
514537
515538
516539def should_print_component (component , components_done ):
@@ -526,20 +549,30 @@ def should_print_component(component, components_done):
526549def process_component (component , ctx ):
527550 """
528551 Process a single component: check regular build and bcond builds if needed.
529-
552+ Stores availability data in ctx.component_availability.
553+
530554 Args:
531555 component: Name of the component to process
532556 ctx: RebuildContext with shared data and state
533-
557+
534558 Prints the component or bcond identifier if ready to rebuild.
535559 """
536560 ready_to_rebuild , number_of_resolved = check_regular_build (component , ctx )
537-
561+ availability = {
562+ 'can_build_regular' : ready_to_rebuild ,
563+ 'buildable_bconds' : [],
564+ 'non_buildable_bconds' : [],
565+ }
566+
538567 if ready_to_rebuild :
539568 if should_print_component (component , ctx .components_done ):
540569 print (component )
541570 else :
542- check_bcond_builds (component , number_of_resolved , ctx )
571+ buildable_bconds , non_buildable_bconds = check_bcond_builds (component , number_of_resolved , ctx )
572+ availability ['buildable_bconds' ] = buildable_bconds
573+ availability ['non_buildable_bconds' ] = non_buildable_bconds
574+
575+ ctx .component_availability [component ] = availability
543576
544577
545578def assemble_component_info (component , count , ctx ):
@@ -596,7 +629,6 @@ def generate_reports(ctx):
596629 log ('\n The 20 most commonly last-blocking small combinations of components are:' )
597630 for components_tuple , count in ctx .blocker_counter ['combinations' ].most_common (20 ):
598631 log (f'{ count :>5} { ", " .join (components_tuple )} ' )
599-
600632 report_data ['most_commonly_last_blocking_combinations' ].append ({
601633 'components' : list (components_tuple ),
602634 'count' : count
@@ -610,6 +642,35 @@ def generate_reports(ctx):
610642 log ('\n Report saved to commonly-needed-report.json' )
611643
612644
645+ def generate_component_availability_report (ctx ):
646+ """
647+ Generate and save full component availability report.
648+ Creates component-availability-report.json with per-component data.
649+
650+ Args:
651+ ctx: RebuildContext with component availability data
652+ """
653+
654+ report_data = {}
655+ for component in ctx .components :
656+ availability = ctx .component_availability .get (component , {})
657+
658+ report_data [component ] = {
659+ 'was_already_built' : component in ctx .components_done ,
660+ 'is_resolvable' : component not in ctx .unresolvable_components ,
661+ 'prerel_abi_compatible' : component not in ctx .prerel_abi_blocked_components ,
662+ 'can_build_regular' : availability .get ('can_build_regular' , False ),
663+ 'buildable_bconds' : availability .get ('buildable_bconds' ),
664+ 'non_buildable_bconds' : availability .get ('non_buildable_bconds' ),
665+ 'blocked_by' : sorted (ctx .loop_detector .get (component , [])),
666+ 'blocks_count' : ctx .blocker_counter ['general' ].get (component , 0 )
667+ }
668+
669+ with open ('component-availability-report.json' , 'w' ) as f :
670+ json .dump (report_data , f , indent = 2 , sort_keys = True )
671+ log ('Full component availability report saved to component-availability-report.json' )
672+
673+
613674def main ():
614675 """
615676 Main entry point for rebuild analysis.
@@ -638,8 +699,9 @@ def main():
638699
639700 for component in components_to_process :
640701 process_component (component , ctx )
641-
702+
642703 generate_reports (ctx )
704+ generate_component_availability_report (ctx )
643705
644706
645707if __name__ == '__main__' :
0 commit comments