depinst option to reject dependency cycles

This commit is contained in:
Dmitry Arkhipov
2025-10-10 17:40:09 +03:00
parent d5c93f78bd
commit dad7ac20c9
2 changed files with 68 additions and 21 deletions
+1 -1
View File
@@ -391,7 +391,7 @@ jobs:
- throw_exception
- uuid
args: [ '' ]
args: [ '', '-C' ]
runs-on: ubuntu-latest
+67 -20
View File
@@ -70,7 +70,7 @@ def module_for_header( h, x, gm ):
return None
def scan_header_dependencies( f, x, gm, deps ):
def scan_header_dependencies( f, x, gm, deps, dep_path ):
for line in f:
@@ -89,8 +89,13 @@ def scan_header_dependencies( f, x, gm, deps ):
vprint( 1, 'Adding dependency', mod )
deps[ mod ] = 0
elif len(dep_path) > 1 and mod == dep_path[0]:
def scan_directory( d, x, gm, deps ):
dep_path = dep_path + [ mod ]
raise DependencyCycle( 'Dependency cycle detected: ' + ' -> '.join(dep_path) )
def scan_directory( d, x, gm, deps, dep_path ):
vprint( 1, 'Scanning directory', d )
@@ -108,20 +113,20 @@ def scan_directory( d, x, gm, deps ):
if sys.version_info[0] < 3:
with open( fn, 'r' ) as f:
scan_header_dependencies( f, x, gm, deps )
scan_header_dependencies( f, x, gm, deps, dep_path )
else:
with open( fn, 'r', encoding='latin-1' ) as f:
scan_header_dependencies( f, x, gm, deps )
scan_header_dependencies( f, x, gm, deps, dep_path )
def scan_module_dependencies( m, x, gm, deps, dirs ):
def scan_module_dependencies( m, x, gm, deps, dirs, dep_path ):
vprint( 1, 'Scanning module', m )
for dir in dirs:
scan_directory( os.path.join( 'libs', m, dir ), x, gm, deps )
scan_directory( os.path.join( 'libs', m, dir ), x, gm, deps, dep_path )
def read_exceptions():
@@ -197,7 +202,7 @@ def install_modules( modules, git_args ):
raise Exception( "The command '%s' failed with exit code %d" % (command, r) )
def install_module_dependencies( deps, x, gm, git_args ):
def install_module_dependencies( deps, x, gm, git_args, dep_path ):
modules = []
@@ -214,11 +219,20 @@ def install_module_dependencies( deps, x, gm, git_args ):
install_modules( modules, git_args )
for m in modules:
scan_module_dependencies( m, x, gm, deps, [ 'include', 'src' ] )
next_dep_path = dep_path
if dep_path:
next_dep_path = dep_path + [m]
scan_module_dependencies( m, x, gm, deps, [ 'include', 'src' ], next_dep_path )
return len( modules )
class DependencyCycle(Exception):
pass
if( __name__ == "__main__" ):
parser = argparse.ArgumentParser( description='Installs the dependencies needed to test a Boost library.' )
@@ -230,6 +244,7 @@ if( __name__ == "__main__" ):
parser.add_argument( '-I', '--include', help="additional subdirectory to scan; can be repeated", metavar='DIR', action='append', default=[] )
parser.add_argument( '-g', '--git_args', help="additional arguments to `git submodule update`", default='', action='store' )
parser.add_argument( '-u', '--update', help='update <library> before scanning', action='store_true' )
parser.add_argument( '-C', '--reject-cycles', help='abort if <library> has cyclical dependencies', action='store_true' )
parser.add_argument( 'library', help="name of library to scan ('libs/' will be prepended)" )
args = parser.parse_args()
@@ -239,6 +254,7 @@ if( __name__ == "__main__" ):
vprint( 2, '-X:', args.exclude )
vprint( 2, '-I:', args.include )
vprint( 2, '-N:', args.ignore )
vprint( 2, '-C:', args.reject_cycles )
x = read_exceptions()
vprint( 2, 'Exceptions:', x )
@@ -257,26 +273,57 @@ if( __name__ == "__main__" ):
m = args.library
deps = { m : 1 }
dirs = [ 'include', 'src', 'test' ]
main_dirs = [ 'include', 'src' ]
extra_dirs = [ 'test' ]
for dir in args.exclude:
dirs.remove( dir )
if dir in main_dirs:
main_dirs.remove( dir )
if dir in extra_dirs:
extra_dirs.remove( dir )
for dir in args.include:
dirs.append( dir )
extra_dirs.append( dir )
vprint( 1, 'Directories to scan:', *dirs )
vprint( 1, 'Directories to scan:', *(main_dirs + extra_dirs) )
scan_module_dependencies( m, x, gm, deps, dirs )
dep_path = []
if args.reject_cycles:
dep_path.append( m )
main_deps = { m : 1 }
scan_module_dependencies( m, x, gm, main_deps, main_dirs, dep_path )
extra_deps = { m : 1 }
scan_module_dependencies( m, x, gm, extra_deps, extra_dirs, [] )
for dep in args.ignore:
if dep in deps:
reported = False
if dep in main_deps:
vprint( 1, 'Ignoring dependency', dep )
del deps[dep]
reported = True
del main_deps[dep]
vprint( 2, 'Dependencies:', deps )
if dep in extra_deps:
if not reported:
vprint( 1, 'Ignoring dependency', dep )
reported = True
del extra_deps[dep]
while install_module_dependencies( deps, x, gm, args.git_args ):
pass
all_deps = dict( **main_deps )
all_deps.update( extra_deps )
vprint( 2, 'Dependencies:', all_deps )
try:
while install_module_dependencies( main_deps, x, gm, args.git_args, dep_path ):
pass
while install_module_dependencies( extra_deps, x, gm, args.git_args, [] ):
pass
except DependencyCycle as e:
print( e, file=sys.stderr )
sys.exit(1)