5252from rift .Mock import Mock
5353from rift .Package import Package , Test
5454from rift .Repository import LocalRepository , ProjectArchRepositories
55+ from rift .graph import PackagesDependencyGraph
5556from rift .RPM import RPM , Spec , RPMLINT_CONFIG_V1 , RPMLINT_CONFIG_V2
5657from rift .TempDir import TempDir
5758from rift .TestResults import TestCase , TestResults
@@ -140,6 +141,8 @@ def make_parser():
140141 subprs .add_argument ('-s' , '--sign' , action = 'store_true' ,
141142 help = 'sign built packages with GPG key '
142143 '(implies -p, --publish)' )
144+ subprs .add_argument ('-S' , '--skip-deps' , action = 'store_true' ,
145+ help = 'Skip automatic rebuild of reverse dependencies' )
143146 subprs .add_argument ('--junit' , metavar = 'FILENAME' ,
144147 help = 'write junit result file' )
145148 subprs .add_argument ('--dont-update-repo' , dest = 'updaterepo' , action = 'store_false' ,
@@ -178,6 +181,8 @@ def make_parser():
178181 help = 'write junit result file' )
179182 subprs .add_argument ('-p' , '--publish' , action = 'store_true' ,
180183 help = 'publish build RPMS to repository' )
184+ subprs .add_argument ('-S' , '--skip-deps' , action = 'store_true' ,
185+ help = 'Skip automatic validation of reverse dependencies' )
181186
182187 # Validate diff
183188 subprs = subparsers .add_parser ('validdiff' )
@@ -443,15 +448,19 @@ def __init__(self, pkg, config=None):
443448 Test .__init__ (self , cmd , "basic_install" )
444449 self .local = False
445450
446- def build_pkg (config , args , pkg , arch ):
451+ def build_pkg (config , args , pkg , arch , staging ):
447452 """
448453 Build a package for a specific architecture
449454 - config: rift configuration
455+ - args: command line arguments
450456 - pkg: package to build
451- - repo: rpm repositories to use
452- - suppl_repos: optional additional repositories
457+ - arch: CPU architecture
458+ - staging: temporary staging rpm repositories to hold dependencies when
459+ testing builds of reserve dependencies recursively.
453460 """
454- repos = ProjectArchRepositories (config , arch )
461+ repos = ProjectArchRepositories (config , arch ,
462+ extra = staging .consumables [arch ]
463+ if staging is not None else None )
455464 if args .publish and not repos .can_publish ():
456465 raise RiftError ("Cannot publish if 'working_repo' is undefined" )
457466
@@ -468,6 +477,14 @@ def build_pkg(config, args, pkg, arch):
468477 logging .info ('Built: %s' , rpm .filepath )
469478 message ("RPMS successfully built" )
470479
480+ # If defined, publish in staging repository
481+ if staging :
482+ message ("Publishing RPMS in staging repository..." )
483+ mock .publish (staging )
484+
485+ message ("Updating staging repository..." )
486+ staging .update ()
487+
471488 # Publish
472489 if args .publish :
473490 message ("Publishing RPMS..." )
@@ -576,7 +593,11 @@ def validate_pkgs(config, args, pkgs, arch):
576593 - launch tests
577594 """
578595
579- repos = ProjectArchRepositories (config , arch )
596+ # Create staging repository for all packages and add it to the project
597+ # supplementary repositories.
598+ (staging , stagedir ) = create_staging_repo (config )
599+ repos = ProjectArchRepositories (config , arch ,
600+ extra = staging .consumables [arch ])
580601
581602 if args .publish and not repos .can_publish ():
582603 raise RiftError ("Cannot publish if 'working_repo' is undefined" )
@@ -615,10 +636,12 @@ def validate_pkgs(config, args, pkgs, arch):
615636 message ('Validate specfile...' )
616637 spec .check (pkg )
617638
618- (staging , stagedir ) = create_staging_repo (config )
619-
620639 message ('Preparing Mock environment...' )
621640 mock = Mock (config , arch , config .get ('version' ))
641+
642+ for repo in repos .all :
643+ logging .debug ("Mock with repo %s: %s" , repo .name , repo .url )
644+
622645 mock .init (repos .all )
623646
624647 try :
@@ -665,7 +688,8 @@ def validate_pkgs(config, args, pkgs, arch):
665688 message ("Keep environment, VM is running. Use: rift vm connect" )
666689 else :
667690 mock .clean ()
668- stagedir .delete ()
691+
692+ stagedir .delete ()
669693
670694 banner (f"All packages checked on architecture { arch } " )
671695
@@ -747,7 +771,7 @@ def action_vm(args, config):
747771 ret = vm_build (vm , args , config )
748772 return ret
749773
750- def build_pkgs (config , args , pkgs , arch ):
774+ def build_pkgs (config , args , pkgs , arch , staging ):
751775 """
752776 Build a list of packages on a given architecture and return results.
753777 """
@@ -776,7 +800,7 @@ def build_pkgs(config, args, pkgs, arch):
776800 now = time .time ()
777801 try :
778802 pkg .load ()
779- build_pkg (config , args , pkg , arch )
803+ build_pkg (config , args , pkg , arch , staging )
780804 except RiftError as ex :
781805 logging .error ("Build failure: %s" , str (ex ))
782806 results .add_failure (case , time .time () - now , err = str (ex ))
@@ -800,17 +824,30 @@ def action_build(args, config):
800824 results = TestResults ('build' )
801825
802826 staff , modules = staff_modules (config )
827+ pkgs = get_packages_to_build (config , staff , modules , args )
828+ logging .info (
829+ "Ordered list of packages to build: %s" ,
830+ str ([pkg .name for pkg in pkgs ])
831+ )
803832
804833 # Build all packages for all project supported architectures
805834 for arch in config .get ('arch' ):
806835
807- pkgs = Package .list (config , staff , modules , args .packages )
808- results .extend (build_pkgs (config , args , pkgs , arch ))
836+ # Create temporary staging repository to hold dependencies unless
837+ # dependency tracking is disabled in project configuration or user set
838+ # --skip-deps argument.
839+ staging = stagedir = None
840+ if config .get ('dependency_tracking' ) and not args .skip_deps :
841+ (staging , stagedir ) = create_staging_repo (config )
842+
843+ results .extend (build_pkgs (config , args , pkgs , arch , staging ))
809844
810845 if getattr (args , 'junit' , False ):
811846 logging .info ('Writing test results in %s' , args .junit )
812847 results .junit (args .junit )
813848
849+ if stagedir :
850+ stagedir .delete ()
814851 banner (f"All packages processed for architecture { arch } " )
815852
816853 banner ('All architectures processed' )
@@ -866,13 +903,18 @@ def action_validate(args, config):
866903
867904 staff , modules = staff_modules (config )
868905 results = TestResults ('validate' )
906+ pkgs = get_packages_to_build (config , staff , modules , args )
907+ logging .info (
908+ "Ordered list of packages to validate: %s" ,
909+ str ([pkg .name for pkg in pkgs ])
910+ )
869911 # Validate packages on all project supported architectures
870912 for arch in config .get ('arch' ):
871913 results .extend (
872914 validate_pkgs (
873915 config ,
874916 args ,
875- Package . list ( config , staff , modules , args . packages ) ,
917+ pkgs ,
876918 arch
877919 )
878920 )
@@ -1047,6 +1089,54 @@ def get_packages_from_patch(patch, config, modules, staff):
10471089
10481090 return updated , removed
10491091
1092+ def get_packages_to_build (config , staff , modules , args ):
1093+ """
1094+ Return ordered list of Packages to build. If dependency_tracking is disabled
1095+ in project configuration or --skip-deps arguments is set, only the list of
1096+ packages in arguments is selected. Else, this function builds a dependency
1097+ graph of all packages in the project to determine the list of packages that
1098+ reverse depends on the list of packages in arguments, recursively.
1099+ """
1100+ if not config .get ('dependency_tracking' ) or args .skip_deps :
1101+ return list (Package .list (config , staff , modules , args .packages ))
1102+
1103+ # Build dependency graph with all projects packages.
1104+ graph = PackagesDependencyGraph .from_project (config , staff , modules )
1105+
1106+ result = []
1107+
1108+ def result_position (new_build_requirements ):
1109+ """
1110+ Return the first index in result of packages in provided build
1111+ requirements list.
1112+ """
1113+ for build_requirement in new_build_requirements :
1114+ for index , package in enumerate (result ):
1115+ if build_requirement .package == package :
1116+ return index
1117+ return - 1
1118+
1119+ for pkg in Package .list (config , staff , modules , args .packages ):
1120+ required_builds = graph .solve (pkg )
1121+ for index , required_build in enumerate (required_builds ):
1122+ if required_build .package in result :
1123+ continue
1124+ # Search the position in result before all its own reverse
1125+ # dependencies.
1126+ position = result_position (required_builds [index + 1 :])
1127+ logging .info (
1128+ "Package %s must be built: %s" ,
1129+ required_build .package .name ,
1130+ required_build .reasons ,
1131+ )
1132+ # No position constraint in result, just append the package at the
1133+ # end. Else insert at the right position.
1134+ if position == - 1 :
1135+ result .append (required_build .package )
1136+ else :
1137+ result .insert (position , required_build .package )
1138+ return result
1139+
10501140def create_staging_repo (config ):
10511141 """
10521142 Create and return staging temporary repository with a 2-tuple containing
0 commit comments