76 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			76 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| #
 | |
| # This script figures the order in which workspace crates must be published to
 | |
| # crates.io.  Along the way it also ensures there are no circular dependencies
 | |
| # that would cause a |cargo publish| to fail.
 | |
| #
 | |
| # On success an ordered list of Cargo.toml files is written to stdout
 | |
| #
 | |
| 
 | |
| import os
 | |
| import json
 | |
| import subprocess
 | |
| import sys;
 | |
| 
 | |
| real_file = os.path.realpath(__file__)
 | |
| ci_path = os.path.dirname(real_file)
 | |
| src_root = os.path.dirname(ci_path)
 | |
| 
 | |
| def load_metadata():
 | |
|     cmd = f'{src_root}/cargo metadata --no-deps --format-version=1'
 | |
|     return json.loads(subprocess.Popen(
 | |
|         cmd, shell=True, stdout=subprocess.PIPE).communicate()[0])
 | |
| 
 | |
| def get_packages():
 | |
|     metadata = load_metadata()
 | |
| 
 | |
|     manifest_path = dict()
 | |
| 
 | |
|     # Build dictionary of packages and their immediate solana-only dependencies
 | |
|     dependency_graph = dict()
 | |
|     for pkg in metadata['packages']:
 | |
|         manifest_path[pkg['name']] = pkg['manifest_path'];
 | |
|         dependency_graph[pkg['name']] = [x['name'] for x in pkg['dependencies'] if x['name'].startswith('solana')];
 | |
| 
 | |
|     # Check for direct circular dependencies
 | |
|     circular_dependencies = set()
 | |
|     for package, dependencies in dependency_graph.items():
 | |
|         for dependency in dependencies:
 | |
|             if dependency in dependency_graph and package in dependency_graph[dependency]:
 | |
|                 circular_dependencies.add(' <--> '.join(sorted([package, dependency])))
 | |
| 
 | |
|     for dependency in circular_dependencies:
 | |
|         sys.stderr.write('Error: Circular dependency: {}\n'.format(dependency))
 | |
| 
 | |
|     if len(circular_dependencies) != 0:
 | |
|         sys.exit(1)
 | |
| 
 | |
|     # Order dependencies
 | |
|     sorted_dependency_graph = []
 | |
|     max_iterations = pow(len(dependency_graph),2)
 | |
|     while dependency_graph:
 | |
|         deleted_packages = []
 | |
|         if max_iterations == 0:
 | |
|             # One day be more helpful and find the actual cycle for the user...
 | |
|             sys.exit('Error: Circular dependency suspected between these packages: \n {}\n'.format('\n '.join(dependency_graph.keys())))
 | |
| 
 | |
|         max_iterations -= 1
 | |
| 
 | |
|         for package, dependencies in dependency_graph.items():
 | |
|             if package in deleted_packages:
 | |
|                 continue
 | |
|             for dependency in dependencies:
 | |
|                 if dependency in dependency_graph:
 | |
|                     break
 | |
|             else:
 | |
|                 deleted_packages.append(package)
 | |
|                 sorted_dependency_graph.append((package, manifest_path[package]))
 | |
| 
 | |
|         dependency_graph = {p: d for p, d in dependency_graph.items() if not p in deleted_packages }
 | |
| 
 | |
| 
 | |
|     return sorted_dependency_graph
 | |
| 
 | |
| for package, manifest in get_packages():
 | |
|     print(os.path.relpath(manifest))
 |